Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial Commit

  • Loading branch information...
commit cfb8eca0a5b56c08fc5b62adb3e59ba9ac6f5bfc 1 parent eeb79c2
@tonydspaniard tonydspaniard authored
View
24 .gitignore
@@ -0,0 +1,24 @@
+# cache directories
+Thumbs.db
+*.DS_Store
+*.empty
+
+#phpstorm project files
+.idea
+
+#netbeans project files
+nbproject
+
+#eclipse, zend studio, aptana or other eclipse like project files
+.buildpath
+.project
+.settings
+
+# sass cache files
+*.sass-cache
+
+# mac deployment helpers
+switch
+index
+
+
View
281 EAssetManagerBoost.php
@@ -0,0 +1,281 @@
+<?php
+/**
+ * EAssetManagerBoost class
+ *
+ * Extended version of CAssetManager to compress published assets
+ *
+ * @author Antonio Ramirez Cobos
+ * @link www.ramirezcobos.com
+ *
+ *
+ * @copyright
+ *
+ * Copyright (c) 2010 Antonio Ramirez Cobos
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ * The above copyright notice and this permission notice shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
+ * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+ * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+class EAssetManagerBoost extends CAssetManager {
+
+ /**
+ * @var mixed $minifiedExtensionFlags specify the extension names that
+ * the extension will check against in order to not compress/minify the
+ * files. This flags are ignored if $forceCompress is true
+ */
+ public $minifiedExtensionFlags = array('packed.js', 'min.js');
+ /**
+ * @var boolean $forceCompress if true, all files will be processed to
+ * be minified
+ */
+ public $forceCompress = false;
+ /**
+ * @var array published assets
+ */
+ private $_published = array();
+
+ /**
+ * Modified version of CAssetManager::publish method
+ * -------------------------------------------------------
+ * Publishes a file or a directory.
+ * This method will copy the specified asset to a web accessible directory
+ * and return the URL for accessing the published asset.
+ * <ul>
+ * <li>If the asset is a file, its file modification time will be checked
+ * to avoid unnecessary file copying;</li>
+ * <li>If the asset is a directory, all files and subdirectories under it will
+ * be published recursively. Note, in this case the method only checks the
+ * existence of the target directory to avoid repetitive copying.</li>
+ * </ul>
+ *
+ * Note: On rare scenario, a race condition can develop that will lead to a
+ * one-time-manifestation of a non-critical problem in the creation of the directory
+ * that holds the published assets. This problem can be avoided altogether by 'requesting'
+ * in advance all the resources that are supposed to trigger a 'publish()' call, and doing
+ * that in the application deployment phase, before system goes live. See more in the following
+ * discussion: http://code.google.com/p/yii/issues/detail?id=2579
+ *
+ * @param string $path the asset (file or directory) to be published
+ * @param boolean $hashByName whether the published directory should be named as the hashed basename.
+ * If false, the name will be the hashed dirname of the path being published.
+ * Defaults to false. Set true if the path being published is shared among
+ * different extensions.
+ * @param integer $level level of recursive copying when the asset is a directory.
+ * Level -1 means publishing all subdirectories and files;
+ * Level 0 means publishing only the files DIRECTLY under the directory;
+ * level N means copying those directories that are within N levels.
+ * @param boolean $forceCopy whether we should copy the asset file or directory even if it is already published before.
+ * This parameter is set true mainly during development stage when the original
+ * assets are being constantly changed. The consequence is that the performance
+ * is degraded, which is not a concern during development, however.
+ * This parameter has been available since version 1.1.2.
+ * @return string an absolute URL to the published asset
+ * @throws CException if the asset to be published does not exist.
+ */
+ public function publish($path, $hashByName=false, $level=-1, $forceCopy=false)
+ {
+ if (isset($this->_published[$path]))
+ return $this->_published[$path];
+ else if (($src = realpath($path)) !== false)
+ {
+ if (is_file($src))
+ {
+ $dir = $this->hash($hashByName ? basename($src) : dirname($src));
+ $fileName = basename($src);
+ $dstDir = $this->getBasePath() . DIRECTORY_SEPARATOR . $dir;
+ $dstFile = $dstDir . DIRECTORY_SEPARATOR . $fileName;
+
+ if ($this->linkAssets)
+ {
+ if (!is_file($dstFile))
+ {
+ if (!is_dir($dstDir))
+ {
+ mkdir($dstDir);
+ @chmod($dstDir, $this->newDirMode);
+ }
+ symlink($src, $dstFile);
+ }
+ }
+ else if (@filemtime($dstFile) < @filemtime($src) || $forceCopy)
+ {
+ if (!is_dir($dstDir))
+ {
+ mkdir($dstDir);
+ @chmod($dstDir, $this->newDirMode);
+ }
+ $this->copyFile($src, $dstFile);
+ @chmod($dstFile, $this->newFileMode);
+ }
+
+ return $this->_published[$path] = $this->getBaseUrl() . "/$dir/$fileName";
+ }
+ else if (is_dir($src))
+ {
+ $dir = $this->hash($hashByName ? basename($src) : $src);
+ $dstDir = $this->getBasePath() . DIRECTORY_SEPARATOR . $dir;
+
+ if ($this->linkAssets)
+ {
+ if (!is_dir($dstDir))
+ symlink($src, $dstDir);
+ }
+ else if (!is_dir($dstDir) || $forceCopy)
+ {
+ $fileTypes = array();
+ $options = array(
+ 'newDirMode' => $this->newDirMode,
+ 'newFileMode' => $this->newFileMode);
+
+ $this->copyDirectoryRecursive($src, $dstDir, '', array(), $this->excludeFiles, $level, $options);
+ }
+
+ return $this->_published[$path] = $this->getBaseUrl() . '/' . $dir;
+ }
+ }
+ throw new CException(Yii::t('yii', 'The asset "{asset}" to be published does not exist.', array('{asset}' => $path)));
+ }
+ /**
+ * Custom copy file to compress/copy|copy files
+ * @param string $src the full path of the file to read from
+ * @param string $dstFile the full path of the file to write to
+ */
+ public function copyFile($src, $dstFile)
+ {
+ // assumed config includes the required path aliases to use
+ // EScriptBoost
+ $ext = strtoupper(substr(strrchr($src, '.'), 1));
+
+ if (($ext == 'JS' && !$this->strpos_arr($dstFile, $this->minifiedExtensionFlags)) || $this->forceCompress )
+ {
+
+ Yii::trace('copyFile JS Compressing: ' . $src, 'EAssetManagerBoost');
+
+ @file_put_contents(
+ $dstFile, EScriptBoost::minifyJs(@file_get_contents($src), EScriptBoost::JS_MIN_PLUS));
+ @touch($dstFile, @filemtime($src));
+ }
+ else if (($ext == 'CSS' && !$this->strpos_arr($dstFile, $this->minifiedExtensionFlags)) || $this->forceCompress )
+ {
+
+ Yii::trace('copyFile CSS Compressing: ' . $src, 'EAssetManagerBoost');
+
+ @file_put_contents(
+ $dstFile, EScriptBoost::minifyCss(@file_get_contents($src)));
+ @touch($dstFile, @filemtime($src));
+ }
+ else
+ {
+ copy($src, $dstFile);
+ }
+
+ }
+
+ /**
+ * Modified version of CFileHelper::copyDirectoryRecursive
+ * -------------------------------------------------------
+ * Copies a directory.
+ * This method is mainly used by {@link copyDirectory}.
+ * @param string $src the source directory
+ * @param string $dst the destination directory
+ * @param string $base the path relative to the original source directory
+ * @param array $fileTypes list of file name suffix (without dot). Only files with these suffixes will be copied.
+ * @param array $exclude list of directory and file exclusions. Each exclusion can be either a name or a path.
+ * If a file or directory name or path matches the exclusion, it will not be copied. For example, an exclusion of
+ * '.svn' will exclude all files and directories whose name is '.svn'. And an exclusion of '/a/b' will exclude
+ * file or directory '$src/a/b'. Note, that '/' should be used as separator regardless of the value of the DIRECTORY_SEPARATOR constant.
+ * @param integer $level recursion depth. It defaults to -1.
+ * Level -1 means copying all directories and files under the directory;
+ * Level 0 means copying only the files DIRECTLY under the directory;
+ * level N means copying those directories that are within N levels.
+ * @param array $options additional options. The following options are supported:
+ * newDirMode - the permission to be set for newly copied directories (defaults to 0777);
+ * newFileMode - the permission to be set for newly copied files (defaults to the current environment setting).
+ */
+ protected function copyDirectoryRecursive($src, $dst, $base, $fileTypes, $exclude, $level, $options)
+ {
+ if (!is_dir($dst))
+ mkdir($dst);
+ if (isset($options['newDirMode']))
+ @chmod($dst, $options['newDirMode']);
+ else
+ @chmod($dst, 0777);
+ $folder = opendir($src);
+ while (($file = readdir($folder)) !== false)
+ {
+ if ($file === '.' || $file === '..')
+ continue;
+ $path = $src . DIRECTORY_SEPARATOR . $file;
+ $isFile = is_file($path);
+ if ($this->validatePath($base, $file, $isFile, $fileTypes, $exclude))
+ {
+ if ($isFile)
+ {
+ $this->copyFile($path, $dst . DIRECTORY_SEPARATOR . $file);
+ if (isset($options['newFileMode']))
+ @chmod($dst . DIRECTORY_SEPARATOR . $file, $options['newFileMode']);
+ }
+ else if ($level)
+ $this->copyDirectoryRecursive($path, $dst . DIRECTORY_SEPARATOR . $file, $base . '/' . $file, $fileTypes, $exclude, $level - 1, $options);
+ }
+ }
+ closedir($folder);
+ }
+
+ /**
+ * Forced included from CFileHelper::validate in order to
+ * use copyDirectoryRecursive -why not static?
+ * -------------------------------------------------------
+ * Validates a file or directory.
+ * @param string $base the path relative to the original source directory
+ * @param string $file the file or directory name
+ * @param boolean $isFile whether this is a file
+ * @param array $fileTypes list of file name suffix (without dot). Only files with these suffixes will be copied.
+ * @param array $exclude list of directory and file exclusions. Each exclusion can be either a name or a path.
+ * If a file or directory name or path matches the exclusion, it will not be copied. For example, an exclusion of
+ * '.svn' will exclude all files and directories whose name is '.svn'. And an exclusion of '/a/b' will exclude
+ * file or directory '$src/a/b'. Note, that '/' should be used as separator regardless of the value of the DIRECTORY_SEPARATOR constant.
+ * @return boolean whether the file or directory is valid
+ */
+ protected function validatePath($base, $file, $isFile, $fileTypes, $exclude)
+ {
+ foreach ($exclude as $e)
+ {
+ if ($file === $e || strpos($base . '/' . $file, $e) === 0)
+ return false;
+ }
+ if (!$isFile || empty($fileTypes))
+ return true;
+ if (($type = pathinfo($file, PATHINFO_EXTENSION)) !== '')
+ return in_array($type, $fileTypes);
+ else
+ return false;
+ }
+ /**
+ * Custom strpos to support arrays as needles
+ * @param string $haystack the string to check against
+ * @param mixed $needle the array|string to check against
+ * @return boolean
+ */
+ protected function strpos_arr($haystack, $needle) {
+ if (!is_array($needle))
+ return strpos($haystack, $needle);
+
+ foreach ($needle as $what)
+ {
+ if (($pos = strpos($haystack, $what)) !== false)
+ return $pos;
+ }
+ return false;
+ }
+
+}
View
45 EClientScriptBoost.php
@@ -0,0 +1,45 @@
+<?php
+/**
+ * EClientScriptBoost class
+ *
+ * Is an extended version of CClientScript to compress registered scripts
+ *
+ * @author Antonio Ramirez Cobos
+ * @link www.ramirezcobos.com
+ *
+ *
+ * @copyright
+ *
+ * Copyright (c) 2010 Antonio Ramirez Cobos
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ * The above copyright notice and this permission notice shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
+ * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+ * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+class EClientScriptBoost extends CClientScript
+{
+ public $cacheDuration = 0;
+
+ public function registerScript($id, $script, $position=self::POS_READY)
+ {
+ // assumed config includes the required path aliases to use
+ // EScriptBoost
+ $compressed = YII_DEBUG ? $script : Yii::app()->cache->get($id);
+
+ if($compressed === false)
+ {
+ $compressed = EScriptBoost::minifyJs($script);
+ Yii::app()->cache->set($id, $compressed, $this->cacheDuration);
+ }
+ parent::registerScript($id, $compressed, $position);
+ }
+}
View
172 EScriptBoost.php
@@ -0,0 +1,172 @@
+<?php
+/**
+ * EScriptBoost class
+ *
+ * Compresses CSS and JS script source with different adapters
+ *
+ * @author Antonio Ramirez Cobos
+ * @link www.ramirezcobos.com
+ *
+ *
+ * @copyright
+ *
+ * Copyright (c) 2010 Antonio Ramirez Cobos
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ * The above copyright notice and this permission notice shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
+ * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+ * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+$assets = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'assets';
+
+Yii::setPathOfAlias('scriptboostjs', $assets . DIRECTORY_SEPARATOR . 'js');
+Yii::setPathOfAlias('scriptboostcss', $assets . DIRECTORY_SEPARATOR . 'css');
+Yii::setPathOfAlias('scriptboosthtml', $assets . DIRECTORY_SEPARATOR . 'html');
+
+Yii::import('scriptboostjs.*');
+Yii::import('scriptboostcss.*');
+Yii::import('scriptboosthtml.HTMLMin');
+
+class EScriptBoost extends CComponent {
+
+ const CSS_COMPRESSOR = 'CssCompressor';
+ const CSS_MIN = 'CssMin';
+ const CSS_MINIFIER = 'CssMinifier';
+
+ const JS_MIN = 'JSMin';
+ const JS_MIN_PLUS = 'JSMinPlus';
+
+ /**
+ * CssMin filter options. Default values according cssMin doc.
+ */
+ protected static $cssMinFilters = array
+ (
+ 'ImportImports' => false,
+ 'RemoveComments' => true,
+ 'RemoveEmptyRulesets' => true,
+ 'RemoveEmptyAtBlocks' => true,
+ 'ConvertLevel3AtKeyframes' => false,
+ 'ConvertLevel3Properties' => false,
+ 'RemoveLastDelarationSemiColon' => true
+ );
+
+ /**
+ * CssMin plugin options. Maximum compression and conversion.
+ */
+ protected static $cssMinPlugins = array
+ (
+ 'Variables' => true,
+ 'ConvertFontWeight' => false,
+ 'ConvertHslColors' => false,
+ 'ConvertRgbColors' => false,
+ 'ConvertNamedColors' => false,
+ 'CompressColorValues' => false,
+ 'CompressUnitValues' => true,
+ 'CompressExpressionValues' => false,
+ );
+
+ /**
+ *
+ * @param string $content the content to compres
+ * @param string $adapter the adapter to use to minify
+ * @param array $options the options for the adapter
+ * @return compressed content if successful | false otherwise
+ */
+ public static function minifyCss($content, $adapter = self::CSS_MIN, $options = array())
+ {
+ if(is_string($adapter))
+ {
+ switch($adapter)
+ {
+ case self::CSS_COMPRESSOR:
+ return call_user_func_array(array($adapter,'process'), array($content, $options));
+ break;
+ case self::CSS_MIN:
+ // check css/CssMin class for options
+ $filters = isset($options['filters']) ? array_merge(self::$cssMinFilters, $options['filters']) : self::$cssMinFilters;
+ $plugins = isset($options['plugins']) ? array_merge(self::$cssMinPlugins, $options['plugins']) : self::$cssMinPlugins;
+ return call_user_func_array(array($adapter, 'minify'), array($content, $filters, $plugins));
+ break;
+ case self::CSS_MINIFIER:
+ // check css/CssMinifier for options
+ return call_user_func_array(array($adapter, 'minify'), array($content, $options));
+ break;
+ }
+ }
+ return false;
+ }
+
+ /**
+ *
+ * @param string $content the content to minify
+ * @param string $adapter the adapter to use to minify
+ * @param array $options the options to the adapter
+ * @return compressed code if successful | false otherwise
+ */
+ public static function minifyJs($content, $adapter = self::JS_MIN, $options = array())
+ {
+ if(is_string($adapter) && ($adapter == self::JS_MIN || $adapter == self::JS_MIN_PLUS))
+ {
+ return call_user_func_array(array($adapter, 'minify'), array($content));
+ }
+ return false;
+ }
+
+ /**
+ *
+ * @param string $content the content to parse
+ * @param array $options
+ * @see HTMLMin class to check the options
+ * @return minified content
+ */
+ public static function minifyHTML($content, $options = array())
+ {
+ return HTMLMin::minify($content, $options);
+ }
+
+ /**
+ *
+ * @param string $content the content to pack
+ * @param string | integer $encoding the compression type
+ * @see JavaScriptPacker class
+ * @param boolean $fastDecode
+ * @param boolean $specialChars
+ * @return packed content
+ */
+ public static function packJs($content, $encoding = 'Normal', $fastDecode = true, $specialChars = false)
+ {
+ $jsSize = strlen($content);
+ if ($jsSize > 256 && $jsSize < 1048576)
+ { // prevent memory error
+ $packer = new JavaScriptPacker($content, $encoding, $fastDecode, $specialChars);
+ return $packer->pack();
+ }
+ return $content;
+ }
+
+ /**
+ * Helper function to register compressed script to CClientScript
+ * @param string $id the id of the script
+ * @param script code $script
+ * @param integer $cacheDuration
+ * @param integer $position the position where to register the script
+ */
+ public static function registerScript($id, $script, $cacheDuration=0, $position=CClientScript::POS_READY)
+ {
+ $js = Yii::app()->cache->get($id);
+ if(!$js)
+ {
+ $js = self::minifyJs($script);
+ Yii::app()->cache->set($id, $js, $cacheDuration);
+ }
+ Yii::app()->clientScript->registerScript($id, $js, $position);
+ }
+}
View
157 README.md
@@ -1,4 +1,159 @@
escriptboost
============
-Minify the javascript/css code of your application's assets and pages
+***Minify the javascript/css code of your application's assets and pages***
+
+##Introduction
+Probably a lot of you would wonder why, having so many good extensions related to minifying/compressing/packing your javascript and css files around, here comes this guy offering us another solution.
+
+I did check out all the extensions in our repository, just to name some of them:
+
+ * [clientscriptpacker](http://www.yiiframework.com/extension/clientscriptpacker)
+ * [dynamicres](http://www.yiiframework.com/extension/dynamicres/)
+ * [minscript](http://www.yiiframework.com/extension/minscript/)
+
+All of them are great, but none were filling the requirements we had. I did not have any issue compressing all our files as our team, will use the YUI compressor jar file to create our compressed javascript versions and then use the wonderful mapping features of CClientScript. The issue was with the assets of external, or own developed, extensions and the javascript code that, even Yii own widgets, were writing to the `POS_BEGIN`, `POS_END`, `POS_HEAD`, `POS_LOAD`, `POS_READY` positions. Thats exactly what this library is doing: allowing Yii coders to minify those scripts.
+
+
+##Requirements
+
+You need to enable any caching mechanism in Yii in order to use it. For example:
+
+~~~
+'cache' => array(
+ 'class' => 'system.caching.CApcCache',
+),
+~~~
+
+
+##Library
+
+The library comes with three flavors:
+
+ * EScriptBoost Component
+ * EClientScriptBoost Extension
+ * EAssetManagerBoost Extension
+
+###EScriptBoost Component
+
+This is a very easy to use component to compress your HTML, Javascript or CSS code at your will. The minifiers used are:
+
+ * For CSS- [CssMin](http://code.google.com/p/cssmin/ "CssMin") and [CssMinify](http://code.google.com/p/minify/ "CssMinify") (with CssCompressor and CssUriRewriter classes that you can also use independently)
+ * For JS- [JsMin](http://code.google.com/p/jsmin-php/ "JsMin"), [JsMinPlus](http://crisp.tweakblogs.net/blog/cat/716 "JsMinPlus") and [JavaScriptPacker](http://dean.edwards.name/packer/usage/ "JavaScriptPacker")
+
+ * For HTML- [HTMLMin](http://code.google.com/p/minify/source/browse/tags/beta_2.0.2/lib/Minify/HTML.php)
+
+***Usage***
+
+~~~
+// this is a very simple example :)
+// we use cache as we do not want to
+// compress/minify all the time our
+// script
+$js = Yii::app()->cache->get('scriptID');
+
+if(!$js)
+{
+ $cacheDuration = 30;
+ $js = <<<EOD
+ // my long and uncompressed code here
+EOD;
+ // $js = EScriptBoost::packJs($js);
+ // $js = EScriptBoost::minifyJs($js, EScriptBoost::JS_MIN_PLUS);
+ $js = EScriptBoost::minifyJs($js, EScriptBoost::JS_MIN);
+
+ // see Cache guide for more options | dependencies
+ Yii::app()->cache->set('scriptID', $cacheDuration);
+}
+Yii::app()->clientScript->registerScript('scriptID', $js);
+~~~
+
+That was troublesome right? No worries, if you don't really care about using JS_MIN, or JS_MIN_PLUS, you can use its helper function **registerScript**, it will handle all of the above automatically:
+
+~~~
+$js = <<<EOD
+ // my long and uncompressed code here
+EOD;
+EScriptBoost::registerScript('scriptID', $js);
+~~~
+
+###EClientScriptBoost Extension
+
+**EScriptBoost** was good for the javascript code written by me but what about the ones written by Yii widgets? **EClientScriptBoost** was developed to solve that:
+
+***Usage***
+
+On your main.php config file:
+
+~~~
+'import' => array(
+// ... other configuration settings on main.php
+// ... importing the folder where scriptboost is
+ 'application.extensions.scriptboost.*',
+// ... more configuration settings
+ ),
+// ... other configuration settings on main.php
+'components' => array(
+ 'clientScript' => array(
+// ... assuming you have previously imported the folder
+// where EClientScriptBoost is
+ 'class'=>'EClientScriptBoost',
+ 'cacheDuration'=>30,
+// ... more configuration settings
+~~~
+
+Done! now, every time you or other component on your application registers some script, it will be minified and cached as you specify on your cache settings. Easy right?
+
+###EAssetManagerBoost Extension
+
+But there was one more challenge to solve. Some extensions, widgets, etc, do publish a whole bunch of files in our assets that are not minified. How to solve that? This is where **EAssetManagerBoost** comes handy.
+
+This extension does not only minify about to be registered javascript/css files, but also makes sure that the files do not match any of its $minifiedExtensionFlags. This way we can avoid to minify/compress files that do not require it.
+
+***Usage***
+
+Make sure you have deleted your previous assets folder contents.
+
+~~~
+'import' => array(
+// ... other configuration settings on main.php
+// ... importing the folder where scriptboost is
+ 'application.extensions.scriptboost.*',
+// ... more configuration settings
+ ),
+// ... other configuration settings on main.php
+'components' => array(
+ 'assetManager' => array(
+// ... assuming you have previously imported the folder
+ 'class' => 'EAssetManagerBoost',
+ 'minifiedExtensionFlags'=>array('min.js','minified.js','packed.js')
+ ),
+// ... more configuration settings
+~~~
+
+***Important Note***
+There is a small drawback to use EAssetManagerBoost and is that the first time your application is requested, it will take a bit of time as it will go throughout all your asset files to be published and minify them.
+
+###Minifying CSS and/or HTML
+EScriptBoost comes with a set of handy helper functions, among those minifyCSS and minifyHTML. They are very easy to use, for example with minifyCSS if you look at the code of EAssetManagerBoost lines 172-173, you will see a use of it:
+
+~~~
+[php]
+@file_put_contents($dstFile, EScriptBoost::minifyCss(@file_get_contents($src)));
+~~~
+
+As you can see, you need to pass the CSS contents of a file or a script, in order to be minified. To minifyHTML is the same, you can get the contents returned by renderPartial to be minified or even do it with the whole view. How to be applied will depend on the requirements of the application and it needs to be carefully studied as that could slow down your application without the proper caching technique.
+
+Please, check the reference links in order to get information about how to configure the minifiers included in this extension.
+
+
+##ChangeLog
+ * Version 1.0.1 Included HTML Compression
+ * Version 1.0.0 Initial public release
+
+##Resources
+ * [Project page](http://www.ramirezcobos.com/)
+ * [Minify Google Code Project](http://code.google.com/p/minify/)
+ * [JsMinPlus page](http://crisp.tweakblogs.net/blog/cat/716 "JsMinPlus")
+ * [CssMin](http://code.google.com/p/cssmin/ "CssMin")
+ * [Yii Forum Post](http://www.yiiframework.com/forum/index.php?/topic/26550-extension-escriptboost/page__pid__127736#entry127736)
View
259 assets/css/CssCompressor.php
@@ -0,0 +1,259 @@
+<?php
+
+/**
+ * CssCompressor
+ * @package scriptboost
+ */
+
+/**
+ * Compress CSS
+ *
+ * This is a heavy regex-based removal of whitespace, unnecessary
+ * comments and tokens, and some CSS value minimization, where practical.
+ * Many steps have been taken to avoid breaking comment-based hacks,
+ * including the ie5/mac filter (and its inversion), but expect tricky
+ * hacks involving comment tokens in 'content' value strings to break
+ * minimization badly. A test suite is available.
+ *
+ * @author Stephen Clay <steve@mrclay.org>
+ */
+class CssCompressor {
+
+ /**
+ * Minify a CSS string
+ *
+ * @param string $css
+ *
+ * @param array $options (currently ignored)
+ *
+ * @return string
+ */
+ public static function process($css, $options = array())
+ {
+ $obj = new CssCompressor($options);
+ return $obj->_process($css);
+ }
+
+ /**
+ * @var array options
+ */
+ protected $_options = null;
+
+ /**
+ * @var bool Are we "in" a hack?
+ *
+ * I.e. are some browsers targetted until the next comment?
+ */
+ protected $_inHack = false;
+
+ /**
+ * Constructor
+ *
+ * @param array $options (currently ignored)
+ *
+ * @return null
+ */
+ private function __construct($options)
+ {
+ $this->_options = $options;
+ }
+
+ /**
+ * Minify a CSS string
+ *
+ * @param string $css
+ *
+ * @return string
+ */
+ protected function _process($css)
+ {
+ $css = str_replace("\r\n", "\n", $css);
+
+ // preserve empty comment after '>'
+ // http://www.webdevout.net/css-hacks#in_css-selectors
+ $css = preg_replace('@>/\\*\\s*\\*/@', '>/*keep*/', $css);
+
+ // preserve empty comment between property and value
+ // http://css-discuss.incutio.com/?page=BoxModelHack
+ $css = preg_replace('@/\\*\\s*\\*/\\s*:@', '/*keep*/:', $css);
+ $css = preg_replace('@:\\s*/\\*\\s*\\*/@', ':/*keep*/', $css);
+
+ // apply callback to all valid comments (and strip out surrounding ws
+ $css = preg_replace_callback('@\\s*/\\*([\\s\\S]*?)\\*/\\s*@'
+ , array($this, '_commentCB'), $css);
+
+ // remove ws around { } and last semicolon in declaration block
+ $css = preg_replace('/\\s*{\\s*/', '{', $css);
+ $css = preg_replace('/;?\\s*}\\s*/', '}', $css);
+
+ // remove ws surrounding semicolons
+ $css = preg_replace('/\\s*;\\s*/', ';', $css);
+
+ // remove ws around urls
+ $css = preg_replace('/
+ url\\( # url(
+ \\s*
+ ([^\\)]+?) # 1 = the URL (really just a bunch of non right parenthesis)
+ \\s*
+ \\) # )
+ /x', 'url($1)', $css);
+
+ // remove ws between rules and colons
+ $css = preg_replace('/
+ \\s*
+ ([{;]) # 1 = beginning of block or rule separator
+ \\s*
+ ([\\*_]?[\\w\\-]+) # 2 = property (and maybe IE filter)
+ \\s*
+ :
+ \\s*
+ (\\b|[#\'"-]) # 3 = first character of a value
+ /x', '$1$2:$3', $css);
+
+ // remove ws in selectors
+ $css = preg_replace_callback('/
+ (?: # non-capture
+ \\s*
+ [^~>+,\\s]+ # selector part
+ \\s*
+ [,>+~] # combinators
+ )+
+ \\s*
+ [^~>+,\\s]+ # selector part
+ { # open declaration block
+ /x'
+ , array($this, '_selectorsCB'), $css);
+
+ // minimize hex colors
+ $css = preg_replace('/([^=])#([a-f\\d])\\2([a-f\\d])\\3([a-f\\d])\\4([\\s;\\}])/i'
+ , '$1#$2$3$4$5', $css);
+
+ // remove spaces between font families
+ $css = preg_replace_callback('/font-family:([^;}]+)([;}])/'
+ , array($this, '_fontFamilyCB'), $css);
+
+ $css = preg_replace('/@import\\s+url/', '@import url', $css);
+
+ // replace any ws involving newlines with a single newline
+ $css = preg_replace('/[ \\t]*\\n+\\s*/', "\n", $css);
+
+ // separate common descendent selectors w/ newlines (to limit line lengths)
+ $css = preg_replace('/([\\w#\\.\\*]+)\\s+([\\w#\\.\\*]+){/', "$1\n$2{", $css);
+
+ // Use newline after 1st numeric value (to limit line lengths).
+ $css = preg_replace('/
+ ((?:padding|margin|border|outline):\\d+(?:px|em)?) # 1 = prop : 1st numeric value
+ \\s+
+ /x'
+ , "$1\n", $css);
+
+ // prevent triggering IE6 bug: http://www.crankygeek.com/ie6pebug/
+ $css = preg_replace('/:first-l(etter|ine)\\{/', ':first-l$1 {', $css);
+
+ return trim($css);
+ }
+
+ /**
+ * Replace what looks like a set of selectors
+ *
+ * @param array $m regex matches
+ *
+ * @return string
+ */
+ protected function _selectorsCB($m)
+ {
+ // remove ws around the combinators
+ return preg_replace('/\\s*([,>+~])\\s*/', '$1', $m[0]);
+ }
+
+ /**
+ * Process a comment and return a replacement
+ *
+ * @param array $m regex matches
+ *
+ * @return string
+ */
+ protected function _commentCB($m)
+ {
+ $hasSurroundingWs = (trim($m[0]) !== $m[1]);
+ $m = $m[1];
+ // $m is the comment content w/o the surrounding tokens,
+ // but the return value will replace the entire comment.
+ if ($m === 'keep')
+ {
+ return '/**/';
+ }
+ if ($m === '" "')
+ {
+ // component of http://tantek.com/CSS/Examples/midpass.html
+ return '/*" "*/';
+ }
+ if (preg_match('@";\\}\\s*\\}/\\*\\s+@', $m))
+ {
+ // component of http://tantek.com/CSS/Examples/midpass.html
+ return '/*";}}/* */';
+ }
+ if ($this->_inHack)
+ {
+ // inversion: feeding only to one browser
+ if (preg_match('@
+ ^/ # comment started like /*/
+ \\s*
+ (\\S[\\s\\S]+?) # has at least some non-ws content
+ \\s*
+ /\\* # ends like /*/ or /**/
+ @x', $m, $n))
+ {
+ // end hack mode after this comment, but preserve the hack and comment content
+ $this->_inHack = false;
+ return "/*/{$n[1]}/**/";
+ }
+ }
+ if (substr($m, -1) === '\\')
+ { // comment ends like \*/
+ // begin hack mode and preserve hack
+ $this->_inHack = true;
+ return '/*\\*/';
+ }
+ if ($m !== '' && $m[0] === '/')
+ { // comment looks like /*/ foo */
+ // begin hack mode and preserve hack
+ $this->_inHack = true;
+ return '/*/*/';
+ }
+ if ($this->_inHack)
+ {
+ // a regular comment ends hack mode but should be preserved
+ $this->_inHack = false;
+ return '/**/';
+ }
+ // Issue 107: if there's any surrounding whitespace, it may be important, so
+ // replace the comment with a single space
+ return $hasSurroundingWs; // remove all other comments ? ' ' : '';
+ }
+
+ /**
+ * Process a font-family listing and return a replacement
+ *
+ * @param array $m regex matches
+ *
+ * @return string
+ */
+ protected function _fontFamilyCB($m)
+ {
+ // Issue 210: must not eliminate WS between words in unquoted families
+ $pieces = preg_split('/(\'[^\']+\'|"[^"]+")/', $m[1], null, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
+ $out = 'font-family:';
+ while (null !== ($piece = array_shift($pieces)))
+ {
+ if ($piece[0] !== '"' && $piece[0] !== "'")
+ {
+ $piece = preg_replace('/\\s+/', ' ', $piece);
+ $piece = preg_replace('/\\s?,\\s?/', ',', $piece);
+ }
+ $out .= $piece;
+ }
+ return $out . $m[2];
+ }
+
+}
View
5,243 assets/css/CssMin.php
5,243 additions, 0 deletions not shown
View
184 assets/css/CssMinify.php
@@ -0,0 +1,184 @@
+<?php
+/**
+ * Class CssMinify
+ * @package scriptboost
+ */
+
+/**
+ * Minify CSS
+ *
+ * This class uses Css_Compressor and CssUriRewriter to
+ * minify CSS and rewrite relative URIs.
+ *
+ * @author Stephen Clay <steve@mrclay.org>
+ * @author http://code.google.com/u/1stvamp/ (Issue 64 patch)
+ */
+class CssMinify {
+
+ /**
+ * Minify a CSS string
+ *
+ * @param string $css
+ *
+ * @param array $options available options:
+ *
+ * 'preserveComments': (default true) multi-line comments that begin
+ * with "/*!" will be preserved with newlines before and after to
+ * enhance readability.
+ *
+ * 'removeCharsets': (default true) remove all @charset at-rules
+ *
+ * 'prependRelativePath': (default null) if given, this string will be
+ * prepended to all relative URIs in import/url declarations
+ *
+ * 'currentDir': (default null) if given, this is assumed to be the
+ * directory of the current CSS file. Using this, minify will rewrite
+ * all relative URIs in import/url declarations to correctly point to
+ * the desired files. For this to work, the files *must* exist and be
+ * visible by the PHP process.
+ *
+ * 'symlinks': (default = array()) If the CSS file is stored in
+ * a symlink-ed directory, provide an array of link paths to
+ * target paths, where the link paths are within the document root. Because
+ * paths need to be normalized for this to work, use "//" to substitute
+ * the doc root in the link paths (the array keys). E.g.:
+ * <code>
+ * array('//symlink' => '/real/target/path') // unix
+ * array('//static' => 'D:\\staticStorage') // Windows
+ * </code>
+ *
+ * 'docRoot': (default = $_SERVER['DOCUMENT_ROOT'])
+ * see CssUriRewriter::rewrite
+ *
+ * @return string
+ */
+ public static function minify($css, $options = array())
+ {
+ $options = array_merge(array(
+ 'removeCharsets' => true,
+ 'preserveComments' => true,
+ 'currentDir' => null,
+ 'docRoot' => $_SERVER['DOCUMENT_ROOT'],
+ 'prependRelativePath' => null,
+ 'symlinks' => array(),
+ ), $options);
+
+ if ($options['removeCharsets']) {
+ $css = preg_replace('/@charset[^;]+;\\s*/', '', $css);
+ }
+ if (! $options['preserveComments']) {
+ $css = CssCompressor::process($css, $options);
+ } else {
+ $css = CommentPreserver::process(
+ $css
+ ,array('CssCompressor', 'process')
+ ,array($options)
+ );
+ }
+ if (! $options['currentDir'] && ! $options['prependRelativePath']) {
+ return $css;
+ }
+ if ($options['currentDir']) {
+ return CssUriRewriter::rewrite(
+ $css
+ ,$options['currentDir']
+ ,$options['docRoot']
+ ,$options['symlinks']
+ );
+ } else {
+ return CssUriRewriter::prepend(
+ $css
+ ,$options['prependRelativePath']
+ );
+ }
+ }
+}
+
+/**
+ * Class CommentPreserver
+ * @package Minify
+ */
+
+/**
+ * Process a string in pieces preserving C-style comments that begin with "/*!"
+ *
+ * @package Minify
+ * @author Stephen Clay <steve@mrclay.org>
+ */
+class CommentPreserver {
+
+ /**
+ * String to be prepended to each preserved comment
+ *
+ * @var string
+ */
+ public static $prepend = "\n";
+
+ /**
+ * String to be appended to each preserved comment
+ *
+ * @var string
+ */
+ public static $append = "\n";
+
+ /**
+ * Process a string outside of C-style comments that begin with "/*!"
+ *
+ * On each non-empty string outside these comments, the given processor
+ * function will be called. The comments will be surrounded by
+ * Minify_CommentPreserver::$preprend and Minify_CommentPreserver::$append.
+ *
+ * @param string $content
+ * @param callback $processor function
+ * @param array $args array of extra arguments to pass to the processor
+ * function (default = array())
+ * @return string
+ */
+ public static function process($content, $processor, $args = array())
+ {
+ $ret = '';
+ while (true) {
+ list($beforeComment, $comment, $afterComment) = self::_nextComment($content);
+ if ('' !== $beforeComment) {
+ $callArgs = $args;
+ array_unshift($callArgs, $beforeComment);
+ $ret .= call_user_func_array($processor, $callArgs);
+ }
+ if (false === $comment) {
+ break;
+ }
+ $ret .= $comment;
+ $content = $afterComment;
+ }
+ return $ret;
+ }
+
+ /**
+ * Extract comments that YUI Compressor preserves.
+ *
+ * @param string $in input
+ *
+ * @return array 3 elements are returned. If a YUI comment is found, the
+ * 2nd element is the comment and the 1st and 3rd are the surrounding
+ * strings. If no comment is found, the entire string is returned as the
+ * 1st element and the other two are false.
+ */
+ private static function _nextComment($in)
+ {
+ if (
+ false === ($start = strpos($in, '/*!'))
+ || false === ($end = strpos($in, '*/', $start + 3))
+ ) {
+ return array($in, false, false);
+ }
+ $ret = array(
+ substr($in, 0, $start)
+ ,self::$prepend . '/*!' . substr($in, $start + 3, $end - $start - 1) . self::$append
+ );
+ $endChars = (strlen($in) - $end - 2);
+ $ret[] = (0 === $endChars)
+ ? ''
+ : substr($in, -$endChars);
+ return $ret;
+ }
+}
View
289 assets/css/CssUriRewriter.php
@@ -0,0 +1,289 @@
+<?php
+/**
+ * Class CssUriRewriter
+ * @package scriptboost
+ */
+
+/**
+ * Rewrite file-relative URIs as root-relative in CSS files
+ *
+ * @author Stephen Clay <steve@mrclay.org>
+ */
+class CssUriRewriter {
+
+ /**
+ * rewrite() and rewriteRelative() append debugging information here
+ * @var string
+ */
+ public static $debugText = '';
+
+ /**
+ * In CSS content, rewrite file relative URIs as root relative
+ *
+ * @param string $css
+ *
+ * @param string $currentDir The directory of the current CSS file.
+ *
+ * @param string $docRoot The document root of the web site in which
+ * the CSS file resides (default = $_SERVER['DOCUMENT_ROOT']).
+ *
+ * @param array $symlinks (default = array()) If the CSS file is stored in
+ * a symlink-ed directory, provide an array of link paths to
+ * target paths, where the link paths are within the document root. Because
+ * paths need to be normalized for this to work, use "//" to substitute
+ * the doc root in the link paths (the array keys). E.g.:
+ * <code>
+ * array('//symlink' => '/real/target/path') // unix
+ * array('//static' => 'D:\\staticStorage') // Windows
+ * </code>
+ *
+ * @return string
+ */
+ public static function rewrite($css, $currentDir, $docRoot = null, $symlinks = array())
+ {
+ self::$_docRoot = self::_realpath(
+ $docRoot ? $docRoot : $_SERVER['DOCUMENT_ROOT']
+ );
+ self::$_currentDir = self::_realpath($currentDir);
+ self::$_symlinks = array();
+
+ // normalize symlinks
+ foreach ($symlinks as $link => $target) {
+ $link = ($link === '//')
+ ? self::$_docRoot
+ : str_replace('//', self::$_docRoot . '/', $link);
+ $link = strtr($link, '/', DIRECTORY_SEPARATOR);
+ self::$_symlinks[$link] = self::_realpath($target);
+ }
+
+ self::$debugText .= "docRoot : " . self::$_docRoot . "\n"
+ . "currentDir : " . self::$_currentDir . "\n";
+ if (self::$_symlinks) {
+ self::$debugText .= "symlinks : " . var_export(self::$_symlinks, 1) . "\n";
+ }
+ self::$debugText .= "\n";
+
+ $css = self::_trimUrls($css);
+
+ // rewrite
+ $css = preg_replace_callback('/@import\\s+([\'"])(.*?)[\'"]/'
+ ,array(self::$className, '_processUriCB'), $css);
+ $css = preg_replace_callback('/url\\(\\s*([^\\)\\s]+)\\s*\\)/'
+ ,array(self::$className, '_processUriCB'), $css);
+
+ return $css;
+ }
+
+ /**
+ * In CSS content, prepend a path to relative URIs
+ *
+ * @param string $css
+ *
+ * @param string $path The path to prepend.
+ *
+ * @return string
+ */
+ public static function prepend($css, $path)
+ {
+ self::$_prependPath = $path;
+
+ $css = self::_trimUrls($css);
+
+ // append
+ $css = preg_replace_callback('/@import\\s+([\'"])(.*?)[\'"]/'
+ ,array(self::$className, '_processUriCB'), $css);
+ $css = preg_replace_callback('/url\\(\\s*([^\\)\\s]+)\\s*\\)/'
+ ,array(self::$className, '_processUriCB'), $css);
+
+ self::$_prependPath = null;
+ return $css;
+ }
+
+ /**
+ * Get a root relative URI from a file relative URI
+ *
+ * <code>
+ * Minify_CSS_UriRewriter::rewriteRelative(
+ * '../img/hello.gif'
+ * , '/home/user/www/css' // path of CSS file
+ * , '/home/user/www' // doc root
+ * );
+ * // returns '/img/hello.gif'
+ *
+ * // example where static files are stored in a symlinked directory
+ * Minify_CSS_UriRewriter::rewriteRelative(
+ * 'hello.gif'
+ * , '/var/staticFiles/theme'
+ * , '/home/user/www'
+ * , array('/home/user/www/static' => '/var/staticFiles')
+ * );
+ * // returns '/static/theme/hello.gif'
+ * </code>
+ *
+ * @param string $uri file relative URI
+ *
+ * @param string $realCurrentDir realpath of the current file's directory.
+ *
+ * @param string $realDocRoot realpath of the site document root.
+ *
+ * @param array $symlinks (default = array()) If the file is stored in
+ * a symlink-ed directory, provide an array of link paths to
+ * real target paths, where the link paths "appear" to be within the document
+ * root. E.g.:
+ * <code>
+ * array('/home/foo/www/not/real/path' => '/real/target/path') // unix
+ * array('C:\\htdocs\\not\\real' => 'D:\\real\\target\\path') // Windows
+ * </code>
+ *
+ * @return string
+ */
+ public static function rewriteRelative($uri, $realCurrentDir, $realDocRoot, $symlinks = array())
+ {
+ // prepend path with current dir separator (OS-independent)
+ $path = strtr($realCurrentDir, '/', DIRECTORY_SEPARATOR)
+ . DIRECTORY_SEPARATOR . strtr($uri, '/', DIRECTORY_SEPARATOR);
+
+ self::$debugText .= "file-relative URI : {$uri}\n"
+ . "path prepended : {$path}\n";
+
+ // "unresolve" a symlink back to doc root
+ foreach ($symlinks as $link => $target) {
+ if (0 === strpos($path, $target)) {
+ // replace $target with $link
+ $path = $link . substr($path, strlen($target));
+
+ self::$debugText .= "symlink unresolved : {$path}\n";
+
+ break;
+ }
+ }
+ // strip doc root
+ $path = substr($path, strlen($realDocRoot));
+
+ self::$debugText .= "docroot stripped : {$path}\n";
+
+ // fix to root-relative URI
+
+ $uri = strtr($path, '/\\', '//');
+
+ $uri = self::removeDots($uri);
+
+ self::$debugText .= "traversals removed : {$uri}\n\n";
+
+ return $uri;
+ }
+
+ /**
+ * Remove instances of "./" and "../" where possible from a root-relative URI
+ * @param string $uri
+ * @return string
+ */
+ public static function removeDots($uri)
+ {
+ $uri = str_replace('/./', '/', $uri);
+ // inspired by patch from Oleg Cherniy
+ do {
+ $uri = preg_replace('@/[^/]+/\\.\\./@', '/', $uri, 1, $changed);
+ } while ($changed);
+ return $uri;
+ }
+
+ /**
+ * Defines which class to call as part of callbacks, change this
+ * if you extend Minify_CSS_UriRewriter
+ * @var string
+ */
+ protected static $className = 'CssUriRewriter';
+
+ /**
+ * Get realpath with any trailing slash removed. If realpath() fails,
+ * just remove the trailing slash.
+ *
+ * @param string $path
+ *
+ * @return mixed path with no trailing slash
+ */
+ protected static function _realpath($path)
+ {
+ $realPath = realpath($path);
+ if ($realPath !== false) {
+ $path = $realPath;
+ }
+ return rtrim($path, '/\\');
+ }
+
+ /**
+ * @var string directory of this stylesheet
+ */
+ private static $_currentDir = '';
+
+ /**
+ * @var string DOC_ROOT
+ */
+ private static $_docRoot = '';
+
+ /**
+ * @var array directory replacements to map symlink targets back to their
+ * source (within the document root) E.g. '/var/www/symlink' => '/var/realpath'
+ */
+ private static $_symlinks = array();
+
+ /**
+ * @var string path to prepend
+ */
+ private static $_prependPath = null;
+
+ private static function _trimUrls($css)
+ {
+ return preg_replace('/
+ url\\( # url(
+ \\s*
+ ([^\\)]+?) # 1 = URI (assuming does not contain ")")
+ \\s*
+ \\) # )
+ /x', 'url($1)', $css);
+ }
+
+ private static function _processUriCB($m)
+ {
+ // $m matched either '/@import\\s+([\'"])(.*?)[\'"]/' or '/url\\(\\s*([^\\)\\s]+)\\s*\\)/'
+ $isImport = ($m[0][0] === '@');
+ // determine URI and the quote character (if any)
+ if ($isImport) {
+ $quoteChar = $m[1];
+ $uri = $m[2];
+ } else {
+ // $m[1] is either quoted or not
+ $quoteChar = ($m[1][0] === "'" || $m[1][0] === '"')
+ ? $m[1][0]
+ : '';
+ $uri = ($quoteChar === '')
+ ? $m[1]
+ : substr($m[1], 1, strlen($m[1]) - 2);
+ }
+ // analyze URI
+ if ('/' !== $uri[0] // root-relative
+ && false === strpos($uri, '//') // protocol (non-data)
+ && 0 !== strpos($uri, 'data:') // data protocol
+ ) {
+ // URI is file-relative: rewrite depending on options
+ if (self::$_prependPath === null) {
+ $uri = self::rewriteRelative($uri, self::$_currentDir, self::$_docRoot, self::$_symlinks);
+ } else {
+ $uri = self::$_prependPath . $uri;
+ if ($uri[0] === '/') {
+ $root = '';
+ $rootRelative = $uri;
+ $uri = $root . self::removeDots($rootRelative);
+ } elseif (preg_match('@^((https?\:)?//([^/]+))/@', $uri, $m) && (false !== strpos($m[3], '.'))) {
+ $root = $m[1];
+ $rootRelative = substr($uri, strlen($root));
+ $uri = $root . self::removeDots($rootRelative);
+ }
+ }
+ }
+ return $isImport
+ ? "@import {$quoteChar}{$uri}{$quoteChar}"
+ : "url({$quoteChar}{$uri}{$quoteChar})";
+ }
+}
View
240 assets/html/HTMLMin.php
@@ -0,0 +1,240 @@
+<?php
+/**
+ * Compress HTML
+ *
+ * This is a heavy regex-based removal of whitespace, unnecessary comments and
+ * tokens. IE conditional comments are preserved. There are also options to have
+ * STYLE and SCRIPT blocks compressed by callback functions.
+ *
+ * A test suite is available.
+ *
+ * @package Minify
+ * @author Stephen Clay <steve@mrclay.org>
+ */
+class HTMLMin {
+
+ /**
+ * "Minify" an HTML page
+ *
+ * @param string $html
+ *
+ * @param array $options
+ *
+ * 'cssMinifier' : (optional) callback function to process content of STYLE
+ * elements.
+ *
+ * 'jsMinifier' : (optional) callback function to process content of SCRIPT
+ * elements. Note: the type attribute is ignored.
+ *
+ * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
+ * unset, minify will sniff for an XHTML doctype.
+ *
+ * @return string
+ */
+ public static function minify($html, $options = array()) {
+ $min = new HTMLMin($html, $options);
+ return $min->process();
+ }
+
+
+ /**
+ * Create a minifier object
+ *
+ * @param string $html
+ *
+ * @param array $options
+ *
+ * 'cssMinifier' : (optional) callback function to process content of STYLE
+ * elements.
+ *
+ * 'jsMinifier' : (optional) callback function to process content of SCRIPT
+ * elements. Note: the type attribute is ignored.
+ *
+ * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
+ * unset, minify will sniff for an XHTML doctype.
+ *
+ * @return null
+ */
+ public function __construct($html, $options = array())
+ {
+ $this->_html = str_replace("\r\n", "\n", trim($html));
+ if (isset($options['xhtml'])) {
+ $this->_isXhtml = (bool)$options['xhtml'];
+ }
+ if (isset($options['cssMinifier'])) {
+ $this->_cssMinifier = $options['cssMinifier'];
+ }
+ if (isset($options['jsMinifier'])) {
+ $this->_jsMinifier = $options['jsMinifier'];
+ }
+ }
+
+
+ /**
+ * Minify the markeup given in the constructor
+ *
+ * @return string
+ */
+ public function process()
+ {
+ if ($this->_isXhtml === null) {
+ $this->_isXhtml = (false !== strpos($this->_html, '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML'));
+ }
+
+ $this->_replacementHash = 'MINIFYHTML' . md5($_SERVER['REQUEST_TIME']);
+ $this->_placeholders = array();
+
+ // replace SCRIPTs (and minify) with placeholders
+ $this->_html = preg_replace_callback(
+ '/(\\s*)(<script\\b[^>]*?>)([\\s\\S]*?)<\\/script>(\\s*)/i'
+ ,array($this, '_removeScriptCB')
+ ,$this->_html);
+
+ // replace STYLEs (and minify) with placeholders
+ $this->_html = preg_replace_callback(
+ '/\\s*(<style\\b[^>]*?>)([\\s\\S]*?)<\\/style>\\s*/i'
+ ,array($this, '_removeStyleCB')
+ ,$this->_html);
+
+ // remove HTML comments (not containing IE conditional comments).
+ $this->_html = preg_replace_callback(
+ '/<!--([\\s\\S]*?)-->/'
+ ,array($this, '_commentCB')
+ ,$this->_html);
+
+ // replace PREs with placeholders
+ $this->_html = preg_replace_callback('/\\s*(<pre\\b[^>]*?>[\\s\\S]*?<\\/pre>)\\s*/i'
+ ,array($this, '_removePreCB')
+ ,$this->_html);
+
+ // replace TEXTAREAs with placeholders
+ $this->_html = preg_replace_callback(
+ '/\\s*(<textarea\\b[^>]*?>[\\s\\S]*?<\\/textarea>)\\s*/i'
+ ,array($this, '_removeTextareaCB')
+ ,$this->_html);
+
+ // trim each line.
+ // @todo take into account attribute values that span multiple lines.
+ $this->_html = preg_replace('/^\\s+|\\s+$/m', '', $this->_html);
+
+ // remove ws around block/undisplayed elements
+ $this->_html = preg_replace('/\\s+(<\\/?(?:area|base(?:font)?|blockquote|body'
+ .'|caption|center|cite|col(?:group)?|dd|dir|div|dl|dt|fieldset|form'
+ .'|frame(?:set)?|h[1-6]|head|hr|html|legend|li|link|map|menu|meta'
+ .'|ol|opt(?:group|ion)|p|param|t(?:able|body|head|d|h||r|foot|itle)'
+ .'|ul)\\b[^>]*>)/i', '$1', $this->_html);
+
+ // remove ws outside of all elements
+ $this->_html = preg_replace_callback(
+ '/>([^<]+)</'
+ ,array($this, '_outsideTagCB')
+ ,$this->_html);
+
+ // use newlines before 1st attribute in open tags (to limit line lengths)
+ $this->_html = preg_replace('/(<[a-z\\-]+)\\s+([^>]+>)/i', "$1\n$2", $this->_html);
+
+ // fill placeholders
+ $this->_html = str_replace(
+ array_keys($this->_placeholders)
+ ,array_values($this->_placeholders)
+ ,$this->_html
+ );
+ return $this->_html;
+ }
+
+ protected function _commentCB($m)
+ {
+ return (0 === strpos($m[1], '[') || false !== strpos($m[1], '<!['))
+ ? $m[0]
+ : '';
+ }
+
+ protected function _reservePlace($content)
+ {
+ $placeholder = '%' . $this->_replacementHash . count($this->_placeholders) . '%';
+ $this->_placeholders[$placeholder] = $content;
+ return $placeholder;
+ }
+
+ protected $_isXhtml = null;
+ protected $_replacementHash = null;
+ protected $_placeholders = array();
+ protected $_cssMinifier = null;
+ protected $_jsMinifier = null;
+
+ protected function _outsideTagCB($m)
+ {
+ return '>' . preg_replace('/^\\s+|\\s+$/', ' ', $m[1]) . '<';
+ }
+
+ protected function _removePreCB($m)
+ {
+ return $this->_reservePlace($m[1]);
+ }
+
+ protected function _removeTextareaCB($m)
+ {
+ return $this->_reservePlace($m[1]);
+ }
+
+ protected function _removeStyleCB($m)
+ {
+ $openStyle = $m[1];
+ $css = $m[2];
+ // remove HTML comments
+ $css = preg_replace('/(?:^\\s*<!--|-->\\s*$)/', '', $css);
+
+ // remove CDATA section markers
+ $css = $this->_removeCdata($css);
+
+ // minify
+ $minifier = $this->_cssMinifier
+ ? $this->_cssMinifier
+ : 'trim';
+ $css = call_user_func($minifier, $css);
+
+ return $this->_reservePlace($this->_needsCdata($css)
+ ? "{$openStyle}/*<![CDATA[*/{$css}/*]]>*/</style>"
+ : "{$openStyle}{$css}</style>"
+ );
+ }
+
+ protected function _removeScriptCB($m)
+ {
+ $openScript = $m[2];
+ $js = $m[3];
+
+ // whitespace surrounding? preserve at least one space
+ $ws1 = ($m[1] === '') ? '' : ' ';
+ $ws2 = ($m[4] === '') ? '' : ' ';
+
+ // remove HTML comments (and ending "//" if present)
+ $js = preg_replace('/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/', '', $js);
+
+ // remove CDATA section markers
+ $js = $this->_removeCdata($js);
+
+ // minify
+ $minifier = $this->_jsMinifier
+ ? $this->_jsMinifier
+ : 'trim';
+ $js = call_user_func($minifier, $js);
+
+ return $this->_reservePlace($this->_needsCdata($js)
+ ? "{$ws1}{$openScript}/*<![CDATA[*/{$js}/*]]>*/</script>{$ws2}"
+ : "{$ws1}{$openScript}{$js}</script>{$ws2}"
+ );
+ }
+
+ protected function _removeCdata($str)
+ {
+ return (false !== strpos($str, '<![CDATA['))
+ ? str_replace(array('<![CDATA[', ']]>'), '', $str)
+ : $str;
+ }
+
+ protected function _needsCdata($str)
+ {
+ return ($this->_isXhtml && preg_match('/(?:[<&]|\\-\\-|\\]\\]>)/', $str));
+ }
+}
View
359 assets/js/JSMin.php
@@ -0,0 +1,359 @@
+<?php
+/**
+ * JSMin.php - modified PHP implementation of Douglas Crockford's JSMin.
+ *
+ * <code>
+ * $minifiedJs = JSMin::minify($js);
+ * </code>
+ *
+ * This is a modified port of jsmin.c. Improvements:
+ *
+ * Does not choke on some regexp literals containing quote characters. E.g. /'/
+ *
+ * Spaces are preserved after some add/sub operators, so they are not mistakenly
+ * converted to post-inc/dec. E.g. a + ++b -> a+ ++b
+ *
+ * Preserves multi-line comments that begin with /*!
+ *
+ * PHP 5 or higher is required.
+ *
+ * Permission is hereby granted to use this version of the library under the
+ * same terms as jsmin.c, which has the following license:
+ *
+ * --
+ * Copyright (c) 2002 Douglas Crockford (www.crockford.com)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * The Software shall be used for Good, not Evil.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * --
+ *
+ * @package JSMin
+ * @author Ryan Grove <ryan@wonko.com> (PHP port)
+ * @author Steve Clay <steve@mrclay.org> (modifications + cleanup)
+ * @author Andrea Giammarchi <http://www.3site.eu> (spaceBeforeRegExp)
+ * @copyright 2002 Douglas Crockford <douglas@crockford.com> (jsmin.c)
+ * @copyright 2008 Ryan Grove <ryan@wonko.com> (PHP port)
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @link http://code.google.com/p/jsmin-php/
+ */
+
+class JSMin {
+ const ORD_LF = 10;
+ const ORD_SPACE = 32;
+ const ACTION_KEEP_A = 1;
+ const ACTION_DELETE_A = 2;
+ const ACTION_DELETE_A_B = 3;
+
+ protected $a = "\n";
+ protected $b = '';
+ protected $input = '';
+ protected $inputIndex = 0;
+ protected $inputLength = 0;
+ protected $lookAhead = null;
+ protected $output = '';
+ protected $lastByteOut = '';
+
+ /**
+ * Minify Javascript.
+ *
+ * @param string $js Javascript to be minified
+ * @return string
+ */
+ public static function minify($js)
+ {
+ $jsmin = new JSMin($js);
+ return $jsmin->min();
+ }
+
+ /**
+ * @param string $input
+ */
+ public function __construct($input)
+ {
+ $this->input = $input;
+ }
+
+ /**
+ * Perform minification, return result
+ */
+ public function min()
+ {
+ if ($this->output !== '') { // min already run
+ return $this->output;
+ }
+
+ $mbIntEnc = null;
+ if (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) {
+ $mbIntEnc = mb_internal_encoding();
+ mb_internal_encoding('8bit');
+ }
+ $this->input = str_replace("\r\n", "\n", $this->input);
+ $this->inputLength = strlen($this->input);
+
+ $this->action(self::ACTION_DELETE_A_B);
+
+ while ($this->a !== null) {
+ // determine next command
+ $command = self::ACTION_KEEP_A; // default
+ if ($this->a === ' ') {
+ if (($this->lastByteOut === '+' || $this->lastByteOut === '-')
+ && ($this->b === $this->lastByteOut)) {
+ // Don't delete this space. If we do, the addition/subtraction
+ // could be parsed as a post-increment
+ } elseif (! $this->isAlphaNum($this->b)) {
+ $command = self::ACTION_DELETE_A;
+ }
+ } elseif ($this->a === "\n") {
+ if ($this->b === ' ') {
+ $command = self::ACTION_DELETE_A_B;
+ // in case of mbstring.func_overload & 2, must check for null b,
+ // otherwise mb_strpos will give WARNING
+ } elseif ($this->b === null
+ || (false === strpos('{[(+-', $this->b)
+ && ! $this->isAlphaNum($this->b))) {
+ $command = self::ACTION_DELETE_A;
+ }
+ } elseif (! $this->isAlphaNum($this->a)) {
+ if ($this->b === ' '
+ || ($this->b === "\n"
+ && (false === strpos('}])+-"\'', $this->a)))) {
+ $command = self::ACTION_DELETE_A_B;
+ }
+ }
+ $this->action($command);
+ }
+ $this->output = trim($this->output);
+
+ if ($mbIntEnc !== null) {
+ mb_internal_encoding($mbIntEnc);
+ }
+ return $this->output;
+ }
+
+ /**
+ * ACTION_KEEP_A = Output A. Copy B to A. Get the next B.
+ * ACTION_DELETE_A = Copy B to A. Get the next B.
+ * ACTION_DELETE_A_B = Get the next B.
+ */
+ protected function action($command)
+ {
+ if ($command === self::ACTION_DELETE_A_B
+ && $this->b === ' '
+ && ($this->a === '+' || $this->a === '-')) {
+ // Note: we're at an addition/substraction operator; the inputIndex
+ // will certainly be a valid index
+ if ($this->input[$this->inputIndex] === $this->a) {
+ // This is "+ +" or "- -". Don't delete the space.
+ $command = self::ACTION_KEEP_A;
+ }
+ }
+ switch ($command) {
+ case self::ACTION_KEEP_A:
+ $this->output .= $this->a;
+ $this->lastByteOut = $this->a;
+
+ // fallthrough
+ case self::ACTION_DELETE_A:
+ $this->a = $this->b;
+ if ($this->a === "'" || $this->a === '"') { // string literal
+ $str = $this->a; // in case needed for exception
+ while (true) {
+ $this->output .= $this->a;
+ $this->lastByteOut = $this->a;
+
+ $this->a = $this->get();
+ if ($this->a === $this->b) { // end quote
+ break;
+ }
+ if (ord($this->a) <= self::ORD_LF) {
+ throw new JSMin_UnterminatedStringException(
+ "JSMin: Unterminated String at byte "
+ . $this->inputIndex . ": {$str}");
+ }
+ $str .= $this->a;
+ if ($this->a === '\\') {
+ $this->output .= $this->a;
+ $this->lastByteOut = $this->a;
+
+ $this->a = $this->get();
+ $str .= $this->a;
+ }
+ }
+ }
+ // fallthrough
+ case self::ACTION_DELETE_A_B:
+ $this->b = $this->next();
+ if ($this->b === '/' && $this->isRegexpLiteral()) { // RegExp literal
+ $this->output .= $this->a . $this->b;
+ $pattern = '/'; // in case needed for exception
+ while (true) {
+ $this->a = $this->get();
+ $pattern .= $this->a;
+ if ($this->a === '/') { // end pattern
+ break; // while (true)
+ } elseif ($this->a === '\\') {
+ $this->output .= $this->a;
+ $this->a = $this->get();
+ $pattern .= $this->a;
+ } elseif (ord($this->a) <= self::ORD_LF) {
+ throw new JSMin_UnterminatedRegExpException(
+ "JSMin: Unterminated RegExp at byte "
+ . $this->inputIndex .": {$pattern}");
+ }
+ $this->output .= $this->a;
+ $this->lastByteOut = $this->a;
+ }
+ $this->b = $this->next();
+ }
+ // end case ACTION_DELETE_A_B
+ }
+ }
+
+ protected function isRegexpLiteral()
+ {
+ if (false !== strpos("\n{;(,=:[!&|?", $this->a)) { // we aren't dividing
+ return true;
+ }
+ if (' ' === $this->a) {
+ $length = strlen($this->output);
+ if ($length < 2) { // weird edge case
+ return true;
+ }
+ // you can't divide a keyword
+ if (preg_match('/(?:case|else|in|return|typeof)$/', $this->output, $m)) {
+ if ($this->output === $m[0]) { // odd but could happen
+ return true;
+ }
+ // make sure it's a keyword, not end of an identifier
+ $charBeforeKeyword = substr($this->output, $length - strlen($m[0]) - 1, 1);
+ if (! $this->isAlphaNum($charBeforeKeyword)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Get next char. Convert ctrl char to space.
+ */
+ protected function get()
+ {
+ $c = $this->lookAhead;
+ $this->lookAhead = null;
+ if ($c === null) {
+ if ($this->inputIndex < $this->inputLength) {
+ $c = $this->input[$this->inputIndex];
+ $this->inputIndex += 1;
+ } else {
+ return null;
+ }
+ }
+ if ($c === "\r" || $c === "\n") {
+ return "\n";
+ }
+ if (ord($c) < self::ORD_SPACE) { // control char
+ return ' ';
+ }
+ return $c;
+ }
+
+ /**
+ * Get next char. If is ctrl character, translate to a space or newline.
+ */
+ protected function peek()
+ {
+ $this->lookAhead = $this->get();
+ return $this->lookAhead;
+ }
+
+ /**
+ * Is $c a letter, digit, underscore, dollar sign, escape, or non-ASCII?
+ */
+ protected function isAlphaNum($c)
+ {
+ return (preg_match('/^[0-9a-zA-Z_\\$\\\\]$/', $c) || ord($c) > 126);
+ }
+
+ protected function singleLineComment()
+ {
+ $comment = '';
+ while (true) {
+ $get = $this->get();
+ $comment .= $get;
+ if (ord($get) <= self::ORD_LF) { // EOL reached
+ // if IE conditional comment
+ if (preg_match('/^\\/@(?:cc_on|if|elif|else|end)\\b/', $comment)) {
+ return "/{$comment}";
+ }
+ return $get;
+ }
+ }
+ }
+
+ protected function multipleLineComment()
+ {
+ $this->get();
+ $comment = '';
+ while (true) {
+ $get = $this->get();
+ if ($get === '*') {
+ if ($this->peek() === '/') { // end of comment reached
+ $this->get();
+ // if comment preserved by YUI Compressor
+ if (0 === strpos($comment, '!')) {
+ return "\n/*!" . substr($comment, 1) . "*/\n";
+ }
+ // if IE conditional comment
+ if (preg_match('/^@(?:cc_on|if|elif|else|end)\\b/', $comment)) {
+ return "/*{$comment}*/";
+ }
+ return ' ';
+ }
+ } elseif ($get === null) {
+ throw new JSMin_UnterminatedCommentException(
+ "JSMin: Unterminated comment at byte "
+ . $this->inputIndex . ": /*{$comment}");
+ }
+ $comment .= $get;
+ }
+ }
+
+ /**
+ * Get the next character, skipping over comments.
+ * Some comments may be preserved.
+ */
+ protected function next()
+ {
+ $get = $this->get();
+ if ($get !== '/') {
+ return $get;
+ }
+ switch ($this->peek()) {
+ case '/': return $this->singleLineComment();
+ case '*': return $this->multipleLineComment();
+ default: return $get;
+ }
+ }
+}
+
+class JSMin_UnterminatedStringException extends Exception {}
+class JSMin_UnterminatedCommentException extends Exception {}
+class JSMin_UnterminatedRegExpException extends Exception {}
View
2,086 assets/js/JSMinPlus.php
@@ -0,0 +1,2086 @@
+<?php
+
+/**
+ * JSMinPlus version 1.4
+ *
+ * Minifies a javascript file using a javascript parser
+ *
+ * This implements a PHP port of Brendan Eich's Narcissus open source javascript engine (in javascript)
+ * References: http://en.wikipedia.org/wiki/Narcissus_(JavaScript_engine)
+ * Narcissus sourcecode: http://mxr.mozilla.org/mozilla/source/js/narcissus/
+ * JSMinPlus weblog: http://crisp.tweakblogs.net/blog/cat/716
+ *
+ * Tino Zijdel <crisp@tweakers.net>
+ *
+ * Usage: $minified = JSMinPlus::minify($script [, $filename])
+ *
+ * Versionlog (see also changelog.txt):
+ * 23-07-2011 - remove dynamic creation of OP_* and KEYWORD_* defines and declare them on top
+ * reduce memory footprint by minifying by block-scope
+ * some small byte-saving and performance improvements
+ * 12-05-2009 - fixed hook:colon precedence, fixed empty body in loop and if-constructs
+ * 18-04-2009 - fixed crashbug in PHP 5.2.9 and several other bugfixes
+ * 12-04-2009 - some small bugfixes and performance improvements
+ * 09-04-2009 - initial open sourced version 1.0
+ *
+ * Latest version of this script: http://files.tweakers.net/jsminplus/jsminplus.zip
+ *
+ */
+
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Narcissus JavaScript engine.
+ *
+ * The Initial Developer of the Original Code is
+ * Brendan Eich <brendan@mozilla.org>.
+ * Portions created by the Initial Developer are Copyright (C) 2004
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s): Tino Zijdel <crisp@tweakers.net>
+ * PHP port, modifications and minifier routine are (C) 2009-2011
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('TOKEN_END', 1);
+define('TOKEN_NUMBER', 2);
+define('TOKEN_IDENTIFIER', 3);
+define('TOKEN_STRING', 4);
+define('TOKEN_REGEXP', 5);
+define('TOKEN_NEWLINE', 6);
+define('TOKEN_CONDCOMMENT_START', 7);
+define('TOKEN_CONDCOMMENT_END', 8);
+
+define('JS_SCRIPT', 100);
+define('JS_BLOCK', 101);
+define('JS_LABEL', 102);
+define('JS_FOR_IN', 103);
+define('JS_CALL', 104);
+define('JS_NEW_WITH_ARGS', 105);
+define('JS_INDEX', 106);
+define('JS_ARRAY_INIT', 107);
+define('JS_OBJECT_INIT', 108);
+define('JS_PROPERTY_INIT', 109);
+define('JS_GETTER', 110);
+define('JS_SETTER', 111);
+define('JS_GROUP', 112);
+define('JS_LIST', 113);
+
+define('JS_MINIFIED', 999);
+
+define('DECLARED_FORM', 0);
+define('EXPRESSED_FORM', 1);
+define('STATEMENT_FORM', 2);
+
+/* Operators */
+define('OP_SEMICOLON', ';');
+define('OP_COMMA', ',');
+define('OP_HOOK', '?');
+define('OP_COLON', ':');
+define('OP_OR', '||');
+define('OP_AND', '&&');
+define('OP_BITWISE_OR', '|');
+define('OP_BITWISE_XOR', '^');
+define('OP_BITWISE_AND', '&');
+define('OP_STRICT_EQ', '===');
+define('OP_EQ', '==');
+define('OP_ASSIGN', '=');
+define('OP_STRICT_NE', '!==');
+define('OP_NE', '!=');
+define('OP_LSH', '<<');
+define('OP_LE', '<=');
+define('OP_LT', '<');
+define('OP_URSH', '>>>');
+define('OP_RSH', '>>');
+define('OP_GE', '>=');
+define('OP_GT', '>');
+define('OP_INCREMENT', '++');
+define('OP_DECREMENT', '--');
+define('OP_PLUS', '+');
+define('OP_MINUS', '-');
+define('OP_MUL', '*');
+define('OP_DIV', '/');
+define('OP_MOD', '%');
+define('OP_NOT', '!');
+define('OP_BITWISE_NOT', '~');
+define('OP_DOT', '.');
+define('OP_LEFT_BRACKET', '[');
+define('OP_RIGHT_BRACKET', ']');
+define('OP_LEFT_CURLY', '{');
+define('OP_RIGHT_CURLY', '}');
+define('OP_LEFT_PAREN', '(');
+define('OP_RIGHT_PAREN', ')');
+define('OP_CONDCOMMENT_END', '@*/');
+
+define('OP_UNARY_PLUS', 'U+');
+define('OP_UNARY_MINUS', 'U-');