Added all the files, updated the Readme

This commit is contained in:
Timothy Warren 2011-07-06 19:31:40 -04:00
parent e93f1aeb95
commit 9069bd4c68
6 changed files with 525 additions and 0 deletions

View File

@ -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

15
config/css_groups.php Normal file
View File

@ -0,0 +1,15 @@
<?php
return array(
/*-----
Css
-----*/
/*
For each group create an array like so
'my_group' => array(
'path/to/css/file1.css',
'path/to/css/file2.css'
),
*/
);

11
config/js_groups.php Normal file
View File

@ -0,0 +1,11 @@
<?php
return array(
/*
For each group create an array like so
'my_group' => array(
'path/to/css/file1.css',
'path/to/css/file2.css'
),
*/
);

333
config/jshrink.php Normal file
View File

@ -0,0 +1,333 @@
<?php
/*
JShrink
Copyright (c) 2009, Robert Hafner
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following
disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of the <ORGANIZATION> 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 <tedivm@tedivm.com>
* @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 {}
?>

82
css.php Normal file
View File

@ -0,0 +1,82 @@
<?php
//Change as needed
$base_path = $_SERVER['DOCUMENT_ROOT'];
//This GZIPs the CSS for transmission to the user
//making file size smaller and transfer rate quicker
ob_start("ob_gzhandler");
//Function for compressing the CSS as tightly as possible
function compress($buffer) {
//Remove CSS comments
$buffer = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $buffer);
//Remove tabs, spaces, newlines, etc.
$buffer = str_replace(array("\r\n", "\r", "\n", "\t", ' ', ' ', ' '), '', $buffer);
$buffer = preg_replace('`\s+`', ' ', $buffer);
return $buffer;
}
//Creative rewriting
$pi = $_SERVER['PATH_INFO'];
$pia = explode('/', $pi);
$pia_len = count($pia);
$i = 1;
while($i < $pia_len)
{
$j = $i+1;
$j = (isset($pia[$j])) ? $j : $i;
$_GET[$pia[$i]] = $pia[$j];
$i = $j + 1;
};
//Include the css groups
$groups = require("./config/css_groups.php");
$css = '';
$modified = array();
if(isset($groups[$_GET['g']]))
{
foreach($groups[$_GET['g']] as $file)
{
$new_file = realpath($base_path.$file);
$css .= file_get_contents($new_file);
$modified[] = filemtime($new_file);
}
}
//Add this page too
$modified[] = filemtime($base_path."css.tsp");
//Get the latest modified date
rsort($modified);
$last_modified = $modified[0];
if(!isset($_GET['debug']))
{
$css = compress($css);
}
$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: text/css; 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.'css.tsp') + 691200))." GMT");
echo $css;
ob_end_flush();
?>

74
js.php Normal file
View File

@ -0,0 +1,74 @@
<?php
//Change as needed
$base_path = $_SERVER['DOCUMENT_ROOT'];
require('./config/jshrink.php');
//This GZIPs the js for transmission to the user
//making file size smaller and transfer rate quicker
ob_start("ob_gzhandler");
//Creative rewriting
$pi = $_SERVER['PATH_INFO'];
$pia = explode('/', $pi);
$pia_len = count($pia);
$i = 1;
while($i < $pia_len)
{
$j = $i+1;
$j = (isset($pia[$j])) ? $j : $i;
$_GET[$pia[$i]] = $pia[$j];
$i = $j + 1;
};
//Include the js groups
$groups = require("./config/js_groups.php");
$js = '';
$modified = array();
if(isset($groups[$_GET['g']]))
{
foreach($groups[$_GET['g']] as $file)
{
$new_file = realpath($base_path.$file);
$js .= file_get_contents($new_file);
$modified[] = filemtime($new_file);
}
}
//Add this page too
$modified[] = filemtime($base_path."js.tsp");
//Get the latest modified date
rsort($modified);
$last_modified = $modified[0];
if(!isset($_GET['debug']))
{
$js = JShrink::minify($js, array('flaggedComments' => 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();
?>