From c6117149e842b0d67059d4a239a07db53efbb85a Mon Sep 17 00:00:00 2001 From: Julio Cesar Laura Date: Mon, 17 Sep 2012 10:54:39 -0400 Subject: [PATCH] BUG 6213 Improve bandwith usage (minimize js and css files) SOLVED - The minimization wasn't applied to some js files because the library used gives some errors - Upgrade the jsmin class and minify the remaining js files --- gulliver/system/class.g.php | 8 +- gulliver/thirdparty/jsmin/jsmin.php | 152 ++++++++++++++++++++++------ 2 files changed, 127 insertions(+), 33 deletions(-) diff --git a/gulliver/system/class.g.php b/gulliver/system/class.g.php index 0edb3d581..94ea227aa 100755 --- a/gulliver/system/class.g.php +++ b/gulliver/system/class.g.php @@ -1442,8 +1442,8 @@ $output = $outputHeader.$output; case 'maborak.js' : $oHeadPublisher =& headPublisher::getSingleton(); foreach ( $oHeadPublisher->maborakFiles as $fileJS ) { - //$output .= JSMin::minify ( file_get_contents ( $fileJS ) ); - $output .= G::trimSourceCodeFile ($fileJS ); + $output .= JSMin::minify ( file_get_contents ( $fileJS ) ); + //$output .= G::trimSourceCodeFile ($fileJS ); } break; case 'maborak.loader.js': @@ -1454,8 +1454,8 @@ $output = $outputHeader.$output; } break; default : - $output = file_get_contents ( $filename ) ; - //$output = JSMin::minify ( file_get_contents ( $filename ) ); + //$output = file_get_contents ( $filename ) ; + $output = JSMin::minify ( file_get_contents ( $filename ) ); //$output = G::trimSourceCodeFile ($filename ); } print $output; diff --git a/gulliver/thirdparty/jsmin/jsmin.php b/gulliver/thirdparty/jsmin/jsmin.php index 2e01ce467..5e4e9b81f 100755 --- a/gulliver/thirdparty/jsmin/jsmin.php +++ b/gulliver/thirdparty/jsmin/jsmin.php @@ -1,6 +1,6 @@ * @copyright 2002 Douglas Crockford (jsmin.c) * @copyright 2008 Ryan Grove (PHP port) + * @copyright 2012 Adam Goforth (Updates) * @license http://opensource.org/licenses/mit-license.php MIT License - * @version 1.1.1 (2008-03-02) - * @link http://code.google.com/p/jsmin-php/ + * @version 1.1.2 (2012-05-01) + * @link https://github.com/rgrove/jsmin-php */ class JSMin { - const ORD_LF = 10; - const ORD_SPACE = 32; + const ORD_LF = 10; + const ORD_SPACE = 32; + const ACTION_KEEP_A = 1; + const ACTION_DELETE_A = 2; + const ACTION_DELETE_A_B = 3; protected $a = ''; protected $b = ''; @@ -59,6 +63,14 @@ class JSMin { // -- Public Static Methods -------------------------------------------------- + /** + * Minify Javascript + * + * @uses __construct() + * @uses min() + * @param string $js Javascript to be minified + * @return string + */ public static function minify($js) { $jsmin = new JSMin($js); return $jsmin->min(); @@ -66,20 +78,41 @@ class JSMin { // -- Public Instance Methods ------------------------------------------------ + /** + * Constructor + * + * @param string $input Javascript to be minified + */ public function __construct($input) { $this->input = str_replace("\r\n", "\n", $input); -// $this->input = str_replace("\n", "", $this->input); $this->inputLength = strlen($this->input); } // -- Protected Instance Methods --------------------------------------------- - protected function action($d) { - switch($d) { - case 1: + /** + * Action -- do something! What to do is determined by the $command argument. + * + * action treats a string as a single character. Wow! + * action recognizes a regular expression if it is preceded by ( or , or =. + * + * @uses next() + * @uses get() + * @throws JSMinException If parser errors are found: + * - Unterminated string literal + * - Unterminated regular expression set in regex literal + * - Unterminated regular expression literal + * @param int $command One of class constants: + * ACTION_KEEP_A Output A. Copy B to A. Get the next B. + * ACTION_DELETE_A Copy B to A. Get the next B. (Delete A). + * ACTION_DELETE_A_B Get the next B. (Delete B). + */ + protected function action($command) { + switch($command) { + case self::ACTION_KEEP_A: $this->output .= $this->a; - case 2: + case self::ACTION_DELETE_A: $this->a = $this->b; if ($this->a === "'" || $this->a === '"') { @@ -102,27 +135,46 @@ class JSMin { } } - case 3: + case self::ACTION_DELETE_A_B: $this->b = $this->next(); if ($this->b === '/' && ( $this->a === '(' || $this->a === ',' || $this->a === '=' || $this->a === ':' || $this->a === '[' || $this->a === '!' || - $this->a === '&' || $this->a === '|' || $this->a === '?')) { + $this->a === '&' || $this->a === '|' || $this->a === '?' || + $this->a === '{' || $this->a === '}' || $this->a === ';' || + $this->a === "\n" )) { $this->output .= $this->a . $this->b; for (;;) { $this->a = $this->get(); - if ($this->a === '/') { + if ($this->a === '[') { + /* + inside a regex [...] set, which MAY contain a '/' itself. Example: mootools Form.Validator near line 460: + return Form.Validator.getValidator('IsEmpty').test(element) || (/^(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]\.?){0,63}[a-z0-9!#$%&'*+/=?^_`{|}~-]@(?:(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\])$/i).test(element.get('value')); + */ + for (;;) { + $this->output .= $this->a; + $this->a = $this->get(); + + if ($this->a === ']') { + break; + } elseif ($this->a === '\\') { + $this->output .= $this->a; + $this->a = $this->get(); + } elseif (ord($this->a) <= self::ORD_LF) { + throw new JSMinException('Unterminated regular expression set in regex literal.'); + } + } + } elseif ($this->a === '/') { break; } elseif ($this->a === '\\') { $this->output .= $this->a; $this->a = $this->get(); } elseif (ord($this->a) <= self::ORD_LF) { - throw new JSMinException('Unterminated regular expression '. - 'literal.'); + throw new JSMinException('Unterminated regular expression literal.'); } $this->output .= $this->a; @@ -133,13 +185,18 @@ class JSMin { } } + /** + * Get next char. Convert ctrl char to space. + * + * @return string|null + */ protected function get() { $c = $this->lookAhead; $this->lookAhead = null; if ($c === null) { if ($this->inputIndex < $this->inputLength) { - $c = $this->input[$this->inputIndex]; + $c = substr($this->input, $this->inputIndex, 1); $this->inputIndex += 1; } else { $c = null; @@ -157,21 +214,41 @@ class JSMin { return ' '; } + /** + * Is $c a letter, digit, underscore, dollar sign, or non-ASCII character. + * + * @return bool + */ protected function isAlphaNum($c) { return ord($c) > 126 || $c === '\\' || preg_match('/^[\w\$]$/', $c) === 1; } + /** + * Perform minification, return result + * + * @uses action() + * @uses isAlphaNum() + * @uses get() + * @uses peek() + * @return string + */ protected function min() { + if (0 == strncmp($this->peek(), "\xef", 1)) { + $this->get(); + $this->get(); + $this->get(); + } + $this->a = "\n"; - $this->action(3); + $this->action(self::ACTION_DELETE_A_B); while ($this->a !== null) { switch ($this->a) { case ' ': if ($this->isAlphaNum($this->b)) { - $this->action(1); + $this->action(self::ACTION_KEEP_A); } else { - $this->action(2); + $this->action(self::ACTION_DELETE_A); } break; @@ -182,19 +259,21 @@ class JSMin { case '(': case '+': case '-': - $this->action(1); + case '!': + case '~': + $this->action(self::ACTION_KEEP_A); break; case ' ': - $this->action(3); + $this->action(self::ACTION_DELETE_A_B); break; default: if ($this->isAlphaNum($this->b)) { - $this->action(1); + $this->action(self::ACTION_KEEP_A); } else { - $this->action(2); + $this->action(self::ACTION_DELETE_A); } } break; @@ -203,11 +282,11 @@ class JSMin { switch ($this->b) { case ' ': if ($this->isAlphaNum($this->a)) { - $this->action(1); + $this->action(self::ACTION_KEEP_A); break; } - $this->action(3); + $this->action(self::ACTION_DELETE_A_B); break; case "\n": @@ -219,21 +298,21 @@ class JSMin { case '-': case '"': case "'": - $this->action(1); + $this->action(self::ACTION_KEEP_A); break; default: if ($this->isAlphaNum($this->a)) { - $this->action(1); + $this->action(self::ACTION_KEEP_A); } else { - $this->action(3); + $this->action(self::ACTION_DELETE_A_B); } } break; default: - $this->action(1); + $this->action(self::ACTION_KEEP_A); break; } } @@ -242,6 +321,15 @@ class JSMin { return $this->output; } + /** + * Get the next character, skipping over comments. peek() is used to see + * if a '/' is followed by a '/' or '*'. + * + * @uses get() + * @uses peek() + * @throws JSMinException On unterminated comment. + * @return string + */ protected function next() { $c = $this->get(); @@ -281,6 +369,12 @@ class JSMin { return $c; } + /** + * Get next char. If is ctrl character, translate to a space or newline. + * + * @uses get() + * @return string|null + */ protected function peek() { $this->lookAhead = $this->get(); return $this->lookAhead;