ILIAS  release_5-3 Revision v5.3.23-19-g915713cf615
JSMin_lib.php
Go to the documentation of this file.
1 <?php
85 define('JSMIN_VERSION', '0.2');
86 
92 define('EOF', false);
93 
99 define('ORD_NL', ord("\n"));
100 define('ORD_space', ord(' '));
101 define('ORD_cA', ord('A'));
102 define('ORD_cZ', ord('Z'));
103 define('ORD_a', ord('a'));
104 define('ORD_z', ord('z'));
105 define('ORD_0', ord('0'));
106 define('ORD_9', ord('9'));
107 
111 /*
112 class JSMinException extends Exception {
113 }
114 */
116 {
117 }
118 
124 {
125 }
126 
132 {
133 }
134 
140 {
141 }
142 
148 {
149 }
150 
155 define('JSMIN_ACT_FULL', 1);
156 
161 define('JSMIN_ACT_BUF', 2);
162 
167 define('JSMIN_ACT_IMM', 3);
168 
180 class JSMin
181 {
182 
188  public $in;
189 
195  public $out;
196 
201  public $theA;
202 
207  public $theB;
208 
210  public $inLength = 0;
211  public $inPos = 0;
212  public $isString = false;
213 
220  public function isAlphaNum($c)
221  {
222 
223  // Get ASCII value of character for C-like comparisons
224 
225  $a = ord($c);
226 
227  // Compare using defined character ordinals, or between PHP strings
228  // Note : === is micro-faster than == when types are known to be the same
229 
230  return
231  ($a >= ORD_a && $a <= ORD_z) ||
232  ($a >= ORD_0 && $a <= ORD_9) ||
233  ($a >= ORD_cA && $a <= ORD_cZ) ||
234  $c === '_' || $c === '$' || $c === '\\' || $a > 126
235  ;
236  }
237 
247  public function get()
248  {
249 
250  // Get next input character and advance position in file
251 
252  if ($this->isString) {
253  if ($this->inPos < $this->inLength) {
254  $c = $this->in[$this->inPos];
255  ++$this->inPos;
256  } else {
257  return EOF;
258  }
259  } else {
260  $c = $this->in->fgetc();
261  }
262 
263  // Test for non-problematic characters
264 
265  if ($c === "\n" || $c === EOF || ord($c) >= ORD_space) {
266  return $c;
267  }
268 
269  // else
270  // Make linefeeds into newlines
271 
272  if ($c === "\r") {
273  return "\n";
274  }
275 
276  // else
277  // Consider space
278 
279  return ' ';
280  }
281 
290  public function peek()
291  {
292  if ($this->isString) {
293  if ($this->inPos < $this->inLength) {
294  $c = $this->in[$this->inPos];
295  } else {
296  return EOF;
297  }
298  } else {
299  // Get next input character
300 
301  $c = $this->in->fgetc();
302 
303  // Regress position in file
304 
305  $this->in->fseek(-1, SEEK_CUR);
306 
307  // Return character obtained
308  }
309 
310  return $c;
311  }
312 
317  public function put($c)
318  {
319  if ($this->isString) {
320  $this->out .= $c;
321  } else {
322  $this->out->fwrite($c);
323  }
324  }
325 
335  public function next()
336  {
337 
338  // Get next char from input, translated if necessary
339 
340  $c = $this->get();
341 
342  // Check comment possibility
343 
344  if ($c == '/') {
345 
346  // Look ahead : a comment is two slashes or slashes followed by asterisk (to be closed)
347 
348  switch ($this->peek()) {
349 
350  case '/':
351 
352  // Comment is up to the end of the line
353  // TOTEST : simple $this->in->fgets()
354 
355  while (true) {
356  $c = $this->get();
357 
358  if (ord($c) <= ORD_NL) {
359  return $c;
360  }
361  }
362 
363  // no break
364  case '*':
365 
366  // Comment is up to comment close.
367  // Might not be terminated, if we hit the end of file.
368 
369  while (true) {
370 
371  // N.B. not using switch() because of having to test EOF with ===
372 
373  $c = $this->get();
374 
375  if ($c == '*') {
376 
377  // Comment termination if the char ahead is a slash
378 
379  if ($this->peek() == '/') {
380 
381  // Advance again and make into a single space
382 
383  $this->get();
384  return ' ';
385  }
386  } elseif ($c === EOF) {
387 
388  // Whoopsie
389 
390  //throw new UnterminatedCommentJSMinException();
391  trigger_error('UnterminatedComment', E_USER_ERROR);
392  }
393  }
394 
395  // no break
396  default:
397 
398  // Not a comment after all
399 
400  return $c;
401  }
402  }
403 
404  // No risk of a comment
405 
406  return $c;
407  }
408 
423  public function action($action)
424  {
425 
426  // Choice of possible actions
427  // Note the frequent fallthroughs : the actions are decrementally "long"
428 
429  switch ($action) {
430 
431  case JSMIN_ACT_FULL:
432 
433  // Write A to output, then fall through
434 
435  $this->put($this->theA);
436 
437  // no break
438  case JSMIN_ACT_BUF: // N.B. possible fallthrough from above
439 
440  // Copy B to A
441 
442  $tmpA = $this->theA = $this->theB;
443 
444  // Treating a string as a single char : outputting it whole
445  // Note that the string-opening char (" or ') is memorized in B
446 
447  if ($tmpA == '\'' || $tmpA == '"') {
448  while (true) {
449 
450  // Output string contents
451 
452  $this->put($tmpA);
453 
454  // Get next character, watching out for termination of the current string,
455  // new line & co (then the string is not terminated !), or a backslash
456  // (upon which the following char is directly output to serve the escape mechanism)
457 
458  $tmpA = $this->theA = $this->get();
459 
460  if ($tmpA == $this->theB) {
461 
462  // String terminated
463 
464  break; // from while(true)
465  }
466 
467  // else
468 
469  if (ord($tmpA) <= ORD_NL) {
470 
471  // Whoopsie
472 
473  //throw new UnterminatedStringLiteralJSMinException();
474  trigger_error('UnterminatedStringLiteral', E_USER_ERROR);
475  }
476 
477  // else
478 
479  if ($tmpA == '\\') {
480 
481  // Escape next char immediately
482 
483  $this->put($tmpA);
484  $tmpA = $this->theA = $this->get();
485  }
486  }
487  }
488 
489  // no break
490  case JSMIN_ACT_IMM: // N.B. possible fallthrough from above
491 
492  // Get the next B
493 
494  $this->theB = $this->next();
495 
496  // Special case of recognising regular expressions (beginning with /) that are
497  // preceded by '(', ',' or '='
498 
499  $tmpA = $this->theA;
500 
501  if ($this->theB == '/' && ($tmpA == '(' || $tmpA == ',' || $tmpA == '=')) {
502 
503  // Output the two successive chars
504 
505  $this->put($tmpA);
506  $this->put($this->theB);
507 
508  // Look for the end of the RE literal, watching out for escaped chars or a control /
509  // end of line char (the RE literal then being unterminated !)
510 
511  while (true) {
512  $tmpA = $this->theA = $this->get();
513 
514  if ($tmpA == '/') {
515 
516  // RE literal terminated
517 
518  break; // from while(true)
519  }
520 
521  // else
522 
523  if ($tmpA == '\\') {
524 
525  // Escape next char immediately
526 
527  $this->put($tmpA);
528  $tmpA = $this->theA = $this->get();
529  } elseif (ord($tmpA) <= ORD_NL) {
530 
531  // Whoopsie
532 
533  //throw new UnterminatedRegExpLiteralJSMinException();
534  trigger_error('UnterminatedRegExpLiteral', E_USER_ERROR);
535  }
536 
537  // Output RE characters
538 
539  $this->put($tmpA);
540  }
541 
542  // Move forward after the RE literal
543 
544  $this->theB = $this->next();
545  }
546 
547  break;
548  default:
549  //throw new JSMinException('Expected a JSMin::ACT_* constant in action().');
550  trigger_error('Expected a JSMin::ACT_* constant in action()', E_USER_ERROR);
551  }
552  }
553 
569  public function minify()
570  {
571 
572  // Initialize A and run the first (minimal) action
573 
574  $this->theA = "\n";
575  $this->action(JSMIN_ACT_IMM);
576 
577  // Proceed all the way to the end of the input file
578 
579  while ($this->theA !== EOF) {
580  switch ($this->theA) {
581 
582  case ' ':
583 
584  if (JSMin::isAlphaNum($this->theB)) {
585  $this->action(JSMIN_ACT_FULL);
586  } else {
587  $this->action(JSMIN_ACT_BUF);
588  }
589 
590  break;
591  case "\n":
592 
593  switch ($this->theB) {
594 
595  case '{': case '[': case '(':
596  case '+': case '-':
597 
598  $this->action(JSMIN_ACT_FULL);
599 
600  break;
601  case ' ':
602 
603  $this->action(JSMIN_ACT_IMM);
604 
605  break;
606  default:
607 
608  if (JSMin::isAlphaNum($this->theB)) {
609  $this->action(JSMIN_ACT_FULL);
610  } else {
611  $this->action(JSMIN_ACT_BUF);
612  }
613 
614  break;
615  }
616 
617  break;
618  default:
619 
620  switch ($this->theB) {
621 
622  case ' ':
623 
624  if (JSMin::isAlphaNum($this->theA)) {
625  $this->action(JSMIN_ACT_FULL);
626  break;
627  }
628 
629  // else
630 
631  $this->action(JSMIN_ACT_IMM);
632 
633  break;
634  case "\n":
635 
636  switch ($this->theA) {
637 
638  case '}': case ']': case ')': case '+':
639  case '-': case '"': case '\'':
640 
641  $this->action(JSMIN_ACT_FULL);
642 
643  break;
644  default:
645 
646  if (JSMin::isAlphaNum($this->theA)) {
647  $this->action(JSMIN_ACT_FULL);
648  } else {
649  $this->action(JSMIN_ACT_IMM);
650  }
651 
652  break;
653  }
654 
655  break;
656  default:
657 
658  $this->action(JSMIN_ACT_FULL);
659 
660  break;
661  }
662 
663  break;
664  }
665  }
666 
667  if ($this->isString) {
668  return $this->out;
669  }
670  }
671 
682  public function __construct($inFileName = '-', $outFileName = '-', $comments = null)
683  {
684  if ($outFileName === false) {
685  $this->JSMin_String($inFileName, $comments);
686  } else {
687  $this->JSMin_File($inFileName, $outFileName, $comments);
688  }
689  }
690 
691  public function JSMin_File($inFileName = '-', $outFileName = '-', $comments = null)
692  {
693 
694  // Recuperate input and output streams.
695  // Use STDIN and STDOUT by default, if they are defined (CLI mode) and no file names are provided
696 
697  if ($inFileName == '-') {
698  $inFileName = 'php://stdin';
699  }
700  if ($outFileName == '-') {
701  $outFileName = 'php://stdout';
702  }
703 
704  /*try {
705 
706  $this->in = new SplFileObject($inFileName, 'rb', TRUE);
707  }
708  catch (Exception $e) {
709 
710  throw new FileOpenFailedJSMinException(
711  'Failed to open "'.$inFileName.'" for reading only.'
712  );
713  }
714 
715  try {
716 
717  $this->out = new SplFileObject($outFileName, 'wb', TRUE);
718  }
719  catch (Exception $e) {
720 
721  throw new FileOpenFailedJSMinException(
722  'Failed to open "'.$outFileName.'" for writing only.'
723  );
724  }
725  */
726  $this->in = fopen($inFileName, 'rb');
727  if (!$this->in) {
728  trigger_error('Failed to open "' . $inFileName, E_USER_ERROR);
729  }
730  $this->out = fopen($outFileName, 'wb');
731  if (!$this->out) {
732  trigger_error('Failed to open "' . $outFileName, E_USER_ERROR);
733  }
734 
735  // Present possible initial comments
736 
737  if ($comments !== null) {
738  foreach ($comments as $comm) {
739  $this->out->fwrite('// ' . str_replace("\n", " ", $comm) . "\n");
740  }
741  }
742  }
743 
744  public function JSMin_String($inString, $comments = null)
745  {
746  $this->in = $inString;
747  $this->out = '';
748  $this->inLength = strlen($inString);
749  $this->inPos = 0;
750  $this->isString = true;
751 
752  if ($comments !== null) {
753  foreach ($comments as $comm) {
754  $this->out .= '// ' . str_replace("\n", " ", $comm) . "\n";
755  }
756  }
757  }
758 }
759 
const ORD_space
Definition: JSMin_lib.php:100
JSMin_String($inString, $comments=null)
Definition: JSMin_lib.php:744
const JSMIN_ACT_BUF
Constant describing an action() : Copy B to A.
Definition: JSMin_lib.php:161
A JSMin exception indicatig that an unterminated string literal was encountered in input...
Definition: JSMin_lib.php:139
$action
next()
Get the next character from the input stream, excluding comments.
Definition: JSMin_lib.php:335
JSMin_File($inFileName='-', $outFileName='-', $comments=null)
Definition: JSMin_lib.php:691
Generic exception class related to JSMin.
Definition: JSMin_lib.php:115
__construct($inFileName='-', $outFileName='-', $comments=null)
Prepare a new JSMin application.
Definition: JSMin_lib.php:682
const ORD_z
Definition: JSMin_lib.php:104
isAlphaNum($c)
Indicates whether a character is alphanumeric or _, $, \ or non-ASCII.
Definition: JSMin_lib.php:220
const ORD_cA
Definition: JSMin_lib.php:101
const ORD_NL
Some ASCII character ordinals.
Definition: JSMin_lib.php:99
const ORD_a
Definition: JSMin_lib.php:103
const ORD_cZ
Definition: JSMin_lib.php:102
const ORD_0
Definition: JSMin_lib.php:105
const JSMIN_ACT_IMM
Constant describing an action() : Get the next B.
Definition: JSMin_lib.php:167
const ORD_9
Definition: JSMin_lib.php:106
A JSMin exception indicatig that an unterminated regular expression lieteral was encountered in input...
Definition: JSMin_lib.php:147
A JSMin exception indicating that a file provided for input or output could not be properly opened...
Definition: JSMin_lib.php:123
const JSMIN_ACT_FULL
Constant describing an action() : Output A.
Definition: JSMin_lib.php:155
action($action)
Do something !
Definition: JSMin_lib.php:423
put($c)
Adds a char to the output steram / string.
Definition: JSMin_lib.php:317
minify()
Run the JSMin application : minify some JS code.
Definition: JSMin_lib.php:569
Main JSMin application class.
Definition: JSMin_lib.php:180
peek()
Get the next character from the input stream, without gettng it.
Definition: JSMin_lib.php:290
A JSMin exception indicating that an unterminated comment was encountered in input.
Definition: JSMin_lib.php:131
const EOF
How fgetc() reports an End Of File.
Definition: JSMin_lib.php:92