mirror of
				https://github.com/getgrav/grav.git
				synced 2025-10-31 10:25:59 +01:00 
			
		
		
		
	initial commit of new asset pipeline work + user agent handling
This commit is contained in:
		
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -7,6 +7,8 @@ composer.lock | ||||
| # Grav Specific | ||||
| cache/* | ||||
| !cache/.* | ||||
| assets/* | ||||
| !assets/.* | ||||
| logs/* | ||||
| !logs/.* | ||||
| images/* | ||||
|   | ||||
| @@ -20,6 +20,8 @@ | ||||
|         "doctrine/cache": "1.4.*@dev", | ||||
|         "tracy/tracy": "dev-master", | ||||
|         "gregwar/image": "dev-master", | ||||
|         "ircmaxell/password-compat": "1.0.*" | ||||
|         "ircmaxell/password-compat": "1.0.*", | ||||
|         "mrclay/minify": "dev-master", | ||||
|         "ornicar/php-user-agent": "1.0.*@dev" | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -35,6 +35,7 @@ try { | ||||
|     $registry->store('Twig', new Twig); | ||||
|     $registry->store('Pages', new Page\Pages); | ||||
|     $registry->store('Taxonomy', new Taxonomy); | ||||
|     $registry->store('Assets', new Assets); | ||||
|  | ||||
|     $grav->process(); | ||||
|  | ||||
|   | ||||
| @@ -31,6 +31,13 @@ twig: | ||||
|   auto_reload: true                     # Refresh cache on changes | ||||
|   autoescape: false                     # Autoescape Twig vars | ||||
|  | ||||
| assets:                                 # Configuration for Assets Manager (JS, CSS) | ||||
|   css_pipeline: false                   # The CSS pipeline is the unification of multiple CSS resources into one file | ||||
|   css_minify: true                      # Minify the CSS during pipelining | ||||
|   css_rewrite: true                     # Rewrite any CSS relative URLs during pipelining | ||||
|   js_pipeline: false                    # The JS pipeline is the unification of multiple JS resources into one file | ||||
|   js_minify: true                       # Minify the JS during pipelining | ||||
|  | ||||
| debugger: | ||||
|   enabled: true                         # Enable Grav debugger | ||||
|   max_depth: 10                         # How many nested levels to display for objects or arrays | ||||
|   | ||||
| @@ -12,6 +12,7 @@ if (!defined('ROOT_DIR')) { | ||||
| define('USER_PATH', 'user/'); | ||||
| define('USER_DIR', ROOT_DIR . USER_PATH); | ||||
| define('SYSTEM_DIR', ROOT_DIR .'system/'); | ||||
| define('ASSETS_DIR', ROOT_DIR . 'assets/'); | ||||
| define('CACHE_DIR', ROOT_DIR .'cache/'); | ||||
| define('IMAGES_DIR', ROOT_DIR . 'images/'); | ||||
| define('LOG_DIR', ROOT_DIR .'logs/'); | ||||
|   | ||||
							
								
								
									
										682
									
								
								system/src/Grav/Common/Assets.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										682
									
								
								system/src/Grav/Common/Assets.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,682 @@ | ||||
| <?php | ||||
| namespace Grav\Common; | ||||
|  | ||||
| use \Tracy\Debugger; | ||||
| use Closure; | ||||
| use Exception; | ||||
| use FilesystemIterator; | ||||
| use RecursiveDirectoryIterator; | ||||
| use RecursiveIteratorIterator; | ||||
| use RegexIterator; | ||||
|  | ||||
| define('CSS_ASSET', true); | ||||
| define('JS_ASSET', false); | ||||
|  | ||||
| /** | ||||
|  * The Wrapper object for handling JS, CSS assets | ||||
|  * | ||||
|  * Based on stolz/assets package modified for use with Grav | ||||
|  * | ||||
|  * | ||||
|  * @author RocketTheme | ||||
|  * @license MIT | ||||
|  */ | ||||
|  | ||||
| class Assets | ||||
| { | ||||
|     /** @const Regex to match CSS and JavaScript files */ | ||||
|     const DEFAULT_REGEX = '/.\.(css|js)$/i'; | ||||
|  | ||||
|     /** @const Regex to match CSS files */ | ||||
|     const CSS_REGEX = '/.\.css$/i'; | ||||
|  | ||||
|     /** @const Regex to match JavaScript files */ | ||||
|     const JS_REGEX = '/.\.js$/i'; | ||||
|  | ||||
|     /** @const Regex to match CSS urls */ | ||||
|     const CSS_URL_REGEX = '{url\([\'\"]((?!http|//).*?)[\'\"]\)}'; | ||||
|  | ||||
|     /** @const Regex to match CSS sourcemap comments */ | ||||
|     const CSS_SOURCEMAP_REGEX = '{\/\*# (.*) \*\/}'; | ||||
|  | ||||
|     /** | ||||
|      * Closure used by the pipeline to fetch assets. | ||||
|      * | ||||
|      * Useful when file_get_contents() function is not available in your PHP | ||||
|      * instalation or when you want to apply any kind of preprocessing to | ||||
|      * your assets before they get pipelined. | ||||
|      * | ||||
|      * The closure will receive as the only parameter a string with the path/URL of the asset and | ||||
|      * it should return the content of the asset file as a string. | ||||
|      * @var Closure | ||||
|      */ | ||||
|     protected $fetch_command; | ||||
|  | ||||
|     protected $css_pipeline = false; | ||||
|     protected $js_pipeline = false; | ||||
|  | ||||
|     protected $collections = array(); | ||||
|     protected $css = array(); | ||||
|     protected $js = array(); | ||||
|  | ||||
|     protected $config; | ||||
|     protected $theme_url; | ||||
|     protected $base_url; | ||||
|  | ||||
|     protected $css_minify = true; | ||||
|     protected $css_rewrite = true; | ||||
|     protected $js_minify = true; | ||||
|  | ||||
|     protected $css_no_pipeline = array(); | ||||
|     protected $js_no_pipeline = array(); | ||||
|  | ||||
|  | ||||
|     public function __construct(array $options = array()) | ||||
|     { | ||||
|         // Forward config options | ||||
|         if($options) | ||||
|             $this->config($options); | ||||
|     } | ||||
|  | ||||
|     public function init() | ||||
|     { | ||||
|         // $this->config = $config; | ||||
|  | ||||
|         $this->config = Registry::get('Config'); | ||||
|         $base_url = $this->config->get('system.base_url_relative'); | ||||
|         $theme = $this->config->get('system.pages.theme'); | ||||
|         $asset_config = (array)$this->config->get('system.assets'); | ||||
|  | ||||
|         $this->config($asset_config); | ||||
|         $this->base_url = $base_url; | ||||
|         $this->theme_url = $base_url .'/'. USER_PATH . basename(THEMES_DIR) .'/'. $theme; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Set up configuration options. | ||||
|      * | ||||
|      * All the class properties except 'js' and 'css' are accepted here. | ||||
|      * Also, an extra option 'autoload' may be passed containing an array of | ||||
|      * assets and/or collections that will be automatically added on startup. | ||||
|      * | ||||
|      * @param  array $options Configurable options. | ||||
|      * @return Manager | ||||
|      * @throws Exception | ||||
|      */ | ||||
|     public function config(array $config) | ||||
|     { | ||||
|         // Set pipeline modes | ||||
|         if(isset($config['css_pipeline'])) | ||||
|             $this->css_pipeline = $config['css_pipeline']; | ||||
|  | ||||
|         if(isset($config['js_pipeline'])) | ||||
|             $this->js_pipeline = $config['js_pipeline']; | ||||
|  | ||||
|         // Pipeline requires public dir | ||||
|         if(($this->js_pipeline || $this->css_pipeline) && ! is_dir(ASSETS_DIR)) | ||||
|             throw new Exception('Assets: Public dir not found'); | ||||
|  | ||||
|         // Set custom pipeline fetch command | ||||
|         if(isset($config['fetch_command']) and ($config['fetch_command'] instanceof Closure)) | ||||
|             $this->fetch_command = $config['fetch_command']; | ||||
|  | ||||
|         // Set CSS Minify state | ||||
|         if(isset($config['css_minify'])) | ||||
|             $this->css_minify = $config['css_minify']; | ||||
|  | ||||
|         if(isset($config['css_rewrite'])) | ||||
|             $this->css_rewrite = $config['css_rewrite']; | ||||
|  | ||||
|         // Set JS Minify state | ||||
|         if(isset($config['js_minify'])) | ||||
|             $this->js_minify = $config['js_minify']; | ||||
|  | ||||
|         // Set collections | ||||
|         if(isset($config['collections']) and is_array($config['collections'])) | ||||
|             $this->collections = $config['collections']; | ||||
|  | ||||
|         // Autoload assets | ||||
|         if(isset($config['autoload']) and is_array($config['autoload'])) | ||||
|         { | ||||
|             foreach($config['autoload'] as $asset) | ||||
|             { | ||||
|                 $this->add($asset); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Add an asset or a collection of assets. | ||||
|      * | ||||
|      * It automatically detects the asset type (JavaScript, CSS or collection). | ||||
|      * You may add more than one asset passing an array as argument. | ||||
|      * | ||||
|      * @param  mixed   $asset | ||||
|      * @param  int     $priority the priority, bigger comes first | ||||
|      * @param  bool    $pipeline false if this should not be pipelined | ||||
|      * @return Manager | ||||
|      */ | ||||
|     public function add($asset, $priority = 10, $pipeline = true) | ||||
|     { | ||||
|         // More than one asset | ||||
|         if(is_array($asset)) | ||||
|         { | ||||
|             foreach($asset as $a) | ||||
|                 $this->add($a, $priority, $pipeline); | ||||
|         } | ||||
|         // Collection | ||||
|         elseif(isset($this->collections[$asset])) | ||||
|         { | ||||
|             $this->add($this->collections[$asset], $priority, $pipeline); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             // JavaScript or CSS | ||||
|             $info = pathinfo($asset); | ||||
|             if(isset($info['extension'])) | ||||
|             { | ||||
|                 $ext = strtolower($info['extension']); | ||||
|                 if($ext === 'css') | ||||
|                     $this->addCss($asset, $priority, $pipeline); | ||||
|                 elseif($ext === 'js') | ||||
|                     $this->addJs($asset, $priority, $pipeline); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Add a CSS asset. | ||||
|      * | ||||
|      * It checks for duplicates. | ||||
|      * You may add more than one asset passing an array as argument. | ||||
|      * | ||||
|      * @param  mixed   $asset | ||||
|      * @param  int     $priority the priority, bigger comes first | ||||
|      * @param  bool    $pipeline false if this should not be pipelined | ||||
|      * @return Manager | ||||
|      */ | ||||
|     public function addCss($asset, $priority = 10, $pipeline = true) | ||||
|     { | ||||
|         if(is_array($asset)) | ||||
|         { | ||||
|             foreach($asset as $a) | ||||
|                 $this->addCss($a, $priority, $pipeline); | ||||
|  | ||||
|             return $this; | ||||
|         } | ||||
|  | ||||
|         if( ! $this->isRemoteLink($asset)) | ||||
|             $asset = $this->buildLocalLink($asset); | ||||
|  | ||||
|         if( ! in_array($asset, $this->css)) | ||||
|             $this->css[] = ['asset'=>$asset, 'priority'=>$priority, 'pipeline'=>$pipeline]; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Add a JavaScript asset. | ||||
|      * | ||||
|      * It checks for duplicates. | ||||
|      * You may add more than one asset passing an array as argument. | ||||
|      * | ||||
|      * @param  mixed   $asset | ||||
|      * @param  int     $priority the priority, bigger comes first | ||||
|      * @param  bool    $pipeline false if this should not be pipelined | ||||
|      * @return Manager | ||||
|      */ | ||||
|     public function addJs($asset, $priority = 10, $pipeline = true) | ||||
|     { | ||||
|         if(is_array($asset)) | ||||
|         { | ||||
|             foreach($asset as $a) | ||||
|                 $this->addJs($a, $priority, $pipeline); | ||||
|  | ||||
|             return $this; | ||||
|         } | ||||
|  | ||||
|         if( ! $this->isRemoteLink($asset)) | ||||
|             $asset = $this->buildLocalLink($asset); | ||||
|  | ||||
|         if( ! in_array($asset, $this->js)) | ||||
|             $this->js[] = ['asset'=>$asset, 'priority'=>$priority, 'pipeline'=>$pipeline]; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Build the CSS link tags. | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public function css() | ||||
|     { | ||||
|         if( ! $this->css) | ||||
|             return null; | ||||
|  | ||||
|         // Sort array by priorities (larger priority first) | ||||
|         usort($this->css, function ($a, $b) {return $a['priority'] - $b['priority'];}); | ||||
|         $this->css = array_reverse($this->css); | ||||
|  | ||||
|  | ||||
|  | ||||
|         $output = ''; | ||||
|         if($this->css_pipeline) { | ||||
|             $output .= '<link type="text/css" rel="stylesheet" href="'.$this->pipeline(CSS_ASSET).'" />'."\n"; | ||||
|  | ||||
|             foreach ($this->css_no_pipeline as $file) { | ||||
|                 $output .= '<link type="text/css" rel="stylesheet" href="'.$file['asset'].'" />'."\n"; | ||||
|             } | ||||
|             return $output; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         foreach($this->css as $file) | ||||
|             $output .= '<link type="text/css" rel="stylesheet" href="'.$file['asset'].'" />'."\n"; | ||||
|  | ||||
|         return $output; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Build the JavaScript script tags. | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public function js() | ||||
|     { | ||||
|         if( ! $this->js) | ||||
|             return null; | ||||
|  | ||||
|         // Sort array by priorities (larger priority first) | ||||
|         usort($this->js, function ($a, $b) {return $a['priority'] - $b['priority'];}); | ||||
|         $this->js = array_reverse($this->js); | ||||
|  | ||||
|         $output = ''; | ||||
|         if($this->js_pipeline) { | ||||
|             $output .= '<script type="text/javascript" src="'.$this->pipeline(JS_ASSET).'"></script>'."\n"; | ||||
|             foreach ($this->js_no_pipeline as $file) { | ||||
|                 $output .= '<script type="text/javascript" src="'.$file['asset'].'"></script>'."\n"; | ||||
|             } | ||||
|             return $output; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         foreach($this->js as $file) | ||||
|             $output .= '<script type="text/javascript" src="'.$file['asset'].'"></script>'."\n"; | ||||
|  | ||||
|         return $output; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Add/replace collection. | ||||
|      * | ||||
|      * @param  string  $collectionName | ||||
|      * @param  array   $assets | ||||
|      * @return Manager | ||||
|      */ | ||||
|     public function registerCollection($collectionName, Array $assets) | ||||
|     { | ||||
|         $this->collections[$collectionName] = $assets; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Reset all assets. | ||||
|      * | ||||
|      * @return Manager | ||||
|      */ | ||||
|     public function reset() | ||||
|     { | ||||
|         return $this->resetCss()->resetJs(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Reset CSS assets. | ||||
|      * | ||||
|      * @return Manager | ||||
|      */ | ||||
|     public function resetCss() | ||||
|     { | ||||
|         $this->css = array(); | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Reset JavaScript assets. | ||||
|      * | ||||
|      * @return Manager | ||||
|      */ | ||||
|     public function resetJs() | ||||
|     { | ||||
|         $this->js = array(); | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Minifiy and concatenate CSS / JS files. | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     protected function pipeline($css = true) | ||||
|     { | ||||
|         $cache = Registry::get('Cache'); | ||||
|         $key = '?'.$cache->getKey(); | ||||
|  | ||||
|         if ($css) { | ||||
|             $file = md5(json_encode($this->css) . $this->js_minify . $this->css_minify . $this->css_rewrite) . '.css'; | ||||
|             foreach ($this->css as $id => $asset) { | ||||
|                 if (!$asset['pipeline']) { | ||||
|                     $this->css_no_pipeline[] = $asset; | ||||
|                     unset($this->css[$id]); | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|             $file = md5(json_encode($this->js) . $this->js_minify . $this->css_minify . $this->css_rewrite) . '.js'; | ||||
|             foreach ($this->js as $id => $asset) { | ||||
|                 if (!$asset['pipeline']) { | ||||
|                     $this->js_no_pipeline[] = $asset; | ||||
|                     unset($this->js[$id]); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $relative_path = "{$this->base_url}/".basename(ASSETS_DIR)."/{$file}"; | ||||
|         $absolute_path = ASSETS_DIR.$file; | ||||
|  | ||||
|  | ||||
|         // If pipeline exist return it | ||||
|         if(file_exists($absolute_path)) | ||||
|             return $relative_path . $key; | ||||
|  | ||||
|         // Concatenate files | ||||
|         if ($css) { | ||||
|             $buffer = $this->gatherLinks($this->css, CSS_ASSET); | ||||
|             if ($this->css_minify) { | ||||
|                 $min = new \CSSmin(); | ||||
|                 $buffer = $min->run($buffer); | ||||
|             } | ||||
|         } else { | ||||
|             $buffer = $this->gatherLinks($this->js, JS_ASSET); | ||||
|             if ($this->js_minify) { | ||||
|                 $buffer = \JSMin::minify($buffer); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Write file | ||||
|         file_put_contents($absolute_path, $buffer); | ||||
|  | ||||
|         return $relative_path . $key; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Download and concatenate the content of several links. | ||||
|      * | ||||
|      * @param  array  $links | ||||
|      * @return string | ||||
|      */ | ||||
|     protected function gatherLinks(array $links, $css = true) | ||||
|     { | ||||
|         $buffer = ''; | ||||
|         $local = true; | ||||
|  | ||||
|         foreach($links as $asset) | ||||
|         { | ||||
|             $link = $asset['asset']; | ||||
|  | ||||
|             if($this->isRemoteLink($link)) { | ||||
|                 $local = false; | ||||
|                 if('//' === substr($link, 0, 2)) | ||||
|                     $link = 'http:' . $link; | ||||
|             } else { | ||||
|                 $relative_path = str_replace($this->base_url.'/', '', $link); | ||||
|                 $relative_dir = dirname ($relative_path); | ||||
|                 $link = ROOT_DIR . $relative_path; | ||||
|             } | ||||
|  | ||||
|             $file = ($this->fetch_command instanceof Closure) ? $this->fetch_command->__invoke($link) : file_get_contents($link); | ||||
|             $buffer .= $file; | ||||
|         } | ||||
|  | ||||
|         // If this is CSS + the file is local + rewrite enabled | ||||
|         if ($css && $local && $this->css_rewrite) { | ||||
|             $buffer = $this->cssRewrite($buffer, $relative_dir); | ||||
|         } | ||||
|  | ||||
|         // Pull out @imports and move to top | ||||
|         if ($css) { | ||||
|             $buffer = $this->moveImports($buffer); | ||||
|         } | ||||
|  | ||||
|         return $buffer; | ||||
|     } | ||||
|  | ||||
|     protected function moveImports($file) | ||||
|     { | ||||
|         $this->imports = array(); | ||||
|  | ||||
|         $file = preg_replace_callback('{@import(.*);}', | ||||
|             function($matches) { | ||||
|                 $this->imports[] = $matches[0]; | ||||
|                 return ''; | ||||
|             }, | ||||
|             $file | ||||
|         ); | ||||
|  | ||||
|         return implode("\n", $this->imports) . "\n\n" . $file; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Finds relative CSS urls() and rewrites the URL with an absolute one | ||||
|      * @param  string $file          the css source file | ||||
|      * @param  string $relative_path relative path to the css file | ||||
|      * @return [type]                [description] | ||||
|      */ | ||||
|     protected function cssRewrite($file, $relative_path) | ||||
|     { | ||||
|  | ||||
|         // Strip any sourcemap comments | ||||
|         $file = preg_replace(self::CSS_SOURCEMAP_REGEX, '', $file); | ||||
|  | ||||
|         // Find any css url() elements, grab the URLs and calculate an absolute path | ||||
|         // Then replace the old url with the new one | ||||
|         $file = preg_replace_callback(self::CSS_URL_REGEX, | ||||
|                 function($matches) use ($relative_path) { | ||||
|  | ||||
|                     $old_url = $matches[1]; | ||||
|                     $newpath = array(); | ||||
|                     $paths = explode('/', $old_url); | ||||
|  | ||||
|                     foreach ($paths as $path) { | ||||
|                         if ($path == '..') { | ||||
|                             $relative_path = dirname($relative_path); | ||||
|                         } else { | ||||
|                             $newpath[] = $path; | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     $new_url = $this->base_url . '/' . $relative_path . '/' . implode('/', $newpath); | ||||
|  | ||||
|                     return str_replace($old_url, $new_url, $matches[0]); | ||||
|                 }, | ||||
|                 $file | ||||
|         ); | ||||
|  | ||||
|         return $file; | ||||
|     } | ||||
|  | ||||
|     protected function buildLocalLink($asset) | ||||
|     { | ||||
|  | ||||
|         $matches = $this->assetIsGravPackage($asset); | ||||
|         $base_url = $this->config->get('system.base_url_relative'); | ||||
|  | ||||
|         if($matches === false) | ||||
|             return $base_url . '/' . $asset; | ||||
|  | ||||
|         if($matches[1] == 'theme') { | ||||
|             return $this->theme_url . '/' . $matches[2] . '/' . $matches[3]; | ||||
|         } elseif ($matches[1] == 'plugin') { | ||||
|             return $base_url . '/user/plugins/' . $matches[2] . '/' . $matches[3]; | ||||
|         } else { | ||||
|             return $base_url . '/' . $asset; | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     protected function assetIsGravPackage($asset) | ||||
|     { | ||||
|         if(preg_match('{^@([a-z]+)/(.*?):(.*)$}', $asset, $matches)) | ||||
|             return $matches; | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Determine whether a link is local or remote. | ||||
|      * | ||||
|      * Undestands both "http://" and "https://" as well as protocol agnostic links "//" | ||||
|      * | ||||
|      * @param  string $link | ||||
|      * @return bool | ||||
|      */ | ||||
|     protected function isRemoteLink($link) | ||||
|     { | ||||
|         return ('http://' === substr($link, 0, 7) or 'https://' === substr($link, 0, 8) or '//' === substr($link, 0, 2)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get all CSS assets already added. | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function getCss() | ||||
|     { | ||||
|         return $this->css; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get all JavaScript assets already added. | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function getJs() | ||||
|     { | ||||
|         return $this->js; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Add all assets matching $pattern within $directory. | ||||
|      * | ||||
|      * @param  string $directory Relative to $this->public_dir | ||||
|      * @param  string $pattern (regex) | ||||
|      * @return Manager | ||||
|      * @throws Exception | ||||
|      */ | ||||
|     public function addDir($directory, $pattern = self::DEFAULT_REGEX) | ||||
|     { | ||||
|         // Check if public_dir exists | ||||
|         if( ! is_dir(ASSETS_DIR)) | ||||
|             throw new Exception('Assets: Public dir not found'); | ||||
|  | ||||
|         // Get files | ||||
|         $files = $this->rglob(ASSETS_DIR . DIRECTORY_SEPARATOR . $directory, $pattern, ASSETS_DIR); | ||||
|  | ||||
|         // No luck? Nothing to do | ||||
|         if( ! $files) | ||||
|             return $this; | ||||
|  | ||||
|         // Add CSS files | ||||
|         if($pattern === self::CSS_REGEX) | ||||
|         { | ||||
|             $this->css = array_unique(array_merge($this->css, $files)); | ||||
|             return $this; | ||||
|         } | ||||
|  | ||||
|         // Add JavaScript files | ||||
|         if($pattern === self::JS_REGEX) | ||||
|         { | ||||
|             $this->js = array_unique(array_merge($this->js, $files)); | ||||
|             return $this; | ||||
|         } | ||||
|  | ||||
|         // Unknown pattern. We must poll to know the extension :( | ||||
|         foreach($files as $asset) | ||||
|         { | ||||
|             $info = pathinfo($asset); | ||||
|             if(isset($info['extension'])) | ||||
|             { | ||||
|                 $ext = strtolower($info['extension']); | ||||
|                 if($ext === 'css' and ! in_array($asset, $this->css)) | ||||
|                     $this->css[] = $asset; | ||||
|                 elseif($ext === 'js' and ! in_array($asset, $this->js)) | ||||
|                     $this->js[] = $asset; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Add all CSS assets within $directory (relative to public dir). | ||||
|      * | ||||
|      * @param  string $directory Relative to $this->public_dir | ||||
|      * @return Manager | ||||
|      */ | ||||
|     public function addDirCss($directory) | ||||
|     { | ||||
|         return $this->addDir($directory, self::CSS_REGEX); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Add all JavaScript assets within $directory. | ||||
|      * | ||||
|      * @param  string $directory Relative to $this->public_dir | ||||
|      * @return Manager | ||||
|      */ | ||||
|     public function addDirJs($directory) | ||||
|     { | ||||
|         return $this->addDir($directory, self::JS_REGEX); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Recursively get files matching $pattern within $directory. | ||||
|      * | ||||
|      * @param  string $directory | ||||
|      * @param  string $pattern (regex) | ||||
|      * @param  string $ltrim Will be trimed from the left of the file path | ||||
|      * @return array | ||||
|      */ | ||||
|     protected function rglob($directory, $pattern, $ltrim = null) | ||||
|     { | ||||
|         $iterator = new RegexIterator(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($directory, FilesystemIterator::SKIP_DOTS)), $pattern); | ||||
|         $offset = strlen($ltrim); | ||||
|         $files = array(); | ||||
|  | ||||
|         foreach($iterator as $file) | ||||
|             $files[] = substr($file->getPathname(), $offset); | ||||
|  | ||||
|         return $files; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @var Config | ||||
|      */ | ||||
|     protected function priorityCompare($a, $b) | ||||
|     { | ||||
|         return $a ['priority'] - $b ['priority']; | ||||
|     } | ||||
|  | ||||
|     public function __toString() { | ||||
|         return ''; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -66,6 +66,11 @@ class Grav extends Getters | ||||
|      */ | ||||
|     protected $twig; | ||||
|  | ||||
|     /** | ||||
|      * @var  Assets | ||||
|      */ | ||||
|     protected $assets; | ||||
|  | ||||
|     /** | ||||
|      * @var Taxonomy | ||||
|      */ | ||||
| @@ -79,6 +84,9 @@ class Grav extends Getters | ||||
|         // Get the Configuration settings and caching | ||||
|         $this->config = Registry::get('Config'); | ||||
|  | ||||
|         // Get User Agent | ||||
|         $this->user_agent = new \phpUserAgent(); | ||||
|  | ||||
|         Debugger::$logDirectory = $this->config->get('system.debugger.log.enabled') ? LOG_DIR : null; | ||||
|         Debugger::$maxDepth = $this->config->get('system.debugger.max_depth'); | ||||
|  | ||||
| @@ -103,6 +111,11 @@ class Grav extends Getters | ||||
|         $themes = new Themes(); | ||||
|         $this->plugins['Theme'] = $themes->load(); | ||||
|  | ||||
|         // Get assets and fire event to hook onto | ||||
|         $this->assets = Registry::get('Assets'); | ||||
|         $this->assets->init(); | ||||
|         $this->fireEvent('onAfterGetAssets'); | ||||
|  | ||||
|         // Get twig object | ||||
|         $this->twig = Registry::get('Twig'); | ||||
|         $this->twig->init(); | ||||
|   | ||||
| @@ -28,6 +28,11 @@ class Twig | ||||
|      */ | ||||
|     protected $config; | ||||
|  | ||||
|     /** | ||||
|      * @var Useragent | ||||
|      */ | ||||
|     protected $user_agent; | ||||
|  | ||||
|     /** | ||||
|      * @var Uri | ||||
|      */ | ||||
| @@ -69,9 +74,10 @@ class Twig | ||||
|             // get Grav and Config | ||||
|             $this->grav = Registry::get('Grav'); | ||||
|             $this->config = $this->grav->config; | ||||
|             $this->user_agent = $this->grav->user_agent; | ||||
|             $this->uri = Registry::get('Uri'); | ||||
|             $this->taxonomy = Registry::get('Taxonomy'); | ||||
|  | ||||
|             $this->assets = Registry::get('Assets'); | ||||
|  | ||||
|             $this->twig_paths = array(THEMES_DIR . $this->config->get('system.pages.theme') . '/templates'); | ||||
|             $this->grav->fireEvent('onAfterTwigTemplatesPaths'); | ||||
| @@ -114,9 +120,9 @@ class Twig | ||||
|                 'theme_dir' => THEMES_DIR . $theme, | ||||
|                 'theme_url' => $themeUrl, | ||||
|                 'site' => $this->config->get('site'), | ||||
|                 'stylesheets' => array(), | ||||
|                 'scripts' => array(), | ||||
|                 'assets' => $this->assets, | ||||
|                 'taxonomy' => $this->taxonomy, | ||||
|                 'user_agent' => $this->user_agent, | ||||
|             ); | ||||
|  | ||||
|         } | ||||
|   | ||||
| @@ -57,6 +57,26 @@ class CleanCommand extends Command { | ||||
|         'vendor/ircmaxell/password-compat/version-test.php', | ||||
|         'vendor/ircmaxell/password-compat/.travis.yml', | ||||
|         'vendor/ircmaxell/password-compat/test', | ||||
|         'vendor/mrclay/minify/.editorconfig', | ||||
|         'vendor/mrclay/minify/.git', | ||||
|         'vendor/mrclay/minify/.gitignore', | ||||
|         'vendor/mrclay/minify/composer.json', | ||||
|         'vendor/mrclay/minify/min_extras', | ||||
|         'vendor/mrclay/minify/min_unit_tests', | ||||
|         'vendor/mrclay/minify/min/.htaccess', | ||||
|         'vendor/mrclay/minify/min/builder', | ||||
|         'vendor/mrclay/minify/min/config-test.php', | ||||
|         'vendor/mrclay/minify/min/config.php', | ||||
|         'vendor/mrclay/minify/min/groupsConfig.php', | ||||
|         'vendor/mrclay/minify/min/index.php', | ||||
|         'vendor/mrclay/minify/min/quick-test.css', | ||||
|         'vendor/mrclay/minify/min/quick-test.js', | ||||
|         'vendor/mrclay/minify/min/utils.php', | ||||
|         'vendor/ornicar/php-user-agent/.git', | ||||
|         'vendor/ornicar/php-user-agent/.gitignore', | ||||
|         'vendor/ornicar/php-user-agent/composer.json', | ||||
|         'vendor/ornicar/php-user-agent/prove.php', | ||||
|         'vendor/ornicar/php-user-agent/test', | ||||
|         'vendor/symfony/console/Symfony/Component/Console/composer.json', | ||||
|         'vendor/symfony/console/Symfony/Component/Console/phpunit.xml.dist', | ||||
|         'vendor/symfony/console/Symfony/Component/Console/.gitignore', | ||||
|   | ||||
| @@ -13,7 +13,8 @@ class ClearCacheCommand extends Command { | ||||
|  | ||||
|     protected $paths_to_remove = [ | ||||
|         'cache', | ||||
|         'images' | ||||
|         'images', | ||||
|         'assets' | ||||
|     ]; | ||||
|  | ||||
|     protected function configure() { | ||||
|   | ||||
| @@ -23,6 +23,13 @@ twig: | ||||
|   auto_reload: true | ||||
|   autoescape: false | ||||
|  | ||||
| assets: | ||||
|   css_pipeline: false | ||||
|   css_minify: true | ||||
|   css_rewrite: true | ||||
|   js_pipeline: false | ||||
|   js_minify: true | ||||
|  | ||||
| debugger: | ||||
|   enabled: true | ||||
|   max_depth: 10 | ||||
|   | ||||
							
								
								
									
										2
									
								
								vendor/autoload.php
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/autoload.php
									
									
									
									
										vendored
									
									
								
							| @@ -4,4 +4,4 @@ | ||||
|  | ||||
| require_once __DIR__ . '/composer' . '/autoload_real.php'; | ||||
|  | ||||
| return ComposerAutoloaderInit59d31b9205a8126f2a856995d42bb492::getLoader(); | ||||
| return ComposerAutoloaderInit70d1fc134524e0edbf7795cd92686187::getLoader(); | ||||
|   | ||||
							
								
								
									
										50
									
								
								vendor/composer/autoload_classmap.php
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										50
									
								
								vendor/composer/autoload_classmap.php
									
									
									
									
										vendored
									
									
								
							| @@ -6,6 +6,56 @@ $vendorDir = dirname(dirname(__FILE__)); | ||||
| $baseDir = dirname($vendorDir); | ||||
|  | ||||
| return array( | ||||
|     'CSSmin' => $vendorDir . '/mrclay/minify/min/lib/CSSmin.php', | ||||
|     'DooDigestAuth' => $vendorDir . '/mrclay/minify/min/lib/DooDigestAuth.php', | ||||
|     'FirePHP' => $vendorDir . '/mrclay/minify/min/lib/FirePHP.php', | ||||
|     'HTTP_ConditionalGet' => $vendorDir . '/mrclay/minify/min/lib/HTTP/ConditionalGet.php', | ||||
|     'HTTP_Encoder' => $vendorDir . '/mrclay/minify/min/lib/HTTP/Encoder.php', | ||||
|     'JSCompilerContext' => $vendorDir . '/mrclay/minify/min/lib/JSMinPlus.php', | ||||
|     'JSMin' => $vendorDir . '/mrclay/minify/min/lib/JSMin.php', | ||||
|     'JSMinPlus' => $vendorDir . '/mrclay/minify/min/lib/JSMinPlus.php', | ||||
|     'JSMin_UnterminatedCommentException' => $vendorDir . '/mrclay/minify/min/lib/JSMin.php', | ||||
|     'JSMin_UnterminatedRegExpException' => $vendorDir . '/mrclay/minify/min/lib/JSMin.php', | ||||
|     'JSMin_UnterminatedStringException' => $vendorDir . '/mrclay/minify/min/lib/JSMin.php', | ||||
|     'JSNode' => $vendorDir . '/mrclay/minify/min/lib/JSMinPlus.php', | ||||
|     'JSParser' => $vendorDir . '/mrclay/minify/min/lib/JSMinPlus.php', | ||||
|     'JSToken' => $vendorDir . '/mrclay/minify/min/lib/JSMinPlus.php', | ||||
|     'JSTokenizer' => $vendorDir . '/mrclay/minify/min/lib/JSMinPlus.php', | ||||
|     'Minify' => $vendorDir . '/mrclay/minify/min/lib/Minify.php', | ||||
|     'Minify_Build' => $vendorDir . '/mrclay/minify/min/lib/Minify/Build.php', | ||||
|     'Minify_CSS' => $vendorDir . '/mrclay/minify/min/lib/Minify/CSS.php', | ||||
|     'Minify_CSS_Compressor' => $vendorDir . '/mrclay/minify/min/lib/Minify/CSS/Compressor.php', | ||||
|     'Minify_CSS_UriRewriter' => $vendorDir . '/mrclay/minify/min/lib/Minify/CSS/UriRewriter.php', | ||||
|     'Minify_CSSmin' => $vendorDir . '/mrclay/minify/min/lib/Minify/CSSmin.php', | ||||
|     'Minify_Cache_APC' => $vendorDir . '/mrclay/minify/min/lib/Minify/Cache/APC.php', | ||||
|     'Minify_Cache_File' => $vendorDir . '/mrclay/minify/min/lib/Minify/Cache/File.php', | ||||
|     'Minify_Cache_Memcache' => $vendorDir . '/mrclay/minify/min/lib/Minify/Cache/Memcache.php', | ||||
|     'Minify_Cache_XCache' => $vendorDir . '/mrclay/minify/min/lib/Minify/Cache/XCache.php', | ||||
|     'Minify_Cache_ZendPlatform' => $vendorDir . '/mrclay/minify/min/lib/Minify/Cache/ZendPlatform.php', | ||||
|     'Minify_ClosureCompiler' => $vendorDir . '/mrclay/minify/min/lib/Minify/ClosureCompiler.php', | ||||
|     'Minify_ClosureCompiler_Exception' => $vendorDir . '/mrclay/minify/min/lib/Minify/ClosureCompiler.php', | ||||
|     'Minify_CommentPreserver' => $vendorDir . '/mrclay/minify/min/lib/Minify/CommentPreserver.php', | ||||
|     'Minify_Controller_Base' => $vendorDir . '/mrclay/minify/min/lib/Minify/Controller/Base.php', | ||||
|     'Minify_Controller_Files' => $vendorDir . '/mrclay/minify/min/lib/Minify/Controller/Files.php', | ||||
|     'Minify_Controller_Groups' => $vendorDir . '/mrclay/minify/min/lib/Minify/Controller/Groups.php', | ||||
|     'Minify_Controller_MinApp' => $vendorDir . '/mrclay/minify/min/lib/Minify/Controller/MinApp.php', | ||||
|     'Minify_Controller_Page' => $vendorDir . '/mrclay/minify/min/lib/Minify/Controller/Page.php', | ||||
|     'Minify_Controller_Version1' => $vendorDir . '/mrclay/minify/min/lib/Minify/Controller/Version1.php', | ||||
|     'Minify_DebugDetector' => $vendorDir . '/mrclay/minify/min/lib/Minify/DebugDetector.php', | ||||
|     'Minify_HTML' => $vendorDir . '/mrclay/minify/min/lib/Minify/HTML.php', | ||||
|     'Minify_HTML_Helper' => $vendorDir . '/mrclay/minify/min/lib/Minify/HTML/Helper.php', | ||||
|     'Minify_ImportProcessor' => $vendorDir . '/mrclay/minify/min/lib/Minify/ImportProcessor.php', | ||||
|     'Minify_JS_ClosureCompiler' => $vendorDir . '/mrclay/minify/min/lib/Minify/JS/ClosureCompiler.php', | ||||
|     'Minify_JS_ClosureCompiler_Exception' => $vendorDir . '/mrclay/minify/min/lib/Minify/JS/ClosureCompiler.php', | ||||
|     'Minify_Lines' => $vendorDir . '/mrclay/minify/min/lib/Minify/Lines.php', | ||||
|     'Minify_Loader' => $vendorDir . '/mrclay/minify/min/lib/Minify/Loader.php', | ||||
|     'Minify_Logger' => $vendorDir . '/mrclay/minify/min/lib/Minify/Logger.php', | ||||
|     'Minify_Packer' => $vendorDir . '/mrclay/minify/min/lib/Minify/Packer.php', | ||||
|     'Minify_Source' => $vendorDir . '/mrclay/minify/min/lib/Minify/Source.php', | ||||
|     'Minify_YUICompressor' => $vendorDir . '/mrclay/minify/min/lib/Minify/YUICompressor.php', | ||||
|     'Minify_YUI_CssCompressor' => $vendorDir . '/mrclay/minify/min/lib/Minify/YUI/CssCompressor.php', | ||||
|     'MrClay\\Cli' => $vendorDir . '/mrclay/minify/min/lib/MrClay/Cli.php', | ||||
|     'MrClay\\Cli\\Arg' => $vendorDir . '/mrclay/minify/min/lib/MrClay/Cli/Arg.php', | ||||
|     'Tracy\\Bar' => $vendorDir . '/tracy/tracy/src/Tracy/Bar.php', | ||||
|     'Tracy\\BlueScreen' => $vendorDir . '/tracy/tracy/src/Tracy/BlueScreen.php', | ||||
|     'Tracy\\Debugger' => $vendorDir . '/tracy/tracy/src/Tracy/Debugger.php', | ||||
|   | ||||
							
								
								
									
										4
									
								
								vendor/composer/autoload_files.php
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								vendor/composer/autoload_files.php
									
									
									
									
										vendored
									
									
								
							| @@ -6,6 +6,8 @@ $vendorDir = dirname(dirname(__FILE__)); | ||||
| $baseDir = dirname($vendorDir); | ||||
|  | ||||
| return array( | ||||
|     $vendorDir . '/tracy/tracy/src/shortcuts.php', | ||||
|     $vendorDir . '/ircmaxell/password-compat/lib/password.php', | ||||
|     $vendorDir . '/tracy/tracy/src/shortcuts.php', | ||||
|     $vendorDir . '/ornicar/php-user-agent/lib/phpUserAgent.php', | ||||
|     $vendorDir . '/ornicar/php-user-agent/lib/phpUserAgentStringParser.php', | ||||
| ); | ||||
|   | ||||
							
								
								
									
										10
									
								
								vendor/composer/autoload_real.php
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								vendor/composer/autoload_real.php
									
									
									
									
										vendored
									
									
								
							| @@ -2,7 +2,7 @@ | ||||
|  | ||||
| // autoload_real.php @generated by Composer | ||||
|  | ||||
| class ComposerAutoloaderInit59d31b9205a8126f2a856995d42bb492 | ||||
| class ComposerAutoloaderInit70d1fc134524e0edbf7795cd92686187 | ||||
| { | ||||
|     private static $loader; | ||||
|  | ||||
| @@ -19,9 +19,9 @@ class ComposerAutoloaderInit59d31b9205a8126f2a856995d42bb492 | ||||
|             return self::$loader; | ||||
|         } | ||||
|  | ||||
|         spl_autoload_register(array('ComposerAutoloaderInit59d31b9205a8126f2a856995d42bb492', 'loadClassLoader'), true, true); | ||||
|         spl_autoload_register(array('ComposerAutoloaderInit70d1fc134524e0edbf7795cd92686187', 'loadClassLoader'), true, true); | ||||
|         self::$loader = $loader = new \Composer\Autoload\ClassLoader(); | ||||
|         spl_autoload_unregister(array('ComposerAutoloaderInit59d31b9205a8126f2a856995d42bb492', 'loadClassLoader')); | ||||
|         spl_autoload_unregister(array('ComposerAutoloaderInit70d1fc134524e0edbf7795cd92686187', 'loadClassLoader')); | ||||
|  | ||||
|         $map = require __DIR__ . '/autoload_namespaces.php'; | ||||
|         foreach ($map as $namespace => $path) { | ||||
| @@ -42,14 +42,14 @@ class ComposerAutoloaderInit59d31b9205a8126f2a856995d42bb492 | ||||
|  | ||||
|         $includeFiles = require __DIR__ . '/autoload_files.php'; | ||||
|         foreach ($includeFiles as $file) { | ||||
|             composerRequire59d31b9205a8126f2a856995d42bb492($file); | ||||
|             composerRequire70d1fc134524e0edbf7795cd92686187($file); | ||||
|         } | ||||
|  | ||||
|         return $loader; | ||||
|     } | ||||
| } | ||||
|  | ||||
| function composerRequire59d31b9205a8126f2a856995d42bb492($file) | ||||
| function composerRequire70d1fc134524e0edbf7795cd92686187($file) | ||||
| { | ||||
|     require $file; | ||||
| } | ||||
|   | ||||
							
								
								
									
										407
									
								
								vendor/composer/installed.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										407
									
								
								vendor/composer/installed.json
									
									
									
									
										vendored
									
									
								
							| @@ -1,45 +1,4 @@ | ||||
| [ | ||||
|     { | ||||
|         "name": "erusev/parsedown", | ||||
|         "version": "dev-master", | ||||
|         "version_normalized": "9999999-dev", | ||||
|         "source": { | ||||
|             "type": "git", | ||||
|             "url": "https://github.com/erusev/parsedown.git", | ||||
|             "reference": "e33ac1c56ea591f21b9cf2fa74356ef708d4e130" | ||||
|         }, | ||||
|         "dist": { | ||||
|             "type": "zip", | ||||
|             "url": "https://api.github.com/repos/erusev/parsedown/zipball/e33ac1c56ea591f21b9cf2fa74356ef708d4e130", | ||||
|             "reference": "e33ac1c56ea591f21b9cf2fa74356ef708d4e130", | ||||
|             "shasum": "" | ||||
|         }, | ||||
|         "time": "2014-06-18 09:27:25", | ||||
|         "type": "library", | ||||
|         "installation-source": "source", | ||||
|         "autoload": { | ||||
|             "psr-0": { | ||||
|                 "Parsedown": "" | ||||
|             } | ||||
|         }, | ||||
|         "notification-url": "https://packagist.org/downloads/", | ||||
|         "license": [ | ||||
|             "MIT" | ||||
|         ], | ||||
|         "authors": [ | ||||
|             { | ||||
|                 "name": "Emanuil Rusev", | ||||
|                 "email": "hello@erusev.com", | ||||
|                 "homepage": "http://erusev.com" | ||||
|             } | ||||
|         ], | ||||
|         "description": "Parser for Markdown.", | ||||
|         "homepage": "http://parsedown.org", | ||||
|         "keywords": [ | ||||
|             "markdown", | ||||
|             "parser" | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "name": "doctrine/cache", | ||||
|         "version": "dev-master", | ||||
| @@ -111,67 +70,6 @@ | ||||
|             "caching" | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "name": "tracy/tracy", | ||||
|         "version": "dev-master", | ||||
|         "version_normalized": "9999999-dev", | ||||
|         "source": { | ||||
|             "type": "git", | ||||
|             "url": "https://github.com/nette/tracy.git", | ||||
|             "reference": "1250ac4907947b28ec66d6e00a337dadaddaad4c" | ||||
|         }, | ||||
|         "dist": { | ||||
|             "type": "zip", | ||||
|             "url": "https://api.github.com/repos/nette/tracy/zipball/1250ac4907947b28ec66d6e00a337dadaddaad4c", | ||||
|             "reference": "1250ac4907947b28ec66d6e00a337dadaddaad4c", | ||||
|             "shasum": "" | ||||
|         }, | ||||
|         "require": { | ||||
|             "php": ">=5.3.1" | ||||
|         }, | ||||
|         "require-dev": { | ||||
|             "nette/tester": "~1.0" | ||||
|         }, | ||||
|         "time": "2014-08-07 17:19:48", | ||||
|         "type": "library", | ||||
|         "extra": { | ||||
|             "branch-alias": { | ||||
|                 "dev-master": "2.3-dev" | ||||
|             } | ||||
|         }, | ||||
|         "installation-source": "source", | ||||
|         "autoload": { | ||||
|             "classmap": [ | ||||
|                 "src/Tracy" | ||||
|             ], | ||||
|             "files": [ | ||||
|                 "src/shortcuts.php" | ||||
|             ] | ||||
|         }, | ||||
|         "notification-url": "https://packagist.org/downloads/", | ||||
|         "license": [ | ||||
|             "BSD-3-Clause", | ||||
|             "GPL-2.0", | ||||
|             "GPL-3.0" | ||||
|         ], | ||||
|         "authors": [ | ||||
|             { | ||||
|                 "name": "David Grudl", | ||||
|                 "homepage": "http://davidgrudl.com" | ||||
|             }, | ||||
|             { | ||||
|                 "name": "Nette Community", | ||||
|                 "homepage": "http://nette.org/contributors" | ||||
|             } | ||||
|         ], | ||||
|         "description": "Tracy: useful PHP debugger", | ||||
|         "homepage": "http://tracy.nette.org", | ||||
|         "keywords": [ | ||||
|             "debug", | ||||
|             "debugger", | ||||
|             "nette" | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "name": "gregwar/cache", | ||||
|         "version": "v1.0.9", | ||||
| @@ -373,63 +271,6 @@ | ||||
|         "description": "Symfony Yaml Component", | ||||
|         "homepage": "http://symfony.com" | ||||
|     }, | ||||
|     { | ||||
|         "name": "symfony/console", | ||||
|         "version": "2.5.x-dev", | ||||
|         "version_normalized": "2.5.9999999.9999999-dev", | ||||
|         "target-dir": "Symfony/Component/Console", | ||||
|         "source": { | ||||
|             "type": "git", | ||||
|             "url": "https://github.com/symfony/Console.git", | ||||
|             "reference": "cd2d1e4bac2206b337326b0140ff475fe9ad5f63" | ||||
|         }, | ||||
|         "dist": { | ||||
|             "type": "zip", | ||||
|             "url": "https://api.github.com/repos/symfony/Console/zipball/cd2d1e4bac2206b337326b0140ff475fe9ad5f63", | ||||
|             "reference": "cd2d1e4bac2206b337326b0140ff475fe9ad5f63", | ||||
|             "shasum": "" | ||||
|         }, | ||||
|         "require": { | ||||
|             "php": ">=5.3.3" | ||||
|         }, | ||||
|         "require-dev": { | ||||
|             "psr/log": "~1.0", | ||||
|             "symfony/event-dispatcher": "~2.1" | ||||
|         }, | ||||
|         "suggest": { | ||||
|             "psr/log": "For using the console logger", | ||||
|             "symfony/event-dispatcher": "" | ||||
|         }, | ||||
|         "time": "2014-08-05 09:00:40", | ||||
|         "type": "library", | ||||
|         "extra": { | ||||
|             "branch-alias": { | ||||
|                 "dev-master": "2.5-dev" | ||||
|             } | ||||
|         }, | ||||
|         "installation-source": "source", | ||||
|         "autoload": { | ||||
|             "psr-0": { | ||||
|                 "Symfony\\Component\\Console\\": "" | ||||
|             } | ||||
|         }, | ||||
|         "notification-url": "https://packagist.org/downloads/", | ||||
|         "license": [ | ||||
|             "MIT" | ||||
|         ], | ||||
|         "authors": [ | ||||
|             { | ||||
|                 "name": "Symfony Community", | ||||
|                 "homepage": "http://symfony.com/contributors" | ||||
|             }, | ||||
|             { | ||||
|                 "name": "Fabien Potencier", | ||||
|                 "email": "fabien@symfony.com" | ||||
|             } | ||||
|         ], | ||||
|         "description": "Symfony Console Component", | ||||
|         "homepage": "http://symfony.com" | ||||
|     }, | ||||
|     { | ||||
|         "name": "ircmaxell/password-compat", | ||||
|         "version": "1.0.3", | ||||
| @@ -470,5 +311,253 @@ | ||||
|             "hashing", | ||||
|             "password" | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "name": "erusev/parsedown", | ||||
|         "version": "dev-master", | ||||
|         "version_normalized": "9999999-dev", | ||||
|         "source": { | ||||
|             "type": "git", | ||||
|             "url": "https://github.com/erusev/parsedown.git", | ||||
|             "reference": "9437766539f6f88e748e7b6183ea31f78e154df5" | ||||
|         }, | ||||
|         "dist": { | ||||
|             "type": "zip", | ||||
|             "url": "https://api.github.com/repos/erusev/parsedown/zipball/9437766539f6f88e748e7b6183ea31f78e154df5", | ||||
|             "reference": "9437766539f6f88e748e7b6183ea31f78e154df5", | ||||
|             "shasum": "" | ||||
|         }, | ||||
|         "time": "2014-08-13 22:27:48", | ||||
|         "type": "library", | ||||
|         "installation-source": "source", | ||||
|         "autoload": { | ||||
|             "psr-0": { | ||||
|                 "Parsedown": "" | ||||
|             } | ||||
|         }, | ||||
|         "notification-url": "https://packagist.org/downloads/", | ||||
|         "license": [ | ||||
|             "MIT" | ||||
|         ], | ||||
|         "authors": [ | ||||
|             { | ||||
|                 "name": "Emanuil Rusev", | ||||
|                 "email": "hello@erusev.com", | ||||
|                 "homepage": "http://erusev.com" | ||||
|             } | ||||
|         ], | ||||
|         "description": "Parser for Markdown.", | ||||
|         "homepage": "http://parsedown.org", | ||||
|         "keywords": [ | ||||
|             "markdown", | ||||
|             "parser" | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "name": "tracy/tracy", | ||||
|         "version": "dev-master", | ||||
|         "version_normalized": "9999999-dev", | ||||
|         "source": { | ||||
|             "type": "git", | ||||
|             "url": "https://github.com/nette/tracy.git", | ||||
|             "reference": "e432452e0a68b2e5adfba2061eba412f51dbab4c" | ||||
|         }, | ||||
|         "dist": { | ||||
|             "type": "zip", | ||||
|             "url": "https://api.github.com/repos/nette/tracy/zipball/e432452e0a68b2e5adfba2061eba412f51dbab4c", | ||||
|             "reference": "e432452e0a68b2e5adfba2061eba412f51dbab4c", | ||||
|             "shasum": "" | ||||
|         }, | ||||
|         "require": { | ||||
|             "php": ">=5.3.1" | ||||
|         }, | ||||
|         "require-dev": { | ||||
|             "nette/tester": "~1.0" | ||||
|         }, | ||||
|         "time": "2014-08-14 17:58:22", | ||||
|         "type": "library", | ||||
|         "extra": { | ||||
|             "branch-alias": { | ||||
|                 "dev-master": "2.3-dev" | ||||
|             } | ||||
|         }, | ||||
|         "installation-source": "source", | ||||
|         "autoload": { | ||||
|             "classmap": [ | ||||
|                 "src/Tracy" | ||||
|             ], | ||||
|             "files": [ | ||||
|                 "src/shortcuts.php" | ||||
|             ] | ||||
|         }, | ||||
|         "notification-url": "https://packagist.org/downloads/", | ||||
|         "license": [ | ||||
|             "BSD-3-Clause", | ||||
|             "GPL-2.0", | ||||
|             "GPL-3.0" | ||||
|         ], | ||||
|         "authors": [ | ||||
|             { | ||||
|                 "name": "David Grudl", | ||||
|                 "homepage": "http://davidgrudl.com" | ||||
|             }, | ||||
|             { | ||||
|                 "name": "Nette Community", | ||||
|                 "homepage": "http://nette.org/contributors" | ||||
|             } | ||||
|         ], | ||||
|         "description": "Tracy: useful PHP debugger", | ||||
|         "homepage": "http://tracy.nette.org", | ||||
|         "keywords": [ | ||||
|             "debug", | ||||
|             "debugger", | ||||
|             "nette" | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "name": "symfony/console", | ||||
|         "version": "2.5.x-dev", | ||||
|         "version_normalized": "2.5.9999999.9999999-dev", | ||||
|         "target-dir": "Symfony/Component/Console", | ||||
|         "source": { | ||||
|             "type": "git", | ||||
|             "url": "https://github.com/symfony/Console.git", | ||||
|             "reference": "748beed2a1e73179c3f5154d33fe6ae100c1aeb1" | ||||
|         }, | ||||
|         "dist": { | ||||
|             "type": "zip", | ||||
|             "url": "https://api.github.com/repos/symfony/Console/zipball/748beed2a1e73179c3f5154d33fe6ae100c1aeb1", | ||||
|             "reference": "748beed2a1e73179c3f5154d33fe6ae100c1aeb1", | ||||
|             "shasum": "" | ||||
|         }, | ||||
|         "require": { | ||||
|             "php": ">=5.3.3" | ||||
|         }, | ||||
|         "require-dev": { | ||||
|             "psr/log": "~1.0", | ||||
|             "symfony/event-dispatcher": "~2.1" | ||||
|         }, | ||||
|         "suggest": { | ||||
|             "psr/log": "For using the console logger", | ||||
|             "symfony/event-dispatcher": "" | ||||
|         }, | ||||
|         "time": "2014-08-14 16:10:54", | ||||
|         "type": "library", | ||||
|         "extra": { | ||||
|             "branch-alias": { | ||||
|                 "dev-master": "2.5-dev" | ||||
|             } | ||||
|         }, | ||||
|         "installation-source": "source", | ||||
|         "autoload": { | ||||
|             "psr-0": { | ||||
|                 "Symfony\\Component\\Console\\": "" | ||||
|             } | ||||
|         }, | ||||
|         "notification-url": "https://packagist.org/downloads/", | ||||
|         "license": [ | ||||
|             "MIT" | ||||
|         ], | ||||
|         "authors": [ | ||||
|             { | ||||
|                 "name": "Symfony Community", | ||||
|                 "homepage": "http://symfony.com/contributors" | ||||
|             }, | ||||
|             { | ||||
|                 "name": "Fabien Potencier", | ||||
|                 "email": "fabien@symfony.com" | ||||
|             } | ||||
|         ], | ||||
|         "description": "Symfony Console Component", | ||||
|         "homepage": "http://symfony.com" | ||||
|     }, | ||||
|     { | ||||
|         "name": "mrclay/minify", | ||||
|         "version": "dev-master", | ||||
|         "version_normalized": "9999999-dev", | ||||
|         "source": { | ||||
|             "type": "git", | ||||
|             "url": "https://github.com/mrclay/minify.git", | ||||
|             "reference": "fb3931f8cd54a637b2a42170c3a9a1c4da9e69a9" | ||||
|         }, | ||||
|         "dist": { | ||||
|             "type": "zip", | ||||
|             "url": "https://api.github.com/repos/mrclay/minify/zipball/fb3931f8cd54a637b2a42170c3a9a1c4da9e69a9", | ||||
|             "reference": "fb3931f8cd54a637b2a42170c3a9a1c4da9e69a9", | ||||
|             "shasum": "" | ||||
|         }, | ||||
|         "require": { | ||||
|             "ext-pcre": "*", | ||||
|             "php": ">=5.2.1" | ||||
|         }, | ||||
|         "time": "2014-04-03 23:53:48", | ||||
|         "type": "library", | ||||
|         "installation-source": "source", | ||||
|         "autoload": { | ||||
|             "classmap": [ | ||||
|                 "min/lib/" | ||||
|             ] | ||||
|         }, | ||||
|         "notification-url": "https://packagist.org/downloads/", | ||||
|         "license": [ | ||||
|             "BSD-3-Clause" | ||||
|         ], | ||||
|         "authors": [ | ||||
|             { | ||||
|                 "name": "Steve Clay", | ||||
|                 "email": "steve@mrclay.org", | ||||
|                 "homepage": "http://www.mrclay.org/", | ||||
|                 "role": "Developer" | ||||
|             } | ||||
|         ], | ||||
|         "description": "Minify is a PHP5 app that helps you follow several rules for client-side performance. It combines multiple CSS or Javascript files, removes unnecessary whitespace and comments, and serves them with gzip encoding and optimal client-side cache headers", | ||||
|         "homepage": "http://code.google.com/p/minify/" | ||||
|     }, | ||||
|     { | ||||
|         "name": "ornicar/php-user-agent", | ||||
|         "version": "dev-master", | ||||
|         "version_normalized": "9999999-dev", | ||||
|         "source": { | ||||
|             "type": "git", | ||||
|             "url": "https://github.com/ornicar/php-user-agent.git", | ||||
|             "reference": "91f648fc5080ac1e8a8b51bcd90867b6ddeaf633" | ||||
|         }, | ||||
|         "dist": { | ||||
|             "type": "zip", | ||||
|             "url": "https://api.github.com/repos/ornicar/php-user-agent/zipball/91f648fc5080ac1e8a8b51bcd90867b6ddeaf633", | ||||
|             "reference": "91f648fc5080ac1e8a8b51bcd90867b6ddeaf633", | ||||
|             "shasum": "" | ||||
|         }, | ||||
|         "require": { | ||||
|             "php": ">=5.3.0" | ||||
|         }, | ||||
|         "time": "2013-07-09 13:04:43", | ||||
|         "type": "library", | ||||
|         "extra": { | ||||
|             "branch-alias": { | ||||
|                 "dev-master": "1.0-dev" | ||||
|             } | ||||
|         }, | ||||
|         "installation-source": "source", | ||||
|         "autoload": { | ||||
|             "files": [ | ||||
|                 "lib/phpUserAgent.php", | ||||
|                 "lib/phpUserAgentStringParser.php" | ||||
|             ] | ||||
|         }, | ||||
|         "notification-url": "https://packagist.org/downloads/", | ||||
|         "license": [ | ||||
|             "MIT" | ||||
|         ], | ||||
|         "authors": [ | ||||
|             { | ||||
|                 "name": "Thibault Duplessis", | ||||
|                 "email": "thibault.duplessis@gmail.com", | ||||
|                 "homepage": "http://ornicar.github.com" | ||||
|             } | ||||
|         ], | ||||
|         "keywords": [ | ||||
|             "user-agent" | ||||
|         ] | ||||
|     } | ||||
| ] | ||||
|   | ||||
							
								
								
									
										2
									
								
								vendor/erusev/parsedown/Parsedown.php
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/erusev/parsedown/Parsedown.php
									
									
									
									
										vendored
									
									
								
							| @@ -619,7 +619,7 @@ class Parsedown | ||||
|  | ||||
|     protected function identifyMarkup($Line) | ||||
|     { | ||||
|         if (preg_match('/^<(\w[\w\d]*)(?:[ ][^>\/]*)?(\/?)[ ]*>/', $Line['text'], $matches)) | ||||
|         if (preg_match('/^<(\w[\w\d]*)(?:[ ][^>]*)?(\/?)[ ]*>/', $Line['text'], $matches)) | ||||
|         { | ||||
|             if (in_array($matches[1], $this->textLevelElements)) | ||||
|             { | ||||
|   | ||||
							
								
								
									
										141
									
								
								vendor/mrclay/minify/HISTORY.txt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								vendor/mrclay/minify/HISTORY.txt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,141 @@ | ||||
| Minify Release History | ||||
|  | ||||
| (master) | ||||
|     * Builder styled with Bootstrap (thanks to help from acidvertigo) | ||||
|  | ||||
| Version 2.2.0 | ||||
|     * Fix handling of RegEx in certain situations in JSMin | ||||
|       * Thanks to Vovan-VE for reporting this | ||||
|     * Update composer.json with support info | ||||
|     * Add ability to set ClosureCompiler URL | ||||
|       * Thanks Elan Ruusamäe for the pull request | ||||
|     * Better report of temp directory errors | ||||
|       * Also thanks to Elan Ruusamäe for anatoher pull request | ||||
|     * Updated CSSmin and added Minify_CSSmin wrapper | ||||
|     * Fix windows issue associated with long cache filenames | ||||
|     * Fix issue with web-based tool | ||||
|     * Fix bug in JSMin exceptions | ||||
|     * Fix "about:blank" bug in CSS_UriRewriter | ||||
|     * Cite is no longer a block element in HTML minification | ||||
|     * Allow for definition of custom config locations outside of the min directory | ||||
|       * Thanks Sam Bauers for the pull request | ||||
|     * Allow option for overriding the maximum byte size POST limit for ClosureCompiler and other additions | ||||
|       * Thanks Joscha Feth for the code | ||||
|     * Fixes to file-relative URL identification in UriRewriter | ||||
|     * Allow far-future expiration and file versioning with the "v" querystirng parameter in addition to existing method | ||||
|     * Lots of general code tidy ups | ||||
|  | ||||
| Version 2.1.7 | ||||
|     * Fixes arbitrary file inclusion vulnerability on some systems | ||||
|       * Thanks to Matt Mecham for reporting this | ||||
|  | ||||
| Version 2.1.6 | ||||
|     * JSMin fixes | ||||
|     * Prevents some Closure Compiler API failures | ||||
|     * Uses autoloading for all class loading | ||||
|     * Multiple group support in HTML Helper | ||||
|     * Cache adaptor for XCache | ||||
|     * Allow setting stack-size in YUI Compressor wrapper | ||||
|     * Adds jsCleanComments option to HTML minifier | ||||
|     * Upgrades CSSmin | ||||
|     * CLI script more portable | ||||
|     * Adds composer.json | ||||
|  | ||||
| Version 2.1.5 | ||||
|     * Removed XSS vulnerability | ||||
|     * Disabled builder bby default | ||||
|     * command line tools to minify and rewrite URIs in CSS | ||||
|     * upgrade (optional) JSMin+ library | ||||
|     * more efficient JS minification when using CC/YUIC | ||||
|     * Closure Compiler uses cURL when allow_url_fopen is off | ||||
|     * Missing file notices when using groups | ||||
|  | ||||
| Version 2.1.4 | ||||
|     * Option to minify JS with Closure Compiler API w/ JSMin failover | ||||
|     * Cookie/bookmarklet-based debug mode. No HTML editing! | ||||
|     * Allows 1 file to be missing w/o complete failure | ||||
|     * Combine multiple groups and files in single URI | ||||
|     * More useful HTML helpers for writing versioned URIs | ||||
|     * More detailed error logging, including minifier exceptions | ||||
|     * Builder offers more helpful messages/PHP environment warnings | ||||
|     * Bypass minification based on filename pattern. e.g. foo.min.js / foo-min.css | ||||
|     * JSMin won't choke on common Closure compiler syntaxes (i+ ++j) | ||||
|     * Better caching in IE6 | ||||
|     * Cache ids are influenced by group/file names | ||||
|     * Debug mode for Javascript doesn't break on common XPath strings (Prototype 1.6) | ||||
|     * Removed annoying maxFiles limit | ||||
|     * mbstring.func_overload usage is safer | ||||
|  | ||||
| Version 2.1.3 | ||||
|     * HTTP fixes | ||||
|       * ETag generation now valid (different when gzipped) | ||||
|       * Vary header always sent when Accept-Encoding is sniffed | ||||
|       * Cache-Control no longer has "must-revalidate" due to webkit bug | ||||
|         See: http://mrclay.org/index.php/2009/02/24/safari-4-beta-cache-controlmust-revalidate-bug/ | ||||
|       * Dropped deflate encoding. Browser and proxy support could be buggy. | ||||
|         See: http://stackoverflow.com/questions/883841/ | ||||
|     * File cache now works w/o setting $min_cachePath | ||||
|     * Allow setting contentType in Minify_Source objects | ||||
|     * No more 5.3 deprecation warnings: split() removed | ||||
|  | ||||
| Version 2.1.2 | ||||
|     * Javascript fixes | ||||
|       * Debug mode no longer confused by "*/*" in strings/RegExps (jQuery) | ||||
|       * quote characters inside RegExp literals no longer cause exception | ||||
|       * files ending in single-line comments no longer cause code loss | ||||
|     * CSS: data: URLs no longer mangled | ||||
|     * Optional error logging to Firefox's FirePHP extension | ||||
|     * Unit tests to check for common DOCUMENT_ROOT problems | ||||
|       * DOCUMENT_ROOT no longer overwritten on IIS servers | ||||
|     * Builder app doesn't fail on systems without gzdeflate() | ||||
|     * APC caching class included | ||||
|  | ||||
| Version 2.1.1 | ||||
|     * Bug fix release | ||||
|     * Detection and workarounds for zlib.output_compression and non-PHP encoding modules | ||||
|     * Zlib not required (mod_rewrite, et.al., can still be used for encoding) | ||||
|     * HTML : More IE conditional comments preserved | ||||
|     * Minify_groupUri() utility fixed | ||||
|  | ||||
| Version 2.1.0 | ||||
|     * "min" default application for quick deployment | ||||
|     * Minify URI Builder app & bookmarklet for quickly creating minify URIs | ||||
|     * Relative URIs in CSS file are fixed automatically by default | ||||
|     * "debug" mode for revealing original line #s in combined files | ||||
|     * Better IIS support | ||||
|     * Improved minifier classes: | ||||
|       * JS: preserves IE conditional comments | ||||
|       * CSS: smaller output, preserves more hacks and valid CSS syntax,  | ||||
|              shorter line lengths, other bug fixes | ||||
|       * HTML: smaller output, shorter line lengths, other bug fixes | ||||
|     * Default Cache-Control: max-age of 30 minutes | ||||
|     * Conditional GETs supported even when max-age sent | ||||
|     * Experimental memcache cache class (default is files) | ||||
|     * Minify_Cache_File has flock()s (by default) | ||||
|     * Workaround for Windows mtime reporting bug | ||||
|  | ||||
| Version 2.0.2 beta (2008-06-24) | ||||
|     * Fast new cache system. Cached files served almost 3x as fast. | ||||
|     * Dropped support of compress encoding (though HTTP_Encoder still supports it) | ||||
|  | ||||
| Version 2.0.1 (2008-05-31) | ||||
|     * E_STRICT compliance (Cache_Lite_File). | ||||
|  | ||||
| Version 2.0.0 (2008-05-22) | ||||
|     * Complete code overhaul. Minify is now a PEAR-style class and toolkit  | ||||
|       for building customized minifying file servers. | ||||
|     * Content-Encoding: deflate/gzip/compress, based on request headers | ||||
|     * Expanded CSS and HTML minifiers with test cases | ||||
|     * Easily plug-in 3rd-party minifiers (like Packer) | ||||
|     * Plug-able front end controller allows changing the way files are chosen | ||||
|     * Compression & encoding modules lazy-loaded as needed (304 responses use  | ||||
|       use minimal code) | ||||
|     * Separate utility classes for HTTP encoding and cache control  | ||||
|  | ||||
| Version 1.0.1 (2007-05-05) | ||||
|     * Fixed various problems resolving pathnames when hosted on an NFS mount. | ||||
|     * Fixed 'undefined constant' notice. | ||||
|     * Replaced old JSMin library with a much faster custom implementation. | ||||
|  | ||||
| Version 1.0.0 (2007-05-02) | ||||
|     * First release. | ||||
							
								
								
									
										26
									
								
								vendor/mrclay/minify/LICENSE.txt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								vendor/mrclay/minify/LICENSE.txt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| Copyright (c) 2008 Ryan Grove <ryan@wonko.com> | ||||
| Copyright (c) 2008 Steve Clay <steve@mrclay.org> | ||||
| All rights reserved. | ||||
|  | ||||
| Redistribution and use in source and binary forms, with or without | ||||
| modification, are permitted provided that the following conditions are met: | ||||
|  | ||||
|   * Redistributions of source code must retain the above copyright notice, | ||||
|     this list of conditions and the following disclaimer. | ||||
|   * Redistributions in binary form must reproduce the above copyright notice, | ||||
|     this list of conditions and the following disclaimer in the documentation | ||||
|     and/or other materials provided with the distribution. | ||||
|   * Neither the name of this project nor the names of its contributors may be | ||||
|     used to endorse or promote products derived from this software without | ||||
|     specific prior written permission. | ||||
|  | ||||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | ||||
| ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||||
| WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||
| DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR | ||||
| ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||||
| (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||||
| LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||||
| ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||||
| SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
							
								
								
									
										181
									
								
								vendor/mrclay/minify/MIN.txt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										181
									
								
								vendor/mrclay/minify/MIN.txt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,181 @@ | ||||
| The files in the /min/ directory represent the default Minify setup designed to ease | ||||
| integration with your site. This app will combine and minify your Javascript or | ||||
| CSS files and serve them with HTTP compression and cache headers. | ||||
|  | ||||
|  | ||||
| RECOMMENDED | ||||
|  | ||||
| It's recommended to edit /min/config.php to set $min_cachePath to a writeable | ||||
| (by PHP) directory on your system. This will improve performance. | ||||
|  | ||||
|  | ||||
| GETTING STARTED | ||||
|  | ||||
| The quickest way to get started is to use the Minify URI Builder application | ||||
| on your website: http://example.com/min/builder/ | ||||
|  | ||||
|  | ||||
| MINIFYING A SINGLE FILE | ||||
|  | ||||
| Let's say you want to serve this file: | ||||
|   http://example.com/wp-content/themes/default/default.css | ||||
|  | ||||
| Here's the "Minify URL" for this file: | ||||
|   http://example.com/min/?f=wp-content/themes/default/default.css | ||||
|  | ||||
| In other words, the "f" argument is set to the file path from root without the | ||||
| initial "/". As CSS files may contain relative URIs, Minify will automatically | ||||
| "fix" these by rewriting them as root relative. | ||||
|  | ||||
|  | ||||
| COMBINING MULTIPLE FILES IN ONE DOWNLOAD | ||||
|  | ||||
| Separate the paths given to "f" with commas. | ||||
|  | ||||
| Let's say you have CSS files at these URLs: | ||||
|   http://example.com/scripts/jquery-1.2.6.js | ||||
|   http://example.com/scripts/site.js | ||||
|  | ||||
| You can combine these files through Minify by requesting this URL: | ||||
|   http://example.com/min/?f=scripts/jquery-1.2.6.js,scripts/site.js | ||||
|  | ||||
|  | ||||
| SIMPLIFYING URLS WITH A BASE PATH | ||||
|  | ||||
| If you're combining files that share the same ancestor directory, you can use | ||||
| the "b" argument to set the base directory for the "f" argument. Do not include | ||||
| the leading or trailing "/" characters. | ||||
|  | ||||
| E.g., the following URLs will serve the exact same content: | ||||
|   http://example.com/min/?f=scripts/jquery-1.2.6.js,scripts/site.js,scripts/home.js | ||||
|   http://example.com/min/?b=scripts&f=jquery-1.2.6.js,site.js,home.js | ||||
|  | ||||
|  | ||||
| MINIFY URLS IN HTML | ||||
|  | ||||
| In HTML files, don't forget to replace any "&" characters with "&". | ||||
|  | ||||
|  | ||||
| SPECIFYING ALLOWED DIRECTORIES | ||||
|  | ||||
| By default, Minify will serve any *.css/*.js files within the DOCUMENT_ROOT. If | ||||
| you'd prefer to limit Minify's access to certain directories, set the | ||||
| $min_serveOptions['minApp']['allowDirs'] array in config.php. E.g. to limit | ||||
| to the /js and /themes/default directories, use: | ||||
|  | ||||
| $min_serveOptions['minApp']['allowDirs'] = array('//js', '//themes/default'); | ||||
|  | ||||
|  | ||||
| GROUPS: NICER URLS | ||||
|  | ||||
| For nicer URLs, edit groupsConfig.php to pre-specify groups of files | ||||
| to be combined under preset keys. E.g., here's an example configuration in | ||||
| groupsConfig.php: | ||||
|  | ||||
| return array( | ||||
|     'js' => array('//js/Class.js', '//js/email.js') | ||||
| ); | ||||
|  | ||||
| This pre-selects the following files to be combined under the key "js": | ||||
|   http://example.com/js/Class.js | ||||
|   http://example.com/js/email.js | ||||
|  | ||||
| You can now serve these files with this simple URL: | ||||
|   http://example.com/min/?g=js | ||||
|  | ||||
|  | ||||
| GROUPS: SPECIFYING FILES OUTSIDE THE DOC_ROOT | ||||
|  | ||||
| In the groupsConfig.php array, the "//" in the file paths is a shortcut for | ||||
| the DOCUMENT_ROOT, but you can also specify paths from the root of the filesystem | ||||
| or relative to the DOC_ROOT: | ||||
|  | ||||
| return array( | ||||
|     'js' => array( | ||||
|         '//js/file.js'            // file within DOC_ROOT | ||||
|         ,'//../file.js'           // file in parent directory of DOC_ROOT | ||||
|         ,'C:/Users/Steve/file.js' // file anywhere on filesystem | ||||
|     ) | ||||
| ); | ||||
|  | ||||
|  | ||||
| COMBINE MULTIPLE GROUPS AND FILES IN ONE URL | ||||
|  | ||||
| E.g.: http://example.com/min/?g=js&f=more/scripts.js | ||||
|  | ||||
| Separate group keys with commas: | ||||
|   http://example.com/min/?g=baseCss,css1&f=moreStyles.css | ||||
|  | ||||
|  | ||||
| FAR-FUTURE EXPIRES HEADERS | ||||
|  | ||||
| Minify can send far-future (one year) Expires headers. To enable this you must | ||||
| add a number or the parameter "v" to the querystring (e.g. /min/?g=js&1234 or | ||||
| /min/?g=js&v=1234) and alter it whenever a source file is changed. If you have a | ||||
| build process you can use a build/source control revision number. | ||||
|  | ||||
| You can alternately use the utility function Minify_getUri() to get a "versioned" | ||||
| Minify URI for use in your HTML. E.g.: | ||||
|  | ||||
| <?php | ||||
| require $_SERVER['DOCUMENT_ROOT'] . '/min/utils.php'; | ||||
|  | ||||
| $jsUri = Minify_getUri('js'); // a key in groupsConfig.php | ||||
| echo "<script src='{$jsUri}'></script>"; | ||||
|  | ||||
| $cssUri = Minify_getUri(array( | ||||
|      '//css/styles1.css' | ||||
|     ,'//css/styles2.css' | ||||
| )); // a list of files | ||||
| echo "<link rel=stylesheet href='{$cssUri}'>"; | ||||
|  | ||||
|  | ||||
| STORING CONFIG FILES OUTSIDE THE MINIFY DIRECTORY | ||||
|  | ||||
| It is possible to store config files (min/config.php, min/config-test.php, | ||||
| min/groupsConfig.php) in a custom directory outside the Minify directory. This is | ||||
| useful if you wish to include Minify as an external dependency inside another | ||||
| project via SVN external or Git submodule inclusion. | ||||
|  | ||||
| For example, let's assume you have a Minify directory "min" in your site root. Then | ||||
| you could create a new directory called "min-configs" in the site root. Copy any | ||||
| config files you wish to modify to "min-configs", and modify as desired. | ||||
|  | ||||
| Then create a new file, for example "min.php" in your site root. The contents of | ||||
| this file could look like this: | ||||
|  | ||||
| <?php | ||||
| $customConfigDirectory = dirname(__FILE__) . '/min-configs'; | ||||
| $min_customConfigPaths = array( | ||||
|     'base'   => $customConfigDirectory . '/config.php', | ||||
|     'test'   => $customConfigDirectory . '/config-test.php', | ||||
|     'groups' => $customConfigDirectory . '/groupsConfig.php' | ||||
| ); | ||||
|  | ||||
| include_once 'min/index.php'; | ||||
|  | ||||
| You would then reference min.php in your JS and CSS links instead of min/index.php. | ||||
|  | ||||
| This method will affect those using the Minify_getUri() function. You will need | ||||
| to add options to calls to that function, e.g.: | ||||
|  | ||||
| <?php | ||||
| require $_SERVER['DOCUMENT_ROOT'] . '/min/utils.php'; | ||||
|  | ||||
| $jsUri = Minify_getUri('//js/file.js', array('minAppUri' => '/min.php')); | ||||
| echo "<script src='{$jsUri}'></script>"; | ||||
|  | ||||
|  | ||||
| DEBUG MODE | ||||
|  | ||||
| In debug mode, instead of compressing files, Minify sends combined files with | ||||
| comments prepended to each line to show the line number in the original source | ||||
| file. To enable this, set $min_allowDebugFlag to true in config.php and append | ||||
| "&debug=1" to your URIs. E.g. /min/?f=script1.js,script2.js&debug=1 | ||||
|  | ||||
| Known issue: files with comment-like strings/regexps can cause problems in this mode. | ||||
|  | ||||
|  | ||||
| QUESTIONS? | ||||
|  | ||||
| http://groups.google.com/group/minify | ||||
							
								
								
									
										68
									
								
								vendor/mrclay/minify/README.txt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								vendor/mrclay/minify/README.txt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | ||||
| WELCOME TO MINIFY! | ||||
|  | ||||
| Minify is an HTTP content server. It compresses sources of content  | ||||
| (usually files), combines the result and serves it with appropriate  | ||||
| HTTP headers. These headers can allow clients to perform conditional  | ||||
| GETs (serving content only when clients do not have a valid cache)  | ||||
| and tell clients to cache the file for a period of time.  | ||||
| More info: http://code.google.com/p/minify/ | ||||
|  | ||||
|  | ||||
| WORDPRESS USER? | ||||
|  | ||||
| These WP plugins integrate Minify into WordPress's style and script hooks to | ||||
| get you set up faster. | ||||
|   http://wordpress.org/extend/plugins/bwp-minify/ | ||||
|   http://wordpress.org/extend/plugins/w3-total-cache/ | ||||
|  | ||||
|  | ||||
| INSTALLATION | ||||
|  | ||||
| Place the /min/ directory as a child of your DOCUMENT_ROOT | ||||
| directory: i.e. you will have: /home/example/www/min | ||||
|  | ||||
| You can see verify that it is working by visiting these two URLs: | ||||
|   http://example.org/min/?f=min/quick-test.js | ||||
|   http://example.org/min/?f=min/quick-test.css | ||||
|  | ||||
| If your server supports mod_rewrite, this URL should also work: | ||||
|   http://example.org/min/f=min/quick-test.js | ||||
|  | ||||
| CONFIGURATION & USAGE | ||||
|  | ||||
| See the MIN.txt file and http://code.google.com/p/minify/wiki/UserGuide | ||||
|  | ||||
| Minify also comes with a URI Builder application that can help you write URLs | ||||
| for use with Minify or configure groups of files. See here for details: | ||||
|   http://code.google.com/p/minify/wiki/BuilderApp | ||||
|  | ||||
| The cookbook also provides some more advanced options for minification: | ||||
|   http://code.google.com/p/minify/wiki/CookBook | ||||
|  | ||||
| UPGRADING | ||||
|  | ||||
| See UPGRADING.txt for instructions. | ||||
|  | ||||
|  | ||||
| UNIT TESTING: | ||||
|  | ||||
| 1. Place the /min_unit_tests/ directory as a child of your DOCUMENT_ROOT  | ||||
| directory: i.e. you will have: /home/example/www/min_unit_tests | ||||
|  | ||||
| 2. To run unit tests, access: http://example.org/min_unit_tests/test_all.php | ||||
|  | ||||
| (If you wish, the other test_*.php files can be run to test individual | ||||
| components with more verbose output.) | ||||
|  | ||||
| 3. Remove /min_unit_tests/ from your DOCUMENT_ROOT when you are done. | ||||
|  | ||||
|  | ||||
| FILE ENCODINGS | ||||
|  | ||||
| Minify *should* work fine with files encoded in UTF-8 or other 8-bit  | ||||
| encodings like ISO 8859/Windows-1252. By default Minify appends | ||||
| ";charset=utf-8" to the Content-Type headers it sends.  | ||||
|  | ||||
| Leading UTF-8 BOMs are stripped from all sources to prevent  | ||||
| duplication in output files, and files are converted to Unix newlines. | ||||
|  | ||||
							
								
								
									
										28
									
								
								vendor/mrclay/minify/UPGRADING.txt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								vendor/mrclay/minify/UPGRADING.txt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| Minify Upgrade Guide | ||||
|  | ||||
| UPGRADING FROM 2.1.* | ||||
|  | ||||
| 1. Rename the following files: | ||||
|  | ||||
|     /min/config.php       --> /min/old_config.php | ||||
|     /min/groupsConfig.php --> /min/old_groupsConfig.php | ||||
|  | ||||
| 2. Overwrite all files in /min (and /min_unit_tests) with those from this zip. | ||||
|  | ||||
| 3. Delete /min/groupsConfig.php | ||||
|  | ||||
| 4. Rename /min/old_groupsConfig.php --> /min/groupsConfig.php | ||||
|  | ||||
| 5. Merge your settings in old_config.php into config.php. | ||||
|  | ||||
| 6. (optional) Delete /min/old_config.php. | ||||
|  | ||||
|  | ||||
| INSTALLING FRESH | ||||
|  | ||||
| See README.txt for instructions on installing this app for the first time. | ||||
|     | ||||
|  | ||||
| SUPPORT | ||||
|  | ||||
| Send a message to http://groups.google.com/group/minify | ||||
							
								
								
									
										775
									
								
								vendor/mrclay/minify/min/lib/CSSmin.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										775
									
								
								vendor/mrclay/minify/min/lib/CSSmin.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,775 @@ | ||||
| <?php | ||||
|  | ||||
| /*! | ||||
|  * cssmin.php 2.4.8-2 | ||||
|  * Author: Tubal Martin - http://tubalmartin.me/ | ||||
|  * Repo: https://github.com/tubalmartin/YUI-CSS-compressor-PHP-port | ||||
|  * | ||||
|  * This is a PHP port of the CSS minification tool distributed with YUICompressor, | ||||
|  * itself a port of the cssmin utility by Isaac Schlueter - http://foohack.com/ | ||||
|  * Permission is hereby granted to use the PHP version under the same | ||||
|  * conditions as the YUICompressor. | ||||
|  */ | ||||
|  | ||||
| /*! | ||||
|  * YUI Compressor | ||||
|  * http://developer.yahoo.com/yui/compressor/ | ||||
|  * Author: Julien Lecomte - http://www.julienlecomte.net/ | ||||
|  * Copyright (c) 2013 Yahoo! Inc. All rights reserved. | ||||
|  * The copyrights embodied in the content of this file are licensed | ||||
|  * by Yahoo! Inc. under the BSD (revised) open source license. | ||||
|  */ | ||||
|  | ||||
| class CSSmin | ||||
| { | ||||
|     const NL = '___YUICSSMIN_PRESERVED_NL___'; | ||||
|     const TOKEN = '___YUICSSMIN_PRESERVED_TOKEN_'; | ||||
|     const COMMENT = '___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_'; | ||||
|     const CLASSCOLON = '___YUICSSMIN_PSEUDOCLASSCOLON___'; | ||||
|     const QUERY_FRACTION = '___YUICSSMIN_QUERY_FRACTION___'; | ||||
|  | ||||
|     private $comments; | ||||
|     private $preserved_tokens; | ||||
|     private $memory_limit; | ||||
|     private $max_execution_time; | ||||
|     private $pcre_backtrack_limit; | ||||
|     private $pcre_recursion_limit; | ||||
|     private $raise_php_limits; | ||||
|  | ||||
|     /** | ||||
|      * @param bool|int $raise_php_limits | ||||
|      * If true, PHP settings will be raised if needed | ||||
|      */ | ||||
|     public function __construct($raise_php_limits = TRUE) | ||||
|     { | ||||
|         // Set suggested PHP limits | ||||
|         $this->memory_limit = 128 * 1048576; // 128MB in bytes | ||||
|         $this->max_execution_time = 60; // 1 min | ||||
|         $this->pcre_backtrack_limit = 1000 * 1000; | ||||
|         $this->pcre_recursion_limit =  500 * 1000; | ||||
|  | ||||
|         $this->raise_php_limits = (bool) $raise_php_limits; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Minify a string of CSS | ||||
|      * @param string $css | ||||
|      * @param int|bool $linebreak_pos | ||||
|      * @return string | ||||
|      */ | ||||
|     public function run($css = '', $linebreak_pos = FALSE) | ||||
|     { | ||||
|         if (empty($css)) { | ||||
|             return ''; | ||||
|         } | ||||
|  | ||||
|         if ($this->raise_php_limits) { | ||||
|             $this->do_raise_php_limits(); | ||||
|         } | ||||
|  | ||||
|         $this->comments = array(); | ||||
|         $this->preserved_tokens = array(); | ||||
|  | ||||
|         $start_index = 0; | ||||
|         $length = strlen($css); | ||||
|  | ||||
|         $css = $this->extract_data_urls($css); | ||||
|  | ||||
|         // collect all comment blocks... | ||||
|         while (($start_index = $this->index_of($css, '/*', $start_index)) >= 0) { | ||||
|             $end_index = $this->index_of($css, '*/', $start_index + 2); | ||||
|             if ($end_index < 0) { | ||||
|                 $end_index = $length; | ||||
|             } | ||||
|             $comment_found = $this->str_slice($css, $start_index + 2, $end_index); | ||||
|             $this->comments[] = $comment_found; | ||||
|             $comment_preserve_string = self::COMMENT . (count($this->comments) - 1) . '___'; | ||||
|             $css = $this->str_slice($css, 0, $start_index + 2) . $comment_preserve_string . $this->str_slice($css, $end_index); | ||||
|             // Set correct start_index: Fixes issue #2528130 | ||||
|             $start_index = $end_index + 2 + strlen($comment_preserve_string) - strlen($comment_found); | ||||
|         } | ||||
|  | ||||
|         // preserve strings so their content doesn't get accidentally minified | ||||
|         $css = preg_replace_callback('/(?:"(?:[^\\\\"]|\\\\.|\\\\)*")|'."(?:'(?:[^\\\\']|\\\\.|\\\\)*')/S", array($this, 'replace_string'), $css); | ||||
|  | ||||
|         // Let's divide css code in chunks of 5.000 chars aprox. | ||||
|         // Reason: PHP's PCRE functions like preg_replace have a "backtrack limit" | ||||
|         // of 100.000 chars by default (php < 5.3.7) so if we're dealing with really | ||||
|         // long strings and a (sub)pattern matches a number of chars greater than | ||||
|         // the backtrack limit number (i.e. /(.*)/s) PCRE functions may fail silently | ||||
|         // returning NULL and $css would be empty. | ||||
|         $charset = ''; | ||||
|         $charset_regexp = '/(@charset)( [^;]+;)/i'; | ||||
|         $css_chunks = array(); | ||||
|         $css_chunk_length = 5000; // aprox size, not exact | ||||
|         $start_index = 0; | ||||
|         $i = $css_chunk_length; // save initial iterations | ||||
|         $l = strlen($css); | ||||
|  | ||||
|  | ||||
|         // if the number of characters is 25000 or less, do not chunk | ||||
|         if ($l <= $css_chunk_length) { | ||||
|             $css_chunks[] = $css; | ||||
|         } else { | ||||
|             // chunk css code securely | ||||
|             while ($i < $l) { | ||||
|                 $i += 50; // save iterations | ||||
|                 if ($l - $start_index <= $css_chunk_length || $i >= $l) { | ||||
|                     $css_chunks[] = $this->str_slice($css, $start_index); | ||||
|                     break; | ||||
|                 } | ||||
|                 if ($css[$i - 1] === '}' && $i - $start_index > $css_chunk_length) { | ||||
|                     // If there are two ending curly braces }} separated or not by spaces, | ||||
|                     // join them in the same chunk (i.e. @media blocks) | ||||
|                     $next_chunk = substr($css, $i); | ||||
|                     if (preg_match('/^\s*\}/', $next_chunk)) { | ||||
|                         $i = $i + $this->index_of($next_chunk, '}') + 1; | ||||
|                     } | ||||
|  | ||||
|                     $css_chunks[] = $this->str_slice($css, $start_index, $i); | ||||
|                     $start_index = $i; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Minify each chunk | ||||
|         for ($i = 0, $n = count($css_chunks); $i < $n; $i++) { | ||||
|             $css_chunks[$i] = $this->minify($css_chunks[$i], $linebreak_pos); | ||||
|             // Keep the first @charset at-rule found | ||||
|             if (empty($charset) && preg_match($charset_regexp, $css_chunks[$i], $matches)) { | ||||
|                 $charset = strtolower($matches[1]) . $matches[2]; | ||||
|             } | ||||
|             // Delete all @charset at-rules | ||||
|             $css_chunks[$i] = preg_replace($charset_regexp, '', $css_chunks[$i]); | ||||
|         } | ||||
|  | ||||
|         // Update the first chunk and push the charset to the top of the file. | ||||
|         $css_chunks[0] = $charset . $css_chunks[0]; | ||||
|  | ||||
|         return implode('', $css_chunks); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Sets the memory limit for this script | ||||
|      * @param int|string $limit | ||||
|      */ | ||||
|     public function set_memory_limit($limit) | ||||
|     { | ||||
|         $this->memory_limit = $this->normalize_int($limit); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Sets the maximum execution time for this script | ||||
|      * @param int|string $seconds | ||||
|      */ | ||||
|     public function set_max_execution_time($seconds) | ||||
|     { | ||||
|         $this->max_execution_time = (int) $seconds; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Sets the PCRE backtrack limit for this script | ||||
|      * @param int $limit | ||||
|      */ | ||||
|     public function set_pcre_backtrack_limit($limit) | ||||
|     { | ||||
|         $this->pcre_backtrack_limit = (int) $limit; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Sets the PCRE recursion limit for this script | ||||
|      * @param int $limit | ||||
|      */ | ||||
|     public function set_pcre_recursion_limit($limit) | ||||
|     { | ||||
|         $this->pcre_recursion_limit = (int) $limit; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Try to configure PHP to use at least the suggested minimum settings | ||||
|      */ | ||||
|     private function do_raise_php_limits() | ||||
|     { | ||||
|         $php_limits = array( | ||||
|             'memory_limit' => $this->memory_limit, | ||||
|             'max_execution_time' => $this->max_execution_time, | ||||
|             'pcre.backtrack_limit' => $this->pcre_backtrack_limit, | ||||
|             'pcre.recursion_limit' =>  $this->pcre_recursion_limit | ||||
|         ); | ||||
|  | ||||
|         // If current settings are higher respect them. | ||||
|         foreach ($php_limits as $name => $suggested) { | ||||
|             $current = $this->normalize_int(ini_get($name)); | ||||
|             // memory_limit exception: allow -1 for "no memory limit". | ||||
|             if ($current > -1 && ($suggested == -1 || $current < $suggested)) { | ||||
|                 ini_set($name, $suggested); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Does bulk of the minification | ||||
|      * @param string $css | ||||
|      * @param int|bool $linebreak_pos | ||||
|      * @return string | ||||
|      */ | ||||
|     private function minify($css, $linebreak_pos) | ||||
|     { | ||||
|         // strings are safe, now wrestle the comments | ||||
|         for ($i = 0, $max = count($this->comments); $i < $max; $i++) { | ||||
|  | ||||
|             $token = $this->comments[$i]; | ||||
|             $placeholder = '/' . self::COMMENT . $i . '___/'; | ||||
|  | ||||
|             // ! in the first position of the comment means preserve | ||||
|             // so push to the preserved tokens keeping the ! | ||||
|             if (substr($token, 0, 1) === '!') { | ||||
|                 $this->preserved_tokens[] = $token; | ||||
|                 $token_tring = self::TOKEN . (count($this->preserved_tokens) - 1) . '___'; | ||||
|                 $css = preg_replace($placeholder, $token_tring, $css, 1); | ||||
|                 // Preserve new lines for /*! important comments | ||||
|                 $css = preg_replace('/\s*[\n\r\f]+\s*(\/\*'. $token_tring .')/S', self::NL.'$1', $css); | ||||
|                 $css = preg_replace('/('. $token_tring .'\*\/)\s*[\n\r\f]+\s*/', '$1'.self::NL, $css); | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             // \ in the last position looks like hack for Mac/IE5 | ||||
|             // shorten that to /*\*/ and the next one to /**/ | ||||
|             if (substr($token, (strlen($token) - 1), 1) === '\\') { | ||||
|                 $this->preserved_tokens[] = '\\'; | ||||
|                 $css = preg_replace($placeholder,  self::TOKEN . (count($this->preserved_tokens) - 1) . '___', $css, 1); | ||||
|                 $i = $i + 1; // attn: advancing the loop | ||||
|                 $this->preserved_tokens[] = ''; | ||||
|                 $css = preg_replace('/' . self::COMMENT . $i . '___/',  self::TOKEN . (count($this->preserved_tokens) - 1) . '___', $css, 1); | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             // keep empty comments after child selectors (IE7 hack) | ||||
|             // e.g. html >/**/ body | ||||
|             if (strlen($token) === 0) { | ||||
|                 $start_index = $this->index_of($css, $this->str_slice($placeholder, 1, -1)); | ||||
|                 if ($start_index > 2) { | ||||
|                     if (substr($css, $start_index - 3, 1) === '>') { | ||||
|                         $this->preserved_tokens[] = ''; | ||||
|                         $css = preg_replace($placeholder,  self::TOKEN . (count($this->preserved_tokens) - 1) . '___', $css, 1); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             // in all other cases kill the comment | ||||
|             $css = preg_replace('/\/\*' . $this->str_slice($placeholder, 1, -1) . '\*\//', '', $css, 1); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         // Normalize all whitespace strings to single spaces. Easier to work with that way. | ||||
|         $css = preg_replace('/\s+/', ' ', $css); | ||||
|  | ||||
| 		// Fix IE7 issue on matrix filters which browser accept whitespaces between Matrix parameters | ||||
| 		$css = preg_replace_callback('/\s*filter\:\s*progid:DXImageTransform\.Microsoft\.Matrix\(([^\)]+)\)/', array($this, 'preserve_old_IE_specific_matrix_definition'), $css); | ||||
|  | ||||
|         // Shorten & preserve calculations calc(...) since spaces are important | ||||
|         $css = preg_replace_callback('/calc(\(((?:[^\(\)]+|(?1))*)\))/i', array($this, 'replace_calc'), $css); | ||||
|  | ||||
|         // Replace positive sign from numbers preceded by : or a white-space before the leading space is removed | ||||
|         // +1.2em to 1.2em, +.8px to .8px, +2% to 2% | ||||
|         $css = preg_replace('/((?<!\\\\)\:|\s)\+(\.?\d+)/S', '$1$2', $css); | ||||
|  | ||||
|         // Remove leading zeros from integer and float numbers preceded by : or a white-space | ||||
|         // 000.6 to .6, -0.8 to -.8, 0050 to 50, -01.05 to -1.05 | ||||
|         $css = preg_replace('/((?<!\\\\)\:|\s)(\-?)0+(\.?\d+)/S', '$1$2$3', $css); | ||||
|  | ||||
|         // Remove trailing zeros from float numbers preceded by : or a white-space | ||||
|         // -6.0100em to -6.01em, .0100 to .01, 1.200px to 1.2px | ||||
|         $css = preg_replace('/((?<!\\\\)\:|\s)(\-?)(\d?\.\d+?)0+([^\d])/S', '$1$2$3$4', $css); | ||||
|  | ||||
|         // Remove trailing .0 -> -9.0 to -9 | ||||
|         $css = preg_replace('/((?<!\\\\)\:|\s)(\-?\d+)\.0([^\d])/S', '$1$2$3', $css); | ||||
|  | ||||
|         // Replace 0 length numbers with 0 | ||||
|         $css = preg_replace('/((?<!\\\\)\:|\s)\-?\.?0+([^\d])/S', '${1}0$2', $css); | ||||
|  | ||||
|         // Remove the spaces before the things that should not have spaces before them. | ||||
|         // But, be careful not to turn "p :link {...}" into "p:link{...}" | ||||
|         // Swap out any pseudo-class colons with the token, and then swap back. | ||||
|         $css = preg_replace_callback('/(?:^|\})(?:(?:[^\{\:])+\:)+(?:[^\{]*\{)/', array($this, 'replace_colon'), $css); | ||||
|  | ||||
|         // Remove spaces before the things that should not have spaces before them. | ||||
|         $css = preg_replace('/\s+([\!\{\}\;\:\>\+\(\)\]\~\=,])/', '$1', $css); | ||||
|  | ||||
|         // Restore spaces for !important | ||||
|         $css = preg_replace('/\!important/i', ' !important', $css); | ||||
|  | ||||
|         // bring back the colon | ||||
|         $css = preg_replace('/' . self::CLASSCOLON . '/', ':', $css); | ||||
|  | ||||
|         // retain space for special IE6 cases | ||||
|         $css = preg_replace_callback('/\:first\-(line|letter)(\{|,)/i', array($this, 'lowercase_pseudo_first'), $css); | ||||
|  | ||||
|         // no space after the end of a preserved comment | ||||
|         $css = preg_replace('/\*\/ /', '*/', $css); | ||||
|  | ||||
|         // lowercase some popular @directives | ||||
|         $css = preg_replace_callback('/@(font-face|import|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?keyframe|media|page|namespace)/i', array($this, 'lowercase_directives'), $css); | ||||
|  | ||||
|         // lowercase some more common pseudo-elements | ||||
|         $css = preg_replace_callback('/:(active|after|before|checked|disabled|empty|enabled|first-(?:child|of-type)|focus|hover|last-(?:child|of-type)|link|only-(?:child|of-type)|root|:selection|target|visited)/i', array($this, 'lowercase_pseudo_elements'), $css); | ||||
|  | ||||
|         // lowercase some more common functions | ||||
|         $css = preg_replace_callback('/:(lang|not|nth-child|nth-last-child|nth-last-of-type|nth-of-type|(?:-(?:moz|webkit)-)?any)\(/i', array($this, 'lowercase_common_functions'), $css); | ||||
|  | ||||
|         // lower case some common function that can be values | ||||
|         // NOTE: rgb() isn't useful as we replace with #hex later, as well as and() is already done for us | ||||
|         $css = preg_replace_callback('/([:,\( ]\s*)(attr|color-stop|from|rgba|to|url|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?(?:calc|max|min|(?:repeating-)?(?:linear|radial)-gradient)|-webkit-gradient)/iS', array($this, 'lowercase_common_functions_values'), $css); | ||||
|  | ||||
|         // Put the space back in some cases, to support stuff like | ||||
|         // @media screen and (-webkit-min-device-pixel-ratio:0){ | ||||
|         $css = preg_replace('/\band\(/i', 'and (', $css); | ||||
|  | ||||
|         // Remove the spaces after the things that should not have spaces after them. | ||||
|         $css = preg_replace('/([\!\{\}\:;\>\+\(\[\~\=,])\s+/S', '$1', $css); | ||||
|  | ||||
|         // remove unnecessary semicolons | ||||
|         $css = preg_replace('/;+\}/', '}', $css); | ||||
|  | ||||
|         // Fix for issue: #2528146 | ||||
|         // Restore semicolon if the last property is prefixed with a `*` (lte IE7 hack) | ||||
|         // to avoid issues on Symbian S60 3.x browsers. | ||||
|         $css = preg_replace('/(\*[a-z0-9\-]+\s*\:[^;\}]+)(\})/', '$1;$2', $css); | ||||
|  | ||||
|         // Replace 0 length units 0(px,em,%) with 0. | ||||
|         $css = preg_replace('/(^|[^0-9])(?:0?\.)?0(?:em|ex|ch|rem|vw|vh|vm|vmin|cm|mm|in|px|pt|pc|%|deg|g?rad|m?s|k?hz)/iS', '${1}0', $css); | ||||
|  | ||||
| 		// 0% step in a keyframe? restore the % unit | ||||
| 		$css = preg_replace_callback('/(@[a-z\-]*?keyframes[^\{]*?\{)(.*?\}\s*\})/iS', array($this, 'replace_keyframe_zero'), $css); | ||||
|  | ||||
|         // Replace 0 0; or 0 0 0; or 0 0 0 0; with 0. | ||||
|         $css = preg_replace('/\:0(?: 0){1,3}(;|\}| \!)/', ':0$1', $css); | ||||
|  | ||||
|         // Fix for issue: #2528142 | ||||
|         // Replace text-shadow:0; with text-shadow:0 0 0; | ||||
|         $css = preg_replace('/(text-shadow\:0)(;|\}| \!)/i', '$1 0 0$2', $css); | ||||
|  | ||||
|         // Replace background-position:0; with background-position:0 0; | ||||
|         // same for transform-origin | ||||
|         // Changing -webkit-mask-position: 0 0 to just a single 0 will result in the second parameter defaulting to 50% (center) | ||||
|         $css = preg_replace('/(background\-position|webkit-mask-position|(?:webkit|moz|o|ms|)\-?transform\-origin)\:0(;|\}| \!)/iS', '$1:0 0$2', $css); | ||||
|  | ||||
|         // Shorten colors from rgb(51,102,153) to #336699, rgb(100%,0%,0%) to #ff0000 (sRGB color space) | ||||
|         // Shorten colors from hsl(0, 100%, 50%) to #ff0000 (sRGB color space) | ||||
|         // This makes it more likely that it'll get further compressed in the next step. | ||||
|         $css = preg_replace_callback('/rgb\s*\(\s*([0-9,\s\-\.\%]+)\s*\)(.{1})/i', array($this, 'rgb_to_hex'), $css); | ||||
|         $css = preg_replace_callback('/hsl\s*\(\s*([0-9,\s\-\.\%]+)\s*\)(.{1})/i', array($this, 'hsl_to_hex'), $css); | ||||
|  | ||||
|         // Shorten colors from #AABBCC to #ABC or short color name. | ||||
|         $css = $this->compress_hex_colors($css); | ||||
|  | ||||
|         // border: none to border:0, outline: none to outline:0 | ||||
|         $css = preg_replace('/(border\-?(?:top|right|bottom|left|)|outline)\:none(;|\}| \!)/iS', '$1:0$2', $css); | ||||
|  | ||||
|         // shorter opacity IE filter | ||||
|         $css = preg_replace('/progid\:DXImageTransform\.Microsoft\.Alpha\(Opacity\=/i', 'alpha(opacity=', $css); | ||||
|  | ||||
|         // Find a fraction that is used for Opera's -o-device-pixel-ratio query | ||||
|         // Add token to add the "\" back in later | ||||
|         $css = preg_replace('/\(([a-z\-]+):([0-9]+)\/([0-9]+)\)/i', '($1:$2'. self::QUERY_FRACTION .'$3)', $css); | ||||
|  | ||||
|         // Remove empty rules. | ||||
|         $css = preg_replace('/[^\};\{\/]+\{\}/S', '', $css); | ||||
|  | ||||
|         // Add "/" back to fix Opera -o-device-pixel-ratio query | ||||
|         $css = preg_replace('/'. self::QUERY_FRACTION .'/', '/', $css); | ||||
|  | ||||
| 		// Replace multiple semi-colons in a row by a single one | ||||
|         // See SF bug #1980989 | ||||
|         $css = preg_replace('/;;+/', ';', $css); | ||||
|  | ||||
|         // Restore new lines for /*! important comments | ||||
|         $css = preg_replace('/'. self::NL .'/', "\n", $css); | ||||
|  | ||||
|         // Lowercase all uppercase properties | ||||
|         $css = preg_replace_callback('/(\{|\;)([A-Z\-]+)(\:)/', array($this, 'lowercase_properties'), $css); | ||||
|  | ||||
|         // Some source control tools don't like it when files containing lines longer | ||||
|         // than, say 8000 characters, are checked in. The linebreak option is used in | ||||
|         // that case to split long lines after a specific column. | ||||
|         if ($linebreak_pos !== FALSE && (int) $linebreak_pos >= 0) { | ||||
|             $linebreak_pos = (int) $linebreak_pos; | ||||
|             $start_index = $i = 0; | ||||
|             while ($i < strlen($css)) { | ||||
|                 $i++; | ||||
|                 if ($css[$i - 1] === '}' && $i - $start_index > $linebreak_pos) { | ||||
|                     $css = $this->str_slice($css, 0, $i) . "\n" . $this->str_slice($css, $i); | ||||
|                     $start_index = $i; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // restore preserved comments and strings in reverse order | ||||
|         for ($i = count($this->preserved_tokens) - 1; $i >= 0; $i--) { | ||||
|             $css = preg_replace('/' . self::TOKEN . $i . '___/', $this->preserved_tokens[$i], $css, 1); | ||||
|         } | ||||
|  | ||||
|         // Trim the final string (for any leading or trailing white spaces) | ||||
|         return trim($css); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Utility method to replace all data urls with tokens before we start | ||||
|      * compressing, to avoid performance issues running some of the subsequent | ||||
|      * regexes against large strings chunks. | ||||
|      * | ||||
|      * @param string $css | ||||
|      * @return string | ||||
|      */ | ||||
|     private function extract_data_urls($css) | ||||
|     { | ||||
|         // Leave data urls alone to increase parse performance. | ||||
|         $max_index = strlen($css) - 1; | ||||
|         $append_index = $index = $last_index = $offset = 0; | ||||
|         $sb = array(); | ||||
|         $pattern = '/url\(\s*(["\']?)data\:/i'; | ||||
|  | ||||
|         // Since we need to account for non-base64 data urls, we need to handle | ||||
|         // ' and ) being part of the data string. Hence switching to indexOf, | ||||
|         // to determine whether or not we have matching string terminators and | ||||
|         // handling sb appends directly, instead of using matcher.append* methods. | ||||
|  | ||||
|         while (preg_match($pattern, $css, $m, 0, $offset)) { | ||||
|             $index = $this->index_of($css, $m[0], $offset); | ||||
|             $last_index = $index + strlen($m[0]); | ||||
|             $start_index = $index + 4; // "url(".length() | ||||
|             $end_index = $last_index - 1; | ||||
|             $terminator = $m[1]; // ', " or empty (not quoted) | ||||
|             $found_terminator = FALSE; | ||||
|  | ||||
|             if (strlen($terminator) === 0) { | ||||
|                 $terminator = ')'; | ||||
|             } | ||||
|  | ||||
|             while ($found_terminator === FALSE && $end_index+1 <= $max_index) { | ||||
|                 $end_index = $this->index_of($css, $terminator, $end_index + 1); | ||||
|  | ||||
|                 // endIndex == 0 doesn't really apply here | ||||
|                 if ($end_index > 0 && substr($css, $end_index - 1, 1) !== '\\') { | ||||
|                     $found_terminator = TRUE; | ||||
|                     if (')' != $terminator) { | ||||
|                         $end_index = $this->index_of($css, ')', $end_index); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             // Enough searching, start moving stuff over to the buffer | ||||
|             $sb[] = $this->str_slice($css, $append_index, $index); | ||||
|  | ||||
|             if ($found_terminator) { | ||||
|                 $token = $this->str_slice($css, $start_index, $end_index); | ||||
|                 $token = preg_replace('/\s+/', '', $token); | ||||
|                 $this->preserved_tokens[] = $token; | ||||
|  | ||||
|                 $preserver = 'url(' . self::TOKEN . (count($this->preserved_tokens) - 1) . '___)'; | ||||
|                 $sb[] = $preserver; | ||||
|  | ||||
|                 $append_index = $end_index + 1; | ||||
|             } else { | ||||
|                 // No end terminator found, re-add the whole match. Should we throw/warn here? | ||||
|                 $sb[] = $this->str_slice($css, $index, $last_index); | ||||
|                 $append_index = $last_index; | ||||
|             } | ||||
|  | ||||
|             $offset = $last_index; | ||||
|         } | ||||
|  | ||||
|         $sb[] = $this->str_slice($css, $append_index); | ||||
|  | ||||
|         return implode('', $sb); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Utility method to compress hex color values of the form #AABBCC to #ABC or short color name. | ||||
|      * | ||||
|      * DOES NOT compress CSS ID selectors which match the above pattern (which would break things). | ||||
|      * e.g. #AddressForm { ... } | ||||
|      * | ||||
|      * DOES NOT compress IE filters, which have hex color values (which would break things). | ||||
|      * e.g. filter: chroma(color="#FFFFFF"); | ||||
|      * | ||||
|      * DOES NOT compress invalid hex values. | ||||
|      * e.g. background-color: #aabbccdd | ||||
|      * | ||||
|      * @param string $css | ||||
|      * @return string | ||||
|      */ | ||||
|     private function compress_hex_colors($css) | ||||
|     { | ||||
|         // Look for hex colors inside { ... } (to avoid IDs) and which don't have a =, or a " in front of them (to avoid filters) | ||||
|         $pattern = '/(\=\s*?["\']?)?#([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])(\}|[^0-9a-f{][^{]*?\})/iS'; | ||||
|         $_index = $index = $last_index = $offset = 0; | ||||
|         $sb = array(); | ||||
|         // See: http://ajaxmin.codeplex.com/wikipage?title=CSS%20Colors | ||||
|         $short_safe = array( | ||||
|             '#808080' => 'gray', | ||||
|             '#008000' => 'green', | ||||
|             '#800000' => 'maroon', | ||||
|             '#000080' => 'navy', | ||||
|             '#808000' => 'olive', | ||||
|             '#ffa500' => 'orange', | ||||
|             '#800080' => 'purple', | ||||
|             '#c0c0c0' => 'silver', | ||||
|             '#008080' => 'teal', | ||||
|             '#f00' => 'red' | ||||
|         ); | ||||
|  | ||||
|         while (preg_match($pattern, $css, $m, 0, $offset)) { | ||||
|             $index = $this->index_of($css, $m[0], $offset); | ||||
|             $last_index = $index + strlen($m[0]); | ||||
|             $is_filter = $m[1] !== null && $m[1] !== ''; | ||||
|  | ||||
|             $sb[] = $this->str_slice($css, $_index, $index); | ||||
|  | ||||
|             if ($is_filter) { | ||||
|                 // Restore, maintain case, otherwise filter will break | ||||
|                 $sb[] = $m[1] . '#' . $m[2] . $m[3] . $m[4] . $m[5] . $m[6] . $m[7]; | ||||
|             } else { | ||||
|                 if (strtolower($m[2]) == strtolower($m[3]) && | ||||
|                     strtolower($m[4]) == strtolower($m[5]) && | ||||
|                     strtolower($m[6]) == strtolower($m[7])) { | ||||
|                     // Compress. | ||||
|                     $hex = '#' . strtolower($m[3] . $m[5] . $m[7]); | ||||
|                 } else { | ||||
|                     // Non compressible color, restore but lower case. | ||||
|                     $hex = '#' . strtolower($m[2] . $m[3] . $m[4] . $m[5] . $m[6] . $m[7]); | ||||
|                 } | ||||
|                 // replace Hex colors to short safe color names | ||||
|                 $sb[] = array_key_exists($hex, $short_safe) ? $short_safe[$hex] : $hex; | ||||
|             } | ||||
|  | ||||
|             $_index = $offset = $last_index - strlen($m[8]); | ||||
|         } | ||||
|  | ||||
|         $sb[] = $this->str_slice($css, $_index); | ||||
|  | ||||
|         return implode('', $sb); | ||||
|     } | ||||
|  | ||||
|     /* CALLBACKS | ||||
|      * --------------------------------------------------------------------------------------------- | ||||
|      */ | ||||
|  | ||||
|     private function replace_string($matches) | ||||
|     { | ||||
|         $match = $matches[0]; | ||||
|         $quote = substr($match, 0, 1); | ||||
|         // Must use addcslashes in PHP to avoid parsing of backslashes | ||||
|         $match = addcslashes($this->str_slice($match, 1, -1), '\\'); | ||||
|  | ||||
|         // maybe the string contains a comment-like substring? | ||||
|         // one, maybe more? put'em back then | ||||
|         if (($pos = $this->index_of($match, self::COMMENT)) >= 0) { | ||||
|             for ($i = 0, $max = count($this->comments); $i < $max; $i++) { | ||||
|                 $match = preg_replace('/' . self::COMMENT . $i . '___/', $this->comments[$i], $match, 1); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // minify alpha opacity in filter strings | ||||
|         $match = preg_replace('/progid\:DXImageTransform\.Microsoft\.Alpha\(Opacity\=/i', 'alpha(opacity=', $match); | ||||
|  | ||||
|         $this->preserved_tokens[] = $match; | ||||
|         return $quote . self::TOKEN . (count($this->preserved_tokens) - 1) . '___' . $quote; | ||||
|     } | ||||
|  | ||||
|     private function replace_colon($matches) | ||||
|     { | ||||
|         return preg_replace('/\:/', self::CLASSCOLON, $matches[0]); | ||||
|     } | ||||
|  | ||||
|     private function replace_calc($matches) | ||||
|     { | ||||
|         $this->preserved_tokens[] = trim(preg_replace('/\s*([\*\/\(\),])\s*/', '$1', $matches[2])); | ||||
|         return 'calc('. self::TOKEN . (count($this->preserved_tokens) - 1) . '___' . ')'; | ||||
|     } | ||||
|  | ||||
| 	private function preserve_old_IE_specific_matrix_definition($matches) | ||||
| 	{ | ||||
| 		$this->preserved_tokens[] = $matches[1]; | ||||
| 		return 'filter:progid:DXImageTransform.Microsoft.Matrix(' . self::TOKEN . (count($this->preserved_tokens) - 1) . '___' . ')'; | ||||
|     } | ||||
|  | ||||
| 	private function replace_keyframe_zero($matches) | ||||
|     { | ||||
|         return $matches[1] . preg_replace('/0\s*,/', '0%,', preg_replace('/\s*0\s*\{/', '0%{', $matches[2])); | ||||
|     } | ||||
|  | ||||
|     private function rgb_to_hex($matches) | ||||
|     { | ||||
|         // Support for percentage values rgb(100%, 0%, 45%); | ||||
|         if ($this->index_of($matches[1], '%') >= 0){ | ||||
|             $rgbcolors = explode(',', str_replace('%', '', $matches[1])); | ||||
|             for ($i = 0; $i < count($rgbcolors); $i++) { | ||||
|                 $rgbcolors[$i] = $this->round_number(floatval($rgbcolors[$i]) * 2.55); | ||||
|             } | ||||
|         } else { | ||||
|             $rgbcolors = explode(',', $matches[1]); | ||||
|         } | ||||
|  | ||||
|         // Values outside the sRGB color space should be clipped (0-255) | ||||
|         for ($i = 0; $i < count($rgbcolors); $i++) { | ||||
|             $rgbcolors[$i] = $this->clamp_number(intval($rgbcolors[$i], 10), 0, 255); | ||||
|             $rgbcolors[$i] = sprintf("%02x", $rgbcolors[$i]); | ||||
|         } | ||||
|  | ||||
|         // Fix for issue #2528093 | ||||
|         if (!preg_match('/[\s\,\);\}]/', $matches[2])){ | ||||
|             $matches[2] = ' ' . $matches[2]; | ||||
|         } | ||||
|  | ||||
|         return '#' . implode('', $rgbcolors) . $matches[2]; | ||||
|     } | ||||
|  | ||||
|     private function hsl_to_hex($matches) | ||||
|     { | ||||
|         $values = explode(',', str_replace('%', '', $matches[1])); | ||||
|         $h = floatval($values[0]); | ||||
|         $s = floatval($values[1]); | ||||
|         $l = floatval($values[2]); | ||||
|  | ||||
|         // Wrap and clamp, then fraction! | ||||
|         $h = ((($h % 360) + 360) % 360) / 360; | ||||
|         $s = $this->clamp_number($s, 0, 100) / 100; | ||||
|         $l = $this->clamp_number($l, 0, 100) / 100; | ||||
|  | ||||
|         if ($s == 0) { | ||||
|             $r = $g = $b = $this->round_number(255 * $l); | ||||
|         } else { | ||||
|             $v2 = $l < 0.5 ? $l * (1 + $s) : ($l + $s) - ($s * $l); | ||||
|             $v1 = (2 * $l) - $v2; | ||||
|             $r = $this->round_number(255 * $this->hue_to_rgb($v1, $v2, $h + (1/3))); | ||||
|             $g = $this->round_number(255 * $this->hue_to_rgb($v1, $v2, $h)); | ||||
|             $b = $this->round_number(255 * $this->hue_to_rgb($v1, $v2, $h - (1/3))); | ||||
|         } | ||||
|  | ||||
|         return $this->rgb_to_hex(array('', $r.','.$g.','.$b, $matches[2])); | ||||
|     } | ||||
|  | ||||
|     private function lowercase_pseudo_first($matches) | ||||
|     { | ||||
|         return ':first-'. strtolower($matches[1]) .' '. $matches[2]; | ||||
|     } | ||||
|  | ||||
|     private function lowercase_directives($matches) | ||||
|     { | ||||
|         return '@'. strtolower($matches[1]); | ||||
|     } | ||||
|  | ||||
|     private function lowercase_pseudo_elements($matches) | ||||
|     { | ||||
|         return ':'. strtolower($matches[1]); | ||||
|     } | ||||
|  | ||||
|     private function lowercase_common_functions($matches) | ||||
|     { | ||||
|         return ':'. strtolower($matches[1]) .'('; | ||||
|     } | ||||
|  | ||||
|     private function lowercase_common_functions_values($matches) | ||||
|     { | ||||
|         return $matches[1] . strtolower($matches[2]); | ||||
|     } | ||||
|  | ||||
|     private function lowercase_properties($matches) | ||||
|     { | ||||
|         return $matches[1].strtolower($matches[2]).$matches[3]; | ||||
|     } | ||||
|  | ||||
|     /* HELPERS | ||||
|      * --------------------------------------------------------------------------------------------- | ||||
|      */ | ||||
|  | ||||
|     private function hue_to_rgb($v1, $v2, $vh) | ||||
|     { | ||||
|         $vh = $vh < 0 ? $vh + 1 : ($vh > 1 ? $vh - 1 : $vh); | ||||
|         if ($vh * 6 < 1) return $v1 + ($v2 - $v1) * 6 * $vh; | ||||
|         if ($vh * 2 < 1) return $v2; | ||||
|         if ($vh * 3 < 2) return $v1 + ($v2 - $v1) * ((2/3) - $vh) * 6; | ||||
|         return $v1; | ||||
|     } | ||||
|  | ||||
|     private function round_number($n) | ||||
|     { | ||||
|         return intval(floor(floatval($n) + 0.5), 10); | ||||
|     } | ||||
|  | ||||
|     private function clamp_number($n, $min, $max) | ||||
|     { | ||||
|         return min(max($n, $min), $max); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * PHP port of Javascript's "indexOf" function for strings only | ||||
|      * Author: Tubal Martin http://blog.margenn.com | ||||
|      * | ||||
|      * @param string $haystack | ||||
|      * @param string $needle | ||||
|      * @param int    $offset index (optional) | ||||
|      * @return int | ||||
|      */ | ||||
|     private function index_of($haystack, $needle, $offset = 0) | ||||
|     { | ||||
|         $index = strpos($haystack, $needle, $offset); | ||||
|  | ||||
|         return ($index !== FALSE) ? $index : -1; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * PHP port of Javascript's "slice" function for strings only | ||||
|      * Author: Tubal Martin http://blog.margenn.com | ||||
|      * Tests: http://margenn.com/tubal/str_slice/ | ||||
|      * | ||||
|      * @param string   $str | ||||
|      * @param int      $start index | ||||
|      * @param int|bool $end index (optional) | ||||
|      * @return string | ||||
|      */ | ||||
|     private function str_slice($str, $start = 0, $end = FALSE) | ||||
|     { | ||||
|         if ($end !== FALSE && ($start < 0 || $end <= 0)) { | ||||
|             $max = strlen($str); | ||||
|  | ||||
|             if ($start < 0) { | ||||
|                 if (($start = $max + $start) < 0) { | ||||
|                     return ''; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             if ($end < 0) { | ||||
|                 if (($end = $max + $end) < 0) { | ||||
|                     return ''; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             if ($end <= $start) { | ||||
|                 return ''; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $slice = ($end === FALSE) ? substr($str, $start) : substr($str, $start, $end - $start); | ||||
|         return ($slice === FALSE) ? '' : $slice; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Convert strings like "64M" or "30" to int values | ||||
|      * @param mixed $size | ||||
|      * @return int | ||||
|      */ | ||||
|     private function normalize_int($size) | ||||
|     { | ||||
|         if (is_string($size)) { | ||||
|             switch (substr($size, -1)) { | ||||
|                 case 'M': case 'm': return $size * 1048576; | ||||
|                 case 'K': case 'k': return $size * 1024; | ||||
|                 case 'G': case 'g': return $size * 1073741824; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return (int) $size; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										121
									
								
								vendor/mrclay/minify/min/lib/DooDigestAuth.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								vendor/mrclay/minify/min/lib/DooDigestAuth.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,121 @@ | ||||
| <?php | ||||
| /** | ||||
|  * DooDigestAuth class file. | ||||
|  * | ||||
|  * @author Leng Sheng Hong <darkredz@gmail.com> | ||||
|  * @link http://www.doophp.com/ | ||||
|  * @copyright Copyright © 2009 Leng Sheng Hong | ||||
|  * @license http://www.doophp.com/license | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * Handles HTTP digest authentication | ||||
|  * | ||||
|  * <p>HTTP digest authentication can be used with the URI router. | ||||
|  * HTTP digest is much more recommended over the use of HTTP Basic auth which doesn't provide any encryption. | ||||
|  * If you are running PHP on Apache in CGI/FastCGI mode, you would need to | ||||
|  * add the following line to your .htaccess for digest auth to work correctly.</p> | ||||
|  * <code>RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]</code> | ||||
|  * | ||||
|  * <p>This class is tested under Apache 2.2 and Cherokee web server. It should work in both mod_php and cgi mode.</p> | ||||
|  * | ||||
|  * @author Leng Sheng Hong <darkredz@gmail.com> | ||||
|  * @version $Id: DooDigestAuth.php 1000 2009-07-7 18:27:22 | ||||
|  * @package doo.auth | ||||
|  * @since 1.0 | ||||
|  */ | ||||
| class DooDigestAuth{ | ||||
|  | ||||
|     /** | ||||
|      * Authenticate against a list of username and passwords. | ||||
|      * | ||||
|      * <p>HTTP Digest Authentication doesn't work with PHP in CGI mode, | ||||
|      * you have to add this into your .htaccess <code>RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]</code></p> | ||||
|      * | ||||
|      * @param string $realm Name of the authentication session | ||||
|      * @param array $users An assoc array of username and password: array('uname1'=>'pwd1', 'uname2'=>'pwd2') | ||||
|      * @param string $fail_msg Message to be displayed if the User cancel the login | ||||
|      * @param string $fail_url URL to be redirect if the User cancel the login | ||||
|      * @return string The username if login success. | ||||
|      */ | ||||
|     public static function http_auth($realm, $users, $fail_msg=NULL, $fail_url=NULL){ | ||||
|         $realm = "Restricted area - $realm"; | ||||
|  | ||||
|         //user => password | ||||
|         //$users = array('admin' => '1234', 'guest' => 'guest'); | ||||
|         if(!empty($_SERVER['REDIRECT_HTTP_AUTHORIZATION']) && strpos($_SERVER['REDIRECT_HTTP_AUTHORIZATION'], 'Digest')===0){ | ||||
|             $_SERVER['PHP_AUTH_DIGEST'] = $_SERVER['REDIRECT_HTTP_AUTHORIZATION']; | ||||
|         } | ||||
|  | ||||
|         if (empty($_SERVER['PHP_AUTH_DIGEST'])) { | ||||
|             header('WWW-Authenticate: Digest realm="'.$realm. | ||||
|                    '",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"'); | ||||
|             header('HTTP/1.1 401 Unauthorized'); | ||||
|             if($fail_msg!=NULL) | ||||
|                 die($fail_msg); | ||||
|             if($fail_url!=NULL) | ||||
|                 die("<script>window.location.href = '$fail_url'</script>"); | ||||
|             exit; | ||||
|         } | ||||
|  | ||||
|         // analyze the PHP_AUTH_DIGEST variable | ||||
|         if (!($data = self::http_digest_parse($_SERVER['PHP_AUTH_DIGEST'])) || !isset($users[$data['username']])){ | ||||
|             header('WWW-Authenticate: Digest realm="'.$realm. | ||||
|                    '",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"'); | ||||
|             header('HTTP/1.1 401 Unauthorized'); | ||||
|             if($fail_msg!=NULL) | ||||
|                 die($fail_msg); | ||||
|             if($fail_url!=NULL) | ||||
|                 die("<script>window.location.href = '$fail_url'</script>"); | ||||
|             exit; | ||||
|         } | ||||
|  | ||||
|         // generate the valid response | ||||
|         $A1 = md5($data['username'] . ':' . $realm . ':' . $users[$data['username']]); | ||||
|         $A2 = md5($_SERVER['REQUEST_METHOD'].':'.$data['uri']); | ||||
|         $valid_response = md5($A1.':'.$data['nonce'].':'.$data['nc'].':'.$data['cnonce'].':'.$data['qop'].':'.$A2); | ||||
|  | ||||
|         if ($data['response'] != $valid_response){ | ||||
|             header('HTTP/1.1 401 Unauthorized'); | ||||
|             header('WWW-Authenticate: Digest realm="'.$realm. | ||||
|                    '",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"'); | ||||
|             if($fail_msg!=NULL) | ||||
|                 die($fail_msg); | ||||
|             if($fail_url!=NULL) | ||||
|                 die("<script>window.location.href = '$fail_url'</script>"); | ||||
|             exit; | ||||
|         } | ||||
|  | ||||
|         // ok, valid username & password | ||||
|         return $data['username']; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Method to parse the http auth header, works with IE. | ||||
|      * | ||||
|      * Internet Explorer returns a qop="xxxxxxxxxxx" in the header instead of qop=xxxxxxxxxxx as most browsers do. | ||||
|      * | ||||
|      * @param string $txt header string to parse | ||||
|      * @return array An assoc array of the digest auth session | ||||
|      */ | ||||
|     private static function http_digest_parse($txt) | ||||
|     { | ||||
|         $res = preg_match("/username=\"([^\"]+)\"/i", $txt, $match); | ||||
|         $data['username'] = (isset($match[1]))?$match[1]:null; | ||||
|         $res = preg_match('/nonce=\"([^\"]+)\"/i', $txt, $match); | ||||
|         $data['nonce'] = $match[1]; | ||||
|         $res = preg_match('/nc=([0-9]+)/i', $txt, $match); | ||||
|         $data['nc'] = $match[1]; | ||||
|         $res = preg_match('/cnonce=\"([^\"]+)\"/i', $txt, $match); | ||||
|         $data['cnonce'] = $match[1]; | ||||
|         $res = preg_match('/qop=([^,]+)/i', $txt, $match); | ||||
|         $data['qop'] = str_replace('"','',$match[1]); | ||||
|         $res = preg_match('/uri=\"([^\"]+)\"/i', $txt, $match); | ||||
|         $data['uri'] = $match[1]; | ||||
|         $res = preg_match('/response=\"([^\"]+)\"/i', $txt, $match); | ||||
|         $data['response'] = $match[1]; | ||||
|         return $data; | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
							
								
								
									
										1370
									
								
								vendor/mrclay/minify/min/lib/FirePHP.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1370
									
								
								vendor/mrclay/minify/min/lib/FirePHP.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										366
									
								
								vendor/mrclay/minify/min/lib/HTTP/ConditionalGet.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										366
									
								
								vendor/mrclay/minify/min/lib/HTTP/ConditionalGet.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,366 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Class HTTP_ConditionalGet   | ||||
|  * @package Minify | ||||
|  * @subpackage HTTP | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * Implement conditional GET via a timestamp or hash of content | ||||
|  * | ||||
|  * E.g. Content from DB with update time: | ||||
|  * <code> | ||||
|  * list($updateTime, $content) = getDbUpdateAndContent(); | ||||
|  * $cg = new HTTP_ConditionalGet(array( | ||||
|  *     'lastModifiedTime' => $updateTime | ||||
|  *     ,'isPublic' => true | ||||
|  * )); | ||||
|  * $cg->sendHeaders(); | ||||
|  * if ($cg->cacheIsValid) { | ||||
|  *     exit(); | ||||
|  * } | ||||
|  * echo $content; | ||||
|  * </code> | ||||
|  *  | ||||
|  * E.g. Shortcut for the above | ||||
|  * <code> | ||||
|  * HTTP_ConditionalGet::check($updateTime, true); // exits if client has cache | ||||
|  * echo $content; | ||||
|  * </code> | ||||
|  * | ||||
|  * E.g. Content from DB with no update time: | ||||
|  * <code> | ||||
|  * $content = getContentFromDB(); | ||||
|  * $cg = new HTTP_ConditionalGet(array( | ||||
|  *     'contentHash' => md5($content) | ||||
|  * )); | ||||
|  * $cg->sendHeaders(); | ||||
|  * if ($cg->cacheIsValid) { | ||||
|  *     exit(); | ||||
|  * } | ||||
|  * echo $content; | ||||
|  * </code> | ||||
|  *  | ||||
|  * E.g. Static content with some static includes: | ||||
|  * <code> | ||||
|  * // before content | ||||
|  * $cg = new HTTP_ConditionalGet(array( | ||||
|  *     'lastUpdateTime' => max( | ||||
|  *         filemtime(__FILE__) | ||||
|  *         ,filemtime('/path/to/header.inc') | ||||
|  *         ,filemtime('/path/to/footer.inc') | ||||
|  *     ) | ||||
|  * )); | ||||
|  * $cg->sendHeaders(); | ||||
|  * if ($cg->cacheIsValid) { | ||||
|  *     exit(); | ||||
|  * } | ||||
|  * </code> | ||||
|  * @package Minify | ||||
|  * @subpackage HTTP | ||||
|  * @author Stephen Clay <steve@mrclay.org> | ||||
|  */ | ||||
| class HTTP_ConditionalGet { | ||||
|  | ||||
|     /** | ||||
|      * Does the client have a valid copy of the requested resource? | ||||
|      *  | ||||
|      * You'll want to check this after instantiating the object. If true, do | ||||
|      * not send content, just call sendHeaders() if you haven't already. | ||||
|      * | ||||
|      * @var bool | ||||
|      */ | ||||
|     public $cacheIsValid = null; | ||||
|  | ||||
|     /** | ||||
|      * @param array $spec options | ||||
|      *  | ||||
|      * 'isPublic': (bool) if false, the Cache-Control header will contain | ||||
|      * "private", allowing only browser caching. (default false) | ||||
|      *  | ||||
|      * 'lastModifiedTime': (int) if given, both ETag AND Last-Modified headers | ||||
|      * will be sent with content. This is recommended. | ||||
|      * | ||||
|      * 'encoding': (string) if set, the header "Vary: Accept-Encoding" will | ||||
|      * always be sent and a truncated version of the encoding will be appended | ||||
|      * to the ETag. E.g. "pub123456;gz". This will also trigger a more lenient  | ||||
|      * checking of the client's If-None-Match header, as the encoding portion of | ||||
|      * the ETag will be stripped before comparison. | ||||
|      *  | ||||
|      * 'contentHash': (string) if given, only the ETag header can be sent with | ||||
|      * content (only HTTP1.1 clients can conditionally GET). The given string  | ||||
|      * should be short with no quote characters and always change when the  | ||||
|      * resource changes (recommend md5()). This is not needed/used if  | ||||
|      * lastModifiedTime is given. | ||||
|      *  | ||||
|      * 'eTag': (string) if given, this will be used as the ETag header rather | ||||
|      * than values based on lastModifiedTime or contentHash. Also the encoding | ||||
|      * string will not be appended to the given value as described above. | ||||
|      *  | ||||
|      * 'invalidate': (bool) if true, the client cache will be considered invalid | ||||
|      * without testing. Effectively this disables conditional GET.  | ||||
|      * (default false) | ||||
|      *  | ||||
|      * 'maxAge': (int) if given, this will set the Cache-Control max-age in  | ||||
|      * seconds, and also set the Expires header to the equivalent GMT date.  | ||||
|      * After the max-age period has passed, the browser will again send a  | ||||
|      * conditional GET to revalidate its cache. | ||||
|      */ | ||||
|     public function __construct($spec) | ||||
|     { | ||||
|         $scope = (isset($spec['isPublic']) && $spec['isPublic']) | ||||
|             ? 'public' | ||||
|             : 'private'; | ||||
|         $maxAge = 0; | ||||
|         // backwards compatibility (can be removed later) | ||||
|         if (isset($spec['setExpires'])  | ||||
|             && is_numeric($spec['setExpires']) | ||||
|             && ! isset($spec['maxAge'])) { | ||||
|             $spec['maxAge'] = $spec['setExpires'] - $_SERVER['REQUEST_TIME']; | ||||
|         } | ||||
|         if (isset($spec['maxAge'])) { | ||||
|             $maxAge = $spec['maxAge']; | ||||
|             $this->_headers['Expires'] = self::gmtDate( | ||||
|                 $_SERVER['REQUEST_TIME'] + $spec['maxAge']  | ||||
|             ); | ||||
|         } | ||||
|         $etagAppend = ''; | ||||
|         if (isset($spec['encoding'])) { | ||||
|             $this->_stripEtag = true; | ||||
|             $this->_headers['Vary'] = 'Accept-Encoding'; | ||||
|             if ('' !== $spec['encoding']) { | ||||
|                 if (0 === strpos($spec['encoding'], 'x-')) { | ||||
|                     $spec['encoding'] = substr($spec['encoding'], 2); | ||||
|                 } | ||||
|                 $etagAppend = ';' . substr($spec['encoding'], 0, 2); | ||||
|             } | ||||
|         } | ||||
|         if (isset($spec['lastModifiedTime'])) { | ||||
|             $this->_setLastModified($spec['lastModifiedTime']); | ||||
|             if (isset($spec['eTag'])) { // Use it | ||||
|                 $this->_setEtag($spec['eTag'], $scope); | ||||
|             } else { // base both headers on time | ||||
|                 $this->_setEtag($spec['lastModifiedTime'] . $etagAppend, $scope); | ||||
|             } | ||||
|         } elseif (isset($spec['eTag'])) { // Use it | ||||
|             $this->_setEtag($spec['eTag'], $scope); | ||||
|         } elseif (isset($spec['contentHash'])) { // Use the hash as the ETag | ||||
|             $this->_setEtag($spec['contentHash'] . $etagAppend, $scope); | ||||
|         } | ||||
|         $privacy = ($scope === 'private') | ||||
|             ? ', private' | ||||
|             : ''; | ||||
|         $this->_headers['Cache-Control'] = "max-age={$maxAge}{$privacy}"; | ||||
|         // invalidate cache if disabled, otherwise check | ||||
|         $this->cacheIsValid = (isset($spec['invalidate']) && $spec['invalidate']) | ||||
|             ? false | ||||
|             : $this->_isCacheValid(); | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Get array of output headers to be sent | ||||
|      *  | ||||
|      * In the case of 304 responses, this array will only contain the response | ||||
|      * code header: array('_responseCode' => 'HTTP/1.0 304 Not Modified') | ||||
|      *  | ||||
|      * Otherwise something like:  | ||||
|      * <code> | ||||
|      * array( | ||||
|      *     'Cache-Control' => 'max-age=0, public' | ||||
|      *     ,'ETag' => '"foobar"' | ||||
|      * ) | ||||
|      * </code> | ||||
|      * | ||||
|      * @return array  | ||||
|      */ | ||||
|     public function getHeaders() | ||||
|     { | ||||
|         return $this->_headers; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Set the Content-Length header in bytes | ||||
|      *  | ||||
|      * With most PHP configs, as long as you don't flush() output, this method | ||||
|      * is not needed and PHP will buffer all output and set Content-Length for  | ||||
|      * you. Otherwise you'll want to call this to let the client know up front. | ||||
|      *  | ||||
|      * @param int $bytes | ||||
|      *  | ||||
|      * @return int copy of input $bytes | ||||
|      */ | ||||
|     public function setContentLength($bytes) | ||||
|     { | ||||
|         return $this->_headers['Content-Length'] = $bytes; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Send headers | ||||
|      *  | ||||
|      * @see getHeaders() | ||||
|      *  | ||||
|      * Note this doesn't "clear" the headers. Calling sendHeaders() will | ||||
|      * call header() again (but probably have not effect) and getHeaders() will | ||||
|      * still return the headers. | ||||
|      * | ||||
|      * @return null | ||||
|      */ | ||||
|     public function sendHeaders() | ||||
|     { | ||||
|         $headers = $this->_headers; | ||||
|         if (array_key_exists('_responseCode', $headers)) { | ||||
|             // FastCGI environments require 3rd arg to header() to be set | ||||
|             list(, $code) = explode(' ', $headers['_responseCode'], 3); | ||||
|             header($headers['_responseCode'], true, $code); | ||||
|             unset($headers['_responseCode']); | ||||
|         } | ||||
|         foreach ($headers as $name => $val) { | ||||
|             header($name . ': ' . $val); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Exit if the client's cache is valid for this resource | ||||
|      * | ||||
|      * This is a convenience method for common use of the class | ||||
|      * | ||||
|      * @param int $lastModifiedTime if given, both ETag AND Last-Modified headers | ||||
|      * will be sent with content. This is recommended. | ||||
|      * | ||||
|      * @param bool $isPublic (default false) if true, the Cache-Control header  | ||||
|      * will contain "public", allowing proxies to cache the content. Otherwise  | ||||
|      * "private" will be sent, allowing only browser caching. | ||||
|      * | ||||
|      * @param array $options (default empty) additional options for constructor | ||||
|      */ | ||||
|     public static function check($lastModifiedTime = null, $isPublic = false, $options = array()) | ||||
|     { | ||||
|         if (null !== $lastModifiedTime) { | ||||
|             $options['lastModifiedTime'] = (int)$lastModifiedTime; | ||||
|         } | ||||
|         $options['isPublic'] = (bool)$isPublic; | ||||
|         $cg = new HTTP_ConditionalGet($options); | ||||
|         $cg->sendHeaders(); | ||||
|         if ($cg->cacheIsValid) { | ||||
|             exit(); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|      | ||||
|     /** | ||||
|      * Get a GMT formatted date for use in HTTP headers | ||||
|      *  | ||||
|      * <code> | ||||
|      * header('Expires: ' . HTTP_ConditionalGet::gmtdate($time)); | ||||
|      * </code>   | ||||
|      * | ||||
|      * @param int $time unix timestamp | ||||
|      *  | ||||
|      * @return string | ||||
|      */ | ||||
|     public static function gmtDate($time) | ||||
|     { | ||||
|         return gmdate('D, d M Y H:i:s \G\M\T', $time); | ||||
|     } | ||||
|      | ||||
|     protected $_headers = array(); | ||||
|     protected $_lmTime = null; | ||||
|     protected $_etag = null; | ||||
|     protected $_stripEtag = false; | ||||
|  | ||||
|     /** | ||||
|      * @param string $hash | ||||
|      * | ||||
|      * @param string $scope | ||||
|      */ | ||||
|     protected function _setEtag($hash, $scope) | ||||
|     { | ||||
|         $this->_etag = '"' . substr($scope, 0, 3) . $hash . '"'; | ||||
|         $this->_headers['ETag'] = $this->_etag; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param int $time | ||||
|      */ | ||||
|     protected function _setLastModified($time) | ||||
|     { | ||||
|         $this->_lmTime = (int)$time; | ||||
|         $this->_headers['Last-Modified'] = self::gmtDate($time); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Determine validity of client cache and queue 304 header if valid | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     protected function _isCacheValid() | ||||
|     { | ||||
|         if (null === $this->_etag) { | ||||
|             // lmTime is copied to ETag, so this condition implies that the | ||||
|             // server sent neither ETag nor Last-Modified, so the client can't  | ||||
|             // possibly has a valid cache. | ||||
|             return false; | ||||
|         } | ||||
|         $isValid = ($this->resourceMatchedEtag() || $this->resourceNotModified()); | ||||
|         if ($isValid) { | ||||
|             $this->_headers['_responseCode'] = 'HTTP/1.0 304 Not Modified'; | ||||
|         } | ||||
|         return $isValid; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return bool | ||||
|      */ | ||||
|     protected function resourceMatchedEtag() | ||||
|     { | ||||
|         if (!isset($_SERVER['HTTP_IF_NONE_MATCH'])) { | ||||
|             return false; | ||||
|         } | ||||
|         $clientEtagList = get_magic_quotes_gpc() | ||||
|             ? stripslashes($_SERVER['HTTP_IF_NONE_MATCH']) | ||||
|             : $_SERVER['HTTP_IF_NONE_MATCH']; | ||||
|         $clientEtags = explode(',', $clientEtagList); | ||||
|          | ||||
|         $compareTo = $this->normalizeEtag($this->_etag); | ||||
|         foreach ($clientEtags as $clientEtag) { | ||||
|             if ($this->normalizeEtag($clientEtag) === $compareTo) { | ||||
|                 // respond with the client's matched ETag, even if it's not what | ||||
|                 // we would've sent by default | ||||
|                 $this->_headers['ETag'] = trim($clientEtag); | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $etag | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     protected function normalizeEtag($etag) { | ||||
|         $etag = trim($etag); | ||||
|         return $this->_stripEtag | ||||
|             ? preg_replace('/;\\w\\w"$/', '"', $etag) | ||||
|             : $etag; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return bool | ||||
|      */ | ||||
|     protected function resourceNotModified() | ||||
|     { | ||||
|         if (!isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { | ||||
|             return false; | ||||
|         } | ||||
|         // strip off IE's extra data (semicolon) | ||||
|         list($ifModifiedSince) = explode(';', $_SERVER['HTTP_IF_MODIFIED_SINCE'], 2); | ||||
|         if (strtotime($ifModifiedSince) >= $this->_lmTime) { | ||||
|             // Apache 2.2's behavior. If there was no ETag match, send the  | ||||
|             // non-encoded version of the ETag value. | ||||
|             $this->_headers['ETag'] = $this->normalizeEtag($this->_etag); | ||||
|             return true; | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										335
									
								
								vendor/mrclay/minify/min/lib/HTTP/Encoder.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										335
									
								
								vendor/mrclay/minify/min/lib/HTTP/Encoder.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,335 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Class HTTP_Encoder   | ||||
|  * @package Minify | ||||
|  * @subpackage HTTP | ||||
|  */ | ||||
|   | ||||
| /** | ||||
|  * Encode and send gzipped/deflated content | ||||
|  * | ||||
|  * The "Vary: Accept-Encoding" header is sent. If the client allows encoding,  | ||||
|  * Content-Encoding and Content-Length are added. | ||||
|  * | ||||
|  * <code> | ||||
|  * // Send a CSS file, compressed if possible | ||||
|  * $he = new HTTP_Encoder(array( | ||||
|  *     'content' => file_get_contents($cssFile) | ||||
|  *     ,'type' => 'text/css' | ||||
|  * )); | ||||
|  * $he->encode(); | ||||
|  * $he->sendAll(); | ||||
|  * </code> | ||||
|  * | ||||
|  * <code> | ||||
|  * // Shortcut to encoding output | ||||
|  * header('Content-Type: text/css'); // needed if not HTML | ||||
|  * HTTP_Encoder::output($css); | ||||
|  * </code> | ||||
|  *  | ||||
|  * <code> | ||||
|  * // Just sniff for the accepted encoding | ||||
|  * $encoding = HTTP_Encoder::getAcceptedEncoding(); | ||||
|  * </code> | ||||
|  * | ||||
|  * For more control over headers, use getHeaders() and getData() and send your | ||||
|  * own output. | ||||
|  *  | ||||
|  * Note: If you don't need header mgmt, use PHP's native gzencode, gzdeflate,  | ||||
|  * and gzcompress functions for gzip, deflate, and compress-encoding | ||||
|  * respectively. | ||||
|  *  | ||||
|  * @package Minify | ||||
|  * @subpackage HTTP | ||||
|  * @author Stephen Clay <steve@mrclay.org> | ||||
|  */ | ||||
| class HTTP_Encoder { | ||||
|  | ||||
|     /** | ||||
|      * Should the encoder allow HTTP encoding to IE6?  | ||||
|      *  | ||||
|      * If you have many IE6 users and the bandwidth savings is worth troubling  | ||||
|      * some of them, set this to true. | ||||
|      *  | ||||
|      * By default, encoding is only offered to IE7+. When this is true, | ||||
|      * getAcceptedEncoding() will return an encoding for IE6 if its user agent | ||||
|      * string contains "SV1". This has been documented in many places as "safe", | ||||
|      * but there seem to be remaining, intermittent encoding bugs in patched  | ||||
|      * IE6 on the wild web. | ||||
|      *  | ||||
|      * @var bool | ||||
|      */ | ||||
|     public static $encodeToIe6 = true; | ||||
|      | ||||
|      | ||||
|     /** | ||||
|      * Default compression level for zlib operations | ||||
|      *  | ||||
|      * This level is used if encode() is not given a $compressionLevel | ||||
|      *  | ||||
|      * @var int | ||||
|      */ | ||||
|     public static $compressionLevel = 6; | ||||
|      | ||||
|  | ||||
|     /** | ||||
|      * Get an HTTP Encoder object | ||||
|      *  | ||||
|      * @param array $spec options | ||||
|      *  | ||||
|      * 'content': (string required) content to be encoded | ||||
|      *  | ||||
|      * 'type': (string) if set, the Content-Type header will have this value. | ||||
|      *  | ||||
|      * 'method: (string) only set this if you are forcing a particular encoding | ||||
|      * method. If not set, the best method will be chosen by getAcceptedEncoding() | ||||
|      * The available methods are 'gzip', 'deflate', 'compress', and '' (no | ||||
|      * encoding) | ||||
|      */ | ||||
|     public function __construct($spec)  | ||||
|     { | ||||
|         $this->_useMbStrlen = (function_exists('mb_strlen') | ||||
|                                && (ini_get('mbstring.func_overload') !== '') | ||||
|                                && ((int)ini_get('mbstring.func_overload') & 2)); | ||||
|         $this->_content = $spec['content']; | ||||
|         $this->_headers['Content-Length'] = $this->_useMbStrlen | ||||
|             ? (string)mb_strlen($this->_content, '8bit') | ||||
|             : (string)strlen($this->_content); | ||||
|         if (isset($spec['type'])) { | ||||
|             $this->_headers['Content-Type'] = $spec['type']; | ||||
|         } | ||||
|         if (isset($spec['method']) | ||||
|             && in_array($spec['method'], array('gzip', 'deflate', 'compress', ''))) | ||||
|         { | ||||
|             $this->_encodeMethod = array($spec['method'], $spec['method']); | ||||
|         } else { | ||||
|             $this->_encodeMethod = self::getAcceptedEncoding(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get content in current form | ||||
|      *  | ||||
|      * Call after encode() for encoded content. | ||||
|      *  | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getContent()  | ||||
|     { | ||||
|         return $this->_content; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Get array of output headers to be sent | ||||
|      *  | ||||
|      * E.g. | ||||
|      * <code> | ||||
|      * array( | ||||
|      *     'Content-Length' => '615' | ||||
|      *     ,'Content-Encoding' => 'x-gzip' | ||||
|      *     ,'Vary' => 'Accept-Encoding' | ||||
|      * ) | ||||
|      * </code> | ||||
|      * | ||||
|      * @return array  | ||||
|      */ | ||||
|     public function getHeaders() | ||||
|     { | ||||
|         return $this->_headers; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Send output headers | ||||
|      *  | ||||
|      * You must call this before headers are sent and it probably cannot be | ||||
|      * used in conjunction with zlib output buffering / mod_gzip. Errors are | ||||
|      * not handled purposefully. | ||||
|      *  | ||||
|      * @see getHeaders() | ||||
|      */ | ||||
|     public function sendHeaders() | ||||
|     { | ||||
|         foreach ($this->_headers as $name => $val) { | ||||
|             header($name . ': ' . $val); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Send output headers and content | ||||
|      *  | ||||
|      * A shortcut for sendHeaders() and echo getContent() | ||||
|      * | ||||
|      * You must call this before headers are sent and it probably cannot be | ||||
|      * used in conjunction with zlib output buffering / mod_gzip. Errors are | ||||
|      * not handled purposefully. | ||||
|      */ | ||||
|     public function sendAll() | ||||
|     { | ||||
|         $this->sendHeaders(); | ||||
|         echo $this->_content; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Determine the client's best encoding method from the HTTP Accept-Encoding  | ||||
|      * header. | ||||
|      *  | ||||
|      * If no Accept-Encoding header is set, or the browser is IE before v6 SP2, | ||||
|      * this will return ('', ''), the "identity" encoding. | ||||
|      *  | ||||
|      * A syntax-aware scan is done of the Accept-Encoding, so the method must | ||||
|      * be non 0. The methods are favored in order of gzip, deflate, then  | ||||
|      * compress. Deflate is always smallest and generally faster, but is  | ||||
|      * rarely sent by servers, so client support could be buggier. | ||||
|      *  | ||||
|      * @param bool $allowCompress allow the older compress encoding | ||||
|      *  | ||||
|      * @param bool $allowDeflate allow the more recent deflate encoding | ||||
|      *  | ||||
|      * @return array two values, 1st is the actual encoding method, 2nd is the | ||||
|      * alias of that method to use in the Content-Encoding header (some browsers | ||||
|      * call gzip "x-gzip" etc.) | ||||
|      */ | ||||
|     public static function getAcceptedEncoding($allowCompress = true, $allowDeflate = true) | ||||
|     { | ||||
|         // @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html | ||||
|          | ||||
|         if (! isset($_SERVER['HTTP_ACCEPT_ENCODING']) | ||||
|             || self::isBuggyIe()) | ||||
|         { | ||||
|             return array('', ''); | ||||
|         } | ||||
|         $ae = $_SERVER['HTTP_ACCEPT_ENCODING']; | ||||
|         // gzip checks (quick) | ||||
|         if (0 === strpos($ae, 'gzip,')             // most browsers | ||||
|             || 0 === strpos($ae, 'deflate, gzip,') // opera | ||||
|         ) { | ||||
|             return array('gzip', 'gzip'); | ||||
|         } | ||||
|         // gzip checks (slow) | ||||
|         if (preg_match( | ||||
|                 '@(?:^|,)\\s*((?:x-)?gzip)\\s*(?:$|,|;\\s*q=(?:0\\.|1))@' | ||||
|                 ,$ae | ||||
|                 ,$m)) { | ||||
|             return array('gzip', $m[1]); | ||||
|         } | ||||
|         if ($allowDeflate) { | ||||
|             // deflate checks     | ||||
|             $aeRev = strrev($ae); | ||||
|             if (0 === strpos($aeRev, 'etalfed ,') // ie, webkit | ||||
|                 || 0 === strpos($aeRev, 'etalfed,') // gecko | ||||
|                 || 0 === strpos($ae, 'deflate,') // opera | ||||
|                 // slow parsing | ||||
|                 || preg_match( | ||||
|                     '@(?:^|,)\\s*deflate\\s*(?:$|,|;\\s*q=(?:0\\.|1))@', $ae)) { | ||||
|                 return array('deflate', 'deflate'); | ||||
|             } | ||||
|         } | ||||
|         if ($allowCompress && preg_match( | ||||
|                 '@(?:^|,)\\s*((?:x-)?compress)\\s*(?:$|,|;\\s*q=(?:0\\.|1))@' | ||||
|                 ,$ae | ||||
|                 ,$m)) { | ||||
|             return array('compress', $m[1]); | ||||
|         } | ||||
|         return array('', ''); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Encode (compress) the content | ||||
|      *  | ||||
|      * If the encode method is '' (none) or compression level is 0, or the 'zlib' | ||||
|      * extension isn't loaded, we return false. | ||||
|      *  | ||||
|      * Then the appropriate gz_* function is called to compress the content. If | ||||
|      * this fails, false is returned. | ||||
|      *  | ||||
|      * The header "Vary: Accept-Encoding" is added. If encoding is successful,  | ||||
|      * the Content-Length header is updated, and Content-Encoding is also added. | ||||
|      *  | ||||
|      * @param int $compressionLevel given to zlib functions. If not given, the | ||||
|      * class default will be used. | ||||
|      *  | ||||
|      * @return bool success true if the content was actually compressed | ||||
|      */ | ||||
|     public function encode($compressionLevel = null) | ||||
|     { | ||||
|         if (! self::isBuggyIe()) { | ||||
|             $this->_headers['Vary'] = 'Accept-Encoding'; | ||||
|         } | ||||
|         if (null === $compressionLevel) { | ||||
|             $compressionLevel = self::$compressionLevel; | ||||
|         } | ||||
|         if ('' === $this->_encodeMethod[0] | ||||
|             || ($compressionLevel == 0) | ||||
|             || !extension_loaded('zlib')) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|         if ($this->_encodeMethod[0] === 'deflate') { | ||||
|             $encoded = gzdeflate($this->_content, $compressionLevel); | ||||
|         } elseif ($this->_encodeMethod[0] === 'gzip') { | ||||
|             $encoded = gzencode($this->_content, $compressionLevel); | ||||
|         } else { | ||||
|             $encoded = gzcompress($this->_content, $compressionLevel); | ||||
|         } | ||||
|         if (false === $encoded) { | ||||
|             return false; | ||||
|         } | ||||
|         $this->_headers['Content-Length'] = $this->_useMbStrlen | ||||
|             ? (string)mb_strlen($encoded, '8bit') | ||||
|             : (string)strlen($encoded); | ||||
|         $this->_headers['Content-Encoding'] = $this->_encodeMethod[1]; | ||||
|         $this->_content = $encoded; | ||||
|         return true; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Encode and send appropriate headers and content | ||||
|      * | ||||
|      * This is a convenience method for common use of the class | ||||
|      *  | ||||
|      * @param string $content | ||||
|      *  | ||||
|      * @param int $compressionLevel given to zlib functions. If not given, the | ||||
|      * class default will be used. | ||||
|      *  | ||||
|      * @return bool success true if the content was actually compressed | ||||
|      */ | ||||
|     public static function output($content, $compressionLevel = null) | ||||
|     { | ||||
|         if (null === $compressionLevel) { | ||||
|             $compressionLevel = self::$compressionLevel; | ||||
|         } | ||||
|         $he = new HTTP_Encoder(array('content' => $content)); | ||||
|         $ret = $he->encode($compressionLevel); | ||||
|         $he->sendAll(); | ||||
|         return $ret; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Is the browser an IE version earlier than 6 SP2? | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public static function isBuggyIe() | ||||
|     { | ||||
|         if (empty($_SERVER['HTTP_USER_AGENT'])) { | ||||
|             return false; | ||||
|         } | ||||
|         $ua = $_SERVER['HTTP_USER_AGENT']; | ||||
|         // quick escape for non-IEs | ||||
|         if (0 !== strpos($ua, 'Mozilla/4.0 (compatible; MSIE ') | ||||
|             || false !== strpos($ua, 'Opera')) { | ||||
|             return false; | ||||
|         } | ||||
|         // no regex = faaast | ||||
|         $version = (float)substr($ua, 30); | ||||
|         return self::$encodeToIe6 | ||||
|             ? ($version < 6 || ($version == 6 && false === strpos($ua, 'SV1'))) | ||||
|             : ($version < 7); | ||||
|     } | ||||
|      | ||||
|     protected $_content = ''; | ||||
|     protected $_headers = array(); | ||||
|     protected $_encodeMethod = array('', ''); | ||||
|     protected $_useMbStrlen = false; | ||||
| } | ||||
							
								
								
									
										449
									
								
								vendor/mrclay/minify/min/lib/JSMin.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										449
									
								
								vendor/mrclay/minify/min/lib/JSMin.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,449 @@ | ||||
| <?php | ||||
| /** | ||||
|  * JSMin.php - modified PHP implementation of Douglas Crockford's JSMin. | ||||
|  * | ||||
|  * <code> | ||||
|  * $minifiedJs = JSMin::minify($js); | ||||
|  * </code> | ||||
|  * | ||||
|  * This is a modified port of jsmin.c. Improvements: | ||||
|  * | ||||
|  * Does not choke on some regexp literals containing quote characters. E.g. /'/ | ||||
|  * | ||||
|  * Spaces are preserved after some add/sub operators, so they are not mistakenly | ||||
|  * converted to post-inc/dec. E.g. a + ++b -> a+ ++b | ||||
|  * | ||||
|  * Preserves multi-line comments that begin with /*! | ||||
|  * | ||||
|  * PHP 5 or higher is required. | ||||
|  * | ||||
|  * Permission is hereby granted to use this version of the library under the | ||||
|  * same terms as jsmin.c, which has the following license: | ||||
|  * | ||||
|  * -- | ||||
|  * Copyright (c) 2002 Douglas Crockford  (www.crockford.com) | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy of | ||||
|  * this software and associated documentation files (the "Software"), to deal in | ||||
|  * the Software without restriction, including without limitation the rights to | ||||
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies | ||||
|  * of the Software, and to permit persons to whom the Software is furnished to do | ||||
|  * so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all | ||||
|  * copies or substantial portions of the Software. | ||||
|  * | ||||
|  * The Software shall be used for Good, not Evil. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|  * SOFTWARE. | ||||
|  * -- | ||||
|  * | ||||
|  * @package JSMin | ||||
|  * @author Ryan Grove <ryan@wonko.com> (PHP port) | ||||
|  * @author Steve Clay <steve@mrclay.org> (modifications + cleanup) | ||||
|  * @author Andrea Giammarchi <http://www.3site.eu> (spaceBeforeRegExp) | ||||
|  * @copyright 2002 Douglas Crockford <douglas@crockford.com> (jsmin.c) | ||||
|  * @copyright 2008 Ryan Grove <ryan@wonko.com> (PHP port) | ||||
|  * @license http://opensource.org/licenses/mit-license.php MIT License | ||||
|  * @link http://code.google.com/p/jsmin-php/ | ||||
|  */ | ||||
|  | ||||
| class JSMin { | ||||
|     const ORD_LF            = 10; | ||||
|     const ORD_SPACE         = 32; | ||||
|     const ACTION_KEEP_A     = 1; | ||||
|     const ACTION_DELETE_A   = 2; | ||||
|     const ACTION_DELETE_A_B = 3; | ||||
|  | ||||
|     protected $a           = "\n"; | ||||
|     protected $b           = ''; | ||||
|     protected $input       = ''; | ||||
|     protected $inputIndex  = 0; | ||||
|     protected $inputLength = 0; | ||||
|     protected $lookAhead   = null; | ||||
|     protected $output      = ''; | ||||
|     protected $lastByteOut  = ''; | ||||
|     protected $keptComment = ''; | ||||
|  | ||||
|     /** | ||||
|      * Minify Javascript. | ||||
|      * | ||||
|      * @param string $js Javascript to be minified | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public static function minify($js) | ||||
|     { | ||||
|         $jsmin = new JSMin($js); | ||||
|         return $jsmin->min(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $input | ||||
|      */ | ||||
|     public function __construct($input) | ||||
|     { | ||||
|         $this->input = $input; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Perform minification, return result | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public function min() | ||||
|     { | ||||
|         if ($this->output !== '') { // min already run | ||||
|             return $this->output; | ||||
|         } | ||||
|  | ||||
|         $mbIntEnc = null; | ||||
|         if (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) { | ||||
|             $mbIntEnc = mb_internal_encoding(); | ||||
|             mb_internal_encoding('8bit'); | ||||
|         } | ||||
|         $this->input = str_replace("\r\n", "\n", $this->input); | ||||
|         $this->inputLength = strlen($this->input); | ||||
|  | ||||
|         $this->action(self::ACTION_DELETE_A_B); | ||||
|  | ||||
|         while ($this->a !== null) { | ||||
|             // determine next command | ||||
|             $command = self::ACTION_KEEP_A; // default | ||||
|             if ($this->a === ' ') { | ||||
|                 if (($this->lastByteOut === '+' || $this->lastByteOut === '-') | ||||
|                         && ($this->b === $this->lastByteOut)) { | ||||
|                     // Don't delete this space. If we do, the addition/subtraction | ||||
|                     // could be parsed as a post-increment | ||||
|                 } elseif (! $this->isAlphaNum($this->b)) { | ||||
|                     $command = self::ACTION_DELETE_A; | ||||
|                 } | ||||
|             } elseif ($this->a === "\n") { | ||||
|                 if ($this->b === ' ') { | ||||
|                     $command = self::ACTION_DELETE_A_B; | ||||
|  | ||||
|                     // in case of mbstring.func_overload & 2, must check for null b, | ||||
|                     // otherwise mb_strpos will give WARNING | ||||
|                 } elseif ($this->b === null | ||||
|                           || (false === strpos('{[(+-!~', $this->b) | ||||
|                               && ! $this->isAlphaNum($this->b))) { | ||||
|                     $command = self::ACTION_DELETE_A; | ||||
|                 } | ||||
|             } elseif (! $this->isAlphaNum($this->a)) { | ||||
|                 if ($this->b === ' ' | ||||
|                     || ($this->b === "\n" | ||||
|                         && (false === strpos('}])+-"\'', $this->a)))) { | ||||
|                     $command = self::ACTION_DELETE_A_B; | ||||
|                 } | ||||
|             } | ||||
|             $this->action($command); | ||||
|         } | ||||
|         $this->output = trim($this->output); | ||||
|  | ||||
|         if ($mbIntEnc !== null) { | ||||
|             mb_internal_encoding($mbIntEnc); | ||||
|         } | ||||
|         return $this->output; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * ACTION_KEEP_A = Output A. Copy B to A. Get the next B. | ||||
|      * ACTION_DELETE_A = Copy B to A. Get the next B. | ||||
|      * ACTION_DELETE_A_B = Get the next B. | ||||
|      * | ||||
|      * @param int $command | ||||
|      * @throws JSMin_UnterminatedRegExpException|JSMin_UnterminatedStringException | ||||
|      */ | ||||
|     protected function action($command) | ||||
|     { | ||||
|         // make sure we don't compress "a + ++b" to "a+++b", etc. | ||||
|         if ($command === self::ACTION_DELETE_A_B | ||||
|             && $this->b === ' ' | ||||
|             && ($this->a === '+' || $this->a === '-')) { | ||||
|             // Note: we're at an addition/substraction operator; the inputIndex | ||||
|             // will certainly be a valid index | ||||
|             if ($this->input[$this->inputIndex] === $this->a) { | ||||
|                 // This is "+ +" or "- -". Don't delete the space. | ||||
|                 $command = self::ACTION_KEEP_A; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         switch ($command) { | ||||
|             case self::ACTION_KEEP_A: // 1 | ||||
|                 $this->output .= $this->a; | ||||
|  | ||||
|                 if ($this->keptComment) { | ||||
|                     $this->output = rtrim($this->output, "\n"); | ||||
|                     $this->output .= $this->keptComment; | ||||
|                     $this->keptComment = ''; | ||||
|                 } | ||||
|  | ||||
|                 $this->lastByteOut = $this->a; | ||||
|  | ||||
|                 // fallthrough intentional | ||||
|             case self::ACTION_DELETE_A: // 2 | ||||
|                 $this->a = $this->b; | ||||
|                 if ($this->a === "'" || $this->a === '"') { // string literal | ||||
|                     $str = $this->a; // in case needed for exception | ||||
|                     for(;;) { | ||||
|                         $this->output .= $this->a; | ||||
|                         $this->lastByteOut = $this->a; | ||||
|  | ||||
|                         $this->a = $this->get(); | ||||
|                         if ($this->a === $this->b) { // end quote | ||||
|                             break; | ||||
|                         } | ||||
|                         if ($this->isEOF($this->a)) { | ||||
|                             $byte = $this->inputIndex - 1; | ||||
|                             throw new JSMin_UnterminatedStringException( | ||||
|                                 "JSMin: Unterminated String at byte {$byte}: {$str}"); | ||||
|                         } | ||||
|                         $str .= $this->a; | ||||
|                         if ($this->a === '\\') { | ||||
|                             $this->output .= $this->a; | ||||
|                             $this->lastByteOut = $this->a; | ||||
|  | ||||
|                             $this->a       = $this->get(); | ||||
|                             $str .= $this->a; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 // fallthrough intentional | ||||
|             case self::ACTION_DELETE_A_B: // 3 | ||||
|                 $this->b = $this->next(); | ||||
|                 if ($this->b === '/' && $this->isRegexpLiteral()) { | ||||
|                     $this->output .= $this->a . $this->b; | ||||
|                     $pattern = '/'; // keep entire pattern in case we need to report it in the exception | ||||
|                     for(;;) { | ||||
|                         $this->a = $this->get(); | ||||
|                         $pattern .= $this->a; | ||||
|                         if ($this->a === '[') { | ||||
|                             for(;;) { | ||||
|                                 $this->output .= $this->a; | ||||
|                                 $this->a = $this->get(); | ||||
|                                 $pattern .= $this->a; | ||||
|                                 if ($this->a === ']') { | ||||
|                                     break; | ||||
|                                 } | ||||
|                                 if ($this->a === '\\') { | ||||
|                                     $this->output .= $this->a; | ||||
|                                     $this->a = $this->get(); | ||||
|                                     $pattern .= $this->a; | ||||
|                                 } | ||||
|                                 if ($this->isEOF($this->a)) { | ||||
|                                     throw new JSMin_UnterminatedRegExpException( | ||||
|                                         "JSMin: Unterminated set in RegExp at byte " | ||||
|                                             . $this->inputIndex .": {$pattern}"); | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|  | ||||
|                         if ($this->a === '/') { // end pattern | ||||
|                             break; // while (true) | ||||
|                         } elseif ($this->a === '\\') { | ||||
|                             $this->output .= $this->a; | ||||
|                             $this->a = $this->get(); | ||||
|                             $pattern .= $this->a; | ||||
|                         } elseif ($this->isEOF($this->a)) { | ||||
|                             $byte = $this->inputIndex - 1; | ||||
|                             throw new JSMin_UnterminatedRegExpException( | ||||
|                                 "JSMin: Unterminated RegExp at byte {$byte}: {$pattern}"); | ||||
|                         } | ||||
|                         $this->output .= $this->a; | ||||
|                         $this->lastByteOut = $this->a; | ||||
|                     } | ||||
|                     $this->b = $this->next(); | ||||
|                 } | ||||
|             // end case ACTION_DELETE_A_B | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return bool | ||||
|      */ | ||||
|     protected function isRegexpLiteral() | ||||
|     { | ||||
|         if (false !== strpos("(,=:[!&|?+-~*{;", $this->a)) { | ||||
|             // we obviously aren't dividing | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
| 		// we have to check for a preceding keyword, and we don't need to pattern | ||||
| 		// match over the whole output. | ||||
| 		$recentOutput = substr($this->output, -10); | ||||
|  | ||||
| 		// check if return/typeof directly precede a pattern without a space | ||||
| 		foreach (array('return', 'typeof') as $keyword) { | ||||
|             if ($this->a !== substr($keyword, -1)) { | ||||
|                 // certainly wasn't keyword | ||||
|                 continue; | ||||
|             } | ||||
|             if (preg_match("~(^|[\\s\\S])" . substr($keyword, 0, -1) . "$~", $recentOutput, $m)) { | ||||
|                 if ($m[1] === '' || !$this->isAlphaNum($m[1])) { | ||||
|                     return true; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
| 		// check all keywords | ||||
| 		if ($this->a === ' ' || $this->a === "\n") { | ||||
| 			if (preg_match('~(^|[\\s\\S])(?:case|else|in|return|typeof)$~', $recentOutput, $m)) { | ||||
| 				if ($m[1] === '' || !$this->isAlphaNum($m[1])) { | ||||
| 					return true; | ||||
| 				} | ||||
| 			} | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Return the next character from stdin. Watch out for lookahead. If the character is a control character, | ||||
|      * translate it to a space or linefeed. | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     protected function get() | ||||
|     { | ||||
|         $c = $this->lookAhead; | ||||
|         $this->lookAhead = null; | ||||
|         if ($c === null) { | ||||
|             // getc(stdin) | ||||
|             if ($this->inputIndex < $this->inputLength) { | ||||
|                 $c = $this->input[$this->inputIndex]; | ||||
|                 $this->inputIndex += 1; | ||||
|             } else { | ||||
|                 $c = null; | ||||
|             } | ||||
|         } | ||||
|         if (ord($c) >= self::ORD_SPACE || $c === "\n" || $c === null) { | ||||
|             return $c; | ||||
|         } | ||||
|         if ($c === "\r") { | ||||
|             return "\n"; | ||||
|         } | ||||
|         return ' '; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Does $a indicate end of input? | ||||
|      * | ||||
|      * @param string $a | ||||
|      * @return bool | ||||
|      */ | ||||
|     protected function isEOF($a) | ||||
|     { | ||||
|         return ord($a) <= self::ORD_LF; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get next char (without getting it). If is ctrl character, translate to a space or newline. | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     protected function peek() | ||||
|     { | ||||
|         $this->lookAhead = $this->get(); | ||||
|         return $this->lookAhead; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Return true if the character is a letter, digit, underscore, dollar sign, or non-ASCII character. | ||||
|      * | ||||
|      * @param string $c | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     protected function isAlphaNum($c) | ||||
|     { | ||||
|         return (preg_match('/^[a-z0-9A-Z_\\$\\\\]$/', $c) || ord($c) > 126); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Consume a single line comment from input (possibly retaining it) | ||||
|      */ | ||||
|     protected function consumeSingleLineComment() | ||||
|     { | ||||
|         $comment = ''; | ||||
|         while (true) { | ||||
|             $get = $this->get(); | ||||
|             $comment .= $get; | ||||
|             if (ord($get) <= self::ORD_LF) { // end of line reached | ||||
|                 // if IE conditional comment | ||||
|                 if (preg_match('/^\\/@(?:cc_on|if|elif|else|end)\\b/', $comment)) { | ||||
|                     $this->keptComment .= "/{$comment}"; | ||||
|                 } | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Consume a multiple line comment from input (possibly retaining it) | ||||
|      * | ||||
|      * @throws JSMin_UnterminatedCommentException | ||||
|      */ | ||||
|     protected function consumeMultipleLineComment() | ||||
|     { | ||||
|         $this->get(); | ||||
|         $comment = ''; | ||||
|         for(;;) { | ||||
|             $get = $this->get(); | ||||
|             if ($get === '*') { | ||||
|                 if ($this->peek() === '/') { // end of comment reached | ||||
|                     $this->get(); | ||||
|                     if (0 === strpos($comment, '!')) { | ||||
|                         // preserved by YUI Compressor | ||||
|                         if (!$this->keptComment) { | ||||
|                             // don't prepend a newline if two comments right after one another | ||||
|                             $this->keptComment = "\n"; | ||||
|                         } | ||||
|                         $this->keptComment .= "/*!" . substr($comment, 1) . "*/\n"; | ||||
|                     } else if (preg_match('/^@(?:cc_on|if|elif|else|end)\\b/', $comment)) { | ||||
|                         // IE conditional | ||||
|                         $this->keptComment .= "/*{$comment}*/"; | ||||
|                     } | ||||
|                     return; | ||||
|                 } | ||||
|             } elseif ($get === null) { | ||||
|                 throw new JSMin_UnterminatedCommentException( | ||||
|                     "JSMin: Unterminated comment at byte {$this->inputIndex}: /*{$comment}"); | ||||
|             } | ||||
|             $comment .= $get; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the next character, skipping over comments. Some comments may be preserved. | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     protected function next() | ||||
|     { | ||||
|         $get = $this->get(); | ||||
|         if ($get === '/') { | ||||
|             switch ($this->peek()) { | ||||
|                 case '/': | ||||
|                     $this->consumeSingleLineComment(); | ||||
|                     $get = "\n"; | ||||
|                     break; | ||||
|                 case '*': | ||||
|                     $this->consumeMultipleLineComment(); | ||||
|                     $get = ' '; | ||||
|                     break; | ||||
|             } | ||||
|         } | ||||
|         return $get; | ||||
|     } | ||||
| } | ||||
|  | ||||
| class JSMin_UnterminatedStringException extends Exception {} | ||||
| class JSMin_UnterminatedCommentException extends Exception {} | ||||
| class JSMin_UnterminatedRegExpException extends Exception {} | ||||
							
								
								
									
										2086
									
								
								vendor/mrclay/minify/min/lib/JSMinPlus.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2086
									
								
								vendor/mrclay/minify/min/lib/JSMinPlus.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										608
									
								
								vendor/mrclay/minify/min/lib/Minify.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										608
									
								
								vendor/mrclay/minify/min/lib/Minify.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,608 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Class Minify   | ||||
|  * @package Minify | ||||
|  */ | ||||
|   | ||||
| /** | ||||
|  * Minify - Combines, minifies, and caches JavaScript and CSS files on demand. | ||||
|  * | ||||
|  * See README for usage instructions (for now). | ||||
|  * | ||||
|  * This library was inspired by {@link mailto:flashkot@mail.ru jscsscomp by Maxim Martynyuk} | ||||
|  * and by the article {@link http://www.hunlock.com/blogs/Supercharged_Javascript "Supercharged JavaScript" by Patrick Hunlock}. | ||||
|  * | ||||
|  * Requires PHP 5.1.0. | ||||
|  * Tested on PHP 5.1.6. | ||||
|  * | ||||
|  * @package Minify | ||||
|  * @author Ryan Grove <ryan@wonko.com> | ||||
|  * @author Stephen Clay <steve@mrclay.org> | ||||
|  * @copyright 2008 Ryan Grove, Stephen Clay. All rights reserved. | ||||
|  * @license http://opensource.org/licenses/bsd-license.php  New BSD License | ||||
|  * @link http://code.google.com/p/minify/ | ||||
|  */ | ||||
| class Minify { | ||||
|      | ||||
|     const VERSION = '2.2.0'; | ||||
|     const TYPE_CSS = 'text/css'; | ||||
|     const TYPE_HTML = 'text/html'; | ||||
|     // there is some debate over the ideal JS Content-Type, but this is the | ||||
|     // Apache default and what Yahoo! uses.. | ||||
|     const TYPE_JS = 'application/x-javascript'; | ||||
|     const URL_DEBUG = 'http://code.google.com/p/minify/wiki/Debugging'; | ||||
|      | ||||
|     /** | ||||
|      * How many hours behind are the file modification times of uploaded files? | ||||
|      *  | ||||
|      * If you upload files from Windows to a non-Windows server, Windows may report | ||||
|      * incorrect mtimes for the files. Immediately after modifying and uploading a  | ||||
|      * file, use the touch command to update the mtime on the server. If the mtime  | ||||
|      * jumps ahead by a number of hours, set this variable to that number. If the mtime  | ||||
|      * moves back, this should not be needed. | ||||
|      * | ||||
|      * @var int $uploaderHoursBehind | ||||
|      */ | ||||
|     public static $uploaderHoursBehind = 0; | ||||
|      | ||||
|     /** | ||||
|      * If this string is not empty AND the serve() option 'bubbleCssImports' is | ||||
|      * NOT set, then serve() will check CSS files for @import declarations that | ||||
|      * appear too late in the combined stylesheet. If found, serve() will prepend | ||||
|      * the output with this warning. | ||||
|      * | ||||
|      * @var string $importWarning | ||||
|      */ | ||||
|     public static $importWarning = "/* See http://code.google.com/p/minify/wiki/CommonProblems#@imports_can_appear_in_invalid_locations_in_combined_CSS_files */\n"; | ||||
|  | ||||
|     /** | ||||
|      * Has the DOCUMENT_ROOT been set in user code? | ||||
|      *  | ||||
|      * @var bool | ||||
|      */ | ||||
|     public static $isDocRootSet = false; | ||||
|  | ||||
|     /** | ||||
|      * Specify a cache object (with identical interface as Minify_Cache_File) or | ||||
|      * a path to use with Minify_Cache_File. | ||||
|      *  | ||||
|      * If not called, Minify will not use a cache and, for each 200 response, will  | ||||
|      * need to recombine files, minify and encode the output. | ||||
|      * | ||||
|      * @param mixed $cache object with identical interface as Minify_Cache_File or | ||||
|      * a directory path, or null to disable caching. (default = '') | ||||
|      *  | ||||
|      * @param bool $fileLocking (default = true) This only applies if the first | ||||
|      * parameter is a string. | ||||
|      * | ||||
|      * @return null | ||||
|      */ | ||||
|     public static function setCache($cache = '', $fileLocking = true) | ||||
|     { | ||||
|         if (is_string($cache)) { | ||||
|             self::$_cache = new Minify_Cache_File($cache, $fileLocking); | ||||
|         } else { | ||||
|             self::$_cache = $cache; | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Serve a request for a minified file.  | ||||
|      *  | ||||
|      * Here are the available options and defaults in the base controller: | ||||
|      *  | ||||
|      * 'isPublic' : send "public" instead of "private" in Cache-Control  | ||||
|      * headers, allowing shared caches to cache the output. (default true) | ||||
|      *  | ||||
|      * 'quiet' : set to true to have serve() return an array rather than sending | ||||
|      * any headers/output (default false) | ||||
|      *  | ||||
|      * 'encodeOutput' : set to false to disable content encoding, and not send | ||||
|      * the Vary header (default true) | ||||
|      *  | ||||
|      * 'encodeMethod' : generally you should let this be determined by  | ||||
|      * HTTP_Encoder (leave null), but you can force a particular encoding | ||||
|      * to be returned, by setting this to 'gzip' or '' (no encoding) | ||||
|      *  | ||||
|      * 'encodeLevel' : level of encoding compression (0 to 9, default 9) | ||||
|      *  | ||||
|      * 'contentTypeCharset' : appended to the Content-Type header sent. Set to a falsey | ||||
|      * value to remove. (default 'utf-8')   | ||||
|      *  | ||||
|      * 'maxAge' : set this to the number of seconds the client should use its cache | ||||
|      * before revalidating with the server. This sets Cache-Control: max-age and the | ||||
|      * Expires header. Unlike the old 'setExpires' setting, this setting will NOT | ||||
|      * prevent conditional GETs. Note this has nothing to do with server-side caching. | ||||
|      *  | ||||
|      * 'rewriteCssUris' : If true, serve() will automatically set the 'currentDir' | ||||
|      * minifier option to enable URI rewriting in CSS files (default true) | ||||
|      *  | ||||
|      * 'bubbleCssImports' : If true, all @import declarations in combined CSS | ||||
|      * files will be move to the top. Note this may alter effective CSS values | ||||
|      * due to a change in order. (default false) | ||||
|      *  | ||||
|      * 'debug' : set to true to minify all sources with the 'Lines' controller, which | ||||
|      * eases the debugging of combined files. This also prevents 304 responses. | ||||
|      * @see Minify_Lines::minify() | ||||
|      *  | ||||
|      * 'minifiers' : to override Minify's default choice of minifier function for  | ||||
|      * a particular content-type, specify your callback under the key of the  | ||||
|      * content-type: | ||||
|      * <code> | ||||
|      * // call customCssMinifier($css) for all CSS minification | ||||
|      * $options['minifiers'][Minify::TYPE_CSS] = 'customCssMinifier'; | ||||
|      *  | ||||
|      * // don't minify Javascript at all | ||||
|      * $options['minifiers'][Minify::TYPE_JS] = ''; | ||||
|      * </code> | ||||
|      *  | ||||
|      * 'minifierOptions' : to send options to the minifier function, specify your options | ||||
|      * under the key of the content-type. E.g. To send the CSS minifier an option:  | ||||
|      * <code> | ||||
|      * // give CSS minifier array('optionName' => 'optionValue') as 2nd argument  | ||||
|      * $options['minifierOptions'][Minify::TYPE_CSS]['optionName'] = 'optionValue'; | ||||
|      * </code> | ||||
|      *  | ||||
|      * 'contentType' : (optional) this is only needed if your file extension is not  | ||||
|      * js/css/html. The given content-type will be sent regardless of source file | ||||
|      * extension, so this should not be used in a Groups config with other | ||||
|      * Javascript/CSS files. | ||||
|      *  | ||||
|      * Any controller options are documented in that controller's setupSources() method. | ||||
|      *  | ||||
|      * @param mixed $controller instance of subclass of Minify_Controller_Base or string | ||||
|      * name of controller. E.g. 'Files' | ||||
|      *  | ||||
|      * @param array $options controller/serve options | ||||
|      *  | ||||
|      * @return null|array if the 'quiet' option is set to true, an array | ||||
|      * with keys "success" (bool), "statusCode" (int), "content" (string), and | ||||
|      * "headers" (array). | ||||
|      * | ||||
|      * @throws Exception | ||||
|      */ | ||||
|     public static function serve($controller, $options = array()) | ||||
|     { | ||||
|         if (! self::$isDocRootSet && 0 === stripos(PHP_OS, 'win')) { | ||||
|             self::setDocRoot(); | ||||
|         } | ||||
|  | ||||
|         if (is_string($controller)) { | ||||
|             // make $controller into object | ||||
|             $class = 'Minify_Controller_' . $controller; | ||||
|             $controller = new $class(); | ||||
|             /* @var Minify_Controller_Base $controller */ | ||||
|         } | ||||
|          | ||||
|         // set up controller sources and mix remaining options with | ||||
|         // controller defaults | ||||
|         $options = $controller->setupSources($options); | ||||
|         $options = $controller->analyzeSources($options); | ||||
|         self::$_options = $controller->mixInDefaultOptions($options); | ||||
|          | ||||
|         // check request validity | ||||
|         if (! $controller->sources) { | ||||
|             // invalid request! | ||||
|             if (! self::$_options['quiet']) { | ||||
|                 self::_errorExit(self::$_options['badRequestHeader'], self::URL_DEBUG); | ||||
|             } else { | ||||
|                 list(,$statusCode) = explode(' ', self::$_options['badRequestHeader']); | ||||
|                 return array( | ||||
|                     'success' => false | ||||
|                     ,'statusCode' => (int)$statusCode | ||||
|                     ,'content' => '' | ||||
|                     ,'headers' => array() | ||||
|                 ); | ||||
|             } | ||||
|         } | ||||
|          | ||||
|         self::$_controller = $controller; | ||||
|          | ||||
|         if (self::$_options['debug']) { | ||||
|             self::_setupDebug($controller->sources); | ||||
|             self::$_options['maxAge'] = 0; | ||||
|         } | ||||
|          | ||||
|         // determine encoding | ||||
|         if (self::$_options['encodeOutput']) { | ||||
|             $sendVary = true; | ||||
|             if (self::$_options['encodeMethod'] !== null) { | ||||
|                 // controller specifically requested this | ||||
|                 $contentEncoding = self::$_options['encodeMethod']; | ||||
|             } else { | ||||
|                 // sniff request header | ||||
|                 // depending on what the client accepts, $contentEncoding may be | ||||
|                 // 'x-gzip' while our internal encodeMethod is 'gzip'. Calling | ||||
|                 // getAcceptedEncoding(false, false) leaves out compress and deflate as options. | ||||
|                 list(self::$_options['encodeMethod'], $contentEncoding) = HTTP_Encoder::getAcceptedEncoding(false, false); | ||||
|                 $sendVary = ! HTTP_Encoder::isBuggyIe(); | ||||
|             } | ||||
|         } else { | ||||
|             self::$_options['encodeMethod'] = ''; // identity (no encoding) | ||||
|         } | ||||
|          | ||||
|         // check client cache | ||||
|         $cgOptions = array( | ||||
|             'lastModifiedTime' => self::$_options['lastModifiedTime'] | ||||
|             ,'isPublic' => self::$_options['isPublic'] | ||||
|             ,'encoding' => self::$_options['encodeMethod'] | ||||
|         ); | ||||
|         if (self::$_options['maxAge'] > 0) { | ||||
|             $cgOptions['maxAge'] = self::$_options['maxAge']; | ||||
|         } elseif (self::$_options['debug']) { | ||||
|             $cgOptions['invalidate'] = true; | ||||
|         } | ||||
|         $cg = new HTTP_ConditionalGet($cgOptions); | ||||
|         if ($cg->cacheIsValid) { | ||||
|             // client's cache is valid | ||||
|             if (! self::$_options['quiet']) { | ||||
|                 $cg->sendHeaders(); | ||||
|                 return; | ||||
|             } else { | ||||
|                 return array( | ||||
|                     'success' => true | ||||
|                     ,'statusCode' => 304 | ||||
|                     ,'content' => '' | ||||
|                     ,'headers' => $cg->getHeaders() | ||||
|                 ); | ||||
|             } | ||||
|         } else { | ||||
|             // client will need output | ||||
|             $headers = $cg->getHeaders(); | ||||
|             unset($cg); | ||||
|         } | ||||
|          | ||||
|         if (self::$_options['contentType'] === self::TYPE_CSS | ||||
|             && self::$_options['rewriteCssUris']) { | ||||
|             foreach($controller->sources as $key => $source) { | ||||
|                 if ($source->filepath  | ||||
|                     && !isset($source->minifyOptions['currentDir']) | ||||
|                     && !isset($source->minifyOptions['prependRelativePath']) | ||||
|                 ) { | ||||
|                     $source->minifyOptions['currentDir'] = dirname($source->filepath); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|          | ||||
|         // check server cache | ||||
|         if (null !== self::$_cache && ! self::$_options['debug']) { | ||||
|             // using cache | ||||
|             // the goal is to use only the cache methods to sniff the length and  | ||||
|             // output the content, as they do not require ever loading the file into | ||||
|             // memory. | ||||
|             $cacheId = self::_getCacheId(); | ||||
|             $fullCacheId = (self::$_options['encodeMethod']) | ||||
|                 ? $cacheId . '.gz' | ||||
|                 : $cacheId; | ||||
|             // check cache for valid entry | ||||
|             $cacheIsReady = self::$_cache->isValid($fullCacheId, self::$_options['lastModifiedTime']);  | ||||
|             if ($cacheIsReady) { | ||||
|                 $cacheContentLength = self::$_cache->getSize($fullCacheId);     | ||||
|             } else { | ||||
|                 // generate & cache content | ||||
|                 try { | ||||
|                     $content = self::_combineMinify(); | ||||
|                 } catch (Exception $e) { | ||||
|                     self::$_controller->log($e->getMessage()); | ||||
|                     if (! self::$_options['quiet']) { | ||||
|                         self::_errorExit(self::$_options['errorHeader'], self::URL_DEBUG); | ||||
|                     } | ||||
|                     throw $e; | ||||
|                 } | ||||
|                 self::$_cache->store($cacheId, $content); | ||||
|                 if (function_exists('gzencode') && self::$_options['encodeMethod']) { | ||||
|                     self::$_cache->store($cacheId . '.gz', gzencode($content, self::$_options['encodeLevel'])); | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|             // no cache | ||||
|             $cacheIsReady = false; | ||||
|             try { | ||||
|                 $content = self::_combineMinify(); | ||||
|             } catch (Exception $e) { | ||||
|                 self::$_controller->log($e->getMessage()); | ||||
|                 if (! self::$_options['quiet']) { | ||||
|                     self::_errorExit(self::$_options['errorHeader'], self::URL_DEBUG); | ||||
|                 } | ||||
|                 throw $e; | ||||
|             } | ||||
|         } | ||||
|         if (! $cacheIsReady && self::$_options['encodeMethod']) { | ||||
|             // still need to encode | ||||
|             $content = gzencode($content, self::$_options['encodeLevel']); | ||||
|         } | ||||
|          | ||||
|         // add headers | ||||
|         $headers['Content-Length'] = $cacheIsReady | ||||
|             ? $cacheContentLength | ||||
|             : ((function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) | ||||
|                 ? mb_strlen($content, '8bit') | ||||
|                 : strlen($content) | ||||
|             ); | ||||
|         $headers['Content-Type'] = self::$_options['contentTypeCharset'] | ||||
|             ? self::$_options['contentType'] . '; charset=' . self::$_options['contentTypeCharset'] | ||||
|             : self::$_options['contentType']; | ||||
|         if (self::$_options['encodeMethod'] !== '') { | ||||
|             $headers['Content-Encoding'] = $contentEncoding; | ||||
|         } | ||||
|         if (self::$_options['encodeOutput'] && $sendVary) { | ||||
|             $headers['Vary'] = 'Accept-Encoding'; | ||||
|         } | ||||
|  | ||||
|         if (! self::$_options['quiet']) { | ||||
|             // output headers & content | ||||
|             foreach ($headers as $name => $val) { | ||||
|                 header($name . ': ' . $val); | ||||
|             } | ||||
|             if ($cacheIsReady) { | ||||
|                 self::$_cache->display($fullCacheId); | ||||
|             } else { | ||||
|                 echo $content; | ||||
|             } | ||||
|         } else { | ||||
|             return array( | ||||
|                 'success' => true | ||||
|                 ,'statusCode' => 200 | ||||
|                 ,'content' => $cacheIsReady | ||||
|                     ? self::$_cache->fetch($fullCacheId) | ||||
|                     : $content | ||||
|                 ,'headers' => $headers | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Return combined minified content for a set of sources | ||||
|      * | ||||
|      * No internal caching will be used and the content will not be HTTP encoded. | ||||
|      *  | ||||
|      * @param array $sources array of filepaths and/or Minify_Source objects | ||||
|      *  | ||||
|      * @param array $options (optional) array of options for serve. By default | ||||
|      * these are already set: quiet = true, encodeMethod = '', lastModifiedTime = 0. | ||||
|      *  | ||||
|      * @return string | ||||
|      */ | ||||
|     public static function combine($sources, $options = array()) | ||||
|     { | ||||
|         $cache = self::$_cache; | ||||
|         self::$_cache = null; | ||||
|         $options = array_merge(array( | ||||
|             'files' => (array)$sources | ||||
|             ,'quiet' => true | ||||
|             ,'encodeMethod' => '' | ||||
|             ,'lastModifiedTime' => 0 | ||||
|         ), $options); | ||||
|         $out = self::serve('Files', $options); | ||||
|         self::$_cache = $cache; | ||||
|         return $out['content']; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Set $_SERVER['DOCUMENT_ROOT']. On IIS, the value is created from SCRIPT_FILENAME and SCRIPT_NAME. | ||||
|      *  | ||||
|      * @param string $docRoot value to use for DOCUMENT_ROOT | ||||
|      */ | ||||
|     public static function setDocRoot($docRoot = '') | ||||
|     { | ||||
|         self::$isDocRootSet = true; | ||||
|         if ($docRoot) { | ||||
|             $_SERVER['DOCUMENT_ROOT'] = $docRoot; | ||||
|         } elseif (isset($_SERVER['SERVER_SOFTWARE']) | ||||
|                   && 0 === strpos($_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS/')) { | ||||
|             $_SERVER['DOCUMENT_ROOT'] = substr( | ||||
|                 $_SERVER['SCRIPT_FILENAME'] | ||||
|                 ,0 | ||||
|                 ,strlen($_SERVER['SCRIPT_FILENAME']) - strlen($_SERVER['SCRIPT_NAME'])); | ||||
|             $_SERVER['DOCUMENT_ROOT'] = rtrim($_SERVER['DOCUMENT_ROOT'], '\\'); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Any Minify_Cache_* object or null (i.e. no server cache is used) | ||||
|      * | ||||
|      * @var Minify_Cache_File | ||||
|      */ | ||||
|     private static $_cache = null; | ||||
|      | ||||
|     /** | ||||
|      * Active controller for current request | ||||
|      * | ||||
|      * @var Minify_Controller_Base | ||||
|      */ | ||||
|     protected static $_controller = null; | ||||
|      | ||||
|     /** | ||||
|      * Options for current request | ||||
|      * | ||||
|      * @var array | ||||
|      */ | ||||
|     protected static $_options = null; | ||||
|  | ||||
|     /** | ||||
|      * @param string $header | ||||
|      * | ||||
|      * @param string $url | ||||
|      */ | ||||
|     protected static function _errorExit($header, $url) | ||||
|     { | ||||
|         $url = htmlspecialchars($url, ENT_QUOTES); | ||||
|         list(,$h1) = explode(' ', $header, 2); | ||||
|         $h1 = htmlspecialchars($h1); | ||||
|         // FastCGI environments require 3rd arg to header() to be set | ||||
|         list(, $code) = explode(' ', $header, 3); | ||||
|         header($header, true, $code); | ||||
|         header('Content-Type: text/html; charset=utf-8'); | ||||
|         echo "<h1>$h1</h1>"; | ||||
|         echo "<p>Please see <a href='$url'>$url</a>.</p>"; | ||||
|         exit(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Set up sources to use Minify_Lines | ||||
|      * | ||||
|      * @param Minify_Source[] $sources Minify_Source instances | ||||
|      */ | ||||
|     protected static function _setupDebug($sources) | ||||
|     { | ||||
|         foreach ($sources as $source) { | ||||
|             $source->minifier = array('Minify_Lines', 'minify'); | ||||
|             $id = $source->getId(); | ||||
|             $source->minifyOptions = array( | ||||
|                 'id' => (is_file($id) ? basename($id) : $id) | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Combines sources and minifies the result. | ||||
|      * | ||||
|      * @return string | ||||
|      * | ||||
|      * @throws Exception | ||||
|      */ | ||||
|     protected static function _combineMinify() | ||||
|     { | ||||
|         $type = self::$_options['contentType']; // ease readability | ||||
|          | ||||
|         // when combining scripts, make sure all statements separated and | ||||
|         // trailing single line comment is terminated | ||||
|         $implodeSeparator = ($type === self::TYPE_JS) | ||||
|             ? "\n;" | ||||
|             : ''; | ||||
|         // allow the user to pass a particular array of options to each | ||||
|         // minifier (designated by type). source objects may still override | ||||
|         // these | ||||
|         $defaultOptions = isset(self::$_options['minifierOptions'][$type]) | ||||
|             ? self::$_options['minifierOptions'][$type] | ||||
|             : array(); | ||||
|         // if minifier not set, default is no minification. source objects | ||||
|         // may still override this | ||||
|         $defaultMinifier = isset(self::$_options['minifiers'][$type]) | ||||
|             ? self::$_options['minifiers'][$type] | ||||
|             : false; | ||||
|  | ||||
|         // process groups of sources with identical minifiers/options | ||||
|         $content = array(); | ||||
|         $i = 0; | ||||
|         $l = count(self::$_controller->sources); | ||||
|         $groupToProcessTogether = array(); | ||||
|         $lastMinifier = null; | ||||
|         $lastOptions = null; | ||||
|         do { | ||||
|             // get next source | ||||
|             $source = null; | ||||
|             if ($i < $l) { | ||||
|                 $source = self::$_controller->sources[$i]; | ||||
|                 /* @var Minify_Source $source */ | ||||
|                 $sourceContent = $source->getContent(); | ||||
|  | ||||
|                 // allow the source to override our minifier and options | ||||
|                 $minifier = (null !== $source->minifier) | ||||
|                     ? $source->minifier | ||||
|                     : $defaultMinifier; | ||||
|                 $options = (null !== $source->minifyOptions) | ||||
|                     ? array_merge($defaultOptions, $source->minifyOptions) | ||||
|                     : $defaultOptions; | ||||
|             } | ||||
|             // do we need to process our group right now? | ||||
|             if ($i > 0                               // yes, we have at least the first group populated | ||||
|                 && ( | ||||
|                     ! $source                        // yes, we ran out of sources | ||||
|                     || $type === self::TYPE_CSS      // yes, to process CSS individually (avoiding PCRE bugs/limits) | ||||
|                     || $minifier !== $lastMinifier   // yes, minifier changed | ||||
|                     || $options !== $lastOptions)    // yes, options changed | ||||
|                 ) | ||||
|             { | ||||
|                 // minify previous sources with last settings | ||||
|                 $imploded = implode($implodeSeparator, $groupToProcessTogether); | ||||
|                 $groupToProcessTogether = array(); | ||||
|                 if ($lastMinifier) { | ||||
|                     try { | ||||
|                         $content[] = call_user_func($lastMinifier, $imploded, $lastOptions); | ||||
|                     } catch (Exception $e) { | ||||
|                         throw new Exception("Exception in minifier: " . $e->getMessage()); | ||||
|                     } | ||||
|                 } else { | ||||
|                     $content[] = $imploded; | ||||
|                 } | ||||
|             } | ||||
|             // add content to the group | ||||
|             if ($source) { | ||||
|                 $groupToProcessTogether[] = $sourceContent; | ||||
|                 $lastMinifier = $minifier; | ||||
|                 $lastOptions = $options; | ||||
|             } | ||||
|             $i++; | ||||
|         } while ($source); | ||||
|  | ||||
|         $content = implode($implodeSeparator, $content); | ||||
|          | ||||
|         if ($type === self::TYPE_CSS && false !== strpos($content, '@import')) { | ||||
|             $content = self::_handleCssImports($content); | ||||
|         } | ||||
|          | ||||
|         // do any post-processing (esp. for editing build URIs) | ||||
|         if (self::$_options['postprocessorRequire']) { | ||||
|             require_once self::$_options['postprocessorRequire']; | ||||
|         } | ||||
|         if (self::$_options['postprocessor']) { | ||||
|             $content = call_user_func(self::$_options['postprocessor'], $content, $type); | ||||
|         } | ||||
|         return $content; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Make a unique cache id for for this request. | ||||
|      *  | ||||
|      * Any settings that could affect output are taken into consideration   | ||||
|      * | ||||
|      * @param string $prefix | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     protected static function _getCacheId($prefix = 'minify') | ||||
|     { | ||||
|         $name = preg_replace('/[^a-zA-Z0-9\\.=_,]/', '', self::$_controller->selectionId); | ||||
|         $name = preg_replace('/\\.+/', '.', $name); | ||||
|         $name = substr($name, 0, 100 - 34 - strlen($prefix)); | ||||
|         $md5 = md5(serialize(array( | ||||
|             Minify_Source::getDigest(self::$_controller->sources) | ||||
|             ,self::$_options['minifiers']  | ||||
|             ,self::$_options['minifierOptions'] | ||||
|             ,self::$_options['postprocessor'] | ||||
|             ,self::$_options['bubbleCssImports'] | ||||
|             ,self::VERSION | ||||
|         ))); | ||||
|         return "{$prefix}_{$name}_{$md5}"; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Bubble CSS @imports to the top or prepend a warning if an import is detected not at the top. | ||||
|      * | ||||
|      * @param string $css | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     protected static function _handleCssImports($css) | ||||
|     { | ||||
|         if (self::$_options['bubbleCssImports']) { | ||||
|             // bubble CSS imports | ||||
|             preg_match_all('/@import.*?;/', $css, $imports); | ||||
|             $css = implode('', $imports[0]) . preg_replace('/@import.*?;/', '', $css); | ||||
|         } else if ('' !== self::$importWarning) { | ||||
|             // remove comments so we don't mistake { in a comment as a block | ||||
|             $noCommentCss = preg_replace('@/\\*[\\s\\S]*?\\*/@', '', $css); | ||||
|             $lastImportPos = strrpos($noCommentCss, '@import'); | ||||
|             $firstBlockPos = strpos($noCommentCss, '{'); | ||||
|             if (false !== $lastImportPos | ||||
|                 && false !== $firstBlockPos | ||||
|                 && $firstBlockPos < $lastImportPos | ||||
|             ) { | ||||
|                 // { appears before @import : prepend warning | ||||
|                 $css = self::$importWarning . $css; | ||||
|             } | ||||
|         } | ||||
|         return $css; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										101
									
								
								vendor/mrclay/minify/min/lib/Minify/Build.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								vendor/mrclay/minify/min/lib/Minify/Build.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,101 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Class Minify_Build   | ||||
|  * @package Minify | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * Maintain a single last modification time for a group of Minify sources to | ||||
|  * allow use of far off Expires headers in Minify. | ||||
|  *  | ||||
|  * <code> | ||||
|  * // in config file | ||||
|  * $groupSources = array( | ||||
|  *   'js' => array('file1.js', 'file2.js') | ||||
|  *   ,'css' => array('file1.css', 'file2.css', 'file3.css') | ||||
|  * ) | ||||
|  *  | ||||
|  * // during HTML generation | ||||
|  * $jsBuild = new Minify_Build($groupSources['js']); | ||||
|  * $cssBuild = new Minify_Build($groupSources['css']); | ||||
|  *  | ||||
|  * $script = "<script type='text/javascript' src='" | ||||
|  *     . $jsBuild->uri('/min.php/js') . "'></script>"; | ||||
|  * $link = "<link rel='stylesheet' type='text/css' href='" | ||||
|  *     . $cssBuild->uri('/min.php/css') . "'>"; | ||||
|  *  | ||||
|  * // in min.php | ||||
|  * Minify::serve('Groups', array( | ||||
|  *   'groups' => $groupSources | ||||
|  *   ,'setExpires' => (time() + 86400 * 365) | ||||
|  * )); | ||||
|  * </code> | ||||
|  *  | ||||
|  * @package Minify | ||||
|  * @author Stephen Clay <steve@mrclay.org> | ||||
|  */ | ||||
| class Minify_Build { | ||||
|      | ||||
|     /** | ||||
|      * Last modification time of all files in the build | ||||
|      *  | ||||
|      * @var int  | ||||
|      */ | ||||
|     public $lastModified = 0; | ||||
|      | ||||
|     /** | ||||
|      * String to use as ampersand in uri(). Set this to '&' if | ||||
|      * you are not HTML-escaping URIs. | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     public static $ampersand = '&'; | ||||
|      | ||||
|     /** | ||||
|      * Get a time-stamped URI | ||||
|      *  | ||||
|      * <code> | ||||
|      * echo $b->uri('/site.js'); | ||||
|      * // outputs "/site.js?1678242" | ||||
|      *  | ||||
|      * echo $b->uri('/scriptaculous.js?load=effects'); | ||||
|      * // outputs "/scriptaculous.js?load=effects&1678242" | ||||
|      * </code> | ||||
|      * | ||||
|      * @param string $uri | ||||
|      * @param boolean $forceAmpersand (default = false) Force the use of ampersand to  | ||||
|      * append the timestamp to the URI. | ||||
|      * @return string | ||||
|      */ | ||||
|     public function uri($uri, $forceAmpersand = false) { | ||||
|         $sep = ($forceAmpersand || strpos($uri, '?') !== false) | ||||
|             ? self::$ampersand | ||||
|             : '?'; | ||||
|         return "{$uri}{$sep}{$this->lastModified}"; | ||||
|     } | ||||
|  | ||||
| 	/** | ||||
|      * Create a build object | ||||
|      *  | ||||
|      * @param array $sources array of Minify_Source objects and/or file paths | ||||
|      *  | ||||
|      * @return null | ||||
|      */ | ||||
|     public function __construct($sources)  | ||||
|     { | ||||
|         $max = 0; | ||||
|         foreach ((array)$sources as $source) { | ||||
|             if ($source instanceof Minify_Source) { | ||||
|                 $max = max($max, $source->lastModified); | ||||
|             } elseif (is_string($source)) { | ||||
|                 if (0 === strpos($source, '//')) { | ||||
|                     $source = $_SERVER['DOCUMENT_ROOT'] . substr($source, 1); | ||||
|                 } | ||||
|                 if (is_file($source)) { | ||||
|                     $max = max($max, filemtime($source)); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         $this->lastModified = $max; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										99
									
								
								vendor/mrclay/minify/min/lib/Minify/CSS.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								vendor/mrclay/minify/min/lib/Minify/CSS.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,99 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Class Minify_CSS   | ||||
|  * @package Minify | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * Minify CSS | ||||
|  * | ||||
|  * This class uses Minify_CSS_Compressor and Minify_CSS_UriRewriter to  | ||||
|  * minify CSS and rewrite relative URIs. | ||||
|  *  | ||||
|  * @package Minify | ||||
|  * @author Stephen Clay <steve@mrclay.org> | ||||
|  * @author http://code.google.com/u/1stvamp/ (Issue 64 patch) | ||||
|  */ | ||||
| class Minify_CSS { | ||||
|      | ||||
|     /** | ||||
|      * Minify a CSS string | ||||
|      *  | ||||
|      * @param string $css | ||||
|      *  | ||||
|      * @param array $options available options: | ||||
|      *  | ||||
|      * 'preserveComments': (default true) multi-line comments that begin | ||||
|      * with "/*!" will be preserved with newlines before and after to | ||||
|      * enhance readability. | ||||
|      * | ||||
|      * 'removeCharsets': (default true) remove all @charset at-rules | ||||
|      *  | ||||
|      * 'prependRelativePath': (default null) if given, this string will be | ||||
|      * prepended to all relative URIs in import/url declarations | ||||
|      *  | ||||
|      * 'currentDir': (default null) if given, this is assumed to be the | ||||
|      * directory of the current CSS file. Using this, minify will rewrite | ||||
|      * all relative URIs in import/url declarations to correctly point to | ||||
|      * the desired files. For this to work, the files *must* exist and be | ||||
|      * visible by the PHP process. | ||||
|      * | ||||
|      * 'symlinks': (default = array()) If the CSS file is stored in  | ||||
|      * a symlink-ed directory, provide an array of link paths to | ||||
|      * target paths, where the link paths are within the document root. Because  | ||||
|      * paths need to be normalized for this to work, use "//" to substitute  | ||||
|      * the doc root in the link paths (the array keys). E.g.: | ||||
|      * <code> | ||||
|      * array('//symlink' => '/real/target/path') // unix | ||||
|      * array('//static' => 'D:\\staticStorage')  // Windows | ||||
|      * </code> | ||||
|      * | ||||
|      * 'docRoot': (default = $_SERVER['DOCUMENT_ROOT']) | ||||
|      * see Minify_CSS_UriRewriter::rewrite | ||||
|      *  | ||||
|      * @return string | ||||
|      */ | ||||
|     public static function minify($css, $options = array())  | ||||
|     { | ||||
|         $options = array_merge(array( | ||||
|             'compress' => true, | ||||
|             'removeCharsets' => true, | ||||
|             'preserveComments' => true, | ||||
|             'currentDir' => null, | ||||
|             'docRoot' => $_SERVER['DOCUMENT_ROOT'], | ||||
|             'prependRelativePath' => null, | ||||
|             'symlinks' => array(), | ||||
|         ), $options); | ||||
|          | ||||
|         if ($options['removeCharsets']) { | ||||
|             $css = preg_replace('/@charset[^;]+;\\s*/', '', $css); | ||||
|         } | ||||
|         if ($options['compress']) { | ||||
|             if (! $options['preserveComments']) { | ||||
|                 $css = Minify_CSS_Compressor::process($css, $options); | ||||
|             } else { | ||||
|                 $css = Minify_CommentPreserver::process( | ||||
|                     $css | ||||
|                     ,array('Minify_CSS_Compressor', 'process') | ||||
|                     ,array($options) | ||||
|                 ); | ||||
|             } | ||||
|         } | ||||
|         if (! $options['currentDir'] && ! $options['prependRelativePath']) { | ||||
|             return $css; | ||||
|         } | ||||
|         if ($options['currentDir']) { | ||||
|             return Minify_CSS_UriRewriter::rewrite( | ||||
|                 $css | ||||
|                 ,$options['currentDir'] | ||||
|                 ,$options['docRoot'] | ||||
|                 ,$options['symlinks'] | ||||
|             );   | ||||
|         } else { | ||||
|             return Minify_CSS_UriRewriter::prepend( | ||||
|                 $css | ||||
|                 ,$options['prependRelativePath'] | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										249
									
								
								vendor/mrclay/minify/min/lib/Minify/CSS/Compressor.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										249
									
								
								vendor/mrclay/minify/min/lib/Minify/CSS/Compressor.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,249 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Class Minify_CSS_Compressor  | ||||
|  * @package Minify | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * Compress CSS | ||||
|  * | ||||
|  * This is a heavy regex-based removal of whitespace, unnecessary | ||||
|  * comments and tokens, and some CSS value minimization, where practical. | ||||
|  * Many steps have been taken to avoid breaking comment-based hacks,  | ||||
|  * including the ie5/mac filter (and its inversion), but expect tricky | ||||
|  * hacks involving comment tokens in 'content' value strings to break | ||||
|  * minimization badly. A test suite is available. | ||||
|  *  | ||||
|  * @package Minify | ||||
|  * @author Stephen Clay <steve@mrclay.org> | ||||
|  * @author http://code.google.com/u/1stvamp/ (Issue 64 patch) | ||||
|  */ | ||||
| class Minify_CSS_Compressor { | ||||
|  | ||||
|     /** | ||||
|      * Minify a CSS string | ||||
|      *  | ||||
|      * @param string $css | ||||
|      *  | ||||
|      * @param array $options (currently ignored) | ||||
|      *  | ||||
|      * @return string | ||||
|      */ | ||||
|     public static function process($css, $options = array()) | ||||
|     { | ||||
|         $obj = new Minify_CSS_Compressor($options); | ||||
|         return $obj->_process($css); | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * @var array | ||||
|      */ | ||||
|     protected $_options = null; | ||||
|      | ||||
|     /** | ||||
|      * Are we "in" a hack? I.e. are some browsers targetted until the next comment? | ||||
|      * | ||||
|      * @var bool | ||||
|      */ | ||||
|     protected $_inHack = false; | ||||
|      | ||||
|      | ||||
|     /** | ||||
|      * Constructor | ||||
|      *  | ||||
|      * @param array $options (currently ignored) | ||||
|      */ | ||||
|     private function __construct($options) { | ||||
|         $this->_options = $options; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Minify a CSS string | ||||
|      *  | ||||
|      * @param string $css | ||||
|      *  | ||||
|      * @return string | ||||
|      */ | ||||
|     protected function _process($css) | ||||
|     { | ||||
|         $css = str_replace("\r\n", "\n", $css); | ||||
|          | ||||
|         // preserve empty comment after '>' | ||||
|         // http://www.webdevout.net/css-hacks#in_css-selectors | ||||
|         $css = preg_replace('@>/\\*\\s*\\*/@', '>/*keep*/', $css); | ||||
|          | ||||
|         // preserve empty comment between property and value | ||||
|         // http://css-discuss.incutio.com/?page=BoxModelHack | ||||
|         $css = preg_replace('@/\\*\\s*\\*/\\s*:@', '/*keep*/:', $css); | ||||
|         $css = preg_replace('@:\\s*/\\*\\s*\\*/@', ':/*keep*/', $css); | ||||
|          | ||||
|         // apply callback to all valid comments (and strip out surrounding ws | ||||
|         $css = preg_replace_callback('@\\s*/\\*([\\s\\S]*?)\\*/\\s*@' | ||||
|             ,array($this, '_commentCB'), $css); | ||||
|  | ||||
|         // remove ws around { } and last semicolon in declaration block | ||||
|         $css = preg_replace('/\\s*{\\s*/', '{', $css); | ||||
|         $css = preg_replace('/;?\\s*}\\s*/', '}', $css); | ||||
|          | ||||
|         // remove ws surrounding semicolons | ||||
|         $css = preg_replace('/\\s*;\\s*/', ';', $css); | ||||
|          | ||||
|         // remove ws around urls | ||||
|         $css = preg_replace('/ | ||||
|                 url\\(      # url( | ||||
|                 \\s* | ||||
|                 ([^\\)]+?)  # 1 = the URL (really just a bunch of non right parenthesis) | ||||
|                 \\s* | ||||
|                 \\)         # ) | ||||
|             /x', 'url($1)', $css); | ||||
|          | ||||
|         // remove ws between rules and colons | ||||
|         $css = preg_replace('/ | ||||
|                 \\s* | ||||
|                 ([{;])              # 1 = beginning of block or rule separator  | ||||
|                 \\s* | ||||
|                 ([\\*_]?[\\w\\-]+)  # 2 = property (and maybe IE filter) | ||||
|                 \\s* | ||||
|                 : | ||||
|                 \\s* | ||||
|                 (\\b|[#\'"-])        # 3 = first character of a value | ||||
|             /x', '$1$2:$3', $css); | ||||
|          | ||||
|         // remove ws in selectors | ||||
|         $css = preg_replace_callback('/ | ||||
|                 (?:              # non-capture | ||||
|                     \\s* | ||||
|                     [^~>+,\\s]+  # selector part | ||||
|                     \\s* | ||||
|                     [,>+~]       # combinators | ||||
|                 )+ | ||||
|                 \\s* | ||||
|                 [^~>+,\\s]+      # selector part | ||||
|                 {                # open declaration block | ||||
|             /x' | ||||
|             ,array($this, '_selectorsCB'), $css); | ||||
|          | ||||
|         // minimize hex colors | ||||
|         $css = preg_replace('/([^=])#([a-f\\d])\\2([a-f\\d])\\3([a-f\\d])\\4([\\s;\\}])/i' | ||||
|             , '$1#$2$3$4$5', $css); | ||||
|          | ||||
|         // remove spaces between font families | ||||
|         $css = preg_replace_callback('/font-family:([^;}]+)([;}])/' | ||||
|             ,array($this, '_fontFamilyCB'), $css); | ||||
|          | ||||
|         $css = preg_replace('/@import\\s+url/', '@import url', $css); | ||||
|          | ||||
|         // replace any ws involving newlines with a single newline | ||||
|         $css = preg_replace('/[ \\t]*\\n+\\s*/', "\n", $css); | ||||
|          | ||||
|         // separate common descendent selectors w/ newlines (to limit line lengths) | ||||
|         $css = preg_replace('/([\\w#\\.\\*]+)\\s+([\\w#\\.\\*]+){/', "$1\n$2{", $css); | ||||
|          | ||||
|         // Use newline after 1st numeric value (to limit line lengths). | ||||
|         $css = preg_replace('/ | ||||
|             ((?:padding|margin|border|outline):\\d+(?:px|em)?) # 1 = prop : 1st numeric value | ||||
|             \\s+ | ||||
|             /x' | ||||
|             ,"$1\n", $css); | ||||
|          | ||||
|         // prevent triggering IE6 bug: http://www.crankygeek.com/ie6pebug/ | ||||
|         $css = preg_replace('/:first-l(etter|ine)\\{/', ':first-l$1 {', $css); | ||||
|              | ||||
|         return trim($css); | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Replace what looks like a set of selectors   | ||||
|      * | ||||
|      * @param array $m regex matches | ||||
|      *  | ||||
|      * @return string | ||||
|      */ | ||||
|     protected function _selectorsCB($m) | ||||
|     { | ||||
|         // remove ws around the combinators | ||||
|         return preg_replace('/\\s*([,>+~])\\s*/', '$1', $m[0]); | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Process a comment and return a replacement | ||||
|      *  | ||||
|      * @param array $m regex matches | ||||
|      *  | ||||
|      * @return string | ||||
|      */ | ||||
|     protected function _commentCB($m) | ||||
|     { | ||||
|         $hasSurroundingWs = (trim($m[0]) !== $m[1]); | ||||
|         $m = $m[1];  | ||||
|         // $m is the comment content w/o the surrounding tokens,  | ||||
|         // but the return value will replace the entire comment. | ||||
|         if ($m === 'keep') { | ||||
|             return '/**/'; | ||||
|         } | ||||
|         if ($m === '" "') { | ||||
|             // component of http://tantek.com/CSS/Examples/midpass.html | ||||
|             return '/*" "*/'; | ||||
|         } | ||||
|         if (preg_match('@";\\}\\s*\\}/\\*\\s+@', $m)) { | ||||
|             // component of http://tantek.com/CSS/Examples/midpass.html | ||||
|             return '/*";}}/* */'; | ||||
|         } | ||||
|         if ($this->_inHack) { | ||||
|             // inversion: feeding only to one browser | ||||
|             if (preg_match('@ | ||||
|                     ^/               # comment started like /*/ | ||||
|                     \\s* | ||||
|                     (\\S[\\s\\S]+?)  # has at least some non-ws content | ||||
|                     \\s* | ||||
|                     /\\*             # ends like /*/ or /**/ | ||||
|                 @x', $m, $n)) { | ||||
|                 // end hack mode after this comment, but preserve the hack and comment content | ||||
|                 $this->_inHack = false; | ||||
|                 return "/*/{$n[1]}/**/"; | ||||
|             } | ||||
|         } | ||||
|         if (substr($m, -1) === '\\') { // comment ends like \*/ | ||||
|             // begin hack mode and preserve hack | ||||
|             $this->_inHack = true; | ||||
|             return '/*\\*/'; | ||||
|         } | ||||
|         if ($m !== '' && $m[0] === '/') { // comment looks like /*/ foo */ | ||||
|             // begin hack mode and preserve hack | ||||
|             $this->_inHack = true; | ||||
|             return '/*/*/'; | ||||
|         } | ||||
|         if ($this->_inHack) { | ||||
|             // a regular comment ends hack mode but should be preserved | ||||
|             $this->_inHack = false; | ||||
|             return '/**/'; | ||||
|         } | ||||
|         // Issue 107: if there's any surrounding whitespace, it may be important, so  | ||||
|         // replace the comment with a single space | ||||
|         return $hasSurroundingWs // remove all other comments | ||||
|             ? ' ' | ||||
|             : ''; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Process a font-family listing and return a replacement | ||||
|      *  | ||||
|      * @param array $m regex matches | ||||
|      *  | ||||
|      * @return string    | ||||
|      */ | ||||
|     protected function _fontFamilyCB($m) | ||||
|     { | ||||
|         // Issue 210: must not eliminate WS between words in unquoted families | ||||
|         $pieces = preg_split('/(\'[^\']+\'|"[^"]+")/', $m[1], null, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); | ||||
|         $out = 'font-family:'; | ||||
|         while (null !== ($piece = array_shift($pieces))) { | ||||
|             if ($piece[0] !== '"' && $piece[0] !== "'") { | ||||
|                 $piece = preg_replace('/\\s+/', ' ', $piece); | ||||
|                 $piece = preg_replace('/\\s?,\\s?/', ',', $piece); | ||||
|             } | ||||
|             $out .= $piece; | ||||
|         } | ||||
|         return $out . $m[2]; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										307
									
								
								vendor/mrclay/minify/min/lib/Minify/CSS/UriRewriter.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										307
									
								
								vendor/mrclay/minify/min/lib/Minify/CSS/UriRewriter.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,307 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Class Minify_CSS_UriRewriter   | ||||
|  * @package Minify | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * Rewrite file-relative URIs as root-relative in CSS files | ||||
|  * | ||||
|  * @package Minify | ||||
|  * @author Stephen Clay <steve@mrclay.org> | ||||
|  */ | ||||
| class Minify_CSS_UriRewriter { | ||||
|      | ||||
|     /** | ||||
|      * rewrite() and rewriteRelative() append debugging information here | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     public static $debugText = ''; | ||||
|      | ||||
|     /** | ||||
|      * In CSS content, rewrite file relative URIs as root relative | ||||
|      *  | ||||
|      * @param string $css | ||||
|      *  | ||||
|      * @param string $currentDir The directory of the current CSS file. | ||||
|      *  | ||||
|      * @param string $docRoot The document root of the web site in which  | ||||
|      * the CSS file resides (default = $_SERVER['DOCUMENT_ROOT']). | ||||
|      *  | ||||
|      * @param array $symlinks (default = array()) If the CSS file is stored in  | ||||
|      * a symlink-ed directory, provide an array of link paths to | ||||
|      * target paths, where the link paths are within the document root. Because  | ||||
|      * paths need to be normalized for this to work, use "//" to substitute  | ||||
|      * the doc root in the link paths (the array keys). E.g.: | ||||
|      * <code> | ||||
|      * array('//symlink' => '/real/target/path') // unix | ||||
|      * array('//static' => 'D:\\staticStorage')  // Windows | ||||
|      * </code> | ||||
|      *  | ||||
|      * @return string | ||||
|      */ | ||||
|     public static function rewrite($css, $currentDir, $docRoot = null, $symlinks = array())  | ||||
|     { | ||||
|         self::$_docRoot = self::_realpath( | ||||
|             $docRoot ? $docRoot : $_SERVER['DOCUMENT_ROOT'] | ||||
|         ); | ||||
|         self::$_currentDir = self::_realpath($currentDir); | ||||
|         self::$_symlinks = array(); | ||||
|          | ||||
|         // normalize symlinks | ||||
|         foreach ($symlinks as $link => $target) { | ||||
|             $link = ($link === '//') | ||||
|                 ? self::$_docRoot | ||||
|                 : str_replace('//', self::$_docRoot . '/', $link); | ||||
|             $link = strtr($link, '/', DIRECTORY_SEPARATOR); | ||||
|             self::$_symlinks[$link] = self::_realpath($target); | ||||
|         } | ||||
|          | ||||
|         self::$debugText .= "docRoot    : " . self::$_docRoot . "\n" | ||||
|                           . "currentDir : " . self::$_currentDir . "\n"; | ||||
|         if (self::$_symlinks) { | ||||
|             self::$debugText .= "symlinks : " . var_export(self::$_symlinks, 1) . "\n"; | ||||
|         } | ||||
|         self::$debugText .= "\n"; | ||||
|          | ||||
|         $css = self::_trimUrls($css); | ||||
|          | ||||
|         // rewrite | ||||
|         $css = preg_replace_callback('/@import\\s+([\'"])(.*?)[\'"]/' | ||||
|             ,array(self::$className, '_processUriCB'), $css); | ||||
|         $css = preg_replace_callback('/url\\(\\s*([\'"](.*?)[\'"]|[^\\)\\s]+)\\s*\\)/' | ||||
|             ,array(self::$className, '_processUriCB'), $css); | ||||
|  | ||||
|         return $css; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * In CSS content, prepend a path to relative URIs | ||||
|      *  | ||||
|      * @param string $css | ||||
|      *  | ||||
|      * @param string $path The path to prepend. | ||||
|      *  | ||||
|      * @return string | ||||
|      */ | ||||
|     public static function prepend($css, $path) | ||||
|     { | ||||
|         self::$_prependPath = $path; | ||||
|          | ||||
|         $css = self::_trimUrls($css); | ||||
|          | ||||
|         // append | ||||
|         $css = preg_replace_callback('/@import\\s+([\'"])(.*?)[\'"]/' | ||||
|             ,array(self::$className, '_processUriCB'), $css); | ||||
|         $css = preg_replace_callback('/url\\(\\s*([\'"](.*?)[\'"]|[^\\)\\s]+)\\s*\\)/' | ||||
|             ,array(self::$className, '_processUriCB'), $css); | ||||
|  | ||||
|         self::$_prependPath = null; | ||||
|         return $css; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Get a root relative URI from a file relative URI | ||||
|      * | ||||
|      * <code> | ||||
|      * Minify_CSS_UriRewriter::rewriteRelative( | ||||
|      *       '../img/hello.gif' | ||||
|      *     , '/home/user/www/css'  // path of CSS file | ||||
|      *     , '/home/user/www'      // doc root | ||||
|      * ); | ||||
|      * // returns '/img/hello.gif' | ||||
|      *  | ||||
|      * // example where static files are stored in a symlinked directory | ||||
|      * Minify_CSS_UriRewriter::rewriteRelative( | ||||
|      *       'hello.gif' | ||||
|      *     , '/var/staticFiles/theme' | ||||
|      *     , '/home/user/www' | ||||
|      *     , array('/home/user/www/static' => '/var/staticFiles') | ||||
|      * ); | ||||
|      * // returns '/static/theme/hello.gif' | ||||
|      * </code> | ||||
|      *  | ||||
|      * @param string $uri file relative URI | ||||
|      *  | ||||
|      * @param string $realCurrentDir realpath of the current file's directory. | ||||
|      *  | ||||
|      * @param string $realDocRoot realpath of the site document root. | ||||
|      *  | ||||
|      * @param array $symlinks (default = array()) If the file is stored in  | ||||
|      * a symlink-ed directory, provide an array of link paths to | ||||
|      * real target paths, where the link paths "appear" to be within the document  | ||||
|      * root. E.g.: | ||||
|      * <code> | ||||
|      * array('/home/foo/www/not/real/path' => '/real/target/path') // unix | ||||
|      * array('C:\\htdocs\\not\\real' => 'D:\\real\\target\\path')  // Windows | ||||
|      * </code> | ||||
|      *  | ||||
|      * @return string | ||||
|      */ | ||||
|     public static function rewriteRelative($uri, $realCurrentDir, $realDocRoot, $symlinks = array()) | ||||
|     { | ||||
|         // prepend path with current dir separator (OS-independent) | ||||
|         $path = strtr($realCurrentDir, '/', DIRECTORY_SEPARATOR)   | ||||
|             . DIRECTORY_SEPARATOR . strtr($uri, '/', DIRECTORY_SEPARATOR); | ||||
|          | ||||
|         self::$debugText .= "file-relative URI  : {$uri}\n" | ||||
|                           . "path prepended     : {$path}\n"; | ||||
|          | ||||
|         // "unresolve" a symlink back to doc root | ||||
|         foreach ($symlinks as $link => $target) { | ||||
|             if (0 === strpos($path, $target)) { | ||||
|                 // replace $target with $link | ||||
|                 $path = $link . substr($path, strlen($target)); | ||||
|                  | ||||
|                 self::$debugText .= "symlink unresolved : {$path}\n"; | ||||
|                  | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         // strip doc root | ||||
|         $path = substr($path, strlen($realDocRoot)); | ||||
|          | ||||
|         self::$debugText .= "docroot stripped   : {$path}\n"; | ||||
|          | ||||
|         // fix to root-relative URI | ||||
|         $uri = strtr($path, '/\\', '//'); | ||||
|         $uri = self::removeDots($uri); | ||||
|        | ||||
|         self::$debugText .= "traversals removed : {$uri}\n\n"; | ||||
|          | ||||
|         return $uri; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Remove instances of "./" and "../" where possible from a root-relative URI | ||||
|      * | ||||
|      * @param string $uri | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public static function removeDots($uri) | ||||
|     { | ||||
|         $uri = str_replace('/./', '/', $uri); | ||||
|         // inspired by patch from Oleg Cherniy | ||||
|         do { | ||||
|             $uri = preg_replace('@/[^/]+/\\.\\./@', '/', $uri, 1, $changed); | ||||
|         } while ($changed); | ||||
|         return $uri; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Defines which class to call as part of callbacks, change this | ||||
|      * if you extend Minify_CSS_UriRewriter | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     protected static $className = 'Minify_CSS_UriRewriter'; | ||||
|  | ||||
|     /** | ||||
|      * Get realpath with any trailing slash removed. If realpath() fails, | ||||
|      * just remove the trailing slash. | ||||
|      *  | ||||
|      * @param string $path | ||||
|      *  | ||||
|      * @return mixed path with no trailing slash | ||||
|      */ | ||||
|     protected static function _realpath($path) | ||||
|     { | ||||
|         $realPath = realpath($path); | ||||
|         if ($realPath !== false) { | ||||
|             $path = $realPath; | ||||
|         } | ||||
|         return rtrim($path, '/\\'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Directory of this stylesheet | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     private static $_currentDir = ''; | ||||
|  | ||||
|     /** | ||||
|      * DOC_ROOT | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     private static $_docRoot = ''; | ||||
|  | ||||
|     /** | ||||
|      * directory replacements to map symlink targets back to their | ||||
|      * source (within the document root) E.g. '/var/www/symlink' => '/var/realpath' | ||||
|      * | ||||
|      * @var array | ||||
|      */ | ||||
|     private static $_symlinks = array(); | ||||
|  | ||||
|     /** | ||||
|      * Path to prepend | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     private static $_prependPath = null; | ||||
|  | ||||
|     /** | ||||
|      * @param string $css | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     private static function _trimUrls($css) | ||||
|     { | ||||
|         return preg_replace('/ | ||||
|             url\\(      # url( | ||||
|             \\s* | ||||
|             ([^\\)]+?)  # 1 = URI (assuming does not contain ")") | ||||
|             \\s* | ||||
|             \\)         # ) | ||||
|         /x', 'url($1)', $css); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array $m | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     private static function _processUriCB($m) | ||||
|     { | ||||
|         // $m matched either '/@import\\s+([\'"])(.*?)[\'"]/' or '/url\\(\\s*([^\\)\\s]+)\\s*\\)/' | ||||
|         $isImport = ($m[0][0] === '@'); | ||||
|         // determine URI and the quote character (if any) | ||||
|         if ($isImport) { | ||||
|             $quoteChar = $m[1]; | ||||
|             $uri = $m[2]; | ||||
|         } else { | ||||
|             // $m[1] is either quoted or not | ||||
|             $quoteChar = ($m[1][0] === "'" || $m[1][0] === '"') | ||||
|                 ? $m[1][0] | ||||
|                 : ''; | ||||
|             $uri = ($quoteChar === '') | ||||
|                 ? $m[1] | ||||
|                 : substr($m[1], 1, strlen($m[1]) - 2); | ||||
|         } | ||||
|         // if not root/scheme relative and not starts with scheme | ||||
|         if (!preg_match('~^(/|[a-z]+\:)~', $uri)) { | ||||
|             // URI is file-relative: rewrite depending on options | ||||
|             if (self::$_prependPath === null) { | ||||
|                 $uri = self::rewriteRelative($uri, self::$_currentDir, self::$_docRoot, self::$_symlinks); | ||||
|             } else { | ||||
|                 $uri = self::$_prependPath . $uri; | ||||
|                 if ($uri[0] === '/') { | ||||
|                     $root = ''; | ||||
|                     $rootRelative = $uri; | ||||
|                     $uri = $root . self::removeDots($rootRelative); | ||||
|                 } elseif (preg_match('@^((https?\:)?//([^/]+))/@', $uri, $m) && (false !== strpos($m[3], '.'))) { | ||||
|                     $root = $m[1]; | ||||
|                     $rootRelative = substr($uri, strlen($root)); | ||||
|                     $uri = $root . self::removeDots($rootRelative); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return $isImport | ||||
|             ? "@import {$quoteChar}{$uri}{$quoteChar}" | ||||
|             : "url({$quoteChar}{$uri}{$quoteChar})"; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										85
									
								
								vendor/mrclay/minify/min/lib/Minify/CSSmin.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								vendor/mrclay/minify/min/lib/Minify/CSSmin.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,85 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Class Minify_CSSmin | ||||
|  * @package Minify | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * Wrapper for CSSmin | ||||
|  * | ||||
|  * This class uses CSSmin and Minify_CSS_UriRewriter to minify CSS and rewrite relative URIs. | ||||
|  *  | ||||
|  * @package Minify | ||||
|  * @author Stephen Clay <steve@mrclay.org> | ||||
|  */ | ||||
| class Minify_CSSmin { | ||||
|      | ||||
|     /** | ||||
|      * Minify a CSS string | ||||
|      *  | ||||
|      * @param string $css | ||||
|      *  | ||||
|      * @param array $options available options: | ||||
|      *  | ||||
|      * 'removeCharsets': (default true) remove all @charset at-rules | ||||
|      * | ||||
|      * 'prependRelativePath': (default null) if given, this string will be | ||||
|      * prepended to all relative URIs in import/url declarations | ||||
|      *  | ||||
|      * 'currentDir': (default null) if given, this is assumed to be the | ||||
|      * directory of the current CSS file. Using this, minify will rewrite | ||||
|      * all relative URIs in import/url declarations to correctly point to | ||||
|      * the desired files. For this to work, the files *must* exist and be | ||||
|      * visible by the PHP process. | ||||
|      * | ||||
|      * 'symlinks': (default = array()) If the CSS file is stored in  | ||||
|      * a symlink-ed directory, provide an array of link paths to | ||||
|      * target paths, where the link paths are within the document root. Because  | ||||
|      * paths need to be normalized for this to work, use "//" to substitute  | ||||
|      * the doc root in the link paths (the array keys). E.g.: | ||||
|      * <code> | ||||
|      * array('//symlink' => '/real/target/path') // unix | ||||
|      * array('//static' => 'D:\\staticStorage')  // Windows | ||||
|      * </code> | ||||
|      * | ||||
|      * 'docRoot': (default = $_SERVER['DOCUMENT_ROOT']) | ||||
|      * see Minify_CSS_UriRewriter::rewrite | ||||
|      *  | ||||
|      * @return string | ||||
|      */ | ||||
|     public static function minify($css, $options = array())  | ||||
|     { | ||||
|         $options = array_merge(array( | ||||
|             'compress' => true, | ||||
|             'removeCharsets' => true, | ||||
|             'currentDir' => null, | ||||
|             'docRoot' => $_SERVER['DOCUMENT_ROOT'], | ||||
|             'prependRelativePath' => null, | ||||
|             'symlinks' => array(), | ||||
|         ), $options); | ||||
|          | ||||
|         if ($options['removeCharsets']) { | ||||
|             $css = preg_replace('/@charset[^;]+;\\s*/', '', $css); | ||||
|         } | ||||
|         if ($options['compress']) { | ||||
|             $obj = new CSSmin(); | ||||
|             $css = $obj->run($css); | ||||
|         } | ||||
|         if (! $options['currentDir'] && ! $options['prependRelativePath']) { | ||||
|             return $css; | ||||
|         } | ||||
|         if ($options['currentDir']) { | ||||
|             return Minify_CSS_UriRewriter::rewrite( | ||||
|                 $css | ||||
|                 ,$options['currentDir'] | ||||
|                 ,$options['docRoot'] | ||||
|                 ,$options['symlinks'] | ||||
|             );   | ||||
|         } else { | ||||
|             return Minify_CSS_UriRewriter::prepend( | ||||
|                 $css | ||||
|                 ,$options['prependRelativePath'] | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										133
									
								
								vendor/mrclay/minify/min/lib/Minify/Cache/APC.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								vendor/mrclay/minify/min/lib/Minify/Cache/APC.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,133 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Class Minify_Cache_APC | ||||
|  * @package Minify | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * APC-based cache class for Minify | ||||
|  *  | ||||
|  * <code> | ||||
|  * Minify::setCache(new Minify_Cache_APC()); | ||||
|  * </code> | ||||
|  *  | ||||
|  * @package Minify | ||||
|  * @author Chris Edwards | ||||
|  **/ | ||||
| class Minify_Cache_APC { | ||||
|  | ||||
|     /** | ||||
|      * Create a Minify_Cache_APC object, to be passed to | ||||
|      * Minify::setCache(). | ||||
|      * | ||||
|      * | ||||
|      * @param int $expire seconds until expiration (default = 0 | ||||
|      * meaning the item will not get an expiration date) | ||||
|      * | ||||
|      * @return null | ||||
|      */ | ||||
|     public function __construct($expire = 0) | ||||
|     { | ||||
|         $this->_exp = $expire; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Write data to cache. | ||||
|      * | ||||
|      * @param string $id cache id | ||||
|      * | ||||
|      * @param string $data | ||||
|      * | ||||
|      * @return bool success | ||||
|      */ | ||||
|     public function store($id, $data) | ||||
|     { | ||||
|         return apc_store($id, "{$_SERVER['REQUEST_TIME']}|{$data}", $this->_exp); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the size of a cache entry | ||||
|      * | ||||
|      * @param string $id cache id | ||||
|      * | ||||
|      * @return int size in bytes | ||||
|      */ | ||||
|     public function getSize($id) | ||||
|     { | ||||
|         if (! $this->_fetch($id)) { | ||||
|             return false; | ||||
|         } | ||||
|         return (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) | ||||
|             ? mb_strlen($this->_data, '8bit') | ||||
|             : strlen($this->_data); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Does a valid cache entry exist? | ||||
|      * | ||||
|      * @param string $id cache id | ||||
|      * | ||||
|      * @param int $srcMtime mtime of the original source file(s) | ||||
|      * | ||||
|      * @return bool exists | ||||
|      */ | ||||
|     public function isValid($id, $srcMtime) | ||||
|     { | ||||
|         return ($this->_fetch($id) && ($this->_lm >= $srcMtime)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Send the cached content to output | ||||
|      * | ||||
|      * @param string $id cache id | ||||
|      */ | ||||
|     public function display($id) | ||||
|     { | ||||
|         echo $this->_fetch($id) | ||||
|             ? $this->_data | ||||
|             : ''; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Fetch the cached content | ||||
|      * | ||||
|      * @param string $id cache id | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public function fetch($id) | ||||
|     { | ||||
|         return $this->_fetch($id) | ||||
|             ? $this->_data | ||||
|             : ''; | ||||
|     } | ||||
|  | ||||
|     private $_exp = null; | ||||
|  | ||||
|     // cache of most recently fetched id | ||||
|     private $_lm = null; | ||||
|     private $_data = null; | ||||
|     private $_id = null; | ||||
|  | ||||
|     /** | ||||
|      * Fetch data and timestamp from apc, store in instance | ||||
|      * | ||||
|      * @param string $id | ||||
|      * | ||||
|      * @return bool success | ||||
|      */ | ||||
|     private function _fetch($id) | ||||
|     { | ||||
|         if ($this->_id === $id) { | ||||
|             return true; | ||||
|         } | ||||
|         $ret = apc_fetch($id); | ||||
|         if (false === $ret) { | ||||
|             $this->_id = null; | ||||
|             return false; | ||||
|         } | ||||
|         list($this->_lm, $this->_data) = explode('|', $ret, 2); | ||||
|         $this->_id = $id; | ||||
|         return true; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										197
									
								
								vendor/mrclay/minify/min/lib/Minify/Cache/File.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										197
									
								
								vendor/mrclay/minify/min/lib/Minify/Cache/File.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,197 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Class Minify_Cache_File   | ||||
|  * @package Minify | ||||
|  */ | ||||
|  | ||||
| class Minify_Cache_File { | ||||
|      | ||||
|     public function __construct($path = '', $fileLocking = false) | ||||
|     { | ||||
|         if (! $path) { | ||||
|             $path = self::tmp(); | ||||
|         } | ||||
|         $this->_locking = $fileLocking; | ||||
|         $this->_path = $path; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Write data to cache. | ||||
|      * | ||||
|      * @param string $id cache id (e.g. a filename) | ||||
|      *  | ||||
|      * @param string $data | ||||
|      *  | ||||
|      * @return bool success | ||||
|      */ | ||||
|     public function store($id, $data) | ||||
|     { | ||||
|         $flag = $this->_locking | ||||
|             ? LOCK_EX | ||||
|             : null; | ||||
|         $file = $this->_path . '/' . $id; | ||||
|         if (! @file_put_contents($file, $data, $flag)) { | ||||
|             $this->_log("Minify_Cache_File: Write failed to '$file'"); | ||||
|         } | ||||
|         // write control | ||||
|         if ($data !== $this->fetch($id)) { | ||||
|             @unlink($file); | ||||
|             $this->_log("Minify_Cache_File: Post-write read failed for '$file'"); | ||||
|             return false; | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Get the size of a cache entry | ||||
|      * | ||||
|      * @param string $id cache id (e.g. a filename) | ||||
|      *  | ||||
|      * @return int size in bytes | ||||
|      */ | ||||
|     public function getSize($id) | ||||
|     { | ||||
|         return filesize($this->_path . '/' . $id); | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Does a valid cache entry exist? | ||||
|      * | ||||
|      * @param string $id cache id (e.g. a filename) | ||||
|      *  | ||||
|      * @param int $srcMtime mtime of the original source file(s) | ||||
|      *  | ||||
|      * @return bool exists | ||||
|      */ | ||||
|     public function isValid($id, $srcMtime) | ||||
|     { | ||||
|         $file = $this->_path . '/' . $id; | ||||
|         return (is_file($file) && (filemtime($file) >= $srcMtime)); | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Send the cached content to output | ||||
|      * | ||||
|      * @param string $id cache id (e.g. a filename) | ||||
|      */ | ||||
|     public function display($id) | ||||
|     { | ||||
|         if ($this->_locking) { | ||||
|             $fp = fopen($this->_path . '/' . $id, 'rb'); | ||||
|             flock($fp, LOCK_SH); | ||||
|             fpassthru($fp); | ||||
|             flock($fp, LOCK_UN); | ||||
|             fclose($fp); | ||||
|         } else { | ||||
|             readfile($this->_path . '/' . $id);             | ||||
|         } | ||||
|     } | ||||
|      | ||||
| 	/** | ||||
|      * Fetch the cached content | ||||
|      * | ||||
|      * @param string $id cache id (e.g. a filename) | ||||
|      *  | ||||
|      * @return string | ||||
|      */ | ||||
|     public function fetch($id) | ||||
|     { | ||||
|         if ($this->_locking) { | ||||
|             $fp = fopen($this->_path . '/' . $id, 'rb'); | ||||
|             if (!$fp) { | ||||
|                 return false; | ||||
|             } | ||||
|             flock($fp, LOCK_SH); | ||||
|             $ret = stream_get_contents($fp); | ||||
|             flock($fp, LOCK_UN); | ||||
|             fclose($fp); | ||||
|             return $ret; | ||||
|         } else { | ||||
|             return file_get_contents($this->_path . '/' . $id); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Fetch the cache path used | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getPath() | ||||
|     { | ||||
|         return $this->_path; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get a usable temp directory | ||||
|      * | ||||
|      * Adapted from Solar/Dir.php | ||||
|      * @author Paul M. Jones <pmjones@solarphp.com> | ||||
|      * @license http://opensource.org/licenses/bsd-license.php BSD | ||||
|      * @link http://solarphp.com/trac/core/browser/trunk/Solar/Dir.php | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public static function tmp() | ||||
|     { | ||||
|         static $tmp = null; | ||||
|         if (! $tmp) { | ||||
|             $tmp = function_exists('sys_get_temp_dir') | ||||
|                 ? sys_get_temp_dir() | ||||
|                 : self::_tmp(); | ||||
|             $tmp = rtrim($tmp, DIRECTORY_SEPARATOR); | ||||
|         } | ||||
|         return $tmp; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns the OS-specific directory for temporary files | ||||
|      * | ||||
|      * @author Paul M. Jones <pmjones@solarphp.com> | ||||
|      * @license http://opensource.org/licenses/bsd-license.php BSD | ||||
|      * @link http://solarphp.com/trac/core/browser/trunk/Solar/Dir.php | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     protected static function _tmp() | ||||
|     { | ||||
|         // non-Windows system? | ||||
|         if (strtolower(substr(PHP_OS, 0, 3)) != 'win') { | ||||
|             $tmp = empty($_ENV['TMPDIR']) ? getenv('TMPDIR') : $_ENV['TMPDIR']; | ||||
|             if ($tmp) { | ||||
|                 return $tmp; | ||||
|             } else { | ||||
|                 return '/tmp'; | ||||
|             } | ||||
|         } | ||||
|         // Windows 'TEMP' | ||||
|         $tmp = empty($_ENV['TEMP']) ? getenv('TEMP') : $_ENV['TEMP']; | ||||
|         if ($tmp) { | ||||
|             return $tmp; | ||||
|         } | ||||
|         // Windows 'TMP' | ||||
|         $tmp = empty($_ENV['TMP']) ? getenv('TMP') : $_ENV['TMP']; | ||||
|         if ($tmp) { | ||||
|             return $tmp; | ||||
|         } | ||||
|         // Windows 'windir' | ||||
|         $tmp = empty($_ENV['windir']) ? getenv('windir') : $_ENV['windir']; | ||||
|         if ($tmp) { | ||||
|             return $tmp; | ||||
|         } | ||||
|         // final fallback for Windows | ||||
|         return getenv('SystemRoot') . '\\temp'; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Send message to the Minify logger | ||||
|      * @param string $msg | ||||
|      * @return null | ||||
|      */ | ||||
|     protected function _log($msg) | ||||
|     { | ||||
|         Minify_Logger::log($msg); | ||||
|     } | ||||
|      | ||||
|     private $_path = null; | ||||
|     private $_locking = null; | ||||
| } | ||||
							
								
								
									
										140
									
								
								vendor/mrclay/minify/min/lib/Minify/Cache/Memcache.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								vendor/mrclay/minify/min/lib/Minify/Cache/Memcache.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,140 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Class Minify_Cache_Memcache | ||||
|  * @package Minify | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * Memcache-based cache class for Minify | ||||
|  *  | ||||
|  * <code> | ||||
|  * // fall back to disk caching if memcache can't connect | ||||
|  * $memcache = new Memcache; | ||||
|  * if ($memcache->connect('localhost', 11211)) { | ||||
|  *     Minify::setCache(new Minify_Cache_Memcache($memcache)); | ||||
|  * } else { | ||||
|  *     Minify::setCache(); | ||||
|  * } | ||||
|  * </code> | ||||
|  **/ | ||||
| class Minify_Cache_Memcache { | ||||
|      | ||||
|     /** | ||||
|      * Create a Minify_Cache_Memcache object, to be passed to  | ||||
|      * Minify::setCache(). | ||||
|      * | ||||
|      * @param Memcache $memcache already-connected instance | ||||
|      *  | ||||
|      * @param int $expire seconds until expiration (default = 0 | ||||
|      * meaning the item will not get an expiration date) | ||||
|      *  | ||||
|      * @return null | ||||
|      */ | ||||
|     public function __construct($memcache, $expire = 0) | ||||
|     { | ||||
|         $this->_mc = $memcache; | ||||
|         $this->_exp = $expire; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Write data to cache. | ||||
|      * | ||||
|      * @param string $id cache id | ||||
|      *  | ||||
|      * @param string $data | ||||
|      *  | ||||
|      * @return bool success | ||||
|      */ | ||||
|     public function store($id, $data) | ||||
|     { | ||||
|         return $this->_mc->set($id, "{$_SERVER['REQUEST_TIME']}|{$data}", 0, $this->_exp); | ||||
|     } | ||||
|      | ||||
|      | ||||
|     /** | ||||
|      * Get the size of a cache entry | ||||
|      * | ||||
|      * @param string $id cache id | ||||
|      *  | ||||
|      * @return int size in bytes | ||||
|      */ | ||||
|     public function getSize($id) | ||||
|     { | ||||
|         if (! $this->_fetch($id)) { | ||||
|             return false; | ||||
|         } | ||||
|         return (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) | ||||
|             ? mb_strlen($this->_data, '8bit') | ||||
|             : strlen($this->_data); | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Does a valid cache entry exist? | ||||
|      * | ||||
|      * @param string $id cache id | ||||
|      *  | ||||
|      * @param int $srcMtime mtime of the original source file(s) | ||||
|      *  | ||||
|      * @return bool exists | ||||
|      */ | ||||
|     public function isValid($id, $srcMtime) | ||||
|     { | ||||
|         return ($this->_fetch($id) && ($this->_lm >= $srcMtime)); | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Send the cached content to output | ||||
|      * | ||||
|      * @param string $id cache id | ||||
|      */ | ||||
|     public function display($id) | ||||
|     { | ||||
|         echo $this->_fetch($id) | ||||
|             ? $this->_data | ||||
|             : ''; | ||||
|     } | ||||
|      | ||||
| 	/** | ||||
|      * Fetch the cached content | ||||
|      * | ||||
|      * @param string $id cache id | ||||
|      *  | ||||
|      * @return string | ||||
|      */ | ||||
|     public function fetch($id) | ||||
|     { | ||||
|         return $this->_fetch($id) | ||||
|             ? $this->_data | ||||
|             : ''; | ||||
|     } | ||||
|      | ||||
|     private $_mc = null; | ||||
|     private $_exp = null; | ||||
|      | ||||
|     // cache of most recently fetched id | ||||
|     private $_lm = null; | ||||
|     private $_data = null; | ||||
|     private $_id = null; | ||||
|      | ||||
| 	/** | ||||
|      * Fetch data and timestamp from memcache, store in instance | ||||
|      *  | ||||
|      * @param string $id | ||||
|      *  | ||||
|      * @return bool success | ||||
|      */ | ||||
|     private function _fetch($id) | ||||
|     { | ||||
|         if ($this->_id === $id) { | ||||
|             return true; | ||||
|         } | ||||
|         $ret = $this->_mc->get($id); | ||||
|         if (false === $ret) { | ||||
|             $this->_id = null; | ||||
|             return false; | ||||
|         } | ||||
|         list($this->_lm, $this->_data) = explode('|', $ret, 2); | ||||
|         $this->_id = $id; | ||||
|         return true; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										126
									
								
								vendor/mrclay/minify/min/lib/Minify/Cache/XCache.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								vendor/mrclay/minify/min/lib/Minify/Cache/XCache.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,126 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Class Minify_Cache_XCache | ||||
|  * | ||||
|  * @link http://xcache.lighttpd.net/ | ||||
|  * @package Minify | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * XCache-based cache class for Minify | ||||
|  * {@see http://xcache.lighttpd.net/wiki/XcacheApi XCache API} | ||||
|  * | ||||
|  * <code> | ||||
|  * Minify::setCache(new Minify_Cache_XCache()); | ||||
|  * </code> | ||||
|  * | ||||
|  * @package Minify | ||||
|  * @author Elan Ruusamäe <glen@delfi.ee> | ||||
|  **/ | ||||
| class Minify_Cache_XCache { | ||||
|  | ||||
|     /** | ||||
|      * Create a Minify_Cache_XCache object, to be passed to | ||||
|      * Minify::setCache(). | ||||
|      * | ||||
|      * @param int $expire seconds until expiration (default = 0 | ||||
|      * meaning the item will not get an expiration date) | ||||
|      */ | ||||
|     public function __construct($expire = 0) | ||||
|     { | ||||
|         $this->_exp = $expire; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Write data to cache. | ||||
|      * | ||||
|      * @param string $id cache id | ||||
|      * @param string $data | ||||
|      * @return bool success | ||||
|      */ | ||||
|     public function store($id, $data) | ||||
|     { | ||||
|         return xcache_set($id, "{$_SERVER['REQUEST_TIME']}|{$data}", $this->_exp); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the size of a cache entry | ||||
|      * | ||||
|      * @param string $id cache id | ||||
|      * @return int size in bytes | ||||
|      */ | ||||
|     public function getSize($id) | ||||
|     { | ||||
|         if (! $this->_fetch($id)) { | ||||
|             return false; | ||||
|         } | ||||
|         return (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) | ||||
|             ? mb_strlen($this->_data, '8bit') | ||||
|             : strlen($this->_data); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Does a valid cache entry exist? | ||||
|      * | ||||
|      * @param string $id cache id | ||||
|      * @param int $srcMtime mtime of the original source file(s) | ||||
|      * @return bool exists | ||||
|      */ | ||||
|     public function isValid($id, $srcMtime) | ||||
|     { | ||||
|         return ($this->_fetch($id) && ($this->_lm >= $srcMtime)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Send the cached content to output | ||||
|      * | ||||
|      * @param string $id cache id | ||||
|      */ | ||||
|     public function display($id) | ||||
|     { | ||||
|         echo $this->_fetch($id) | ||||
|             ? $this->_data | ||||
|             : ''; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Fetch the cached content | ||||
|      * | ||||
|      * @param string $id cache id | ||||
|      * @return string | ||||
|      */ | ||||
|     public function fetch($id) | ||||
|     { | ||||
|         return $this->_fetch($id) | ||||
|             ? $this->_data | ||||
|             : ''; | ||||
|     } | ||||
|  | ||||
|     private $_exp = null; | ||||
|  | ||||
|     // cache of most recently fetched id | ||||
|     private $_lm = null; | ||||
|     private $_data = null; | ||||
|     private $_id = null; | ||||
|  | ||||
|     /** | ||||
|      * Fetch data and timestamp from xcache, store in instance | ||||
|      * | ||||
|      * @param string $id | ||||
|      * @return bool success | ||||
|      */ | ||||
|     private function _fetch($id) | ||||
|     { | ||||
|         if ($this->_id === $id) { | ||||
|             return true; | ||||
|         } | ||||
|         $ret = xcache_get($id); | ||||
|         if (false === $ret) { | ||||
|             $this->_id = null; | ||||
|             return false; | ||||
|         } | ||||
|         list($this->_lm, $this->_data) = explode('|', $ret, 2); | ||||
|         $this->_id = $id; | ||||
|         return true; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										142
									
								
								vendor/mrclay/minify/min/lib/Minify/Cache/ZendPlatform.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								vendor/mrclay/minify/min/lib/Minify/Cache/ZendPlatform.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,142 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Class Minify_Cache_ZendPlatform | ||||
|  * @package Minify | ||||
|  */ | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * ZendPlatform-based cache class for Minify | ||||
|  * | ||||
|  * Based on Minify_Cache_APC, uses output_cache_get/put (currently deprecated) | ||||
|  *  | ||||
|  * <code> | ||||
|  * Minify::setCache(new Minify_Cache_ZendPlatform()); | ||||
|  * </code> | ||||
|  * | ||||
|  * @package Minify | ||||
|  * @author Patrick van Dissel | ||||
|  */ | ||||
| class Minify_Cache_ZendPlatform { | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Create a Minify_Cache_ZendPlatform object, to be passed to | ||||
|      * Minify::setCache(). | ||||
|      * | ||||
|      * @param int $expire seconds until expiration (default = 0 | ||||
|      * meaning the item will not get an expiration date) | ||||
|      * | ||||
|      * @return null | ||||
|      */ | ||||
|     public function __construct($expire = 0) | ||||
|     { | ||||
|         $this->_exp = $expire; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Write data to cache. | ||||
|      * | ||||
|      * @param string $id cache id | ||||
|      * | ||||
|      * @param string $data | ||||
|      * | ||||
|      * @return bool success | ||||
|      */ | ||||
|     public function store($id, $data) | ||||
|     { | ||||
|         return output_cache_put($id, "{$_SERVER['REQUEST_TIME']}|{$data}"); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Get the size of a cache entry | ||||
|      * | ||||
|      * @param string $id cache id | ||||
|      * | ||||
|      * @return int size in bytes | ||||
|      */ | ||||
|     public function getSize($id) | ||||
|     { | ||||
|         return $this->_fetch($id) | ||||
|             ? strlen($this->_data) | ||||
|             : false; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Does a valid cache entry exist? | ||||
|      * | ||||
|      * @param string $id cache id | ||||
|      * | ||||
|      * @param int $srcMtime mtime of the original source file(s) | ||||
|      * | ||||
|      * @return bool exists | ||||
|      */ | ||||
|     public function isValid($id, $srcMtime) | ||||
|     { | ||||
|         $ret = ($this->_fetch($id) && ($this->_lm >= $srcMtime)); | ||||
|         return $ret; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Send the cached content to output | ||||
|      * | ||||
|      * @param string $id cache id | ||||
|      */ | ||||
|     public function display($id) | ||||
|     { | ||||
|         echo $this->_fetch($id) | ||||
|             ? $this->_data | ||||
|             : ''; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Fetch the cached content | ||||
|      * | ||||
|      * @param string $id cache id | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public function fetch($id) | ||||
|     { | ||||
|         return $this->_fetch($id) | ||||
|             ? $this->_data | ||||
|             : ''; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     private $_exp = null; | ||||
|  | ||||
|  | ||||
|     // cache of most recently fetched id | ||||
|     private $_lm = null; | ||||
|     private $_data = null; | ||||
|     private $_id = null; | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Fetch data and timestamp from ZendPlatform, store in instance | ||||
|      * | ||||
|      * @param string $id | ||||
|      * | ||||
|      * @return bool success | ||||
|      */ | ||||
|     private function _fetch($id) | ||||
|     { | ||||
|         if ($this->_id === $id) { | ||||
|             return true; | ||||
|         } | ||||
|         $ret = output_cache_get($id, $this->_exp); | ||||
|         if (false === $ret) { | ||||
|             $this->_id = null; | ||||
|             return false; | ||||
|         } | ||||
|         list($this->_lm, $this->_data) = explode('|', $ret, 2); | ||||
|         $this->_id = $id; | ||||
|         return true; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										139
									
								
								vendor/mrclay/minify/min/lib/Minify/ClosureCompiler.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								vendor/mrclay/minify/min/lib/Minify/ClosureCompiler.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,139 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Class Minify_ClosureCompiler | ||||
|  * @package Minify | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * Compress Javascript using the Closure Compiler | ||||
|  * | ||||
|  * You must set $jarFile and $tempDir before calling the minify functions. | ||||
|  * Also, depending on your shell's environment, you may need to specify | ||||
|  * the full path to java in $javaExecutable or use putenv() to setup the | ||||
|  * Java environment. | ||||
|  * | ||||
|  * <code> | ||||
|  * Minify_ClosureCompiler::$jarFile = '/path/to/closure-compiler-20120123.jar'; | ||||
|  * Minify_ClosureCompiler::$tempDir = '/tmp'; | ||||
|  * $code = Minify_ClosureCompiler::minify( | ||||
|  *   $code, | ||||
|  *   array('compilation_level' => 'SIMPLE_OPTIMIZATIONS') | ||||
|  * ); | ||||
|  * | ||||
|  * --compilation_level WHITESPACE_ONLY, SIMPLE_OPTIMIZATIONS, ADVANCED_OPTIMIZATIONS | ||||
|  * | ||||
|  * </code> | ||||
|  * | ||||
|  * @todo unit tests, $options docs | ||||
|  * @todo more options support (or should just passthru them all?) | ||||
|  * | ||||
|  * @package Minify | ||||
|  * @author Stephen Clay <steve@mrclay.org> | ||||
|  * @author Elan Ruusamäe <glen@delfi.ee> | ||||
|  */ | ||||
| class Minify_ClosureCompiler { | ||||
|  | ||||
|     const OPTION_CHARSET = 'charset'; | ||||
|     const OPTION_COMPILATION_LEVEL = 'compilation_level'; | ||||
|  | ||||
|     public static $isDebug = false; | ||||
|  | ||||
|     /** | ||||
|      * Filepath of the Closure Compiler jar file. This must be set before | ||||
|      * calling minifyJs(). | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     public static $jarFile = null; | ||||
|  | ||||
|     /** | ||||
|      * Writable temp directory. This must be set before calling minifyJs(). | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     public static $tempDir = null; | ||||
|  | ||||
|     /** | ||||
|      * Filepath of "java" executable (may be needed if not in shell's PATH) | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     public static $javaExecutable = 'java'; | ||||
|  | ||||
|     /** | ||||
|      * Minify a Javascript string | ||||
|      * | ||||
|      * @param string $js | ||||
|      * | ||||
|      * @param array $options (verbose is ignored) | ||||
|      * | ||||
|      * @see https://code.google.com/p/closure-compiler/source/browse/trunk/README | ||||
|      * | ||||
|      * @return string | ||||
|      * | ||||
|      * @throws Minify_ClosureCompiler_Exception | ||||
|      */ | ||||
|     public static function minify($js, $options = array()) | ||||
|     { | ||||
|         self::_prepare(); | ||||
|         if (! ($tmpFile = tempnam(self::$tempDir, 'cc_'))) { | ||||
|             throw new Minify_ClosureCompiler_Exception('Minify_ClosureCompiler : could not create temp file in "'.self::$tempDir.'".'); | ||||
|         } | ||||
|         file_put_contents($tmpFile, $js); | ||||
|         $cmd = self::_getCmd($options, $tmpFile); | ||||
|         exec($cmd, $output, $result_code); | ||||
|         unlink($tmpFile); | ||||
|         if ($result_code != 0) { | ||||
|             $message = 'Minify_ClosureCompiler : Closure Compiler execution failed.'; | ||||
|             if (self::$isDebug) {  | ||||
|                 exec($cmd . ' 2>&1', $error); | ||||
|                 if ($error) { | ||||
|                     $message .= "\nReason:\n" . join("\n", $error); | ||||
|                 } | ||||
|             }  | ||||
|             throw new Minify_ClosureCompiler_Exception($message); | ||||
|         } | ||||
|         return implode("\n", $output); | ||||
|     } | ||||
|  | ||||
|     private static function _getCmd($userOptions, $tmpFile) | ||||
|     { | ||||
|         $o = array_merge( | ||||
|             array( | ||||
|                 self::OPTION_CHARSET => 'utf-8', | ||||
|                 self::OPTION_COMPILATION_LEVEL => 'SIMPLE_OPTIMIZATIONS', | ||||
|             ), | ||||
|             $userOptions | ||||
|         ); | ||||
|         $charsetOption = $o[self::OPTION_CHARSET]; | ||||
|         $cmd = self::$javaExecutable . ' -jar ' . escapeshellarg(self::$jarFile) | ||||
|              . (preg_match('/^[\\da-zA-Z0-9\\-]+$/', $charsetOption) | ||||
|                 ? " --charset {$charsetOption}" | ||||
|                 : ''); | ||||
|  | ||||
|         foreach (array(self::OPTION_COMPILATION_LEVEL) as $opt) { | ||||
|             if ($o[$opt]) { | ||||
|                 $cmd .= " --{$opt} ". escapeshellarg($o[$opt]); | ||||
|             } | ||||
|         } | ||||
|         return $cmd . ' ' . escapeshellarg($tmpFile); | ||||
|     } | ||||
|  | ||||
|     private static function _prepare() | ||||
|     { | ||||
|         if (! is_file(self::$jarFile)) { | ||||
|             throw new Minify_ClosureCompiler_Exception('Minify_ClosureCompiler : $jarFile('.self::$jarFile.') is not a valid file.'); | ||||
|         } | ||||
|         if (! is_readable(self::$jarFile)) { | ||||
|             throw new Minify_ClosureCompiler_Exception('Minify_ClosureCompiler : $jarFile('.self::$jarFile.') is not readable.'); | ||||
|         } | ||||
|         if (! is_dir(self::$tempDir)) { | ||||
|             throw new Minify_ClosureCompiler_Exception('Minify_ClosureCompiler : $tempDir('.self::$tempDir.') is not a valid direcotry.'); | ||||
|         } | ||||
|         if (! is_writable(self::$tempDir)) { | ||||
|             throw new Minify_ClosureCompiler_Exception('Minify_ClosureCompiler : $tempDir('.self::$tempDir.') is not writable.'); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| class Minify_ClosureCompiler_Exception extends Exception {} | ||||
							
								
								
									
										89
									
								
								vendor/mrclay/minify/min/lib/Minify/CommentPreserver.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								vendor/mrclay/minify/min/lib/Minify/CommentPreserver.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,89 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Class Minify_CommentPreserver  | ||||
|  * @package Minify | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * Process a string in pieces preserving C-style comments that begin with "/*!" | ||||
|  *  | ||||
|  * @package Minify | ||||
|  * @author Stephen Clay <steve@mrclay.org> | ||||
|  */ | ||||
| class Minify_CommentPreserver { | ||||
|      | ||||
|     /** | ||||
|      * String to be prepended to each preserved comment | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     public static $prepend = "\n"; | ||||
|      | ||||
|     /** | ||||
|      * String to be appended to each preserved comment | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     public static $append = "\n"; | ||||
|      | ||||
|     /** | ||||
|      * Process a string outside of C-style comments that begin with "/*!" | ||||
|      * | ||||
|      * On each non-empty string outside these comments, the given processor  | ||||
|      * function will be called. The comments will be surrounded by  | ||||
|      * Minify_CommentPreserver::$preprend and Minify_CommentPreserver::$append. | ||||
|      *  | ||||
|      * @param string $content | ||||
|      * @param callback $processor function | ||||
|      * @param array $args array of extra arguments to pass to the processor  | ||||
|      * function (default = array()) | ||||
|      * @return string | ||||
|      */ | ||||
|     public static function process($content, $processor, $args = array()) | ||||
|     { | ||||
|         $ret = ''; | ||||
|         while (true) { | ||||
|             list($beforeComment, $comment, $afterComment) = self::_nextComment($content); | ||||
|             if ('' !== $beforeComment) { | ||||
|                 $callArgs = $args; | ||||
|                 array_unshift($callArgs, $beforeComment); | ||||
|                 $ret .= call_user_func_array($processor, $callArgs);     | ||||
|             } | ||||
|             if (false === $comment) { | ||||
|                 break; | ||||
|             } | ||||
|             $ret .= $comment; | ||||
|             $content = $afterComment; | ||||
|         } | ||||
|         return $ret; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Extract comments that YUI Compressor preserves. | ||||
|      *  | ||||
|      * @param string $in input | ||||
|      *  | ||||
|      * @return array 3 elements are returned. If a YUI comment is found, the | ||||
|      * 2nd element is the comment and the 1st and 3rd are the surrounding | ||||
|      * strings. If no comment is found, the entire string is returned as the  | ||||
|      * 1st element and the other two are false. | ||||
|      */ | ||||
|     private static function _nextComment($in) | ||||
|     { | ||||
|         if ( | ||||
|             false === ($start = strpos($in, '/*!')) | ||||
|             || false === ($end = strpos($in, '*/', $start + 3)) | ||||
|         ) { | ||||
|             return array($in, false, false); | ||||
|         } | ||||
|         $ret = array( | ||||
|             substr($in, 0, $start) | ||||
|             ,self::$prepend . '/*!' . substr($in, $start + 3, $end - $start - 1) . self::$append | ||||
|         ); | ||||
|         $endChars = (strlen($in) - $end - 2); | ||||
|         $ret[] = (0 === $endChars) | ||||
|             ? '' | ||||
|             : substr($in, -$endChars); | ||||
|         return $ret; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										222
									
								
								vendor/mrclay/minify/min/lib/Minify/Controller/Base.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										222
									
								
								vendor/mrclay/minify/min/lib/Minify/Controller/Base.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,222 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Class Minify_Controller_Base   | ||||
|  * @package Minify | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * Base class for Minify controller | ||||
|  *  | ||||
|  * The controller class validates a request and uses it to create sources | ||||
|  * for minification and set options like contentType. It's also responsible | ||||
|  * for loading minifier code upon request. | ||||
|  *  | ||||
|  * @package Minify | ||||
|  * @author Stephen Clay <steve@mrclay.org> | ||||
|  */ | ||||
| abstract class Minify_Controller_Base { | ||||
|      | ||||
|     /** | ||||
|      * Setup controller sources and set an needed options for Minify::source | ||||
|      *  | ||||
|      * You must override this method in your subclass controller to set  | ||||
|      * $this->sources. If the request is NOT valid, make sure $this->sources  | ||||
|      * is left an empty array. Then strip any controller-specific options from  | ||||
|      * $options and return it. To serve files, $this->sources must be an array of | ||||
|      * Minify_Source objects. | ||||
|      *  | ||||
|      * @param array $options controller and Minify options | ||||
|      *  | ||||
|      * @return array $options Minify::serve options | ||||
|      */ | ||||
|     abstract public function setupSources($options); | ||||
|      | ||||
|     /** | ||||
|      * Get default Minify options for this controller. | ||||
|      *  | ||||
|      * Override in subclass to change defaults | ||||
|      * | ||||
|      * @return array options for Minify | ||||
|      */ | ||||
|     public function getDefaultMinifyOptions() { | ||||
|         return array( | ||||
|             'isPublic' => true | ||||
|             ,'encodeOutput' => function_exists('gzdeflate') | ||||
|             ,'encodeMethod' => null // determine later | ||||
|             ,'encodeLevel' => 9 | ||||
|             ,'minifierOptions' => array() // no minifier options | ||||
|             ,'contentTypeCharset' => 'utf-8' | ||||
|             ,'maxAge' => 1800 // 30 minutes | ||||
|             ,'rewriteCssUris' => true | ||||
|             ,'bubbleCssImports' => false | ||||
|             ,'quiet' => false // serve() will send headers and output | ||||
|             ,'debug' => false | ||||
|              | ||||
|             // if you override these, the response codes MUST be directly after | ||||
|             // the first space. | ||||
|             ,'badRequestHeader' => 'HTTP/1.0 400 Bad Request' | ||||
|             ,'errorHeader'      => 'HTTP/1.0 500 Internal Server Error' | ||||
|              | ||||
|             // callback function to see/modify content of all sources | ||||
|             ,'postprocessor' => null | ||||
|             // file to require to load preprocessor | ||||
|             ,'postprocessorRequire' => null | ||||
|         ); | ||||
|     }   | ||||
|  | ||||
|     /** | ||||
|      * Get default minifiers for this controller. | ||||
|      *  | ||||
|      * Override in subclass to change defaults | ||||
|      * | ||||
|      * @return array minifier callbacks for common types | ||||
|      */ | ||||
|     public function getDefaultMinifers() { | ||||
|         $ret[Minify::TYPE_JS] = array('JSMin', 'minify'); | ||||
|         $ret[Minify::TYPE_CSS] = array('Minify_CSS', 'minify'); | ||||
|         $ret[Minify::TYPE_HTML] = array('Minify_HTML', 'minify'); | ||||
|         return $ret; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Is a user-given file within an allowable directory, existing, | ||||
|      * and having an extension js/css/html/txt ? | ||||
|      *  | ||||
|      * This is a convenience function for controllers that have to accept | ||||
|      * user-given paths | ||||
|      * | ||||
|      * @param string $file full file path (already processed by realpath()) | ||||
|      *  | ||||
|      * @param array $safeDirs directories where files are safe to serve. Files can also | ||||
|      * be in subdirectories of these directories. | ||||
|      *  | ||||
|      * @return bool file is safe | ||||
|      * | ||||
|      * @deprecated use checkAllowDirs, checkNotHidden instead | ||||
|      */ | ||||
|     public static function _fileIsSafe($file, $safeDirs) | ||||
|     { | ||||
|         $pathOk = false; | ||||
|         foreach ((array)$safeDirs as $safeDir) { | ||||
|             if (strpos($file, $safeDir) === 0) { | ||||
|                 $pathOk = true; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         $base = basename($file); | ||||
|         if (! $pathOk || ! is_file($file) || $base[0] === '.') { | ||||
|             return false; | ||||
|         } | ||||
|         list($revExt) = explode('.', strrev($base)); | ||||
|         return in_array(strrev($revExt), array('js', 'css', 'html', 'txt')); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $file | ||||
|      * @param array $allowDirs | ||||
|      * @param string $uri | ||||
|      * @return bool | ||||
|      * @throws Exception | ||||
|      */ | ||||
|     public static function checkAllowDirs($file, $allowDirs, $uri) | ||||
|     { | ||||
|         foreach ((array)$allowDirs as $allowDir) { | ||||
|             if (strpos($file, $allowDir) === 0) { | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|         throw new Exception("File '$file' is outside \$allowDirs. If the path is" | ||||
|             . " resolved via an alias/symlink, look into the \$min_symlinks option." | ||||
|             . " E.g. \$min_symlinks['/" . dirname($uri) . "'] = '" . dirname($file) . "';"); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $file | ||||
|      * @throws Exception | ||||
|      */ | ||||
|     public static function checkNotHidden($file) | ||||
|     { | ||||
|         $b = basename($file); | ||||
|         if (0 === strpos($b, '.')) { | ||||
|             throw new Exception("Filename '$b' starts with period (may be hidden)"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * instances of Minify_Source, which provide content and any individual minification needs. | ||||
|      * | ||||
|      * @var array | ||||
|      *  | ||||
|      * @see Minify_Source | ||||
|      */ | ||||
|     public $sources = array(); | ||||
|      | ||||
|     /** | ||||
|      * Short name to place inside cache id | ||||
|      * | ||||
|      * The setupSources() method may choose to set this, making it easier to | ||||
|      * recognize a particular set of sources/settings in the cache folder. It | ||||
|      * will be filtered and truncated to make the final cache id <= 250 bytes. | ||||
|      *  | ||||
|      * @var string | ||||
|      */ | ||||
|     public $selectionId = ''; | ||||
|  | ||||
|     /** | ||||
|      * Mix in default controller options with user-given options | ||||
|      *  | ||||
|      * @param array $options user options | ||||
|      *  | ||||
|      * @return array mixed options | ||||
|      */ | ||||
|     public final function mixInDefaultOptions($options) | ||||
|     { | ||||
|         $ret = array_merge( | ||||
|             $this->getDefaultMinifyOptions(), $options | ||||
|         ); | ||||
|         if (! isset($options['minifiers'])) { | ||||
|             $options['minifiers'] = array(); | ||||
|         } | ||||
|         $ret['minifiers'] = array_merge( | ||||
|             $this->getDefaultMinifers(), $options['minifiers'] | ||||
|         ); | ||||
|         return $ret; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Analyze sources (if there are any) and set $options 'contentType'  | ||||
|      * and 'lastModifiedTime' if they already aren't. | ||||
|      *  | ||||
|      * @param array $options options for Minify | ||||
|      *  | ||||
|      * @return array options for Minify | ||||
|      */ | ||||
|     public final function analyzeSources($options = array())  | ||||
|     { | ||||
|         if ($this->sources) { | ||||
|             if (! isset($options['contentType'])) { | ||||
|                 $options['contentType'] = Minify_Source::getContentType($this->sources); | ||||
|             } | ||||
|             // last modified is needed for caching, even if setExpires is set | ||||
|             if (! isset($options['lastModifiedTime'])) { | ||||
|                 $max = 0; | ||||
|                 foreach ($this->sources as $source) { | ||||
|                     $max = max($source->lastModified, $max); | ||||
|                 } | ||||
|                 $options['lastModifiedTime'] = $max; | ||||
|             }     | ||||
|         } | ||||
|         return $options; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Send message to the Minify logger | ||||
|      * | ||||
|      * @param string $msg | ||||
|      * | ||||
|      * @return null | ||||
|      */ | ||||
|     public function log($msg) { | ||||
|         Minify_Logger::log($msg); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										76
									
								
								vendor/mrclay/minify/min/lib/Minify/Controller/Files.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								vendor/mrclay/minify/min/lib/Minify/Controller/Files.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,76 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Class Minify_Controller_Files   | ||||
|  * @package Minify | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * Controller class for minifying a set of files | ||||
|  *  | ||||
|  * E.g. the following would serve the minified Javascript for a site | ||||
|  * <code> | ||||
|  * Minify::serve('Files', array( | ||||
|  *     'files' => array( | ||||
|  *         '//js/jquery.js' | ||||
|  *         ,'//js/plugins.js' | ||||
|  *         ,'/home/username/file.js' | ||||
|  *     ) | ||||
|  * )); | ||||
|  * </code> | ||||
|  *  | ||||
|  * As a shortcut, the controller will replace "//" at the beginning | ||||
|  * of a filename with $_SERVER['DOCUMENT_ROOT'] . '/'. | ||||
|  * | ||||
|  * @package Minify | ||||
|  * @author Stephen Clay <steve@mrclay.org> | ||||
|  */ | ||||
| class Minify_Controller_Files extends Minify_Controller_Base { | ||||
|      | ||||
|     /** | ||||
|      * Set up file sources | ||||
|      *  | ||||
|      * @param array $options controller and Minify options | ||||
|      * @return array Minify options | ||||
|      *  | ||||
|      * Controller options: | ||||
|      *  | ||||
|      * 'files': (required) array of complete file paths, or a single path | ||||
|      */ | ||||
|     public function setupSources($options) { | ||||
|         // strip controller options | ||||
|          | ||||
|         $files = $options['files']; | ||||
|         // if $files is a single object, casting will break it | ||||
|         if (is_object($files)) { | ||||
|             $files = array($files); | ||||
|         } elseif (! is_array($files)) { | ||||
|             $files = (array)$files; | ||||
|         } | ||||
|         unset($options['files']); | ||||
|          | ||||
|         $sources = array(); | ||||
|         foreach ($files as $file) { | ||||
|             if ($file instanceof Minify_Source) { | ||||
|                 $sources[] = $file; | ||||
|                 continue; | ||||
|             } | ||||
|             if (0 === strpos($file, '//')) { | ||||
|                 $file = $_SERVER['DOCUMENT_ROOT'] . substr($file, 1); | ||||
|             } | ||||
|             $realPath = realpath($file); | ||||
|             if (is_file($realPath)) { | ||||
|                 $sources[] = new Minify_Source(array( | ||||
|                     'filepath' => $realPath | ||||
|                 ));     | ||||
|             } else { | ||||
|                 $this->log("The path \"{$file}\" could not be found (or was not a file)"); | ||||
|                 return $options; | ||||
|             } | ||||
|         } | ||||
|         if ($sources) { | ||||
|             $this->sources = $sources; | ||||
|         } | ||||
|         return $options; | ||||
|     } | ||||
| } | ||||
|  | ||||
							
								
								
									
										91
									
								
								vendor/mrclay/minify/min/lib/Minify/Controller/Groups.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								vendor/mrclay/minify/min/lib/Minify/Controller/Groups.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,91 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Class Minify_Controller_Groups   | ||||
|  * @package Minify | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * Controller class for serving predetermined groups of minimized sets, selected | ||||
|  * by PATH_INFO | ||||
|  *  | ||||
|  * <code> | ||||
|  * Minify::serve('Groups', array(  | ||||
|  *     'groups' => array( | ||||
|  *         'css' => array('//css/type.css', '//css/layout.css') | ||||
|  *        ,'js' => array('//js/jquery.js', '//js/site.js') | ||||
|  *     ) | ||||
|  * )); | ||||
|  * </code> | ||||
|  *  | ||||
|  * If the above code were placed in /serve.php, it would enable the URLs | ||||
|  * /serve.php/js and /serve.php/css | ||||
|  *  | ||||
|  * As a shortcut, the controller will replace "//" at the beginning | ||||
|  * of a filename with $_SERVER['DOCUMENT_ROOT'] . '/'. | ||||
|  *  | ||||
|  * @package Minify | ||||
|  * @author Stephen Clay <steve@mrclay.org> | ||||
|  */ | ||||
| class Minify_Controller_Groups extends Minify_Controller_Base { | ||||
|      | ||||
|     /** | ||||
|      * Set up groups of files as sources | ||||
|      *  | ||||
|      * @param array $options controller and Minify options | ||||
|      * | ||||
|      * 'groups': (required) array mapping PATH_INFO strings to arrays | ||||
|      * of complete file paths. @see Minify_Controller_Groups | ||||
|      * | ||||
|      * @return array Minify options | ||||
|      */ | ||||
|     public function setupSources($options) { | ||||
|         // strip controller options | ||||
|         $groups = $options['groups']; | ||||
|         unset($options['groups']); | ||||
|          | ||||
|         // mod_fcgid places PATH_INFO in ORIG_PATH_INFO | ||||
|         $pi = isset($_SERVER['ORIG_PATH_INFO']) | ||||
|             ? substr($_SERVER['ORIG_PATH_INFO'], 1)  | ||||
|             : (isset($_SERVER['PATH_INFO']) | ||||
|                 ? substr($_SERVER['PATH_INFO'], 1)  | ||||
|                 : false | ||||
|             ); | ||||
|         if (false === $pi || ! isset($groups[$pi])) { | ||||
|             // no PATH_INFO or not a valid group | ||||
|             $this->log("Missing PATH_INFO or no group set for \"$pi\""); | ||||
|             return $options; | ||||
|         } | ||||
|         $sources = array(); | ||||
|          | ||||
|         $files = $groups[$pi]; | ||||
|         // if $files is a single object, casting will break it | ||||
|         if (is_object($files)) { | ||||
|             $files = array($files); | ||||
|         } elseif (! is_array($files)) { | ||||
|             $files = (array)$files; | ||||
|         } | ||||
|         foreach ($files as $file) { | ||||
|             if ($file instanceof Minify_Source) { | ||||
|                 $sources[] = $file; | ||||
|                 continue; | ||||
|             } | ||||
|             if (0 === strpos($file, '//')) { | ||||
|                 $file = $_SERVER['DOCUMENT_ROOT'] . substr($file, 1); | ||||
|             } | ||||
|             $realPath = realpath($file); | ||||
|             if (is_file($realPath)) { | ||||
|                 $sources[] = new Minify_Source(array( | ||||
|                     'filepath' => $realPath | ||||
|                 ));     | ||||
|             } else { | ||||
|                 $this->log("The path \"{$file}\" could not be found (or was not a file)"); | ||||
|                 return $options; | ||||
|             } | ||||
|         } | ||||
|         if ($sources) { | ||||
|             $this->sources = $sources; | ||||
|         } | ||||
|         return $options; | ||||
|     } | ||||
| } | ||||
|  | ||||
							
								
								
									
										238
									
								
								vendor/mrclay/minify/min/lib/Minify/Controller/MinApp.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										238
									
								
								vendor/mrclay/minify/min/lib/Minify/Controller/MinApp.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,238 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Class Minify_Controller_MinApp   | ||||
|  * @package Minify | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * Controller class for requests to /min/index.php | ||||
|  *  | ||||
|  * @package Minify | ||||
|  * @author Stephen Clay <steve@mrclay.org> | ||||
|  */ | ||||
| class Minify_Controller_MinApp extends Minify_Controller_Base { | ||||
|      | ||||
|     /** | ||||
|      * Set up groups of files as sources | ||||
|      *  | ||||
|      * @param array $options controller and Minify options | ||||
|      * | ||||
|      * @return array Minify options | ||||
|      */ | ||||
|     public function setupSources($options) { | ||||
|         // PHP insecure by default: realpath() and other FS functions can't handle null bytes. | ||||
|         foreach (array('g', 'b', 'f') as $key) { | ||||
|             if (isset($_GET[$key])) { | ||||
|                 $_GET[$key] = str_replace("\x00", '', (string)$_GET[$key]); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // filter controller options | ||||
|         $cOptions = array_merge( | ||||
|             array( | ||||
|                 'allowDirs' => '//' | ||||
|                 ,'groupsOnly' => false | ||||
|                 ,'groups' => array() | ||||
|                 ,'noMinPattern' => '@[-\\.]min\\.(?:js|css)$@i' // matched against basename | ||||
|             ) | ||||
|             ,(isset($options['minApp']) ? $options['minApp'] : array()) | ||||
|         ); | ||||
|         unset($options['minApp']); | ||||
|         $sources = array(); | ||||
|         $this->selectionId = ''; | ||||
|         $firstMissingResource = null; | ||||
|         if (isset($_GET['g'])) { | ||||
|             // add group(s) | ||||
|             $this->selectionId .= 'g=' . $_GET['g']; | ||||
|             $keys = explode(',', $_GET['g']); | ||||
|             if ($keys != array_unique($keys)) { | ||||
|                 $this->log("Duplicate group key found."); | ||||
|                 return $options; | ||||
|             } | ||||
|             $keys = explode(',', $_GET['g']); | ||||
|             foreach ($keys as $key) { | ||||
|                 if (! isset($cOptions['groups'][$key])) { | ||||
|                     $this->log("A group configuration for \"{$key}\" was not found"); | ||||
|                     return $options; | ||||
|                 } | ||||
|                 $files = $cOptions['groups'][$key]; | ||||
|                 // if $files is a single object, casting will break it | ||||
|                 if (is_object($files)) { | ||||
|                     $files = array($files); | ||||
|                 } elseif (! is_array($files)) { | ||||
|                     $files = (array)$files; | ||||
|                 } | ||||
|                 foreach ($files as $file) { | ||||
|                     if ($file instanceof Minify_Source) { | ||||
|                         $sources[] = $file; | ||||
|                         continue; | ||||
|                     } | ||||
|                     if (0 === strpos($file, '//')) { | ||||
|                         $file = $_SERVER['DOCUMENT_ROOT'] . substr($file, 1); | ||||
|                     } | ||||
|                     $realpath = realpath($file); | ||||
|                     if ($realpath && is_file($realpath)) { | ||||
|                         $sources[] = $this->_getFileSource($realpath, $cOptions); | ||||
|                     } else { | ||||
|                         $this->log("The path \"{$file}\" (realpath \"{$realpath}\") could not be found (or was not a file)"); | ||||
|                         if (null === $firstMissingResource) { | ||||
|                             $firstMissingResource = basename($file); | ||||
|                             continue; | ||||
|                         } else { | ||||
|                             $secondMissingResource = basename($file); | ||||
|                             $this->log("More than one file was missing: '$firstMissingResource', '$secondMissingResource'"); | ||||
|                             return $options; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 if ($sources) { | ||||
|                     try { | ||||
|                         $this->checkType($sources[0]); | ||||
|                     } catch (Exception $e) { | ||||
|                         $this->log($e->getMessage()); | ||||
|                         return $options; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         if (! $cOptions['groupsOnly'] && isset($_GET['f'])) { | ||||
|             // try user files | ||||
|             // The following restrictions are to limit the URLs that minify will | ||||
|             // respond to. | ||||
|             if (// verify at least one file, files are single comma separated,  | ||||
|                 // and are all same extension | ||||
|                 ! preg_match('/^[^,]+\\.(css|js)(?:,[^,]+\\.\\1)*$/', $_GET['f'], $m) | ||||
|                 // no "//" | ||||
|                 || strpos($_GET['f'], '//') !== false | ||||
|                 // no "\" | ||||
|                 || strpos($_GET['f'], '\\') !== false | ||||
|             ) { | ||||
|                 $this->log("GET param 'f' was invalid"); | ||||
|                 return $options; | ||||
|             } | ||||
|             $ext = ".{$m[1]}"; | ||||
|             try { | ||||
|                 $this->checkType($m[1]); | ||||
|             } catch (Exception $e) { | ||||
|                 $this->log($e->getMessage()); | ||||
|                 return $options; | ||||
|             } | ||||
|             $files = explode(',', $_GET['f']); | ||||
|             if ($files != array_unique($files)) { | ||||
|                 $this->log("Duplicate files were specified"); | ||||
|                 return $options; | ||||
|             } | ||||
|             if (isset($_GET['b'])) { | ||||
|                 // check for validity | ||||
|                 if (preg_match('@^[^/]+(?:/[^/]+)*$@', $_GET['b']) | ||||
|                     && false === strpos($_GET['b'], '..') | ||||
|                     && $_GET['b'] !== '.') { | ||||
|                     // valid base | ||||
|                     $base = "/{$_GET['b']}/";        | ||||
|                 } else { | ||||
|                     $this->log("GET param 'b' was invalid"); | ||||
|                     return $options; | ||||
|                 } | ||||
|             } else { | ||||
|                 $base = '/'; | ||||
|             } | ||||
|             $allowDirs = array(); | ||||
|             foreach ((array)$cOptions['allowDirs'] as $allowDir) { | ||||
|                 $allowDirs[] = realpath(str_replace('//', $_SERVER['DOCUMENT_ROOT'] . '/', $allowDir)); | ||||
|             } | ||||
|             $basenames = array(); // just for cache id | ||||
|             foreach ($files as $file) { | ||||
|                 $uri = $base . $file; | ||||
|                 $path = $_SERVER['DOCUMENT_ROOT'] . $uri; | ||||
|                 $realpath = realpath($path); | ||||
|                 if (false === $realpath || ! is_file($realpath)) { | ||||
|                     $this->log("The path \"{$path}\" (realpath \"{$realpath}\") could not be found (or was not a file)"); | ||||
|                     if (null === $firstMissingResource) { | ||||
|                         $firstMissingResource = $uri; | ||||
|                         continue; | ||||
|                     } else { | ||||
|                         $secondMissingResource = $uri; | ||||
|                         $this->log("More than one file was missing: '$firstMissingResource', '$secondMissingResource`'"); | ||||
|                         return $options; | ||||
|                     } | ||||
|                 } | ||||
|                 try { | ||||
|                     parent::checkNotHidden($realpath); | ||||
|                     parent::checkAllowDirs($realpath, $allowDirs, $uri); | ||||
|                 } catch (Exception $e) { | ||||
|                     $this->log($e->getMessage()); | ||||
|                     return $options; | ||||
|                 } | ||||
|                 $sources[] = $this->_getFileSource($realpath, $cOptions); | ||||
|                 $basenames[] = basename($realpath, $ext); | ||||
|             } | ||||
|             if ($this->selectionId) { | ||||
|                 $this->selectionId .= '_f='; | ||||
|             } | ||||
|             $this->selectionId .= implode(',', $basenames) . $ext; | ||||
|         } | ||||
|         if ($sources) { | ||||
|             if (null !== $firstMissingResource) { | ||||
|                 array_unshift($sources, new Minify_Source(array( | ||||
|                     'id' => 'missingFile' | ||||
|                     // should not cause cache invalidation | ||||
|                     ,'lastModified' => 0 | ||||
|                     // due to caching, filename is unreliable. | ||||
|                     ,'content' => "/* Minify: at least one missing file. See " . Minify::URL_DEBUG . " */\n" | ||||
|                     ,'minifier' => '' | ||||
|                 ))); | ||||
|             } | ||||
|             $this->sources = $sources; | ||||
|         } else { | ||||
|             $this->log("No sources to serve"); | ||||
|         } | ||||
|         return $options; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $file | ||||
|      * | ||||
|      * @param array $cOptions | ||||
|      * | ||||
|      * @return Minify_Source | ||||
|      */ | ||||
|     protected function _getFileSource($file, $cOptions) | ||||
|     { | ||||
|         $spec['filepath'] = $file; | ||||
|         if ($cOptions['noMinPattern'] && preg_match($cOptions['noMinPattern'], basename($file))) { | ||||
|             if (preg_match('~\.css$~i', $file)) { | ||||
|                 $spec['minifyOptions']['compress'] = false; | ||||
|             } else { | ||||
|                 $spec['minifier'] = ''; | ||||
|             } | ||||
|         } | ||||
|         return new Minify_Source($spec); | ||||
|     } | ||||
|  | ||||
|     protected $_type = null; | ||||
|  | ||||
|     /** | ||||
|      * Make sure that only source files of a single type are registered | ||||
|      * | ||||
|      * @param string $sourceOrExt | ||||
|      * | ||||
|      * @throws Exception | ||||
|      */ | ||||
|     public function checkType($sourceOrExt) | ||||
|     { | ||||
|         if ($sourceOrExt === 'js') { | ||||
|             $type = Minify::TYPE_JS; | ||||
|         } elseif ($sourceOrExt === 'css') { | ||||
|             $type = Minify::TYPE_CSS; | ||||
|         } elseif ($sourceOrExt->contentType !== null) { | ||||
|             $type = $sourceOrExt->contentType; | ||||
|         } else { | ||||
|             return; | ||||
|         } | ||||
|         if ($this->_type === null) { | ||||
|             $this->_type = $type; | ||||
|         } elseif ($this->_type !== $type) { | ||||
|             throw new Exception('Content-Type mismatch'); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										68
									
								
								vendor/mrclay/minify/min/lib/Minify/Controller/Page.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								vendor/mrclay/minify/min/lib/Minify/Controller/Page.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Class Minify_Controller_Page   | ||||
|  * @package Minify | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * Controller class for serving a single HTML page | ||||
|  *  | ||||
|  * @link http://code.google.com/p/minify/source/browse/trunk/web/examples/1/index.php#59 | ||||
|  * @package Minify | ||||
|  * @author Stephen Clay <steve@mrclay.org> | ||||
|  */ | ||||
| class Minify_Controller_Page extends Minify_Controller_Base { | ||||
|      | ||||
|     /** | ||||
|      * Set up source of HTML content | ||||
|      *  | ||||
|      * @param array $options controller and Minify options | ||||
|      * @return array Minify options | ||||
|      *  | ||||
|      * Controller options: | ||||
|      *  | ||||
|      * 'content': (required) HTML markup | ||||
|      *  | ||||
|      * 'id': (required) id of page (string for use in server-side caching) | ||||
|      *  | ||||
|      * 'lastModifiedTime': timestamp of when this content changed. This | ||||
|      * is recommended to allow both server and client-side caching. | ||||
|      *  | ||||
|      * 'minifyAll': should all CSS and Javascript blocks be individually  | ||||
|      * minified? (default false)  | ||||
|      * | ||||
|      * @todo Add 'file' option to read HTML file. | ||||
|      */ | ||||
|     public function setupSources($options) { | ||||
|         if (isset($options['file'])) { | ||||
|             $sourceSpec = array( | ||||
|                 'filepath' => $options['file'] | ||||
|             ); | ||||
|             $f = $options['file']; | ||||
|         } else { | ||||
|             // strip controller options | ||||
|             $sourceSpec = array( | ||||
|                 'content' => $options['content'] | ||||
|                 ,'id' => $options['id'] | ||||
|             ); | ||||
|             $f = $options['id']; | ||||
|             unset($options['content'], $options['id']); | ||||
|         } | ||||
|         // something like "builder,index.php" or "directory,file.html" | ||||
|         $this->selectionId = strtr(substr($f, 1 + strlen(dirname(dirname($f)))), '/\\', ',,'); | ||||
|  | ||||
|         if (isset($options['minifyAll'])) { | ||||
|             // this will be the 2nd argument passed to Minify_HTML::minify() | ||||
|             $sourceSpec['minifyOptions'] = array( | ||||
|                 'cssMinifier' => array('Minify_CSS', 'minify') | ||||
|                 ,'jsMinifier' => array('JSMin', 'minify') | ||||
|             ); | ||||
|             unset($options['minifyAll']); | ||||
|         } | ||||
|         $this->sources[] = new Minify_Source($sourceSpec); | ||||
|          | ||||
|         $options['contentType'] = Minify::TYPE_HTML; | ||||
|         return $options; | ||||
|     } | ||||
| } | ||||
|  | ||||
							
								
								
									
										119
									
								
								vendor/mrclay/minify/min/lib/Minify/Controller/Version1.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								vendor/mrclay/minify/min/lib/Minify/Controller/Version1.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,119 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Class Minify_Controller_Version1   | ||||
|  * @package Minify | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * Controller class for emulating version 1 of minify.php (mostly a proof-of-concept) | ||||
|  *  | ||||
|  * <code> | ||||
|  * Minify::serve('Version1'); | ||||
|  * </code> | ||||
|  *  | ||||
|  * @package Minify | ||||
|  * @author Stephen Clay <steve@mrclay.org> | ||||
|  */ | ||||
| class Minify_Controller_Version1 extends Minify_Controller_Base { | ||||
|      | ||||
|     /** | ||||
|      * Set up groups of files as sources | ||||
|      *  | ||||
|      * @param array $options controller and Minify options | ||||
|      * @return array Minify options | ||||
|      *  | ||||
|      */ | ||||
|     public function setupSources($options) { | ||||
|         // PHP insecure by default: realpath() and other FS functions can't handle null bytes. | ||||
|         if (isset($_GET['files'])) { | ||||
|             $_GET['files'] = str_replace("\x00", '', (string)$_GET['files']); | ||||
|         } | ||||
|  | ||||
|         self::_setupDefines(); | ||||
|         if (MINIFY_USE_CACHE) { | ||||
|             $cacheDir = defined('MINIFY_CACHE_DIR') | ||||
|                 ? MINIFY_CACHE_DIR | ||||
|                 : ''; | ||||
|             Minify::setCache($cacheDir); | ||||
|         } | ||||
|         $options['badRequestHeader'] = 'HTTP/1.0 404 Not Found'; | ||||
|         $options['contentTypeCharset'] = MINIFY_ENCODING; | ||||
|  | ||||
|         // The following restrictions are to limit the URLs that minify will | ||||
|         // respond to. Ideally there should be only one way to reference a file. | ||||
|         if (! isset($_GET['files']) | ||||
|             // verify at least one file, files are single comma separated,  | ||||
|             // and are all same extension | ||||
|             || ! preg_match('/^[^,]+\\.(css|js)(,[^,]+\\.\\1)*$/', $_GET['files'], $m) | ||||
|             // no "//" (makes URL rewriting easier) | ||||
|             || strpos($_GET['files'], '//') !== false | ||||
|             // no "\" | ||||
|             || strpos($_GET['files'], '\\') !== false | ||||
|             // no "./" | ||||
|             || preg_match('/(?:^|[^\\.])\\.\\//', $_GET['files']) | ||||
|         ) { | ||||
|             return $options; | ||||
|         } | ||||
|  | ||||
|         $files = explode(',', $_GET['files']); | ||||
|         if (count($files) > MINIFY_MAX_FILES) { | ||||
|             return $options; | ||||
|         } | ||||
|          | ||||
|         // strings for prepending to relative/absolute paths | ||||
|         $prependRelPaths = dirname($_SERVER['SCRIPT_FILENAME']) | ||||
|             . DIRECTORY_SEPARATOR; | ||||
|         $prependAbsPaths = $_SERVER['DOCUMENT_ROOT']; | ||||
|          | ||||
|         $goodFiles = array(); | ||||
|         $hasBadSource = false; | ||||
|          | ||||
|         $allowDirs = isset($options['allowDirs']) | ||||
|             ? $options['allowDirs'] | ||||
|             : MINIFY_BASE_DIR; | ||||
|          | ||||
|         foreach ($files as $file) { | ||||
|             // prepend appropriate string for abs/rel paths | ||||
|             $file = ($file[0] === '/' ? $prependAbsPaths : $prependRelPaths) . $file; | ||||
|             // make sure a real file! | ||||
|             $file = realpath($file); | ||||
|             // don't allow unsafe or duplicate files | ||||
|             if (parent::_fileIsSafe($file, $allowDirs)  | ||||
|                 && !in_array($file, $goodFiles))  | ||||
|             { | ||||
|                 $goodFiles[] = $file; | ||||
|                 $srcOptions = array( | ||||
|                     'filepath' => $file | ||||
|                 ); | ||||
|                 $this->sources[] = new Minify_Source($srcOptions); | ||||
|             } else { | ||||
|                 $hasBadSource = true; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         if ($hasBadSource) { | ||||
|             $this->sources = array(); | ||||
|         } | ||||
|         if (! MINIFY_REWRITE_CSS_URLS) { | ||||
|             $options['rewriteCssUris'] = false; | ||||
|         } | ||||
|         return $options; | ||||
|     } | ||||
|      | ||||
|     private static function _setupDefines() | ||||
|     { | ||||
|         $defaults = array( | ||||
|             'MINIFY_BASE_DIR' => realpath($_SERVER['DOCUMENT_ROOT']) | ||||
|             ,'MINIFY_ENCODING' => 'utf-8' | ||||
|             ,'MINIFY_MAX_FILES' => 16 | ||||
|             ,'MINIFY_REWRITE_CSS_URLS' => true | ||||
|             ,'MINIFY_USE_CACHE' => true | ||||
|         ); | ||||
|         foreach ($defaults as $const => $val) { | ||||
|             if (! defined($const)) { | ||||
|                 define($const, $val); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
							
								
								
									
										26
									
								
								vendor/mrclay/minify/min/lib/Minify/DebugDetector.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								vendor/mrclay/minify/min/lib/Minify/DebugDetector.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Detect whether request should be debugged | ||||
|  * | ||||
|  * @package Minify | ||||
|  * @author Stephen Clay <steve@mrclay.org> | ||||
|  */ | ||||
| class Minify_DebugDetector { | ||||
|     public static function shouldDebugRequest($cookie, $get, $requestUri) | ||||
|     { | ||||
|         if (isset($get['debug'])) { | ||||
|             return true; | ||||
|         } | ||||
|         if (! empty($cookie['minifyDebug'])) { | ||||
|             foreach (preg_split('/\\s+/', $cookie['minifyDebug']) as $debugUri) { | ||||
|                 $pattern = '@' . preg_quote($debugUri, '@') . '@i'; | ||||
|                 $pattern = str_replace(array('\\*', '\\?'), array('.*', '.'), $pattern); | ||||
|                 if (preg_match($pattern, $requestUri)) { | ||||
|                     return true; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										255
									
								
								vendor/mrclay/minify/min/lib/Minify/HTML.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										255
									
								
								vendor/mrclay/minify/min/lib/Minify/HTML.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,255 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Class Minify_HTML | ||||
|  * @package Minify | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * Compress HTML | ||||
|  * | ||||
|  * This is a heavy regex-based removal of whitespace, unnecessary comments and | ||||
|  * tokens. IE conditional comments are preserved. There are also options to have | ||||
|  * STYLE and SCRIPT blocks compressed by callback functions. | ||||
|  * | ||||
|  * A test suite is available. | ||||
|  * | ||||
|  * @package Minify | ||||
|  * @author Stephen Clay <steve@mrclay.org> | ||||
|  */ | ||||
| class Minify_HTML { | ||||
|     /** | ||||
|      * @var boolean | ||||
|      */ | ||||
|     protected $_jsCleanComments = true; | ||||
|  | ||||
|     /** | ||||
|      * "Minify" an HTML page | ||||
|      * | ||||
|      * @param string $html | ||||
|      * | ||||
|      * @param array $options | ||||
|      * | ||||
|      * 'cssMinifier' : (optional) callback function to process content of STYLE | ||||
|      * elements. | ||||
|      * | ||||
|      * 'jsMinifier' : (optional) callback function to process content of SCRIPT | ||||
|      * elements. Note: the type attribute is ignored. | ||||
|      * | ||||
|      * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If | ||||
|      * unset, minify will sniff for an XHTML doctype. | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public static function minify($html, $options = array()) { | ||||
|         $min = new self($html, $options); | ||||
|         return $min->process(); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Create a minifier object | ||||
|      * | ||||
|      * @param string $html | ||||
|      * | ||||
|      * @param array $options | ||||
|      * | ||||
|      * 'cssMinifier' : (optional) callback function to process content of STYLE | ||||
|      * elements. | ||||
|      * | ||||
|      * 'jsMinifier' : (optional) callback function to process content of SCRIPT | ||||
|      * elements. Note: the type attribute is ignored. | ||||
|      * | ||||
|      * 'jsCleanComments' : (optional) whether to remove HTML comments beginning and end of script block | ||||
|      * | ||||
|      * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If | ||||
|      * unset, minify will sniff for an XHTML doctype. | ||||
|      */ | ||||
|     public function __construct($html, $options = array()) | ||||
|     { | ||||
|         $this->_html = str_replace("\r\n", "\n", trim($html)); | ||||
|         if (isset($options['xhtml'])) { | ||||
|             $this->_isXhtml = (bool)$options['xhtml']; | ||||
|         } | ||||
|         if (isset($options['cssMinifier'])) { | ||||
|             $this->_cssMinifier = $options['cssMinifier']; | ||||
|         } | ||||
|         if (isset($options['jsMinifier'])) { | ||||
|             $this->_jsMinifier = $options['jsMinifier']; | ||||
|         } | ||||
|         if (isset($options['jsCleanComments'])) { | ||||
|             $this->_jsCleanComments = (bool)$options['jsCleanComments']; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Minify the markeup given in the constructor | ||||
|      *  | ||||
|      * @return string | ||||
|      */ | ||||
|     public function process() | ||||
|     { | ||||
|         if ($this->_isXhtml === null) { | ||||
|             $this->_isXhtml = (false !== strpos($this->_html, '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML')); | ||||
|         } | ||||
|          | ||||
|         $this->_replacementHash = 'MINIFYHTML' . md5($_SERVER['REQUEST_TIME']); | ||||
|         $this->_placeholders = array(); | ||||
|          | ||||
|         // replace SCRIPTs (and minify) with placeholders | ||||
|         $this->_html = preg_replace_callback( | ||||
|             '/(\\s*)<script(\\b[^>]*?>)([\\s\\S]*?)<\\/script>(\\s*)/i' | ||||
|             ,array($this, '_removeScriptCB') | ||||
|             ,$this->_html); | ||||
|          | ||||
|         // replace STYLEs (and minify) with placeholders | ||||
|         $this->_html = preg_replace_callback( | ||||
|             '/\\s*<style(\\b[^>]*>)([\\s\\S]*?)<\\/style>\\s*/i' | ||||
|             ,array($this, '_removeStyleCB') | ||||
|             ,$this->_html); | ||||
|          | ||||
|         // remove HTML comments (not containing IE conditional comments). | ||||
|         $this->_html = preg_replace_callback( | ||||
|             '/<!--([\\s\\S]*?)-->/' | ||||
|             ,array($this, '_commentCB') | ||||
|             ,$this->_html); | ||||
|          | ||||
|         // replace PREs with placeholders | ||||
|         $this->_html = preg_replace_callback('/\\s*<pre(\\b[^>]*?>[\\s\\S]*?<\\/pre>)\\s*/i' | ||||
|             ,array($this, '_removePreCB') | ||||
|             ,$this->_html); | ||||
|          | ||||
|         // replace TEXTAREAs with placeholders | ||||
|         $this->_html = preg_replace_callback( | ||||
|             '/\\s*<textarea(\\b[^>]*?>[\\s\\S]*?<\\/textarea>)\\s*/i' | ||||
|             ,array($this, '_removeTextareaCB') | ||||
|             ,$this->_html); | ||||
|          | ||||
|         // trim each line. | ||||
|         // @todo take into account attribute values that span multiple lines. | ||||
|         $this->_html = preg_replace('/^\\s+|\\s+$/m', '', $this->_html); | ||||
|          | ||||
|         // remove ws around block/undisplayed elements | ||||
|         $this->_html = preg_replace('/\\s+(<\\/?(?:area|base(?:font)?|blockquote|body' | ||||
|             .'|caption|center|col(?:group)?|dd|dir|div|dl|dt|fieldset|form' | ||||
|             .'|frame(?:set)?|h[1-6]|head|hr|html|legend|li|link|map|menu|meta' | ||||
|             .'|ol|opt(?:group|ion)|p|param|t(?:able|body|head|d|h||r|foot|itle)' | ||||
|             .'|ul)\\b[^>]*>)/i', '$1', $this->_html); | ||||
|          | ||||
|         // remove ws outside of all elements | ||||
|         $this->_html = preg_replace( | ||||
|             '/>(\\s(?:\\s*))?([^<]+)(\\s(?:\s*))?</' | ||||
|             ,'>$1$2$3<' | ||||
|             ,$this->_html); | ||||
|          | ||||
|         // use newlines before 1st attribute in open tags (to limit line lengths) | ||||
|         $this->_html = preg_replace('/(<[a-z\\-]+)\\s+([^>]+>)/i', "$1\n$2", $this->_html); | ||||
|          | ||||
|         // fill placeholders | ||||
|         $this->_html = str_replace( | ||||
|             array_keys($this->_placeholders) | ||||
|             ,array_values($this->_placeholders) | ||||
|             ,$this->_html | ||||
|         ); | ||||
|         // issue 229: multi-pass to catch scripts that didn't get replaced in textareas | ||||
|         $this->_html = str_replace( | ||||
|             array_keys($this->_placeholders) | ||||
|             ,array_values($this->_placeholders) | ||||
|             ,$this->_html | ||||
|         ); | ||||
|         return $this->_html; | ||||
|     } | ||||
|      | ||||
|     protected function _commentCB($m) | ||||
|     { | ||||
|         return (0 === strpos($m[1], '[') || false !== strpos($m[1], '<![')) | ||||
|             ? $m[0] | ||||
|             : ''; | ||||
|     } | ||||
|      | ||||
|     protected function _reservePlace($content) | ||||
|     { | ||||
|         $placeholder = '%' . $this->_replacementHash . count($this->_placeholders) . '%'; | ||||
|         $this->_placeholders[$placeholder] = $content; | ||||
|         return $placeholder; | ||||
|     } | ||||
|  | ||||
|     protected $_isXhtml = null; | ||||
|     protected $_replacementHash = null; | ||||
|     protected $_placeholders = array(); | ||||
|     protected $_cssMinifier = null; | ||||
|     protected $_jsMinifier = null; | ||||
|  | ||||
|     protected function _removePreCB($m) | ||||
|     { | ||||
|         return $this->_reservePlace("<pre{$m[1]}"); | ||||
|     } | ||||
|      | ||||
|     protected function _removeTextareaCB($m) | ||||
|     { | ||||
|         return $this->_reservePlace("<textarea{$m[1]}"); | ||||
|     } | ||||
|  | ||||
|     protected function _removeStyleCB($m) | ||||
|     { | ||||
|         $openStyle = "<style{$m[1]}"; | ||||
|         $css = $m[2]; | ||||
|         // remove HTML comments | ||||
|         $css = preg_replace('/(?:^\\s*<!--|-->\\s*$)/', '', $css); | ||||
|          | ||||
|         // remove CDATA section markers | ||||
|         $css = $this->_removeCdata($css); | ||||
|          | ||||
|         // minify | ||||
|         $minifier = $this->_cssMinifier | ||||
|             ? $this->_cssMinifier | ||||
|             : 'trim'; | ||||
|         $css = call_user_func($minifier, $css); | ||||
|          | ||||
|         return $this->_reservePlace($this->_needsCdata($css) | ||||
|             ? "{$openStyle}/*<![CDATA[*/{$css}/*]]>*/</style>" | ||||
|             : "{$openStyle}{$css}</style>" | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     protected function _removeScriptCB($m) | ||||
|     { | ||||
|         $openScript = "<script{$m[2]}"; | ||||
|         $js = $m[3]; | ||||
|          | ||||
|         // whitespace surrounding? preserve at least one space | ||||
|         $ws1 = ($m[1] === '') ? '' : ' '; | ||||
|         $ws2 = ($m[4] === '') ? '' : ' '; | ||||
|  | ||||
|         // remove HTML comments (and ending "//" if present) | ||||
|         if ($this->_jsCleanComments) { | ||||
|             $js = preg_replace('/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/', '', $js); | ||||
|         } | ||||
|  | ||||
|         // remove CDATA section markers | ||||
|         $js = $this->_removeCdata($js); | ||||
|          | ||||
|         // minify | ||||
|         $minifier = $this->_jsMinifier | ||||
|             ? $this->_jsMinifier | ||||
|             : 'trim'; | ||||
|         $js = call_user_func($minifier, $js); | ||||
|          | ||||
|         return $this->_reservePlace($this->_needsCdata($js) | ||||
|             ? "{$ws1}{$openScript}/*<![CDATA[*/{$js}/*]]>*/</script>{$ws2}" | ||||
|             : "{$ws1}{$openScript}{$js}</script>{$ws2}" | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     protected function _removeCdata($str) | ||||
|     { | ||||
|         return (false !== strpos($str, '<![CDATA[')) | ||||
|             ? str_replace(array('<![CDATA[', ']]>'), '', $str) | ||||
|             : $str; | ||||
|     } | ||||
|      | ||||
|     protected function _needsCdata($str) | ||||
|     { | ||||
|         return ($this->_isXhtml && preg_match('/(?:[<&]|\\-\\-|\\]\\]>)/', $str)); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										225
									
								
								vendor/mrclay/minify/min/lib/Minify/HTML/Helper.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										225
									
								
								vendor/mrclay/minify/min/lib/Minify/HTML/Helper.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,225 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Class Minify_HTML_Helper | ||||
|  * @package Minify | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * Helpers for writing Minfy URIs into HTML | ||||
|  * | ||||
|  * @package Minify | ||||
|  * @author Stephen Clay <steve@mrclay.org> | ||||
|  */ | ||||
| class Minify_HTML_Helper { | ||||
|     public $rewriteWorks = true; | ||||
|     public $minAppUri = '/min'; | ||||
|     public $groupsConfigFile = ''; | ||||
|  | ||||
|     /** | ||||
|      * Get an HTML-escaped Minify URI for a group or set of files | ||||
|      *  | ||||
|      * @param string|array $keyOrFiles a group key or array of filepaths/URIs | ||||
|      * @param array $opts options: | ||||
|      *   'farExpires' : (default true) append a modified timestamp for cache revving | ||||
|      *   'debug' : (default false) append debug flag | ||||
|      *   'charset' : (default 'UTF-8') for htmlspecialchars | ||||
|      *   'minAppUri' : (default '/min') URI of min directory | ||||
|      *   'rewriteWorks' : (default true) does mod_rewrite work in min app? | ||||
|      *   'groupsConfigFile' : specify if different | ||||
|      * @return string | ||||
|      */ | ||||
|     public static function getUri($keyOrFiles, $opts = array()) | ||||
|     { | ||||
|         $opts = array_merge(array( // default options | ||||
|             'farExpires' => true | ||||
|             ,'debug' => false | ||||
|             ,'charset' => 'UTF-8' | ||||
|             ,'minAppUri' => '/min' | ||||
|             ,'rewriteWorks' => true | ||||
|             ,'groupsConfigFile' => '' | ||||
|         ), $opts); | ||||
|         $h = new self; | ||||
|         $h->minAppUri = $opts['minAppUri']; | ||||
|         $h->rewriteWorks = $opts['rewriteWorks']; | ||||
|         $h->groupsConfigFile = $opts['groupsConfigFile']; | ||||
|         if (is_array($keyOrFiles)) { | ||||
|             $h->setFiles($keyOrFiles, $opts['farExpires']); | ||||
|         } else { | ||||
|             $h->setGroup($keyOrFiles, $opts['farExpires']); | ||||
|         } | ||||
|         $uri = $h->getRawUri($opts['farExpires'], $opts['debug']); | ||||
|         return htmlspecialchars($uri, ENT_QUOTES, $opts['charset']); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get non-HTML-escaped URI to minify the specified files | ||||
|      * | ||||
|      * @param bool $farExpires | ||||
|      * @param bool $debug | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getRawUri($farExpires = true, $debug = false) | ||||
|     { | ||||
|         $path = rtrim($this->minAppUri, '/') . '/'; | ||||
|         if (! $this->rewriteWorks) { | ||||
|             $path .= '?'; | ||||
|         } | ||||
|         if (null === $this->_groupKey) { | ||||
|             // @todo: implement shortest uri | ||||
|             $path = self::_getShortestUri($this->_filePaths, $path); | ||||
|         } else { | ||||
|             $path .= "g=" . $this->_groupKey; | ||||
|         } | ||||
|         if ($debug) { | ||||
|             $path .= "&debug"; | ||||
|         } elseif ($farExpires && $this->_lastModified) { | ||||
|             $path .= "&" . $this->_lastModified; | ||||
|         } | ||||
|         return $path; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Set the files that will comprise the URI we're building | ||||
|      * | ||||
|      * @param array $files | ||||
|      * @param bool $checkLastModified | ||||
|      */ | ||||
|     public function setFiles($files, $checkLastModified = true) | ||||
|     { | ||||
|         $this->_groupKey = null; | ||||
|         if ($checkLastModified) { | ||||
|             $this->_lastModified = self::getLastModified($files); | ||||
|         } | ||||
|         // normalize paths like in /min/f=<paths> | ||||
|         foreach ($files as $k => $file) { | ||||
|             if (0 === strpos($file, '//')) { | ||||
|                 $file = substr($file, 2); | ||||
|             } elseif (0 === strpos($file, '/') | ||||
|                       || 1 === strpos($file, ':\\')) { | ||||
|                 $file = substr($file, strlen($_SERVER['DOCUMENT_ROOT']) + 1); | ||||
|             } | ||||
|             $file = strtr($file, '\\', '/'); | ||||
|             $files[$k] = $file; | ||||
|         } | ||||
|         $this->_filePaths = $files; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Set the group of files that will comprise the URI we're building | ||||
|      * | ||||
|      * @param string $key | ||||
|      * @param bool $checkLastModified | ||||
|      */ | ||||
|     public function setGroup($key, $checkLastModified = true) | ||||
|     { | ||||
|         $this->_groupKey = $key; | ||||
|         if ($checkLastModified) { | ||||
|             if (! $this->groupsConfigFile) { | ||||
|                 $this->groupsConfigFile = dirname(dirname(dirname(dirname(__FILE__)))) . '/groupsConfig.php'; | ||||
|             } | ||||
|             if (is_file($this->groupsConfigFile)) { | ||||
|                 $gc = (require $this->groupsConfigFile); | ||||
|                 $keys = explode(',', $key); | ||||
|                 foreach ($keys as $key) { | ||||
|                     if (isset($gc[$key])) { | ||||
|                         $this->_lastModified = self::getLastModified($gc[$key], $this->_lastModified); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the max(lastModified) of all files | ||||
|      * | ||||
|      * @param array|string $sources | ||||
|      * @param int $lastModified | ||||
|      * @return int | ||||
|      */ | ||||
|     public static function getLastModified($sources, $lastModified = 0) | ||||
|     { | ||||
|         $max = $lastModified; | ||||
|         foreach ((array)$sources as $source) { | ||||
|             if (is_object($source) && isset($source->lastModified)) { | ||||
|                 $max = max($max, $source->lastModified); | ||||
|             } elseif (is_string($source)) { | ||||
|                 if (0 === strpos($source, '//')) { | ||||
|                     $source = $_SERVER['DOCUMENT_ROOT'] . substr($source, 1); | ||||
|                 } | ||||
|                 if (is_file($source)) { | ||||
|                     $max = max($max, filemtime($source)); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return $max; | ||||
|     } | ||||
|  | ||||
|     protected $_groupKey = null; // if present, URI will be like g=... | ||||
|     protected $_filePaths = array(); | ||||
|     protected $_lastModified = null; | ||||
|  | ||||
|      | ||||
|     /** | ||||
|      * In a given array of strings, find the character they all have at | ||||
|      * a particular index | ||||
|      * | ||||
|      * @param array $arr array of strings | ||||
|      * @param int $pos index to check | ||||
|      * @return mixed a common char or '' if any do not match | ||||
|      */ | ||||
|     protected static function _getCommonCharAtPos($arr, $pos) { | ||||
|         if (!isset($arr[0][$pos])) { | ||||
|             return ''; | ||||
|         } | ||||
|         $c = $arr[0][$pos]; | ||||
|         $l = count($arr); | ||||
|         if ($l === 1) { | ||||
|             return $c; | ||||
|         } | ||||
|         for ($i = 1; $i < $l; ++$i) { | ||||
|             if ($arr[$i][$pos] !== $c) { | ||||
|                 return ''; | ||||
|             } | ||||
|         } | ||||
|         return $c; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the shortest URI to minify the set of source files | ||||
|      * | ||||
|      * @param array $paths root-relative URIs of files | ||||
|      * @param string $minRoot root-relative URI of the "min" application | ||||
|      * @return string | ||||
|      */ | ||||
|     protected static function _getShortestUri($paths, $minRoot = '/min/') { | ||||
|         $pos = 0; | ||||
|         $base = ''; | ||||
|         while (true) { | ||||
|             $c = self::_getCommonCharAtPos($paths, $pos); | ||||
|             if ($c === '') { | ||||
|                 break; | ||||
|             } else { | ||||
|                 $base .= $c; | ||||
|             } | ||||
|             ++$pos; | ||||
|         } | ||||
|         $base = preg_replace('@[^/]+$@', '', $base); | ||||
|         $uri = $minRoot . 'f=' . implode(',', $paths); | ||||
|          | ||||
|         if (substr($base, -1) === '/') { | ||||
|             // we have a base dir! | ||||
|             $basedPaths = $paths; | ||||
|             $l = count($paths); | ||||
|             for ($i = 0; $i < $l; ++$i) { | ||||
|                 $basedPaths[$i] = substr($paths[$i], strlen($base)); | ||||
|             } | ||||
|             $base = substr($base, 0, strlen($base) - 1); | ||||
|             $bUri = $minRoot . 'b=' . $base . '&f=' . implode(',', $basedPaths); | ||||
|  | ||||
|             $uri = strlen($uri) < strlen($bUri) | ||||
|                 ? $uri | ||||
|                 : $bUri; | ||||
|         } | ||||
|         return $uri; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										216
									
								
								vendor/mrclay/minify/min/lib/Minify/ImportProcessor.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										216
									
								
								vendor/mrclay/minify/min/lib/Minify/ImportProcessor.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,216 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Class Minify_ImportProcessor | ||||
|  * @package Minify | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * Linearize a CSS/JS file by including content specified by CSS import | ||||
|  * declarations. In CSS files, relative URIs are fixed. | ||||
|  * | ||||
|  * @imports will be processed regardless of where they appear in the source | ||||
|  * files; i.e. @imports commented out or in string content will still be | ||||
|  * processed! | ||||
|  * | ||||
|  * This has a unit test but should be considered "experimental". | ||||
|  * | ||||
|  * @package Minify | ||||
|  * @author Stephen Clay <steve@mrclay.org> | ||||
|  * @author Simon Schick <simonsimcity@gmail.com> | ||||
|  */ | ||||
| class Minify_ImportProcessor { | ||||
|  | ||||
|     public static $filesIncluded = array(); | ||||
|  | ||||
|     public static function process($file) | ||||
|     { | ||||
|         self::$filesIncluded = array(); | ||||
|         self::$_isCss = (strtolower(substr($file, -4)) === '.css'); | ||||
|         $obj = new Minify_ImportProcessor(dirname($file)); | ||||
|         return $obj->_getContent($file); | ||||
|     } | ||||
|  | ||||
|     // allows callback funcs to know the current directory | ||||
|     private $_currentDir = null; | ||||
|  | ||||
|     // allows callback funcs to know the directory of the file that inherits this one | ||||
|     private $_previewsDir = null; | ||||
|  | ||||
|     // allows _importCB to write the fetched content back to the obj | ||||
|     private $_importedContent = ''; | ||||
|  | ||||
|     private static $_isCss = null; | ||||
|  | ||||
|     /** | ||||
|      * @param String $currentDir | ||||
|      * @param String $previewsDir Is only used internally | ||||
|      */ | ||||
|     private function __construct($currentDir, $previewsDir = "") | ||||
|     { | ||||
|         $this->_currentDir = $currentDir; | ||||
|         $this->_previewsDir = $previewsDir; | ||||
|     } | ||||
|  | ||||
|     private function _getContent($file, $is_imported = false) | ||||
|     { | ||||
|         $file = realpath($file); | ||||
|         if (! $file | ||||
|             || in_array($file, self::$filesIncluded) | ||||
|             || false === ($content = @file_get_contents($file)) | ||||
|         ) { | ||||
|             // file missing, already included, or failed read | ||||
|             return ''; | ||||
|         } | ||||
|         self::$filesIncluded[] = realpath($file); | ||||
|         $this->_currentDir = dirname($file); | ||||
|  | ||||
|         // remove UTF-8 BOM if present | ||||
|         if (pack("CCC",0xef,0xbb,0xbf) === substr($content, 0, 3)) { | ||||
|             $content = substr($content, 3); | ||||
|         } | ||||
|         // ensure uniform EOLs | ||||
|         $content = str_replace("\r\n", "\n", $content); | ||||
|  | ||||
|         // process @imports | ||||
|         $content = preg_replace_callback( | ||||
|             '/ | ||||
|                 @import\\s+ | ||||
|                 (?:url\\(\\s*)?      # maybe url( | ||||
|                 [\'"]?               # maybe quote | ||||
|                 (.*?)                # 1 = URI | ||||
|                 [\'"]?               # maybe end quote | ||||
|                 (?:\\s*\\))?         # maybe ) | ||||
|                 ([a-zA-Z,\\s]*)?     # 2 = media list | ||||
|                 ;                    # end token | ||||
|             /x' | ||||
|             ,array($this, '_importCB') | ||||
|             ,$content | ||||
|         ); | ||||
|  | ||||
|         // You only need to rework the import-path if the script is imported | ||||
|         if (self::$_isCss && $is_imported) { | ||||
|             // rewrite remaining relative URIs | ||||
|             $content = preg_replace_callback( | ||||
|                 '/url\\(\\s*([^\\)\\s]+)\\s*\\)/' | ||||
|                 ,array($this, '_urlCB') | ||||
|                 ,$content | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         return $this->_importedContent . $content; | ||||
|     } | ||||
|  | ||||
|     private function _importCB($m) | ||||
|     { | ||||
|         $url = $m[1]; | ||||
|         $mediaList = preg_replace('/\\s+/', '', $m[2]); | ||||
|  | ||||
|         if (strpos($url, '://') > 0) { | ||||
|             // protocol, leave in place for CSS, comment for JS | ||||
|             return self::$_isCss | ||||
|                 ? $m[0] | ||||
|                 : "/* Minify_ImportProcessor will not include remote content */"; | ||||
|         } | ||||
|         if ('/' === $url[0]) { | ||||
|             // protocol-relative or root path | ||||
|             $url = ltrim($url, '/'); | ||||
|             $file = realpath($_SERVER['DOCUMENT_ROOT']) . DIRECTORY_SEPARATOR | ||||
|                 . strtr($url, '/', DIRECTORY_SEPARATOR); | ||||
|         } else { | ||||
|             // relative to current path | ||||
|             $file = $this->_currentDir . DIRECTORY_SEPARATOR | ||||
|                 . strtr($url, '/', DIRECTORY_SEPARATOR); | ||||
|         } | ||||
|         $obj = new Minify_ImportProcessor(dirname($file), $this->_currentDir); | ||||
|         $content = $obj->_getContent($file, true); | ||||
|         if ('' === $content) { | ||||
|             // failed. leave in place for CSS, comment for JS | ||||
|             return self::$_isCss | ||||
|                 ? $m[0] | ||||
|                 : "/* Minify_ImportProcessor could not fetch '{$file}' */"; | ||||
|         } | ||||
|         return (!self::$_isCss || preg_match('@(?:^$|\\ball\\b)@', $mediaList)) | ||||
|             ? $content | ||||
|             : "@media {$mediaList} {\n{$content}\n}\n"; | ||||
|     } | ||||
|  | ||||
|     private function _urlCB($m) | ||||
|     { | ||||
|         // $m[1] is either quoted or not | ||||
|         $quote = ($m[1][0] === "'" || $m[1][0] === '"') | ||||
|             ? $m[1][0] | ||||
|             : ''; | ||||
|         $url = ($quote === '') | ||||
|             ? $m[1] | ||||
|             : substr($m[1], 1, strlen($m[1]) - 2); | ||||
|         if ('/' !== $url[0]) { | ||||
|             if (strpos($url, '//') > 0) { | ||||
|                 // probably starts with protocol, do not alter | ||||
|             } else { | ||||
|                 // prepend path with current dir separator (OS-independent) | ||||
|                 $path = $this->_currentDir | ||||
|                     . DIRECTORY_SEPARATOR . strtr($url, '/', DIRECTORY_SEPARATOR); | ||||
|                 // update the relative path by the directory of the file that imported this one | ||||
|                 $url = self::getPathDiff(realpath($this->_previewsDir), $path); | ||||
|             } | ||||
|         } | ||||
|         return "url({$quote}{$url}{$quote})"; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $from | ||||
|      * @param string $to | ||||
|      * @param string $ps | ||||
|      * @return string | ||||
|      */ | ||||
|     private function getPathDiff($from, $to, $ps = DIRECTORY_SEPARATOR) | ||||
|     { | ||||
|         $realFrom = $this->truepath($from); | ||||
|         $realTo = $this->truepath($to); | ||||
|  | ||||
|         $arFrom = explode($ps, rtrim($realFrom, $ps)); | ||||
|         $arTo = explode($ps, rtrim($realTo, $ps)); | ||||
|         while (count($arFrom) && count($arTo) && ($arFrom[0] == $arTo[0])) | ||||
|         { | ||||
|             array_shift($arFrom); | ||||
|             array_shift($arTo); | ||||
|         } | ||||
|         return str_pad("", count($arFrom) * 3, '..' . $ps) . implode($ps, $arTo); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * This function is to replace PHP's extremely buggy realpath(). | ||||
|      * @param string $path The original path, can be relative etc. | ||||
|      * @return string The resolved path, it might not exist. | ||||
|      * @see http://stackoverflow.com/questions/4049856/replace-phps-realpath | ||||
|      */ | ||||
|     function truepath($path) | ||||
|     { | ||||
|         // whether $path is unix or not | ||||
|         $unipath = strlen($path) == 0 || $path{0} != '/'; | ||||
|         // attempts to detect if path is relative in which case, add cwd | ||||
|         if (strpos($path, ':') === false && $unipath) | ||||
|             $path = $this->_currentDir . DIRECTORY_SEPARATOR . $path; | ||||
|  | ||||
|         // resolve path parts (single dot, double dot and double delimiters) | ||||
|         $path = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $path); | ||||
|         $parts = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'strlen'); | ||||
|         $absolutes = array(); | ||||
|         foreach ($parts as $part) { | ||||
|             if ('.' == $part) | ||||
|                 continue; | ||||
|             if ('..' == $part) { | ||||
|                 array_pop($absolutes); | ||||
|             } else { | ||||
|                 $absolutes[] = $part; | ||||
|             } | ||||
|         } | ||||
|         $path = implode(DIRECTORY_SEPARATOR, $absolutes); | ||||
|         // resolve any symlinks | ||||
|         if (file_exists($path) && linkinfo($path) > 0) | ||||
|             $path = readlink($path); | ||||
|         // put initial separator that could have been lost | ||||
|         $path = !$unipath ? '/' . $path : $path; | ||||
|         return $path; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										230
									
								
								vendor/mrclay/minify/min/lib/Minify/JS/ClosureCompiler.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										230
									
								
								vendor/mrclay/minify/min/lib/Minify/JS/ClosureCompiler.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,230 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Class Minify_JS_ClosureCompiler | ||||
|  * @package Minify | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * Minify Javascript using Google's Closure Compiler API | ||||
|  * | ||||
|  * @link http://code.google.com/closure/compiler/ | ||||
|  * @package Minify | ||||
|  * @author Stephen Clay <steve@mrclay.org> | ||||
|  * | ||||
|  * @todo can use a stream wrapper to unit test this? | ||||
|  */ | ||||
| class Minify_JS_ClosureCompiler { | ||||
|  | ||||
|     /** | ||||
|      * @var string The option key for the maximum POST byte size | ||||
|      */ | ||||
|     const OPTION_MAX_BYTES = 'maxBytes'; | ||||
|  | ||||
|     /** | ||||
|      * @var string The option key for additional params. @see __construct | ||||
|      */ | ||||
|     const OPTION_ADDITIONAL_OPTIONS = 'additionalParams'; | ||||
|  | ||||
|     /** | ||||
|      * @var string The option key for the fallback Minifier | ||||
|      */ | ||||
|     const OPTION_FALLBACK_FUNCTION = 'fallbackFunc'; | ||||
|  | ||||
|     /** | ||||
|      * @var string The option key for the service URL | ||||
|      */ | ||||
|     const OPTION_COMPILER_URL = 'compilerUrl'; | ||||
|  | ||||
|     /** | ||||
|      * @var int The default maximum POST byte size according to https://developers.google.com/closure/compiler/docs/api-ref | ||||
|      */ | ||||
|     const DEFAULT_MAX_BYTES = 200000; | ||||
|  | ||||
|     /** | ||||
|      * @var string[] $DEFAULT_OPTIONS The default options to pass to the compiler service | ||||
|      * | ||||
|      * @note This would be a constant if PHP allowed it | ||||
|      */ | ||||
|     private static $DEFAULT_OPTIONS = array( | ||||
|         'output_format' => 'text', | ||||
|         'compilation_level' => 'SIMPLE_OPTIMIZATIONS' | ||||
|     ); | ||||
|  | ||||
|     /** | ||||
|      * @var string $url URL of compiler server. defaults to Google's | ||||
|      */ | ||||
|     protected $serviceUrl = 'http://closure-compiler.appspot.com/compile'; | ||||
|  | ||||
|     /** | ||||
|      * @var int $maxBytes The maximum JS size that can be sent to the compiler server in bytes | ||||
|      */ | ||||
|     protected $maxBytes = self::DEFAULT_MAX_BYTES; | ||||
|  | ||||
|     /** | ||||
|      * @var string[] $additionalOptions Additional options to pass to the compiler service | ||||
|      */ | ||||
|     protected $additionalOptions = array(); | ||||
|  | ||||
|     /** | ||||
|      * @var callable Function to minify JS if service fails. Default is JSMin | ||||
|      */ | ||||
|     protected $fallbackMinifier = array('JSMin', 'minify'); | ||||
|  | ||||
|     /** | ||||
|      * Minify JavaScript code via HTTP request to a Closure Compiler API | ||||
|      * | ||||
|      * @param string $js input code | ||||
|      * @param array $options Options passed to __construct(). @see __construct | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public static function minify($js, array $options = array()) | ||||
|     { | ||||
|         $obj = new self($options); | ||||
|         return $obj->min($js); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array $options Options with keys available below: | ||||
|      * | ||||
|      *  fallbackFunc     : (callable) function to minify if service unavailable. Default is JSMin. | ||||
|      * | ||||
|      *  compilerUrl      : (string) URL to closure compiler server | ||||
|      * | ||||
|      *  maxBytes         : (int) The maximum amount of bytes to be sent as js_code in the POST request. | ||||
|      *                     Defaults to 200000. | ||||
|      * | ||||
|      *  additionalParams : (string[]) Additional parameters to pass to the compiler server. Can be anything named | ||||
|      *                     in https://developers.google.com/closure/compiler/docs/api-ref except for js_code and | ||||
|      *                     output_info | ||||
|      */ | ||||
|     public function __construct(array $options = array()) | ||||
|     { | ||||
|         if (isset($options[self::OPTION_FALLBACK_FUNCTION])) { | ||||
|             $this->fallbackMinifier = $options[self::OPTION_FALLBACK_FUNCTION]; | ||||
|         } | ||||
|         if (isset($options[self::OPTION_COMPILER_URL])) { | ||||
|             $this->serviceUrl = $options[self::OPTION_COMPILER_URL]; | ||||
|         } | ||||
|         if (isset($options[self::OPTION_ADDITIONAL_OPTIONS]) && is_array($options[self::OPTION_ADDITIONAL_OPTIONS])) { | ||||
|             $this->additionalOptions = $options[self::OPTION_ADDITIONAL_OPTIONS]; | ||||
|         } | ||||
|         if (isset($options[self::OPTION_MAX_BYTES])) { | ||||
|             $this->maxBytes = (int) $options[self::OPTION_MAX_BYTES]; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Call the service to perform the minification | ||||
|      * | ||||
|      * @param string $js JavaScript code | ||||
|      * @return string | ||||
|      * @throws Minify_JS_ClosureCompiler_Exception | ||||
|      */ | ||||
|     public function min($js) | ||||
|     { | ||||
|         $postBody = $this->buildPostBody($js); | ||||
|  | ||||
|         if ($this->maxBytes > 0) { | ||||
|             $bytes = (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) | ||||
|                 ? mb_strlen($postBody, '8bit') | ||||
|                 : strlen($postBody); | ||||
|             if ($bytes > $this->maxBytes) { | ||||
|                 throw new Minify_JS_ClosureCompiler_Exception( | ||||
|                     'POST content larger than ' . $this->maxBytes . ' bytes' | ||||
|                 ); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $response = $this->getResponse($postBody); | ||||
|  | ||||
|         if (preg_match('/^Error\(\d\d?\):/', $response)) { | ||||
|             if (is_callable($this->fallbackMinifier)) { | ||||
|                 // use fallback | ||||
|                 $response = "/* Received errors from Closure Compiler API:\n$response" | ||||
|                           . "\n(Using fallback minifier)\n*/\n"; | ||||
|                 $response .= call_user_func($this->fallbackMinifier, $js); | ||||
|             } else { | ||||
|                 throw new Minify_JS_ClosureCompiler_Exception($response); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if ($response === '') { | ||||
|             $errors = $this->getResponse($this->buildPostBody($js, true)); | ||||
|             throw new Minify_JS_ClosureCompiler_Exception($errors); | ||||
|         } | ||||
|  | ||||
|         return $response; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the response for a given POST body | ||||
|      * | ||||
|      * @param string $postBody | ||||
|      * @return string | ||||
|      * @throws Minify_JS_ClosureCompiler_Exception | ||||
|      */ | ||||
|     protected function getResponse($postBody) | ||||
|     { | ||||
|         $allowUrlFopen = preg_match('/1|yes|on|true/i', ini_get('allow_url_fopen')); | ||||
|  | ||||
|         if ($allowUrlFopen) { | ||||
|             $contents = file_get_contents($this->serviceUrl, false, stream_context_create(array( | ||||
|                 'http' => array( | ||||
|                     'method' => 'POST', | ||||
|                     'header' => "Content-type: application/x-www-form-urlencoded\r\nConnection: close\r\n", | ||||
|                     'content' => $postBody, | ||||
|                     'max_redirects' => 0, | ||||
|                     'timeout' => 15, | ||||
|                 ) | ||||
|             ))); | ||||
|         } elseif (defined('CURLOPT_POST')) { | ||||
|             $ch = curl_init($this->serviceUrl); | ||||
|             curl_setopt($ch, CURLOPT_POST, true); | ||||
|             curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); | ||||
|             curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type: application/x-www-form-urlencoded')); | ||||
|             curl_setopt($ch, CURLOPT_POSTFIELDS, $postBody); | ||||
|             curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false); | ||||
|             curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15); | ||||
|             $contents = curl_exec($ch); | ||||
|             curl_close($ch); | ||||
|         } else { | ||||
|             throw new Minify_JS_ClosureCompiler_Exception( | ||||
|                "Could not make HTTP request: allow_url_open is false and cURL not available" | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         if (false === $contents) { | ||||
|             throw new Minify_JS_ClosureCompiler_Exception( | ||||
|                "No HTTP response from server" | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         return trim($contents); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Build a POST request body | ||||
|      * | ||||
|      * @param string $js JavaScript code | ||||
|      * @param bool $returnErrors | ||||
|      * @return string | ||||
|      */ | ||||
|     protected function buildPostBody($js, $returnErrors = false) | ||||
|     { | ||||
|         return http_build_query( | ||||
|             array_merge( | ||||
|                 self::$DEFAULT_OPTIONS, | ||||
|                 $this->additionalOptions, | ||||
|                 array( | ||||
|                     'js_code' => $js, | ||||
|                     'output_info' => ($returnErrors ? 'errors' : 'compiled_code') | ||||
|                 ) | ||||
|             ), | ||||
|             null, | ||||
|             '&' | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|  | ||||
| class Minify_JS_ClosureCompiler_Exception extends Exception {} | ||||
							
								
								
									
										143
									
								
								vendor/mrclay/minify/min/lib/Minify/Lines.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								vendor/mrclay/minify/min/lib/Minify/Lines.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,143 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Class Minify_Lines   | ||||
|  * @package Minify | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * Add line numbers in C-style comments for easier debugging of combined content | ||||
|  * | ||||
|  * @package Minify | ||||
|  * @author Stephen Clay <steve@mrclay.org> | ||||
|  * @author Adam Pedersen (Issue 55 fix) | ||||
|  */ | ||||
| class Minify_Lines { | ||||
|  | ||||
|     /** | ||||
|      * Add line numbers in C-style comments | ||||
|      * | ||||
|      * This uses a very basic parser easily fooled by comment tokens inside | ||||
|      * strings or regexes, but, otherwise, generally clean code will not be  | ||||
|      * mangled. URI rewriting can also be performed. | ||||
|      * | ||||
|      * @param string $content | ||||
|      *  | ||||
|      * @param array $options available options: | ||||
|      *  | ||||
|      * 'id': (optional) string to identify file. E.g. file name/path | ||||
|      * | ||||
|      * 'currentDir': (default null) if given, this is assumed to be the | ||||
|      * directory of the current CSS file. Using this, minify will rewrite | ||||
|      * all relative URIs in import/url declarations to correctly point to | ||||
|      * the desired files, and prepend a comment with debugging information about | ||||
|      * this process. | ||||
|      *  | ||||
|      * @return string  | ||||
|      */ | ||||
|     public static function minify($content, $options = array())  | ||||
|     { | ||||
|         $id = (isset($options['id']) && $options['id']) | ||||
|             ? $options['id'] | ||||
|             : ''; | ||||
|         $content = str_replace("\r\n", "\n", $content); | ||||
|  | ||||
|         // Hackily rewrite strings with XPath expressions that are | ||||
|         // likely to throw off our dumb parser (for Prototype 1.6.1). | ||||
|         $content = str_replace('"/*"', '"/"+"*"', $content); | ||||
|         $content = preg_replace('@([\'"])(\\.?//?)\\*@', '$1$2$1+$1*', $content); | ||||
|  | ||||
|         $lines = explode("\n", $content); | ||||
|         $numLines = count($lines); | ||||
|         // determine left padding | ||||
|         $padTo = strlen((string) $numLines); // e.g. 103 lines = 3 digits | ||||
|         $inComment = false; | ||||
|         $i = 0; | ||||
|         $newLines = array(); | ||||
|         while (null !== ($line = array_shift($lines))) { | ||||
|             if (('' !== $id) && (0 == $i % 50)) { | ||||
|                 if ($inComment) { | ||||
|                     array_push($newLines, '', "/* {$id} *|", ''); | ||||
|                 } else { | ||||
|                     array_push($newLines, '', "/* {$id} */", ''); | ||||
|                 } | ||||
|             } | ||||
|             ++$i; | ||||
|             $newLines[] = self::_addNote($line, $i, $inComment, $padTo); | ||||
|             $inComment = self::_eolInComment($line, $inComment); | ||||
|         } | ||||
|         $content = implode("\n", $newLines) . "\n"; | ||||
|          | ||||
|         // check for desired URI rewriting | ||||
|         if (isset($options['currentDir'])) { | ||||
|             Minify_CSS_UriRewriter::$debugText = ''; | ||||
|             $content = Minify_CSS_UriRewriter::rewrite( | ||||
|                  $content | ||||
|                 ,$options['currentDir'] | ||||
|                 ,isset($options['docRoot']) ? $options['docRoot'] : $_SERVER['DOCUMENT_ROOT'] | ||||
|                 ,isset($options['symlinks']) ? $options['symlinks'] : array() | ||||
|             ); | ||||
|             $content = "/* Minify_CSS_UriRewriter::\$debugText\n\n"  | ||||
|                      . Minify_CSS_UriRewriter::$debugText . "*/\n" | ||||
|                      . $content; | ||||
|         } | ||||
|          | ||||
|         return $content; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Is the parser within a C-style comment at the end of this line? | ||||
|      * | ||||
|      * @param string $line current line of code | ||||
|      *  | ||||
|      * @param bool $inComment was the parser in a comment at the | ||||
|      * beginning of the line? | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     private static function _eolInComment($line, $inComment) | ||||
|     { | ||||
|         // crude way to avoid things like // */ | ||||
|         $line = preg_replace('~//.*?(\\*/|/\\*).*~', '', $line); | ||||
|  | ||||
|         while (strlen($line)) { | ||||
|             $search = $inComment | ||||
|                 ? '*/' | ||||
|                 : '/*'; | ||||
|             $pos = strpos($line, $search); | ||||
|             if (false === $pos) { | ||||
|                 return $inComment; | ||||
|             } else { | ||||
|                 if ($pos == 0 | ||||
|                     || ($inComment | ||||
|                         ? substr($line, $pos, 3) | ||||
|                         : substr($line, $pos-1, 3)) != '*/*') | ||||
|                 { | ||||
|                         $inComment = ! $inComment; | ||||
|                 } | ||||
|                 $line = substr($line, $pos + 2); | ||||
|             } | ||||
|         } | ||||
|         return $inComment; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Prepend a comment (or note) to the given line | ||||
|      * | ||||
|      * @param string $line current line of code | ||||
|      * | ||||
|      * @param string $note content of note/comment | ||||
|      *  | ||||
|      * @param bool $inComment was the parser in a comment at the | ||||
|      * beginning of the line? | ||||
|      * | ||||
|      * @param int $padTo minimum width of comment | ||||
|      *  | ||||
|      * @return string | ||||
|      */ | ||||
|     private static function _addNote($line, $note, $inComment, $padTo) | ||||
|     { | ||||
|         return $inComment | ||||
|             ? '/* ' . str_pad($note, $padTo, ' ', STR_PAD_RIGHT) . ' *| ' . $line | ||||
|             : '/* ' . str_pad($note, $padTo, ' ', STR_PAD_RIGHT) . ' */ ' . $line; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										28
									
								
								vendor/mrclay/minify/min/lib/Minify/Loader.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								vendor/mrclay/minify/min/lib/Minify/Loader.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Class Minify_Loader | ||||
|  * @package Minify | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * Class autoloader | ||||
|  * | ||||
|  * @package Minify | ||||
|  * @author Stephen Clay <steve@mrclay.org> | ||||
|  */ | ||||
| class Minify_Loader { | ||||
|     public function loadClass($class) | ||||
|     { | ||||
|         $file = dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR; | ||||
|         $file .= strtr($class, "\\_", DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR) . '.php'; | ||||
|         if (is_readable($file)) { | ||||
|             require $file; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     static public function register() | ||||
|     { | ||||
|         $inst = new self(); | ||||
|         spl_autoload_register(array($inst, 'loadClass')); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										47
									
								
								vendor/mrclay/minify/min/lib/Minify/Logger.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								vendor/mrclay/minify/min/lib/Minify/Logger.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Class Minify_Logger   | ||||
|  * @package Minify | ||||
|  */ | ||||
|  | ||||
| /**  | ||||
|  * Message logging class | ||||
|  *  | ||||
|  * @package Minify | ||||
|  * @author Stephen Clay <steve@mrclay.org> | ||||
|  * | ||||
|  * @todo lose this singleton! pass log object in Minify::serve and distribute to others | ||||
|  */ | ||||
| class Minify_Logger { | ||||
|  | ||||
|     /** | ||||
|      * Set logger object.  | ||||
|      * | ||||
|      * The object should have a method "log" that accepts a value as 1st argument and | ||||
|      * an optional string label as the 2nd. | ||||
|      * | ||||
|      * @param mixed $obj or a "falsey" value to disable | ||||
|      * @return null | ||||
|      */ | ||||
|     public static function setLogger($obj = null) { | ||||
|         self::$_logger = $obj | ||||
|             ? $obj | ||||
|             : null; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Pass a message to the logger (if set) | ||||
|      * | ||||
|      * @param string $msg message to log | ||||
|      * @return null | ||||
|      */ | ||||
|     public static function log($msg, $label = 'Minify') { | ||||
|         if (! self::$_logger) return; | ||||
|         self::$_logger->log($msg, $label); | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * @var mixed logger object (like FirePHP) or null (i.e. no logger available) | ||||
|      */ | ||||
|     private static $_logger = null; | ||||
| } | ||||
							
								
								
									
										37
									
								
								vendor/mrclay/minify/min/lib/Minify/Packer.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								vendor/mrclay/minify/min/lib/Minify/Packer.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Class Minify_Packer | ||||
|  * | ||||
|  * To use this class you must first download the PHP port of Packer | ||||
|  * and place the file "class.JavaScriptPacker.php" in /lib (or your | ||||
|  * include_path).  | ||||
|  * @link http://joliclic.free.fr/php/javascript-packer/en/ | ||||
|  * | ||||
|  * Be aware that, as long as HTTP encoding is used, scripts minified with JSMin | ||||
|  * will provide better client-side performance, as they need not be unpacked in | ||||
|  * client-side code. | ||||
|  *  | ||||
|  * @package Minify   | ||||
|  */ | ||||
|  | ||||
| if (false === (@include 'class.JavaScriptPacker.php')) { | ||||
|     trigger_error( | ||||
|         'The script "class.JavaScriptPacker.php" is required. Please see: http:' | ||||
|         .'//code.google.com/p/minify/source/browse/trunk/min/lib/Minify/Packer.php' | ||||
|         ,E_USER_ERROR | ||||
|     ); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Minify Javascript using Dean Edward's Packer | ||||
|  *  | ||||
|  * @package Minify | ||||
|  */ | ||||
| class Minify_Packer { | ||||
|     public static function minify($code, $options = array()) | ||||
|     { | ||||
|         // @todo: set encoding options based on $options :) | ||||
|         $packer = new JavascriptPacker($code, 'Normal', true, false); | ||||
|         return trim($packer->pack()); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										187
									
								
								vendor/mrclay/minify/min/lib/Minify/Source.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								vendor/mrclay/minify/min/lib/Minify/Source.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,187 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Class Minify_Source   | ||||
|  * @package Minify | ||||
|  */ | ||||
|  | ||||
| /**  | ||||
|  * A content source to be minified by Minify.  | ||||
|  *  | ||||
|  * This allows per-source minification options and the mixing of files with | ||||
|  * content from other sources. | ||||
|  *  | ||||
|  * @package Minify | ||||
|  * @author Stephen Clay <steve@mrclay.org> | ||||
|  */ | ||||
| class Minify_Source { | ||||
|  | ||||
|     /** | ||||
|      * @var int time of last modification | ||||
|      */ | ||||
|     public $lastModified = null; | ||||
|      | ||||
|     /** | ||||
|      * @var callback minifier function specifically for this source. | ||||
|      */ | ||||
|     public $minifier = null; | ||||
|      | ||||
|     /** | ||||
|      * @var array minification options specific to this source. | ||||
|      */ | ||||
|     public $minifyOptions = null; | ||||
|  | ||||
|     /** | ||||
|      * @var string full path of file | ||||
|      */ | ||||
|     public $filepath = null; | ||||
|      | ||||
|     /** | ||||
|      * @var string HTTP Content Type (Minify requires one of the constants Minify::TYPE_*) | ||||
|      */ | ||||
|     public $contentType = null; | ||||
|      | ||||
|     /** | ||||
|      * Create a Minify_Source | ||||
|      *  | ||||
|      * In the $spec array(), you can either provide a 'filepath' to an existing | ||||
|      * file (existence will not be checked!) or give 'id' (unique string for  | ||||
|      * the content), 'content' (the string content) and 'lastModified'  | ||||
|      * (unixtime of last update). | ||||
|      *  | ||||
|      * As a shortcut, the controller will replace "//" at the beginning | ||||
|      * of a filepath with $_SERVER['DOCUMENT_ROOT'] . '/'. | ||||
|      * | ||||
|      * @param array $spec options | ||||
|      */ | ||||
|     public function __construct($spec) | ||||
|     { | ||||
|         if (isset($spec['filepath'])) { | ||||
|             if (0 === strpos($spec['filepath'], '//')) { | ||||
|                 $spec['filepath'] = $_SERVER['DOCUMENT_ROOT'] . substr($spec['filepath'], 1); | ||||
|             } | ||||
|             $segments = explode('.', $spec['filepath']); | ||||
|             $ext = strtolower(array_pop($segments)); | ||||
|             switch ($ext) { | ||||
|             case 'js'   : $this->contentType = 'application/x-javascript'; | ||||
|                           break; | ||||
|             case 'css'  : $this->contentType = 'text/css'; | ||||
|                           break; | ||||
|             case 'htm'  : // fallthrough | ||||
|             case 'html' : $this->contentType = 'text/html'; | ||||
|                           break; | ||||
|             } | ||||
|             $this->filepath = $spec['filepath']; | ||||
|             $this->_id = $spec['filepath']; | ||||
|             $this->lastModified = filemtime($spec['filepath']) | ||||
|                 // offset for Windows uploaders with out of sync clocks | ||||
|                 + round(Minify::$uploaderHoursBehind * 3600); | ||||
|         } elseif (isset($spec['id'])) { | ||||
|             $this->_id = 'id::' . $spec['id']; | ||||
|             if (isset($spec['content'])) { | ||||
|                 $this->_content = $spec['content']; | ||||
|             } else { | ||||
|                 $this->_getContentFunc = $spec['getContentFunc']; | ||||
|             } | ||||
|             $this->lastModified = isset($spec['lastModified']) | ||||
|                 ? $spec['lastModified'] | ||||
|                 : time(); | ||||
|         } | ||||
|         if (isset($spec['contentType'])) { | ||||
|             $this->contentType = $spec['contentType']; | ||||
|         } | ||||
|         if (isset($spec['minifier'])) { | ||||
|             $this->minifier = $spec['minifier']; | ||||
|         } | ||||
|         if (isset($spec['minifyOptions'])) { | ||||
|             $this->minifyOptions = $spec['minifyOptions']; | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Get content | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getContent() | ||||
|     { | ||||
|         $content = (null !== $this->filepath) | ||||
|             ? file_get_contents($this->filepath) | ||||
|             : ((null !== $this->_content) | ||||
|                 ? $this->_content | ||||
|                 : call_user_func($this->_getContentFunc, $this->_id) | ||||
|             ); | ||||
|         // remove UTF-8 BOM if present | ||||
|         return (pack("CCC",0xef,0xbb,0xbf) === substr($content, 0, 3)) | ||||
|             ? substr($content, 3) | ||||
|             : $content; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Get id | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getId() | ||||
|     { | ||||
|         return $this->_id; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Verifies a single minification call can handle all sources | ||||
|      * | ||||
|      * @param array $sources Minify_Source instances | ||||
|      *  | ||||
|      * @return bool true iff there no sources with specific minifier preferences. | ||||
|      */ | ||||
|     public static function haveNoMinifyPrefs($sources) | ||||
|     { | ||||
|         foreach ($sources as $source) { | ||||
|             if (null !== $source->minifier | ||||
|                 || null !== $source->minifyOptions) { | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Get unique string for a set of sources | ||||
|      * | ||||
|      * @param array $sources Minify_Source instances | ||||
|      *  | ||||
|      * @return string | ||||
|      */ | ||||
|     public static function getDigest($sources) | ||||
|     { | ||||
|         foreach ($sources as $source) { | ||||
|             $info[] = array( | ||||
|                 $source->_id, $source->minifier, $source->minifyOptions | ||||
|             ); | ||||
|         } | ||||
|         return md5(serialize($info)); | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Get content type from a group of sources | ||||
|      *  | ||||
|      * This is called if the user doesn't pass in a 'contentType' options   | ||||
|      *  | ||||
|      * @param array $sources Minify_Source instances | ||||
|      *  | ||||
|      * @return string content type. e.g. 'text/css' | ||||
|      */ | ||||
|     public static function getContentType($sources) | ||||
|     { | ||||
|         foreach ($sources as $source) { | ||||
|             if ($source->contentType !== null) { | ||||
|                 return $source->contentType; | ||||
|             } | ||||
|         } | ||||
|         return 'text/plain'; | ||||
|     } | ||||
|      | ||||
|     protected $_content = null; | ||||
|     protected $_getContentFunc = null; | ||||
|     protected $_id = null; | ||||
| } | ||||
|  | ||||
							
								
								
									
										382
									
								
								vendor/mrclay/minify/min/lib/Minify/YUI/CssCompressor.java
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										382
									
								
								vendor/mrclay/minify/min/lib/Minify/YUI/CssCompressor.java
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,382 @@ | ||||
| /* | ||||
|  * YUI Compressor | ||||
|  * http://developer.yahoo.com/yui/compressor/ | ||||
|  * Author: Julien Lecomte -  http://www.julienlecomte.net/ | ||||
|  * Author: Isaac Schlueter - http://foohack.com/ | ||||
|  * Author: Stoyan Stefanov - http://phpied.com/ | ||||
|  * Copyright (c) 2011 Yahoo! Inc.  All rights reserved. | ||||
|  * The copyrights embodied in the content of this file are licensed | ||||
|  * by Yahoo! Inc. under the BSD (revised) open source license. | ||||
|  */ | ||||
| package com.yahoo.platform.yui.compressor; | ||||
|  | ||||
| import java.io.IOException; | ||||
| import java.io.Reader; | ||||
| import java.io.Writer; | ||||
| import java.util.regex.Pattern; | ||||
| import java.util.regex.Matcher; | ||||
| import java.util.ArrayList; | ||||
|  | ||||
| public class CssCompressor { | ||||
|  | ||||
|     private StringBuffer srcsb = new StringBuffer(); | ||||
|  | ||||
|     public CssCompressor(Reader in) throws IOException { | ||||
|         // Read the stream... | ||||
|         int c; | ||||
|         while ((c = in.read()) != -1) { | ||||
|             srcsb.append((char) c); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Leave data urls alone to increase parse performance. | ||||
|     protected String extractDataUrls(String css, ArrayList preservedTokens) { | ||||
|  | ||||
|     	int maxIndex = css.length() - 1; | ||||
|         int appendIndex = 0; | ||||
|  | ||||
|     	StringBuffer sb = new StringBuffer(); | ||||
|  | ||||
|         Pattern p = Pattern.compile("url\\(\\s*([\"']?)data\\:"); | ||||
|         Matcher m = p.matcher(css); | ||||
|          | ||||
|         /*  | ||||
|          * Since we need to account for non-base64 data urls, we need to handle  | ||||
|          * ' and ) being part of the data string. Hence switching to indexOf, | ||||
|          * to determine whether or not we have matching string terminators and | ||||
|          * handling sb appends directly, instead of using matcher.append* methods. | ||||
|          */ | ||||
|  | ||||
|         while (m.find()) { | ||||
|  | ||||
|         	int startIndex = m.start() + 4;  	// "url(".length() | ||||
|     		String terminator = m.group(1);     // ', " or empty (not quoted) | ||||
|     		 | ||||
|     		if (terminator.length() == 0) { | ||||
|     		 	terminator = ")"; | ||||
|     		} | ||||
|  | ||||
|     		boolean foundTerminator = false; | ||||
|  | ||||
|     		int endIndex = m.end() - 1; | ||||
|     		while(foundTerminator == false && endIndex+1 <= maxIndex) { | ||||
|     			endIndex = css.indexOf(terminator, endIndex+1); | ||||
|  | ||||
|     			if ((endIndex > 0) && (css.charAt(endIndex-1) != '\\')) { | ||||
|     				foundTerminator = true; | ||||
|     				if (!")".equals(terminator)) { | ||||
|     					endIndex = css.indexOf(")", endIndex);  | ||||
|     				} | ||||
|     			} | ||||
|     		} | ||||
|  | ||||
|     		// Enough searching, start moving stuff over to the buffer | ||||
| 			sb.append(css.substring(appendIndex, m.start())); | ||||
|  | ||||
|     		if (foundTerminator) { | ||||
|     			String token = css.substring(startIndex, endIndex); | ||||
|     			token = token.replaceAll("\\s+", ""); | ||||
| 	    		preservedTokens.add(token); | ||||
|  | ||||
| 	    		String preserver = "url(___YUICSSMIN_PRESERVED_TOKEN_" + (preservedTokens.size() - 1) + "___)"; | ||||
| 	    		sb.append(preserver); | ||||
|  | ||||
| 	    		appendIndex = endIndex + 1; | ||||
|     		} else { | ||||
|     			// No end terminator found, re-add the whole match. Should we throw/warn here? | ||||
|     			sb.append(css.substring(m.start(), m.end())); | ||||
|     			appendIndex = m.end(); | ||||
|     		} | ||||
|         } | ||||
|  | ||||
|         sb.append(css.substring(appendIndex)); | ||||
|  | ||||
|         return sb.toString(); | ||||
|     } | ||||
|  | ||||
|     public void compress(Writer out, int linebreakpos) | ||||
|             throws IOException { | ||||
|  | ||||
|         Pattern p; | ||||
|         Matcher m; | ||||
|         String css = srcsb.toString(); | ||||
|  | ||||
|         int startIndex = 0; | ||||
|         int endIndex = 0; | ||||
|         int i = 0; | ||||
|         int max = 0; | ||||
|         ArrayList preservedTokens = new ArrayList(0); | ||||
|         ArrayList comments = new ArrayList(0); | ||||
|         String token; | ||||
|         int totallen = css.length(); | ||||
|         String placeholder; | ||||
|  | ||||
|         css = this.extractDataUrls(css, preservedTokens); | ||||
|  | ||||
|         StringBuffer sb = new StringBuffer(css); | ||||
|  | ||||
|         // collect all comment blocks... | ||||
|         while ((startIndex = sb.indexOf("/*", startIndex)) >= 0) { | ||||
|             endIndex = sb.indexOf("*/", startIndex + 2); | ||||
|             if (endIndex < 0) { | ||||
|                 endIndex = totallen; | ||||
|             } | ||||
|  | ||||
|             token = sb.substring(startIndex + 2, endIndex); | ||||
|             comments.add(token); | ||||
|             sb.replace(startIndex + 2, endIndex, "___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_" + (comments.size() - 1) + "___"); | ||||
|             startIndex += 2; | ||||
|         } | ||||
|         css = sb.toString(); | ||||
|  | ||||
|         // preserve strings so their content doesn't get accidentally minified | ||||
|         sb = new StringBuffer(); | ||||
|         p = Pattern.compile("(\"([^\\\\\"]|\\\\.|\\\\)*\")|(\'([^\\\\\']|\\\\.|\\\\)*\')"); | ||||
|         m = p.matcher(css); | ||||
|         while (m.find()) { | ||||
|             token = m.group(); | ||||
|             char quote = token.charAt(0); | ||||
|             token = token.substring(1, token.length() - 1); | ||||
|  | ||||
|             // maybe the string contains a comment-like substring? | ||||
|             // one, maybe more? put'em back then | ||||
|             if (token.indexOf("___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_") >= 0) { | ||||
|                 for (i = 0, max = comments.size(); i < max; i += 1) { | ||||
|                     token = token.replace("___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_" + i + "___", comments.get(i).toString()); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             // minify alpha opacity in filter strings | ||||
|             token = token.replaceAll("(?i)progid:DXImageTransform.Microsoft.Alpha\\(Opacity=", "alpha(opacity="); | ||||
|  | ||||
|             preservedTokens.add(token); | ||||
|             String preserver = quote + "___YUICSSMIN_PRESERVED_TOKEN_" + (preservedTokens.size() - 1) + "___" + quote; | ||||
|             m.appendReplacement(sb, preserver); | ||||
|         } | ||||
|         m.appendTail(sb); | ||||
|         css = sb.toString(); | ||||
|  | ||||
|  | ||||
|         // strings are safe, now wrestle the comments | ||||
|         for (i = 0, max = comments.size(); i < max; i += 1) { | ||||
|  | ||||
|             token = comments.get(i).toString(); | ||||
|             placeholder = "___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_" + i + "___"; | ||||
|  | ||||
|             // ! in the first position of the comment means preserve | ||||
|             // so push to the preserved tokens while stripping the ! | ||||
|             if (token.startsWith("!")) { | ||||
|                 preservedTokens.add(token); | ||||
|                 css = css.replace(placeholder,  "___YUICSSMIN_PRESERVED_TOKEN_" + (preservedTokens.size() - 1) + "___"); | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             // \ in the last position looks like hack for Mac/IE5 | ||||
|             // shorten that to /*\*/ and the next one to /**/ | ||||
|             if (token.endsWith("\\")) { | ||||
|                 preservedTokens.add("\\"); | ||||
|                 css = css.replace(placeholder,  "___YUICSSMIN_PRESERVED_TOKEN_" + (preservedTokens.size() - 1) + "___"); | ||||
|                 i = i + 1; // attn: advancing the loop | ||||
|                 preservedTokens.add(""); | ||||
|                 css = css.replace("___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_" + i + "___",  "___YUICSSMIN_PRESERVED_TOKEN_" + (preservedTokens.size() - 1) + "___"); | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             // keep empty comments after child selectors (IE7 hack) | ||||
|             // e.g. html >/**/ body | ||||
|             if (token.length() == 0) { | ||||
|                 startIndex = css.indexOf(placeholder); | ||||
|                 if (startIndex > 2) { | ||||
|                     if (css.charAt(startIndex - 3) == '>') { | ||||
|                         preservedTokens.add(""); | ||||
|                         css = css.replace(placeholder,  "___YUICSSMIN_PRESERVED_TOKEN_" + (preservedTokens.size() - 1) + "___"); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             // in all other cases kill the comment | ||||
|             css = css.replace("/*" + placeholder + "*/", ""); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         // Normalize all whitespace strings to single spaces. Easier to work with that way. | ||||
|         css = css.replaceAll("\\s+", " "); | ||||
|  | ||||
|         // Remove the spaces before the things that should not have spaces before them. | ||||
|         // But, be careful not to turn "p :link {...}" into "p:link{...}" | ||||
|         // Swap out any pseudo-class colons with the token, and then swap back. | ||||
|         sb = new StringBuffer(); | ||||
|         p = Pattern.compile("(^|\\})(([^\\{:])+:)+([^\\{]*\\{)"); | ||||
|         m = p.matcher(css); | ||||
|         while (m.find()) { | ||||
|             String s = m.group(); | ||||
|             s = s.replaceAll(":", "___YUICSSMIN_PSEUDOCLASSCOLON___"); | ||||
|             s = s.replaceAll( "\\\\", "\\\\\\\\" ).replaceAll( "\\$", "\\\\\\$" ); | ||||
|             m.appendReplacement(sb, s); | ||||
|         } | ||||
|         m.appendTail(sb); | ||||
|         css = sb.toString(); | ||||
|         // Remove spaces before the things that should not have spaces before them. | ||||
|         css = css.replaceAll("\\s+([!{};:>+\\(\\)\\],])", "$1"); | ||||
|         // bring back the colon | ||||
|         css = css.replaceAll("___YUICSSMIN_PSEUDOCLASSCOLON___", ":"); | ||||
|  | ||||
|         // retain space for special IE6 cases | ||||
|         css = css.replaceAll(":first\\-(line|letter)(\\{|,)", ":first-$1 $2"); | ||||
|  | ||||
|         // no space after the end of a preserved comment | ||||
|         css = css.replaceAll("\\*/ ", "*/"); | ||||
|  | ||||
|         // If there is a @charset, then only allow one, and push to the top of the file. | ||||
|         css = css.replaceAll("^(.*)(@charset \"[^\"]*\";)", "$2$1"); | ||||
|         css = css.replaceAll("^(\\s*@charset [^;]+;\\s*)+", "$1"); | ||||
|  | ||||
|         // Put the space back in some cases, to support stuff like | ||||
|         // @media screen and (-webkit-min-device-pixel-ratio:0){ | ||||
|         css = css.replaceAll("\\band\\(", "and ("); | ||||
|  | ||||
|         // Remove the spaces after the things that should not have spaces after them. | ||||
|         css = css.replaceAll("([!{}:;>+\\(\\[,])\\s+", "$1"); | ||||
|  | ||||
|         // remove unnecessary semicolons | ||||
|         css = css.replaceAll(";+}", "}"); | ||||
|  | ||||
|         // Replace 0(px,em,%) with 0. | ||||
|         css = css.replaceAll("([\\s:])(0)(px|em|%|in|cm|mm|pc|pt|ex)", "$1$2"); | ||||
|  | ||||
|         // Replace 0 0 0 0; with 0. | ||||
|         css = css.replaceAll(":0 0 0 0(;|})", ":0$1"); | ||||
|         css = css.replaceAll(":0 0 0(;|})", ":0$1"); | ||||
|         css = css.replaceAll(":0 0(;|})", ":0$1"); | ||||
|  | ||||
|  | ||||
|         // Replace background-position:0; with background-position:0 0; | ||||
|         // same for transform-origin | ||||
|         sb = new StringBuffer(); | ||||
|         p = Pattern.compile("(?i)(background-position|transform-origin|webkit-transform-origin|moz-transform-origin|o-transform-origin|ms-transform-origin):0(;|})"); | ||||
|         m = p.matcher(css); | ||||
|         while (m.find()) { | ||||
|             m.appendReplacement(sb, m.group(1).toLowerCase() + ":0 0" + m.group(2)); | ||||
|         } | ||||
|         m.appendTail(sb); | ||||
|         css = sb.toString(); | ||||
|  | ||||
|         // Replace 0.6 to .6, but only when preceded by : or a white-space | ||||
|         css = css.replaceAll("(:|\\s)0+\\.(\\d+)", "$1.$2"); | ||||
|  | ||||
|         // Shorten colors from rgb(51,102,153) to #336699 | ||||
|         // This makes it more likely that it'll get further compressed in the next step. | ||||
|         p = Pattern.compile("rgb\\s*\\(\\s*([0-9,\\s]+)\\s*\\)"); | ||||
|         m = p.matcher(css); | ||||
|         sb = new StringBuffer(); | ||||
|         while (m.find()) { | ||||
|             String[] rgbcolors = m.group(1).split(","); | ||||
|             StringBuffer hexcolor = new StringBuffer("#"); | ||||
|             for (i = 0; i < rgbcolors.length; i++) { | ||||
|                 int val = Integer.parseInt(rgbcolors[i]); | ||||
|                 if (val < 16) { | ||||
|                     hexcolor.append("0"); | ||||
|                 } | ||||
|                 hexcolor.append(Integer.toHexString(val)); | ||||
|             } | ||||
|             m.appendReplacement(sb, hexcolor.toString()); | ||||
|         } | ||||
|         m.appendTail(sb); | ||||
|         css = sb.toString(); | ||||
|  | ||||
|         // Shorten colors from #AABBCC to #ABC. Note that we want to make sure | ||||
|         // the color is not preceded by either ", " or =. Indeed, the property | ||||
|         //     filter: chroma(color="#FFFFFF"); | ||||
|         // would become | ||||
|         //     filter: chroma(color="#FFF"); | ||||
|         // which makes the filter break in IE. | ||||
|         // We also want to make sure we're only compressing #AABBCC patterns inside { }, not id selectors ( #FAABAC {} ) | ||||
|         // We also want to avoid compressing invalid values (e.g. #AABBCCD to #ABCD) | ||||
|         p = Pattern.compile("(\\=\\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])" + "(:?\\}|[^0-9a-fA-F{][^{]*?\\})"); | ||||
|  | ||||
|         m = p.matcher(css); | ||||
|         sb = new StringBuffer(); | ||||
|         int index = 0; | ||||
|  | ||||
|         while (m.find(index)) { | ||||
|  | ||||
|         	sb.append(css.substring(index, m.start())); | ||||
|  | ||||
|         	boolean isFilter = (m.group(1) != null && !"".equals(m.group(1)));  | ||||
|  | ||||
|         	if (isFilter) { | ||||
|         		// Restore, as is. Compression will break filters | ||||
|         		sb.append(m.group(1) + "#" + m.group(2) + m.group(3) + m.group(4) + m.group(5) + m.group(6) + m.group(7)); | ||||
|         	} else { | ||||
|         		if( m.group(2).equalsIgnoreCase(m.group(3)) && | ||||
|                     m.group(4).equalsIgnoreCase(m.group(5)) && | ||||
|                     m.group(6).equalsIgnoreCase(m.group(7))) { | ||||
|  | ||||
| 	        		// #AABBCC pattern | ||||
| 	                sb.append("#" + (m.group(3) + m.group(5) + m.group(7)).toLowerCase()); | ||||
|  | ||||
|         		} else { | ||||
|  | ||||
|         			// Non-compressible color, restore, but lower case. | ||||
|         			sb.append("#" + (m.group(2) + m.group(3) + m.group(4) + m.group(5) + m.group(6) + m.group(7)).toLowerCase()); | ||||
|         		} | ||||
|             } | ||||
|  | ||||
|         	index = m.end(7); | ||||
|         } | ||||
|  | ||||
|         sb.append(css.substring(index)); | ||||
|         css = sb.toString(); | ||||
|  | ||||
|         // border: none -> border:0 | ||||
|         sb = new StringBuffer(); | ||||
|         p = Pattern.compile("(?i)(border|border-top|border-right|border-bottom|border-right|outline|background):none(;|})"); | ||||
|         m = p.matcher(css); | ||||
|         while (m.find()) { | ||||
|             m.appendReplacement(sb, m.group(1).toLowerCase() + ":0" + m.group(2)); | ||||
|         } | ||||
|         m.appendTail(sb); | ||||
|         css = sb.toString(); | ||||
|  | ||||
|         // shorter opacity IE filter | ||||
|         css = css.replaceAll("(?i)progid:DXImageTransform.Microsoft.Alpha\\(Opacity=", "alpha(opacity="); | ||||
|  | ||||
|         // Remove empty rules. | ||||
|         css = css.replaceAll("[^\\}\\{/;]+\\{\\}", ""); | ||||
|  | ||||
|         // TODO: Should this be after we re-insert tokens. These could alter the break points. However then | ||||
|         // we'd need to make sure we don't break in the middle of a string etc. | ||||
|         if (linebreakpos >= 0) { | ||||
|             // Some source control tools don't like it when files containing lines longer | ||||
|             // than, say 8000 characters, are checked in. The linebreak option is used in | ||||
|             // that case to split long lines after a specific column. | ||||
|             i = 0; | ||||
|             int linestartpos = 0; | ||||
|             sb = new StringBuffer(css); | ||||
|             while (i < sb.length()) { | ||||
|                 char c = sb.charAt(i++); | ||||
|                 if (c == '}' && i - linestartpos > linebreakpos) { | ||||
|                     sb.insert(i, '\n'); | ||||
|                     linestartpos = i; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             css = sb.toString(); | ||||
|         } | ||||
|  | ||||
|         // Replace multiple semi-colons in a row by a single one | ||||
|         // See SF bug #1980989 | ||||
|         css = css.replaceAll(";;+", ";"); | ||||
|  | ||||
|         // restore preserved comments and strings | ||||
|         for(i = 0, max = preservedTokens.size(); i < max; i++) { | ||||
|             css = css.replace("___YUICSSMIN_PRESERVED_TOKEN_" + i + "___", preservedTokens.get(i).toString()); | ||||
|         } | ||||
|  | ||||
|         // Trim the final string (for any leading or trailing white spaces) | ||||
|         css = css.trim(); | ||||
|  | ||||
|         // Write the output... | ||||
|         out.write(css); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										171
									
								
								vendor/mrclay/minify/min/lib/Minify/YUI/CssCompressor.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										171
									
								
								vendor/mrclay/minify/min/lib/Minify/YUI/CssCompressor.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,171 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Class Minify_YUI_CssCompressor  | ||||
|  * @package Minify | ||||
|  * | ||||
|  * YUI Compressor | ||||
|  * Author: Julien Lecomte -  http://www.julienlecomte.net/ | ||||
|  * Author: Isaac Schlueter - http://foohack.com/ | ||||
|  * Author: Stoyan Stefanov - http://phpied.com/ | ||||
|  * Author: Steve Clay      - http://www.mrclay.org/ (PHP port) | ||||
|  * Copyright (c) 2009 Yahoo! Inc.  All rights reserved. | ||||
|  * The copyrights embodied in the content of this file are licensed | ||||
|  * by Yahoo! Inc. under the BSD (revised) open source license. | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * Compress CSS (incomplete DO NOT USE) | ||||
|  *  | ||||
|  * @see https://github.com/yui/yuicompressor/blob/master/src/com/yahoo/platform/yui/compressor/CssCompressor.java | ||||
|  * | ||||
|  * @package Minify | ||||
|  */ | ||||
| class Minify_YUI_CssCompressor { | ||||
|  | ||||
|     /** | ||||
|      * Minify a CSS string | ||||
|      * | ||||
|      * @param string $css | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public function compress($css, $linebreakpos = 0) | ||||
|     { | ||||
|         $css = str_replace("\r\n", "\n", $css); | ||||
|  | ||||
|         /** | ||||
|          * @todo comment removal | ||||
|          * @todo re-port from newer Java version | ||||
|          */ | ||||
|  | ||||
|         // Normalize all whitespace strings to single spaces. Easier to work with that way. | ||||
|         $css = preg_replace('@\s+@', ' ', $css); | ||||
|  | ||||
|         // Make a pseudo class for the Box Model Hack | ||||
|         $css = preg_replace("@\"\\\\\"}\\\\\"\"@", "___PSEUDOCLASSBMH___", $css); | ||||
|  | ||||
|         // Remove the spaces before the things that should not have spaces before them. | ||||
|         // But, be careful not to turn "p :link {...}" into "p:link{...}" | ||||
|         // Swap out any pseudo-class colons with the token, and then swap back. | ||||
|         $css = preg_replace_callback("@(^|\\})(([^\\{:])+:)+([^\\{]*\\{)@", array($this, '_removeSpacesCB'), $css); | ||||
|  | ||||
|         $css = preg_replace("@\\s+([!{};:>+\\(\\)\\],])@", "$1", $css); | ||||
|         $css = str_replace("___PSEUDOCLASSCOLON___", ":", $css); | ||||
|  | ||||
|         // Remove the spaces after the things that should not have spaces after them. | ||||
|         $css = preg_replace("@([!{}:;>+\\(\\[,])\\s+@", "$1", $css); | ||||
|  | ||||
|         // Add the semicolon where it's missing. | ||||
|         $css = preg_replace("@([^;\\}])}@", "$1;}", $css); | ||||
|  | ||||
|         // Replace 0(px,em,%) with 0. | ||||
|         $css = preg_replace("@([\\s:])(0)(px|em|%|in|cm|mm|pc|pt|ex)@", "$1$2", $css); | ||||
|  | ||||
|         // Replace 0 0 0 0; with 0. | ||||
|         $css = str_replace(":0 0 0 0;", ":0;", $css); | ||||
|         $css = str_replace(":0 0 0;", ":0;", $css); | ||||
|         $css = str_replace(":0 0;", ":0;", $css); | ||||
|  | ||||
|         // Replace background-position:0; with background-position:0 0; | ||||
|         $css = str_replace("background-position:0;", "background-position:0 0;", $css); | ||||
|  | ||||
|         // Replace 0.6 to .6, but only when preceded by : or a white-space | ||||
|         $css = preg_replace("@(:|\\s)0+\\.(\\d+)@", "$1.$2", $css); | ||||
|  | ||||
|         // Shorten colors from rgb(51,102,153) to #336699 | ||||
|         // This makes it more likely that it'll get further compressed in the next step. | ||||
|         $css = preg_replace_callback("@rgb\\s*\\(\\s*([0-9,\\s]+)\\s*\\)@", array($this, '_shortenRgbCB'), $css); | ||||
|  | ||||
|         // Shorten colors from #AABBCC to #ABC. Note that we want to make sure | ||||
|         // the color is not preceded by either ", " or =. Indeed, the property | ||||
|         //     filter: chroma(color="#FFFFFF"); | ||||
|         // would become | ||||
|         //     filter: chroma(color="#FFF"); | ||||
|         // which makes the filter break in IE. | ||||
|         $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); | ||||
|  | ||||
|         // Remove empty rules. | ||||
|         $css = preg_replace("@[^\\}]+\\{;\\}@", "", $css); | ||||
|  | ||||
|         $linebreakpos = isset($this->_options['linebreakpos']) | ||||
|             ? $this->_options['linebreakpos'] | ||||
|             : 0; | ||||
|  | ||||
|         if ($linebreakpos > 0) { | ||||
|             // Some source control tools don't like it when files containing lines longer | ||||
|             // than, say 8000 characters, are checked in. The linebreak option is used in | ||||
|             // that case to split long lines after a specific column. | ||||
|             $i = 0; | ||||
|             $linestartpos = 0; | ||||
|             $sb = $css; | ||||
|  | ||||
|             // make sure strlen returns byte count | ||||
|             $mbIntEnc = null; | ||||
|             if (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) { | ||||
|                 $mbIntEnc = mb_internal_encoding(); | ||||
|                 mb_internal_encoding('8bit'); | ||||
|             } | ||||
|             $sbLength = strlen($css); | ||||
|             while ($i < $sbLength) { | ||||
|                 $c = $sb[$i++]; | ||||
|                 if ($c === '}' && $i - $linestartpos > $linebreakpos) { | ||||
|                     $sb = substr_replace($sb, "\n", $i, 0); | ||||
|                     $sbLength++; | ||||
|                     $linestartpos = $i; | ||||
|                 } | ||||
|             } | ||||
|             $css = $sb; | ||||
|  | ||||
|             // undo potential mb_encoding change | ||||
|             if ($mbIntEnc !== null) { | ||||
|                 mb_internal_encoding($mbIntEnc); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Replace the pseudo class for the Box Model Hack | ||||
|         $css = str_replace("___PSEUDOCLASSBMH___", "\"\\\\\"}\\\\\"\"", $css); | ||||
|  | ||||
|         // Replace multiple semi-colons in a row by a single one | ||||
|         // See SF bug #1980989 | ||||
|         $css = preg_replace("@;;+@", ";", $css); | ||||
|  | ||||
|         // prevent triggering IE6 bug: http://www.crankygeek.com/ie6pebug/ | ||||
|         $css = preg_replace('/:first-l(etter|ine)\\{/', ':first-l$1 {', $css); | ||||
|  | ||||
|         // Trim the final string (for any leading or trailing white spaces) | ||||
|         $css = trim($css); | ||||
|  | ||||
|         return $css; | ||||
|     } | ||||
|  | ||||
|     protected function _removeSpacesCB($m) | ||||
|     { | ||||
|         return str_replace(':', '___PSEUDOCLASSCOLON___', $m[0]); | ||||
|     } | ||||
|  | ||||
|     protected function _shortenRgbCB($m) | ||||
|     { | ||||
|         $rgbcolors = explode(',', $m[1]); | ||||
|         $hexcolor = '#'; | ||||
|         for ($i = 0; $i < count($rgbcolors); $i++) { | ||||
|             $val = round($rgbcolors[$i]); | ||||
|             if ($val < 16) { | ||||
|                 $hexcolor .= '0'; | ||||
|             } | ||||
|             $hexcolor .= dechex($val); | ||||
|         } | ||||
|         return $hexcolor; | ||||
|     } | ||||
|  | ||||
|     protected function _shortenHexCB($m) | ||||
|     { | ||||
|         // Test for AABBCC pattern | ||||
|         if ((strtolower($m[3])===strtolower($m[4])) && | ||||
|                 (strtolower($m[5])===strtolower($m[6])) && | ||||
|                 (strtolower($m[7])===strtolower($m[8]))) { | ||||
|             return $m[1] . $m[2] . "#" . $m[3] . $m[5] . $m[7]; | ||||
|         } else { | ||||
|             return $m[0]; | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										156
									
								
								vendor/mrclay/minify/min/lib/Minify/YUICompressor.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								vendor/mrclay/minify/min/lib/Minify/YUICompressor.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,156 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Class Minify_YUICompressor  | ||||
|  * @package Minify | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * Compress Javascript/CSS using the YUI Compressor | ||||
|  *  | ||||
|  * You must set $jarFile and $tempDir before calling the minify functions. | ||||
|  * Also, depending on your shell's environment, you may need to specify | ||||
|  * the full path to java in $javaExecutable or use putenv() to setup the | ||||
|  * Java environment. | ||||
|  *  | ||||
|  * <code> | ||||
|  * Minify_YUICompressor::$jarFile = '/path/to/yuicompressor-2.4.6.jar'; | ||||
|  * Minify_YUICompressor::$tempDir = '/tmp'; | ||||
|  * $code = Minify_YUICompressor::minifyJs( | ||||
|  *   $code | ||||
|  *   ,array('nomunge' => true, 'line-break' => 1000) | ||||
|  * ); | ||||
|  * </code> | ||||
|  * | ||||
|  * Note: In case you run out stack (default is 512k), you may increase stack size in $options: | ||||
|  *   array('stack-size' => '2048k') | ||||
|  * | ||||
|  * @todo unit tests, $options docs | ||||
|  *  | ||||
|  * @package Minify | ||||
|  * @author Stephen Clay <steve@mrclay.org> | ||||
|  */ | ||||
| class Minify_YUICompressor { | ||||
|  | ||||
|     /** | ||||
|      * Filepath of the YUI Compressor jar file. This must be set before | ||||
|      * calling minifyJs() or minifyCss(). | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     public static $jarFile = null; | ||||
|      | ||||
|     /** | ||||
|      * Writable temp directory. This must be set before calling minifyJs() | ||||
|      * or minifyCss(). | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     public static $tempDir = null; | ||||
|      | ||||
|     /** | ||||
|      * Filepath of "java" executable (may be needed if not in shell's PATH) | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     public static $javaExecutable = 'java'; | ||||
|      | ||||
|     /** | ||||
|      * Minify a Javascript string | ||||
|      *  | ||||
|      * @param string $js | ||||
|      *  | ||||
|      * @param array $options (verbose is ignored) | ||||
|      *  | ||||
|      * @see http://www.julienlecomte.net/yuicompressor/README | ||||
|      *  | ||||
|      * @return string  | ||||
|      */ | ||||
|     public static function minifyJs($js, $options = array()) | ||||
|     { | ||||
|         return self::_minify('js', $js, $options); | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Minify a CSS string | ||||
|      *  | ||||
|      * @param string $css | ||||
|      *  | ||||
|      * @param array $options (verbose is ignored) | ||||
|      *  | ||||
|      * @see http://www.julienlecomte.net/yuicompressor/README | ||||
|      *  | ||||
|      * @return string  | ||||
|      */ | ||||
|     public static function minifyCss($css, $options = array()) | ||||
|     { | ||||
|         return self::_minify('css', $css, $options); | ||||
|     } | ||||
|      | ||||
|     private static function _minify($type, $content, $options) | ||||
|     { | ||||
|         self::_prepare(); | ||||
|         if (! ($tmpFile = tempnam(self::$tempDir, 'yuic_'))) { | ||||
|             throw new Exception('Minify_YUICompressor : could not create temp file in "'.self::$tempDir.'".'); | ||||
|         } | ||||
|         file_put_contents($tmpFile, $content); | ||||
|         exec(self::_getCmd($options, $type, $tmpFile), $output, $result_code); | ||||
|         unlink($tmpFile); | ||||
|         if ($result_code != 0) { | ||||
|             throw new Exception('Minify_YUICompressor : YUI compressor execution failed.'); | ||||
|         } | ||||
|         return implode("\n", $output); | ||||
|     } | ||||
|      | ||||
|     private static function _getCmd($userOptions, $type, $tmpFile) | ||||
|     { | ||||
|         $o = array_merge( | ||||
|             array( | ||||
|                 'charset' => '' | ||||
|                 ,'line-break' => 5000 | ||||
|                 ,'type' => $type | ||||
|                 ,'nomunge' => false | ||||
|                 ,'preserve-semi' => false | ||||
|                 ,'disable-optimizations' => false | ||||
| 	            ,'stack-size' => '' | ||||
|             ) | ||||
|             ,$userOptions | ||||
|         ); | ||||
|         $cmd = self::$javaExecutable | ||||
| 	         . (!empty($o['stack-size']) | ||||
| 	            ? ' -Xss' . $o['stack-size'] | ||||
| 	            : '') | ||||
| 	         . ' -jar ' . escapeshellarg(self::$jarFile) | ||||
|              . " --type {$type}" | ||||
|              . (preg_match('/^[\\da-zA-Z0-9\\-]+$/', $o['charset']) | ||||
|                 ? " --charset {$o['charset']}"  | ||||
|                 : '') | ||||
|              . (is_numeric($o['line-break']) && $o['line-break'] >= 0 | ||||
|                 ? ' --line-break ' . (int)$o['line-break'] | ||||
|                 : ''); | ||||
|         if ($type === 'js') { | ||||
|             foreach (array('nomunge', 'preserve-semi', 'disable-optimizations') as $opt) { | ||||
|                 $cmd .= $o[$opt]  | ||||
|                     ? " --{$opt}" | ||||
|                     : ''; | ||||
|             } | ||||
|         } | ||||
|         return $cmd . ' ' . escapeshellarg($tmpFile); | ||||
|     } | ||||
|      | ||||
|     private static function _prepare() | ||||
|     { | ||||
|         if (! is_file(self::$jarFile)) { | ||||
|             throw new Exception('Minify_YUICompressor : $jarFile('.self::$jarFile.') is not a valid file.'); | ||||
|         } | ||||
|         if (! is_readable(self::$jarFile)) { | ||||
|             throw new Exception('Minify_YUICompressor : $jarFile('.self::$jarFile.') is not readable.'); | ||||
|         } | ||||
|         if (! is_dir(self::$tempDir)) { | ||||
|             throw new Exception('Minify_YUICompressor : $tempDir('.self::$tempDir.') is not a valid direcotry.'); | ||||
|         } | ||||
|         if (! is_writable(self::$tempDir)) { | ||||
|             throw new Exception('Minify_YUICompressor : $tempDir('.self::$tempDir.') is not writable.'); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
							
								
								
									
										384
									
								
								vendor/mrclay/minify/min/lib/MrClay/Cli.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										384
									
								
								vendor/mrclay/minify/min/lib/MrClay/Cli.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,384 @@ | ||||
| <?php  | ||||
|  | ||||
| namespace MrClay; | ||||
|  | ||||
| use MrClay\Cli\Arg; | ||||
| use InvalidArgumentException; | ||||
|  | ||||
| /** | ||||
|  * Forms a front controller for a console app, handling and validating arguments (options) | ||||
|  * | ||||
|  * Instantiate, add arguments, then call validate(). Afterwards, the user's valid arguments | ||||
|  * and their values will be available in $cli->values. | ||||
|  * | ||||
|  * You may also specify that some arguments be used to provide input/output. By communicating | ||||
|  * solely through the file pointers provided by openInput()/openOutput(), you can make your | ||||
|  * app more flexible to end users. | ||||
|  * | ||||
|  * @author Steve Clay <steve@mrclay.org> | ||||
|  * @license http://www.opensource.org/licenses/mit-license.php  MIT License | ||||
|  */ | ||||
| class Cli { | ||||
|      | ||||
|     /** | ||||
|      * @var array validation errors | ||||
|      */ | ||||
|     public $errors = array(); | ||||
|      | ||||
|     /** | ||||
|      * @var array option values available after validation. | ||||
|      *  | ||||
|      * E.g. array( | ||||
|      *      'a' => false              // option was missing | ||||
|      *     ,'b' => true               // option was present | ||||
|      *     ,'c' => "Hello"            // option had value | ||||
|      *     ,'f' => "/home/user/file"  // file path from root | ||||
|      *     ,'f.raw' => "~/file"       // file path as given to option | ||||
|      * ) | ||||
|      */ | ||||
|     public $values = array(); | ||||
|  | ||||
|     /** | ||||
|      * @var array | ||||
|      */ | ||||
|     public $moreArgs = array(); | ||||
|  | ||||
|     /** | ||||
|      * @var array | ||||
|      */ | ||||
|     public $debug = array(); | ||||
|  | ||||
|     /** | ||||
|      * @var bool The user wants help info | ||||
|      */ | ||||
|     public $isHelpRequest = false; | ||||
|  | ||||
|     /** | ||||
|      * @var Arg[] | ||||
|      */ | ||||
|     protected $_args = array(); | ||||
|  | ||||
|     /** | ||||
|      * @var resource | ||||
|      */ | ||||
|     protected $_stdin = null; | ||||
|  | ||||
|     /** | ||||
|      * @var resource | ||||
|      */ | ||||
|     protected $_stdout = null; | ||||
|      | ||||
|     /** | ||||
|      * @param bool $exitIfNoStdin (default true) Exit() if STDIN is not defined | ||||
|      */ | ||||
|     public function __construct($exitIfNoStdin = true) | ||||
|     { | ||||
|         if ($exitIfNoStdin && ! defined('STDIN')) { | ||||
|             exit('This script is for command-line use only.'); | ||||
|         } | ||||
|         if (isset($GLOBALS['argv'][1]) | ||||
|              && ($GLOBALS['argv'][1] === '-?' || $GLOBALS['argv'][1] === '--help')) { | ||||
|             $this->isHelpRequest = true; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Arg|string $letter | ||||
|      * @return Arg | ||||
|      */ | ||||
|     public function addOptionalArg($letter) | ||||
|     { | ||||
|         return $this->addArgument($letter, false); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Arg|string $letter | ||||
|      * @return Arg | ||||
|      */ | ||||
|     public function addRequiredArg($letter) | ||||
|     { | ||||
|         return $this->addArgument($letter, true); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $letter | ||||
|      * @param bool $required | ||||
|      * @param Arg|null $arg | ||||
|      * @return Arg | ||||
|      * @throws InvalidArgumentException | ||||
|      */ | ||||
|     public function addArgument($letter, $required, Arg $arg = null) | ||||
|     { | ||||
|         if (! preg_match('/^[a-zA-Z]$/', $letter)) { | ||||
|             throw new InvalidArgumentException('$letter must be in [a-zA-Z]'); | ||||
|         } | ||||
|         if (! $arg) { | ||||
|             $arg = new Arg($required); | ||||
|         } | ||||
|         $this->_args[$letter] = $arg; | ||||
|         return $arg; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $letter | ||||
|      * @return Arg|null | ||||
|      */ | ||||
|     public function getArgument($letter) | ||||
|     { | ||||
|         return isset($this->_args[$letter]) ? $this->_args[$letter] : null; | ||||
|     } | ||||
|  | ||||
|     /* | ||||
|      * Read and validate options | ||||
|      *  | ||||
|      * @return bool true if all options are valid | ||||
|      */ | ||||
|     public function validate() | ||||
|     { | ||||
|         $options = ''; | ||||
|         $this->errors = array(); | ||||
|         $this->values = array(); | ||||
|         $this->_stdin = null; | ||||
|          | ||||
|         if ($this->isHelpRequest) { | ||||
|             return false; | ||||
|         } | ||||
|          | ||||
|         $lettersUsed = ''; | ||||
|         foreach ($this->_args as $letter => $arg) { | ||||
|             /* @var Arg $arg  */ | ||||
|             $options .= $letter; | ||||
|             $lettersUsed .= $letter; | ||||
|              | ||||
|             if ($arg->mayHaveValue || $arg->mustHaveValue) { | ||||
|                 $options .= ($arg->mustHaveValue ? ':' : '::'); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $this->debug['argv'] = $GLOBALS['argv']; | ||||
|         $argvCopy = array_slice($GLOBALS['argv'], 1); | ||||
|         $o = getopt($options); | ||||
|         $this->debug['getopt_options'] = $options; | ||||
|         $this->debug['getopt_return'] = $o; | ||||
|  | ||||
|         foreach ($this->_args as $letter => $arg) { | ||||
|             /* @var Arg $arg  */ | ||||
|             $this->values[$letter] = false; | ||||
|             if (isset($o[$letter])) { | ||||
|                 if (is_bool($o[$letter])) { | ||||
|  | ||||
|                     // remove from argv copy | ||||
|                     $k = array_search("-$letter", $argvCopy); | ||||
|                     if ($k !== false) { | ||||
|                         array_splice($argvCopy, $k, 1); | ||||
|                     } | ||||
|  | ||||
|                     if ($arg->mustHaveValue) { | ||||
|                         $this->addError($letter, "Missing value"); | ||||
|                     } else { | ||||
|                         $this->values[$letter] = true; | ||||
|                     } | ||||
|                 } else { | ||||
|                     // string | ||||
|                     $this->values[$letter] = $o[$letter]; | ||||
|                     $v =& $this->values[$letter]; | ||||
|  | ||||
|                     // remove from argv copy | ||||
|                     // first look for -ovalue or -o=value | ||||
|                     $pattern = "/^-{$letter}=?" . preg_quote($v, '/') . "$/"; | ||||
|                     $foundInArgv = false; | ||||
|                     foreach ($argvCopy as $k => $argV) { | ||||
|                         if (preg_match($pattern, $argV)) { | ||||
|                             array_splice($argvCopy, $k, 1); | ||||
|                             $foundInArgv = true; | ||||
|                             break; | ||||
|                         } | ||||
|                     } | ||||
|                     if (! $foundInArgv) { | ||||
|                         // space separated | ||||
|                         $k = array_search("-$letter", $argvCopy); | ||||
|                         if ($k !== false) { | ||||
|                             array_splice($argvCopy, $k, 2); | ||||
|                         } | ||||
|                     } | ||||
|                      | ||||
|                     // check that value isn't really another option | ||||
|                     if (strlen($lettersUsed) > 1) { | ||||
|                         $pattern = "/^-[" . str_replace($letter, '', $lettersUsed) . "]/i"; | ||||
|                         if (preg_match($pattern, $v)) { | ||||
|                             $this->addError($letter, "Value was read as another option: %s", $v); | ||||
|                             return false; | ||||
|                         }     | ||||
|                     } | ||||
|                     if ($arg->assertFile || $arg->assertDir) { | ||||
|                         if ($v[0] !== '/' && $v[0] !== '~') { | ||||
|                             $this->values["$letter.raw"] = $v; | ||||
|                             $v = getcwd() . "/$v"; | ||||
|                         } | ||||
|                     } | ||||
|                     if ($arg->assertFile) { | ||||
|                         if ($arg->useAsInfile) { | ||||
|                             $this->_stdin = $v; | ||||
|                         } elseif ($arg->useAsOutfile) { | ||||
|                             $this->_stdout = $v; | ||||
|                         } | ||||
|                         if ($arg->assertReadable && ! is_readable($v)) { | ||||
|                             $this->addError($letter, "File not readable: %s", $v); | ||||
|                             continue; | ||||
|                         } | ||||
|                         if ($arg->assertWritable) { | ||||
|                             if (is_file($v)) { | ||||
|                                 if (! is_writable($v)) { | ||||
|                                     $this->addError($letter, "File not writable: %s", $v); | ||||
|                                 } | ||||
|                             } else { | ||||
|                                 if (! is_writable(dirname($v))) { | ||||
|                                     $this->addError($letter, "Directory not writable: %s", dirname($v)); | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|                     } elseif ($arg->assertDir && $arg->assertWritable && ! is_writable($v)) { | ||||
|                         $this->addError($letter, "Directory not readable: %s", $v); | ||||
|                     } | ||||
|                 } | ||||
|             } else { | ||||
|                 if ($arg->isRequired()) { | ||||
|                     $this->addError($letter, "Missing"); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         $this->moreArgs = $argvCopy; | ||||
|         reset($this->moreArgs); | ||||
|         return empty($this->errors); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the full paths of file(s) passed in as unspecified arguments | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function getPathArgs() | ||||
|     { | ||||
|         $r = $this->moreArgs; | ||||
|         foreach ($r as $k => $v) { | ||||
|             if ($v[0] !== '/' && $v[0] !== '~') { | ||||
|                 $v = getcwd() . "/$v"; | ||||
|                 $v = str_replace('/./', '/', $v); | ||||
|                 do { | ||||
|                     $v = preg_replace('@/[^/]+/\\.\\./@', '/', $v, 1, $changed); | ||||
|                 } while ($changed); | ||||
|                 $r[$k] = $v; | ||||
|             } | ||||
|         } | ||||
|         return $r; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Get a short list of errors with options | ||||
|      *  | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getErrorReport() | ||||
|     { | ||||
|         if (empty($this->errors)) { | ||||
|             return ''; | ||||
|         } | ||||
|         $r = "Some arguments did not pass validation:\n"; | ||||
|         foreach ($this->errors as $letter => $arr) { | ||||
|             $r .= "  $letter : " . implode(', ', $arr) . "\n"; | ||||
|         } | ||||
|         $r .= "\n"; | ||||
|         return $r; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getArgumentsListing() | ||||
|     { | ||||
|         $r = "\n"; | ||||
|         foreach ($this->_args as $letter => $arg) { | ||||
|             /* @var Arg $arg  */ | ||||
|             $desc = $arg->getDescription(); | ||||
|             $flag = " -$letter "; | ||||
|             if ($arg->mayHaveValue) { | ||||
|                 $flag .= "[VAL]"; | ||||
|             } elseif ($arg->mustHaveValue) { | ||||
|                 $flag .= "VAL"; | ||||
|             } | ||||
|             if ($arg->assertFile) { | ||||
|                 $flag = str_replace('VAL', 'FILE', $flag); | ||||
|             } elseif ($arg->assertDir) { | ||||
|                 $flag = str_replace('VAL', 'DIR', $flag); | ||||
|             } | ||||
|             if ($arg->isRequired()) { | ||||
|                 $desc = "(required) $desc"; | ||||
|             } | ||||
|             $flag = str_pad($flag, 12, " ", STR_PAD_RIGHT); | ||||
|             $desc = wordwrap($desc, 70); | ||||
|             $r .= $flag . str_replace("\n", "\n            ", $desc) . "\n\n"; | ||||
|         } | ||||
|         return $r; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Get resource of open input stream. May be STDIN or a file pointer | ||||
|      * to the file specified by an option with 'STDIN'. | ||||
|      * | ||||
|      * @return resource | ||||
|      */ | ||||
|     public function openInput() | ||||
|     { | ||||
|         if (null === $this->_stdin) { | ||||
|             return STDIN; | ||||
|         } else { | ||||
|             $this->_stdin = fopen($this->_stdin, 'rb'); | ||||
|             return $this->_stdin; | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     public function closeInput() | ||||
|     { | ||||
|         if (null !== $this->_stdin) { | ||||
|             fclose($this->_stdin); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Get resource of open output stream. May be STDOUT or a file pointer | ||||
|      * to the file specified by an option with 'STDOUT'. The file will be | ||||
|      * truncated to 0 bytes on opening. | ||||
|      * | ||||
|      * @return resource | ||||
|      */ | ||||
|     public function openOutput() | ||||
|     { | ||||
|         if (null === $this->_stdout) { | ||||
|             return STDOUT; | ||||
|         } else { | ||||
|             $this->_stdout = fopen($this->_stdout, 'wb'); | ||||
|             return $this->_stdout; | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     public function closeOutput() | ||||
|     { | ||||
|         if (null !== $this->_stdout) { | ||||
|             fclose($this->_stdout); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $letter | ||||
|      * @param string $msg | ||||
|      * @param string $value | ||||
|      */ | ||||
|     protected function addError($letter, $msg, $value = null) | ||||
|     { | ||||
|         if ($value !== null) { | ||||
|             $value = var_export($value, 1); | ||||
|         } | ||||
|         $this->errors[$letter][] = sprintf($msg, $value); | ||||
|     } | ||||
| } | ||||
|  | ||||
							
								
								
									
										183
									
								
								vendor/mrclay/minify/min/lib/MrClay/Cli/Arg.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										183
									
								
								vendor/mrclay/minify/min/lib/MrClay/Cli/Arg.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,183 @@ | ||||
| <?php | ||||
|  | ||||
| namespace MrClay\Cli; | ||||
|  | ||||
| use BadMethodCallException; | ||||
|  | ||||
| /** | ||||
|  * An argument for a CLI app. This specifies the argument, what values it expects and | ||||
|  * how it's treated during validation. | ||||
|  * | ||||
|  * By default, the argument will be assumed to be an optional letter flag with no value following. | ||||
|  * | ||||
|  * If the argument may receive a value, call mayHaveValue(). If there's whitespace after the | ||||
|  * flag, the value will be returned as true instead of the string. | ||||
|  * | ||||
|  * If the argument MUST be accompanied by a value, call mustHaveValue(). In this case, whitespace | ||||
|  * is permitted between the flag and its value. | ||||
|  * | ||||
|  * Use assertFile() or assertDir() to indicate that the argument must return a string value | ||||
|  * specifying a file or directory. During validation, the value will be resolved to a | ||||
|  * full file/dir path (not necessarily existing!) and the original value will be accessible | ||||
|  * via a "*.raw" key. E.g. $cli->values['f.raw'] | ||||
|  * | ||||
|  * Use assertReadable()/assertWritable() to cause the validator to test the file/dir for | ||||
|  * read/write permissions respectively. | ||||
|  * | ||||
|  * @method \MrClay\Cli\Arg mayHaveValue() Assert that the argument, if present, may receive a string value | ||||
|  * @method \MrClay\Cli\Arg mustHaveValue() Assert that the argument, if present, must receive a string value | ||||
|  * @method \MrClay\Cli\Arg assertFile() Assert that the argument's value must specify a file | ||||
|  * @method \MrClay\Cli\Arg assertDir() Assert that the argument's value must specify a directory | ||||
|  * @method \MrClay\Cli\Arg assertReadable() Assert that the specified file/dir must be readable | ||||
|  * @method \MrClay\Cli\Arg assertWritable() Assert that the specified file/dir must be writable | ||||
|  * | ||||
|  * @property-read bool mayHaveValue | ||||
|  * @property-read bool mustHaveValue | ||||
|  * @property-read bool assertFile | ||||
|  * @property-read bool assertDir | ||||
|  * @property-read bool assertReadable | ||||
|  * @property-read bool assertWritable | ||||
|  * @property-read bool useAsInfile | ||||
|  * @property-read bool useAsOutfile | ||||
|  * | ||||
|  * @author Steve Clay <steve@mrclay.org> | ||||
|  * @license http://www.opensource.org/licenses/mit-license.php  MIT License | ||||
|  */ | ||||
| class Arg { | ||||
|     /** | ||||
|      * @return array | ||||
|      */ | ||||
|     public function getDefaultSpec() | ||||
|     { | ||||
|         return array( | ||||
|             'mayHaveValue' => false, | ||||
|             'mustHaveValue' => false, | ||||
|             'assertFile' => false, | ||||
|             'assertDir' => false, | ||||
|             'assertReadable' => false, | ||||
|             'assertWritable' => false, | ||||
|             'useAsInfile' => false, | ||||
|             'useAsOutfile' => false, | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @var array | ||||
|      */ | ||||
|     protected $spec = array(); | ||||
|  | ||||
|     /** | ||||
|      * @var bool | ||||
|      */ | ||||
|     protected $required = false; | ||||
|  | ||||
|     /** | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $description = ''; | ||||
|  | ||||
|     /** | ||||
|      * @param bool $isRequired | ||||
|      */ | ||||
|     public function __construct($isRequired = false) | ||||
|     { | ||||
|         $this->spec = $this->getDefaultSpec(); | ||||
|         $this->required = (bool) $isRequired; | ||||
|         if ($isRequired) { | ||||
|             $this->spec['mustHaveValue'] = true; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Assert that the argument's value points to a writable file. When | ||||
|      * Cli::openOutput() is called, a write pointer to this file will | ||||
|      * be provided. | ||||
|      * @return Arg | ||||
|      */ | ||||
|     public function useAsOutfile() | ||||
|     { | ||||
|         $this->spec['useAsOutfile'] = true; | ||||
|         return $this->assertFile()->assertWritable(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Assert that the argument's value points to a readable file. When | ||||
|      * Cli::openInput() is called, a read pointer to this file will | ||||
|      * be provided. | ||||
|      * @return Arg | ||||
|      */ | ||||
|     public function useAsInfile() | ||||
|     { | ||||
|         $this->spec['useAsInfile'] = true; | ||||
|         return $this->assertFile()->assertReadable(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return array | ||||
|      */ | ||||
|     public function getSpec() | ||||
|     { | ||||
|         return $this->spec; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $desc | ||||
|      * @return Arg | ||||
|      */ | ||||
|     public function setDescription($desc) | ||||
|     { | ||||
|         $this->description = $desc; | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getDescription() | ||||
|     { | ||||
|         return $this->description; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function isRequired() | ||||
|     { | ||||
|         return $this->required; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Note: magic methods declared in class PHPDOC | ||||
|      * | ||||
|      * @param string $name | ||||
|      * @param array $args | ||||
|      * @return Arg | ||||
|      * @throws BadMethodCallException | ||||
|      */ | ||||
|     public function __call($name, array $args = array()) | ||||
|     { | ||||
|         if (array_key_exists($name, $this->spec)) { | ||||
|             $this->spec[$name] = true; | ||||
|             if ($name === 'assertFile' || $name === 'assertDir') { | ||||
|                 $this->spec['mustHaveValue'] = true; | ||||
|             } | ||||
|         } else { | ||||
|             throw new BadMethodCallException('Method does not exist'); | ||||
|         } | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Note: magic properties declared in class PHPDOC | ||||
|      * | ||||
|      * @param string $name | ||||
|      * @return bool|null | ||||
|      */ | ||||
|     public function __get($name) | ||||
|     { | ||||
|         if (array_key_exists($name, $this->spec)) { | ||||
|             return $this->spec[$name]; | ||||
|         } | ||||
|         return null; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										13
									
								
								vendor/ornicar/php-user-agent/CHANGELOG.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								vendor/ornicar/php-user-agent/CHANGELOG.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| # CHANGELOG | ||||
|  | ||||
| ### 1.0.1 (????-??-??) | ||||
|  | ||||
|  * f1b82e5 - [Composer] Add branch alias support | ||||
|  | ||||
| ### 1.0.0 (2013-08-09) | ||||
|  | ||||
|  * Add a version following semver spec. | ||||
|  | ||||
| ### v1.0 (2010-04-13) | ||||
|  | ||||
|  * Create initial 1.0 version | ||||
							
								
								
									
										21
									
								
								vendor/ornicar/php-user-agent/LICENSE
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/ornicar/php-user-agent/LICENSE
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| The MIT License | ||||
|  | ||||
| Copyright (c) 2010 Thibault Duplessis | ||||
|  | ||||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| of this software and associated documentation files (the "Software"), to deal | ||||
| in the Software without restriction, including without limitation the rights | ||||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
| copies of the Software, and to permit persons to whom the Software is | ||||
| furnished to do so, subject to the following conditions: | ||||
|  | ||||
| The above copyright notice and this permission notice shall be included in | ||||
| all copies or substantial portions of the Software. | ||||
|  | ||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
| THE SOFTWARE. | ||||
							
								
								
									
										92
									
								
								vendor/ornicar/php-user-agent/README.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								vendor/ornicar/php-user-agent/README.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,92 @@ | ||||
| # PHP User Agent | ||||
|  | ||||
| Browser detection in PHP5. | ||||
| Uses a simple and fast algorithm to recognize major browsers. | ||||
|  | ||||
| ## Overview | ||||
|  | ||||
| ``` php | ||||
| $userAgent = new phpUserAgent(); | ||||
|  | ||||
| $userAgent->getBrowserName()      // firefox | ||||
| $userAgent->getBrowserVersion()   // 3.6 | ||||
| $userAgent->getOperatingSystem()  // linux | ||||
| $userAgent->getEngine()           // gecko | ||||
| ``` | ||||
|  | ||||
| ### Why you should use it | ||||
|  | ||||
| PHP provides a native function to detect user browser: [get_browser()](http://us2.php.net/manual/en/function.get-browser.php). | ||||
| get_browser() requires the "browscap.ini" file which is 300KB+. | ||||
| Loading and processing this file impact script performance. | ||||
| And sometimes, the production server just doesn't provide browscap.ini. | ||||
|  | ||||
| Although get_browser() surely provides excellent detection results, in most | ||||
| cases a much simpler method can be just as effective. | ||||
| php-user-agent has the advantage of being compact and easy to extend. | ||||
| It is performant as well, since it doesn't do any iteration or recursion. | ||||
|  | ||||
| ## Usage | ||||
|  | ||||
| ``` php | ||||
| // include classes or rely on Composer autoloader | ||||
| require_once '/path/to/php-user-agent/phpUserAgent.php'; | ||||
| require_once '/path/to/php-user-agent/phpUserAgentStringParser.php'; | ||||
|  | ||||
| // Create a user agent | ||||
| $userAgent = new phpUserAgent(); | ||||
|  | ||||
| // Interrogate the user agent | ||||
| $userAgent->getBrowserName()      // firefox | ||||
| $userAgent->getBrowserVersion()   // 3.6 | ||||
| $userAgent->getOperatingSystem()  // linux | ||||
| $userAgent->getEngine()           // gecko | ||||
| ``` | ||||
|  | ||||
| ## Advanced | ||||
|  | ||||
| ### Custom user agent string | ||||
|  | ||||
| When you create a phpUserAgent object, the current user agent string is used. | ||||
| You can specify another user agent string: | ||||
|  | ||||
| ``` php | ||||
| // use another user agent string | ||||
| $userAgent = new phpUserAgent('msnbot/2.0b (+http://search.msn.com/msnbot.htm)'); | ||||
| $userAgent->getBrowserName() // msnbot | ||||
|  | ||||
| // use current user agent string | ||||
| $userAgent = new phpUserAgent($_SERVER['HTTP_USER_AGENT'); | ||||
| // this is equivalent to: | ||||
| $userAgent = new phpUserAgent(); | ||||
| ``` | ||||
|  | ||||
| ### Custom parser class | ||||
|  | ||||
| By default, phpUserAgentStringParser is used to analyse the user agent string. | ||||
| You can replace the parser instance and customize it to match your needs: | ||||
|  | ||||
| ``` php | ||||
| // create a custom user agent string parser | ||||
| class myUserAgentStringParser extends phpUserAgentStringParser | ||||
| { | ||||
|   // override methods | ||||
| } | ||||
|  | ||||
| // inject the custom parser when creating a user agent: | ||||
| $userAgent = new phpUserAgent(null, new myUserAgentStringParser()); | ||||
| ``` | ||||
|  | ||||
| ## Run tests | ||||
|  | ||||
| You can run the unit tests on your server: | ||||
|  | ||||
| ``` bash | ||||
| $ php prove.php | ||||
| ``` | ||||
|  | ||||
| ## Contribute | ||||
|  | ||||
| If you found a browser of operating system this library fails to recognize, | ||||
| feel free to submit an issue. Please provide the user agent string. | ||||
| And well, if you also want to provide the patch, it's even better. | ||||
							
								
								
									
										194
									
								
								vendor/ornicar/php-user-agent/lib/phpUserAgent.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										194
									
								
								vendor/ornicar/php-user-agent/lib/phpUserAgent.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,194 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Simple PHP User agent | ||||
|  * | ||||
|  * @link      http://github.com/ornicar/php-user-agent | ||||
|  * @version   1.0 | ||||
|  * @author    Thibault Duplessis <thibault.duplessis at gmail dot com> | ||||
|  * @license   MIT License | ||||
|  * | ||||
|  * Documentation: http://github.com/ornicar/php-user-agent/blob/master/README.markdown | ||||
|  * Tickets:       http://github.com/ornicar/php-user-agent/issues | ||||
|  */ | ||||
|  | ||||
| class phpUserAgent | ||||
| { | ||||
|   protected $userAgentString; | ||||
|   protected $browserName; | ||||
|   protected $browserVersion; | ||||
|   protected $operatingSystem; | ||||
|   protected $engine; | ||||
|  | ||||
|   public function __construct($userAgentString = null, phpUserAgentStringParser $userAgentStringParser = null) | ||||
|   { | ||||
|     $this->configureFromUserAgentString($userAgentString, $userAgentStringParser); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Get the browser name | ||||
|    * | ||||
|    * @return string the browser name | ||||
|    */ | ||||
|   public function getBrowserName() | ||||
|   { | ||||
|     return $this->browserName; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Set the browser name | ||||
|    * | ||||
|    * @param   string  $name the browser name | ||||
|    */ | ||||
|   public function setBrowserName($name) | ||||
|   { | ||||
|     $this->browserName = $name; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Get the browser version | ||||
|    * | ||||
|    * @return string the browser version | ||||
|    */ | ||||
|   public function getBrowserVersion() | ||||
|   { | ||||
|     return $this->browserVersion; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Set the browser version | ||||
|    * | ||||
|    * @param   string  $version the browser version | ||||
|    */ | ||||
|   public function setBrowserVersion($version) | ||||
|   { | ||||
|     $this->browserVersion = $version; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Get the operating system name | ||||
|    * | ||||
|    * @return  string the operating system name | ||||
|    */ | ||||
|   public function getOperatingSystem() | ||||
|   { | ||||
|     return $this->operatingSystem; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Set the operating system name | ||||
|    * | ||||
|    * @param   string $operatingSystem the operating system name | ||||
|    */ | ||||
|   public function setOperatingSystem($operatingSystem) | ||||
|   { | ||||
|     $this->operatingSystem = $operatingSystem; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Get the engine name | ||||
|    * | ||||
|    * @return  string the engine name | ||||
|    */ | ||||
|   public function getEngine() | ||||
|   { | ||||
|     return $this->engine; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Set the engine name | ||||
|    * | ||||
|    * @param   string $operatingSystem the engine name | ||||
|    */ | ||||
|   public function setEngine($engine) | ||||
|   { | ||||
|     $this->engine = $engine; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Get the user agent string | ||||
|    * | ||||
|    * @return  string the user agent string | ||||
|    */ | ||||
|   public function getUserAgentString() | ||||
|   { | ||||
|     return $this->userAgentString; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Set the user agent string | ||||
|    * | ||||
|    * @param   string $userAgentString the user agent string | ||||
|    */ | ||||
|   public function setUserAgentString($userAgentString) | ||||
|   { | ||||
|     $this->userAgentString = $userAgentString; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Tell whether this user agent is unknown or not | ||||
|    * | ||||
|    * @return boolean  true if this user agent is unknown, false otherwise | ||||
|    */ | ||||
|   public function isUnknown() | ||||
|   { | ||||
|     return empty($this->browserName); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * @return string combined browser name and version | ||||
|    */ | ||||
|   public function getFullName() | ||||
|   { | ||||
|     return $this->getBrowserName().' '.$this->getBrowserVersion(); | ||||
|   } | ||||
|  | ||||
|   public function __toString() | ||||
|   { | ||||
|     return $this->getFullName(); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Configure the user agent from a user agent string | ||||
|    * @param   string                    $userAgentString        the user agent string | ||||
|    * @param   phpUserAgentStringParser  $userAgentStringParser  the parser used to parse the string | ||||
|    */ | ||||
|   public function configureFromUserAgentString($userAgentString, phpUserAgentStringParser $userAgentStringParser = null) | ||||
|   { | ||||
|     if(null === $userAgentStringParser) | ||||
|     { | ||||
|       $userAgentStringParser = new phpUserAgentStringParser(); | ||||
|     } | ||||
|  | ||||
|     $this->setUserAgentString($userAgentString); | ||||
|  | ||||
|     $this->fromArray($userAgentStringParser->parse($userAgentString)); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Convert the user agent to a data array | ||||
|    * | ||||
|    * @return  array data | ||||
|    */ | ||||
|   public function toArray() | ||||
|   { | ||||
|     return array( | ||||
|       'browser_name'      => $this->getBrowserName(), | ||||
|       'browser_version'   => $this->getBrowserVersion(), | ||||
|       'operating_system'  => $this->getOperatingSystem() | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Configure the user agent from a data array | ||||
|    * | ||||
|    * @param array $data | ||||
|    */ | ||||
|   public function fromArray(array $data) | ||||
|   { | ||||
|     $this->setBrowserName($data['browser_name']); | ||||
|     $this->setBrowserVersion($data['browser_version']); | ||||
|     $this->setOperatingSystem($data['operating_system']); | ||||
|     $this->setEngine($data['engine']); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										321
									
								
								vendor/ornicar/php-user-agent/lib/phpUserAgentStringParser.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										321
									
								
								vendor/ornicar/php-user-agent/lib/phpUserAgentStringParser.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,321 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Simple PHP User Agent string parser | ||||
|  */ | ||||
|  | ||||
| class phpUserAgentStringParser | ||||
| { | ||||
|   /** | ||||
|    * Parse a user agent string. | ||||
|    * | ||||
|    * @param   string  $userAgentString  defaults to $_SERVER['HTTP_USER_AGENT'] if empty | ||||
|    * @return  array   (                 the user agent informations | ||||
|    *            'browser_name'      => 'firefox', | ||||
|    *            'browser_version'   => '3.6', | ||||
|    *            'operating_system'  => 'linux' | ||||
|    *          ) | ||||
|    */ | ||||
|   public function parse($userAgentString = null) | ||||
|   { | ||||
|     // use current user agent string as default | ||||
|     if(!$userAgentString) | ||||
|     { | ||||
|       $userAgentString = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : null; | ||||
|     } | ||||
|  | ||||
|     // parse quickly (with medium accuracy) | ||||
|     $informations = $this->doParse($userAgentString); | ||||
|  | ||||
|  | ||||
|     // run some filters to increase accuracy | ||||
|     foreach($this->getFilters() as $filter) | ||||
|     { | ||||
|       $this->$filter($informations); | ||||
|     } | ||||
|  | ||||
|     return $informations; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Detect quickly informations from the user agent string | ||||
|    *  | ||||
|    * @param   string $userAgentString   user agent string | ||||
|    * @return  array                     user agent informations array | ||||
|    */ | ||||
|   protected function doParse($userAgentString) | ||||
|   { | ||||
|     $userAgent = array( | ||||
|       'string'            => $this->cleanUserAgentString($userAgentString), | ||||
|       'browser_name'      => null, | ||||
|       'browser_version'   => null, | ||||
|       'operating_system'  => null, | ||||
|       'engine'            => null | ||||
|     ); | ||||
|  | ||||
|     if(empty($userAgent['string'])) | ||||
|     { | ||||
|       return $userAgent; | ||||
|     } | ||||
|  | ||||
|     // build regex that matches phrases for known browsers | ||||
|     // (e.g. "Firefox/2.0" or "MSIE 6.0" (This only matches the major and minor | ||||
|     // version numbers.  E.g. "2.0.0.6" is parsed as simply "2.0" | ||||
|     $pattern = '#('.join('|', $this->getKnownBrowsers()).')[/ ]+([0-9]+(?:\.[0-9]+)?)#'; | ||||
|  | ||||
|     // Find all phrases (or return empty array if none found) | ||||
|     if (preg_match_all($pattern, $userAgent['string'], $matches)) | ||||
|     { | ||||
|       // Since some UAs have more than one phrase (e.g Firefox has a Gecko phrase, | ||||
|       // Opera 7,8 have a MSIE phrase), use the last one found (the right-most one | ||||
|       // in the UA).  That's usually the most correct. | ||||
|       $i = count($matches[1])-1; | ||||
|  | ||||
|       if (isset($matches[1][$i])) | ||||
|       { | ||||
|         $userAgent['browser_name'] = $matches[1][$i]; | ||||
|       } | ||||
|       if (isset($matches[2][$i])) | ||||
|       { | ||||
|         $userAgent['browser_version'] = $matches[2][$i]; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     // Find operating system | ||||
|     $pattern = '#'.join('|', $this->getKnownOperatingSystems()).'#'; | ||||
|      | ||||
|     if (preg_match($pattern, $userAgent['string'], $match)) | ||||
|     { | ||||
|       if (isset($match[0])) | ||||
|       { | ||||
|         $userAgent['operating_system'] = $match[0]; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     // Find engine | ||||
|     $pattern = '#'.join('|', $this->getKnownEngines()).'#'; | ||||
|      | ||||
|     if (preg_match($pattern, $userAgent['string'], $match)) | ||||
|     { | ||||
|       if (isset($match[0])) | ||||
|       { | ||||
|         $userAgent['engine'] = $match[0]; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     return $userAgent; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Make user agent string lowercase, and replace browser aliases | ||||
|    * | ||||
|    * @param   string $userAgentString the dirty user agent string | ||||
|    * @return  string                  the clean user agent string | ||||
|    */ | ||||
|   public function cleanUserAgentString($userAgentString) | ||||
|   { | ||||
|     // clean up the string | ||||
|     $userAgentString = trim(strtolower($userAgentString)); | ||||
|  | ||||
|     // replace browser names with their aliases | ||||
|     $userAgentString = strtr($userAgentString, $this->getKnownBrowserAliases()); | ||||
|  | ||||
|     // replace operating system names with their aliases | ||||
|     $userAgentString = strtr($userAgentString, $this->getKnownOperatingSystemAliases()); | ||||
|  | ||||
|     // replace engine names with their aliases | ||||
|     $userAgentString = strtr($userAgentString, $this->getKnownEngineAliases()); | ||||
|  | ||||
|     return $userAgentString; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Get the list of filters that get called when parsing a user agent | ||||
|    * | ||||
|    * @return  array list of valid callables | ||||
|    */ | ||||
|   public function getFilters() | ||||
|   { | ||||
|     return array( | ||||
|       'filterAndroid', | ||||
|       'filterGoogleChrome', | ||||
|       'filterSafariVersion', | ||||
|       'filterOperaVersion', | ||||
|       'filterYahoo', | ||||
|       'filterMsie', | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Add a filter to be called when parsing a user agent | ||||
|    *  | ||||
|    * @param   string $filter name of the filter method | ||||
|    */ | ||||
|   public function addFilter($filter) | ||||
|   { | ||||
|     $this->filters += $filter; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Get known browsers | ||||
|    * | ||||
|    * @return  array the browsers | ||||
|    */ | ||||
|   protected function getKnownBrowsers() | ||||
|   { | ||||
|     return array( | ||||
|       'msie', | ||||
|       'firefox', | ||||
|       'safari', | ||||
|       'webkit', | ||||
|       'opera', | ||||
|       'netscape', | ||||
|       'konqueror', | ||||
|       'gecko', | ||||
|       'chrome', | ||||
|       'googlebot', | ||||
|       'iphone', | ||||
|       'msnbot', | ||||
|       'applewebkit' | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Get known browser aliases | ||||
|    * | ||||
|    * @return  array the browser aliases | ||||
|    */ | ||||
|   protected function getKnownBrowserAliases() | ||||
|   { | ||||
|     return array( | ||||
|       'shiretoko'     => 'firefox', | ||||
|       'namoroka'      => 'firefox', | ||||
|       'shredder'      => 'firefox', | ||||
|       'minefield'     => 'firefox', | ||||
|       'granparadiso'  => 'firefox' | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Get known operating system | ||||
|    * | ||||
|    * @return  array the operating systems | ||||
|    */ | ||||
|   protected function getKnownOperatingSystems() | ||||
|   { | ||||
|     return array( | ||||
|       'windows', | ||||
|       'macintosh', | ||||
|       'linux', | ||||
|       'freebsd', | ||||
|       'unix', | ||||
|       'iphone' | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Get known operating system aliases | ||||
|    * | ||||
|    * @return  array the operating system aliases | ||||
|    */ | ||||
|   protected function getKnownOperatingSystemAliases() | ||||
|   { | ||||
|     return array(); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Get known engines | ||||
|    * | ||||
|    * @return  array the engines | ||||
|    */ | ||||
|   protected function getKnownEngines() | ||||
|   { | ||||
|     return array( | ||||
|       'gecko', | ||||
|       'webkit', | ||||
|       'trident', | ||||
|       'presto' | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Get known engines aliases | ||||
|    * | ||||
|    * @return  array the engines aliases | ||||
|    */ | ||||
|   protected function getKnownEngineAliases() | ||||
|   { | ||||
|     return array(); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Filters | ||||
|    */ | ||||
|  | ||||
|   /** | ||||
|    * Google chrome has a safari like signature | ||||
|    */ | ||||
|   protected function filterGoogleChrome(array &$userAgent) | ||||
|   { | ||||
|     if ('safari' === $userAgent['browser_name'] && strpos($userAgent['string'], 'chrome/')) | ||||
|     { | ||||
|       $userAgent['browser_name'] = 'chrome'; | ||||
|       $userAgent['browser_version'] = preg_replace('|.+chrome/([0-9]+(?:\.[0-9]+)?).+|', '$1', $userAgent['string']); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Safari version is not encoded "normally" | ||||
|    */ | ||||
|   protected function filterSafariVersion(array &$userAgent) | ||||
|   { | ||||
|     if ('safari' === $userAgent['browser_name'] && strpos($userAgent['string'], ' version/')) | ||||
|     { | ||||
|       $userAgent['browser_version'] = preg_replace('|.+\sversion/([0-9]+(?:\.[0-9]+)?).+|', '$1', $userAgent['string']); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Opera 10.00 (and higher) version number is located at the end | ||||
|    */ | ||||
|   protected function filterOperaVersion(array &$userAgent) | ||||
|   { | ||||
|     if('opera' === $userAgent['browser_name'] && strpos($userAgent['string'], ' version/')) | ||||
|     { | ||||
|       $userAgent['browser_version'] = preg_replace('|.+\sversion/([0-9]+\.[0-9]+)\s*.*|', '$1', $userAgent['string']); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Yahoo bot has a special user agent string | ||||
|    */ | ||||
|   protected function filterYahoo(array &$userAgent) | ||||
|   { | ||||
|     if (null === $userAgent['browser_name'] && strpos($userAgent['string'], 'yahoo! slurp')) | ||||
|     { | ||||
|       $userAgent['browser_name'] = 'yahoobot'; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * MSIE does not always declare its engine | ||||
|    */ | ||||
|   protected function filterMsie(array &$userAgent) | ||||
|   { | ||||
|     if ('msie' === $userAgent['browser_name'] && empty($userAgent['engine'])) | ||||
|     { | ||||
|       $userAgent['engine'] = 'trident'; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|     /** | ||||
|      * Android has a safari like signature | ||||
|      */ | ||||
|     protected function filterAndroid(array &$userAgent) { | ||||
|         if ('safari' === $userAgent['browser_name'] && strpos($userAgent['string'], 'android ')) { | ||||
|             $userAgent['browser_name'] = 'android'; | ||||
|             $userAgent['operating_system'] = 'android'; | ||||
|             $userAgent['browser_version'] = preg_replace('|.+android ([0-9]+(?:\.[0-9]+)+).+|', '$1', $userAgent['string']); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -215,7 +215,11 @@ class OutputFormatter implements OutputFormatterInterface | ||||
|             } elseif ('bg' == $match[0]) { | ||||
|                 $style->setBackground($match[1]); | ||||
|             } else { | ||||
|                 try { | ||||
|                     $style->setOption($match[1]); | ||||
|                 } catch (\InvalidArgumentException $e) { | ||||
|                     return false; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|   | ||||
							
								
								
									
										39
									
								
								vendor/tracy/tracy/src/Tracy/Debugger.php
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										39
									
								
								vendor/tracy/tracy/src/Tracy/Debugger.php
									
									
									
									
										vendored
									
									
								
							| @@ -23,6 +23,8 @@ class Debugger | ||||
| 		PRODUCTION = TRUE, | ||||
| 		DETECT = NULL; | ||||
|  | ||||
| 	const COOKIE_SECRET = 'tracy-debug'; | ||||
|  | ||||
| 	/** @var string */ | ||||
| 	public static $version = '2.3-dev'; | ||||
|  | ||||
| @@ -126,18 +128,7 @@ class Debugger | ||||
| 		self::$time = isset($_SERVER['REQUEST_TIME_FLOAT']) ? $_SERVER['REQUEST_TIME_FLOAT'] : microtime(TRUE); | ||||
| 		error_reporting(E_ALL | E_STRICT); | ||||
|  | ||||
| 		// production/development mode detection | ||||
| 		if (is_bool($mode)) { | ||||
| 			self::$productionMode = $mode; | ||||
|  | ||||
| 		} elseif ($mode !== self::DETECT || self::$productionMode === NULL) { // IP addresses or computer names whitelist detection | ||||
| 			$list = is_string($mode) ? preg_split('#[,\s]+#', $mode) : (array) $mode; | ||||
| 			if (!isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { | ||||
| 				$list[] = '127.0.0.1'; | ||||
| 				$list[] = '::1'; | ||||
| 			} | ||||
| 			self::$productionMode = !in_array(isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : php_uname('n'), $list, TRUE); | ||||
| 		} | ||||
| 		self::$productionMode = is_bool($mode) ? $mode : !self::detectDebugMode($mode); | ||||
|  | ||||
| 		// logging configuration | ||||
| 		if ($email !== NULL) { | ||||
| @@ -518,4 +509,28 @@ class Debugger | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|  | ||||
| 	/** | ||||
| 	 * Detects debug mode by IP address. | ||||
| 	 * @param  string|array  IP addresses or computer names whitelist detection | ||||
| 	 * @return bool | ||||
| 	 */ | ||||
| 	public static function detectDebugMode($list = NULL) | ||||
| 	{ | ||||
| 		$addr = isset($_SERVER['REMOTE_ADDR']) | ||||
| 			? $_SERVER['REMOTE_ADDR'] | ||||
| 			: php_uname('n'); | ||||
| 		$secret = isset($_COOKIE[self::COOKIE_SECRET]) && is_string($_COOKIE[self::COOKIE_SECRET]) | ||||
| 			? $_COOKIE[self::COOKIE_SECRET] | ||||
| 			: NULL; | ||||
| 		$list = is_string($list) | ||||
| 			? preg_split('#[,\s]+#', $list) | ||||
| 			: (array) $list; | ||||
| 		if (!isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { | ||||
| 			$list[] = '127.0.0.1'; | ||||
| 			$list[] = '::1'; | ||||
| 		} | ||||
| 		return in_array($addr, $list, TRUE) || in_array("$secret@$addr", $list, TRUE); | ||||
| 	} | ||||
|  | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user