Moodle  2.2.1
http://www.collinsharper.com
C:/xampp/htdocs/moodle/lib/minify/lib/JSMin.php
Go to the documentation of this file.
00001 <?php
00053 class JSMin {
00054     const ORD_LF            = 10;
00055     const ORD_SPACE         = 32;
00056     const ACTION_KEEP_A     = 1;
00057     const ACTION_DELETE_A   = 2;
00058     const ACTION_DELETE_A_B = 3;
00059     
00060     protected $a           = "\n";
00061     protected $b           = '';
00062     protected $input       = '';
00063     protected $inputIndex  = 0;
00064     protected $inputLength = 0;
00065     protected $lookAhead   = null;
00066     protected $output      = '';
00067     
00074     public static function minify($js)
00075     {
00076         $jsmin = new JSMin($js);
00077         return $jsmin->min();
00078     }
00079     
00083     public function __construct($input)
00084     {
00085         $this->input       = str_replace("\r\n", "\n", $input);
00086         $this->inputLength = strlen($this->input);
00087     }
00088     
00092     public function min()
00093     {
00094         if ($this->output !== '') { // min already run
00095             return $this->output;
00096         }
00097         $this->action(self::ACTION_DELETE_A_B);
00098         
00099         while ($this->a !== null) {
00100             // determine next command
00101             $command = self::ACTION_KEEP_A; // default
00102             if ($this->a === ' ') {
00103                 if (! $this->isAlphaNum($this->b)) {
00104                     $command = self::ACTION_DELETE_A;
00105                 }
00106             } elseif ($this->a === "\n") {
00107                 if ($this->b === ' ') {
00108                     $command = self::ACTION_DELETE_A_B;
00109                 } elseif (false === strpos('{[(+-', $this->b) 
00110                           && ! $this->isAlphaNum($this->b)) {
00111                     $command = self::ACTION_DELETE_A;
00112                 }
00113             } elseif (! $this->isAlphaNum($this->a)) {
00114                 if ($this->b === ' '
00115                     || ($this->b === "\n" 
00116                         && (false === strpos('}])+-"\'', $this->a)))) {
00117                     $command = self::ACTION_DELETE_A_B;
00118                 }
00119             }
00120             $this->action($command);
00121         }
00122         $this->output = trim($this->output);
00123         return $this->output;
00124     }
00125     
00131     protected function action($command)
00132     {
00133         switch ($command) {
00134             case self::ACTION_KEEP_A:
00135                 $this->output .= $this->a;
00136                 // fallthrough
00137             case self::ACTION_DELETE_A:
00138                 $this->a = $this->b;
00139                 if ($this->a === "'" || $this->a === '"') { // string literal
00140                     $str = $this->a; // in case needed for exception
00141                     while (true) {
00142                         $this->output .= $this->a;
00143                         $this->a       = $this->get();
00144                         if ($this->a === $this->b) { // end quote
00145                             break;
00146                         }
00147                         if (ord($this->a) <= self::ORD_LF) {
00148                             throw new JSMin_UnterminatedStringException(
00149                                 'Unterminated String: ' . var_export($str, true));
00150                         }
00151                         $str .= $this->a;
00152                         if ($this->a === '\\') {
00153                             $this->output .= $this->a;
00154                             $this->a       = $this->get();
00155                             $str .= $this->a;
00156                         }
00157                     }
00158                 }
00159                 // fallthrough
00160             case self::ACTION_DELETE_A_B:
00161                 $this->b = $this->next();
00162                 if ($this->b === '/' && $this->isRegexpLiteral()) { // RegExp literal
00163                     $this->output .= $this->a . $this->b;
00164                     $pattern = '/'; // in case needed for exception
00165                     while (true) {
00166                         $this->a = $this->get();
00167                         $pattern .= $this->a;
00168                         if ($this->a === '/') { // end pattern
00169                             break; // while (true)
00170                         } elseif ($this->a === '\\') {
00171                             $this->output .= $this->a;
00172                             $this->a       = $this->get();
00173                             $pattern      .= $this->a;
00174                         } elseif (ord($this->a) <= self::ORD_LF) {
00175                             throw new JSMin_UnterminatedRegExpException(
00176                                 'Unterminated RegExp: '. var_export($pattern, true));
00177                         }
00178                         $this->output .= $this->a;
00179                     }
00180                     $this->b = $this->next();
00181                 }
00182             // end case ACTION_DELETE_A_B
00183         }
00184     }
00185     
00186     protected function isRegexpLiteral()
00187     {
00188         if (false !== strpos("\n{;(,=:[!&|?", $this->a)) { // we aren't dividing
00189             return true;
00190         }
00191         if (' ' === $this->a) {
00192             $length = strlen($this->output);
00193             if ($length < 2) { // weird edge case
00194                 return true;
00195             }
00196             // you can't divide a keyword
00197             if (preg_match('/(?:case|else|in|return|typeof)$/', $this->output, $m)) {
00198                 if ($this->output === $m[0]) { // odd but could happen
00199                     return true;
00200                 }
00201                 // make sure it's a keyword, not end of an identifier
00202                 $charBeforeKeyword = substr($this->output, $length - strlen($m[0]) - 1, 1);
00203                 if (! $this->isAlphaNum($charBeforeKeyword)) {
00204                     return true;
00205                 }
00206             }
00207         }
00208         return false;
00209     }
00210     
00214     protected function get()
00215     {
00216         $c = $this->lookAhead;
00217         $this->lookAhead = null;
00218         if ($c === null) {
00219             if ($this->inputIndex < $this->inputLength) {
00220                 $c = $this->input[$this->inputIndex];
00221                 $this->inputIndex += 1;
00222             } else {
00223                 return null;
00224             }
00225         }
00226         if ($c === "\r" || $c === "\n") {
00227             return "\n";
00228         }
00229         if (ord($c) < self::ORD_SPACE) { // control char
00230             return ' ';
00231         }
00232         return $c;
00233     }
00234     
00238     protected function peek()
00239     {
00240         $this->lookAhead = $this->get();
00241         return $this->lookAhead;
00242     }
00243     
00247     protected function isAlphaNum($c)
00248     {
00249         return (preg_match('/^[0-9a-zA-Z_\\$\\\\]$/', $c) || ord($c) > 126);
00250     }
00251     
00252     protected function singleLineComment()
00253     {
00254         $comment = '';
00255         while (true) {
00256             $get = $this->get();
00257             $comment .= $get;
00258             if (ord($get) <= self::ORD_LF) { // EOL reached
00259                 // if IE conditional comment
00260                 if (preg_match('/^\\/@(?:cc_on|if|elif|else|end)\\b/', $comment)) {
00261                     return "/{$comment}";
00262                 }
00263                 return $get;
00264             }
00265         }
00266     }
00267     
00268     protected function multipleLineComment()
00269     {
00270         $this->get();
00271         $comment = '';
00272         while (true) {
00273             $get = $this->get();
00274             if ($get === '*') {
00275                 if ($this->peek() === '/') { // end of comment reached
00276                     $this->get();
00277                     // if comment preserved by YUI Compressor
00278                     if (0 === strpos($comment, '!')) {
00279                         return "\n/*" . substr($comment, 1) . "*/\n";
00280                     }
00281                     // if IE conditional comment
00282                     if (preg_match('/^@(?:cc_on|if|elif|else|end)\\b/', $comment)) {
00283                         return "/*{$comment}*/";
00284                     }
00285                     return ' ';
00286                 }
00287             } elseif ($get === null) {
00288                 throw new JSMin_UnterminatedCommentException('Unterminated Comment: ' . var_export('/*' . $comment, true));
00289             }
00290             $comment .= $get;
00291         }
00292     }
00293     
00298     protected function next()
00299     {
00300         $get = $this->get();
00301         if ($get !== '/') {
00302             return $get;
00303         }
00304         switch ($this->peek()) {
00305             case '/': return $this->singleLineComment();
00306             case '*': return $this->multipleLineComment();
00307             default: return $get;
00308         }
00309     }
00310 }
00311 
00312 class JSMin_UnterminatedStringException extends Exception {}
00313 class JSMin_UnterminatedCommentException extends Exception {}
00314 class JSMin_UnterminatedRegExpException extends Exception {}
 All Data Structures Namespaces Files Functions Variables Enumerations