diff --git a/min/lib/MyMin.php b/min/lib/MyMin.php new file mode 100644 index 0000000..15e688a --- /dev/null +++ b/min/lib/MyMin.php @@ -0,0 +1,289 @@ + + * @copyright 2002 Douglas Crockford (jsmin.c) + * @copyright 2007 Ryan Grove (PHP port) + * @copyright 2007 Andrea Giammarchi (improvements + MyMinCompressor + MyMinCSS) + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 1.0.1 (2007-10-05) - updated 2008-02-17 + */ + +// -- Class MyMin -------------------------------------------------------------- +class MyMin { + + const /* char */ LF = "\n", + SPACE = ' ', + EOS = "\x00"; + + protected /* boolean */ $cc_on; + + protected /* char */ $a, + $ahead, + $b; + + protected /* int */ $index = 0, + $length; + + protected /* string */ $input, + $output = ""; + + // -- Public Static Methods ---------------------------------------------------- + static public final function /* string */ parse(/* string */ $input, /* boolean */ $cc_on = true){ + return "".(new MyMin($input, $cc_on)); + } + + // -- Public Instance Methods -------------------------------------------------- + public final function /* object */ __construct(/* string */ $input, /* boolean */ $cc_on = true){ + $this->input = preg_replace("/(\r\n|\n\r|\r|\n)+/", self::LF, trim($input)); + $this->length = strlen($this->input); + $this->cc_on = $cc_on; + $this->b = $this->ahead = self::SPACE; + $this->a = self::LF; + $this->action(3); + while($this->a !== self::EOS){ + switch($this->a){ + case self::SPACE: + $this->action($this->isAlNum($this->b) ? 1 : 2); + break; + case self::LF: + switch($this->b){ + case '{': + case '[': + case '(': + case '+': + case '-': + $this->action(1); + break; + case self::SPACE: + $this->action(3); + break; + default: + $this->action($this->isAlNum($this->b) ? 1 : 2); + break; + } + break; + default: + switch($this->b){ + case self::SPACE: + $this->action($this->isAlNum($this->a) ? 1 : 3); + break; + case self::LF: + switch($this->a){ + case '}': + case ']': + case ')': + case '+': + case '-': + case '"': + case '\'': + $this->action(1); + break; + default: + $this->action($this->isAlNum($this->a) ? 1 : 3); + break; + } + break; + default: + $this->action(1); + break; + } + break; + } + } + } + + public final function /* string */ __toString(/* void */){ + return str_replace("\n\n", "\n", ltrim($this->output)); + } + + // -- Protected Instance Methods ----------------------------------------------- + protected function /* void */ action(/* int */ $i){ + switch($i){ + case 1: + $this->output .= $this->a; + case 2: + $this->a = $this->b; + if($this->a === '\'' || $this->a === '"'){ + while(true){ + $this->output .= $this->a; + if(!$this->nextCharNoSlash($this->b, "Unterminated string literal.")) + break; + } + } + case 3: + $this->b = $this->next(); + if($this->b === '/'){ + switch($this->a){ + case self::LF: + case self::SPACE: + if(!$this->spaceBeforeRegExp($this->output)) + break; + + case '{': + case ';': + + case '(': + case ',': + case '=': + case ':': + case '[': + case '!': + case '&': + case '|': + case '?': + $this->output .= $this->a.$this->b; + while($this->nextCharNoSlash('/', "Unterminated regular expression literal.")) + $this->output .= $this->a; + $this->b = $this->next(); + break; + } + } + break; + } + } + + protected function /* void */ appendComment(/* int */ $pos, /* string */ $open, /* string */ $close) { + $this->output .= $this->a.$open.(new MyMin(substr($this->input, $this->index, $pos - $this->index), $this->cc_on)).$close; + $this->index = $pos; + $this->a = self::LF; + } + + protected function /* void */ conditionalComment(/* char */ $find) { + $single = $find === self::LF; + $pos = strpos($this->input, $find, $this->index); + if($pos === false){ + if($single) + $pos = $this->length; + else + throw new MyMinException("Unterminated comment."); + } + $this->appendComment($pos, $single ? "//" : "/*", $find); + } + + protected function /* char */ get(/* void */) { + $c = $this->ahead; + $this->ahead = self::EOS; + if($c === self::EOS && $this->index < $this->length) + $c = $this->input{$this->index++}; + return ($c === self::EOS || $c === self::LF || $c >= self::SPACE) ? $c : self::SPACE; + } + + protected function /* boolean */ isAlNum(/* char */ $c) { + return $c > 126 || $c === '\\' || preg_match('/^(\w|\$)$/', $c); + } + + protected function /* char */ next(/* void */) { + $c = $this->get(); + $loop = true; + if($c === '/'){ + switch($this->ahead = $this->get()){ + case '/': + if($this->cc_on && $this->input{$this->index} === '@') + $this->conditionalComment(self::LF); + while($loop){ + $c = $this->get(); + if($c <= self::LF) + $loop = false; + } + break; + case '*': + $this->get(); + if($this->cc_on && $this->input{$this->index} === '@') + $this->conditionalComment("*/"); + while($loop){ + switch($this->get()){ + case '*': + if(($this->ahead = $this->get()) === '/'){ + $this->get(); + $c = self::SPACE; + $loop = false; + } + break; + case self::EOS: + throw new MyMinException("Unterminated comment."); + } + } + break; + } + } + return $c; + } + + protected function /* boolean */ nextCharNoSlash(/* char */ $c, /* string */ $message) { + $loop = true; + $this->a = $this->get(); + if($this->a === $c) + $loop = false; + else{ + if($this->a === '\\'){ + $this->output .= $this->a; + $this->a = $this->get(); + } + if($this->a <= self::LF) + throw new MyMinException($message); + } + return $loop; + } + + protected function /* boolean */ spaceBeforeRegExp(/* string */ $output){ + for( + $i = 0, + $length = strlen($output), + $reserved = array("case", "else", "in", "return", "typeof"), + $result = false, + $tmp = ""; + $i < 5 && !$result; + $i++ + ){ + if($length === strlen($reserved[$i])) + $result = $reserved[$i] === $output; + else if($length > strlen($reserved[$i])){ + $tmp = substr($output, $length - strlen($reserved[$i]) - 1); + $result = substr($tmp, 1) === $reserved[$i] && !$this->isAlNum($tmp{0}); + } + }; + return $length < 2 ? true : $result; + } +} + +// -- MyMin Exceptions --------------------------------------------------------- +class MyMinException extends Exception {} diff --git a/min_unit_tests/_test_files/js/before.js b/min_unit_tests/_test_files/js/before.js index daa70f1..cccdb97 100644 --- a/min_unit_tests/_test_files/js/before.js +++ b/min_unit_tests/_test_files/js/before.js @@ -36,14 +36,22 @@ if (is.ua.indexOf('gecko') >= 0) { /*@cc_on /*@if (@_win32) - document.write("OS is 32-bit, browser is IE."); + if (is.ie && is.win) + document.write("PASS: IE/win honored conditional comment.
"); @else @*/ - document.write("Browser is not IE (ie: is Firefox) or Browser is not 32 bit IE."); + if (is.ie && is.win) + document.write("FAIL: IE/win did not honor multi-line conditional comment.
"); + else + document.write("PASS: Non-IE/win browser ignores multi-line conditional comment.
"); /*@end @*/ +var recognizesCondComm = true; //@cc_on/* - -alert("Hello !IE browser"); - +recognizesCondComm = false; //@cc_on*/ + +if ((is.ie && is.win) == recognizesCondComm) + document.write("PASS: IE/win honored single-line conditional comment.
"); +else + document.write("FAIL: Non-IE/win browser did not ignore single-line conditional comment.
"); diff --git a/min_unit_tests/_test_files/js/before.min.js b/min_unit_tests/_test_files/js/before.min.js index 499aab2..6f06c6e 100644 --- a/min_unit_tests/_test_files/js/before.min.js +++ b/min_unit_tests/_test_files/js/before.min.js @@ -10,7 +10,13 @@ var is={ie:navigator.appName=='Microsoft Internet Explorer',java:navigator.javaE is.mac=is.ua.indexOf('mac')>=0;if(is.ua.indexOf('opera')>=0){is.ie=is.ns=false;is.opera=true;} if(is.ua.indexOf('gecko')>=0){is.ie=is.ns=false;is.gecko=true;}/*@cc_on /*@if (@_win32) - document.write("OS is 32-bit, browser is IE."); - @else @*/document.write("Browser is not IE (ie: is Firefox) or Browser is not 32 bit IE.");/*@end -@*///@cc_on/* -alert("Hello !IE browser");//@cc_on*/ \ No newline at end of file + if (is.ie && is.win) + document.write("PASS: IE/win honored conditional comment.
"); + @else @*/if(is.ie&&is.win) +document.write("FAIL: IE/win did not honor multi-line conditional comment.
");else +document.write("PASS: Non-IE/win browser ignores multi-line conditional comment.
");/*@end +@*/var recognizesCondComm=true;//@cc_on/* +recognizesCondComm=false;//@cc_on*/ +if((is.ie&&is.win)==recognizesCondComm) +document.write("PASS: IE/win honored single-line conditional comment.
");else +document.write("FAIL: Non-IE/win browser did not ignore single-line conditional comment.
"); \ No newline at end of file diff --git a/min_unit_tests/_test_files/minify/lines_output.js b/min_unit_tests/_test_files/minify/lines_output.js index 8775dd7..4088973 100644 --- a/min_unit_tests/_test_files/minify/lines_output.js +++ b/min_unit_tests/_test_files/minify/lines_output.js @@ -246,15 +246,26 @@ /* 36 */ /* 37 */ /*@cc_on /* 38 *| /*@if (@_win32) -/* 39 *| document.write("OS is 32-bit, browser is IE."); -/* 40 *| @else @*/ -/* 41 */ document.write("Browser is not IE (ie: is Firefox) or Browser is not 32 bit IE."); -/* 42 */ /*@end -/* 43 *| @*/ -/* 44 */ -/* 45 */ //@cc_on/* -/* 46 *| -/* 47 *| alert("Hello !IE browser"); -/* 48 *| -/* 49 *| //@cc_on*/ -/* 50 */ +/* 39 *| if (is.ie && is.win) +/* 40 *| document.write("PASS: IE/win honored conditional comment.
"); +/* 41 *| @else @*/ +/* 42 */ if (is.ie && is.win) +/* 43 */ document.write("FAIL: IE/win did not honor multi-line conditional comment.
"); +/* 44 */ else +/* 45 */ document.write("PASS: Non-IE/win browser ignores multi-line conditional comment.
"); +/* 46 */ /*@end +/* 47 *| @*/ +/* 48 */ +/* 49 */ var recognizesCondComm = true; +/* 50 */ //@cc_on/* + +/* before.js */ + +/* 51 *| recognizesCondComm = false; +/* 52 *| //@cc_on*/ +/* 53 */ +/* 54 */ if ((is.ie && is.win) == recognizesCondComm) +/* 55 */ document.write("PASS: IE/win honored single-line conditional comment.
"); +/* 56 */ else +/* 57 */ document.write("FAIL: Non-IE/win browser did not ignore single-line conditional comment.
"); +/* 58 */ diff --git a/min_unit_tests/test_MyMin.php b/min_unit_tests/test_MyMin.php new file mode 100644 index 0000000..554cede --- /dev/null +++ b/min_unit_tests/test_MyMin.php @@ -0,0 +1,23 @@ + +Test before.min.js +

Test before.min.js

+ \ No newline at end of file