├── .gitignore ├── system └── expressionengine │ └── third_party │ └── minimee │ ├── config.php │ ├── views │ ├── eedebug_panel.php │ └── settings_form.php │ ├── libraries │ ├── Minify │ │ ├── Loader.php │ │ ├── DebugDetector.php │ │ ├── Packer.php │ │ ├── Logger.php │ │ ├── Controller │ │ │ ├── Files.php │ │ │ ├── Page.php │ │ │ ├── Groups.php │ │ │ ├── Version1.php │ │ │ ├── Base.php │ │ │ └── MinApp.php │ │ ├── CommentPreserver.php │ │ ├── Build.php │ │ ├── Cache │ │ │ ├── APC.php │ │ │ ├── XCache.php │ │ │ ├── ZendPlatform.php │ │ │ ├── Memcache.php │ │ │ └── File.php │ │ ├── CSS.php │ │ ├── ClosureCompiler.php │ │ ├── JS │ │ │ └── ClosureCompiler.php │ │ ├── Lines.php │ │ ├── YUICompressor.php │ │ ├── Source.php │ │ ├── YUI │ │ │ └── CssCompressor.php │ │ ├── HTML │ │ │ └── Helper.php │ │ ├── ImportProcessor.php │ │ ├── CSS │ │ │ ├── Compressor.php │ │ │ └── UriRewriter.php │ │ └── HTML.php │ ├── EpiCurl.php │ └── HTTP │ │ ├── Encoder.php │ │ └── ConditionalGet.php │ ├── language │ └── english │ │ └── lang.minimee.php │ ├── classes │ └── Minimee_helper.php │ └── ext.minimee.php └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store -------------------------------------------------------------------------------- /system/expressionengine/third_party/minimee/config.php: -------------------------------------------------------------------------------- 1 | 2 | INFO:   Basic messages logged at each signficant point in process
3 | DEBUG:  Possible problem or event that may explain unexpected behaviour
4 | ERROR:  Minimee has failed, and this is why. 5 | 6 |
7 | 8 |
    9 | 10 | [' . $log[0] . ']
    ' . $log[1] . ''; 14 | endforeach; 15 | ?> 16 |
-------------------------------------------------------------------------------- /system/expressionengine/third_party/minimee/libraries/Minify/Loader.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class Minify_Loader { 14 | public function loadClass($class) 15 | { 16 | $file = dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR; 17 | $file .= strtr($class, "\\_", DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR) . '.php'; 18 | if (is_readable($file)) { 19 | require $file; 20 | } 21 | } 22 | 23 | static public function register() 24 | { 25 | $inst = new self(); 26 | spl_autoload_register(array($inst, 'loadClass')); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /system/expressionengine/third_party/minimee/libraries/Minify/DebugDetector.php: -------------------------------------------------------------------------------- 1 | 8 | */ 9 | class Minify_DebugDetector { 10 | public static function shouldDebugRequest($cookie, $get, $requestUri) 11 | { 12 | if (isset($get['debug'])) { 13 | return true; 14 | } 15 | if (! empty($cookie['minifyDebug'])) { 16 | foreach (preg_split('/\\s+/', $cookie['minifyDebug']) as $debugUri) { 17 | $pattern = '@' . preg_quote($debugUri, '@') . '@i'; 18 | $pattern = str_replace(array('\\*', '\\?'), array('.*', '.'), $pattern); 19 | if (preg_match($pattern, $requestUri)) { 20 | return true; 21 | } 22 | } 23 | } 24 | return false; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /system/expressionengine/third_party/minimee/libraries/Minify/Packer.php: -------------------------------------------------------------------------------- 1 | pack()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /system/expressionengine/third_party/minimee/libraries/Minify/Logger.php: -------------------------------------------------------------------------------- 1 | 12 | * 13 | * @todo lose this singleton! pass log object in Minify::serve and distribute to others 14 | */ 15 | class Minify_Logger { 16 | 17 | /** 18 | * Set logger object. 19 | * 20 | * The object should have a method "log" that accepts a value as 1st argument and 21 | * an optional string label as the 2nd. 22 | * 23 | * @param mixed $obj or a "falsey" value to disable 24 | * @return null 25 | */ 26 | public static function setLogger($obj = null) { 27 | self::$_logger = $obj 28 | ? $obj 29 | : null; 30 | } 31 | 32 | /** 33 | * Pass a message to the logger (if set) 34 | * 35 | * @param string $msg message to log 36 | * @return null 37 | */ 38 | public static function log($msg, $label = 'Minify') { 39 | if (! self::$_logger) return; 40 | self::$_logger->log($msg, $label); 41 | } 42 | 43 | /** 44 | * @var mixed logger object (like FirePHP) or null (i.e. no logger available) 45 | */ 46 | private static $_logger = null; 47 | } 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #Minimee2 2 | 3 | Minimize, combine & cache your CSS and JS files. Minify your HTML. Because size (still) DOES matter. 4 | 5 | * [Full Documentation](http://johndwells.github.com/Minimee) 6 | * [On @devot-ee](http://devot-ee.com/add-ons/minimee) 7 | * [Forum Support](http://devot-ee.com/add-ons/support/minimee/) 8 | 9 | 10 | # Description 11 | 12 | > "Minimee combines and compresses JavaScript and CSS files, thereby reducing file sizes and HTTP requests, and turning your puddle of online molasses into a digital fire hose."
- Stephen Lewis, founder, [Experience Internet](http://experienceinternet.co.uk/) 13 | 14 | Minimee watches your filesystem for changes to your CSS & JS assets, and automatically combines, minifies & caches these assets whenever changes are detected. It can also detect changes to stylesheet templates (whether saved as files or not). 15 | 16 | Version 2's substantial re-write has ushered in a host of changes big and small. It is the same Minimee you've come to rely on, with more power, intelligence, and fun-ness. 17 | 18 | Minimee is inspired and influenced by [SL Combinator](http://experienceinternet.co.uk/software/sl-combinator/) from Experience Internet, and [Carabiner Asset Management Library](http://codeigniter.com/wiki/Carabiner/) from Tony Dewan. 19 | 20 | 21 | ## Companion Add-Ons 22 | 23 | The architecture of Minimee2 has given me the opportunity to build other add-ons that extend Minimee's capabilities. So if you're curious, have a look at: 24 | 25 | * [MSMinimee](https://github.com/johndwells/MSMinimee) - a module that brings full MSM-compatibility to Minimee 26 | 27 | ## Want Bleeding Edge? 28 | 29 | The master branch will always contain the "stable" release of Minimee, where the version number should match what is listed on the [Full Documentation](http://johndwells.github.com/Minimee) page, as well as [on @devot-ee](http://devot-ee.com/add-ons/minimee) and on my [personal website](http://johndwells.com/software/minimee). 30 | 31 | To follow development of Version 2.x, refer to the [version2 branch](https://github.com/johndwells/Minimee/tree/version2). Note that version increments will always happen at the master branch first. -------------------------------------------------------------------------------- /system/expressionengine/third_party/minimee/libraries/Minify/Controller/Files.php: -------------------------------------------------------------------------------- 1 | 12 | * Minify::serve('Files', array( 13 | * 'files' => array( 14 | * '//js/jquery.js' 15 | * ,'//js/plugins.js' 16 | * ,'/home/username/file.js' 17 | * ) 18 | * )); 19 | * 20 | * 21 | * As a shortcut, the controller will replace "//" at the beginning 22 | * of a filename with $_SERVER['DOCUMENT_ROOT'] . '/'. 23 | * 24 | * @package Minify 25 | * @author Stephen Clay 26 | */ 27 | class Minify_Controller_Files extends Minify_Controller_Base { 28 | 29 | /** 30 | * Set up file sources 31 | * 32 | * @param array $options controller and Minify options 33 | * @return array Minify options 34 | * 35 | * Controller options: 36 | * 37 | * 'files': (required) array of complete file paths, or a single path 38 | */ 39 | public function setupSources($options) { 40 | // strip controller options 41 | 42 | $files = $options['files']; 43 | // if $files is a single object, casting will break it 44 | if (is_object($files)) { 45 | $files = array($files); 46 | } elseif (! is_array($files)) { 47 | $files = (array)$files; 48 | } 49 | unset($options['files']); 50 | 51 | $sources = array(); 52 | foreach ($files as $file) { 53 | if ($file instanceof Minify_Source) { 54 | $sources[] = $file; 55 | continue; 56 | } 57 | if (0 === strpos($file, '//')) { 58 | $file = $_SERVER['DOCUMENT_ROOT'] . substr($file, 1); 59 | } 60 | $realPath = realpath($file); 61 | if (is_file($realPath)) { 62 | $sources[] = new Minify_Source(array( 63 | 'filepath' => $realPath 64 | )); 65 | } else { 66 | $this->log("The path \"{$file}\" could not be found (or was not a file)"); 67 | return $options; 68 | } 69 | } 70 | if ($sources) { 71 | $this->sources = $sources; 72 | } 73 | return $options; 74 | } 75 | } 76 | 77 | -------------------------------------------------------------------------------- /system/expressionengine/third_party/minimee/libraries/Minify/Controller/Page.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | class Minify_Controller_Page extends Minify_Controller_Base { 15 | 16 | /** 17 | * Set up source of HTML content 18 | * 19 | * @param array $options controller and Minify options 20 | * @return array Minify options 21 | * 22 | * Controller options: 23 | * 24 | * 'content': (required) HTML markup 25 | * 26 | * 'id': (required) id of page (string for use in server-side caching) 27 | * 28 | * 'lastModifiedTime': timestamp of when this content changed. This 29 | * is recommended to allow both server and client-side caching. 30 | * 31 | * 'minifyAll': should all CSS and Javascript blocks be individually 32 | * minified? (default false) 33 | * 34 | * @todo Add 'file' option to read HTML file. 35 | */ 36 | public function setupSources($options) { 37 | if (isset($options['file'])) { 38 | $sourceSpec = array( 39 | 'filepath' => $options['file'] 40 | ); 41 | $f = $options['file']; 42 | } else { 43 | // strip controller options 44 | $sourceSpec = array( 45 | 'content' => $options['content'] 46 | ,'id' => $options['id'] 47 | ); 48 | $f = $options['id']; 49 | unset($options['content'], $options['id']); 50 | } 51 | // something like "builder,index.php" or "directory,file.html" 52 | $this->selectionId = strtr(substr($f, 1 + strlen(dirname(dirname($f)))), '/\\', ',,'); 53 | 54 | if (isset($options['minifyAll'])) { 55 | // this will be the 2nd argument passed to Minify_HTML::minify() 56 | $sourceSpec['minifyOptions'] = array( 57 | 'cssMinifier' => array('Minify_CSS', 'minify') 58 | ,'jsMinifier' => array('JSMin', 'minify') 59 | ); 60 | unset($options['minifyAll']); 61 | } 62 | $this->sources[] = new Minify_Source($sourceSpec); 63 | 64 | $options['contentType'] = Minify::TYPE_HTML; 65 | return $options; 66 | } 67 | } 68 | 69 | -------------------------------------------------------------------------------- /system/expressionengine/third_party/minimee/libraries/Minify/CommentPreserver.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class Minify_CommentPreserver { 14 | 15 | /** 16 | * String to be prepended to each preserved comment 17 | * 18 | * @var string 19 | */ 20 | public static $prepend = "\n"; 21 | 22 | /** 23 | * String to be appended to each preserved comment 24 | * 25 | * @var string 26 | */ 27 | public static $append = "\n"; 28 | 29 | /** 30 | * Process a string outside of C-style comments that begin with "/*!" 31 | * 32 | * On each non-empty string outside these comments, the given processor 33 | * function will be called. The comments will be surrounded by 34 | * Minify_CommentPreserver::$preprend and Minify_CommentPreserver::$append. 35 | * 36 | * @param string $content 37 | * @param callback $processor function 38 | * @param array $args array of extra arguments to pass to the processor 39 | * function (default = array()) 40 | * @return string 41 | */ 42 | public static function process($content, $processor, $args = array()) 43 | { 44 | $ret = ''; 45 | while (true) { 46 | list($beforeComment, $comment, $afterComment) = self::_nextComment($content); 47 | if ('' !== $beforeComment) { 48 | $callArgs = $args; 49 | array_unshift($callArgs, $beforeComment); 50 | $ret .= call_user_func_array($processor, $callArgs); 51 | } 52 | if (false === $comment) { 53 | break; 54 | } 55 | $ret .= $comment; 56 | $content = $afterComment; 57 | } 58 | return $ret; 59 | } 60 | 61 | /** 62 | * Extract comments that YUI Compressor preserves. 63 | * 64 | * @param string $in input 65 | * 66 | * @return array 3 elements are returned. If a YUI comment is found, the 67 | * 2nd element is the comment and the 1st and 3rd are the surrounding 68 | * strings. If no comment is found, the entire string is returned as the 69 | * 1st element and the other two are false. 70 | */ 71 | private static function _nextComment($in) 72 | { 73 | if ( 74 | false === ($start = strpos($in, '/*!')) 75 | || false === ($end = strpos($in, '*/', $start + 3)) 76 | ) { 77 | return array($in, false, false); 78 | } 79 | $ret = array( 80 | substr($in, 0, $start) 81 | ,self::$prepend . '/*!' . substr($in, $start + 3, $end - $start - 1) . self::$append 82 | ); 83 | $endChars = (strlen($in) - $end - 2); 84 | $ret[] = (0 === $endChars) 85 | ? '' 86 | : substr($in, -$endChars); 87 | return $ret; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /system/expressionengine/third_party/minimee/libraries/Minify/Controller/Groups.php: -------------------------------------------------------------------------------- 1 | 12 | * Minify::serve('Groups', array( 13 | * 'groups' => array( 14 | * 'css' => array('//css/type.css', '//css/layout.css') 15 | * ,'js' => array('//js/jquery.js', '//js/site.js') 16 | * ) 17 | * )); 18 | * 19 | * 20 | * If the above code were placed in /serve.php, it would enable the URLs 21 | * /serve.php/js and /serve.php/css 22 | * 23 | * As a shortcut, the controller will replace "//" at the beginning 24 | * of a filename with $_SERVER['DOCUMENT_ROOT'] . '/'. 25 | * 26 | * @package Minify 27 | * @author Stephen Clay 28 | */ 29 | class Minify_Controller_Groups extends Minify_Controller_Base { 30 | 31 | /** 32 | * Set up groups of files as sources 33 | * 34 | * @param array $options controller and Minify options 35 | * 36 | * 'groups': (required) array mapping PATH_INFO strings to arrays 37 | * of complete file paths. @see Minify_Controller_Groups 38 | * 39 | * @return array Minify options 40 | */ 41 | public function setupSources($options) { 42 | // strip controller options 43 | $groups = $options['groups']; 44 | unset($options['groups']); 45 | 46 | // mod_fcgid places PATH_INFO in ORIG_PATH_INFO 47 | $pi = isset($_SERVER['ORIG_PATH_INFO']) 48 | ? substr($_SERVER['ORIG_PATH_INFO'], 1) 49 | : (isset($_SERVER['PATH_INFO']) 50 | ? substr($_SERVER['PATH_INFO'], 1) 51 | : false 52 | ); 53 | if (false === $pi || ! isset($groups[$pi])) { 54 | // no PATH_INFO or not a valid group 55 | $this->log("Missing PATH_INFO or no group set for \"$pi\""); 56 | return $options; 57 | } 58 | $sources = array(); 59 | 60 | $files = $groups[$pi]; 61 | // if $files is a single object, casting will break it 62 | if (is_object($files)) { 63 | $files = array($files); 64 | } elseif (! is_array($files)) { 65 | $files = (array)$files; 66 | } 67 | foreach ($files as $file) { 68 | if ($file instanceof Minify_Source) { 69 | $sources[] = $file; 70 | continue; 71 | } 72 | if (0 === strpos($file, '//')) { 73 | $file = $_SERVER['DOCUMENT_ROOT'] . substr($file, 1); 74 | } 75 | $realPath = realpath($file); 76 | if (is_file($realPath)) { 77 | $sources[] = new Minify_Source(array( 78 | 'filepath' => $realPath 79 | )); 80 | } else { 81 | $this->log("The path \"{$file}\" could not be found (or was not a file)"); 82 | return $options; 83 | } 84 | } 85 | if ($sources) { 86 | $this->sources = $sources; 87 | } 88 | return $options; 89 | } 90 | } 91 | 92 | -------------------------------------------------------------------------------- /system/expressionengine/third_party/minimee/libraries/Minify/Build.php: -------------------------------------------------------------------------------- 1 | 12 | * // in config file 13 | * $groupSources = array( 14 | * 'js' => array('file1.js', 'file2.js') 15 | * ,'css' => array('file1.css', 'file2.css', 'file3.css') 16 | * ) 17 | * 18 | * // during HTML generation 19 | * $jsBuild = new Minify_Build($groupSources['js']); 20 | * $cssBuild = new Minify_Build($groupSources['css']); 21 | * 22 | * $script = ""; 24 | * $link = ""; 26 | * 27 | * // in min.php 28 | * Minify::serve('Groups', array( 29 | * 'groups' => $groupSources 30 | * ,'setExpires' => (time() + 86400 * 365) 31 | * )); 32 | * 33 | * 34 | * @package Minify 35 | * @author Stephen Clay 36 | */ 37 | class Minify_Build { 38 | 39 | /** 40 | * Last modification time of all files in the build 41 | * 42 | * @var int 43 | */ 44 | public $lastModified = 0; 45 | 46 | /** 47 | * String to use as ampersand in uri(). Set this to '&' if 48 | * you are not HTML-escaping URIs. 49 | * 50 | * @var string 51 | */ 52 | public static $ampersand = '&'; 53 | 54 | /** 55 | * Get a time-stamped URI 56 | * 57 | * 58 | * echo $b->uri('/site.js'); 59 | * // outputs "/site.js?1678242" 60 | * 61 | * echo $b->uri('/scriptaculous.js?load=effects'); 62 | * // outputs "/scriptaculous.js?load=effects&1678242" 63 | * 64 | * 65 | * @param string $uri 66 | * @param boolean $forceAmpersand (default = false) Force the use of ampersand to 67 | * append the timestamp to the URI. 68 | * @return string 69 | */ 70 | public function uri($uri, $forceAmpersand = false) { 71 | $sep = ($forceAmpersand || strpos($uri, '?') !== false) 72 | ? self::$ampersand 73 | : '?'; 74 | return "{$uri}{$sep}{$this->lastModified}"; 75 | } 76 | 77 | /** 78 | * Create a build object 79 | * 80 | * @param array $sources array of Minify_Source objects and/or file paths 81 | * 82 | * @return null 83 | */ 84 | public function __construct($sources) 85 | { 86 | $max = 0; 87 | foreach ((array)$sources as $source) { 88 | if ($source instanceof Minify_Source) { 89 | $max = max($max, $source->lastModified); 90 | } elseif (is_string($source)) { 91 | if (0 === strpos($source, '//')) { 92 | $source = $_SERVER['DOCUMENT_ROOT'] . substr($source, 1); 93 | } 94 | if (is_file($source)) { 95 | $max = max($max, filemtime($source)); 96 | } 97 | } 98 | } 99 | $this->lastModified = $max; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /system/expressionengine/third_party/minimee/libraries/Minify/Cache/APC.php: -------------------------------------------------------------------------------- 1 | 11 | * Minify::setCache(new Minify_Cache_APC()); 12 | * 13 | * 14 | * @package Minify 15 | * @author Chris Edwards 16 | **/ 17 | class Minify_Cache_APC { 18 | 19 | /** 20 | * Create a Minify_Cache_APC object, to be passed to 21 | * Minify::setCache(). 22 | * 23 | * 24 | * @param int $expire seconds until expiration (default = 0 25 | * meaning the item will not get an expiration date) 26 | * 27 | * @return null 28 | */ 29 | public function __construct($expire = 0) 30 | { 31 | $this->_exp = $expire; 32 | } 33 | 34 | /** 35 | * Write data to cache. 36 | * 37 | * @param string $id cache id 38 | * 39 | * @param string $data 40 | * 41 | * @return bool success 42 | */ 43 | public function store($id, $data) 44 | { 45 | return apc_store($id, "{$_SERVER['REQUEST_TIME']}|{$data}", $this->_exp); 46 | } 47 | 48 | /** 49 | * Get the size of a cache entry 50 | * 51 | * @param string $id cache id 52 | * 53 | * @return int size in bytes 54 | */ 55 | public function getSize($id) 56 | { 57 | if (! $this->_fetch($id)) { 58 | return false; 59 | } 60 | return (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) 61 | ? mb_strlen($this->_data, '8bit') 62 | : strlen($this->_data); 63 | } 64 | 65 | /** 66 | * Does a valid cache entry exist? 67 | * 68 | * @param string $id cache id 69 | * 70 | * @param int $srcMtime mtime of the original source file(s) 71 | * 72 | * @return bool exists 73 | */ 74 | public function isValid($id, $srcMtime) 75 | { 76 | return ($this->_fetch($id) && ($this->_lm >= $srcMtime)); 77 | } 78 | 79 | /** 80 | * Send the cached content to output 81 | * 82 | * @param string $id cache id 83 | */ 84 | public function display($id) 85 | { 86 | echo $this->_fetch($id) 87 | ? $this->_data 88 | : ''; 89 | } 90 | 91 | /** 92 | * Fetch the cached content 93 | * 94 | * @param string $id cache id 95 | * 96 | * @return string 97 | */ 98 | public function fetch($id) 99 | { 100 | return $this->_fetch($id) 101 | ? $this->_data 102 | : ''; 103 | } 104 | 105 | private $_exp = null; 106 | 107 | // cache of most recently fetched id 108 | private $_lm = null; 109 | private $_data = null; 110 | private $_id = null; 111 | 112 | /** 113 | * Fetch data and timestamp from apc, store in instance 114 | * 115 | * @param string $id 116 | * 117 | * @return bool success 118 | */ 119 | private function _fetch($id) 120 | { 121 | if ($this->_id === $id) { 122 | return true; 123 | } 124 | $ret = apc_fetch($id); 125 | if (false === $ret) { 126 | $this->_id = null; 127 | return false; 128 | } 129 | list($this->_lm, $this->_data) = explode('|', $ret, 2); 130 | $this->_id = $id; 131 | return true; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /system/expressionengine/third_party/minimee/libraries/Minify/Cache/XCache.php: -------------------------------------------------------------------------------- 1 | 14 | * Minify::setCache(new Minify_Cache_XCache()); 15 | * 16 | * 17 | * @package Minify 18 | * @author Elan Ruusamäe 19 | **/ 20 | class Minify_Cache_XCache { 21 | 22 | /** 23 | * Create a Minify_Cache_XCache object, to be passed to 24 | * Minify::setCache(). 25 | * 26 | * @param int $expire seconds until expiration (default = 0 27 | * meaning the item will not get an expiration date) 28 | */ 29 | public function __construct($expire = 0) 30 | { 31 | $this->_exp = $expire; 32 | } 33 | 34 | /** 35 | * Write data to cache. 36 | * 37 | * @param string $id cache id 38 | * @param string $data 39 | * @return bool success 40 | */ 41 | public function store($id, $data) 42 | { 43 | return xcache_set($id, "{$_SERVER['REQUEST_TIME']}|{$data}", $this->_exp); 44 | } 45 | 46 | /** 47 | * Get the size of a cache entry 48 | * 49 | * @param string $id cache id 50 | * @return int size in bytes 51 | */ 52 | public function getSize($id) 53 | { 54 | if (! $this->_fetch($id)) { 55 | return false; 56 | } 57 | return (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) 58 | ? mb_strlen($this->_data, '8bit') 59 | : strlen($this->_data); 60 | } 61 | 62 | /** 63 | * Does a valid cache entry exist? 64 | * 65 | * @param string $id cache id 66 | * @param int $srcMtime mtime of the original source file(s) 67 | * @return bool exists 68 | */ 69 | public function isValid($id, $srcMtime) 70 | { 71 | return ($this->_fetch($id) && ($this->_lm >= $srcMtime)); 72 | } 73 | 74 | /** 75 | * Send the cached content to output 76 | * 77 | * @param string $id cache id 78 | */ 79 | public function display($id) 80 | { 81 | echo $this->_fetch($id) 82 | ? $this->_data 83 | : ''; 84 | } 85 | 86 | /** 87 | * Fetch the cached content 88 | * 89 | * @param string $id cache id 90 | * @return string 91 | */ 92 | public function fetch($id) 93 | { 94 | return $this->_fetch($id) 95 | ? $this->_data 96 | : ''; 97 | } 98 | 99 | private $_exp = null; 100 | 101 | // cache of most recently fetched id 102 | private $_lm = null; 103 | private $_data = null; 104 | private $_id = null; 105 | 106 | /** 107 | * Fetch data and timestamp from xcache, store in instance 108 | * 109 | * @param string $id 110 | * @return bool success 111 | */ 112 | private function _fetch($id) 113 | { 114 | if ($this->_id === $id) { 115 | return true; 116 | } 117 | $ret = xcache_get($id); 118 | if (false === $ret) { 119 | $this->_id = null; 120 | return false; 121 | } 122 | list($this->_lm, $this->_data) = explode('|', $ret, 2); 123 | $this->_id = $id; 124 | return true; 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /system/expressionengine/third_party/minimee/libraries/Minify/Cache/ZendPlatform.php: -------------------------------------------------------------------------------- 1 | 14 | * Minify::setCache(new Minify_Cache_ZendPlatform()); 15 | * 16 | * 17 | * @package Minify 18 | * @author Patrick van Dissel 19 | */ 20 | class Minify_Cache_ZendPlatform { 21 | 22 | 23 | /** 24 | * Create a Minify_Cache_ZendPlatform object, to be passed to 25 | * Minify::setCache(). 26 | * 27 | * @param int $expire seconds until expiration (default = 0 28 | * meaning the item will not get an expiration date) 29 | * 30 | * @return null 31 | */ 32 | public function __construct($expire = 0) 33 | { 34 | $this->_exp = $expire; 35 | } 36 | 37 | 38 | /** 39 | * Write data to cache. 40 | * 41 | * @param string $id cache id 42 | * 43 | * @param string $data 44 | * 45 | * @return bool success 46 | */ 47 | public function store($id, $data) 48 | { 49 | return output_cache_put($id, "{$_SERVER['REQUEST_TIME']}|{$data}"); 50 | } 51 | 52 | 53 | /** 54 | * Get the size of a cache entry 55 | * 56 | * @param string $id cache id 57 | * 58 | * @return int size in bytes 59 | */ 60 | public function getSize($id) 61 | { 62 | return $this->_fetch($id) 63 | ? strlen($this->_data) 64 | : false; 65 | } 66 | 67 | 68 | /** 69 | * Does a valid cache entry exist? 70 | * 71 | * @param string $id cache id 72 | * 73 | * @param int $srcMtime mtime of the original source file(s) 74 | * 75 | * @return bool exists 76 | */ 77 | public function isValid($id, $srcMtime) 78 | { 79 | $ret = ($this->_fetch($id) && ($this->_lm >= $srcMtime)); 80 | return $ret; 81 | } 82 | 83 | 84 | /** 85 | * Send the cached content to output 86 | * 87 | * @param string $id cache id 88 | */ 89 | public function display($id) 90 | { 91 | echo $this->_fetch($id) 92 | ? $this->_data 93 | : ''; 94 | } 95 | 96 | 97 | /** 98 | * Fetch the cached content 99 | * 100 | * @param string $id cache id 101 | * 102 | * @return string 103 | */ 104 | public function fetch($id) 105 | { 106 | return $this->_fetch($id) 107 | ? $this->_data 108 | : ''; 109 | } 110 | 111 | 112 | private $_exp = null; 113 | 114 | 115 | // cache of most recently fetched id 116 | private $_lm = null; 117 | private $_data = null; 118 | private $_id = null; 119 | 120 | 121 | /** 122 | * Fetch data and timestamp from ZendPlatform, store in instance 123 | * 124 | * @param string $id 125 | * 126 | * @return bool success 127 | */ 128 | private function _fetch($id) 129 | { 130 | if ($this->_id === $id) { 131 | return true; 132 | } 133 | $ret = output_cache_get($id, $this->_exp); 134 | if (false === $ret) { 135 | $this->_id = null; 136 | return false; 137 | } 138 | list($this->_lm, $this->_data) = explode('|', $ret, 2); 139 | $this->_id = $id; 140 | return true; 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /system/expressionengine/third_party/minimee/libraries/Minify/Cache/Memcache.php: -------------------------------------------------------------------------------- 1 | 11 | * // fall back to disk caching if memcache can't connect 12 | * $memcache = new Memcache; 13 | * if ($memcache->connect('localhost', 11211)) { 14 | * Minify::setCache(new Minify_Cache_Memcache($memcache)); 15 | * } else { 16 | * Minify::setCache(); 17 | * } 18 | * 19 | **/ 20 | class Minify_Cache_Memcache { 21 | 22 | /** 23 | * Create a Minify_Cache_Memcache object, to be passed to 24 | * Minify::setCache(). 25 | * 26 | * @param Memcache $memcache already-connected instance 27 | * 28 | * @param int $expire seconds until expiration (default = 0 29 | * meaning the item will not get an expiration date) 30 | * 31 | * @return null 32 | */ 33 | public function __construct($memcache, $expire = 0) 34 | { 35 | $this->_mc = $memcache; 36 | $this->_exp = $expire; 37 | } 38 | 39 | /** 40 | * Write data to cache. 41 | * 42 | * @param string $id cache id 43 | * 44 | * @param string $data 45 | * 46 | * @return bool success 47 | */ 48 | public function store($id, $data) 49 | { 50 | return $this->_mc->set($id, "{$_SERVER['REQUEST_TIME']}|{$data}", 0, $this->_exp); 51 | } 52 | 53 | 54 | /** 55 | * Get the size of a cache entry 56 | * 57 | * @param string $id cache id 58 | * 59 | * @return int size in bytes 60 | */ 61 | public function getSize($id) 62 | { 63 | if (! $this->_fetch($id)) { 64 | return false; 65 | } 66 | return (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) 67 | ? mb_strlen($this->_data, '8bit') 68 | : strlen($this->_data); 69 | } 70 | 71 | /** 72 | * Does a valid cache entry exist? 73 | * 74 | * @param string $id cache id 75 | * 76 | * @param int $srcMtime mtime of the original source file(s) 77 | * 78 | * @return bool exists 79 | */ 80 | public function isValid($id, $srcMtime) 81 | { 82 | return ($this->_fetch($id) && ($this->_lm >= $srcMtime)); 83 | } 84 | 85 | /** 86 | * Send the cached content to output 87 | * 88 | * @param string $id cache id 89 | */ 90 | public function display($id) 91 | { 92 | echo $this->_fetch($id) 93 | ? $this->_data 94 | : ''; 95 | } 96 | 97 | /** 98 | * Fetch the cached content 99 | * 100 | * @param string $id cache id 101 | * 102 | * @return string 103 | */ 104 | public function fetch($id) 105 | { 106 | return $this->_fetch($id) 107 | ? $this->_data 108 | : ''; 109 | } 110 | 111 | private $_mc = null; 112 | private $_exp = null; 113 | 114 | // cache of most recently fetched id 115 | private $_lm = null; 116 | private $_data = null; 117 | private $_id = null; 118 | 119 | /** 120 | * Fetch data and timestamp from memcache, store in instance 121 | * 122 | * @param string $id 123 | * 124 | * @return bool success 125 | */ 126 | private function _fetch($id) 127 | { 128 | if ($this->_id === $id) { 129 | return true; 130 | } 131 | $ret = $this->_mc->get($id); 132 | if (false === $ret) { 133 | $this->_id = null; 134 | return false; 135 | } 136 | list($this->_lm, $this->_data) = explode('|', $ret, 2); 137 | $this->_id = $id; 138 | return true; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /system/expressionengine/third_party/minimee/libraries/Minify/CSS.php: -------------------------------------------------------------------------------- 1 | 15 | * @author http://code.google.com/u/1stvamp/ (Issue 64 patch) 16 | */ 17 | class Minify_CSS { 18 | 19 | /** 20 | * Minify a CSS string 21 | * 22 | * @param string $css 23 | * 24 | * @param array $options available options: 25 | * 26 | * 'preserveComments': (default true) multi-line comments that begin 27 | * with "/*!" will be preserved with newlines before and after to 28 | * enhance readability. 29 | * 30 | * 'removeCharsets': (default true) remove all @charset at-rules 31 | * 32 | * 'prependRelativePath': (default null) if given, this string will be 33 | * prepended to all relative URIs in import/url declarations 34 | * 35 | * 'currentDir': (default null) if given, this is assumed to be the 36 | * directory of the current CSS file. Using this, minify will rewrite 37 | * all relative URIs in import/url declarations to correctly point to 38 | * the desired files. For this to work, the files *must* exist and be 39 | * visible by the PHP process. 40 | * 41 | * 'symlinks': (default = array()) If the CSS file is stored in 42 | * a symlink-ed directory, provide an array of link paths to 43 | * target paths, where the link paths are within the document root. Because 44 | * paths need to be normalized for this to work, use "//" to substitute 45 | * the doc root in the link paths (the array keys). E.g.: 46 | * 47 | * array('//symlink' => '/real/target/path') // unix 48 | * array('//static' => 'D:\\staticStorage') // Windows 49 | * 50 | * 51 | * 'docRoot': (default = $_SERVER['DOCUMENT_ROOT']) 52 | * see Minify_CSS_UriRewriter::rewrite 53 | * 54 | * @return string 55 | */ 56 | public static function minify($css, $options = array()) 57 | { 58 | $options = array_merge(array( 59 | 'compress' => true, 60 | 'removeCharsets' => true, 61 | 'preserveComments' => true, 62 | 'currentDir' => null, 63 | 'docRoot' => $_SERVER['DOCUMENT_ROOT'], 64 | 'prependRelativePath' => null, 65 | 'symlinks' => array(), 66 | ), $options); 67 | 68 | if ($options['removeCharsets']) { 69 | $css = preg_replace('/@charset[^;]+;\\s*/', '', $css); 70 | } 71 | if ($options['compress']) { 72 | if (! $options['preserveComments']) { 73 | $css = Minify_CSS_Compressor::process($css, $options); 74 | } else { 75 | $css = Minify_CommentPreserver::process( 76 | $css 77 | ,array('Minify_CSS_Compressor', 'process') 78 | ,array($options) 79 | ); 80 | } 81 | } 82 | if (! $options['currentDir'] && ! $options['prependRelativePath']) { 83 | return $css; 84 | } 85 | if ($options['currentDir']) { 86 | return Minify_CSS_UriRewriter::rewrite( 87 | $css 88 | ,$options['currentDir'] 89 | ,$options['docRoot'] 90 | ,$options['symlinks'] 91 | ); 92 | } else { 93 | return Minify_CSS_UriRewriter::prepend( 94 | $css 95 | ,$options['prependRelativePath'] 96 | ); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /system/expressionengine/third_party/minimee/libraries/Minify/ClosureCompiler.php: -------------------------------------------------------------------------------- 1 | 16 | * Minify_ClosureCompiler::$jarFile = '/path/to/closure-compiler-20120123.jar'; 17 | * Minify_ClosureCompiler::$tempDir = '/tmp'; 18 | * $code = Minify_ClosureCompiler::minify( 19 | * $code, 20 | * array('compilation_level' => 'SIMPLE_OPTIMIZATIONS') 21 | * ); 22 | * 23 | * --compilation_level WHITESPACE_ONLY, SIMPLE_OPTIMIZATIONS, ADVANCED_OPTIMIZATIONS 24 | * 25 | * 26 | * 27 | * @todo unit tests, $options docs 28 | * @todo more options support (or should just passthru them all?) 29 | * 30 | * @package Minify 31 | * @author Stephen Clay 32 | * @author Elan Ruusamäe 33 | */ 34 | class Minify_ClosureCompiler { 35 | 36 | /** 37 | * Filepath of the Closure Compiler jar file. This must be set before 38 | * calling minifyJs(). 39 | * 40 | * @var string 41 | */ 42 | public static $jarFile = null; 43 | 44 | /** 45 | * Writable temp directory. This must be set before calling minifyJs(). 46 | * 47 | * @var string 48 | */ 49 | public static $tempDir = null; 50 | 51 | /** 52 | * Filepath of "java" executable (may be needed if not in shell's PATH) 53 | * 54 | * @var string 55 | */ 56 | public static $javaExecutable = 'java'; 57 | 58 | /** 59 | * Minify a Javascript string 60 | * 61 | * @param string $js 62 | * 63 | * @param array $options (verbose is ignored) 64 | * 65 | * @see https://code.google.com/p/closure-compiler/source/browse/trunk/README 66 | * 67 | * @return string 68 | */ 69 | public static function minify($js, $options = array()) 70 | { 71 | self::_prepare(); 72 | if (! ($tmpFile = tempnam(self::$tempDir, 'cc_'))) { 73 | throw new Exception('Minify_ClosureCompiler : could not create temp file.'); 74 | } 75 | file_put_contents($tmpFile, $js); 76 | exec(self::_getCmd($options, $tmpFile), $output, $result_code); 77 | unlink($tmpFile); 78 | if ($result_code != 0) { 79 | throw new Exception('Minify_ClosureCompiler : Closure Compiler execution failed.'); 80 | } 81 | return implode("\n", $output); 82 | } 83 | 84 | private static function _getCmd($userOptions, $tmpFile) 85 | { 86 | $o = array_merge( 87 | array( 88 | 'charset' => 'utf-8', 89 | 'compilation_level' => 'SIMPLE_OPTIMIZATIONS', 90 | ), 91 | $userOptions 92 | ); 93 | $cmd = self::$javaExecutable . ' -jar ' . escapeshellarg(self::$jarFile) 94 | . (preg_match('/^[\\da-zA-Z0-9\\-]+$/', $o['charset']) 95 | ? " --charset {$o['charset']}" 96 | : ''); 97 | 98 | foreach (array('compilation_level') as $opt) { 99 | if ($o[$opt]) { 100 | $cmd .= " --{$opt} ". escapeshellarg($o[$opt]); 101 | } 102 | } 103 | return $cmd . ' ' . escapeshellarg($tmpFile); 104 | } 105 | 106 | private static function _prepare() 107 | { 108 | if (! is_file(self::$jarFile)) { 109 | throw new Exception('Minify_ClosureCompiler : $jarFile('.self::$jarFile.') is not a valid file.'); 110 | } 111 | if (! is_readable(self::$jarFile)) { 112 | throw new Exception('Minify_ClosureCompiler : $jarFile('.self::$jarFile.') is not readable.'); 113 | } 114 | if (! is_dir(self::$tempDir)) { 115 | throw new Exception('Minify_ClosureCompiler : $tempDir('.self::$tempDir.') is not a valid direcotry.'); 116 | } 117 | if (! is_writable(self::$tempDir)) { 118 | throw new Exception('Minify_ClosureCompiler : $tempDir('.self::$tempDir.') is not writable.'); 119 | } 120 | } 121 | } 122 | 123 | /* vim:ts=4:sw=4:et */ 124 | -------------------------------------------------------------------------------- /system/expressionengine/third_party/minimee/libraries/Minify/Controller/Version1.php: -------------------------------------------------------------------------------- 1 | 11 | * Minify::serve('Version1'); 12 | * 13 | * 14 | * @package Minify 15 | * @author Stephen Clay 16 | */ 17 | class Minify_Controller_Version1 extends Minify_Controller_Base { 18 | 19 | /** 20 | * Set up groups of files as sources 21 | * 22 | * @param array $options controller and Minify options 23 | * @return array Minify options 24 | * 25 | */ 26 | public function setupSources($options) { 27 | // PHP insecure by default: realpath() and other FS functions can't handle null bytes. 28 | if (isset($_GET['files'])) { 29 | $_GET['files'] = str_replace("\x00", '', (string)$_GET['files']); 30 | } 31 | 32 | self::_setupDefines(); 33 | if (MINIFY_USE_CACHE) { 34 | $cacheDir = defined('MINIFY_CACHE_DIR') 35 | ? MINIFY_CACHE_DIR 36 | : ''; 37 | Minify::setCache($cacheDir); 38 | } 39 | $options['badRequestHeader'] = 'HTTP/1.0 404 Not Found'; 40 | $options['contentTypeCharset'] = MINIFY_ENCODING; 41 | 42 | // The following restrictions are to limit the URLs that minify will 43 | // respond to. Ideally there should be only one way to reference a file. 44 | if (! isset($_GET['files']) 45 | // verify at least one file, files are single comma separated, 46 | // and are all same extension 47 | || ! preg_match('/^[^,]+\\.(css|js)(,[^,]+\\.\\1)*$/', $_GET['files'], $m) 48 | // no "//" (makes URL rewriting easier) 49 | || strpos($_GET['files'], '//') !== false 50 | // no "\" 51 | || strpos($_GET['files'], '\\') !== false 52 | // no "./" 53 | || preg_match('/(?:^|[^\\.])\\.\\//', $_GET['files']) 54 | ) { 55 | return $options; 56 | } 57 | 58 | $files = explode(',', $_GET['files']); 59 | if (count($files) > MINIFY_MAX_FILES) { 60 | return $options; 61 | } 62 | 63 | // strings for prepending to relative/absolute paths 64 | $prependRelPaths = dirname($_SERVER['SCRIPT_FILENAME']) 65 | . DIRECTORY_SEPARATOR; 66 | $prependAbsPaths = $_SERVER['DOCUMENT_ROOT']; 67 | 68 | $goodFiles = array(); 69 | $hasBadSource = false; 70 | 71 | $allowDirs = isset($options['allowDirs']) 72 | ? $options['allowDirs'] 73 | : MINIFY_BASE_DIR; 74 | 75 | foreach ($files as $file) { 76 | // prepend appropriate string for abs/rel paths 77 | $file = ($file[0] === '/' ? $prependAbsPaths : $prependRelPaths) . $file; 78 | // make sure a real file! 79 | $file = realpath($file); 80 | // don't allow unsafe or duplicate files 81 | if (parent::_fileIsSafe($file, $allowDirs) 82 | && !in_array($file, $goodFiles)) 83 | { 84 | $goodFiles[] = $file; 85 | $srcOptions = array( 86 | 'filepath' => $file 87 | ); 88 | $this->sources[] = new Minify_Source($srcOptions); 89 | } else { 90 | $hasBadSource = true; 91 | break; 92 | } 93 | } 94 | if ($hasBadSource) { 95 | $this->sources = array(); 96 | } 97 | if (! MINIFY_REWRITE_CSS_URLS) { 98 | $options['rewriteCssUris'] = false; 99 | } 100 | return $options; 101 | } 102 | 103 | private static function _setupDefines() 104 | { 105 | $defaults = array( 106 | 'MINIFY_BASE_DIR' => realpath($_SERVER['DOCUMENT_ROOT']) 107 | ,'MINIFY_ENCODING' => 'utf-8' 108 | ,'MINIFY_MAX_FILES' => 16 109 | ,'MINIFY_REWRITE_CSS_URLS' => true 110 | ,'MINIFY_USE_CACHE' => true 111 | ); 112 | foreach ($defaults as $const => $val) { 113 | if (! defined($const)) { 114 | define($const, $val); 115 | } 116 | } 117 | } 118 | } 119 | 120 | -------------------------------------------------------------------------------- /system/expressionengine/third_party/minimee/libraries/Minify/JS/ClosureCompiler.php: -------------------------------------------------------------------------------- 1 | 13 | * 14 | * @todo can use a stream wrapper to unit test this? 15 | */ 16 | class Minify_JS_ClosureCompiler { 17 | const URL = 'http://closure-compiler.appspot.com/compile'; 18 | 19 | /** 20 | * Minify Javascript code via HTTP request to the Closure Compiler API 21 | * 22 | * @param string $js input code 23 | * @param array $options unused at this point 24 | * @return string 25 | */ 26 | public static function minify($js, array $options = array()) 27 | { 28 | $obj = new self($options); 29 | return $obj->min($js); 30 | } 31 | 32 | /** 33 | * 34 | * @param array $options 35 | * 36 | * fallbackFunc : default array($this, 'fallback'); 37 | */ 38 | public function __construct(array $options = array()) 39 | { 40 | $this->_fallbackFunc = isset($options['fallbackMinifier']) 41 | ? $options['fallbackMinifier'] 42 | : array($this, '_fallback'); 43 | } 44 | 45 | public function min($js) 46 | { 47 | $postBody = $this->_buildPostBody($js); 48 | $bytes = (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) 49 | ? mb_strlen($postBody, '8bit') 50 | : strlen($postBody); 51 | if ($bytes > 200000) { 52 | throw new Minify_JS_ClosureCompiler_Exception( 53 | 'POST content larger than 200000 bytes' 54 | ); 55 | } 56 | $response = $this->_getResponse($postBody); 57 | if (preg_match('/^Error\(\d\d?\):/', $response)) { 58 | if (is_callable($this->_fallbackFunc)) { 59 | $response = "/* Received errors from Closure Compiler API:\n$response" 60 | . "\n(Using fallback minifier)\n*/\n"; 61 | $response .= call_user_func($this->_fallbackFunc, $js); 62 | } else { 63 | throw new Minify_JS_ClosureCompiler_Exception($response); 64 | } 65 | } 66 | if ($response === '') { 67 | $errors = $this->_getResponse($this->_buildPostBody($js, true)); 68 | throw new Minify_JS_ClosureCompiler_Exception($errors); 69 | } 70 | return $response; 71 | } 72 | 73 | protected $_fallbackFunc = null; 74 | 75 | protected function _getResponse($postBody) 76 | { 77 | $allowUrlFopen = preg_match('/1|yes|on|true/i', ini_get('allow_url_fopen')); 78 | if ($allowUrlFopen) { 79 | $contents = file_get_contents(self::URL, false, stream_context_create(array( 80 | 'http' => array( 81 | 'method' => 'POST', 82 | 'header' => "Content-type: application/x-www-form-urlencoded\r\nConnection: close\r\n", 83 | 'content' => $postBody, 84 | 'max_redirects' => 0, 85 | 'timeout' => 15, 86 | ) 87 | ))); 88 | } elseif (defined('CURLOPT_POST')) { 89 | $ch = curl_init(self::URL); 90 | curl_setopt($ch, CURLOPT_POST, true); 91 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 92 | curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type: application/x-www-form-urlencoded')); 93 | curl_setopt($ch, CURLOPT_POSTFIELDS, $postBody); 94 | curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false); 95 | curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15); 96 | $contents = curl_exec($ch); 97 | curl_close($ch); 98 | } else { 99 | throw new Minify_JS_ClosureCompiler_Exception( 100 | "Could not make HTTP request: allow_url_open is false and cURL not available" 101 | ); 102 | } 103 | if (false === $contents) { 104 | throw new Minify_JS_ClosureCompiler_Exception( 105 | "No HTTP response from server" 106 | ); 107 | } 108 | return trim($contents); 109 | } 110 | 111 | protected function _buildPostBody($js, $returnErrors = false) 112 | { 113 | return http_build_query(array( 114 | 'js_code' => $js, 115 | 'output_info' => ($returnErrors ? 'errors' : 'compiled_code'), 116 | 'output_format' => 'text', 117 | 'compilation_level' => 'SIMPLE_OPTIMIZATIONS' 118 | ), null, '&'); 119 | } 120 | 121 | /** 122 | * Default fallback function if CC API fails 123 | * @param string $js 124 | * @return string 125 | */ 126 | protected function _fallback($js) 127 | { 128 | return JSMin::minify($js); 129 | } 130 | } 131 | 132 | class Minify_JS_ClosureCompiler_Exception extends Exception {} 133 | -------------------------------------------------------------------------------- /system/expressionengine/third_party/minimee/libraries/Minify/Lines.php: -------------------------------------------------------------------------------- 1 | 12 | * @author Adam Pedersen (Issue 55 fix) 13 | */ 14 | class Minify_Lines { 15 | 16 | /** 17 | * Add line numbers in C-style comments 18 | * 19 | * This uses a very basic parser easily fooled by comment tokens inside 20 | * strings or regexes, but, otherwise, generally clean code will not be 21 | * mangled. URI rewriting can also be performed. 22 | * 23 | * @param string $content 24 | * 25 | * @param array $options available options: 26 | * 27 | * 'id': (optional) string to identify file. E.g. file name/path 28 | * 29 | * 'currentDir': (default null) if given, this is assumed to be the 30 | * directory of the current CSS file. Using this, minify will rewrite 31 | * all relative URIs in import/url declarations to correctly point to 32 | * the desired files, and prepend a comment with debugging information about 33 | * this process. 34 | * 35 | * @return string 36 | */ 37 | public static function minify($content, $options = array()) 38 | { 39 | $id = (isset($options['id']) && $options['id']) 40 | ? $options['id'] 41 | : ''; 42 | $content = str_replace("\r\n", "\n", $content); 43 | 44 | // Hackily rewrite strings with XPath expressions that are 45 | // likely to throw off our dumb parser (for Prototype 1.6.1). 46 | $content = str_replace('"/*"', '"/"+"*"', $content); 47 | $content = preg_replace('@([\'"])(\\.?//?)\\*@', '$1$2$1+$1*', $content); 48 | 49 | $lines = explode("\n", $content); 50 | $numLines = count($lines); 51 | // determine left padding 52 | $padTo = strlen((string) $numLines); // e.g. 103 lines = 3 digits 53 | $inComment = false; 54 | $i = 0; 55 | $newLines = array(); 56 | while (null !== ($line = array_shift($lines))) { 57 | if (('' !== $id) && (0 == $i % 50)) { 58 | if ($inComment) { 59 | array_push($newLines, '', "/* {$id} *|", ''); 60 | } else { 61 | array_push($newLines, '', "/* {$id} */", ''); 62 | } 63 | } 64 | ++$i; 65 | $newLines[] = self::_addNote($line, $i, $inComment, $padTo); 66 | $inComment = self::_eolInComment($line, $inComment); 67 | } 68 | $content = implode("\n", $newLines) . "\n"; 69 | 70 | // check for desired URI rewriting 71 | if (isset($options['currentDir'])) { 72 | Minify_CSS_UriRewriter::$debugText = ''; 73 | $content = Minify_CSS_UriRewriter::rewrite( 74 | $content 75 | ,$options['currentDir'] 76 | ,isset($options['docRoot']) ? $options['docRoot'] : $_SERVER['DOCUMENT_ROOT'] 77 | ,isset($options['symlinks']) ? $options['symlinks'] : array() 78 | ); 79 | $content = "/* Minify_CSS_UriRewriter::\$debugText\n\n" 80 | . Minify_CSS_UriRewriter::$debugText . "*/\n" 81 | . $content; 82 | } 83 | 84 | return $content; 85 | } 86 | 87 | /** 88 | * Is the parser within a C-style comment at the end of this line? 89 | * 90 | * @param string $line current line of code 91 | * 92 | * @param bool $inComment was the parser in a comment at the 93 | * beginning of the line? 94 | * 95 | * @return bool 96 | */ 97 | private static function _eolInComment($line, $inComment) 98 | { 99 | // crude way to avoid things like // */ 100 | $line = preg_replace('~//.*?(\\*/|/\\*).*~', '', $line); 101 | 102 | while (strlen($line)) { 103 | $search = $inComment 104 | ? '*/' 105 | : '/*'; 106 | $pos = strpos($line, $search); 107 | if (false === $pos) { 108 | return $inComment; 109 | } else { 110 | if ($pos == 0 111 | || ($inComment 112 | ? substr($line, $pos, 3) 113 | : substr($line, $pos-1, 3)) != '*/*') 114 | { 115 | $inComment = ! $inComment; 116 | } 117 | $line = substr($line, $pos + 2); 118 | } 119 | } 120 | return $inComment; 121 | } 122 | 123 | /** 124 | * Prepend a comment (or note) to the given line 125 | * 126 | * @param string $line current line of code 127 | * 128 | * @param string $note content of note/comment 129 | * 130 | * @param bool $inComment was the parser in a comment at the 131 | * beginning of the line? 132 | * 133 | * @param int $padTo minimum width of comment 134 | * 135 | * @return string 136 | */ 137 | private static function _addNote($line, $note, $inComment, $padTo) 138 | { 139 | return $inComment 140 | ? '/* ' . str_pad($note, $padTo, ' ', STR_PAD_RIGHT) . ' *| ' . $line 141 | : '/* ' . str_pad($note, $padTo, ' ', STR_PAD_RIGHT) . ' */ ' . $line; 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /system/expressionengine/third_party/minimee/libraries/Minify/YUICompressor.php: -------------------------------------------------------------------------------- 1 | 16 | * Minify_YUICompressor::$jarFile = '/path/to/yuicompressor-2.4.6.jar'; 17 | * Minify_YUICompressor::$tempDir = '/tmp'; 18 | * $code = Minify_YUICompressor::minifyJs( 19 | * $code 20 | * ,array('nomunge' => true, 'line-break' => 1000) 21 | * ); 22 | * 23 | * 24 | * Note: In case you run out stack (default is 512k), you may increase stack size in $options: 25 | * array('stack-size' => '2048k') 26 | * 27 | * @todo unit tests, $options docs 28 | * 29 | * @package Minify 30 | * @author Stephen Clay 31 | */ 32 | class Minify_YUICompressor { 33 | 34 | /** 35 | * Filepath of the YUI Compressor jar file. This must be set before 36 | * calling minifyJs() or minifyCss(). 37 | * 38 | * @var string 39 | */ 40 | public static $jarFile = null; 41 | 42 | /** 43 | * Writable temp directory. This must be set before calling minifyJs() 44 | * or minifyCss(). 45 | * 46 | * @var string 47 | */ 48 | public static $tempDir = null; 49 | 50 | /** 51 | * Filepath of "java" executable (may be needed if not in shell's PATH) 52 | * 53 | * @var string 54 | */ 55 | public static $javaExecutable = 'java'; 56 | 57 | /** 58 | * Minify a Javascript string 59 | * 60 | * @param string $js 61 | * 62 | * @param array $options (verbose is ignored) 63 | * 64 | * @see http://www.julienlecomte.net/yuicompressor/README 65 | * 66 | * @return string 67 | */ 68 | public static function minifyJs($js, $options = array()) 69 | { 70 | return self::_minify('js', $js, $options); 71 | } 72 | 73 | /** 74 | * Minify a CSS string 75 | * 76 | * @param string $css 77 | * 78 | * @param array $options (verbose is ignored) 79 | * 80 | * @see http://www.julienlecomte.net/yuicompressor/README 81 | * 82 | * @return string 83 | */ 84 | public static function minifyCss($css, $options = array()) 85 | { 86 | return self::_minify('css', $css, $options); 87 | } 88 | 89 | private static function _minify($type, $content, $options) 90 | { 91 | self::_prepare(); 92 | if (! ($tmpFile = tempnam(self::$tempDir, 'yuic_'))) { 93 | throw new Exception('Minify_YUICompressor : could not create temp file.'); 94 | } 95 | file_put_contents($tmpFile, $content); 96 | exec(self::_getCmd($options, $type, $tmpFile), $output, $result_code); 97 | unlink($tmpFile); 98 | if ($result_code != 0) { 99 | throw new Exception('Minify_YUICompressor : YUI compressor execution failed.'); 100 | } 101 | return implode("\n", $output); 102 | } 103 | 104 | private static function _getCmd($userOptions, $type, $tmpFile) 105 | { 106 | $o = array_merge( 107 | array( 108 | 'charset' => '' 109 | ,'line-break' => 5000 110 | ,'type' => $type 111 | ,'nomunge' => false 112 | ,'preserve-semi' => false 113 | ,'disable-optimizations' => false 114 | ,'stack-size' => '' 115 | ) 116 | ,$userOptions 117 | ); 118 | $cmd = self::$javaExecutable 119 | . (!empty($o['stack-size']) 120 | ? ' -Xss' . $o['stack-size'] 121 | : '') 122 | . ' -jar ' . escapeshellarg(self::$jarFile) 123 | . " --type {$type}" 124 | . (preg_match('/^[\\da-zA-Z0-9\\-]+$/', $o['charset']) 125 | ? " --charset {$o['charset']}" 126 | : '') 127 | . (is_numeric($o['line-break']) && $o['line-break'] >= 0 128 | ? ' --line-break ' . (int)$o['line-break'] 129 | : ''); 130 | if ($type === 'js') { 131 | foreach (array('nomunge', 'preserve-semi', 'disable-optimizations') as $opt) { 132 | $cmd .= $o[$opt] 133 | ? " --{$opt}" 134 | : ''; 135 | } 136 | } 137 | return $cmd . ' ' . escapeshellarg($tmpFile); 138 | } 139 | 140 | private static function _prepare() 141 | { 142 | if (! is_file(self::$jarFile)) { 143 | throw new Exception('Minify_YUICompressor : $jarFile('.self::$jarFile.') is not a valid file.'); 144 | } 145 | if (! is_readable(self::$jarFile)) { 146 | throw new Exception('Minify_YUICompressor : $jarFile('.self::$jarFile.') is not readable.'); 147 | } 148 | if (! is_dir(self::$tempDir)) { 149 | throw new Exception('Minify_YUICompressor : $tempDir('.self::$tempDir.') is not a valid direcotry.'); 150 | } 151 | if (! is_writable(self::$tempDir)) { 152 | throw new Exception('Minify_YUICompressor : $tempDir('.self::$tempDir.') is not writable.'); 153 | } 154 | } 155 | } 156 | 157 | -------------------------------------------------------------------------------- /system/expressionengine/third_party/minimee/language/english/lang.minimee.php: -------------------------------------------------------------------------------- 1 | '`%s` is not a valid setting.', 10 | 'config_settings_manual_override' => 'Settings have been manually passed.', 11 | 'config_settings_using_defaults' => 'Could not find any settings to use. Trying defaults.', 12 | 'config_extension_manually_inject' => 'Manually injected into extension hooks.', 13 | 'config_settings_saved' => 'Settings have been saved in session cache. Settings came from: %s', 14 | 'config_sanitise_non_array' => 'Trying to sanitise a non-array of settings.', 15 | 'config_settings_from_config' => 'Settings taken from EE config.', 16 | 'config_settings_config_array_empty' => 'Settings taken from EE config must be a non-empty array.', 17 | 'config_settings_config_not_found' => 'No settings found in EE config.', 18 | 'config_settings_legacy_warning' => 'Your Minimee config is using the "legacy" setup from 1.x, please see docs for more.', 19 | 'config_settings_from_legacy' => 'Settings taken from EE config "legacy".', 20 | 'config_settings_from_db' => 'Settings retrieved from database.', 21 | 'config_settings_db_not_found' => 'No settings found in database.', 22 | 'config_settings_legacy_global_var_warning' => 'Minimee is using the "legacy" setup from 1.x, setting via the global vars, which has been deprecated. Please see docs for more.', 23 | 'config_settings_from_legacy' => 'Settings taken from EE global vars "legacy".', 24 | 'config_settings_legacy_not_found' => 'No settings found in EE global vars as "legacy" format.', 25 | 26 | // ------------------------------------------- 27 | // Extensions CP 28 | // ------------------------------------------- 29 | 'advanced_config' => 'Advanced Preferences', 30 | 'basic_config' => 'Basic Preferences', 31 | 'optional' => 'optional', 32 | 'config_location_warning' => 'Minimee appears to be configured elsewhere. There is likely no need to have this extension installed. Consult the docs for more.', 33 | 34 | 'save' => 'Save Settings', 35 | 'auto' => 'Auto', 36 | 'curl' => 'cURL', 37 | 'fgc' => 'file_get_contents()', 38 | 39 | 'sha1' => 'SHA-1', 40 | 'md5' => 'MD5', 41 | 'sanitize' => 'Sanitize', 42 | 43 | 'base_path' => 'Base Path', 44 | 'base_path_note' => 'The location on your webserver where your source CSS and JS files sit.
Optional, defaults to FCPATH constant (the root path to your site).', 45 | 'base_path_hint' => 'e.g. ' . rtrim(FCPATH, '/'), 46 | 47 | 'base_url' => 'Base URL', 48 | 'base_url_note' => 'The base URL from which your source CSS and JS files are served.
Optional, defaults to Site URL.', 49 | 'base_url_hint' => 'e.g. ' . rtrim(get_instance()->config->item('base_url'), '/'), 50 | 51 | 'cache_path' => 'Cache Path', 52 | 'cache_path_note' => 'Assumed to be absolute, but will also test as relative to the Base Path.
If left blank, will guess `cache`.', 53 | 'cache_path_hint' => 'e.g. ' . rtrim(FCPATH, '/') . '/cache', 54 | 55 | 'cache_url' => 'Cache URL', 56 | 'cache_url_note' => 'Assumed to be a fully qualified URL, but will also test as relative to the Base URL.
If left blank, will guess `cache`.', 57 | 'cache_url_hint' => 'e.g. ' . rtrim(get_instance()->config->item('base_url'), '/') . '/cache', 58 | 59 | 'cachebust' => 'Cache-Busting', 60 | 'cachebust_note' => 'Update this to a unique string to force Minimee to create a new cache file.
Optional, and for most scenarios unneccessary. Consult the docs for more.', 61 | 'cachebust_hint' => 'e.g. `1.0.0`.', 62 | 63 | 'cleanup' => 'Cleanup Expired Caches', 64 | 'cleanup_note' => 'Use with caution. When enabled, Minimee will automatically delete any cache file it determines has expired. Consult the docs for more.', 65 | 66 | 'combine' => 'Combine Assets', 67 | 'combine_note' => 'Specify which types of assets to combine.', 68 | 69 | 'css_prepend_mode' => 'CSS Prepend Mode', 70 | 'css_prepend_mode_note' => 'By default when minifying CSS, Minimee will rewrite relative image & @import URLs into absolute URLs. Turn OFF to skip this step.', 71 | 72 | 'css_prepend_url' => 'CSS Prepend URL', 73 | 'css_prepend_url_note' => 'The URL to use when `CSS Prepend Mode` is ON.
Optional, by default uses the Base URL.', 74 | 'css_prepend_url_hint' => 'e.g. ' . rtrim(get_instance()->config->item('base_url'), '/'), 75 | 76 | 'disable' => 'Disable Minimee entirely?', 77 | 78 | 'minify' => 'Minify Assets', 79 | 'minify_note' => 'Specify which types of assets to run through minification engine.
Note: HTML minification only available for EE2.4+', 80 | 81 | 'hash_method' => 'Filename Hash Algorithm', 82 | 'hash_method_note' => 'Choose which algorithm to create the cache filename.
`Sanitize` is only recommended during development; filenames will not exceed 200 characters in length.', 83 | 84 | 'remote_mode' => 'Remote file mode?', 85 | 'remote_mode_note' => 'Specify how to fetch remote and {stylesheet=} URLs. \'Auto\' mode will try cURL first.', 86 | 87 | 'css_library' => 'CSS Library', 88 | 'css_library_note' => 'Specify which library to use for CSS minification. Defaults to Minify.', 89 | 'minify' => 'Minify', 90 | 'cssmin' => 'CSSMin', 91 | 92 | 'js_library' => 'JS Library', 93 | 'js_library_note' => 'Specify which library to use for JS minification. Defaults to JSMin.', 94 | 'jsmin' => 'JSMin', 95 | 'jsminplus' => 'JSMinPlus', 96 | 97 | '' => '' 98 | ); -------------------------------------------------------------------------------- /system/expressionengine/third_party/minimee/libraries/Minify/Cache/File.php: -------------------------------------------------------------------------------- 1 | _locking = $fileLocking; 15 | $this->_path = $path; 16 | } 17 | 18 | /** 19 | * Write data to cache. 20 | * 21 | * @param string $id cache id (e.g. a filename) 22 | * 23 | * @param string $data 24 | * 25 | * @return bool success 26 | */ 27 | public function store($id, $data) 28 | { 29 | $flag = $this->_locking 30 | ? LOCK_EX 31 | : null; 32 | $file = $this->_path . '/' . $id; 33 | if (! @file_put_contents($file, $data, $flag)) { 34 | $this->_log("Minify_Cache_File: Write failed to '$file'"); 35 | } 36 | // write control 37 | if ($data !== $this->fetch($id)) { 38 | @unlink($file); 39 | $this->_log("Minify_Cache_File: Post-write read failed for '$file'"); 40 | return false; 41 | } 42 | return true; 43 | } 44 | 45 | /** 46 | * Get the size of a cache entry 47 | * 48 | * @param string $id cache id (e.g. a filename) 49 | * 50 | * @return int size in bytes 51 | */ 52 | public function getSize($id) 53 | { 54 | return filesize($this->_path . '/' . $id); 55 | } 56 | 57 | /** 58 | * Does a valid cache entry exist? 59 | * 60 | * @param string $id cache id (e.g. a filename) 61 | * 62 | * @param int $srcMtime mtime of the original source file(s) 63 | * 64 | * @return bool exists 65 | */ 66 | public function isValid($id, $srcMtime) 67 | { 68 | $file = $this->_path . '/' . $id; 69 | return (is_file($file) && (filemtime($file) >= $srcMtime)); 70 | } 71 | 72 | /** 73 | * Send the cached content to output 74 | * 75 | * @param string $id cache id (e.g. a filename) 76 | */ 77 | public function display($id) 78 | { 79 | if ($this->_locking) { 80 | $fp = fopen($this->_path . '/' . $id, 'rb'); 81 | flock($fp, LOCK_SH); 82 | fpassthru($fp); 83 | flock($fp, LOCK_UN); 84 | fclose($fp); 85 | } else { 86 | readfile($this->_path . '/' . $id); 87 | } 88 | } 89 | 90 | /** 91 | * Fetch the cached content 92 | * 93 | * @param string $id cache id (e.g. a filename) 94 | * 95 | * @return string 96 | */ 97 | public function fetch($id) 98 | { 99 | if ($this->_locking) { 100 | $fp = fopen($this->_path . '/' . $id, 'rb'); 101 | flock($fp, LOCK_SH); 102 | $ret = stream_get_contents($fp); 103 | flock($fp, LOCK_UN); 104 | fclose($fp); 105 | return $ret; 106 | } else { 107 | return file_get_contents($this->_path . '/' . $id); 108 | } 109 | } 110 | 111 | /** 112 | * Fetch the cache path used 113 | * 114 | * @return string 115 | */ 116 | public function getPath() 117 | { 118 | return $this->_path; 119 | } 120 | 121 | /** 122 | * Get a usable temp directory 123 | * 124 | * Adapted from Solar/Dir.php 125 | * @author Paul M. Jones 126 | * @license http://opensource.org/licenses/bsd-license.php BSD 127 | * @link http://solarphp.com/trac/core/browser/trunk/Solar/Dir.php 128 | * 129 | * @return string 130 | */ 131 | public static function tmp() 132 | { 133 | static $tmp = null; 134 | if (! $tmp) { 135 | $tmp = function_exists('sys_get_temp_dir') 136 | ? sys_get_temp_dir() 137 | : self::_tmp(); 138 | $tmp = rtrim($tmp, DIRECTORY_SEPARATOR); 139 | } 140 | return $tmp; 141 | } 142 | 143 | /** 144 | * Returns the OS-specific directory for temporary files 145 | * 146 | * @author Paul M. Jones 147 | * @license http://opensource.org/licenses/bsd-license.php BSD 148 | * @link http://solarphp.com/trac/core/browser/trunk/Solar/Dir.php 149 | * 150 | * @return string 151 | */ 152 | protected static function _tmp() 153 | { 154 | // non-Windows system? 155 | if (strtolower(substr(PHP_OS, 0, 3)) != 'win') { 156 | $tmp = empty($_ENV['TMPDIR']) ? getenv('TMPDIR') : $_ENV['TMPDIR']; 157 | if ($tmp) { 158 | return $tmp; 159 | } else { 160 | return '/tmp'; 161 | } 162 | } 163 | // Windows 'TEMP' 164 | $tmp = empty($_ENV['TEMP']) ? getenv('TEMP') : $_ENV['TEMP']; 165 | if ($tmp) { 166 | return $tmp; 167 | } 168 | // Windows 'TMP' 169 | $tmp = empty($_ENV['TMP']) ? getenv('TMP') : $_ENV['TMP']; 170 | if ($tmp) { 171 | return $tmp; 172 | } 173 | // Windows 'windir' 174 | $tmp = empty($_ENV['windir']) ? getenv('windir') : $_ENV['windir']; 175 | if ($tmp) { 176 | return $tmp; 177 | } 178 | // final fallback for Windows 179 | return getenv('SystemRoot') . '\\temp'; 180 | } 181 | 182 | /** 183 | * Send message to the Minify logger 184 | * @param string $msg 185 | * @return null 186 | */ 187 | protected function _log($msg) 188 | { 189 | Minify_Logger::log($msg); 190 | } 191 | 192 | private $_path = null; 193 | private $_locking = null; 194 | } 195 | -------------------------------------------------------------------------------- /system/expressionengine/third_party/minimee/libraries/Minify/Source.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | class Minify_Source { 17 | 18 | /** 19 | * @var int time of last modification 20 | */ 21 | public $lastModified = null; 22 | 23 | /** 24 | * @var callback minifier function specifically for this source. 25 | */ 26 | public $minifier = null; 27 | 28 | /** 29 | * @var array minification options specific to this source. 30 | */ 31 | public $minifyOptions = null; 32 | 33 | /** 34 | * @var string full path of file 35 | */ 36 | public $filepath = null; 37 | 38 | /** 39 | * @var string HTTP Content Type (Minify requires one of the constants Minify::TYPE_*) 40 | */ 41 | public $contentType = null; 42 | 43 | /** 44 | * Create a Minify_Source 45 | * 46 | * In the $spec array(), you can either provide a 'filepath' to an existing 47 | * file (existence will not be checked!) or give 'id' (unique string for 48 | * the content), 'content' (the string content) and 'lastModified' 49 | * (unixtime of last update). 50 | * 51 | * As a shortcut, the controller will replace "//" at the beginning 52 | * of a filepath with $_SERVER['DOCUMENT_ROOT'] . '/'. 53 | * 54 | * @param array $spec options 55 | */ 56 | public function __construct($spec) 57 | { 58 | if (isset($spec['filepath'])) { 59 | if (0 === strpos($spec['filepath'], '//')) { 60 | $spec['filepath'] = $_SERVER['DOCUMENT_ROOT'] . substr($spec['filepath'], 1); 61 | } 62 | $segments = explode('.', $spec['filepath']); 63 | $ext = strtolower(array_pop($segments)); 64 | switch ($ext) { 65 | case 'js' : $this->contentType = 'application/x-javascript'; 66 | break; 67 | case 'css' : $this->contentType = 'text/css'; 68 | break; 69 | case 'htm' : // fallthrough 70 | case 'html' : $this->contentType = 'text/html'; 71 | break; 72 | } 73 | $this->filepath = $spec['filepath']; 74 | $this->_id = $spec['filepath']; 75 | $this->lastModified = filemtime($spec['filepath']) 76 | // offset for Windows uploaders with out of sync clocks 77 | + round(Minify::$uploaderHoursBehind * 3600); 78 | } elseif (isset($spec['id'])) { 79 | $this->_id = 'id::' . $spec['id']; 80 | if (isset($spec['content'])) { 81 | $this->_content = $spec['content']; 82 | } else { 83 | $this->_getContentFunc = $spec['getContentFunc']; 84 | } 85 | $this->lastModified = isset($spec['lastModified']) 86 | ? $spec['lastModified'] 87 | : time(); 88 | } 89 | if (isset($spec['contentType'])) { 90 | $this->contentType = $spec['contentType']; 91 | } 92 | if (isset($spec['minifier'])) { 93 | $this->minifier = $spec['minifier']; 94 | } 95 | if (isset($spec['minifyOptions'])) { 96 | $this->minifyOptions = $spec['minifyOptions']; 97 | } 98 | } 99 | 100 | /** 101 | * Get content 102 | * 103 | * @return string 104 | */ 105 | public function getContent() 106 | { 107 | $content = (null !== $this->filepath) 108 | ? file_get_contents($this->filepath) 109 | : ((null !== $this->_content) 110 | ? $this->_content 111 | : call_user_func($this->_getContentFunc, $this->_id) 112 | ); 113 | // remove UTF-8 BOM if present 114 | return (pack("CCC",0xef,0xbb,0xbf) === substr($content, 0, 3)) 115 | ? substr($content, 3) 116 | : $content; 117 | } 118 | 119 | /** 120 | * Get id 121 | * 122 | * @return string 123 | */ 124 | public function getId() 125 | { 126 | return $this->_id; 127 | } 128 | 129 | /** 130 | * Verifies a single minification call can handle all sources 131 | * 132 | * @param array $sources Minify_Source instances 133 | * 134 | * @return bool true iff there no sources with specific minifier preferences. 135 | */ 136 | public static function haveNoMinifyPrefs($sources) 137 | { 138 | foreach ($sources as $source) { 139 | if (null !== $source->minifier 140 | || null !== $source->minifyOptions) { 141 | return false; 142 | } 143 | } 144 | return true; 145 | } 146 | 147 | /** 148 | * Get unique string for a set of sources 149 | * 150 | * @param array $sources Minify_Source instances 151 | * 152 | * @return string 153 | */ 154 | public static function getDigest($sources) 155 | { 156 | foreach ($sources as $source) { 157 | $info[] = array( 158 | $source->_id, $source->minifier, $source->minifyOptions 159 | ); 160 | } 161 | return md5(serialize($info)); 162 | } 163 | 164 | /** 165 | * Get content type from a group of sources 166 | * 167 | * This is called if the user doesn't pass in a 'contentType' options 168 | * 169 | * @param array $sources Minify_Source instances 170 | * 171 | * @return string content type. e.g. 'text/css' 172 | */ 173 | public static function getContentType($sources) 174 | { 175 | foreach ($sources as $source) { 176 | if ($source->contentType !== null) { 177 | return $source->contentType; 178 | } 179 | } 180 | return 'text/plain'; 181 | } 182 | 183 | protected $_content = null; 184 | protected $_getContentFunc = null; 185 | protected $_id = null; 186 | } 187 | 188 | -------------------------------------------------------------------------------- /system/expressionengine/third_party/minimee/libraries/EpiCurl.php: -------------------------------------------------------------------------------- 1 | mc = curl_multi_init(); 29 | $this->properties = array( 30 | 'code' => CURLINFO_HTTP_CODE, 31 | 'time' => CURLINFO_TOTAL_TIME, 32 | 'length'=> CURLINFO_CONTENT_LENGTH_DOWNLOAD, 33 | 'type' => CURLINFO_CONTENT_TYPE, 34 | 'url' => CURLINFO_EFFECTIVE_URL 35 | ); 36 | } 37 | 38 | public function addEasyCurl($ch) 39 | { 40 | $key = $this->getKey($ch); 41 | $this->requests[$key] = $ch; 42 | curl_setopt($ch, CURLOPT_HEADERFUNCTION, array($this, 'headerCallback')); 43 | $done = array('handle' => $ch); 44 | $this->storeResponse($done, false); 45 | $this->startTimer($key); 46 | return new EpiCurlManager($key); 47 | } 48 | 49 | public function addCurl($ch) 50 | { 51 | $key = $this->getKey($ch); 52 | $this->requests[$key] = $ch; 53 | curl_setopt($ch, CURLOPT_HEADERFUNCTION, array($this, 'headerCallback')); 54 | 55 | $code = curl_multi_add_handle($this->mc, $ch); 56 | $this->startTimer($key); 57 | 58 | // (1) 59 | if($code === CURLM_OK || $code === CURLM_CALL_MULTI_PERFORM) 60 | { 61 | do { 62 | $code = $this->execStatus = curl_multi_exec($this->mc, $this->running); 63 | } while ($this->execStatus === CURLM_CALL_MULTI_PERFORM); 64 | 65 | return new EpiCurlManager($key); 66 | } 67 | else 68 | { 69 | return $code; 70 | } 71 | } 72 | 73 | public function getResult($key = null) 74 | { 75 | if($key != null) 76 | { 77 | if(isset($this->responses[$key])) 78 | { 79 | return $this->responses[$key]; 80 | } 81 | 82 | $innerSleepInt = $outerSleepInt = 1; 83 | while($this->running && ($this->execStatus == CURLM_OK || $this->execStatus == CURLM_CALL_MULTI_PERFORM)) 84 | { 85 | usleep(intval($outerSleepInt)); 86 | $outerSleepInt = intval(max(1, ($outerSleepInt*$this->sleepIncrement))); 87 | $ms=curl_multi_select($this->mc, 0); 88 | if($ms > 0) 89 | { 90 | do{ 91 | $this->execStatus = curl_multi_exec($this->mc, $this->running); 92 | usleep(intval($innerSleepInt)); 93 | $innerSleepInt = intval(max(1, ($innerSleepInt*$this->sleepIncrement))); 94 | }while($this->execStatus==CURLM_CALL_MULTI_PERFORM); 95 | $innerSleepInt = 1; 96 | } 97 | $this->storeResponses(); 98 | if(isset($this->responses[$key]['data'])) 99 | { 100 | return $this->responses[$key]; 101 | } 102 | $runningCurrent = $this->running; 103 | } 104 | return null; 105 | } 106 | return false; 107 | } 108 | 109 | public static function getSequence() 110 | { 111 | return new EpiSequence(self::$timers); 112 | } 113 | 114 | public static function getTimers() 115 | { 116 | return self::$timers; 117 | } 118 | 119 | private function getKey($ch) 120 | { 121 | return (string)$ch; 122 | } 123 | 124 | private function headerCallback($ch, $header) 125 | { 126 | $_header = trim($header); 127 | $colonPos= strpos($_header, ':'); 128 | if($colonPos > 0) 129 | { 130 | $key = substr($_header, 0, $colonPos); 131 | $val = preg_replace('/^\W+/','',substr($_header, $colonPos)); 132 | $this->responses[$this->getKey($ch)]['headers'][$key] = $val; 133 | } 134 | return strlen($header); 135 | } 136 | 137 | private function storeResponses() 138 | { 139 | while($done = curl_multi_info_read($this->mc)) 140 | { 141 | $this->storeResponse($done); 142 | } 143 | } 144 | 145 | private function storeResponse($done, $isAsynchronous = true) 146 | { 147 | $key = $this->getKey($done['handle']); 148 | $this->stopTimer($key, $done); 149 | if($isAsynchronous) 150 | $this->responses[$key]['data'] = curl_multi_getcontent($done['handle']); 151 | else 152 | $this->responses[$key]['data'] = curl_exec($done['handle']); 153 | 154 | foreach($this->properties as $name => $const) 155 | { 156 | $this->responses[$key][$name] = curl_getinfo($done['handle'], $const); 157 | } 158 | if($isAsynchronous) 159 | curl_multi_remove_handle($this->mc, $done['handle']); 160 | curl_close($done['handle']); 161 | } 162 | 163 | private function startTimer($key) 164 | { 165 | self::$timers[$key]['start'] = microtime(true); 166 | } 167 | 168 | private function stopTimer($key, $done) 169 | { 170 | self::$timers[$key]['end'] = microtime(true); 171 | self::$timers[$key]['api'] = curl_getinfo($done['handle'], CURLINFO_EFFECTIVE_URL); 172 | self::$timers[$key]['time'] = curl_getinfo($done['handle'], CURLINFO_TOTAL_TIME); 173 | self::$timers[$key]['code'] = curl_getinfo($done['handle'], CURLINFO_HTTP_CODE); 174 | } 175 | 176 | static function getInstance() 177 | { 178 | if(self::$inst == null) 179 | { 180 | self::$singleton = 1; 181 | self::$inst = new EpiCurl(); 182 | } 183 | 184 | return self::$inst; 185 | } 186 | } 187 | 188 | class EpiCurlManager 189 | { 190 | private $key; 191 | private $epiCurl; 192 | 193 | public function __construct($key) 194 | { 195 | $this->key = $key; 196 | $this->epiCurl = EpiCurl::getInstance(); 197 | } 198 | 199 | public function __get($name) 200 | { 201 | $responses = $this->epiCurl->getResult($this->key); 202 | return isset($responses[$name]) ? $responses[$name] : null; 203 | } 204 | 205 | public function __isset($name) 206 | { 207 | $val = self::__get($name); 208 | return empty($val); 209 | } 210 | } 211 | 212 | /* 213 | * Credits: 214 | * - (1) Alistair pointed out that curl_multi_add_handle can return CURLM_CALL_MULTI_PERFORM on success. 215 | */ 216 | -------------------------------------------------------------------------------- /system/expressionengine/third_party/minimee/libraries/Minify/YUI/CssCompressor.php: -------------------------------------------------------------------------------- 1 | +\\(\\)\\],])@", "$1", $css); 53 | $css = str_replace("___PSEUDOCLASSCOLON___", ":", $css); 54 | 55 | // Remove the spaces after the things that should not have spaces after them. 56 | $css = preg_replace("@([!{}:;>+\\(\\[,])\\s+@", "$1", $css); 57 | 58 | // Add the semicolon where it's missing. 59 | $css = preg_replace("@([^;\\}])}@", "$1;}", $css); 60 | 61 | // Replace 0(px,em,%) with 0. 62 | $css = preg_replace("@([\\s:])(0)(px|em|%|in|cm|mm|pc|pt|ex)@", "$1$2", $css); 63 | 64 | // Replace 0 0 0 0; with 0. 65 | $css = str_replace(":0 0 0 0;", ":0;", $css); 66 | $css = str_replace(":0 0 0;", ":0;", $css); 67 | $css = str_replace(":0 0;", ":0;", $css); 68 | 69 | // Replace background-position:0; with background-position:0 0; 70 | $css = str_replace("background-position:0;", "background-position:0 0;", $css); 71 | 72 | // Replace 0.6 to .6, but only when preceded by : or a white-space 73 | $css = preg_replace("@(:|\\s)0+\\.(\\d+)@", "$1.$2", $css); 74 | 75 | // Shorten colors from rgb(51,102,153) to #336699 76 | // This makes it more likely that it'll get further compressed in the next step. 77 | $css = preg_replace_callback("@rgb\\s*\\(\\s*([0-9,\\s]+)\\s*\\)@", array($this, '_shortenRgbCB'), $css); 78 | 79 | // Shorten colors from #AABBCC to #ABC. Note that we want to make sure 80 | // the color is not preceded by either ", " or =. Indeed, the property 81 | // filter: chroma(color="#FFFFFF"); 82 | // would become 83 | // filter: chroma(color="#FFF"); 84 | // which makes the filter break in IE. 85 | $css = preg_replace_callback("@([^\"'=\\s])(\\s*)#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])@", array($this, '_shortenHexCB'), $css); 86 | 87 | // Remove empty rules. 88 | $css = preg_replace("@[^\\}]+\\{;\\}@", "", $css); 89 | 90 | $linebreakpos = isset($this->_options['linebreakpos']) 91 | ? $this->_options['linebreakpos'] 92 | : 0; 93 | 94 | if ($linebreakpos > 0) { 95 | // Some source control tools don't like it when files containing lines longer 96 | // than, say 8000 characters, are checked in. The linebreak option is used in 97 | // that case to split long lines after a specific column. 98 | $i = 0; 99 | $linestartpos = 0; 100 | $sb = $css; 101 | 102 | // make sure strlen returns byte count 103 | $mbIntEnc = null; 104 | if (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) { 105 | $mbIntEnc = mb_internal_encoding(); 106 | mb_internal_encoding('8bit'); 107 | } 108 | $sbLength = strlen($css); 109 | while ($i < $sbLength) { 110 | $c = $sb[$i++]; 111 | if ($c === '}' && $i - $linestartpos > $linebreakpos) { 112 | $sb = substr_replace($sb, "\n", $i, 0); 113 | $sbLength++; 114 | $linestartpos = $i; 115 | } 116 | } 117 | $css = $sb; 118 | 119 | // undo potential mb_encoding change 120 | if ($mbIntEnc !== null) { 121 | mb_internal_encoding($mbIntEnc); 122 | } 123 | } 124 | 125 | // Replace the pseudo class for the Box Model Hack 126 | $css = str_replace("___PSEUDOCLASSBMH___", "\"\\\\\"}\\\\\"\"", $css); 127 | 128 | // Replace multiple semi-colons in a row by a single one 129 | // See SF bug #1980989 130 | $css = preg_replace("@;;+@", ";", $css); 131 | 132 | // prevent triggering IE6 bug: http://www.crankygeek.com/ie6pebug/ 133 | $css = preg_replace('/:first-l(etter|ine)\\{/', ':first-l$1 {', $css); 134 | 135 | // Trim the final string (for any leading or trailing white spaces) 136 | $css = trim($css); 137 | 138 | return $css; 139 | } 140 | 141 | protected function _removeSpacesCB($m) 142 | { 143 | return str_replace(':', '___PSEUDOCLASSCOLON___', $m[0]); 144 | } 145 | 146 | protected function _shortenRgbCB($m) 147 | { 148 | $rgbcolors = explode(',', $m[1]); 149 | $hexcolor = '#'; 150 | for ($i = 0; $i < count($rgbcolors); $i++) { 151 | $val = round($rgbcolors[$i]); 152 | if ($val < 16) { 153 | $hexcolor .= '0'; 154 | } 155 | $hexcolor .= dechex($val); 156 | } 157 | return $hexcolor; 158 | } 159 | 160 | protected function _shortenHexCB($m) 161 | { 162 | // Test for AABBCC pattern 163 | if ((strtolower($m[3])===strtolower($m[4])) && 164 | (strtolower($m[5])===strtolower($m[6])) && 165 | (strtolower($m[7])===strtolower($m[8]))) { 166 | return $m[1] . $m[2] . "#" . $m[3] . $m[5] . $m[7]; 167 | } else { 168 | return $m[0]; 169 | } 170 | } 171 | } -------------------------------------------------------------------------------- /system/expressionengine/third_party/minimee/libraries/Minify/Controller/Base.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | abstract class Minify_Controller_Base { 18 | 19 | /** 20 | * Setup controller sources and set an needed options for Minify::source 21 | * 22 | * You must override this method in your subclass controller to set 23 | * $this->sources. If the request is NOT valid, make sure $this->sources 24 | * is left an empty array. Then strip any controller-specific options from 25 | * $options and return it. To serve files, $this->sources must be an array of 26 | * Minify_Source objects. 27 | * 28 | * @param array $options controller and Minify options 29 | * 30 | * @return array $options Minify::serve options 31 | */ 32 | abstract public function setupSources($options); 33 | 34 | /** 35 | * Get default Minify options for this controller. 36 | * 37 | * Override in subclass to change defaults 38 | * 39 | * @return array options for Minify 40 | */ 41 | public function getDefaultMinifyOptions() { 42 | return array( 43 | 'isPublic' => true 44 | ,'encodeOutput' => function_exists('gzdeflate') 45 | ,'encodeMethod' => null // determine later 46 | ,'encodeLevel' => 9 47 | ,'minifierOptions' => array() // no minifier options 48 | ,'contentTypeCharset' => 'utf-8' 49 | ,'maxAge' => 1800 // 30 minutes 50 | ,'rewriteCssUris' => true 51 | ,'bubbleCssImports' => false 52 | ,'quiet' => false // serve() will send headers and output 53 | ,'debug' => false 54 | 55 | // if you override these, the response codes MUST be directly after 56 | // the first space. 57 | ,'badRequestHeader' => 'HTTP/1.0 400 Bad Request' 58 | ,'errorHeader' => 'HTTP/1.0 500 Internal Server Error' 59 | 60 | // callback function to see/modify content of all sources 61 | ,'postprocessor' => null 62 | // file to require to load preprocessor 63 | ,'postprocessorRequire' => null 64 | ); 65 | } 66 | 67 | /** 68 | * Get default minifiers for this controller. 69 | * 70 | * Override in subclass to change defaults 71 | * 72 | * @return array minifier callbacks for common types 73 | */ 74 | public function getDefaultMinifers() { 75 | $ret[Minify::TYPE_JS] = array('JSMin', 'minify'); 76 | $ret[Minify::TYPE_CSS] = array('Minify_CSS', 'minify'); 77 | $ret[Minify::TYPE_HTML] = array('Minify_HTML', 'minify'); 78 | return $ret; 79 | } 80 | 81 | /** 82 | * Is a user-given file within an allowable directory, existing, 83 | * and having an extension js/css/html/txt ? 84 | * 85 | * This is a convenience function for controllers that have to accept 86 | * user-given paths 87 | * 88 | * @param string $file full file path (already processed by realpath()) 89 | * 90 | * @param array $safeDirs directories where files are safe to serve. Files can also 91 | * be in subdirectories of these directories. 92 | * 93 | * @return bool file is safe 94 | * 95 | * @deprecated use checkAllowDirs, checkNotHidden instead 96 | */ 97 | public static function _fileIsSafe($file, $safeDirs) 98 | { 99 | $pathOk = false; 100 | foreach ((array)$safeDirs as $safeDir) { 101 | if (strpos($file, $safeDir) === 0) { 102 | $pathOk = true; 103 | break; 104 | } 105 | } 106 | $base = basename($file); 107 | if (! $pathOk || ! is_file($file) || $base[0] === '.') { 108 | return false; 109 | } 110 | list($revExt) = explode('.', strrev($base)); 111 | return in_array(strrev($revExt), array('js', 'css', 'html', 'txt')); 112 | } 113 | 114 | /** 115 | * @param string $file 116 | * @param array $allowDirs 117 | * @param string $uri 118 | * @return bool 119 | * @throws Exception 120 | */ 121 | public static function checkAllowDirs($file, $allowDirs, $uri) 122 | { 123 | foreach ((array)$allowDirs as $allowDir) { 124 | if (strpos($file, $allowDir) === 0) { 125 | return true; 126 | } 127 | } 128 | throw new Exception("File '$file' is outside \$allowDirs. If the path is" 129 | . " resolved via an alias/symlink, look into the \$min_symlinks option." 130 | . " E.g. \$min_symlinks['/" . dirname($uri) . "'] = '" . dirname($file) . "';"); 131 | } 132 | 133 | /** 134 | * @param string $file 135 | * @throws Exception 136 | */ 137 | public static function checkNotHidden($file) 138 | { 139 | $b = basename($file); 140 | if (0 === strpos($b, '.')) { 141 | throw new Exception("Filename '$b' starts with period (may be hidden)"); 142 | } 143 | } 144 | 145 | /** 146 | * instances of Minify_Source, which provide content and any individual minification needs. 147 | * 148 | * @var array 149 | * 150 | * @see Minify_Source 151 | */ 152 | public $sources = array(); 153 | 154 | /** 155 | * Short name to place inside cache id 156 | * 157 | * The setupSources() method may choose to set this, making it easier to 158 | * recognize a particular set of sources/settings in the cache folder. It 159 | * will be filtered and truncated to make the final cache id <= 250 bytes. 160 | * 161 | * @var string 162 | */ 163 | public $selectionId = ''; 164 | 165 | /** 166 | * Mix in default controller options with user-given options 167 | * 168 | * @param array $options user options 169 | * 170 | * @return array mixed options 171 | */ 172 | public final function mixInDefaultOptions($options) 173 | { 174 | $ret = array_merge( 175 | $this->getDefaultMinifyOptions(), $options 176 | ); 177 | if (! isset($options['minifiers'])) { 178 | $options['minifiers'] = array(); 179 | } 180 | $ret['minifiers'] = array_merge( 181 | $this->getDefaultMinifers(), $options['minifiers'] 182 | ); 183 | return $ret; 184 | } 185 | 186 | /** 187 | * Analyze sources (if there are any) and set $options 'contentType' 188 | * and 'lastModifiedTime' if they already aren't. 189 | * 190 | * @param array $options options for Minify 191 | * 192 | * @return array options for Minify 193 | */ 194 | public final function analyzeSources($options = array()) 195 | { 196 | if ($this->sources) { 197 | if (! isset($options['contentType'])) { 198 | $options['contentType'] = Minify_Source::getContentType($this->sources); 199 | } 200 | // last modified is needed for caching, even if setExpires is set 201 | if (! isset($options['lastModifiedTime'])) { 202 | $max = 0; 203 | foreach ($this->sources as $source) { 204 | $max = max($source->lastModified, $max); 205 | } 206 | $options['lastModifiedTime'] = $max; 207 | } 208 | } 209 | return $options; 210 | } 211 | 212 | /** 213 | * Send message to the Minify logger 214 | * 215 | * @param string $msg 216 | * 217 | * @return null 218 | */ 219 | public function log($msg) { 220 | Minify_Logger::log($msg); 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /system/expressionengine/third_party/minimee/libraries/Minify/HTML/Helper.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class Minify_HTML_Helper { 14 | public $rewriteWorks = true; 15 | public $minAppUri = '/min'; 16 | public $groupsConfigFile = ''; 17 | 18 | /** 19 | * Get an HTML-escaped Minify URI for a group or set of files 20 | * 21 | * @param string|array $keyOrFiles a group key or array of filepaths/URIs 22 | * @param array $opts options: 23 | * 'farExpires' : (default true) append a modified timestamp for cache revving 24 | * 'debug' : (default false) append debug flag 25 | * 'charset' : (default 'UTF-8') for htmlspecialchars 26 | * 'minAppUri' : (default '/min') URI of min directory 27 | * 'rewriteWorks' : (default true) does mod_rewrite work in min app? 28 | * 'groupsConfigFile' : specify if different 29 | * @return string 30 | */ 31 | public static function getUri($keyOrFiles, $opts = array()) 32 | { 33 | $opts = array_merge(array( // default options 34 | 'farExpires' => true 35 | ,'debug' => false 36 | ,'charset' => 'UTF-8' 37 | ,'minAppUri' => '/min' 38 | ,'rewriteWorks' => true 39 | ,'groupsConfigFile' => '' 40 | ), $opts); 41 | $h = new self; 42 | $h->minAppUri = $opts['minAppUri']; 43 | $h->rewriteWorks = $opts['rewriteWorks']; 44 | $h->groupsConfigFile = $opts['groupsConfigFile']; 45 | if (is_array($keyOrFiles)) { 46 | $h->setFiles($keyOrFiles, $opts['farExpires']); 47 | } else { 48 | $h->setGroup($keyOrFiles, $opts['farExpires']); 49 | } 50 | $uri = $h->getRawUri($opts['farExpires'], $opts['debug']); 51 | return htmlspecialchars($uri, ENT_QUOTES, $opts['charset']); 52 | } 53 | 54 | /** 55 | * Get non-HTML-escaped URI to minify the specified files 56 | * 57 | * @param bool $farExpires 58 | * @param bool $debug 59 | * @return string 60 | */ 61 | public function getRawUri($farExpires = true, $debug = false) 62 | { 63 | $path = rtrim($this->minAppUri, '/') . '/'; 64 | if (! $this->rewriteWorks) { 65 | $path .= '?'; 66 | } 67 | if (null === $this->_groupKey) { 68 | // @todo: implement shortest uri 69 | $path = self::_getShortestUri($this->_filePaths, $path); 70 | } else { 71 | $path .= "g=" . $this->_groupKey; 72 | } 73 | if ($debug) { 74 | $path .= "&debug"; 75 | } elseif ($farExpires && $this->_lastModified) { 76 | $path .= "&" . $this->_lastModified; 77 | } 78 | return $path; 79 | } 80 | 81 | /** 82 | * Set the files that will comprise the URI we're building 83 | * 84 | * @param array $files 85 | * @param bool $checkLastModified 86 | */ 87 | public function setFiles($files, $checkLastModified = true) 88 | { 89 | $this->_groupKey = null; 90 | if ($checkLastModified) { 91 | $this->_lastModified = self::getLastModified($files); 92 | } 93 | // normalize paths like in /min/f= 94 | foreach ($files as $k => $file) { 95 | if (0 === strpos($file, '//')) { 96 | $file = substr($file, 2); 97 | } elseif (0 === strpos($file, '/') 98 | || 1 === strpos($file, ':\\')) { 99 | $file = substr($file, strlen($_SERVER['DOCUMENT_ROOT']) + 1); 100 | } 101 | $file = strtr($file, '\\', '/'); 102 | $files[$k] = $file; 103 | } 104 | $this->_filePaths = $files; 105 | } 106 | 107 | /** 108 | * Set the group of files that will comprise the URI we're building 109 | * 110 | * @param string $key 111 | * @param bool $checkLastModified 112 | */ 113 | public function setGroup($key, $checkLastModified = true) 114 | { 115 | $this->_groupKey = $key; 116 | if ($checkLastModified) { 117 | if (! $this->groupsConfigFile) { 118 | $this->groupsConfigFile = dirname(dirname(dirname(dirname(__FILE__)))) . '/groupsConfig.php'; 119 | } 120 | if (is_file($this->groupsConfigFile)) { 121 | $gc = (require $this->groupsConfigFile); 122 | $keys = explode(',', $key); 123 | foreach ($keys as $key) { 124 | if (isset($gc[$key])) { 125 | $this->_lastModified = self::getLastModified($gc[$key], $this->_lastModified); 126 | } 127 | } 128 | } 129 | } 130 | } 131 | 132 | /** 133 | * Get the max(lastModified) of all files 134 | * 135 | * @param array|string $sources 136 | * @param int $lastModified 137 | * @return int 138 | */ 139 | public static function getLastModified($sources, $lastModified = 0) 140 | { 141 | $max = $lastModified; 142 | foreach ((array)$sources as $source) { 143 | if (is_object($source) && isset($source->lastModified)) { 144 | $max = max($max, $source->lastModified); 145 | } elseif (is_string($source)) { 146 | if (0 === strpos($source, '//')) { 147 | $source = $_SERVER['DOCUMENT_ROOT'] . substr($source, 1); 148 | } 149 | if (is_file($source)) { 150 | $max = max($max, filemtime($source)); 151 | } 152 | } 153 | } 154 | return $max; 155 | } 156 | 157 | protected $_groupKey = null; // if present, URI will be like g=... 158 | protected $_filePaths = array(); 159 | protected $_lastModified = null; 160 | 161 | 162 | /** 163 | * In a given array of strings, find the character they all have at 164 | * a particular index 165 | * 166 | * @param array $arr array of strings 167 | * @param int $pos index to check 168 | * @return mixed a common char or '' if any do not match 169 | */ 170 | protected static function _getCommonCharAtPos($arr, $pos) { 171 | if (!isset($arr[0][$pos])) { 172 | return ''; 173 | } 174 | $c = $arr[0][$pos]; 175 | $l = count($arr); 176 | if ($l === 1) { 177 | return $c; 178 | } 179 | for ($i = 1; $i < $l; ++$i) { 180 | if ($arr[$i][$pos] !== $c) { 181 | return ''; 182 | } 183 | } 184 | return $c; 185 | } 186 | 187 | /** 188 | * Get the shortest URI to minify the set of source files 189 | * 190 | * @param array $paths root-relative URIs of files 191 | * @param string $minRoot root-relative URI of the "min" application 192 | * @return string 193 | */ 194 | protected static function _getShortestUri($paths, $minRoot = '/min/') { 195 | $pos = 0; 196 | $base = ''; 197 | while (true) { 198 | $c = self::_getCommonCharAtPos($paths, $pos); 199 | if ($c === '') { 200 | break; 201 | } else { 202 | $base .= $c; 203 | } 204 | ++$pos; 205 | } 206 | $base = preg_replace('@[^/]+$@', '', $base); 207 | $uri = $minRoot . 'f=' . implode(',', $paths); 208 | 209 | if (substr($base, -1) === '/') { 210 | // we have a base dir! 211 | $basedPaths = $paths; 212 | $l = count($paths); 213 | for ($i = 0; $i < $l; ++$i) { 214 | $basedPaths[$i] = substr($paths[$i], strlen($base)); 215 | } 216 | $base = substr($base, 0, strlen($base) - 1); 217 | $bUri = $minRoot . 'b=' . $base . '&f=' . implode(',', $basedPaths); 218 | 219 | $uri = strlen($uri) < strlen($bUri) 220 | ? $uri 221 | : $bUri; 222 | } 223 | return $uri; 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /system/expressionengine/third_party/minimee/libraries/Minify/ImportProcessor.php: -------------------------------------------------------------------------------- 1 | 19 | * @author Simon Schick 20 | */ 21 | class Minify_ImportProcessor { 22 | 23 | public static $filesIncluded = array(); 24 | 25 | public static function process($file) 26 | { 27 | self::$filesIncluded = array(); 28 | self::$_isCss = (strtolower(substr($file, -4)) === '.css'); 29 | $obj = new Minify_ImportProcessor(dirname($file)); 30 | return $obj->_getContent($file); 31 | } 32 | 33 | // allows callback funcs to know the current directory 34 | private $_currentDir = null; 35 | 36 | // allows callback funcs to know the directory of the file that inherits this one 37 | private $_previewsDir = null; 38 | 39 | // allows _importCB to write the fetched content back to the obj 40 | private $_importedContent = ''; 41 | 42 | private static $_isCss = null; 43 | 44 | /** 45 | * @param String $currentDir 46 | * @param String $previewsDir Is only used internally 47 | */ 48 | private function __construct($currentDir, $previewsDir = "") 49 | { 50 | $this->_currentDir = $currentDir; 51 | $this->_previewsDir = $previewsDir; 52 | } 53 | 54 | private function _getContent($file, $is_imported = false) 55 | { 56 | $file = realpath($file); 57 | if (! $file 58 | || in_array($file, self::$filesIncluded) 59 | || false === ($content = @file_get_contents($file)) 60 | ) { 61 | // file missing, already included, or failed read 62 | return ''; 63 | } 64 | self::$filesIncluded[] = realpath($file); 65 | $this->_currentDir = dirname($file); 66 | 67 | // remove UTF-8 BOM if present 68 | if (pack("CCC",0xef,0xbb,0xbf) === substr($content, 0, 3)) { 69 | $content = substr($content, 3); 70 | } 71 | // ensure uniform EOLs 72 | $content = str_replace("\r\n", "\n", $content); 73 | 74 | // process @imports 75 | $content = preg_replace_callback( 76 | '/ 77 | @import\\s+ 78 | (?:url\\(\\s*)? # maybe url( 79 | [\'"]? # maybe quote 80 | (.*?) # 1 = URI 81 | [\'"]? # maybe end quote 82 | (?:\\s*\\))? # maybe ) 83 | ([a-zA-Z,\\s]*)? # 2 = media list 84 | ; # end token 85 | /x' 86 | ,array($this, '_importCB') 87 | ,$content 88 | ); 89 | 90 | // You only need to rework the import-path if the script is imported 91 | if (self::$_isCss && $is_imported) { 92 | // rewrite remaining relative URIs 93 | $content = preg_replace_callback( 94 | '/url\\(\\s*([^\\)\\s]+)\\s*\\)/' 95 | ,array($this, '_urlCB') 96 | ,$content 97 | ); 98 | } 99 | 100 | return $this->_importedContent . $content; 101 | } 102 | 103 | private function _importCB($m) 104 | { 105 | $url = $m[1]; 106 | $mediaList = preg_replace('/\\s+/', '', $m[2]); 107 | 108 | if (strpos($url, '://') > 0) { 109 | // protocol, leave in place for CSS, comment for JS 110 | return self::$_isCss 111 | ? $m[0] 112 | : "/* Minify_ImportProcessor will not include remote content */"; 113 | } 114 | if ('/' === $url[0]) { 115 | // protocol-relative or root path 116 | $url = ltrim($url, '/'); 117 | $file = realpath($_SERVER['DOCUMENT_ROOT']) . DIRECTORY_SEPARATOR 118 | . strtr($url, '/', DIRECTORY_SEPARATOR); 119 | } else { 120 | // relative to current path 121 | $file = $this->_currentDir . DIRECTORY_SEPARATOR 122 | . strtr($url, '/', DIRECTORY_SEPARATOR); 123 | } 124 | $obj = new Minify_ImportProcessor(dirname($file), $this->_currentDir); 125 | $content = $obj->_getContent($file, true); 126 | if ('' === $content) { 127 | // failed. leave in place for CSS, comment for JS 128 | return self::$_isCss 129 | ? $m[0] 130 | : "/* Minify_ImportProcessor could not fetch '{$file}' */"; 131 | } 132 | return (!self::$_isCss || preg_match('@(?:^$|\\ball\\b)@', $mediaList)) 133 | ? $content 134 | : "@media {$mediaList} {\n{$content}\n}\n"; 135 | } 136 | 137 | private function _urlCB($m) 138 | { 139 | // $m[1] is either quoted or not 140 | $quote = ($m[1][0] === "'" || $m[1][0] === '"') 141 | ? $m[1][0] 142 | : ''; 143 | $url = ($quote === '') 144 | ? $m[1] 145 | : substr($m[1], 1, strlen($m[1]) - 2); 146 | if ('/' !== $url[0]) { 147 | if (strpos($url, '//') > 0) { 148 | // probably starts with protocol, do not alter 149 | } else { 150 | // prepend path with current dir separator (OS-independent) 151 | $path = $this->_currentDir 152 | . DIRECTORY_SEPARATOR . strtr($url, '/', DIRECTORY_SEPARATOR); 153 | // update the relative path by the directory of the file that imported this one 154 | $url = self::getPathDiff(realpath($this->_previewsDir), $path); 155 | } 156 | } 157 | return "url({$quote}{$url}{$quote})"; 158 | } 159 | 160 | /** 161 | * @param string $from 162 | * @param string $to 163 | * @param string $ps 164 | * @return string 165 | */ 166 | private function getPathDiff($from, $to, $ps = DIRECTORY_SEPARATOR) 167 | { 168 | $realFrom = $this->truepath($from); 169 | $realTo = $this->truepath($to); 170 | 171 | $arFrom = explode($ps, rtrim($realFrom, $ps)); 172 | $arTo = explode($ps, rtrim($realTo, $ps)); 173 | while (count($arFrom) && count($arTo) && ($arFrom[0] == $arTo[0])) 174 | { 175 | array_shift($arFrom); 176 | array_shift($arTo); 177 | } 178 | return str_pad("", count($arFrom) * 3, '..' . $ps) . implode($ps, $arTo); 179 | } 180 | 181 | /** 182 | * This function is to replace PHP's extremely buggy realpath(). 183 | * @param string $path The original path, can be relative etc. 184 | * @return string The resolved path, it might not exist. 185 | * @see http://stackoverflow.com/questions/4049856/replace-phps-realpath 186 | */ 187 | function truepath($path) 188 | { 189 | // whether $path is unix or not 190 | $unipath = strlen($path) == 0 || $path{0} != '/'; 191 | // attempts to detect if path is relative in which case, add cwd 192 | if (strpos($path, ':') === false && $unipath) 193 | $path = $this->_currentDir . DIRECTORY_SEPARATOR . $path; 194 | 195 | // resolve path parts (single dot, double dot and double delimiters) 196 | $path = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $path); 197 | $parts = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'strlen'); 198 | $absolutes = array(); 199 | foreach ($parts as $part) { 200 | if ('.' == $part) 201 | continue; 202 | if ('..' == $part) { 203 | array_pop($absolutes); 204 | } else { 205 | $absolutes[] = $part; 206 | } 207 | } 208 | $path = implode(DIRECTORY_SEPARATOR, $absolutes); 209 | // resolve any symlinks 210 | if (file_exists($path) && linkinfo($path) > 0) 211 | $path = readlink($path); 212 | // put initial separator that could have been lost 213 | $path = !$unipath ? '/' . $path : $path; 214 | return $path; 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /system/expressionengine/third_party/minimee/views/settings_form.php: -------------------------------------------------------------------------------- 1 |
' . $config_warning . '

'; 7 | } 8 | 9 | // some variables to use along the way 10 | $label = $setting = $hint = ''; 11 | $note_format = '%s'; 12 | $hint_format = '%s'; 13 | 14 | echo $form_open; 15 | 16 | /** 17 | * Disable 18 | */ 19 | $label = lang('disable', 'disable'); 20 | $setting = form_dropdown('disable', array('no' => lang('no'),'yes' => lang('yes')), $settings['disable'], 'id="disable"'); 21 | echo '

' . $label . '  ' . $setting . '

'; 22 | 23 | 24 | /** 25 | * begin our DOM wrapper 26 | */ 27 | echo '
'; 28 | 29 | 30 | /** 31 | * Open 'basic' table 32 | */ 33 | $this->table->set_template($cp_pad_table_template); 34 | $this->table->set_heading( 35 | array('data' => lang('basic_config'), 'style' => 'width:50%;'), 36 | lang('setting') 37 | ); 38 | 39 | 40 | /** 41 | * Cache Path 42 | */ 43 | $label = lang('cache_path', 'cache_path') . sprintf($note_format, lang('cache_path_note')); 44 | $setting = form_input(array('name' => 'cache_path', 'id' => 'cache_path', 'value' => $settings['cache_path'])) 45 | . sprintf($hint_format, lang('cache_path_hint')); 46 | $this->table->add_row($label, $setting); 47 | 48 | 49 | /** 50 | * Cache URL 51 | */ 52 | $label = lang('cache_url', 'cache_url') . sprintf($note_format, lang('cache_url_note')); 53 | $setting = form_input(array('name' => 'cache_url', 'id' => 'cache_url', 'value' => $settings['cache_url'])) 54 | . sprintf($hint_format, lang('cache_url_hint')); 55 | $this->table->add_row($label, $setting); 56 | 57 | 58 | /** 59 | * Combine settings 60 | */ 61 | $label = lang('combine', 'combine') . sprintf($note_format, lang('combine_note')); 62 | $setting = ''; 63 | $setting .= '     '; 64 | $this->table->add_row($label, $setting); 65 | 66 | 67 | /** 68 | * Minify settings 69 | */ 70 | $label = lang('minify', 'minify') . sprintf($note_format, lang('minify_note')); 71 | $setting = ''; 72 | $setting .= '     '; 73 | $setting .= '     '; 74 | $this->table->add_row($label, $setting); 75 | 76 | 77 | /** 78 | * Spit our our 'basic' table 79 | */ 80 | echo $this->table->generate(); 81 | $this->table->clear(); 82 | 83 | 84 | /** 85 | * Begin building our 'advanced' table 86 | */ 87 | $this->table->set_template($cp_pad_table_template); 88 | $this->table->set_heading( 89 | array('data' => lang('advanced_config'), 'style' => 'width:50%;'), 90 | lang('setting') 91 | ); 92 | 93 | 94 | /** 95 | * Base Path 96 | */ 97 | $label = lang('base_path', 'base_path') . sprintf($note_format, lang('base_path_note')); 98 | $setting = form_input(array('name' => 'base_path', 'id' => 'base_path', 'value' => $settings['base_path'])) 99 | . sprintf($hint_format, lang('base_path_hint')); 100 | $this->table->add_row($label, $setting); 101 | 102 | 103 | /** 104 | * Base URL 105 | */ 106 | $label = lang('base_url', 'base_url') . sprintf($note_format, lang('base_url_note')); 107 | $setting = form_input(array('name' => 'base_url', 'id' => 'base_url', 'value' => $settings['base_url'])) 108 | . sprintf($hint_format, lang('base_url_hint')); 109 | $this->table->add_row($label, $setting); 110 | 111 | 112 | /** 113 | * Cachebust 114 | */ 115 | $label = lang('cachebust', 'cachebust') . sprintf($note_format, lang('cachebust_note')); 116 | $setting = form_input(array('name' => 'cachebust', 'id' => 'cachebust', 'value' => $settings['cachebust'])) 117 | . sprintf($hint_format, lang('cachebust_hint')); 118 | $this->table->add_row($label, $setting); 119 | 120 | 121 | /** 122 | * Cleanup 123 | */ 124 | $label = lang('cleanup', 'cleanup') . sprintf($note_format, lang('cleanup_note')); 125 | $setting = form_dropdown('cleanup', array('no' => lang('No'),'yes' => lang('Yes')), $settings['cleanup'], 'id="cleanup"'); 126 | $this->table->add_row($label, $setting); 127 | 128 | 129 | /** 130 | * Filename Hash 131 | */ 132 | $label = lang('hash_method', 'hash_method') . sprintf($note_format, lang('hash_method_note')); 133 | $setting = form_dropdown('hash_method', array('sha1' => lang('sha1'),'md5' => lang('md5'), 'sanitize' => lang('sanitize')), $settings['hash_method'], 'id="hash_method"'); 134 | $this->table->add_row($label, $setting); 135 | 136 | 137 | /** 138 | * CSS Prepend Mode 139 | */ 140 | $label = lang('css_prepend_mode', 'css_prepend_mode') . sprintf($note_format, lang('css_prepend_mode_note')); 141 | $setting = form_dropdown('css_prepend_mode', array('yes' => lang('On'),'no' => lang('Off')), $settings['css_prepend_mode'], 'id="css_prepend_mode"'); 142 | $this->table->add_row($label, $setting); 143 | 144 | 145 | /** 146 | * CSS Prepend URL 147 | */ 148 | $label = lang('css_prepend_url', 'css_prepend_url') . sprintf($note_format, lang('css_prepend_url_note')); 149 | $setting = form_input(array('name' => 'css_prepend_url', 'id' => 'css_prepend_url', 'value' => $settings['css_prepend_url'])) 150 | . sprintf($hint_format, lang('css_prepend_url_hint')); 151 | $this->table->add_row($label, $setting); 152 | 153 | 154 | /** 155 | * CSS Library 156 | */ 157 | $label = lang('css_library', 'css_library') . sprintf($note_format, lang('css_library_note')); 158 | $setting = form_dropdown('css_library', array('minify' => lang('minify'),'cssmin' => lang('cssmin')), $settings['css_library'], 'id="css_library"'); 159 | $this->table->add_row($label, $setting); 160 | 161 | 162 | /** 163 | * JS Library 164 | */ 165 | $label = lang('js_library', 'js_library') . sprintf($note_format, lang('js_library_note')); 166 | $setting = form_dropdown('js_library', array('jsmin' => lang('jsmin'),'jsminplus' => lang('jsminplus')), $settings['js_library'], 'id="js_library"'); 167 | $this->table->add_row($label, $setting); 168 | 169 | 170 | /** 171 | * Remote mode 172 | */ 173 | $label = lang('remote_mode', 'remote_mode') . sprintf($note_format, lang('remote_mode_note')); 174 | $setting = form_dropdown('remote_mode', array('auto' => lang('auto'),'curl' => lang('curl'),'fgc' => lang('fgc')), $settings['remote_mode'], 'id="remote_mode"'); 175 | $this->table->add_row($label, $setting); 176 | 177 | 178 | /** 179 | * Spit out our advanced table 180 | */ 181 | ?> 182 |

()

183 |
table->generate(); ?>
184 | '; 191 | 192 | 193 | /** 194 | * finish form 195 | */ 196 | echo '

' . form_submit('submit', lang('save'), 'class="submit"') . '

'; 197 | $this->table->clear(); 198 | echo form_close(); 199 | ?> 200 | 201 | 252 | 253 | 19 | * @author http://code.google.com/u/1stvamp/ (Issue 64 patch) 20 | */ 21 | class Minify_CSS_Compressor { 22 | 23 | /** 24 | * Minify a CSS string 25 | * 26 | * @param string $css 27 | * 28 | * @param array $options (currently ignored) 29 | * 30 | * @return string 31 | */ 32 | public static function process($css, $options = array()) 33 | { 34 | $obj = new Minify_CSS_Compressor($options); 35 | return $obj->_process($css); 36 | } 37 | 38 | /** 39 | * @var array 40 | */ 41 | protected $_options = null; 42 | 43 | /** 44 | * Are we "in" a hack? I.e. are some browsers targetted until the next comment? 45 | * 46 | * @var bool 47 | */ 48 | protected $_inHack = false; 49 | 50 | 51 | /** 52 | * Constructor 53 | * 54 | * @param array $options (currently ignored) 55 | */ 56 | private function __construct($options) { 57 | $this->_options = $options; 58 | } 59 | 60 | /** 61 | * Minify a CSS string 62 | * 63 | * @param string $css 64 | * 65 | * @return string 66 | */ 67 | protected function _process($css) 68 | { 69 | $css = str_replace("\r\n", "\n", $css); 70 | 71 | // preserve empty comment after '>' 72 | // http://www.webdevout.net/css-hacks#in_css-selectors 73 | $css = preg_replace('@>/\\*\\s*\\*/@', '>/*keep*/', $css); 74 | 75 | // preserve empty comment between property and value 76 | // http://css-discuss.incutio.com/?page=BoxModelHack 77 | $css = preg_replace('@/\\*\\s*\\*/\\s*:@', '/*keep*/:', $css); 78 | $css = preg_replace('@:\\s*/\\*\\s*\\*/@', ':/*keep*/', $css); 79 | 80 | // apply callback to all valid comments (and strip out surrounding ws 81 | $css = preg_replace_callback('@\\s*/\\*([\\s\\S]*?)\\*/\\s*@' 82 | ,array($this, '_commentCB'), $css); 83 | 84 | // remove ws around { } and last semicolon in declaration block 85 | $css = preg_replace('/\\s*{\\s*/', '{', $css); 86 | $css = preg_replace('/;?\\s*}\\s*/', '}', $css); 87 | 88 | // remove ws surrounding semicolons 89 | $css = preg_replace('/\\s*;\\s*/', ';', $css); 90 | 91 | // remove ws around urls 92 | $css = preg_replace('/ 93 | url\\( # url( 94 | \\s* 95 | ([^\\)]+?) # 1 = the URL (really just a bunch of non right parenthesis) 96 | \\s* 97 | \\) # ) 98 | /x', 'url($1)', $css); 99 | 100 | // remove ws between rules and colons 101 | $css = preg_replace('/ 102 | \\s* 103 | ([{;]) # 1 = beginning of block or rule separator 104 | \\s* 105 | ([\\*_]?[\\w\\-]+) # 2 = property (and maybe IE filter) 106 | \\s* 107 | : 108 | \\s* 109 | (\\b|[#\'"-]) # 3 = first character of a value 110 | /x', '$1$2:$3', $css); 111 | 112 | // remove ws in selectors 113 | $css = preg_replace_callback('/ 114 | (?: # non-capture 115 | \\s* 116 | [^~>+,\\s]+ # selector part 117 | \\s* 118 | [,>+~] # combinators 119 | )+ 120 | \\s* 121 | [^~>+,\\s]+ # selector part 122 | { # open declaration block 123 | /x' 124 | ,array($this, '_selectorsCB'), $css); 125 | 126 | // minimize hex colors 127 | $css = preg_replace('/([^=])#([a-f\\d])\\2([a-f\\d])\\3([a-f\\d])\\4([\\s;\\}])/i' 128 | , '$1#$2$3$4$5', $css); 129 | 130 | // remove spaces between font families 131 | $css = preg_replace_callback('/font-family:([^;}]+)([;}])/' 132 | ,array($this, '_fontFamilyCB'), $css); 133 | 134 | $css = preg_replace('/@import\\s+url/', '@import url', $css); 135 | 136 | // replace any ws involving newlines with a single newline 137 | $css = preg_replace('/[ \\t]*\\n+\\s*/', "\n", $css); 138 | 139 | // separate common descendent selectors w/ newlines (to limit line lengths) 140 | $css = preg_replace('/([\\w#\\.\\*]+)\\s+([\\w#\\.\\*]+){/', "$1\n$2{", $css); 141 | 142 | // Use newline after 1st numeric value (to limit line lengths). 143 | $css = preg_replace('/ 144 | ((?:padding|margin|border|outline):\\d+(?:px|em)?) # 1 = prop : 1st numeric value 145 | \\s+ 146 | /x' 147 | ,"$1\n", $css); 148 | 149 | // prevent triggering IE6 bug: http://www.crankygeek.com/ie6pebug/ 150 | $css = preg_replace('/:first-l(etter|ine)\\{/', ':first-l$1 {', $css); 151 | 152 | return trim($css); 153 | } 154 | 155 | /** 156 | * Replace what looks like a set of selectors 157 | * 158 | * @param array $m regex matches 159 | * 160 | * @return string 161 | */ 162 | protected function _selectorsCB($m) 163 | { 164 | // remove ws around the combinators 165 | return preg_replace('/\\s*([,>+~])\\s*/', '$1', $m[0]); 166 | } 167 | 168 | /** 169 | * Process a comment and return a replacement 170 | * 171 | * @param array $m regex matches 172 | * 173 | * @return string 174 | */ 175 | protected function _commentCB($m) 176 | { 177 | $hasSurroundingWs = (trim($m[0]) !== $m[1]); 178 | $m = $m[1]; 179 | // $m is the comment content w/o the surrounding tokens, 180 | // but the return value will replace the entire comment. 181 | if ($m === 'keep') { 182 | return '/**/'; 183 | } 184 | if ($m === '" "') { 185 | // component of http://tantek.com/CSS/Examples/midpass.html 186 | return '/*" "*/'; 187 | } 188 | if (preg_match('@";\\}\\s*\\}/\\*\\s+@', $m)) { 189 | // component of http://tantek.com/CSS/Examples/midpass.html 190 | return '/*";}}/* */'; 191 | } 192 | if ($this->_inHack) { 193 | // inversion: feeding only to one browser 194 | if (preg_match('@ 195 | ^/ # comment started like /*/ 196 | \\s* 197 | (\\S[\\s\\S]+?) # has at least some non-ws content 198 | \\s* 199 | /\\* # ends like /*/ or /**/ 200 | @x', $m, $n)) { 201 | // end hack mode after this comment, but preserve the hack and comment content 202 | $this->_inHack = false; 203 | return "/*/{$n[1]}/**/"; 204 | } 205 | } 206 | if (substr($m, -1) === '\\') { // comment ends like \*/ 207 | // begin hack mode and preserve hack 208 | $this->_inHack = true; 209 | return '/*\\*/'; 210 | } 211 | if ($m !== '' && $m[0] === '/') { // comment looks like /*/ foo */ 212 | // begin hack mode and preserve hack 213 | $this->_inHack = true; 214 | return '/*/*/'; 215 | } 216 | if ($this->_inHack) { 217 | // a regular comment ends hack mode but should be preserved 218 | $this->_inHack = false; 219 | return '/**/'; 220 | } 221 | // Issue 107: if there's any surrounding whitespace, it may be important, so 222 | // replace the comment with a single space 223 | return $hasSurroundingWs // remove all other comments 224 | ? ' ' 225 | : ''; 226 | } 227 | 228 | /** 229 | * Process a font-family listing and return a replacement 230 | * 231 | * @param array $m regex matches 232 | * 233 | * @return string 234 | */ 235 | protected function _fontFamilyCB($m) 236 | { 237 | // Issue 210: must not eliminate WS between words in unquoted families 238 | $pieces = preg_split('/(\'[^\']+\'|"[^"]+")/', $m[1], null, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); 239 | $out = 'font-family:'; 240 | while (null !== ($piece = array_shift($pieces))) { 241 | if ($piece[0] !== '"' && $piece[0] !== "'") { 242 | $piece = preg_replace('/\\s+/', ' ', $piece); 243 | $piece = preg_replace('/\\s?,\\s?/', ',', $piece); 244 | } 245 | $out .= $piece; 246 | } 247 | return $out . $m[2]; 248 | } 249 | } 250 | -------------------------------------------------------------------------------- /system/expressionengine/third_party/minimee/libraries/Minify/HTML.php: -------------------------------------------------------------------------------- 1 | 18 | */ 19 | class Minify_HTML { 20 | /** 21 | * @var boolean 22 | */ 23 | protected $_jsCleanComments = true; 24 | 25 | /** 26 | * "Minify" an HTML page 27 | * 28 | * @param string $html 29 | * 30 | * @param array $options 31 | * 32 | * 'cssMinifier' : (optional) callback function to process content of STYLE 33 | * elements. 34 | * 35 | * 'jsMinifier' : (optional) callback function to process content of SCRIPT 36 | * elements. Note: the type attribute is ignored. 37 | * 38 | * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If 39 | * unset, minify will sniff for an XHTML doctype. 40 | * 41 | * @return string 42 | */ 43 | public static function minify($html, $options = array()) { 44 | $min = new self($html, $options); 45 | return $min->process(); 46 | } 47 | 48 | 49 | /** 50 | * Create a minifier object 51 | * 52 | * @param string $html 53 | * 54 | * @param array $options 55 | * 56 | * 'cssMinifier' : (optional) callback function to process content of STYLE 57 | * elements. 58 | * 59 | * 'jsMinifier' : (optional) callback function to process content of SCRIPT 60 | * elements. Note: the type attribute is ignored. 61 | * 62 | * 'jsCleanComments' : (optional) whether to remove HTML comments beginning and end of script block 63 | * 64 | * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If 65 | * unset, minify will sniff for an XHTML doctype. 66 | * 67 | * @return null 68 | */ 69 | public function __construct($html, $options = array()) 70 | { 71 | $this->_html = str_replace("\r\n", "\n", trim($html)); 72 | if (isset($options['xhtml'])) { 73 | $this->_isXhtml = (bool)$options['xhtml']; 74 | } 75 | if (isset($options['cssMinifier'])) { 76 | $this->_cssMinifier = $options['cssMinifier']; 77 | } 78 | if (isset($options['jsMinifier'])) { 79 | $this->_jsMinifier = $options['jsMinifier']; 80 | } 81 | if (isset($options['jsCleanComments'])) { 82 | $this->_jsCleanComments = (bool)$options['jsCleanComments']; 83 | } 84 | } 85 | 86 | 87 | /** 88 | * Minify the markeup given in the constructor 89 | * 90 | * @return string 91 | */ 92 | public function process() 93 | { 94 | if ($this->_isXhtml === null) { 95 | $this->_isXhtml = (false !== strpos($this->_html, '_replacementHash = 'MINIFYHTML' . md5($_SERVER['REQUEST_TIME']); 99 | $this->_placeholders = array(); 100 | 101 | // replace SCRIPTs (and minify) with placeholders 102 | $this->_html = preg_replace_callback( 103 | '/(\\s*)]*?>)([\\s\\S]*?)<\\/script>(\\s*)/i' 104 | ,array($this, '_removeScriptCB') 105 | ,$this->_html); 106 | 107 | // replace STYLEs (and minify) with placeholders 108 | $this->_html = preg_replace_callback( 109 | '/\\s*]*>)([\\s\\S]*?)<\\/style>\\s*/i' 110 | ,array($this, '_removeStyleCB') 111 | ,$this->_html); 112 | 113 | // remove HTML comments (not containing IE conditional comments). 114 | $this->_html = preg_replace_callback( 115 | '//' 116 | ,array($this, '_commentCB') 117 | ,$this->_html); 118 | 119 | // replace PREs with placeholders 120 | $this->_html = preg_replace_callback('/\\s*]*?>[\\s\\S]*?<\\/pre>)\\s*/i' 121 | ,array($this, '_removePreCB') 122 | ,$this->_html); 123 | 124 | // replace TEXTAREAs with placeholders 125 | $this->_html = preg_replace_callback( 126 | '/\\s*]*?>[\\s\\S]*?<\\/textarea>)\\s*/i' 127 | ,array($this, '_removeTextareaCB') 128 | ,$this->_html); 129 | 130 | // trim each line. 131 | // @todo take into account attribute values that span multiple lines. 132 | $this->_html = preg_replace('/^\\s+|\\s+$/m', '', $this->_html); 133 | 134 | // remove ws around block/undisplayed elements 135 | $this->_html = preg_replace('/\\s+(<\\/?(?:area|base(?:font)?|blockquote|body' 136 | .'|caption|center|cite|col(?:group)?|dd|dir|div|dl|dt|fieldset|form' 137 | .'|frame(?:set)?|h[1-6]|head|hr|html|legend|li|link|map|menu|meta' 138 | .'|ol|opt(?:group|ion)|p|param|t(?:able|body|head|d|h||r|foot|itle)' 139 | .'|ul)\\b[^>]*>)/i', '$1', $this->_html); 140 | 141 | // remove ws outside of all elements 142 | $this->_html = preg_replace( 143 | '/>(\\s(?:\\s*))?([^<]+)(\\s(?:\s*))?$1$2$3<' 145 | ,$this->_html); 146 | 147 | // use newlines before 1st attribute in open tags (to limit line lengths) 148 | $this->_html = preg_replace('/(<[a-z\\-]+)\\s+([^>]+>)/i', "$1\n$2", $this->_html); 149 | 150 | // fill placeholders 151 | $this->_html = str_replace( 152 | array_keys($this->_placeholders) 153 | ,array_values($this->_placeholders) 154 | ,$this->_html 155 | ); 156 | // issue 229: multi-pass to catch scripts that didn't get replaced in textareas 157 | $this->_html = str_replace( 158 | array_keys($this->_placeholders) 159 | ,array_values($this->_placeholders) 160 | ,$this->_html 161 | ); 162 | return $this->_html; 163 | } 164 | 165 | protected function _commentCB($m) 166 | { 167 | return (0 === strpos($m[1], '[') || false !== strpos($m[1], '_replacementHash . count($this->_placeholders) . '%'; 175 | $this->_placeholders[$placeholder] = $content; 176 | return $placeholder; 177 | } 178 | 179 | protected $_isXhtml = null; 180 | protected $_replacementHash = null; 181 | protected $_placeholders = array(); 182 | protected $_cssMinifier = null; 183 | protected $_jsMinifier = null; 184 | 185 | protected function _removePreCB($m) 186 | { 187 | return $this->_reservePlace("_reservePlace("\\s*$)/', '', $css); 201 | 202 | // remove CDATA section markers 203 | $css = $this->_removeCdata($css); 204 | 205 | // minify 206 | $minifier = $this->_cssMinifier 207 | ? $this->_cssMinifier 208 | : 'trim'; 209 | $css = call_user_func($minifier, $css); 210 | 211 | return $this->_reservePlace($this->_needsCdata($css) 212 | ? "{$openStyle}/**/" 213 | : "{$openStyle}{$css}" 214 | ); 215 | } 216 | 217 | protected function _removeScriptCB($m) 218 | { 219 | $openScript = "_jsCleanComments) { 228 | $js = preg_replace('/(?:^\\s*\\s*$)/', '', $js); 229 | } 230 | 231 | // remove CDATA section markers 232 | $js = $this->_removeCdata($js); 233 | 234 | // minify 235 | $minifier = $this->_jsMinifier 236 | ? $this->_jsMinifier 237 | : 'trim'; 238 | $js = call_user_func($minifier, $js); 239 | 240 | return $this->_reservePlace($this->_needsCdata($js) 241 | ? "{$ws1}{$openScript}/**/{$ws2}" 242 | : "{$ws1}{$openScript}{$js}{$ws2}" 243 | ); 244 | } 245 | 246 | protected function _removeCdata($str) 247 | { 248 | return (false !== strpos($str, ''), '', $str) 250 | : $str; 251 | } 252 | 253 | protected function _needsCdata($str) 254 | { 255 | return ($this->_isXhtml && preg_match('/(?:[<&]|\\-\\-|\\]\\]>)/', $str)); 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /system/expressionengine/third_party/minimee/libraries/Minify/Controller/MinApp.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class Minify_Controller_MinApp extends Minify_Controller_Base { 14 | 15 | /** 16 | * Set up groups of files as sources 17 | * 18 | * @param array $options controller and Minify options 19 | * 20 | * @return array Minify options 21 | */ 22 | public function setupSources($options) { 23 | // PHP insecure by default: realpath() and other FS functions can't handle null bytes. 24 | foreach (array('g', 'b', 'f') as $key) { 25 | if (isset($_GET[$key])) { 26 | $_GET[$key] = str_replace("\x00", '', (string)$_GET[$key]); 27 | } 28 | } 29 | 30 | // filter controller options 31 | $cOptions = array_merge( 32 | array( 33 | 'allowDirs' => '//' 34 | ,'groupsOnly' => false 35 | ,'groups' => array() 36 | ,'noMinPattern' => '@[-\\.]min\\.(?:js|css)$@i' // matched against basename 37 | ) 38 | ,(isset($options['minApp']) ? $options['minApp'] : array()) 39 | ); 40 | unset($options['minApp']); 41 | $sources = array(); 42 | $this->selectionId = ''; 43 | $firstMissingResource = null; 44 | if (isset($_GET['g'])) { 45 | // add group(s) 46 | $this->selectionId .= 'g=' . $_GET['g']; 47 | $keys = explode(',', $_GET['g']); 48 | if ($keys != array_unique($keys)) { 49 | $this->log("Duplicate group key found."); 50 | return $options; 51 | } 52 | $keys = explode(',', $_GET['g']); 53 | foreach ($keys as $key) { 54 | if (! isset($cOptions['groups'][$key])) { 55 | $this->log("A group configuration for \"{$key}\" was not found"); 56 | return $options; 57 | } 58 | $files = $cOptions['groups'][$key]; 59 | // if $files is a single object, casting will break it 60 | if (is_object($files)) { 61 | $files = array($files); 62 | } elseif (! is_array($files)) { 63 | $files = (array)$files; 64 | } 65 | foreach ($files as $file) { 66 | if ($file instanceof Minify_Source) { 67 | $sources[] = $file; 68 | continue; 69 | } 70 | if (0 === strpos($file, '//')) { 71 | $file = $_SERVER['DOCUMENT_ROOT'] . substr($file, 1); 72 | } 73 | $realpath = realpath($file); 74 | if ($realpath && is_file($realpath)) { 75 | $sources[] = $this->_getFileSource($realpath, $cOptions); 76 | } else { 77 | $this->log("The path \"{$file}\" (realpath \"{$realpath}\") could not be found (or was not a file)"); 78 | if (null === $firstMissingResource) { 79 | $firstMissingResource = basename($file); 80 | continue; 81 | } else { 82 | $secondMissingResource = basename($file); 83 | $this->log("More than one file was missing: '$firstMissingResource', '$secondMissingResource'"); 84 | return $options; 85 | } 86 | } 87 | } 88 | if ($sources) { 89 | try { 90 | $this->checkType($sources[0]); 91 | } catch (Exception $e) { 92 | $this->log($e->getMessage()); 93 | return $options; 94 | } 95 | } 96 | } 97 | } 98 | if (! $cOptions['groupsOnly'] && isset($_GET['f'])) { 99 | // try user files 100 | // The following restrictions are to limit the URLs that minify will 101 | // respond to. 102 | if (// verify at least one file, files are single comma separated, 103 | // and are all same extension 104 | ! preg_match('/^[^,]+\\.(css|js)(?:,[^,]+\\.\\1)*$/', $_GET['f'], $m) 105 | // no "//" 106 | || strpos($_GET['f'], '//') !== false 107 | // no "\" 108 | || strpos($_GET['f'], '\\') !== false 109 | ) { 110 | $this->log("GET param 'f' was invalid"); 111 | return $options; 112 | } 113 | $ext = ".{$m[1]}"; 114 | try { 115 | $this->checkType($m[1]); 116 | } catch (Exception $e) { 117 | $this->log($e->getMessage()); 118 | return $options; 119 | } 120 | $files = explode(',', $_GET['f']); 121 | if ($files != array_unique($files)) { 122 | $this->log("Duplicate files were specified"); 123 | return $options; 124 | } 125 | if (isset($_GET['b'])) { 126 | // check for validity 127 | if (preg_match('@^[^/]+(?:/[^/]+)*$@', $_GET['b']) 128 | && false === strpos($_GET['b'], '..') 129 | && $_GET['b'] !== '.') { 130 | // valid base 131 | $base = "/{$_GET['b']}/"; 132 | } else { 133 | $this->log("GET param 'b' was invalid"); 134 | return $options; 135 | } 136 | } else { 137 | $base = '/'; 138 | } 139 | $allowDirs = array(); 140 | foreach ((array)$cOptions['allowDirs'] as $allowDir) { 141 | $allowDirs[] = realpath(str_replace('//', $_SERVER['DOCUMENT_ROOT'] . '/', $allowDir)); 142 | } 143 | $basenames = array(); // just for cache id 144 | foreach ($files as $file) { 145 | $uri = $base . $file; 146 | $path = $_SERVER['DOCUMENT_ROOT'] . $uri; 147 | $realpath = realpath($path); 148 | if (false === $realpath || ! is_file($realpath)) { 149 | $this->log("The path \"{$path}\" (realpath \"{$realpath}\") could not be found (or was not a file)"); 150 | if (null === $firstMissingResource) { 151 | $firstMissingResource = $uri; 152 | continue; 153 | } else { 154 | $secondMissingResource = $uri; 155 | $this->log("More than one file was missing: '$firstMissingResource', '$secondMissingResource`'"); 156 | return $options; 157 | } 158 | } 159 | try { 160 | parent::checkNotHidden($realpath); 161 | parent::checkAllowDirs($realpath, $allowDirs, $uri); 162 | } catch (Exception $e) { 163 | $this->log($e->getMessage()); 164 | return $options; 165 | } 166 | $sources[] = $this->_getFileSource($realpath, $cOptions); 167 | $basenames[] = basename($realpath, $ext); 168 | } 169 | if ($this->selectionId) { 170 | $this->selectionId .= '_f='; 171 | } 172 | $this->selectionId .= implode(',', $basenames) . $ext; 173 | } 174 | if ($sources) { 175 | if (null !== $firstMissingResource) { 176 | array_unshift($sources, new Minify_Source(array( 177 | 'id' => 'missingFile' 178 | // should not cause cache invalidation 179 | ,'lastModified' => 0 180 | // due to caching, filename is unreliable. 181 | ,'content' => "/* Minify: at least one missing file. See " . Minify::URL_DEBUG . " */\n" 182 | ,'minifier' => '' 183 | ))); 184 | } 185 | $this->sources = $sources; 186 | } else { 187 | $this->log("No sources to serve"); 188 | } 189 | return $options; 190 | } 191 | 192 | /** 193 | * @param string $file 194 | * 195 | * @param array $cOptions 196 | * 197 | * @return Minify_Source 198 | */ 199 | protected function _getFileSource($file, $cOptions) 200 | { 201 | $spec['filepath'] = $file; 202 | if ($cOptions['noMinPattern'] && preg_match($cOptions['noMinPattern'], basename($file))) { 203 | if (preg_match('~\.css$~i', $file)) { 204 | $spec['minifyOptions']['compress'] = false; 205 | } else { 206 | $spec['minifier'] = ''; 207 | } 208 | } 209 | return new Minify_Source($spec); 210 | } 211 | 212 | protected $_type = null; 213 | 214 | /** 215 | * Make sure that only source files of a single type are registered 216 | * 217 | * @param string $sourceOrExt 218 | * 219 | * @throws Exception 220 | */ 221 | public function checkType($sourceOrExt) 222 | { 223 | if ($sourceOrExt === 'js') { 224 | $type = Minify::TYPE_JS; 225 | } elseif ($sourceOrExt === 'css') { 226 | $type = Minify::TYPE_CSS; 227 | } elseif ($sourceOrExt->contentType !== null) { 228 | $type = $sourceOrExt->contentType; 229 | } else { 230 | return; 231 | } 232 | if ($this->_type === null) { 233 | $this->_type = $type; 234 | } elseif ($this->_type !== $type) { 235 | throw new Exception('Content-Type mismatch'); 236 | } 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /system/expressionengine/third_party/minimee/classes/Minimee_helper.php: -------------------------------------------------------------------------------- 1 | 9 | * @license http://www.opensource.org/licenses/bsd-license.php BSD license 10 | * @link http://johndwells.com/software/minimee 11 | */ 12 | class Minimee_helper { 13 | 14 | 15 | /** 16 | * Logging levels 17 | */ 18 | private static $_levels = array( 19 | 1 => 'ERROR', 20 | 2 => 'DEBUG', 21 | 3 => 'INFO' 22 | ); 23 | 24 | 25 | /** 26 | * History of logging for EE Debug Toolbar 27 | */ 28 | private static $_log = array(); 29 | 30 | 31 | /** 32 | * Flag for whether to 'flash' our toolbar tab 33 | */ 34 | private static $_log_has_error = FALSE; 35 | 36 | 37 | /** 38 | * Our 'Singleton' config 39 | */ 40 | private static $_config = FALSE; 41 | 42 | 43 | // ---------------------------------------------- 44 | 45 | 46 | /** 47 | * Create an alias to our cache 48 | * 49 | * @return Array Our cache in EE->session->cache 50 | */ 51 | public static function &cache() 52 | { 53 | $ee =& get_instance(); 54 | 55 | // be sure we have a cache set up 56 | if ( ! isset($ee->session->cache['minimee'])) 57 | { 58 | $ee->session->cache['minimee'] = array(); 59 | 60 | self::log('Session cache has been created.', 3); 61 | } 62 | 63 | return $ee->session->cache['minimee']; 64 | } 65 | // ------------------------------------------------------ 66 | 67 | 68 | /** 69 | * Fetch/create singleton instance of config 70 | * 71 | * @return Array Instance Minimee_config 72 | */ 73 | public static function config() 74 | { 75 | if (self::$_config === FALSE) 76 | { 77 | self::$_config = new Minimee_config(); 78 | } 79 | 80 | return self::$_config; 81 | } 82 | // ------------------------------------------------------ 83 | 84 | 85 | /** 86 | * Fetch our static log 87 | * 88 | * @return Array Array of logs 89 | */ 90 | public static function get_log() 91 | { 92 | return self::$_log; 93 | } 94 | // ------------------------------------------------------ 95 | 96 | 97 | /** 98 | * Fetch our static log 99 | * 100 | * @return Array Array of logs 101 | */ 102 | public static function log_has_error() 103 | { 104 | return self::$_log_has_error; 105 | } 106 | // ------------------------------------------------------ 107 | 108 | 109 | /** 110 | * Determine if string is valid URL 111 | * 112 | * @param string String to test 113 | * @return bool TRUE if yes, FALSE if no 114 | */ 115 | public static function is_url($string) 116 | { 117 | // from old _isURL() file from Carabiner Asset Management Library 118 | // modified to support leading with double slashes 119 | return (preg_match('@((https?:)?//([-\w\.]+)+(:\d+)?(/([\w/_\.]*(\?\S+)?)?)?)@', $string) > 0); 120 | } 121 | // ------------------------------------------------------ 122 | 123 | 124 | /** 125 | * Loads our requested library 126 | * 127 | * On first call it will adjust the include_path, for Minify support 128 | * 129 | * @param string Name of library to require 130 | * @return void 131 | */ 132 | public static function library($which) 133 | { 134 | // a few housekeeping items before we start loading our libraries 135 | if ( ! isset(get_instance()->session->cache['loader'])) 136 | { 137 | // try to bump our memory limits for good measure 138 | @ini_set('memory_limit', '12M'); 139 | @ini_set('memory_limit', '16M'); 140 | @ini_set('memory_limit', '32M'); 141 | @ini_set('memory_limit', '64M'); 142 | @ini_set('memory_limit', '128M'); 143 | @ini_set('memory_limit', '256M'); 144 | 145 | // Latest changes to Minify adopt a "loader" over sprinkled require's 146 | require_once(PATH_THIRD . 'minimee/libraries/Minify/Loader.php'); 147 | Minify_Loader::register(); 148 | 149 | // don't do this again 150 | get_instance()->session->cache['loader'] = TRUE; 151 | } 152 | 153 | // require_once our library of choice 154 | switch ($which) : 155 | 156 | case ('minify') : 157 | if ( ! class_exists('Minify_CSS')) 158 | { 159 | require_once(PATH_THIRD . 'minimee/libraries/Minify/CSS.php'); 160 | } 161 | break; 162 | 163 | case ('cssmin') : 164 | if ( ! class_exists('CSSmin')) 165 | { 166 | // this sucks, but it's a case-insensitivity issue that we need to protect ourselves against 167 | if (glob(PATH_THIRD . 'minimee/libraries/CSSmin.php')) 168 | { 169 | require_once(PATH_THIRD . 'minimee/libraries/CSSmin.php'); 170 | } 171 | 172 | else 173 | { 174 | self::log('CSSMin.php in minimee/libraries needs to be renamed to the proper capitalisation of "CSSmin.php".', 2); 175 | require_once(PATH_THIRD . 'minimee/libraries/CSSMin.php'); 176 | } 177 | } 178 | break; 179 | 180 | case ('css_urirewriter') : 181 | if ( ! class_exists('Minify_CSS_UriRewriter')) 182 | { 183 | require_once(PATH_THIRD . 'minimee/libraries/Minify/CSS/UriRewriter.php'); 184 | } 185 | break; 186 | 187 | case ('curl') : 188 | if ( ! class_exists('EpiCurl')) 189 | { 190 | require_once(PATH_THIRD . 'minimee/libraries/EpiCurl.php'); 191 | } 192 | break; 193 | 194 | case ('jsmin') : 195 | 196 | if ( ! class_exists('JSMin')) 197 | { 198 | // this sucks, but it's a case-insensitivity issue that we need to protect ourselves against 199 | if (glob(PATH_THIRD . 'minimee/libraries/JSM*n.php')) 200 | { 201 | require_once(PATH_THIRD . 'minimee/libraries/JSMin.php'); 202 | } 203 | 204 | else 205 | { 206 | self::log('jsmin.php in minimee/libraries needs to be renamed to the proper capitalisation of "JSMin.php".', 2); 207 | require_once(PATH_THIRD . 'minimee/libraries/jsmin.php'); 208 | } 209 | } 210 | break; 211 | 212 | case ('jsminplus') : 213 | if ( ! class_exists('JSMinPlus')) 214 | { 215 | require_once(PATH_THIRD . 'minimee/libraries/JSMinPlus.php'); 216 | } 217 | break; 218 | 219 | case ('html') : 220 | if ( ! class_exists('Minify_HTML')) 221 | { 222 | require_once(PATH_THIRD . 'minimee/libraries/Minify/HTML.php'); 223 | } 224 | break; 225 | 226 | endswitch; 227 | } 228 | // ------------------------------------------------------ 229 | 230 | 231 | /** 232 | * Log method 233 | * 234 | * By default will pass message to log_message(); 235 | * Also will log to template if rendering a PAGE. 236 | * 237 | * @access public 238 | * @param string $message The log entry message. 239 | * @param int $severity The log entry 'level'. 240 | * @return void 241 | */ 242 | public static function log($message, $severity = 1) 243 | { 244 | // translate our severity number into text 245 | $severity = (array_key_exists($severity, self::$_levels)) ? self::$_levels[$severity] : self::$_levels[1]; 246 | 247 | // save our log for EE Debug Toolbar 248 | self::$_log[] = array($severity, $message); 249 | if($severity == 'ERROR') 250 | { 251 | self::$_log_has_error = TRUE; 252 | } 253 | 254 | // basic EE logging 255 | log_message($severity, MINIMEE_NAME . ": {$message}"); 256 | 257 | // Can we also log our message to the template debugger? 258 | if (REQ == 'PAGE') 259 | { 260 | get_instance()->TMPL->log_item(MINIMEE_NAME . " [{$severity}]: {$message}"); 261 | } 262 | } 263 | // ------------------------------------------------------ 264 | 265 | 266 | /** 267 | * Returns an array of all public properties of our Minimee plugin. 268 | * Used to easily reset() to defaults. 269 | * 270 | * @return array Array of public properties of Minimee class 271 | */ 272 | public static function minimee_class_vars() 273 | { 274 | $m = new Minimee; 275 | return get_class_vars(get_class($m)); 276 | } 277 | // ------------------------------------------------------ 278 | 279 | 280 | /** 281 | * Helper function to parse content looking for CSS and JS tags. 282 | * Returns array of links found. 283 | * @param string String to search 284 | * @param string Which type of tags to search for - CSS or JS 285 | * @return array Array of found matches 286 | */ 287 | public static function preg_match_by_type($haystack, $type) 288 | { 289 | // let's find the location of our cache files 290 | switch (strtolower($type)) : 291 | 292 | case 'css' : 293 | $pat = "/]*>/i"; 294 | break; 295 | 296 | case 'js' : 297 | $pat = "/]*>(.*?)<\/script>/i"; 298 | break; 299 | 300 | default : 301 | return FALSE; 302 | break; 303 | 304 | endswitch; 305 | 306 | if ( ! preg_match_all($pat, $haystack, $matches, PREG_PATTERN_ORDER)) 307 | { 308 | return FALSE; 309 | } 310 | 311 | // free memory where possible 312 | unset($pat); 313 | 314 | return $matches; 315 | } 316 | // ------------------------------------------------------ 317 | 318 | 319 | /** 320 | * Modified remove_double_slashes() 321 | * 322 | * If the string passed is a URL, it will preserve leading double slashes 323 | * 324 | * @param string String to remove double slashes from 325 | * @param boolean True if string is a URL 326 | * @return string String without double slashes 327 | */ 328 | public static function remove_double_slashes($string, $url = FALSE) 329 | { 330 | // is our string a URL? 331 | if ($url) 332 | { 333 | // regex pattern removes all double slashes, preserving http:// and '//' at start 334 | return preg_replace("#([^:])//+#", "\\1/", $string); 335 | } 336 | 337 | // nope just a path 338 | else 339 | { 340 | // regex pattern removes all double slashes - straight from EE->functions->remove_double_slashes(); 341 | return preg_replace("#(^|[^:])//+#", "\\1/", $string); 342 | } 343 | } 344 | // ------------------------------------------------------ 345 | 346 | 347 | /** 348 | * A protocol-agnostic function to replace URL with path 349 | * 350 | * @param string base url 351 | * @param boolean base path 352 | * @return string String to perform replacement upon 353 | */ 354 | public static function replace_url_with($url, $with, $haystack) 355 | { 356 | // protocol-agnostic URL 357 | $agnostic_url = substr($url, strpos($url, '//') + 2, strlen($url)); 358 | 359 | // pattern search & replace 360 | return preg_replace('@(https?:)?\/\/' . $agnostic_url . '@', $with, $haystack); 361 | } 362 | } 363 | // END CLASS 364 | 365 | /* End of file Minimee_helper.php */ 366 | /* Location: ./system/expressionengine/third_party/minimee/classes/Minimee_helper.php */ -------------------------------------------------------------------------------- /system/expressionengine/third_party/minimee/libraries/Minify/CSS/UriRewriter.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class Minify_CSS_UriRewriter { 14 | 15 | /** 16 | * rewrite() and rewriteRelative() append debugging information here 17 | * 18 | * @var string 19 | */ 20 | public static $debugText = ''; 21 | 22 | /** 23 | * In CSS content, rewrite file relative URIs as root relative 24 | * 25 | * @param string $css 26 | * 27 | * @param string $currentDir The directory of the current CSS file. 28 | * 29 | * @param string $docRoot The document root of the web site in which 30 | * the CSS file resides (default = $_SERVER['DOCUMENT_ROOT']). 31 | * 32 | * @param array $symlinks (default = array()) If the CSS file is stored in 33 | * a symlink-ed directory, provide an array of link paths to 34 | * target paths, where the link paths are within the document root. Because 35 | * paths need to be normalized for this to work, use "//" to substitute 36 | * the doc root in the link paths (the array keys). E.g.: 37 | * 38 | * array('//symlink' => '/real/target/path') // unix 39 | * array('//static' => 'D:\\staticStorage') // Windows 40 | * 41 | * 42 | * @return string 43 | */ 44 | public static function rewrite($css, $currentDir, $docRoot = null, $symlinks = array()) 45 | { 46 | self::$_docRoot = self::_realpath( 47 | $docRoot ? $docRoot : $_SERVER['DOCUMENT_ROOT'] 48 | ); 49 | self::$_currentDir = self::_realpath($currentDir); 50 | self::$_symlinks = array(); 51 | 52 | // normalize symlinks 53 | foreach ($symlinks as $link => $target) { 54 | $link = ($link === '//') 55 | ? self::$_docRoot 56 | : str_replace('//', self::$_docRoot . '/', $link); 57 | $link = strtr($link, '/', DIRECTORY_SEPARATOR); 58 | self::$_symlinks[$link] = self::_realpath($target); 59 | } 60 | 61 | self::$debugText .= "docRoot : " . self::$_docRoot . "\n" 62 | . "currentDir : " . self::$_currentDir . "\n"; 63 | if (self::$_symlinks) { 64 | self::$debugText .= "symlinks : " . var_export(self::$_symlinks, 1) . "\n"; 65 | } 66 | self::$debugText .= "\n"; 67 | 68 | $css = self::_trimUrls($css); 69 | 70 | // rewrite 71 | $css = preg_replace_callback('/@import\\s+([\'"])(.*?)[\'"]/' 72 | ,array(self::$className, '_processUriCB'), $css); 73 | $css = preg_replace_callback('/url\\(\\s*([^\\)\\s]+)\\s*\\)/' 74 | ,array(self::$className, '_processUriCB'), $css); 75 | 76 | return $css; 77 | } 78 | 79 | /** 80 | * In CSS content, prepend a path to relative URIs 81 | * 82 | * @param string $css 83 | * 84 | * @param string $path The path to prepend. 85 | * 86 | * @return string 87 | */ 88 | public static function prepend($css, $path) 89 | { 90 | self::$_prependPath = $path; 91 | 92 | $css = self::_trimUrls($css); 93 | 94 | // append 95 | $css = preg_replace_callback('/@import\\s+([\'"])(.*?)[\'"]/' 96 | ,array(self::$className, '_processUriCB'), $css); 97 | $css = preg_replace_callback('/url\\(\\s*([^\\)\\s]+)\\s*\\)/' 98 | ,array(self::$className, '_processUriCB'), $css); 99 | 100 | self::$_prependPath = null; 101 | return $css; 102 | } 103 | 104 | /** 105 | * Get a root relative URI from a file relative URI 106 | * 107 | * 108 | * Minify_CSS_UriRewriter::rewriteRelative( 109 | * '../img/hello.gif' 110 | * , '/home/user/www/css' // path of CSS file 111 | * , '/home/user/www' // doc root 112 | * ); 113 | * // returns '/img/hello.gif' 114 | * 115 | * // example where static files are stored in a symlinked directory 116 | * Minify_CSS_UriRewriter::rewriteRelative( 117 | * 'hello.gif' 118 | * , '/var/staticFiles/theme' 119 | * , '/home/user/www' 120 | * , array('/home/user/www/static' => '/var/staticFiles') 121 | * ); 122 | * // returns '/static/theme/hello.gif' 123 | * 124 | * 125 | * @param string $uri file relative URI 126 | * 127 | * @param string $realCurrentDir realpath of the current file's directory. 128 | * 129 | * @param string $realDocRoot realpath of the site document root. 130 | * 131 | * @param array $symlinks (default = array()) If the file is stored in 132 | * a symlink-ed directory, provide an array of link paths to 133 | * real target paths, where the link paths "appear" to be within the document 134 | * root. E.g.: 135 | * 136 | * array('/home/foo/www/not/real/path' => '/real/target/path') // unix 137 | * array('C:\\htdocs\\not\\real' => 'D:\\real\\target\\path') // Windows 138 | * 139 | * 140 | * @return string 141 | */ 142 | public static function rewriteRelative($uri, $realCurrentDir, $realDocRoot, $symlinks = array()) 143 | { 144 | // prepend path with current dir separator (OS-independent) 145 | $path = strtr($realCurrentDir, '/', DIRECTORY_SEPARATOR) 146 | . DIRECTORY_SEPARATOR . strtr($uri, '/', DIRECTORY_SEPARATOR); 147 | 148 | self::$debugText .= "file-relative URI : {$uri}\n" 149 | . "path prepended : {$path}\n"; 150 | 151 | // "unresolve" a symlink back to doc root 152 | foreach ($symlinks as $link => $target) { 153 | if (0 === strpos($path, $target)) { 154 | // replace $target with $link 155 | $path = $link . substr($path, strlen($target)); 156 | 157 | self::$debugText .= "symlink unresolved : {$path}\n"; 158 | 159 | break; 160 | } 161 | } 162 | // strip doc root 163 | $path = substr($path, strlen($realDocRoot)); 164 | 165 | self::$debugText .= "docroot stripped : {$path}\n"; 166 | 167 | // fix to root-relative URI 168 | $uri = strtr($path, '/\\', '//'); 169 | $uri = self::removeDots($uri); 170 | 171 | self::$debugText .= "traversals removed : {$uri}\n\n"; 172 | 173 | return $uri; 174 | } 175 | 176 | /** 177 | * Remove instances of "./" and "../" where possible from a root-relative URI 178 | * 179 | * @param string $uri 180 | * 181 | * @return string 182 | */ 183 | public static function removeDots($uri) 184 | { 185 | $uri = str_replace('/./', '/', $uri); 186 | // inspired by patch from Oleg Cherniy 187 | do { 188 | $uri = preg_replace('@/[^/]+/\\.\\./@', '/', $uri, 1, $changed); 189 | } while ($changed); 190 | return $uri; 191 | } 192 | 193 | /** 194 | * Defines which class to call as part of callbacks, change this 195 | * if you extend Minify_CSS_UriRewriter 196 | * 197 | * @var string 198 | */ 199 | protected static $className = 'Minify_CSS_UriRewriter'; 200 | 201 | /** 202 | * Get realpath with any trailing slash removed. If realpath() fails, 203 | * just remove the trailing slash. 204 | * 205 | * @param string $path 206 | * 207 | * @return mixed path with no trailing slash 208 | */ 209 | protected static function _realpath($path) 210 | { 211 | $realPath = realpath($path); 212 | if ($realPath !== false) { 213 | $path = $realPath; 214 | } 215 | return rtrim($path, '/\\'); 216 | } 217 | 218 | /** 219 | * Directory of this stylesheet 220 | * 221 | * @var string 222 | */ 223 | private static $_currentDir = ''; 224 | 225 | /** 226 | * DOC_ROOT 227 | * 228 | * @var string 229 | */ 230 | private static $_docRoot = ''; 231 | 232 | /** 233 | * directory replacements to map symlink targets back to their 234 | * source (within the document root) E.g. '/var/www/symlink' => '/var/realpath' 235 | * 236 | * @var array 237 | */ 238 | private static $_symlinks = array(); 239 | 240 | /** 241 | * Path to prepend 242 | * 243 | * @var string 244 | */ 245 | private static $_prependPath = null; 246 | 247 | /** 248 | * @param string $css 249 | * 250 | * @return string 251 | */ 252 | private static function _trimUrls($css) 253 | { 254 | return preg_replace('/ 255 | url\\( # url( 256 | \\s* 257 | ([^\\)]+?) # 1 = URI (assuming does not contain ")") 258 | \\s* 259 | \\) # ) 260 | /x', 'url($1)', $css); 261 | } 262 | 263 | /** 264 | * @param array $m 265 | * 266 | * @return string 267 | */ 268 | private static function _processUriCB($m) 269 | { 270 | // $m matched either '/@import\\s+([\'"])(.*?)[\'"]/' or '/url\\(\\s*([^\\)\\s]+)\\s*\\)/' 271 | $isImport = ($m[0][0] === '@'); 272 | // determine URI and the quote character (if any) 273 | if ($isImport) { 274 | $quoteChar = $m[1]; 275 | $uri = $m[2]; 276 | } else { 277 | // $m[1] is either quoted or not 278 | $quoteChar = ($m[1][0] === "'" || $m[1][0] === '"') 279 | ? $m[1][0] 280 | : ''; 281 | $uri = ($quoteChar === '') 282 | ? $m[1] 283 | : substr($m[1], 1, strlen($m[1]) - 2); 284 | } 285 | // analyze URI 286 | if ('/' !== $uri[0] // root-relative 287 | && false === strpos($uri, '//') // protocol (non-data) 288 | && 0 !== strpos($uri, 'data:') // data protocol 289 | ) { 290 | // URI is file-relative: rewrite depending on options 291 | if (self::$_prependPath === null) { 292 | $uri = self::rewriteRelative($uri, self::$_currentDir, self::$_docRoot, self::$_symlinks); 293 | } else { 294 | $uri = self::$_prependPath . $uri; 295 | if ($uri[0] === '/') { 296 | $root = ''; 297 | $rootRelative = $uri; 298 | $uri = $root . self::removeDots($rootRelative); 299 | } elseif (preg_match('@^((https?\:)?//([^/]+))/@', $uri, $m) && (false !== strpos($m[3], '.'))) { 300 | $root = $m[1]; 301 | $rootRelative = substr($uri, strlen($root)); 302 | $uri = $root . self::removeDots($rootRelative); 303 | } 304 | } 305 | } 306 | return $isImport 307 | ? "@import {$quoteChar}{$uri}{$quoteChar}" 308 | : "url({$quoteChar}{$uri}{$quoteChar})"; 309 | } 310 | } 311 | -------------------------------------------------------------------------------- /system/expressionengine/third_party/minimee/libraries/HTTP/Encoder.php: -------------------------------------------------------------------------------- 1 | 15 | * // Send a CSS file, compressed if possible 16 | * $he = new HTTP_Encoder(array( 17 | * 'content' => file_get_contents($cssFile) 18 | * ,'type' => 'text/css' 19 | * )); 20 | * $he->encode(); 21 | * $he->sendAll(); 22 | * 23 | * 24 | * 25 | * // Shortcut to encoding output 26 | * header('Content-Type: text/css'); // needed if not HTML 27 | * HTTP_Encoder::output($css); 28 | * 29 | * 30 | * 31 | * // Just sniff for the accepted encoding 32 | * $encoding = HTTP_Encoder::getAcceptedEncoding(); 33 | * 34 | * 35 | * For more control over headers, use getHeaders() and getData() and send your 36 | * own output. 37 | * 38 | * Note: If you don't need header mgmt, use PHP's native gzencode, gzdeflate, 39 | * and gzcompress functions for gzip, deflate, and compress-encoding 40 | * respectively. 41 | * 42 | * @package Minify 43 | * @subpackage HTTP 44 | * @author Stephen Clay 45 | */ 46 | class HTTP_Encoder { 47 | 48 | /** 49 | * Should the encoder allow HTTP encoding to IE6? 50 | * 51 | * If you have many IE6 users and the bandwidth savings is worth troubling 52 | * some of them, set this to true. 53 | * 54 | * By default, encoding is only offered to IE7+. When this is true, 55 | * getAcceptedEncoding() will return an encoding for IE6 if its user agent 56 | * string contains "SV1". This has been documented in many places as "safe", 57 | * but there seem to be remaining, intermittent encoding bugs in patched 58 | * IE6 on the wild web. 59 | * 60 | * @var bool 61 | */ 62 | public static $encodeToIe6 = true; 63 | 64 | 65 | /** 66 | * Default compression level for zlib operations 67 | * 68 | * This level is used if encode() is not given a $compressionLevel 69 | * 70 | * @var int 71 | */ 72 | public static $compressionLevel = 6; 73 | 74 | 75 | /** 76 | * Get an HTTP Encoder object 77 | * 78 | * @param array $spec options 79 | * 80 | * 'content': (string required) content to be encoded 81 | * 82 | * 'type': (string) if set, the Content-Type header will have this value. 83 | * 84 | * 'method: (string) only set this if you are forcing a particular encoding 85 | * method. If not set, the best method will be chosen by getAcceptedEncoding() 86 | * The available methods are 'gzip', 'deflate', 'compress', and '' (no 87 | * encoding) 88 | */ 89 | public function __construct($spec) 90 | { 91 | $this->_useMbStrlen = (function_exists('mb_strlen') 92 | && (ini_get('mbstring.func_overload') !== '') 93 | && ((int)ini_get('mbstring.func_overload') & 2)); 94 | $this->_content = $spec['content']; 95 | $this->_headers['Content-Length'] = $this->_useMbStrlen 96 | ? (string)mb_strlen($this->_content, '8bit') 97 | : (string)strlen($this->_content); 98 | if (isset($spec['type'])) { 99 | $this->_headers['Content-Type'] = $spec['type']; 100 | } 101 | if (isset($spec['method']) 102 | && in_array($spec['method'], array('gzip', 'deflate', 'compress', ''))) 103 | { 104 | $this->_encodeMethod = array($spec['method'], $spec['method']); 105 | } else { 106 | $this->_encodeMethod = self::getAcceptedEncoding(); 107 | } 108 | } 109 | 110 | /** 111 | * Get content in current form 112 | * 113 | * Call after encode() for encoded content. 114 | * 115 | * @return string 116 | */ 117 | public function getContent() 118 | { 119 | return $this->_content; 120 | } 121 | 122 | /** 123 | * Get array of output headers to be sent 124 | * 125 | * E.g. 126 | * 127 | * array( 128 | * 'Content-Length' => '615' 129 | * ,'Content-Encoding' => 'x-gzip' 130 | * ,'Vary' => 'Accept-Encoding' 131 | * ) 132 | * 133 | * 134 | * @return array 135 | */ 136 | public function getHeaders() 137 | { 138 | return $this->_headers; 139 | } 140 | 141 | /** 142 | * Send output headers 143 | * 144 | * You must call this before headers are sent and it probably cannot be 145 | * used in conjunction with zlib output buffering / mod_gzip. Errors are 146 | * not handled purposefully. 147 | * 148 | * @see getHeaders() 149 | */ 150 | public function sendHeaders() 151 | { 152 | foreach ($this->_headers as $name => $val) { 153 | header($name . ': ' . $val); 154 | } 155 | } 156 | 157 | /** 158 | * Send output headers and content 159 | * 160 | * A shortcut for sendHeaders() and echo getContent() 161 | * 162 | * You must call this before headers are sent and it probably cannot be 163 | * used in conjunction with zlib output buffering / mod_gzip. Errors are 164 | * not handled purposefully. 165 | */ 166 | public function sendAll() 167 | { 168 | $this->sendHeaders(); 169 | echo $this->_content; 170 | } 171 | 172 | /** 173 | * Determine the client's best encoding method from the HTTP Accept-Encoding 174 | * header. 175 | * 176 | * If no Accept-Encoding header is set, or the browser is IE before v6 SP2, 177 | * this will return ('', ''), the "identity" encoding. 178 | * 179 | * A syntax-aware scan is done of the Accept-Encoding, so the method must 180 | * be non 0. The methods are favored in order of gzip, deflate, then 181 | * compress. Deflate is always smallest and generally faster, but is 182 | * rarely sent by servers, so client support could be buggier. 183 | * 184 | * @param bool $allowCompress allow the older compress encoding 185 | * 186 | * @param bool $allowDeflate allow the more recent deflate encoding 187 | * 188 | * @return array two values, 1st is the actual encoding method, 2nd is the 189 | * alias of that method to use in the Content-Encoding header (some browsers 190 | * call gzip "x-gzip" etc.) 191 | */ 192 | public static function getAcceptedEncoding($allowCompress = true, $allowDeflate = true) 193 | { 194 | // @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html 195 | 196 | if (! isset($_SERVER['HTTP_ACCEPT_ENCODING']) 197 | || self::isBuggyIe()) 198 | { 199 | return array('', ''); 200 | } 201 | $ae = $_SERVER['HTTP_ACCEPT_ENCODING']; 202 | // gzip checks (quick) 203 | if (0 === strpos($ae, 'gzip,') // most browsers 204 | || 0 === strpos($ae, 'deflate, gzip,') // opera 205 | ) { 206 | return array('gzip', 'gzip'); 207 | } 208 | // gzip checks (slow) 209 | if (preg_match( 210 | '@(?:^|,)\\s*((?:x-)?gzip)\\s*(?:$|,|;\\s*q=(?:0\\.|1))@' 211 | ,$ae 212 | ,$m)) { 213 | return array('gzip', $m[1]); 214 | } 215 | if ($allowDeflate) { 216 | // deflate checks 217 | $aeRev = strrev($ae); 218 | if (0 === strpos($aeRev, 'etalfed ,') // ie, webkit 219 | || 0 === strpos($aeRev, 'etalfed,') // gecko 220 | || 0 === strpos($ae, 'deflate,') // opera 221 | // slow parsing 222 | || preg_match( 223 | '@(?:^|,)\\s*deflate\\s*(?:$|,|;\\s*q=(?:0\\.|1))@', $ae)) { 224 | return array('deflate', 'deflate'); 225 | } 226 | } 227 | if ($allowCompress && preg_match( 228 | '@(?:^|,)\\s*((?:x-)?compress)\\s*(?:$|,|;\\s*q=(?:0\\.|1))@' 229 | ,$ae 230 | ,$m)) { 231 | return array('compress', $m[1]); 232 | } 233 | return array('', ''); 234 | } 235 | 236 | /** 237 | * Encode (compress) the content 238 | * 239 | * If the encode method is '' (none) or compression level is 0, or the 'zlib' 240 | * extension isn't loaded, we return false. 241 | * 242 | * Then the appropriate gz_* function is called to compress the content. If 243 | * this fails, false is returned. 244 | * 245 | * The header "Vary: Accept-Encoding" is added. If encoding is successful, 246 | * the Content-Length header is updated, and Content-Encoding is also added. 247 | * 248 | * @param int $compressionLevel given to zlib functions. If not given, the 249 | * class default will be used. 250 | * 251 | * @return bool success true if the content was actually compressed 252 | */ 253 | public function encode($compressionLevel = null) 254 | { 255 | if (! self::isBuggyIe()) { 256 | $this->_headers['Vary'] = 'Accept-Encoding'; 257 | } 258 | if (null === $compressionLevel) { 259 | $compressionLevel = self::$compressionLevel; 260 | } 261 | if ('' === $this->_encodeMethod[0] 262 | || ($compressionLevel == 0) 263 | || !extension_loaded('zlib')) 264 | { 265 | return false; 266 | } 267 | if ($this->_encodeMethod[0] === 'deflate') { 268 | $encoded = gzdeflate($this->_content, $compressionLevel); 269 | } elseif ($this->_encodeMethod[0] === 'gzip') { 270 | $encoded = gzencode($this->_content, $compressionLevel); 271 | } else { 272 | $encoded = gzcompress($this->_content, $compressionLevel); 273 | } 274 | if (false === $encoded) { 275 | return false; 276 | } 277 | $this->_headers['Content-Length'] = $this->_useMbStrlen 278 | ? (string)mb_strlen($encoded, '8bit') 279 | : (string)strlen($encoded); 280 | $this->_headers['Content-Encoding'] = $this->_encodeMethod[1]; 281 | $this->_content = $encoded; 282 | return true; 283 | } 284 | 285 | /** 286 | * Encode and send appropriate headers and content 287 | * 288 | * This is a convenience method for common use of the class 289 | * 290 | * @param string $content 291 | * 292 | * @param int $compressionLevel given to zlib functions. If not given, the 293 | * class default will be used. 294 | * 295 | * @return bool success true if the content was actually compressed 296 | */ 297 | public static function output($content, $compressionLevel = null) 298 | { 299 | if (null === $compressionLevel) { 300 | $compressionLevel = self::$compressionLevel; 301 | } 302 | $he = new HTTP_Encoder(array('content' => $content)); 303 | $ret = $he->encode($compressionLevel); 304 | $he->sendAll(); 305 | return $ret; 306 | } 307 | 308 | /** 309 | * Is the browser an IE version earlier than 6 SP2? 310 | * 311 | * @return bool 312 | */ 313 | public static function isBuggyIe() 314 | { 315 | if (empty($_SERVER['HTTP_USER_AGENT'])) { 316 | return false; 317 | } 318 | $ua = $_SERVER['HTTP_USER_AGENT']; 319 | // quick escape for non-IEs 320 | if (0 !== strpos($ua, 'Mozilla/4.0 (compatible; MSIE ') 321 | || false !== strpos($ua, 'Opera')) { 322 | return false; 323 | } 324 | // no regex = faaast 325 | $version = (float)substr($ua, 30); 326 | return self::$encodeToIe6 327 | ? ($version < 6 || ($version == 6 && false === strpos($ua, 'SV1'))) 328 | : ($version < 7); 329 | } 330 | 331 | protected $_content = ''; 332 | protected $_headers = array(); 333 | protected $_encodeMethod = array('', ''); 334 | protected $_useMbStrlen = false; 335 | } 336 | -------------------------------------------------------------------------------- /system/expressionengine/third_party/minimee/libraries/HTTP/ConditionalGet.php: -------------------------------------------------------------------------------- 1 | 13 | * list($updateTime, $content) = getDbUpdateAndContent(); 14 | * $cg = new HTTP_ConditionalGet(array( 15 | * 'lastModifiedTime' => $updateTime 16 | * ,'isPublic' => true 17 | * )); 18 | * $cg->sendHeaders(); 19 | * if ($cg->cacheIsValid) { 20 | * exit(); 21 | * } 22 | * echo $content; 23 | * 24 | * 25 | * E.g. Shortcut for the above 26 | * 27 | * HTTP_ConditionalGet::check($updateTime, true); // exits if client has cache 28 | * echo $content; 29 | * 30 | * 31 | * E.g. Content from DB with no update time: 32 | * 33 | * $content = getContentFromDB(); 34 | * $cg = new HTTP_ConditionalGet(array( 35 | * 'contentHash' => md5($content) 36 | * )); 37 | * $cg->sendHeaders(); 38 | * if ($cg->cacheIsValid) { 39 | * exit(); 40 | * } 41 | * echo $content; 42 | * 43 | * 44 | * E.g. Static content with some static includes: 45 | * 46 | * // before content 47 | * $cg = new HTTP_ConditionalGet(array( 48 | * 'lastUpdateTime' => max( 49 | * filemtime(__FILE__) 50 | * ,filemtime('/path/to/header.inc') 51 | * ,filemtime('/path/to/footer.inc') 52 | * ) 53 | * )); 54 | * $cg->sendHeaders(); 55 | * if ($cg->cacheIsValid) { 56 | * exit(); 57 | * } 58 | * 59 | * @package Minify 60 | * @subpackage HTTP 61 | * @author Stephen Clay 62 | */ 63 | class HTTP_ConditionalGet { 64 | 65 | /** 66 | * Does the client have a valid copy of the requested resource? 67 | * 68 | * You'll want to check this after instantiating the object. If true, do 69 | * not send content, just call sendHeaders() if you haven't already. 70 | * 71 | * @var bool 72 | */ 73 | public $cacheIsValid = null; 74 | 75 | /** 76 | * @param array $spec options 77 | * 78 | * 'isPublic': (bool) if false, the Cache-Control header will contain 79 | * "private", allowing only browser caching. (default false) 80 | * 81 | * 'lastModifiedTime': (int) if given, both ETag AND Last-Modified headers 82 | * will be sent with content. This is recommended. 83 | * 84 | * 'encoding': (string) if set, the header "Vary: Accept-Encoding" will 85 | * always be sent and a truncated version of the encoding will be appended 86 | * to the ETag. E.g. "pub123456;gz". This will also trigger a more lenient 87 | * checking of the client's If-None-Match header, as the encoding portion of 88 | * the ETag will be stripped before comparison. 89 | * 90 | * 'contentHash': (string) if given, only the ETag header can be sent with 91 | * content (only HTTP1.1 clients can conditionally GET). The given string 92 | * should be short with no quote characters and always change when the 93 | * resource changes (recommend md5()). This is not needed/used if 94 | * lastModifiedTime is given. 95 | * 96 | * 'eTag': (string) if given, this will be used as the ETag header rather 97 | * than values based on lastModifiedTime or contentHash. Also the encoding 98 | * string will not be appended to the given value as described above. 99 | * 100 | * 'invalidate': (bool) if true, the client cache will be considered invalid 101 | * without testing. Effectively this disables conditional GET. 102 | * (default false) 103 | * 104 | * 'maxAge': (int) if given, this will set the Cache-Control max-age in 105 | * seconds, and also set the Expires header to the equivalent GMT date. 106 | * After the max-age period has passed, the browser will again send a 107 | * conditional GET to revalidate its cache. 108 | */ 109 | public function __construct($spec) 110 | { 111 | $scope = (isset($spec['isPublic']) && $spec['isPublic']) 112 | ? 'public' 113 | : 'private'; 114 | $maxAge = 0; 115 | // backwards compatibility (can be removed later) 116 | if (isset($spec['setExpires']) 117 | && is_numeric($spec['setExpires']) 118 | && ! isset($spec['maxAge'])) { 119 | $spec['maxAge'] = $spec['setExpires'] - $_SERVER['REQUEST_TIME']; 120 | } 121 | if (isset($spec['maxAge'])) { 122 | $maxAge = $spec['maxAge']; 123 | $this->_headers['Expires'] = self::gmtDate( 124 | $_SERVER['REQUEST_TIME'] + $spec['maxAge'] 125 | ); 126 | } 127 | $etagAppend = ''; 128 | if (isset($spec['encoding'])) { 129 | $this->_stripEtag = true; 130 | $this->_headers['Vary'] = 'Accept-Encoding'; 131 | if ('' !== $spec['encoding']) { 132 | if (0 === strpos($spec['encoding'], 'x-')) { 133 | $spec['encoding'] = substr($spec['encoding'], 2); 134 | } 135 | $etagAppend = ';' . substr($spec['encoding'], 0, 2); 136 | } 137 | } 138 | if (isset($spec['lastModifiedTime'])) { 139 | $this->_setLastModified($spec['lastModifiedTime']); 140 | if (isset($spec['eTag'])) { // Use it 141 | $this->_setEtag($spec['eTag'], $scope); 142 | } else { // base both headers on time 143 | $this->_setEtag($spec['lastModifiedTime'] . $etagAppend, $scope); 144 | } 145 | } elseif (isset($spec['eTag'])) { // Use it 146 | $this->_setEtag($spec['eTag'], $scope); 147 | } elseif (isset($spec['contentHash'])) { // Use the hash as the ETag 148 | $this->_setEtag($spec['contentHash'] . $etagAppend, $scope); 149 | } 150 | $privacy = ($scope === 'private') 151 | ? ', private' 152 | : ''; 153 | $this->_headers['Cache-Control'] = "max-age={$maxAge}{$privacy}"; 154 | // invalidate cache if disabled, otherwise check 155 | $this->cacheIsValid = (isset($spec['invalidate']) && $spec['invalidate']) 156 | ? false 157 | : $this->_isCacheValid(); 158 | } 159 | 160 | /** 161 | * Get array of output headers to be sent 162 | * 163 | * In the case of 304 responses, this array will only contain the response 164 | * code header: array('_responseCode' => 'HTTP/1.0 304 Not Modified') 165 | * 166 | * Otherwise something like: 167 | * 168 | * array( 169 | * 'Cache-Control' => 'max-age=0, public' 170 | * ,'ETag' => '"foobar"' 171 | * ) 172 | * 173 | * 174 | * @return array 175 | */ 176 | public function getHeaders() 177 | { 178 | return $this->_headers; 179 | } 180 | 181 | /** 182 | * Set the Content-Length header in bytes 183 | * 184 | * With most PHP configs, as long as you don't flush() output, this method 185 | * is not needed and PHP will buffer all output and set Content-Length for 186 | * you. Otherwise you'll want to call this to let the client know up front. 187 | * 188 | * @param int $bytes 189 | * 190 | * @return int copy of input $bytes 191 | */ 192 | public function setContentLength($bytes) 193 | { 194 | return $this->_headers['Content-Length'] = $bytes; 195 | } 196 | 197 | /** 198 | * Send headers 199 | * 200 | * @see getHeaders() 201 | * 202 | * Note this doesn't "clear" the headers. Calling sendHeaders() will 203 | * call header() again (but probably have not effect) and getHeaders() will 204 | * still return the headers. 205 | * 206 | * @return null 207 | */ 208 | public function sendHeaders() 209 | { 210 | $headers = $this->_headers; 211 | if (array_key_exists('_responseCode', $headers)) { 212 | // FastCGI environments require 3rd arg to header() to be set 213 | list(, $code) = explode(' ', $headers['_responseCode'], 3); 214 | header($headers['_responseCode'], true, $code); 215 | unset($headers['_responseCode']); 216 | } 217 | foreach ($headers as $name => $val) { 218 | header($name . ': ' . $val); 219 | } 220 | } 221 | 222 | /** 223 | * Exit if the client's cache is valid for this resource 224 | * 225 | * This is a convenience method for common use of the class 226 | * 227 | * @param int $lastModifiedTime if given, both ETag AND Last-Modified headers 228 | * will be sent with content. This is recommended. 229 | * 230 | * @param bool $isPublic (default false) if true, the Cache-Control header 231 | * will contain "public", allowing proxies to cache the content. Otherwise 232 | * "private" will be sent, allowing only browser caching. 233 | * 234 | * @param array $options (default empty) additional options for constructor 235 | */ 236 | public static function check($lastModifiedTime = null, $isPublic = false, $options = array()) 237 | { 238 | if (null !== $lastModifiedTime) { 239 | $options['lastModifiedTime'] = (int)$lastModifiedTime; 240 | } 241 | $options['isPublic'] = (bool)$isPublic; 242 | $cg = new HTTP_ConditionalGet($options); 243 | $cg->sendHeaders(); 244 | if ($cg->cacheIsValid) { 245 | exit(); 246 | } 247 | } 248 | 249 | 250 | /** 251 | * Get a GMT formatted date for use in HTTP headers 252 | * 253 | * 254 | * header('Expires: ' . HTTP_ConditionalGet::gmtdate($time)); 255 | * 256 | * 257 | * @param int $time unix timestamp 258 | * 259 | * @return string 260 | */ 261 | public static function gmtDate($time) 262 | { 263 | return gmdate('D, d M Y H:i:s \G\M\T', $time); 264 | } 265 | 266 | protected $_headers = array(); 267 | protected $_lmTime = null; 268 | protected $_etag = null; 269 | protected $_stripEtag = false; 270 | 271 | /** 272 | * @param string $hash 273 | * 274 | * @param string $scope 275 | */ 276 | protected function _setEtag($hash, $scope) 277 | { 278 | $this->_etag = '"' . substr($scope, 0, 3) . $hash . '"'; 279 | $this->_headers['ETag'] = $this->_etag; 280 | } 281 | 282 | /** 283 | * @param int $time 284 | */ 285 | protected function _setLastModified($time) 286 | { 287 | $this->_lmTime = (int)$time; 288 | $this->_headers['Last-Modified'] = self::gmtDate($time); 289 | } 290 | 291 | /** 292 | * Determine validity of client cache and queue 304 header if valid 293 | * 294 | * @return bool 295 | */ 296 | protected function _isCacheValid() 297 | { 298 | if (null === $this->_etag) { 299 | // lmTime is copied to ETag, so this condition implies that the 300 | // server sent neither ETag nor Last-Modified, so the client can't 301 | // possibly has a valid cache. 302 | return false; 303 | } 304 | $isValid = ($this->resourceMatchedEtag() || $this->resourceNotModified()); 305 | if ($isValid) { 306 | $this->_headers['_responseCode'] = 'HTTP/1.0 304 Not Modified'; 307 | } 308 | return $isValid; 309 | } 310 | 311 | /** 312 | * @return bool 313 | */ 314 | protected function resourceMatchedEtag() 315 | { 316 | if (!isset($_SERVER['HTTP_IF_NONE_MATCH'])) { 317 | return false; 318 | } 319 | $clientEtagList = get_magic_quotes_gpc() 320 | ? stripslashes($_SERVER['HTTP_IF_NONE_MATCH']) 321 | : $_SERVER['HTTP_IF_NONE_MATCH']; 322 | $clientEtags = explode(',', $clientEtagList); 323 | 324 | $compareTo = $this->normalizeEtag($this->_etag); 325 | foreach ($clientEtags as $clientEtag) { 326 | if ($this->normalizeEtag($clientEtag) === $compareTo) { 327 | // respond with the client's matched ETag, even if it's not what 328 | // we would've sent by default 329 | $this->_headers['ETag'] = trim($clientEtag); 330 | return true; 331 | } 332 | } 333 | return false; 334 | } 335 | 336 | /** 337 | * @param string $etag 338 | * 339 | * @return string 340 | */ 341 | protected function normalizeEtag($etag) { 342 | $etag = trim($etag); 343 | return $this->_stripEtag 344 | ? preg_replace('/;\\w\\w"$/', '"', $etag) 345 | : $etag; 346 | } 347 | 348 | /** 349 | * @return bool 350 | */ 351 | protected function resourceNotModified() 352 | { 353 | if (!isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { 354 | return false; 355 | } 356 | // strip off IE's extra data (semicolon) 357 | list($ifModifiedSince) = explode(';', $_SERVER['HTTP_IF_MODIFIED_SINCE'], 2); 358 | if (strtotime($ifModifiedSince) >= $this->_lmTime) { 359 | // Apache 2.2's behavior. If there was no ETag match, send the 360 | // non-encoded version of the ETag value. 361 | $this->_headers['ETag'] = $this->normalizeEtag($this->_etag); 362 | return true; 363 | } 364 | return false; 365 | } 366 | } 367 | -------------------------------------------------------------------------------- /system/expressionengine/third_party/minimee/ext.minimee.php: -------------------------------------------------------------------------------- 1 | 23 | * @license http://www.opensource.org/licenses/bsd-license.php BSD license 24 | * @link http://johndwells.com/software/minimee 25 | */ 26 | class Minimee_ext { 27 | 28 | /** 29 | * EE, obviously 30 | */ 31 | private $EE; 32 | 33 | 34 | /** 35 | * Standard Extension stuff 36 | */ 37 | public $name = MINIMEE_NAME; 38 | public $version = MINIMEE_VER; 39 | public $description = MINIMEE_DESC; 40 | public $docs_url = MINIMEE_DOCS; 41 | public $settings = array(); 42 | public $settings_exist = 'y'; 43 | 44 | 45 | /** 46 | * Our magical config class 47 | */ 48 | public $config; 49 | 50 | 51 | /** 52 | * Reference to our cache 53 | */ 54 | public $cache; 55 | 56 | 57 | // ------------------------------------------------------ 58 | 59 | 60 | /** 61 | * Constructor 62 | * 63 | * NOTE: We never use the $settings variable passed to us, 64 | * because we want our Minimee_config object to always be in charge. 65 | * 66 | * @param mixed Settings array - only passed when activating a hook 67 | * @return void 68 | */ 69 | public function __construct($settings = array()) 70 | { 71 | // Got EE? 72 | $this->EE =& get_instance(); 73 | 74 | // grab a reference to our cache 75 | $this->cache =& Minimee_helper::cache(); 76 | 77 | // grab instance of our config object 78 | $this->config = Minimee_helper::config(); 79 | } 80 | // ------------------------------------------------------ 81 | 82 | 83 | /** 84 | * Activate Extension 85 | * 86 | * @return void 87 | */ 88 | public function activate_extension() 89 | { 90 | // reset our runtime to 'factory' defaults, and return as array 91 | $settings = $this->config->factory()->to_array(); 92 | 93 | // template_post_parse hook 94 | $this->EE->db->insert('extensions', array( 95 | 'class' => __CLASS__, 96 | 'hook' => 'template_post_parse', 97 | 'method' => 'template_post_parse', 98 | 'settings' => serialize($settings), 99 | 'priority' => 10, 100 | 'version' => $this->version, 101 | 'enabled' => 'y' 102 | )); 103 | 104 | // EE Debug Toolbar hook 105 | $this->EE->db->insert('extensions', array( 106 | 'class' => __CLASS__, 107 | 'hook' => 'ee_debug_toolbar_add_panel', 108 | 'method' => 'ee_debug_toolbar_add_panel', 109 | 'settings' => serialize($settings), 110 | 'priority' => 10, 111 | 'version' => $this->version, 112 | 'enabled' => 'y' 113 | )); 114 | 115 | Minimee_helper::log('Extension has been activated.', 3); 116 | } 117 | // ------------------------------------------------------ 118 | 119 | 120 | /** 121 | * Disable Extension 122 | * 123 | * @return void 124 | */ 125 | public function disable_extension() 126 | { 127 | $this->EE->db->where('class', __CLASS__); 128 | $this->EE->db->delete('extensions'); 129 | 130 | Minimee_helper::log('Extension has been disabled.', 3); 131 | } 132 | // ------------------------------------------------------ 133 | 134 | 135 | /** 136 | * Method for template_post_parse hook 137 | * 138 | * @param array Array of debug panels 139 | * @param arrat A collection of toolbar settings and values 140 | * @return array The amended array of debug panels 141 | */ 142 | public function ee_debug_toolbar_add_panel($panels, $view) 143 | { 144 | // do nothing if not a page 145 | if(REQ != 'PAGE') return $panels; 146 | 147 | // play nice with others 148 | $panels = ($this->EE->extensions->last_call != '' ? $this->EE->extensions->last_call : $panels); 149 | 150 | $panels['minimee'] = new Eedt_panel_model(); 151 | $panels['minimee']->set_name('minimee'); 152 | $panels['minimee']->set_button_label("Minimee"); 153 | $panels['minimee']->set_button_icon("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDIxIDc5LjE1NTc3MiwgMjAxNC8wMS8xMy0xOTo0NDowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTQgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NjQxMzVDNTBGRDdCMTFFMzhDQzk5MzI3QzQ4QkE1NDUiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NjQxMzVDNTFGRDdCMTFFMzhDQzk5MzI3QzQ4QkE1NDUiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpFQkM3RkNGNUZENzUxMUUzOENDOTkzMjdDNDhCQTU0NSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpFQkM3RkNGNkZENzUxMUUzOENDOTkzMjdDNDhCQTU0NSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PpUOrpsAAAAQSURBVHjaYvj//z8DQIABAAj8Av7bok0WAAAAAElFTkSuQmCC"); 154 | $panels['minimee']->set_panel_contents($this->EE->load->view('eedebug_panel', array('logs' => Minimee_helper::get_log()), TRUE)); 155 | 156 | if(Minimee_helper::log_has_error()) 157 | { 158 | $panels['minimee']->set_panel_css_class('flash'); 159 | } 160 | 161 | return $panels; 162 | } 163 | // ------------------------------------------------------ 164 | 165 | 166 | /** 167 | * Alias for backwards-compatibility with M1 168 | */ 169 | public function minify_html($template, $sub, $site_id) 170 | { 171 | return $this->template_post_parse($template, $sub, $site_id); 172 | } 173 | // ------------------------------------------------------ 174 | 175 | 176 | 177 | /** 178 | * Method for template_post_parse hook 179 | * 180 | * @param string Parsed template string 181 | * @param bool Whether is a sub-template or not 182 | * @param string Site ID 183 | * @return string Template string, possibly minified 184 | */ 185 | public function template_post_parse($template, $sub, $site_id) 186 | { 187 | // play nice with others 188 | if (isset($this->EE->extensions->last_call) && $this->EE->extensions->last_call) 189 | { 190 | $template = $this->EE->extensions->last_call; 191 | } 192 | 193 | // do nothing if not final template 194 | if ($sub !== FALSE) 195 | { 196 | return $template; 197 | } 198 | 199 | // see if we need to post-render any plugin methods 200 | if (isset($this->cache['template_post_parse'])) 201 | { 202 | if ( ! class_exists('Minimee')) 203 | { 204 | include_once PATH_THIRD . 'minimee/pi.minimee.php'; 205 | } 206 | 207 | // create a new instance of Minimee each time to guarantee defaults 208 | $m = new Minimee(); 209 | 210 | // save our TMPL values to put back into place once finished 211 | $tagparams = $this->EE->TMPL->tagparams; 212 | 213 | // loop through & call each method 214 | foreach($this->cache['template_post_parse'] as $needle => $tag) 215 | { 216 | Minimee_helper::log('Calling Minimee::display("' . $tag['method'] . '") during template_post_parse: ' . serialize($tag['tagparams']), 3); 217 | 218 | $this->EE->TMPL->tagparams = $tag['tagparams']; 219 | 220 | // our second parameter tells Minimee we are calling from template_post_parse 221 | $out = $m->display($tag['method'], TRUE); 222 | 223 | // replace our needle with output 224 | $template = str_replace(LD.$needle.RD, $out, $template); 225 | 226 | // reset Minimee for next loop 227 | $m->reset(); 228 | } 229 | 230 | // put things back into place 231 | $this->EE->TMPL->tagparams = $tagparams; 232 | } 233 | 234 | // do nothing if not (likely) html! 235 | if ( ! preg_match('/webpage|static/i', $this->EE->TMPL->template_type)) 236 | { 237 | return $template; 238 | } 239 | 240 | // Are we configured to run through HTML minifier? 241 | if ($this->config->is_no('minify_html')) 242 | { 243 | Minimee_helper::log('HTML minification is disabled.', 3); 244 | return $template; 245 | } 246 | 247 | // is Minimee nonetheless disabled? 248 | if ($this->config->is_yes('disable')) 249 | { 250 | Minimee_helper::log('HTML minification aborted because Minimee has been disabled completely.', 3); 251 | return $template; 252 | } 253 | 254 | // we've made it this far, so... 255 | Minimee_helper::log('Running HTML minification.', 3); 256 | 257 | Minimee_helper::library('html'); 258 | 259 | // run css & js minification? 260 | $opts = array(); 261 | if($this->config->is_yes('minify_css')) 262 | { 263 | $opts['cssMinifier'] = array('Minify_CSS', 'minify'); 264 | } 265 | if($this->config->is_yes('minify_js')) 266 | { 267 | $opts['jsMinifier'] = array('JSMin', 'minify'); 268 | } 269 | 270 | return Minify_HTML::minify($template, $opts); 271 | } 272 | // ------------------------------------------------------ 273 | 274 | 275 | /** 276 | * Save settings 277 | * 278 | * @return void 279 | */ 280 | public function save_settings() 281 | { 282 | if (empty($_POST)) 283 | { 284 | Minimee_helper::log($this->EE->lang->line('unauthorized_access'), 1); 285 | } 286 | 287 | else 288 | { 289 | // grab our posted form 290 | $settings = $_POST; 291 | 292 | // checkboxes are funny: if they don't exist in post, they must be explicitly added and set to "no" 293 | $checkboxes = array( 294 | 'combine_css', 295 | 'combine_js', 296 | 'minify_css', 297 | 'minify_html', 298 | 'minify_js' 299 | ); 300 | 301 | foreach($checkboxes as $key) 302 | { 303 | if ( ! isset($settings[$key])) 304 | { 305 | $settings[$key] = 'no'; 306 | } 307 | } 308 | 309 | // run our $settings through sanitise_settings() 310 | $settings = $this->config->sanitise_settings(array_merge($this->config->get_allowed(), $settings)); 311 | 312 | // update db 313 | $this->EE->db->where('class', __CLASS__) 314 | ->update('extensions', array('settings' => serialize($settings))); 315 | 316 | Minimee_helper::log('Extension settings have been saved.', 3); 317 | 318 | // save the environment 319 | unset($settings); 320 | 321 | // let frontend know we succeeeded 322 | $this->EE->session->set_flashdata( 323 | 'message_success', 324 | $this->EE->lang->line('preferences_updated') 325 | ); 326 | 327 | $this->EE->functions->redirect(BASE.AMP.'C=addons_extensions'.AMP.'M=extension_settings'.AMP.'file=minimee'); 328 | } 329 | } 330 | // ------------------------------------------------------ 331 | 332 | 333 | /** 334 | * Settings Form 335 | * 336 | * @param Array Current settings from DB 337 | * @return void 338 | */ 339 | public function settings_form($current) 340 | { 341 | $this->EE->load->helper('form'); 342 | $this->EE->load->library('table'); 343 | 344 | // Merge the contents of our db with the allowed 345 | $current = array_merge($this->config->get_allowed(), $current); 346 | 347 | // Used to determine if any advanced settings have been changed 348 | $clean = $this->config->sanitise_settings($this->config->get_allowed()); 349 | $basic = array('disable', 'cache_path', 'cache_url', 'combine_css', 'combine_js', 'minify_css', 'minify_js', 'minify_html'); 350 | 351 | // remove basic settings 352 | $diff = array_diff(array_keys($clean), $basic); 353 | $hide_advanced_on_startup = 'TRUE'; 354 | 355 | foreach($diff as $key) 356 | { 357 | if($clean[$key] != $current[$key]) 358 | { 359 | $hide_advanced_on_startup = FALSE; 360 | break; 361 | } 362 | } 363 | 364 | // view vars 365 | $vars = array( 366 | 'config_warning' => ($this->config->location != 'db') ? lang('config_location_warning') : '', 367 | 'form_open' => form_open('C=addons_extensions'.AMP.'M=save_extension_settings'.AMP.'file=minimee'), 368 | 'settings' => $current, 369 | 'hide_advanced_on_startup' => $hide_advanced_on_startup, 370 | 'flashdata_success' => $this->EE->session->flashdata('message_success') 371 | ); 372 | 373 | // return our view 374 | return $this->EE->load->view('settings_form', $vars, TRUE); 375 | } 376 | // ------------------------------------------------------ 377 | 378 | 379 | /** 380 | * Update Extension 381 | * 382 | * @param string String value of current version 383 | * @return mixed void on update / false if none 384 | */ 385 | public function update_extension($current = '') 386 | { 387 | /** 388 | * Up-to-date 389 | */ 390 | if ($current == '' OR $current == $this->version) 391 | { 392 | return FALSE; 393 | } 394 | 395 | /** 396 | * 2.0.0 397 | * 398 | * - refactor to use new Minimee_config object 399 | */ 400 | if (version_compare($current, '2.0.0', '<')) 401 | { 402 | $query = $this->EE->db 403 | ->select('settings') 404 | ->from('extensions') 405 | ->where('class', __CLASS__) 406 | ->limit(1) 407 | ->get(); 408 | 409 | if ($query->num_rows() > 0) 410 | { 411 | $settings = unserialize($query->row()->settings); 412 | 413 | // migrate combine 414 | if(array_key_exists('combine', $settings)) 415 | { 416 | $settings['combine_css'] = $settings['combine']; 417 | $settings['combine_js'] = $settings['combine']; 418 | unset($settings['combine']); 419 | } 420 | 421 | // migrate minify 422 | if(array_key_exists('minify', $settings)) 423 | { 424 | $settings['minify_css'] = $settings['minify']; 425 | $settings['minify_js'] = $settings['minify']; 426 | $settings['minify_html'] = $settings['minify']; 427 | unset($settings['minify']); 428 | } 429 | 430 | // Sanitise & merge to get a complete up-to-date array of settings 431 | $settings = $this->config->sanitise_settings(array_merge($this->config->get_allowed(), $settings)); 432 | 433 | // update db 434 | $this->EE->db 435 | ->where('class', __CLASS__) 436 | ->update('extensions', array( 437 | 'hook' => 'template_post_parse', 438 | 'method' => 'template_post_parse', 439 | 'settings' => serialize($settings) 440 | )); 441 | } 442 | 443 | $query->free_result(); 444 | 445 | Minimee_helper::log('Upgraded to 2.0.0', 3); 446 | } 447 | 448 | 449 | /** 450 | * 2.1.8 451 | * 452 | * - Include debug panel via EE Debug Toolbar 453 | */ 454 | if (version_compare($current, '2.1.8', '<')) 455 | { 456 | // grab a copy of our settings 457 | $query = $this->EE->db 458 | ->select('settings') 459 | ->from('extensions') 460 | ->where('class', __CLASS__) 461 | ->limit(1) 462 | ->get(); 463 | 464 | if ($query->num_rows() > 0) 465 | { 466 | $settings = $query->row()->settings; 467 | } 468 | else 469 | { 470 | $settings = serialize($this->config->factory()->to_array()); 471 | } 472 | 473 | // add extension hook 474 | $this->EE->db->insert('extensions', array( 475 | 'class' => __CLASS__, 476 | 'hook' => 'ee_debug_toolbar_add_panel', 477 | 'method' => 'ee_debug_toolbar_add_panel', 478 | 'settings' => $settings, 479 | 'priority' => 10, 480 | 'version' => $this->version, 481 | 'enabled' => 'y' 482 | )); 483 | 484 | $query->free_result(); 485 | 486 | Minimee_helper::log('Upgraded to 2.1.8', 3); 487 | } 488 | 489 | // update table row with version 490 | $this->EE->db->where('class', __CLASS__); 491 | $this->EE->db->update( 492 | 'extensions', 493 | array('version' => $this->version) 494 | ); 495 | 496 | Minimee_helper::log('Upgrade complete. Now running ' . $this->version, 3); 497 | } 498 | // ------------------------------------------------------ 499 | 500 | } 501 | // END CLASS 502 | 503 | 504 | /* End of file ext.minimee.php */ 505 | /* Location: ./system/expressionengine/third_party/minimee/ext.minimee.php */ --------------------------------------------------------------------------------