From b88f377dd165d692540e1c9550a76f900124080e Mon Sep 17 00:00:00 2001 From: xue <> Date: Tue, 8 May 2007 19:25:56 +0000 Subject: fixed #613 --- framework/Web/Javascripts/clientscripts.php | 888 +++++++--------------------- 1 file changed, 224 insertions(+), 664 deletions(-) (limited to 'framework/Web') diff --git a/framework/Web/Javascripts/clientscripts.php b/framework/Web/Javascripts/clientscripts.php index 1070a8d0..f66ebb3d 100644 --- a/framework/Web/Javascripts/clientscripts.php +++ b/framework/Web/Javascripts/clientscripts.php @@ -138,16 +138,7 @@ function combine_javascript($files) */ function minify_javascript($content, $files) { - $jsMin = new JSMin($content, false); - try - { - return $jsMin->minify(); - } - catch (Exception $e) - { - error_log('Prado client script: unable to strip javascript comments in one or more files in "'.implode(', ', $files).'"'); - return $content; - } + return jsm_minify($content); } /** @@ -257,704 +248,273 @@ function supports_gzip_encoding() } } -define('JSMIN_AS_LIB', true); - -/** -* JSMin_lib.php (for PHP 4, 5) -* -* PHP adaptation of JSMin, published by Douglas Crockford as jsmin.c, also based -* on its Java translation by John Reilly. -* -* Permission is hereby granted to use the PHP version under the same conditions -* as jsmin.c, which has the following notice : -* -* ---------------------------------------------------------------------------- -* -* Copyright (c) 2002 Douglas Crockford (www.crockford.com) -* -* Permission is hereby granted, free of charge, to any person obtaining a copy of -* this software and associated documentation files (the "Software"), to deal in -* the Software without restriction, including without limitation the rights to -* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -* of the Software, and to permit persons to whom the Software is furnished to do -* so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in all -* copies or substantial portions of the Software. -* -* The Software shall be used for Good, not Evil. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -* SOFTWARE. -*/ - -/** -* Version of this PHP translation. -*/ - -define('JSMIN_VERSION', '0.2'); - -/** -* How fgetc() reports an End Of File. -* N.B. : use === and not == to test the result of fgetc() ! (see manual) -*/ - -define('EOF', FALSE); - -/** -* Some ASCII character ordinals. -* N.B. : PHP identifiers are case-insensitive ! -*/ - -define('ORD_NL', ord("\n")); -define('ORD_space', ord(' ')); -define('ORD_cA', ord('A')); -define('ORD_cZ', ord('Z')); -define('ORD_a', ord('a')); -define('ORD_z', ord('z')); -define('ORD_0', ord('0')); -define('ORD_9', ord('9')); - -/** -* Generic exception class related to JSMin. -*/ -class JSMinException extends Exception { -} - -/** -* A JSMin exception indicating that a file provided for input or output could not be properly opened. -*/ - -class FileOpenFailedJSMinException extends JSMinException { -} - -/** -* A JSMin exception indicating that an unterminated comment was encountered in input. -*/ - -class UnterminatedCommentJSMinException extends JSMinException { -} - -/** -* A JSMin exception indicatig that an unterminated string literal was encountered in input. -*/ - -class UnterminatedStringLiteralJSMinException extends JSMinException { -} - /** -* A JSMin exception indicatig that an unterminated regular expression lieteral was encountered in input. -*/ - -class UnterminatedRegExpLiteralJSMinException extends JSMinException { -} - -/** - * Constant describing an {@link action()} : Output A. Copy B to A. Get the next B. - */ - -define ('JSMIN_ACT_FULL', 1); - -/** - * Constant describing an {@link action()} : Copy B to A. Get the next B. (Delete A). + * JSMin: the following jsm_* functions are adapted from JSMin_lib.php + * written by Douglas Crockford. Below is the original copyright notice: + * + * PHP adaptation of JSMin, published by Douglas Crockford as jsmin.c, also based + * on its Java translation by John Reilly. + * + * Permission is hereby granted to use the PHP version under the same conditions + * as jsmin.c, which has the following notice : + * + * ---------------------------------------------------------------------------- + * + * Copyright (c) 2002 Douglas Crockford (www.crockford.com) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. */ -define ('JSMIN_ACT_BUF', 2); +$jsm_in=''; +$jsm_out=''; +$jsm_length=0; +$jsm_pos=0; +$jsm_c1=''; +$jsm_c2=''; -/** - * Constant describing an {@link action()} : Get the next B. (Delete B). - */ +function jsm_minify($jsCode) +{ + global $jsm_in, $jsm_out, $jsm_length, $jsm_pos, $jsm_c1, $jsm_c2; -define ('JSMIN_ACT_IMM', 3); + $jsm_in=$jsCode; + $jsm_out=''; + $jsm_length=strlen($jsCode); + $jsm_pos=0; -/** -* Main JSMin application class. -* -* Example of use : -* -* $jsMin = new JSMin(...input..., ...output...); -* $jsMin->minify(); -* -* Do not specify input and/or output (or default to '-') to use stdin and/or stdout. -*/ - -class JSMin { - - /** - * The input stream, from which to read a JS file to minimize. Obtained by fopen(). - * NB: might be a string instead of a stream - * @var SplFileObject | string - */ - var $in; - - /** - * The output stream, in which to write the minimized JS file. Obtained by fopen(). - * NB: might be a string instead of a stream - * @var SplFileObject | string - */ - var $out; - - /** - * Temporary I/O character (A). - * @var string - */ - var $theA; - - /** - * Temporary I/O character (B). - * @var string - */ - var $theB; - - /** variables used for string-based parsing **/ - var $inLength = 0; - var $inPos = 0; - var $isString = false; - - /** - * Indicates whether a character is alphanumeric or _, $, \ or non-ASCII. - * - * @param string $c The single character to test. - * @return boolean Whether the char is a letter, digit, underscore, dollar, backslash, or non-ASCII. - */ - function isAlphaNum($c) { - - // Get ASCII value of character for C-like comparisons - - $a = ord($c); - - // Compare using defined character ordinals, or between PHP strings - // Note : === is micro-faster than == when types are known to be the same - - return - ($a >= ORD_a && $a <= ORD_z) || - ($a >= ORD_0 && $a <= ORD_9) || - ($a >= ORD_cA && $a <= ORD_cZ) || - $c === '_' || $c === '$' || $c === '\\' || $a > 126 - ; - } + // Initialize A and run the first (minimal) action + $jsm_c1="\n"; + jsm_next(); - /** - * Get the next character from the input stream. - * - * If said character is a control character, translate it to a space or linefeed. - * - * @return string The next character from the specified input stream. - * @see $in - * @see peek() - */ - function get() { - - // Get next input character and advance position in file - if ($this->isString) { - if ($this->inPos < $this->inLength) { - $c = $this->in[$this->inPos]; - ++$this->inPos; - } - else { - return EOF; - } + // Proceed all the way to the end of the input file + while($jsm_c1!==false) + { + if($jsm_c1===' ') + { + if(jsm_isAlphaNum($jsm_c2)) + jsm_write($jsm_c1); + else + jsm_buffer(); + } + else if($jsm_c1==="\n") + { + if($jsm_c2===' ') + jsm_next(); + else if(jsm_isAlphaNum($jsm_c2) || $jsm_c2==='{' || $jsm_c2==='[' || $jsm_c2==='(' || $jsm_c2==='+' || $jsm_c2==='-') + jsm_write($jsm_c1); + else + jsm_buffer(); } else - $c = $this->in->fgetc(); - - // Test for non-problematic characters - - if ($c === "\n" || $c === EOF || ord($c) >= ORD_space) { - return $c; - } - - // else - // Make linefeeds into newlines - - if ($c === "\r") { - return "\n"; - } - - // else - // Consider space - - return ' '; - } - - /** - * Get the next character from the input stream, without gettng it. - * - * @return string The next character from the specified input stream, without advancing the position - * in the underlying file. - * @see $in - * @see get() - */ - function peek() { - - if ($this->isString) { - if ($this->inPos < $this->inLength) { - $c = $this->in[$this->inPos]; + { + if($jsm_c2===' ') + { + if(jsm_isAlphaNum($jsm_c1)) + jsm_write($jsm_c1); + else + jsm_next(); } - else { - return EOF; + else if($jsm_c2==="\n") + { + if(jsm_isAlphaNum($jsm_c1) || $jsm_c1==='}' || $jsm_c1===']' || $jsm_c1===')' || $jsm_c1==='+' || $jsm_c1==='-' || $jsm_c1==='"' || $jsm_c1==='\'') + jsm_write($jsm_c1); + else + jsm_next(); } - } - else { - // Get next input character - - $c = $this->in->fgetc(); - - // Regress position in file - - $this->in->fseek(-1, SEEK_CUR); - - // Return character obtained - } - - return $c; - } - - /** - * Adds a char to the output steram / string - * @see $out - */ - function put($c) - { - if ($this->isString) { - $this->out .= $c; - } - else { - $this->out->fwrite($c); + else + jsm_write($jsm_c1); } } - /** - * Get the next character from the input stream, excluding comments. - * - * {@link peek()} is used to see if a '/' is followed by a '*' or '/'. - * Multiline comments are actually returned as a single space. - * - * @return string The next character from the specified input stream, skipping comments. - * @see $in - */ - function next() { - - // Get next char from input, translated if necessary - - $c = $this->get(); - - // Check comment possibility - - if ($c == '/') { - - // Look ahead : a comment is two slashes or slashes followed by asterisk (to be closed) - - switch ($this->peek()) { - - case '/' : - - // Comment is up to the end of the line - // TOTEST : simple $this->in->fgets() - - while (true) { - - $c = $this->get(); - - if (ord($c) <= ORD_NL) { - return $c; - } - } - - case '*' : + return $jsm_out; +} - // Comment is up to comment close. - // Might not be terminated, if we hit the end of file. +function jsm_write() +{ + global $jsm_c1; - while (true) { + jsm_put($jsm_c1); + jsm_buffer(); +} - // N.B. not using switch() because of having to test EOF with === +function jsm_buffer() +{ + global $jsm_c1, $jsm_c2; - $c = $this->get(); + $tmpA=$jsm_c1=$jsm_c2; - if ($c == '*') { + // Treating a string as a single char : outputting it whole + // Note that the string-opening char (" or ') is memorized in B - // Comment termination if the char ahead is a slash + if($tmpA==='\'' || $tmpA==='"') + { + while (true) + { + // Output string contents + jsm_put($tmpA); - if ($this->peek() == '/') { + // Get next character, watching out for termination of the current string, + // new line & co (then the string is not terminated !), or a backslash + // (upon which the following char is directly output to serve the escape mechanism) - // Advance again and make into a single space + $tmpA=$jsm_c1=jsm_get(); - $this->get(); - return ' '; - } - } - else if ($c === EOF) { + if($tmpA===$jsm_c2) // String terminated + break; // from while(true) - // Whoopsie - throw new UnterminatedCommentJSMinException(); - } - } + if(ord($tmpA) <= ord("\n")) + die('Unterminated string literal'); - default : + if($tmpA==='\\') + { + // Escape next char immediately + jsm_put($tmpA); + $tmpA=$jsm_c1=jsm_get(); + } + } + } - // Not a comment after all + jsm_next(); +} - return $c; +function jsm_next() +{ + global $jsm_c1, $jsm_c2; + + // Get the next B + $jsm_c2=jsm_get2(); + + // Special case of recognising regular expressions (beginning with /) that are + // preceded by '(', ',' or '=' + $tmpA=$jsm_c1; + + if($jsm_c2==='/' && ($tmpA==='(' || $tmpA===',' || $tmpA==='=')) + { + // Output the two successive chars + jsm_put($tmpA); + jsm_put($jsm_c2); + + // Look for the end of the RE literal, watching out for escaped chars or a control / + // end of line char (the RE literal then being unterminated !) + while (true) + { + $tmpA=$jsm_c1=jsm_get(); + + if($tmpA==='/') + break; // from while(true) + + if($tmpA==='\\') + { + // Escape next char immediately + jsm_put($tmpA); + $tmpA=$jsm_c1=jsm_get(); } - } - - // No risk of a comment - - return $c; - } - - /** - * Do something ! - * - * The action to perform is determined by the argument : - * - * JSMin::ACT_FULL : Output A. Copy B to A. Get the next B. - * JSMin::ACT_BUF : Copy B to A. Get the next B. (Delete A). - * JSMin::ACT_IMM : Get the next B. (Delete B). - * - * A string is treated as a single character. Also, regular expressions are recognized if preceded - * by '(', ',' or '='. - * - * @param int $action The action to perform : one of the JSMin::ACT_* constants. - */ - function action($action) { - - // Choice of possible actions - // Note the frequent fallthroughs : the actions are decrementally "long" - - switch ($action) { - - case JSMIN_ACT_FULL : - - // Write A to output, then fall through - - $this->put($this->theA); - - case JSMIN_ACT_BUF : // N.B. possible fallthrough from above - - // Copy B to A - - $tmpA = $this->theA = $this->theB; - - // Treating a string as a single char : outputting it whole - // Note that the string-opening char (" or ') is memorized in B - - if ($tmpA == '\'' || $tmpA == '"') { - - while (true) { - - // Output string contents - - $this->put($tmpA); - - // Get next character, watching out for termination of the current string, - // new line & co (then the string is not terminated !), or a backslash - // (upon which the following char is directly output to serve the escape mechanism) - - $tmpA = $this->theA = $this->get(); - - if ($tmpA == $this->theB) { + else if(ord($tmpA) <= ord("\n")) + die('Unterminated regexp literal'); - // String terminated - - break; // from while(true) - } - - // else - - if (ord($tmpA) <= ORD_NL) { - - // Whoopsie - - throw new UnterminatedStringLiteralJSMinException(); - } - - // else - - if ($tmpA == '\\') { - - // Escape next char immediately - - $this->put($tmpA); - $tmpA = $this->theA = $this->get(); - } - } - } - - case JSMIN_ACT_IMM : // N.B. possible fallthrough from above - - // Get the next B - - $this->theB = $this->next(); - - // Special case of recognising regular expressions (beginning with /) that are - // preceded by '(', ',' or '=' - - $tmpA = $this->theA; - - if ($this->theB == '/' && ($tmpA == '(' || $tmpA == ',' || $tmpA == '=')) { - - // Output the two successive chars - - $this->put($tmpA); - $this->put($this->theB); - - // Look for the end of the RE literal, watching out for escaped chars or a control / - // end of line char (the RE literal then being unterminated !) - - while (true) { - - $tmpA = $this->theA = $this->get(); - - if ($tmpA == '/') { - - // RE literal terminated - - break; // from while(true) - } - - // else - - if ($tmpA == '\\') { - - // Escape next char immediately - - $this->put($tmpA); - $tmpA = $this->theA = $this->get(); - } - else if (ord($tmpA) <= ORD_NL) { - - // Whoopsie - - throw new UnterminatedRegExpLiteralJSMinException(); - } - - // Output RE characters - - $this->put($tmpA); - } - - // Move forward after the RE literal - - $this->theB = $this->next(); - } - - break; - default : - throw new JSMinException('Expected a JSMin::ACT_* constant in action().'); + // Output RE characters + jsm_put($tmpA); } - } - - /** - * Run the JSMin application : minify some JS code. - * - * The code is read from the input stream, and its minified version is written to the output one. - * In case input is a string, minified vesrions is also returned by this function as string. - * That is : characters which are insignificant to JavaScript are removed, as well as comments ; - * tabs are replaced with spaces ; carriage returns are replaced with linefeeds, and finally most - * spaces and linefeeds are deleted. - * - * Note : name was changed from jsmin() because PHP identifiers are case-insensitive, and it is already - * the name of this class. - * - * @see JSMin() - * @return null | string - */ - function minify() { - - // Initialize A and run the first (minimal) action - - $this->theA = "\n"; - $this->action(JSMIN_ACT_IMM); - - // Proceed all the way to the end of the input file - - while ($this->theA !== EOF) { - - switch ($this->theA) { - - case ' ' : - - if (JSMin::isAlphaNum($this->theB)) { - $this->action(JSMIN_ACT_FULL); - } - else { - $this->action(JSMIN_ACT_BUF); - } - - break; - case "\n" : - - switch ($this->theB) { - - case '{' : case '[' : case '(' : - case '+' : case '-' : - - $this->action(JSMIN_ACT_FULL); - - break; - case ' ' : - - $this->action(JSMIN_ACT_IMM); - - break; - default : - - if (JSMin::isAlphaNum($this->theB)) { - $this->action(JSMIN_ACT_FULL); - } - else { - $this->action(JSMIN_ACT_BUF); - } - - break; - } - - break; - default : - - switch ($this->theB) { - - case ' ' : - - if (JSMin::isAlphaNum($this->theA)) { - - $this->action(JSMIN_ACT_FULL); - break; - } - - // else - $this->action(JSMIN_ACT_IMM); - - break; - case "\n" : - - switch ($this->theA) { - - case '}' : case ']' : case ')' : case '+' : - case '-' : case '"' : case '\'' : - - $this->action(JSMIN_ACT_FULL); + // Move forward after the RE literal + $jsm_c2=jsm_get2(); + } +} - break; - default : +function jsm_get() +{ + global $jsm_pos, $jsm_length, $jsm_in; - if (JSMin::isAlphaNum($this->theA)) { - $this->action(JSMIN_ACT_FULL); - } - else { - $this->action(JSMIN_ACT_IMM); - } + // Get next input character and advance position in file + if($jsm_pos < $jsm_length) + { + $c=$jsm_in[$jsm_pos++]; + if($c==="\n" || ord($c) >= ord(' ')) + return $c; + else if($c==="\r") + return "\n"; + else + return ' '; + } + else + return false; - break; - } +} - break; - default : +function jsm_peek() +{ + global $jsm_pos, $jsm_length, $jsm_in; + if($jsm_pos < $jsm_length) + return $jsm_in[$jsm_pos]; + else + return false; +} - $this->action(JSMIN_ACT_FULL); +function jsm_put($c) +{ + global $jsm_out; + $jsm_out .= $c; +} - break; - } +/** + * Get the next character from the input stream, excluding comments. + */ +function jsm_get2() +{ + // Get next char from input, translated if necessary + if(($c=jsm_get())!=='/') + return $c; - break; - } + // Look ahead : a comment is two slashes or slashes followed by asterisk (to be closed) + if(($c=jsm_peek())==='/') + { + // Comment is up to the end of the line + while (true) + { + $c=jsm_get(); + if(ord($c)<=ord("\n")) + return $c; } - - if ($this->isString) { - return $this->out; - - } - } - - /** - * Prepare a new JSMin application. - * - * The next step is to {@link minify()} the input into the output. - * - * @param string $inFileName The pathname of the input (unminified JS) file. STDIN if '-' or absent. - * @param string $outFileName The pathname of the output (minified JS) file. STDOUT if '-' or absent. - * If outFileName === FALSE, we assume that inFileName is in fact the string to be minified!!! - * @param array $comments Optional lines to present as comments at the beginning of the output. - */ - function JSMin($inFileName = '-', $outFileName = '-', $comments = NULL) { - if ($outFileName === FALSE) { - $this->JSMin_String($inFileName, $comments); - } - else { - $this->JSMin_File($inFileName, $outFileName, $comments); - } } - - function JSMin_File($inFileName = '-', $outFileName = '-', $comments = NULL) { - - // Recuperate input and output streams. - // Use STDIN and STDOUT by default, if they are defined (CLI mode) and no file names are provided - - if ($inFileName == '-') $inFileName = 'php://stdin'; - if ($outFileName == '-') $outFileName = 'php://stdout'; - - try { - - $this->in = new SplFileObject($inFileName, 'rb', TRUE); - } - catch (Exception $e) { - - throw new FileOpenFailedJSMinException( - 'Failed to open "'.$inFileName.'" for reading only.' - ); - } - - try { - - $this->out = new SplFileObject($outFileName, 'wb', TRUE); - } - catch (Exception $e) { - - throw new FileOpenFailedJSMinException( - 'Failed to open "'.$outFileName.'" for writing only.' - ); - } - - /*$this->in = fopen($inFileName, 'rb'); - if (!$this->in) { - trigger_error('Failed to open "'.$inFileName, E_USER_ERROR); - } - $this->out = fopen($outFileName, 'wb'); - if (!$this->out) { - trigger_error('Failed to open "'.$outFileName, E_USER_ERROR); - }*/ - - // Present possible initial comments - - if ($comments !== NULL) { - foreach ($comments as $comm) { - $this->out->fwrite('// '.str_replace("\n", " ", $comm)."\n"); + else if($c==='*') + { + // Comment is up to comment close. + // Might not be terminated, if we hit the end of file. + while(true) + { + // N.B. not using switch() because of having to test EOF with === + $c=jsm_get(); + if($c==='*') + { + // Comment termination if the char ahead is a slash + if(jsm_peek()==='/') + { + // Advance again and make into a single space + jsm_get(); + return ' '; } } - - } - - function JSMin_String($inString, $comments = NULL) { - $this->in = $inString; - $this->out = ''; - $this->inLength = strlen($inString); - $this->inPos = 0; - $this->isString = true; - - if ($comments !== NULL) { - foreach ($comments as $comm) { - $this->out .= '// '.str_replace("\n", " ", $comm)."\n"; - } + else if($c===false) + die('Unterminated comment'); } } + else + return '/'; +} + +/** + * Indicates whether a character is alphanumeric or _, $, \ or non-ASCII. + */ +function jsm_isAlphaNum($c) +{ + return ctype_alnum($c) || $c==='_' || $c==='$' || $c==='\\' || ord($c)>126; } /************** OUTPUT *****************/ -- cgit v1.2.3