diff --git a/README.md b/README.md index e69de29..d69396c 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,10 @@ +# Easy Min # + +A simple set of minifying scripts for CSS and Javascript + +## Basic Use: ## + +1. Figure out your file paths, and set them in css.php and js.php. +2. Add your css and javascript files to groups, in `config/css_groups.php` and `config/js_groups.php` respectively +3. Point your CSS links in your HTML to `css.php/g/[group_name]`, and likewise your javascript to `js.php/g/[group_name]` +4. Enjoy a faster loading website \ No newline at end of file diff --git a/config/css_groups.php b/config/css_groups.php new file mode 100644 index 0000000..ff104bb --- /dev/null +++ b/config/css_groups.php @@ -0,0 +1,15 @@ + array( + 'path/to/css/file1.css', + 'path/to/css/file2.css' + ), + */ +); \ No newline at end of file diff --git a/config/js_groups.php b/config/js_groups.php new file mode 100644 index 0000000..e2789a2 --- /dev/null +++ b/config/js_groups.php @@ -0,0 +1,11 @@ + array( + 'path/to/css/file1.css', + 'path/to/css/file2.css' + ), + */ + ); \ No newline at end of file diff --git a/config/jshrink.php b/config/jshrink.php new file mode 100644 index 0000000..c9afe4f --- /dev/null +++ b/config/jshrink.php @@ -0,0 +1,333 @@ + nor the names of its contributors may be used to endorse or promote + products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +/** + * JShrink + * + * Usage - JShrink::minify($js); + * Usage - JShrink::minify($js, $options); + * Usage - JShrink::minify($js, array('flaggedComments' => false)); + * + * @version 0.2 + * @package JShrink + * @author Robert Hafner + * @license http://www.opensource.org/licenses/bsd-license.php + */ +class JShrink +{ + protected $input; + protected $index = 0; + + protected $a = ''; + protected $b = ''; + protected $c; + + protected $options; + + static protected $defaultOptions = array('flaggedComments' => true); + + static public function minify($js, $options = array()) + { + try{ + $currentOptions = array_merge(self::$defaultOptions, $options); + + ob_start(); + $currentOptions = array_merge(self::$defaultOptions, $options); + $me = new JShrink(); + $me->breakdownScript($js, $currentOptions); + $output = ob_get_clean(); + return $output; + + }catch(Exception $e){ + ob_end_clean(); + throw $e; + } + } + + protected function breakdownScript($js, $currentOptions) + { + $this->options = $currentOptions; + + $js = str_replace("\r\n", "\n", $js); + $this->input = str_replace("\r", "\n", $js); + + $this->a = $this->getReal(); + + // the only time the length can be higher than 1 is if a conditional comment needs to be displayed + // and the only time that can happen for $a is on the very first run + while(strlen($this->a) > 1) + { + echo $this->a; + $this->a = $this->getReal(); + } + + $this->b = $this->getReal(); + + while($this->a !== false && !is_null($this->a) && $this->a !== '') + { + + // now we give $b the same check for conditional comments we gave $a before we began looping + if(strlen($this->b) > 1) + { + echo $this->a . $this->b; + $this->a = $this->getReal(); + $this->b = $this->getReal(); + continue; + } + + switch($this->a) + { + // new lines + case "\n": + // if the next line is something that can't stand alone preserver the newline + if(strpos('(-+{[@', $this->b) !== false) + { + echo $this->a; + $this->saveString(); + break; + } + + // if its a space we move down to the string test below + if($this->b === ' ') + break; + + // otherwise we treat the newline like a space + + case ' ': + if(self::isAlphaNumeric($this->b)) + echo $this->a; + + $this->saveString(); + break; + + default: + switch($this->b) + { + case "\n": + if(strpos('}])+-"\'', $this->a) !== false) + { + echo $this->a; + $this->saveString(); + break; + }else{ + if(self::isAlphaNumeric($this->a)) + { + echo $this->a; + $this->saveString(); + } + } + break; + + case ' ': + if(!self::isAlphaNumeric($this->a)) + break; + + default: + // check for some regex that breaks stuff + if($this->a == '/' && ($this->b == '\'' || $this->b == '"')) + { + $this->saveRegex(); + continue; + } + + echo $this->a; + $this->saveString(); + break; + } + } + + // do reg check of doom + $this->b = $this->getReal(); + + if(($this->b == '/' && strpos('(,=:[!&|?', $this->a) !== false)) + $this->saveRegex(); + } + } + + protected function getChar() + { + if(isset($this->c)) + { + $char = $this->c; + unset($this->c); + }else{ + if(isset($this->input[$this->index])) + { + $char = $this->input[$this->index]; + $this->index++; + }else{ + return false; + } + } + + if($char === "\n" || ord($char) >= 32) + return $char; + + return ' '; + } + + protected function getReal() + { + $startIndex = $this->index; + $char = $this->getChar(); + + if($char == '/') + { + $this->c = $this->getChar(); + + if($this->c == '/') + { + $thirdCommentString = $this->input[$this->index]; + + // kill rest of line + $char = $this->getNext("\n"); + + if($thirdCommentString == '@') + { + $endPoint = ($this->index) - $startIndex; + unset($this->c); + $char = "\n" . substr($this->input, $startIndex, $endPoint);// . "\n"; + }else{ + $char = $this->getChar(); + $char = $this->getChar(); + } + + }elseif($this->c == '*'){ + + $this->getChar(); // current C + $thirdCommentString = $this->getChar(); + + if($thirdCommentString == '@') + { + // we're gonna back up a bit and and send the comment back, where the first + // char will be echoed and the rest will be treated like a string + $this->index = $this->index-2; + return '/'; + + }elseif($this->getNext('*/')){ + // kill everything up to the next */ + + $this->getChar(); // get * + $this->getChar(); // get / + + $char = $this->getChar(); // get next real charactor + + // if YUI-style comments are enabled we reinsert it into the stream + if($this->options['flaggedComments'] && $thirdCommentString == '!') + { + $endPoint = ($this->index - 1) - $startIndex; + echo "\n" . substr($this->input, $startIndex, $endPoint) . "\n"; + } + + }else{ + $char = false; + } + + if($char === false) + throw new JShrinkException('Stray comment. ' . $this->index); + + // if we're here c is part of the comment and therefore tossed + if(isset($this->c)) + unset($this->c); + } + } + return $char; + } + + protected function getNext($string) + { + $pos = strpos($this->input, $string, $this->index); + + if($pos === false) + return false; + + $this->index = $pos ; + return $this->input[$this->index]; + } + + protected function saveString() + { + $this->a = $this->b; + if($this->a == '\'' || $this->a == '"') + { + // save literal string + $stringType = $this->a; + + while(1) + { + echo $this->a; + $this->a = $this->getChar(); + + switch($this->a) + { + case $stringType: + break 2; + + case "\n": + throw new JShrinkException('Unclosed string. ' . $this->index); + break; + + case '\\': + echo $this->a; + $this->a = $this->getChar(); + } + } + } + } + + protected function saveRegex() + { + echo $this->a . $this->b; + + while(($this->a = $this->getChar()) !== false) + { + if($this->a == '/') + break; + + if($this->a == '\\') + { + echo $this->a; + $this->a = $this->getChar(); + } + + if($this->a == "\n") + throw new JShrinkException('Stray regex pattern. ' . $this->index); + + echo $this->a; + } + $this->b = $this->getReal(); + } + + static protected function isAlphaNumeric($char) + { + return preg_match('/^[\w\$]$/', $char) === 1 || $char == '/'; + } + +} + +// Adding a custom exception handler for your own projects just means changing this line +class JShrinkException extends Exception {} +?> \ No newline at end of file diff --git a/css.php b/css.php new file mode 100644 index 0000000..1910670 --- /dev/null +++ b/css.php @@ -0,0 +1,82 @@ + diff --git a/js.php b/js.php new file mode 100644 index 0000000..778dcb2 --- /dev/null +++ b/js.php @@ -0,0 +1,74 @@ + false)); +} + +$requested_time=(isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) + ? strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) + : time(); + +if($last_modified === $requested_time) +{ + header("HTTP/1.1 304 Not Modified"); + exit(); +} + +header("Content-Type: application/x-javascript; charset=utf8"); +header("Cache-control: public, max-age=691200, must-revalidate"); +header("Last-Modified: ".gmdate('D, d M Y H:i:s', $last_modified)." GMT"); +header("Expires: ".gmdate('D, d M Y H:i:s', (filemtime($base_path.'js.tsp') + 691200))." GMT"); + +echo $js; + +ob_end_flush(); +?>