• Main Page
  • Related Pages
  • Modules
  • Namespaces
  • Data Structures
  • Files
  • File List
  • Globals

Modules/Scorm2004/classes/JSMin_lib.php

Go to the documentation of this file.
00001 <?php
00085 define('JSMIN_VERSION', '0.2');
00086 
00092 define('EOF', FALSE);
00093 
00099 define('ORD_NL', ord("\n"));
00100 define('ORD_space', ord(' '));
00101 define('ORD_cA', ord('A'));
00102 define('ORD_cZ', ord('Z'));
00103 define('ORD_a', ord('a'));
00104 define('ORD_z', ord('z'));
00105 define('ORD_0', ord('0'));
00106 define('ORD_9', ord('9'));
00107 
00111 /*
00112 class JSMinException extends Exception {
00113 }
00114 */
00115 class JSMinException {
00116 }
00117 
00122 class FileOpenFailedJSMinException extends JSMinException {
00123 }
00124 
00129 class UnterminatedCommentJSMinException extends JSMinException {
00130 }
00131 
00136 class UnterminatedStringLiteralJSMinException extends JSMinException {
00137 }
00138 
00143 class UnterminatedRegExpLiteralJSMinException extends JSMinException {
00144 }
00145 
00150 define ('JSMIN_ACT_FULL', 1);
00151 
00156 define ('JSMIN_ACT_BUF', 2);
00157 
00162 define ('JSMIN_ACT_IMM', 3);
00163 
00175 class JSMin {
00176 
00182     var $in;
00183 
00189     var $out;
00190 
00195     var $theA;
00196 
00201     var $theB;
00202 
00204         var $inLength = 0;
00205         var $inPos = 0;
00206         var $isString = false;
00207 
00214     function isAlphaNum($c) {
00215 
00216         // Get ASCII value of character for C-like comparisons
00217 
00218         $a = ord($c);
00219 
00220         // Compare using defined character ordinals, or between PHP strings
00221         // Note : === is micro-faster than == when types are known to be the same
00222 
00223         return
00224             ($a >= ORD_a && $a <= ORD_z) ||
00225             ($a >= ORD_0 && $a <= ORD_9) ||
00226             ($a >= ORD_cA && $a <= ORD_cZ) ||
00227             $c === '_' || $c === '$' || $c === '\\' || $a > 126
00228         ;
00229     }
00230 
00240     function get() {
00241 
00242         // Get next input character and advance position in file
00243 
00244                 if ($this->isString) {
00245                         if ($this->inPos < $this->inLength) {
00246                                 $c = $this->in[$this->inPos];
00247                                 ++$this->inPos;
00248                         }
00249                         else {
00250                                 return EOF;
00251                         }
00252                 }
00253                 else
00254                 $c = $this->in->fgetc();
00255 
00256         // Test for non-problematic characters
00257 
00258         if ($c === "\n" || $c === EOF || ord($c) >= ORD_space) {
00259             return $c;
00260         }
00261 
00262         // else
00263         // Make linefeeds into newlines
00264 
00265         if ($c === "\r") {
00266             return "\n";
00267         }
00268 
00269         // else
00270         // Consider space
00271 
00272         return ' ';
00273     }
00274 
00283     function peek() {
00284 
00285                 if ($this->isString) {
00286                         if ($this->inPos < $this->inLength) {
00287                                 $c = $this->in[$this->inPos];
00288                         }
00289                         else {
00290                                 return EOF;
00291                         }
00292                 }
00293                 else {
00294                 // Get next input character
00295 
00296                 $c = $this->in->fgetc();
00297 
00298                 // Regress position in file
00299 
00300                 $this->in->fseek(-1, SEEK_CUR);
00301 
00302                 // Return character obtained
00303             }
00304 
00305         return $c;
00306     }
00307 
00312         function put($c)
00313         {
00314                 if ($this->isString) {
00315                         $this->out .= $c;
00316                 }
00317                 else {
00318                         $this->out->fwrite($c);
00319                 }
00320         }
00321 
00331     function next() {
00332 
00333         // Get next char from input, translated if necessary
00334 
00335         $c = $this->get();
00336 
00337         // Check comment possibility
00338 
00339         if ($c == '/') {
00340 
00341             // Look ahead : a comment is two slashes or slashes followed by asterisk (to be closed)
00342 
00343             switch ($this->peek()) {
00344 
00345                 case '/' :
00346 
00347                     // Comment is up to the end of the line
00348                     // TOTEST : simple $this->in->fgets()
00349 
00350                     while (true) {
00351 
00352                         $c = $this->get();
00353 
00354                         if (ord($c) <= ORD_NL) {
00355                             return $c;
00356                         }
00357                     }
00358 
00359                 case '*' :
00360 
00361                     // Comment is up to comment close.
00362                     // Might not be terminated, if we hit the end of file.
00363 
00364                     while (true) {
00365 
00366                         // N.B. not using switch() because of having to test EOF with ===
00367 
00368                         $c = $this->get();
00369 
00370                         if ($c == '*') {
00371 
00372                             // Comment termination if the char ahead is a slash
00373 
00374                             if ($this->peek() == '/') {
00375 
00376                                 // Advance again and make into a single space
00377 
00378                                 $this->get();
00379                                 return ' ';
00380                             }
00381                         }
00382                         else if ($c === EOF) {
00383 
00384                             // Whoopsie
00385 
00386                             //throw new UnterminatedCommentJSMinException();
00387                             trigger_error('UnterminatedComment', E_USER_ERROR);
00388                         }
00389                     }
00390 
00391                 default :
00392 
00393                     // Not a comment after all
00394 
00395                     return $c;
00396             }
00397         }
00398 
00399         // No risk of a comment
00400 
00401         return $c;
00402     }
00403 
00418     function action($action) {
00419 
00420         // Choice of possible actions
00421         // Note the frequent fallthroughs : the actions are decrementally "long"
00422 
00423         switch ($action) {
00424 
00425             case JSMIN_ACT_FULL :
00426 
00427                 // Write A to output, then fall through
00428 
00429                 $this->put($this->theA);
00430 
00431             case JSMIN_ACT_BUF : // N.B. possible fallthrough from above
00432 
00433                 // Copy B to A
00434 
00435                 $tmpA = $this->theA = $this->theB;
00436 
00437                 // Treating a string as a single char : outputting it whole
00438                 // Note that the string-opening char (" or ') is memorized in B
00439 
00440                 if ($tmpA == '\'' || $tmpA == '"') {
00441 
00442                     while (true) {
00443 
00444                         // Output string contents
00445 
00446                         $this->put($tmpA);
00447 
00448                         // Get next character, watching out for termination of the current string,
00449                         // new line & co (then the string is not terminated !), or a backslash
00450                         // (upon which the following char is directly output to serve the escape mechanism)
00451 
00452                         $tmpA = $this->theA = $this->get();
00453 
00454                         if ($tmpA == $this->theB) {
00455 
00456                             // String terminated
00457 
00458                             break; // from while(true)
00459                         }
00460 
00461                         // else
00462 
00463                         if (ord($tmpA) <= ORD_NL) {
00464 
00465                             // Whoopsie
00466 
00467                             //throw new UnterminatedStringLiteralJSMinException();
00468                             trigger_error('UnterminatedStringLiteral', E_USER_ERROR);
00469                         }
00470 
00471                         // else
00472 
00473                         if ($tmpA == '\\') {
00474 
00475                             // Escape next char immediately
00476 
00477                             $this->put($tmpA);
00478                             $tmpA = $this->theA = $this->get();
00479                         }
00480                     }
00481                 }
00482 
00483             case JSMIN_ACT_IMM : // N.B. possible fallthrough from above
00484 
00485                 // Get the next B
00486 
00487                 $this->theB = $this->next();
00488 
00489                 // Special case of recognising regular expressions (beginning with /) that are
00490                 // preceded by '(', ',' or '='
00491 
00492                 $tmpA = $this->theA;
00493 
00494                 if ($this->theB == '/' && ($tmpA == '(' || $tmpA == ',' || $tmpA == '=')) {
00495 
00496                     // Output the two successive chars
00497 
00498                     $this->put($tmpA);
00499                     $this->put($this->theB);
00500 
00501                     // Look for the end of the RE literal, watching out for escaped chars or a control /
00502                     // end of line char (the RE literal then being unterminated !)
00503 
00504                     while (true) {
00505 
00506                         $tmpA = $this->theA = $this->get();
00507 
00508                         if ($tmpA == '/') {
00509 
00510                             // RE literal terminated
00511 
00512                             break; // from while(true)
00513                         }
00514 
00515                         // else
00516 
00517                         if ($tmpA == '\\') {
00518 
00519                             // Escape next char immediately
00520 
00521                             $this->put($tmpA);
00522                             $tmpA = $this->theA = $this->get();
00523                         }
00524                         else if (ord($tmpA) <= ORD_NL) {
00525 
00526                             // Whoopsie
00527 
00528                             //throw new UnterminatedRegExpLiteralJSMinException();
00529                             trigger_error('UnterminatedRegExpLiteral', E_USER_ERROR);
00530                         }
00531 
00532                         // Output RE characters
00533 
00534                         $this->put($tmpA);
00535                     }
00536 
00537                     // Move forward after the RE literal
00538 
00539                     $this->theB = $this->next();
00540                 }
00541 
00542             break;
00543             default :
00544                                 //throw new JSMinException('Expected a JSMin::ACT_* constant in action().');
00545                                 trigger_error('Expected a JSMin::ACT_* constant in action()', E_USER_ERROR);
00546         }
00547     }
00548 
00564     function minify() {
00565 
00566         // Initialize A and run the first (minimal) action
00567 
00568         $this->theA = "\n";
00569         $this->action(JSMIN_ACT_IMM);
00570 
00571         // Proceed all the way to the end of the input file
00572 
00573         while ($this->theA !== EOF) {
00574 
00575             switch ($this->theA) {
00576 
00577                 case ' ' :
00578 
00579                     if (JSMin::isAlphaNum($this->theB)) {
00580                         $this->action(JSMIN_ACT_FULL);
00581                     }
00582                     else {
00583                         $this->action(JSMIN_ACT_BUF);
00584                     }
00585 
00586                 break;
00587                 case "\n" :
00588 
00589                     switch ($this->theB) {
00590 
00591                         case '{' : case '[' : case '(' :
00592                         case '+' : case '-' :
00593 
00594                             $this->action(JSMIN_ACT_FULL);
00595 
00596                         break;
00597                         case ' ' :
00598 
00599                             $this->action(JSMIN_ACT_IMM);
00600 
00601                         break;
00602                         default :
00603 
00604                             if (JSMin::isAlphaNum($this->theB)) {
00605                                 $this->action(JSMIN_ACT_FULL);
00606                             }
00607                             else {
00608                                 $this->action(JSMIN_ACT_BUF);
00609                             }
00610 
00611                         break;
00612                     }
00613 
00614                 break;
00615                 default :
00616 
00617                     switch ($this->theB) {
00618 
00619                         case ' ' :
00620 
00621                             if (JSMin::isAlphaNum($this->theA)) {
00622 
00623                                 $this->action(JSMIN_ACT_FULL);
00624                                 break;
00625                             }
00626 
00627                             // else
00628 
00629                             $this->action(JSMIN_ACT_IMM);
00630 
00631                         break;
00632                         case "\n" :
00633 
00634                             switch ($this->theA) {
00635 
00636                                 case '}' : case ']' : case ')' : case '+' :
00637                                 case '-' : case '"' : case '\'' :
00638 
00639                                     $this->action(JSMIN_ACT_FULL);
00640 
00641                                 break;
00642                                 default :
00643 
00644                                     if (JSMin::isAlphaNum($this->theA)) {
00645                                         $this->action(JSMIN_ACT_FULL);
00646                                     }
00647                                     else {
00648                                         $this->action(JSMIN_ACT_IMM);
00649                                     }
00650 
00651                                 break;
00652                             }
00653 
00654                         break;
00655                         default :
00656 
00657                             $this->action(JSMIN_ACT_FULL);
00658 
00659                         break;
00660                     }
00661 
00662                 break;
00663             }
00664         }
00665 
00666             if ($this->isString) {
00667                     return $this->out;
00668 
00669             }
00670     }
00671 
00682         function JSMin($inFileName = '-', $outFileName = '-', $comments = NULL) {
00683                 if ($outFileName === FALSE) {
00684                         $this->JSMin_String($inFileName, $comments);
00685                 }
00686                 else {
00687                         $this->JSMin_File($inFileName, $outFileName, $comments);
00688                 }
00689         }
00690 
00691     function JSMin_File($inFileName = '-', $outFileName = '-', $comments = NULL) {
00692 
00693         // Recuperate input and output streams.
00694         // Use STDIN and STDOUT by default, if they are defined (CLI mode) and no file names are provided
00695 
00696             if ($inFileName == '-')  $inFileName  = 'php://stdin';
00697             if ($outFileName == '-') $outFileName = 'php://stdout';
00698 
00699             /*try {
00700 
00701                 $this->in = new SplFileObject($inFileName, 'rb', TRUE);
00702             }
00703             catch (Exception $e) {
00704 
00705                 throw new FileOpenFailedJSMinException(
00706                     'Failed to open "'.$inFileName.'" for reading only.'
00707                 );
00708             }
00709 
00710             try {
00711 
00712                 $this->out = new SplFileObject($outFileName, 'wb', TRUE);
00713             }
00714             catch (Exception $e) {
00715 
00716                 throw new FileOpenFailedJSMinException(
00717                     'Failed to open "'.$outFileName.'" for writing only.'
00718                 );
00719             }
00720                         */
00721                         $this->in = fopen($inFileName, 'rb');
00722                         if (!$this->in) {
00723                                 trigger_error('Failed to open "'.$inFileName, E_USER_ERROR);
00724                         }
00725                         $this->out = fopen($outFileName, 'wb');
00726                         if (!$this->out) {
00727                                 trigger_error('Failed to open "'.$outFileName, E_USER_ERROR);
00728                         }
00729 
00730             // Present possible initial comments
00731 
00732             if ($comments !== NULL) {
00733                 foreach ($comments as $comm) {
00734                     $this->out->fwrite('// '.str_replace("\n", " ", $comm)."\n");
00735                 }
00736             }
00737 
00738     }
00739 
00740     function JSMin_String($inString, $comments = NULL) {
00741         $this->in = $inString;
00742         $this->out = '';
00743                 $this->inLength = strlen($inString);
00744                 $this->inPos = 0;
00745                 $this->isString = true;
00746 
00747         if ($comments !== NULL) {
00748             foreach ($comments as $comm) {
00749                 $this->out .= '// '.str_replace("\n", " ", $comm)."\n";
00750             }
00751         }
00752         }
00753 }
00754 
00760 ?>

Generated on Fri Dec 13 2013 17:56:52 for ILIAS Release_3_9_x_branch .rev 46835 by  doxygen 1.7.1