mirror of
https://github.com/getgrav/grav.git
synced 2025-11-01 02:46:18 +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
|
# Grav Specific
|
||||||
cache/*
|
cache/*
|
||||||
!cache/.*
|
!cache/.*
|
||||||
|
assets/*
|
||||||
|
!assets/.*
|
||||||
logs/*
|
logs/*
|
||||||
!logs/.*
|
!logs/.*
|
||||||
images/*
|
images/*
|
||||||
|
|||||||
@@ -20,6 +20,8 @@
|
|||||||
"doctrine/cache": "1.4.*@dev",
|
"doctrine/cache": "1.4.*@dev",
|
||||||
"tracy/tracy": "dev-master",
|
"tracy/tracy": "dev-master",
|
||||||
"gregwar/image": "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('Twig', new Twig);
|
||||||
$registry->store('Pages', new Page\Pages);
|
$registry->store('Pages', new Page\Pages);
|
||||||
$registry->store('Taxonomy', new Taxonomy);
|
$registry->store('Taxonomy', new Taxonomy);
|
||||||
|
$registry->store('Assets', new Assets);
|
||||||
|
|
||||||
$grav->process();
|
$grav->process();
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,13 @@ twig:
|
|||||||
auto_reload: true # Refresh cache on changes
|
auto_reload: true # Refresh cache on changes
|
||||||
autoescape: false # Autoescape Twig vars
|
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:
|
debugger:
|
||||||
enabled: true # Enable Grav debugger
|
enabled: true # Enable Grav debugger
|
||||||
max_depth: 10 # How many nested levels to display for objects or arrays
|
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_PATH', 'user/');
|
||||||
define('USER_DIR', ROOT_DIR . USER_PATH);
|
define('USER_DIR', ROOT_DIR . USER_PATH);
|
||||||
define('SYSTEM_DIR', ROOT_DIR .'system/');
|
define('SYSTEM_DIR', ROOT_DIR .'system/');
|
||||||
|
define('ASSETS_DIR', ROOT_DIR . 'assets/');
|
||||||
define('CACHE_DIR', ROOT_DIR .'cache/');
|
define('CACHE_DIR', ROOT_DIR .'cache/');
|
||||||
define('IMAGES_DIR', ROOT_DIR . 'images/');
|
define('IMAGES_DIR', ROOT_DIR . 'images/');
|
||||||
define('LOG_DIR', ROOT_DIR .'logs/');
|
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;
|
protected $twig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Assets
|
||||||
|
*/
|
||||||
|
protected $assets;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Taxonomy
|
* @var Taxonomy
|
||||||
*/
|
*/
|
||||||
@@ -79,6 +84,9 @@ class Grav extends Getters
|
|||||||
// Get the Configuration settings and caching
|
// Get the Configuration settings and caching
|
||||||
$this->config = Registry::get('Config');
|
$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::$logDirectory = $this->config->get('system.debugger.log.enabled') ? LOG_DIR : null;
|
||||||
Debugger::$maxDepth = $this->config->get('system.debugger.max_depth');
|
Debugger::$maxDepth = $this->config->get('system.debugger.max_depth');
|
||||||
|
|
||||||
@@ -103,6 +111,11 @@ class Grav extends Getters
|
|||||||
$themes = new Themes();
|
$themes = new Themes();
|
||||||
$this->plugins['Theme'] = $themes->load();
|
$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
|
// Get twig object
|
||||||
$this->twig = Registry::get('Twig');
|
$this->twig = Registry::get('Twig');
|
||||||
$this->twig->init();
|
$this->twig->init();
|
||||||
|
|||||||
@@ -28,6 +28,11 @@ class Twig
|
|||||||
*/
|
*/
|
||||||
protected $config;
|
protected $config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Useragent
|
||||||
|
*/
|
||||||
|
protected $user_agent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Uri
|
* @var Uri
|
||||||
*/
|
*/
|
||||||
@@ -69,9 +74,10 @@ class Twig
|
|||||||
// get Grav and Config
|
// get Grav and Config
|
||||||
$this->grav = Registry::get('Grav');
|
$this->grav = Registry::get('Grav');
|
||||||
$this->config = $this->grav->config;
|
$this->config = $this->grav->config;
|
||||||
|
$this->user_agent = $this->grav->user_agent;
|
||||||
$this->uri = Registry::get('Uri');
|
$this->uri = Registry::get('Uri');
|
||||||
$this->taxonomy = Registry::get('Taxonomy');
|
$this->taxonomy = Registry::get('Taxonomy');
|
||||||
|
$this->assets = Registry::get('Assets');
|
||||||
|
|
||||||
$this->twig_paths = array(THEMES_DIR . $this->config->get('system.pages.theme') . '/templates');
|
$this->twig_paths = array(THEMES_DIR . $this->config->get('system.pages.theme') . '/templates');
|
||||||
$this->grav->fireEvent('onAfterTwigTemplatesPaths');
|
$this->grav->fireEvent('onAfterTwigTemplatesPaths');
|
||||||
@@ -114,9 +120,9 @@ class Twig
|
|||||||
'theme_dir' => THEMES_DIR . $theme,
|
'theme_dir' => THEMES_DIR . $theme,
|
||||||
'theme_url' => $themeUrl,
|
'theme_url' => $themeUrl,
|
||||||
'site' => $this->config->get('site'),
|
'site' => $this->config->get('site'),
|
||||||
'stylesheets' => array(),
|
'assets' => $this->assets,
|
||||||
'scripts' => array(),
|
|
||||||
'taxonomy' => $this->taxonomy,
|
'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/version-test.php',
|
||||||
'vendor/ircmaxell/password-compat/.travis.yml',
|
'vendor/ircmaxell/password-compat/.travis.yml',
|
||||||
'vendor/ircmaxell/password-compat/test',
|
'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/composer.json',
|
||||||
'vendor/symfony/console/Symfony/Component/Console/phpunit.xml.dist',
|
'vendor/symfony/console/Symfony/Component/Console/phpunit.xml.dist',
|
||||||
'vendor/symfony/console/Symfony/Component/Console/.gitignore',
|
'vendor/symfony/console/Symfony/Component/Console/.gitignore',
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ class ClearCacheCommand extends Command {
|
|||||||
|
|
||||||
protected $paths_to_remove = [
|
protected $paths_to_remove = [
|
||||||
'cache',
|
'cache',
|
||||||
'images'
|
'images',
|
||||||
|
'assets'
|
||||||
];
|
];
|
||||||
|
|
||||||
protected function configure() {
|
protected function configure() {
|
||||||
|
|||||||
@@ -23,6 +23,13 @@ twig:
|
|||||||
auto_reload: true
|
auto_reload: true
|
||||||
autoescape: false
|
autoescape: false
|
||||||
|
|
||||||
|
assets:
|
||||||
|
css_pipeline: false
|
||||||
|
css_minify: true
|
||||||
|
css_rewrite: true
|
||||||
|
js_pipeline: false
|
||||||
|
js_minify: true
|
||||||
|
|
||||||
debugger:
|
debugger:
|
||||||
enabled: true
|
enabled: true
|
||||||
max_depth: 10
|
max_depth: 10
|
||||||
|
|||||||
2
vendor/autoload.php
vendored
2
vendor/autoload.php
vendored
@@ -4,4 +4,4 @@
|
|||||||
|
|
||||||
require_once __DIR__ . '/composer' . '/autoload_real.php';
|
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);
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
return array(
|
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\\Bar' => $vendorDir . '/tracy/tracy/src/Tracy/Bar.php',
|
||||||
'Tracy\\BlueScreen' => $vendorDir . '/tracy/tracy/src/Tracy/BlueScreen.php',
|
'Tracy\\BlueScreen' => $vendorDir . '/tracy/tracy/src/Tracy/BlueScreen.php',
|
||||||
'Tracy\\Debugger' => $vendorDir . '/tracy/tracy/src/Tracy/Debugger.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);
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
$vendorDir . '/tracy/tracy/src/shortcuts.php',
|
|
||||||
$vendorDir . '/ircmaxell/password-compat/lib/password.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
|
// autoload_real.php @generated by Composer
|
||||||
|
|
||||||
class ComposerAutoloaderInit59d31b9205a8126f2a856995d42bb492
|
class ComposerAutoloaderInit70d1fc134524e0edbf7795cd92686187
|
||||||
{
|
{
|
||||||
private static $loader;
|
private static $loader;
|
||||||
|
|
||||||
@@ -19,9 +19,9 @@ class ComposerAutoloaderInit59d31b9205a8126f2a856995d42bb492
|
|||||||
return self::$loader;
|
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();
|
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';
|
$map = require __DIR__ . '/autoload_namespaces.php';
|
||||||
foreach ($map as $namespace => $path) {
|
foreach ($map as $namespace => $path) {
|
||||||
@@ -42,14 +42,14 @@ class ComposerAutoloaderInit59d31b9205a8126f2a856995d42bb492
|
|||||||
|
|
||||||
$includeFiles = require __DIR__ . '/autoload_files.php';
|
$includeFiles = require __DIR__ . '/autoload_files.php';
|
||||||
foreach ($includeFiles as $file) {
|
foreach ($includeFiles as $file) {
|
||||||
composerRequire59d31b9205a8126f2a856995d42bb492($file);
|
composerRequire70d1fc134524e0edbf7795cd92686187($file);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $loader;
|
return $loader;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function composerRequire59d31b9205a8126f2a856995d42bb492($file)
|
function composerRequire70d1fc134524e0edbf7795cd92686187($file)
|
||||||
{
|
{
|
||||||
require $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",
|
"name": "doctrine/cache",
|
||||||
"version": "dev-master",
|
"version": "dev-master",
|
||||||
@@ -111,67 +70,6 @@
|
|||||||
"caching"
|
"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",
|
"name": "gregwar/cache",
|
||||||
"version": "v1.0.9",
|
"version": "v1.0.9",
|
||||||
@@ -373,63 +271,6 @@
|
|||||||
"description": "Symfony Yaml Component",
|
"description": "Symfony Yaml Component",
|
||||||
"homepage": "http://symfony.com"
|
"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",
|
"name": "ircmaxell/password-compat",
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
@@ -470,5 +311,253 @@
|
|||||||
"hashing",
|
"hashing",
|
||||||
"password"
|
"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)
|
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))
|
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]) {
|
} elseif ('bg' == $match[0]) {
|
||||||
$style->setBackground($match[1]);
|
$style->setBackground($match[1]);
|
||||||
} else {
|
} else {
|
||||||
$style->setOption($match[1]);
|
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,
|
PRODUCTION = TRUE,
|
||||||
DETECT = NULL;
|
DETECT = NULL;
|
||||||
|
|
||||||
|
const COOKIE_SECRET = 'tracy-debug';
|
||||||
|
|
||||||
/** @var string */
|
/** @var string */
|
||||||
public static $version = '2.3-dev';
|
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);
|
self::$time = isset($_SERVER['REQUEST_TIME_FLOAT']) ? $_SERVER['REQUEST_TIME_FLOAT'] : microtime(TRUE);
|
||||||
error_reporting(E_ALL | E_STRICT);
|
error_reporting(E_ALL | E_STRICT);
|
||||||
|
|
||||||
// production/development mode detection
|
self::$productionMode = is_bool($mode) ? $mode : !self::detectDebugMode($mode);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// logging configuration
|
// logging configuration
|
||||||
if ($email !== NULL) {
|
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