diff --git a/.gitignore b/.gitignore index 9fb5cb23b..d5d22b7a6 100755 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,8 @@ composer.lock # Grav Specific cache/* !cache/.* +assets/* +!assets/.* logs/* !logs/.* images/* diff --git a/.htaccess b/.htaccess index 268afb8e9..0c51c06ee 100755 --- a/.htaccess +++ b/.htaccess @@ -1,26 +1,38 @@ + +Options -Multiviews + RewriteEngine On -# access site +## +# If you are getting 404 errors on subpages, you may have to uncomment the RewriteBase entry +# You should change the '/' to your appropriate subfolder. For example if you have +# your Grav install at the root of your site '/' should work, else it might be something +# along the lines of: RewriteBase / +## + +# RewriteBase / + +# Access site RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d -RewriteRule . index.php [L] +RewriteRule .* index.php [L] -# block various user files from being accessed directly +# Block various user files from being accessed directly RewriteRule ^user/accounts/(.*)$ error [R=301,L] RewriteRule ^user/config/(.*)$ error [R=301,L] RewriteRule ^user/(.*)\.(txt|md|html|php|yaml|json|twig|sh|bat)$ error [R=301,L] -# block cache +# Block cache/ RewriteRule ^cache/(.*) error [R=301,L] -# block bin +# Block bin/ RewriteRule ^bin/(.*)$ error [R=301,L] -# block system +# Block system/ RewriteRule ^system/(.*)$ error [R=301,L] -# block vendor +# Block vendor/ RewriteRule ^vendor/(.*)$ error [R=301,L] diff --git a/README.md b/README.md index a6dbf236e..5aceb9548 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,19 @@ You can download a **ready-built** package from the [Downloads page on http://ge Check out the [install procedures](http://learn.getgrav.org/basics/installation) for more information. +# Contributing +We appreciate any contribution to Grav, whether it is related to bugs, grammar, or simply a suggestion or improvement. +However, we ask that any contribution follow our simple guidelines in order to be properly received. + +All our projects follow the [GitFlow branching model][gitflow-model], from development to release. If you are not familiar with it, there are several guides and tutorials to make you understand what it is about. + +You will probably want to get started by installing [this very good collection of git extensions][gitflow-extensions]. + +What you mainly want to know is that: + +- All the main activity happens in the `develop` branch. Any pull request should be addressed only to that branch. We will not consider pull requests made to the `master`. +- It's very well appreciated, and highly suggested, to start a new feature whenever you want to make changes or add functionalities. It will make it much easier for us to just checkout your feature branch and test it, before merging it into `develop` + # Getting Started * [What is Grav?](http://learn.getgrav.org/basics/what-is-grav) @@ -51,3 +64,7 @@ Check out the [install procedures](http://learn.getgrav.org/basics/installation) # License See [LICENSE](LICENSE) + + +[gitflow-model]: http://nvie.com/posts/a-successful-git-branching-model/ +[gitflow-extensions]: https://github.com/nvie/gitflow diff --git a/VERSION b/VERSION index a3df0a695..ac39a106c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.8.0 +0.9.0 diff --git a/assets/.gitkeep b/assets/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/bin/grav b/bin/grav index 37b41e86c..79015cea1 100755 --- a/bin/grav +++ b/bin/grav @@ -1,9 +1,13 @@ #!/usr/bin/env php =5.3.10", - "twig/twig": "1.16.*@dev", - "erusev/parsedown": "dev-master", - "symfony/yaml": "2.5.*@dev", - "symfony/console": "2.5.*@dev", - "doctrine/cache": "1.4.*@dev", - "tracy/tracy": "dev-master", - "gregwar/image": "dev-master", - "ircmaxell/password-compat": "1.0.*" + "php": ">=5.4.0", + "twig/twig": "~1.16", + "erusev/parsedown-extra": "dev-master", + "symfony/yaml": "~2.5", + "symfony/console": "~2.5", + "symfony/event-dispatcher": "~2.5", + "doctrine/cache": "~1.3", + "tracy/tracy": "~2.2", + "gregwar/image": "~2.0", + "ircmaxell/password-compat": "1.0.*", + "mrclay/minify": "~2.2", + "donatj/phpuseragentparser": "dev-master", + "pimple/pimple": "~3.0" + }, + "autoload": { + "psr-4": { + "Grav\\": "system/src/" + }, + "files": ["system/defines.php"] + }, + "archive": { + "exclude": ["VERSION"] } } diff --git a/index.php b/index.php index df77bbadc..1137ea3ca 100644 --- a/index.php +++ b/index.php @@ -1,46 +1,37 @@ PHP %s to run.', $ver, $req)); + exit(sprintf('You are running PHP %s, but Grav needs at least PHP %s to run.', $ver, $req)); +} +$autoload = __DIR__ . '/vendor/autoload.php'; +if (!is_file($autoload)) { + exit('Please run: composer install -o'); } -use Tracy\Debugger; +use Grav\Common\Grav; +use Grav\Common\Debugger; -// Register system libraries to the auto-loader. -$loader = require_once __DIR__ . '/system/autoload.php'; +// Register the auto-loader. +$loader = require_once $autoload; if (!ini_get('date.timezone')) { date_default_timezone_set('UTC'); } -// Use output buffering to prevent headers from being sent too early. -ob_start(); - -// Start the timer and enable debugger in production mode as we do not have system configuration yet. -// Debugger catches all errors and logs them, for example if the script doesn't have write permissions. -Debugger::timer(); -Debugger::enable(Debugger::DEVELOPMENT, is_dir(LOG_DIR) ? LOG_DIR : null); - -$grav = new Grav; +$grav = Grav::instance( + [ + 'loader' => $loader, + 'debugger' => new Debugger(Debugger::PRODUCTION) + ] +); try { - // Register all the Grav bits into registry. - $registry = Registry::instance(); - $registry->store('autoloader', $loader); - $registry->store('Grav', $grav); - $registry->store('Uri', new Uri); - $registry->store('Config', Config::instance(CACHE_DIR . 'config.php')); - $registry->store('Cache', new Cache); - $registry->store('Twig', new Twig); - $registry->store('Pages', new Page\Pages); - $registry->store('Taxonomy', new Taxonomy); - + $grav['debugger']->init(); $grav->process(); } catch (\Exception $e) { - $grav->fireEvent('onFatalException', $e); + $grav->fireEvent('onFatalException'); throw $e; } -ob_end_flush(); diff --git a/system/autoload.php b/system/autoload.php deleted file mode 100644 index 7419fb852..000000000 --- a/system/autoload.php +++ /dev/null @@ -1,8 +0,0 @@ -addPsr4('Grav\\', LIB_DIR . 'Grav'); - -return $loader; diff --git a/system/config/media.yaml b/system/config/media.yaml index 0e8b592fa..40385268e 100644 --- a/system/config/media.yaml +++ b/system/config/media.yaml @@ -1,3 +1,12 @@ +defaults: + type: file + thumb: media/thumb.png + mime: application/octet-stream + image: + filters: + default: + - enableProgressive + jpg: type: image thumb: media/thumb-jpg.png diff --git a/system/config/streams.yaml b/system/config/streams.yaml new file mode 100644 index 000000000..1ea6f2acd --- /dev/null +++ b/system/config/streams.yaml @@ -0,0 +1,36 @@ +schemes: + plugin: + type: ReadOnlyStream + paths: + - user/plugins + - system/plugins + +# asset: +# type: ReadOnlyStream +# paths: +# - assets + +# cache: +# type: ReadOnlyStream +# paths: +# - cache + +# log: +# type: ReadOnlyStream +# paths: +# - logs + + page: + type: ReadOnlyStream + paths: + - user/pages + + account: + type: ReadOnlyStream + paths: + - user/accounts + + data: + type: ReadOnlyStream + paths: + - user/data diff --git a/system/config/system.yaml b/system/config/system.yaml index 67ca278f4..6fd85e641 100644 --- a/system/config/system.yaml +++ b/system/config/system.yaml @@ -3,6 +3,7 @@ home: pages: theme: antimatter # Default theme (defaults to "antimatter" theme) + markdown_extra: false # Enable support for Markdown Extra support (GFM by default) order: by: defaults # Order pages by "default", "alpha" or "date" dir: asc # Default ordering direction, "asc" or "desc" @@ -15,7 +16,7 @@ pages: markdown: true # Process Markdown twig: false # Process Twig events: - page: false # Enable page level events + page: true # Enable page level events twig: true # Enable twig level events cache: @@ -26,14 +27,23 @@ cache: prefix: 'g' # Cache prefix string (prevents cache conflicts) twig: - cache: false # Set to true to enable twig caching - debug: true # Enable Twig debug - auto_reload: true # Refresh cache on changes - autoescape: false # Autoescape Twig vars + cache: true # Set to true to enable twig caching + debug: false # Enable Twig debug + auto_reload: true # Refresh cache on changes + autoescape: false # Autoescape Twig vars + +assets: # Configuration for Assets Manager (JS, CSS) + css_pipeline: false # The CSS pipeline is the unification of multiple CSS resources into one file + css_minify: true # Minify the CSS during pipelining + css_rewrite: true # Rewrite any CSS relative URLs during pipelining + js_pipeline: false # The JS pipeline is the unification of multiple JS resources into one file + js_minify: true # Minify the JS during pipelining debugger: - enabled: true # Enable Grav debugger - max_depth: 10 # How many nested levels to display for objects or arrays + enabled: false # Enable Grav debugger and following settings + mode: detect # Mode tracy Debugger should be set to when enabled: detect|development|production + strict: false # Throw fatal error also on PHP warnings and notices + max_depth: 10 # How many nested levels to display for objects or arrays log: - enabled: true # Enable logging - timing: false # Enable timing logging + enabled: true # Enable logging + timing: false # Enable timing logging diff --git a/system/defines.php b/system/defines.php index b7d1a3526..6b05bdad7 100644 --- a/system/defines.php +++ b/system/defines.php @@ -2,7 +2,7 @@ // Some standard defines define('GRAV', true); -define('GRAV_VERSION', '0.8.0'); +define('GRAV_VERSION', '0.9.0'); define('DS', '/'); // Directories and Paths @@ -12,6 +12,7 @@ define('USER_PATH', 'user/'); define('USER_DIR', ROOT_DIR . USER_PATH); define('SYSTEM_DIR', ROOT_DIR .'system/'); +define('ASSETS_DIR', ROOT_DIR . 'assets/'); define('CACHE_DIR', ROOT_DIR .'cache/'); define('IMAGES_DIR', ROOT_DIR . 'images/'); define('LOG_DIR', ROOT_DIR .'logs/'); @@ -20,7 +21,6 @@ define('ACCOUNTS_DIR', USER_DIR .'accounts/'); define('DATA_DIR', USER_DIR .'data/'); define('PAGES_DIR', USER_DIR .'pages/'); -define('BLOCKS_DIR', USER_DIR .'blocks/'); define('PLUGINS_DIR', USER_DIR .'plugins/'); define('THEMES_DIR', USER_DIR .'themes/'); diff --git a/system/src/Grav/Browser.php b/system/src/Grav/Browser.php new file mode 100644 index 000000000..2d6e0bdcd --- /dev/null +++ b/system/src/Grav/Browser.php @@ -0,0 +1,36 @@ +useragent = parse_user_agent(); + } + + public function getBrowser() + { + return strtolower($this->useragent['browser']); + } + + public function getPlatform() + { + return strtolower($this->useragent['platform']); + } + + public function getLongVersion() + { + return $this->useragent['version']; + } + + public function getVersion() + { + $version = explode('.', $this->getLongVersion()); + return intval($version[0]); + } +} diff --git a/system/src/Grav/Common/Assets.php b/system/src/Grav/Common/Assets.php new file mode 100644 index 000000000..78e389767 --- /dev/null +++ b/system/src/Grav/Common/Assets.php @@ -0,0 +1,693 @@ +config((array)$options); + } + + /** + * Initialization called in the Grav lifecycle to initialize the Assets with appropriate configuration + */ + public function init() + { + /** @var Config $config */ + $config = self::$grav['config']; + $base_url = trim($config->get('system.base_url_relative')); + $theme = trim($config->get('system.pages.theme')); + $asset_config = (array)$config->get('system.assets'); + + $this->config($asset_config); + $this->base_url = $base_url . '/'; + } + + /** + * 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 $this + * @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 $this + */ + 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 $this + */ + 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 $this + */ + 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 .= ''."\n"; + + foreach ($this->css_no_pipeline as $file) { + $output .= ''."\n"; + } + return $output; + } + + + foreach($this->css as $file) + $output .= ''."\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 .= ''."\n"; + foreach ($this->js_no_pipeline as $file) { + $output .= ''."\n"; + } + return $output; + } + + + foreach($this->js as $file) + $output .= ''."\n"; + + return $output; + } + + /** + * Add/replace collection. + * + * @param string $collectionName + * @param array $assets + * @return $this + */ + public function registerCollection($collectionName, Array $assets) + { + $this->collections[$collectionName] = $assets; + + return $this; + } + + /** + * Reset all assets. + * + * @return $this + */ + public function reset() + { + return $this->resetCss()->resetJs(); + } + + /** + * Reset CSS assets. + * + * @return $this + */ + public function resetCss() + { + $this->css = array(); + + return $this; + } + + /** + * Reset JavaScript assets. + * + * @return $this + */ + public function resetJs() + { + $this->js = array(); + + return $this; + } + + /** + * Minifiy and concatenate CSS / JS files. + * + * @return string + */ + protected function pipeline($css = true) + { + /** @var Cache $cache */ + $cache = self::$grav['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']; + $relative_path = $link; + + if($this->isRemoteLink($link)) { + $local = false; + if('//' === substr($link, 0, 2)) + $link = 'http:' . $link; + } else { + // Fix to remove relative dir if grav is in one + if (($this->base_url != '/') && (strpos($this->base_url, $link) == 0)) { + $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); + + // If this is CSS + the file is local + rewrite enabled + if ($css && $local && $this->css_rewrite) { + $file = $this->cssRewrite($file, $relative_dir); + } + + $buffer .= $file; + } + + // Pull out @imports and move to top + if ($css) { + $buffer = $this->moveImports($buffer); + } + + return $buffer; + } + + /** + * Moves @import statements to the top of the file per the CSS specification + * + * @param string $file the file containing the combined CSS files + * @return string the modified file with any @imports at the top of the file + */ + protected function moveImports($file) + { + $this->imports = array(); + + $file = preg_replace_callback(self::CSS_IMPORT_REGEX, + 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 = rtrim($this->base_url,'/') . $relative_path . '/' . implode('/', $newpath); + + return str_replace($old_url, $new_url, $matches[0]); + }, + $file + ); + + return $file; + } + + /** + * Build local links including grav asset shortcodes + * + * @param string $asset the asset string reference + * @return string the final link url to the asset + */ + protected function buildLocalLink($asset) + { + try { + return $this->base_url . self::$grav['locator']->findResource($asset, false); + } catch (\Exception $e) {} + + return $this->base_url . $asset; + } + + + /** + * 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 $this + * @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 $this + */ + 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 $this + */ + 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; + } + + /** + * @param $a + * @param $b + * @return mixed + */ + protected function priorityCompare($a, $b) + { + return $a ['priority'] - $b ['priority']; + } + + public function __toString() { + return ''; + } + +} diff --git a/system/src/Grav/Common/Cache.php b/system/src/Grav/Common/Cache.php index a032e554d..d3592b389 100644 --- a/system/src/Grav/Common/Cache.php +++ b/system/src/Grav/Common/Cache.php @@ -1,6 +1,8 @@ init($grav); + } + /** * Initialization that sets a base key and the driver based on configuration settings * + * @param Grav $grav * @return void */ - public function init() + public function init(Grav $grav) { /** @var Config $config */ - $config = Registry::get('Config'); - $prefix = $config->get('system.cache.prefix'); + $this->config = $grav['config']; + /** @var Uri $uri */ - $uri = Registry::get('Uri'); + $uri = $grav['uri']; - $this->enabled = (bool) $config->get('system.cache.enabled'); + $prefix = $this->config->get('system.cache.prefix'); + + $this->enabled = (bool) $this->config->get('system.cache.enabled'); // Cache key allows us to invalidate all cache on configuration changes. - $this->key = substr(md5(($prefix ? $prefix : 'g') . $uri->rootUrl(true) . $config->key . GRAV_VERSION), 2, 8); + $this->key = substr(md5(($prefix ? $prefix : 'g') . $uri->rootUrl(true) . $this->config->key . GRAV_VERSION), 2, 8); + + $this->driver = $this->getCacheDriver(); + } + + /** + * Automatically picks the cache mechanism to use. If you pick one manually it will use that + * If there is no config option for $driver in the config, or it's set to 'auto', it will + * pick the best option based on which cache extensions are installed. + * + * @return DoctrineCacheDriver The cache driver to use + */ + public function getCacheDriver() + { + $setting = $this->config->get('system.cache.driver'); + $driver_name = 'file'; - switch ($this->getCacheDriverName($config->get('system.cache.driver'))) { + if (!$setting || $setting == 'auto') { + if (extension_loaded('apc')) { + $driver_name = 'apc'; + } elseif (extension_loaded('wincache')) { + $driver_name = 'wincache'; + } elseif (extension_loaded('xcache')) { + $driver_name = 'xcache'; + } elseif (extension_loaded('memcache')) { + $driver_name = 'memcache'; + } + } else { + $driver_name = $setting; + } + + switch ($driver_name) { case 'apc': $driver = new \Doctrine\Common\Cache\ApcCache(); break; @@ -75,33 +119,8 @@ public function init() $driver = new \Doctrine\Common\Cache\FilesystemCache(CACHE_DIR); break; } - $this->driver = $driver; - } - /** - * Automatically picks the cache mechanism to use. If you pick one manually it will use that - * If there is no config option for $driver in the config, or it's set to 'auto', it will - * pick the best option based on which cache extensions are installed. - * - * @param string $setting - * @return string The name of the best cache driver to use - */ - protected function getCacheDriverName($setting = null) - { - - if (!$setting || $setting == 'auto') { - if (extension_loaded('apc') && ini_get('apc.enabled')) { - return 'apc'; - } elseif (extension_loaded('wincache')) { - return 'wincache'; - } elseif (extension_loaded('xcache') && ini_get('xcache.size') && ini_get('xcache.cacher')) { - return 'xcache'; - } else { - return 'file'; - } - } else { - return $setting; - } + return $driver; } /** diff --git a/system/src/Grav/Common/Config.php b/system/src/Grav/Common/Config.php index 68865b6df..df25af870 100644 --- a/system/src/Grav/Common/Config.php +++ b/system/src/Grav/Common/Config.php @@ -101,16 +101,17 @@ public function save() } /** - * Gets configuration instance. + * Load configuration. * - * @param string $filename + * @param Grav $grav * @return \Grav\Common\Config */ - public static function instance($filename) + public static function instance(Grav $grav) { + $filename = $grav['config_path']; + // Load cached version if available.. if (file_exists($filename)) { - clearstatcache(true, $filename); require_once $filename; if (class_exists('\Grav\Config')) { @@ -131,11 +132,11 @@ public static function instance($filename) // If not set, add manually current base url. if (empty($instance->items['system']['base_url_absolute'])) { - $instance->items['system']['base_url_absolute'] = Registry::get('Uri')->rootUrl(true); + $instance->items['system']['base_url_absolute'] = $grav['uri']->rootUrl(true); } if (empty($instance->items['system']['base_url_relative'])) { - $instance->items['system']['base_url_relative'] = Registry::get('Uri')->rootUrl(false); + $instance->items['system']['base_url_relative'] = $grav['uri']->rootUrl(false); } return $instance; diff --git a/system/src/Grav/Common/Data/Blueprint.php b/system/src/Grav/Common/Data/Blueprint.php index 6339f873a..362a999cf 100644 --- a/system/src/Grav/Common/Data/Blueprint.php +++ b/system/src/Grav/Common/Data/Blueprint.php @@ -1,7 +1,6 @@ get('system.debugger.mode'); + TracyDebugger::$logDirectory = $config->get('system.debugger.log.enabled') ? LOG_DIR : null; + TracyDebugger::$maxDepth = $config->get('system.debugger.max_depth'); + + // Switch debugger into development mode if configured + if ($config->get('system.debugger.enabled')) { + if ($config->get('system.debugger.strict')) { + TracyDebugger::$strictMode = true; + } + + if (function_exists('ini_set')) { + ini_set('display_errors', true); + } + + if ($mode == strtolower('detect')) { + TracyDebugger::$productionMode = self::DETECT; + } elseif ($mode == strtolower('production')) { + TracyDebugger::$productionMode = self::PRODUCTION; + } else { + TracyDebugger::$productionMode = self::DEVELOPMENT; + } + + + } + } + + /** + * Log a message. + * + * @param string $message + */ + public function log($message) + { + if (TracyDebugger::$logDirectory) { + TracyDebugger::log(sprintf($message, TracyDebugger::timer() * 1000)); + } + } + + public static function dump($var) + { + TracyDebugger::dump($var); + } +} diff --git a/system/src/Grav/Common/Filesystem/File/Config.php b/system/src/Grav/Common/Filesystem/File/Config.php index d697b1f65..c54f76c73 100644 --- a/system/src/Grav/Common/Filesystem/File/Config.php +++ b/system/src/Grav/Common/Filesystem/File/Config.php @@ -85,7 +85,7 @@ protected function encode($var) } $vars = implode("\n", $vars); - return "handle = fopen($this->filename, 'wb+'); } $lock = $block ? LOCK_EX : LOCK_EX | LOCK_NB; - return $this->locked = flock($this->handle, $lock); + return $this->locked = $this->handle ? flock($this->handle, $lock) : false; } /** diff --git a/system/src/Grav/Common/Getters.php b/system/src/Grav/Common/Getters.php index 8a98eaa7f..857dccaba 100644 --- a/system/src/Grav/Common/Getters.php +++ b/system/src/Grav/Common/Getters.php @@ -36,7 +36,7 @@ public function __set($offset, $value) */ public function __get($offset) { - return $this->offsetGet($offset); + return $this->offsetGet($offset); } /** diff --git a/system/src/Grav/Common/Grav.php b/system/src/Grav/Common/Grav.php index f224cf7ae..07d548d75 100644 --- a/system/src/Grav/Common/Grav.php +++ b/system/src/Grav/Common/Grav.php @@ -1,10 +1,10 @@ $value) { + $instance->offsetSet($key, $value); + } + } - /** - * @var Twig - */ - protected $twig; + return self::$instance; + } - /** - * @var Taxonomy - */ - protected $taxonomy; + protected static function load(array $values) + { + $container = new static($values); + + $container['config_path'] = CACHE_DIR . 'config.php'; + + $container['grav'] = $container; + + $container['events'] = function ($c) { + return new EventDispatcher; + }; + $container['uri'] = function ($c) { + return new Uri($c); + }; + $container['config'] = function ($c) { + return Config::instance($c); + }; + $container['cache'] = function ($c) { + return new Cache($c); + }; + $container['plugins'] = function ($c) { + return new Plugins($c); + }; + $container['themes'] = function ($c) { + return new Themes($c); + }; + $container['twig'] = function ($c) { + return new Twig($c); + }; + $container['taxonomy'] = function ($c) { + return new Taxonomy($c); + }; + $container['pages'] = function ($c) { + return new Page\Pages($c); + }; + $container['assets'] = function ($c) { + return new Assets(); + }; + $container['page'] = function ($c) { + $page = $c['pages']->dispatch($c['uri']->route()); + + if (!$page || !$page->routable()) { + $event = $c->fireEvent('onPageNotFound'); + + if (isset($event->page)) { + $page = $event->page; + } else { + throw new \RuntimeException('Page Not Found', 404); + } + } + return $page; + }; + $container['output'] = function ($c) { + return $c['twig']->processSite($c['uri']->extension()); + }; + $container['browser'] = function ($c) { + return new Browser(); + }; + + $container->register(new StreamsServiceProvider); + + return $container; + } public function process() { - // Get the URI and URL (needed for configuration) - $this->uri = Registry::get('Uri'); - - // Get the Configuration settings and caching - $this->config = Registry::get('Config'); - - Debugger::$logDirectory = $this->config->get('system.debugger.log.enabled') ? LOG_DIR : null; - Debugger::$maxDepth = $this->config->get('system.debugger.max_depth'); - - // Switch debugger into development mode if configured - if ($this->config->get('system.debugger.enabled')) { - if (function_exists('ini_set')) { - ini_set('display_errors', true); - } - Debugger::$productionMode = Debugger::DEVELOPMENT; - } + // Use output buffering to prevent headers from being sent too early. + ob_start(); - // Get the Caching setup - $this->cache = Registry::get('Cache'); - $this->cache->init(); + // Initialize stream wrappers. + $this['locator']; - // Get Plugins - $plugins = new Plugins(); - $this->plugins = $plugins->load(); - $this->fireEvent('onAfterInitPlugins'); + $this['plugins']->init(); - // Get current theme and hook it into plugins. - $themes = new Themes(); - $this->plugins['Theme'] = $themes->load(); + $this->fireEvent('onPluginsInitialized'); - // Get twig object - $this->twig = Registry::get('Twig'); - $this->twig->init(); + $this['assets']->init(); - // Get all the Pages that Grav knows about - $this->pages = Registry::get('Pages'); - $this->pages->init(); - $this->fireEvent('onAfterGetPages'); + $this->fireEvent('onAssetsInitialized'); - // Get the taxonomy and set it on the grav object - $this->taxonomy = Registry::get('Taxonomy'); + $this['twig']->init(); + $this['pages']->init(); - // Get current page - $this->page = $this->pages->dispatch($this->uri->route()); - $this->fireEvent('onAfterGetPage'); + $this->fireEvent('onPagesInitialized'); - // If there's no page, throw exception - if (!$this->page) { - throw new \RuntimeException('Page Not Found', 404); - } + $this->fireEvent('onPageInitialized'); // Process whole page as required - $this->output = $this->twig->processSite($this->uri->extension()); - $this->fireEvent('onAfterGetOutput'); + $this->output = $this['output']; + + $this->fireEvent('onOutputGenerated'); // Set the header type $this->header(); echo $this->output; + + ob_end_flush(); + flush(); + + $this->fireEvent('onOutputRendered'); } /** @@ -142,7 +157,9 @@ public function process() */ public function redirect($route, $code = 303) { - header("Location: " . rtrim($this->uri->rootUrl(), '/') .'/'. trim($route, '/'), true, $code); + /** @var Uri $uri */ + $uri = $this['uri']; + header("Location: " . rtrim($uri->rootUrl(), '/') .'/'. trim($route, '/'), true, $code); exit(); } @@ -174,40 +191,22 @@ public function mime($format) */ public function header() { - header('Content-type: ' . $this->mime($this->uri->extension())); + /** @var Uri $uri */ + $uri = $this['uri']; + header('Content-type: ' . $this->mime($uri->extension())); } /** - * Log a message. + * Fires an event with optional parameters. * - * @param string $message + * @param string $eventName + * @param Event $event + * @return Event */ - protected static function log($message) + public function fireEvent($eventName, Event $event = null) { - if (Debugger::$logDirectory) { - Debugger::log(sprintf($message, Debugger::timer() * 1000)); - } - } - - /** - * Processes any hooks and runs them. - */ - public function fireEvent() - { - $args = func_get_args(); - $hook_id = array_shift($args); - $no_timing_hooks = array('onAfterPageProcessed','onAfterFolderProcessed', 'onAfterCollectionProcessed'); - - if (!empty($this->plugins)) { - foreach ($this->plugins as $plugin) { - if (is_callable(array($plugin, $hook_id))) { - call_user_func_array(array($plugin, $hook_id), $args); - } - } - } - - if ($this->config && $this->config->get('system.debugger.log.timing') && !in_array($hook_id, $no_timing_hooks)) { - static::log($hook_id.': %f ms'); - } + /** @var EventDispatcher $events */ + $events = $this['events']; + return $events->dispatch($eventName, $event); } } diff --git a/system/src/Grav/Common/GravTrait.php b/system/src/Grav/Common/GravTrait.php new file mode 100644 index 000000000..b18258323 --- /dev/null +++ b/system/src/Grav/Common/GravTrait.php @@ -0,0 +1,26 @@ +page = $page; + } + +} diff --git a/system/src/Grav/Common/Markdown/MarkdownExtra.php b/system/src/Grav/Common/Markdown/MarkdownExtra.php new file mode 100644 index 000000000..b85516f5a --- /dev/null +++ b/system/src/Grav/Common/Markdown/MarkdownExtra.php @@ -0,0 +1,13 @@ +page = $page; + } +} diff --git a/system/src/Grav/Common/Markdown/MarkdownGravLinkTrait.php b/system/src/Grav/Common/Markdown/MarkdownGravLinkTrait.php new file mode 100644 index 000000000..11475a013 --- /dev/null +++ b/system/src/Grav/Common/Markdown/MarkdownGravLinkTrait.php @@ -0,0 +1,83 @@ +page->media(); + + // if there is a media file that matches the path referenced.. + if (isset($media->images()[$url['path']])) { + // get the medium object + $medium = $media->images()[$url['path']]; + + // if there is a query, then parse it and build action calls + if (isset($url['query'])) { + parse_str($url['query'], $actions); + } + + // loop through actions for the image and call them + foreach ($actions as $action => $params) { + // as long as it's not an html, url or ligtbox action + if (!in_array($action, ['html','url','lightbox'])) { + call_user_func_array(array(&$medium, $action), explode(',', $params)); + } + } + + // Get the URL for regular images, or an array of bits needed to put together + // the lightbox HTML + if (!isset($actions['lightbox'])) { + $src = $medium->url(); + } else { + $src = $medium->lightboxRaw(); + } + + // set the src element with the new generated url + if (!isset($actions['lightbox']) && !is_array($src)) { + $Excerpt['element']['attributes']['src'] = $src; + } else { + + // Create the custom lightbox element + $Element = array( + 'name' => 'a', + 'attributes' => array('rel' => $src['a_rel'], 'href' => $src['a_url']), + 'handler' => 'element', + 'text' => array( + 'name' => 'img', + 'attributes' => array('src' => $src['img_url'], 'alt' => $alt, 'title' => $title) + ), + ); + + // Set the lightbox element on the Excerpt + $Excerpt['element'] = $Element; + } + } + } + } + return $Excerpt; + } +} diff --git a/system/src/Grav/Common/Page/Collection.php b/system/src/Grav/Common/Page/Collection.php index 857675af6..9aed0e089 100644 --- a/system/src/Grav/Common/Page/Collection.php +++ b/system/src/Grav/Common/Page/Collection.php @@ -1,7 +1,8 @@ params = $params; - $this->pages = $pages ? $pages : Registry::get('Pages'); + $this->pages = $pages ? $pages : Grav::instance()->offsetGet('pages'); } public function params() @@ -114,9 +115,10 @@ public function order($by, $dir = 'asc', $manual = null) } /** - * Check to see if this item is the first in the collection + * Check to see if this item is the first in the collection. + * * @param string $path - * @return boolean True if item is first + * @return boolean True if item is first. */ public function isFirst($path) { @@ -128,9 +130,10 @@ public function isFirst($path) } /** - * Check to see if this item is the last in the collection + * Check to see if this item is the last in the collection. + * * @param string $path - * @return boolean True if item is last + * @return boolean True if item is last. */ public function isLast($path) { @@ -142,9 +145,10 @@ public function isLast($path) } /** - * Gets the previous sibling based on current position + * Gets the previous sibling based on current position. * - * @return Object the previous item + * @param string $path + * @return Page The previous item. */ public function prevSibling($path) { @@ -152,9 +156,10 @@ public function prevSibling($path) } /** - * Gets the next sibling based on current position + * Gets the next sibling based on current position. * - * @return Object the next item + * @param string $path + * @return Page The next item. */ public function nextSibling($path) { @@ -162,9 +167,11 @@ public function nextSibling($path) } /** - * Returns the adjacent sibling based on a direction + * Returns the adjacent sibling based on a direction. + * + * @param string $path * @param integer $direction either -1 or +1 - * @return Object the sibling item + * @return Page The sibling item. */ public function adjacentSibling($path, $direction = 1) { @@ -177,11 +184,12 @@ public function adjacentSibling($path, $direction = 1) } /** - * Returns the item in the current position - * @param String $path the path the item - * @return Object item in the array the the current position + * Returns the item in the current position. + * + * @param string $path the path the item + * @return Page Item in the array the the current position. */ public function currentPosition($path) { - return array_search($path,array_keys($this->items)); + return array_search($path, array_keys($this->items)); } } diff --git a/system/src/Grav/Common/Page/Media.php b/system/src/Grav/Common/Page/Media.php index cf210a389..801055a51 100644 --- a/system/src/Grav/Common/Page/Media.php +++ b/system/src/Grav/Common/Page/Media.php @@ -2,9 +2,9 @@ namespace Grav\Common\Page; use Grav\Common\Getters; -use Grav\Common\Registry; -use Grav\Config; -use Symfony\Component\Yaml\Yaml; +use Grav\Common\Grav; +use Grav\Common\Config; +use Grav\Common\GravTrait; /** * Media is a holder object that contains references to the media of page. This object is created and @@ -15,6 +15,8 @@ */ class Media extends Getters { + use GravTrait; + protected $gettersVariable = 'instances'; protected $path; @@ -76,7 +78,7 @@ public function get($filename, $create = false) $basename = implode('.', $parts); /** @var Config $config */ - $config = Registry::get('Config'); + $config = self::$grav['config']; // Check if medium type has been configured. $params = $config->get("media.{$ext}"); @@ -85,6 +87,9 @@ public function get($filename, $create = false) } $filePath = $this->path . '/' . $filename; + + // Add default settings for undefined variables. + $params += $config->get('media.defaults'); $params += array( 'type' => 'file', 'thumb' => 'media/thumb.png', diff --git a/system/src/Grav/Common/Page/Medium.php b/system/src/Grav/Common/Page/Medium.php index f966af980..d9f3b5d06 100644 --- a/system/src/Grav/Common/Page/Medium.php +++ b/system/src/Grav/Common/Page/Medium.php @@ -1,11 +1,12 @@ image) { $output = $this->image->cacheFile($this->type, $this->quality); @@ -172,7 +176,8 @@ public function html($title = null, $class = null, $type = null, $quality = 80) } if ($this->linkTarget) { - $config = Registry::get('Config'); + /** @var Config $config */ + $config = self::$grav['config']; $output = 'linkAttributes. ' class="'. $class . '">' . $output . ''; @@ -197,6 +202,15 @@ public function lightbox($width = null, $height = null) return $this->link($width, $height); } + public function lightboxRaw($width = null, $height = null) + { + $url = $this->url(); + $this->link($width, $height); + $lightbox_url = self::$grav['config']->get('system.base_url_relative') . '/'. $this->linkTarget; + + return array('a_url' => $lightbox_url, 'a_rel' => 'lightbox', 'img_url' => $url); + } + /** * Return link HTML for the medium. * diff --git a/system/src/Grav/Common/Page/Page.php b/system/src/Grav/Common/Page/Page.php index 310ade904..63ebc1152 100644 --- a/system/src/Grav/Common/Page/Page.php +++ b/system/src/Grav/Common/Page/Page.php @@ -1,18 +1,21 @@ routable = true; $this->taxonomy = array(); @@ -196,6 +202,9 @@ public function header($var = null) if (isset($this->header->date)) { $this->date = strtotime($this->header->date); } + if (isset($this->header->markdown_extra)) { + $this->markdown_extra = (bool)$this->header->markdown_extra; + } if (isset($this->header->taxonomy)) { foreach ($this->header->taxonomy as $taxonomy => $taxitems) { $this->taxonomy[$taxonomy] = (array)$taxitems; @@ -209,7 +218,9 @@ public function header($var = null) $this->process[$process] = $status; } } + } + return $this->header; } @@ -231,7 +242,7 @@ public function summary($size = null) // Return calculated summary based on setting in site config file /** @var Config $config */ - $config = Registry::get('Config'); + $config = self::$grav['config']; if (!$size && $config->get('site.summary.size')) { $size = $config->get('site.summary.size'); } @@ -269,9 +280,12 @@ public function content($var = null) // If no content, process it if ($this->content === null) { + // Get media + $this->media(); + // Load cached content /** @var Cache $cache */ - $cache = Registry::get('Cache'); + $cache = self::$grav['cache']; $cache_id = md5('page'.$this->id()); $content = $cache->fetch($cache_id); @@ -297,7 +311,7 @@ public function content($var = null) // Do we need to process twig this time? if ($update_cache || $process_twig) { /** @var Twig $twig */ - $twig = Registry::get('Twig'); + $twig = self::$grav['twig']; $content = $twig->processPage($this, $content); } } @@ -316,7 +330,6 @@ public function content($var = null) $this->content = $content; - $this->media(); } return $this->content; @@ -465,7 +478,7 @@ public function copy($parent) public function blueprints() { /** @var Pages $pages */ - $pages = Registry::get('Pages'); + $pages = self::$grav['pages']; return $pages->blueprints($this->template()); } @@ -544,7 +557,7 @@ public function toJson() public function media($var = null) { /** @var Cache $cache */ - $cache = Registry::get('Cache'); + $cache = self::$grav['cache']; if ($var) { $this->media = $var; @@ -762,7 +775,7 @@ public function link($include_host = false) public function url($include_host = false) { /** @var Uri $uri */ - $uri = Registry::get('Uri'); + $uri = self::$grav['uri']; $rootUrl = $uri->rootUrl($include_host); $url = $rootUrl.'/'.trim($this->route(), '/'); @@ -958,7 +971,7 @@ public function maxCount($var = null) } if (empty($this->max_count)) { /** @var Config $config */ - $config = Registry::get('Config'); + $config = self::$grav['config']; $this->max_count = (int) $config->get('system.pages.list.count'); } return $this->max_count; @@ -1029,11 +1042,13 @@ public function shouldProcess($process) */ public function parent(Page $var = null) { - if ($var !== null) { - $this->parent = $var ? $var->path() : ''; + if ($var) { + $this->parent = $var->path(); + return $var; } + /** @var Pages $pages */ - $pages = Registry::get('Pages'); + $pages = self::$grav['pages']; return $pages->get($this->parent); } @@ -1046,7 +1061,7 @@ public function parent(Page $var = null) public function children() { /** @var Pages $pages */ - $pages = Registry::get('Pages'); + $pages = self::$grav['pages']; return $pages->children($this->path()); } @@ -1126,7 +1141,7 @@ public function nth($key) public function isFirst() { /** @var Pages $pages */ - $pages = Registry::get('Pages'); + $pages = self::$grav['pages']; $parent = $pages->get($this->parent); if ($this->path() == array_values($parent->items)[0]) { @@ -1144,7 +1159,7 @@ public function isFirst() public function isLast() { /** @var Pages $pages */ - $pages = Registry::get('Pages'); + $pages = self::$grav['pages']; $parent = $pages->get($this->parent); if ($this->path() == array_values($parent->items)[count($parent->items)-1]) { @@ -1183,7 +1198,7 @@ public function nextSibling() public function adjacentSibling($direction = 1) { /** @var Pages $pages */ - $pages = Registry::get('Pages'); + $pages = self::$grav['pages']; $parent = $pages->get($this->parent); $current = $this->slug(); @@ -1202,7 +1217,7 @@ public function adjacentSibling($direction = 1) public function active() { /** @var Uri $uri */ - $uri = Registry::get('Uri'); + $uri = self::$grav['uri']; if ($this->url() == $uri->url()) { return true; } @@ -1217,7 +1232,7 @@ public function active() */ public function activeChild() { - $uri = Registry::get('Uri'); + $uri = self::$grav['uri']; if (!$this->home() && (strpos($uri->url(), $this->url()) !== false)) { return true; } @@ -1258,7 +1273,7 @@ public function root() public function find($url) { /** @var Pages $pages */ - $pages = Registry::get('Pages'); + $pages = self::$grav['pages']; return $pages->dispatch($url); } @@ -1289,9 +1304,9 @@ public function collection($params = 'content') // TODO: MOVE THIS INTO SOMEWHERE ELSE? /** @var Uri $uri */ - $uri = Registry::get('Uri'); + $uri = self::$grav['uri']; /** @var Config $config */ - $config = Registry::get('Config'); + $config = self::$grav['config']; foreach ((array) $config->get('site.taxonomies') as $taxonomy) { if ($uri->param($taxonomy)) { @@ -1310,7 +1325,7 @@ public function collection($params = 'content') } } - $config->set('system.cache.enabled', false); + $config->set('system.cache.enabled', false); // TODO: Do we still need this? } } // TODO: END OF MOVE @@ -1323,10 +1338,10 @@ public function collection($params = 'content') } /** @var Grav $grav */ - $grav = Registry::get('Grav'); + $grav = self::$grav['grav']; // New Custom event to handle things like pagination. - $grav->fireEvent('onAfterCollectionProcessed', $collection); + $grav->fireEvent('onCollectionProcessed', new Event(['collection' => $collection])); $params = $collection->params(); @@ -1391,7 +1406,7 @@ protected function evaluate($value) // @taxonomy: { category: [ blog, featured ], level: 1 } /** @var Taxonomy $taxonomy_map */ - $taxonomy_map = Registry::get('Taxonomy'); + $taxonomy_map = self::$grav['taxonomy']; if (!empty($parts)) { $params = [implode('.', $parts) => $params]; @@ -1506,7 +1521,15 @@ protected function rawContent($content) */ protected function parseMarkdownContent($content) { - $parsedown = new \Parsedown(); + /** @var Config $config */ + $config = self::$grav['config']; + + // get the appropriate setting for markdown extra + if (isset($this->markdown_extra) ? $this->markdown_extra : $config->get('system.pages.markdown_extra')) { + $parsedown = new MarkdownExtra($this); + } else { + $parsedown = new Markdown($this); + } $content = $parsedown->parse($content); return $content; } @@ -1540,7 +1563,7 @@ protected function doRelocation($reorder) // Do reordering. if ($reorder && $this->order() != $this->_original->order()) { /** @var Pages $pages */ - $pages = Registry::get('Pages'); + $pages = self::$grav['pages']; $parent = $this->parent(); diff --git a/system/src/Grav/Common/Page/Pages.php b/system/src/Grav/Common/Page/Pages.php index 2de156167..9dbcca008 100644 --- a/system/src/Grav/Common/Page/Pages.php +++ b/system/src/Grav/Common/Page/Pages.php @@ -1,17 +1,20 @@ grav = $c; + } + /** * Class initialization. Must be called before using this class. */ public function init() { - $this->grav = Registry::get('Grav'); - $this->config = Registry::get('Config'); - $this->buildPages(); } @@ -221,7 +226,10 @@ public function dispatch($url, $all = false) // If the page cannot be reached, look into site wide routes. if (!$all && (!$page || !$page->routable())) { - $route = $this->config->get("site.routes.{$url}"); + /** @var Config $config */ + $config = $this->grav['config']; + + $route = $config->get("site.routes.{$url}"); if ($route) { $page = $this->dispatch($route, $all); } @@ -249,7 +257,10 @@ public function root() public function blueprints($type) { if (!isset($this->blueprints)) { - $this->blueprints = new Data\Blueprints(THEMES_DIR . $this->config->get('system.pages.theme') . '/blueprints/'); + /** @var Config $config */ + $config = $this->grav['config']; + + $this->blueprints = new Data\Blueprints(THEMES_DIR . $config->get('system.pages.theme') . '/blueprints/'); } try { @@ -259,9 +270,7 @@ public function blueprints($type) } if (!$blueprint->initialized) { - /** @var Grav $grav */ - $grav = Registry::get('Grav'); - $grav->fireEvent('onCreateBlueprint', $blueprint); + $this->grav->fireEvent('onBlueprintCreated', new Event(['blueprint' => $blueprint])); $blueprint->initialized = true; } @@ -305,8 +314,11 @@ public function getList(Page $current = null, $level = 0) */ static public function types() { + $grav = Grav::instance(); + /** @var Config $config */ - $config = Registry::get('Config'); + $config = $grav['config']; + $blueprints = new Data\Blueprints(THEMES_DIR . $config->get('system.pages.theme') . '/blueprints/'); return $blueprints->types(); @@ -319,8 +331,11 @@ static public function types() */ static public function parents() { + $grav = Grav::instance(); + /** @var Pages $pages */ - $pages = Registry::get('Pages'); + $pages = $grav['pages']; + return $pages->getList(); } @@ -332,12 +347,16 @@ static public function parents() protected function buildPages() { $this->sort = array(); - if ($this->config->get('system.cache.enabled')) { + + /** @var Config $config */ + $config = $this->grav['config']; + + if ($config->get('system.cache.enabled')) { /** @var Cache $cache */ - $cache = Registry::get('Cache'); + $cache = $this->grav['cache']; /** @var Taxonomy $taxonomy */ - $taxonomy = Registry::get('Taxonomy'); - $last_modified = $this->config->get('system.cache.check.pages', true) + $taxonomy = $this->grav['taxonomy']; + $last_modified = $config->get('system.cache.check.pages', true) ? Folder::lastModified(PAGES_DIR) : 0; $page_cache_id = md5(USER_DIR.$last_modified); @@ -370,16 +389,18 @@ protected function buildPages() * @throws \RuntimeException * @internal */ - protected function recurse($directory = PAGES_DIR, &$parent = null) + protected function recurse($directory = PAGES_DIR, Page &$parent = null) { $directory = rtrim($directory, DS); $iterator = new \DirectoryIterator($directory); $page = new Page; + $config = $this->grav['config']; $page->path($directory); - $page->parent($parent); - $page->orderDir($this->config->get('system.pages.order.dir')); - $page->orderBy($this->config->get('system.pages.order.by')); + if ($parent) $page->parent($parent); + + $page->orderDir($config->get('system.pages.order.dir')); + $page->orderBy($config->get('system.pages.order.by')); // Add into instances if (!isset($this->instances[$page->path()])) { @@ -399,8 +420,8 @@ protected function recurse($directory = PAGES_DIR, &$parent = null) $page->init($file); - if ($this->config->get('system.pages.events.page')) { - $this->grav->fireEvent('onAfterPageProcessed', $page); + if ($config->get('system.pages.events.page')) { + $this->grav->fireEvent('onPageProcessed', new Event(['page' => $page])); } } elseif ($file->isDir() && !$file->isDot()) { @@ -426,8 +447,8 @@ protected function recurse($directory = PAGES_DIR, &$parent = null) // set the last modified time on pages $this->lastModified($file->getMTime()); - if ($this->config->get('system.pages.events.page')) { - $this->grav->fireEvent('onAfterFolderProcessed', $page); + if ($config->get('system.pages.events.page')) { + $this->grav->fireEvent('onFolderProcessed', new Event(['page' => $page])); } } } @@ -444,7 +465,7 @@ protected function recurse($directory = PAGES_DIR, &$parent = null) protected function buildRoutes() { /** @var $taxonomy Taxonomy */ - $taxonomy = Registry::get('Taxonomy'); + $taxonomy = $this->grav['taxonomy']; // Build routes and taxonomy map. /** @var $page Page */ @@ -465,8 +486,11 @@ protected function buildRoutes() } } + /** @var Config $config */ + $config = $this->grav['config']; + // Alias and set default route to home page. - $home = trim($this->config->get('system.home.alias'), '/'); + $home = trim($config->get('system.home.alias'), '/'); if ($home && isset($this->routes['/' . $home])) { $this->routes['/'] = $this->routes['/' . $home]; $this->get($this->routes['/' . $home])->route('/'); @@ -489,7 +513,7 @@ protected function buildSort($path, array $pages, $order_by = 'default', $manual $child = isset($this->instances[$key]) ? $this->instances[$key] : null; if (!$child) { - throw new \RuntimeException("Page does not exist: {$key}"); + throw new \RuntimeException("Page does not exist: {$key}"); } switch ($order_by) { @@ -515,8 +539,14 @@ protected function buildSort($path, array $pages, $order_by = 'default', $manual } } - // Sort by the new list. - asort($list); + // handle special case when order_by is random + if ($order_by == 'random') { + $list = $this->array_shuffle($list); + } else { + // else just sort the list according to specified key + asort($list); + } + // Move manually ordered items into the beginning of the list. Order of the unlisted items does not change. if (is_array($manual) && !empty($manual)) { @@ -544,4 +574,17 @@ protected function buildSort($path, array $pages, $order_by = 'default', $manual $this->sort[$path][$order_by][$key] = $info; } } + + // Shuffles and associative array + protected function array_shuffle($list) { + $keys = array_keys($list); + shuffle($keys); + + $new = array(); + foreach($keys as $key) { + $new[$key] = $list[$key]; + } + + return $new; + } } diff --git a/system/src/Grav/Common/Plugin.php b/system/src/Grav/Common/Plugin.php index fb25dccdb..0371cd41a 100644 --- a/system/src/Grav/Common/Plugin.php +++ b/system/src/Grav/Common/Plugin.php @@ -1,26 +1,75 @@ grav = $grav; $this->config = $config; } + + /** + * @param array $events + */ + protected function enable(array $events) + { + /** @var EventDispatcher $dispatcher */ + $dispatcher = $this->grav['events']; + + foreach ($events as $eventName => $params) { + if (is_string($params)) { + $dispatcher->addListener($eventName, array($this, $params)); + } elseif (is_string($params[0])) { + $dispatcher->addListener($eventName, array($this, $params[0]), isset($params[1]) ? $params[1] : 0); + } else { + foreach ($params as $listener) { + $dispatcher->addListener($eventName, array($this, $listener[0]), isset($listener[1]) ? $listener[1] : 0); + } + } + } + } } diff --git a/system/src/Grav/Common/Plugins.php b/system/src/Grav/Common/Plugins.php index 1be3f9e7a..5990ca0c4 100644 --- a/system/src/Grav/Common/Plugins.php +++ b/system/src/Grav/Common/Plugins.php @@ -2,6 +2,8 @@ namespace Grav\Common; use Grav\Common\Filesystem\File; +use Grav\Component\EventDispatcher\EventDispatcher; +use Grav\Component\EventDispatcher\EventSubscriberInterface; /** * The Plugins object holds an array of all the plugin objects that @@ -10,13 +12,13 @@ * @author RocketTheme * @license MIT */ -class Plugins +class Plugins extends Iterator { - /** - * @var array|Plugin[] - */ - protected $plugins; + protected $grav; + public function __construct(Grav $grav) { + $this->grav = $grav; + } /** * Recurses through the plugins directory creating Plugin objects for each plugin it finds. @@ -24,20 +26,22 @@ class Plugins * @return array|Plugin[] array of Plugin objects * @throws \RuntimeException */ - public function load() + public function init() { /** @var Config $config */ - $config = Registry::get('Config'); + $config = $this->grav['config']; $plugins = (array) $config->get('plugins'); + /** @var EventDispatcher $events */ + $events = $this->grav['events']; + foreach ($plugins as $plugin => $data) { if (empty($data['enabled'])) { // Only load enabled plugins. continue; } - $folder = PLUGINS_DIR . $plugin; - $filePath = $folder . DS . $plugin . PLUGIN_EXT; + $filePath = $this->grav['locator']('plugin://' . $plugin . DS . $plugin . PLUGIN_EXT); if (!is_file($filePath)) { throw new \RuntimeException(sprintf("Plugin '%s' enabled but not found!", $filePath, $plugin)); } @@ -50,16 +54,25 @@ public function load() throw new \RuntimeException(sprintf("Plugin '%s' class not found!", $plugin)); } - $this->plugins[$pluginClass] = new $pluginClass($config); + $instance = new $pluginClass($this->grav, $config); + if ($instance instanceof EventSubscriberInterface) { + $events->addSubscriber($instance); + } + } + + $instance = $this->grav['themes']->load(); + $instance->configure(); + if ($instance instanceof EventSubscriberInterface) { + $events->addSubscriber($instance); } - return $this->plugins; + return $this->items; } public function add($plugin) { if (is_object($plugin)) { - $this->plugins[get_class($plugin)] = $plugin; + $this->items[get_class($plugin)] = $plugin; } } @@ -71,7 +84,7 @@ public function add($plugin) static public function all() { $list = array(); - $iterator = new \DirectoryIterator(PLUGINS_DIR); + $iterator = new \DirectoryIterator('plugin://'); /** @var \DirectoryIterator $directory */ foreach ($iterator as $directory) { @@ -90,16 +103,16 @@ static public function all() static public function get($type) { - $blueprints = new Data\Blueprints(PLUGINS_DIR . $type); + $blueprints = new Data\Blueprints('plugin://' . $type); $blueprint = $blueprints->get('blueprints'); $blueprint->name = $type; // Load default configuration. - $file = File\Yaml::instance(PLUGINS_DIR . "{$type}/{$type}" . YAML_EXT); + $file = File\Yaml::instance('plugin://' . "{$type}/{$type}" . YAML_EXT); $obj = new Data\Data($file->content(), $blueprint); // Override with user configuration. - $file = File\Yaml::instance(USER_DIR . "config/plugins/{$type}" . YAML_EXT); + $file = File\Yaml::instance('plugin://' . "config/plugins/{$type}" . YAML_EXT); $obj->merge($file->content()); // Save configuration always to user/config. diff --git a/system/src/Grav/Common/Registry.php b/system/src/Grav/Common/Registry.php index 59fbe5d7a..0f7b8d606 100644 --- a/system/src/Grav/Common/Registry.php +++ b/system/src/Grav/Common/Registry.php @@ -7,18 +7,10 @@ * * @author RocketTheme * @license MIT + * @deprecated */ class Registry { - /** - * @var array - */ - private $registry = array(); - - /** - * @var Registry - */ - private static $instance = null; /** * Return global instance. @@ -27,11 +19,8 @@ class Registry */ public static function instance() { - if (self::$instance === null) { - self::$instance = new Registry(); - } - - return self::$instance; + user_error(__METHOD__ . '()', E_USER_DEPRECATED); + return new Registry; } /** @@ -43,11 +32,9 @@ public static function instance() */ public static function get($key) { - if (!isset(self::$instance->registry[$key])) { - throw new \Exception("There is no entry for key " . $key); - } - - return self::$instance->registry[$key]; + user_error(__METHOD__ . '()', E_USER_DEPRECATED); + $instance = Grav::instance(); + return $instance[strtolower($key)]; } /** @@ -73,11 +60,9 @@ private function __clone() */ public function store($key, $value) { - if (isset($this->registry[$key])) { - throw new \Exception("There is already an entry for key " . $key); - } - - $this->registry[$key] = $value; + user_error(__CLASS__ . '::' . __METHOD__ . '()', E_USER_DEPRECATED); + $instance = Grav::instance(); + $instance[strtolower($key)] = $value; } /** @@ -89,10 +74,8 @@ public function store($key, $value) */ public function retrieve($key) { - if (!isset($this->registry[$key])) { - throw new \Exception("There is no entry for key " . $key); - } - - return $this->registry[$key]; + user_error(__CLASS__ . '::' . __METHOD__ . '()', E_USER_DEPRECATED); + $instance = Grav::instance(); + return $instance[strtolower($key)]; } } diff --git a/system/src/Grav/Common/Service/StreamsServiceProvider.php b/system/src/Grav/Common/Service/StreamsServiceProvider.php new file mode 100644 index 000000000..f29ba2a30 --- /dev/null +++ b/system/src/Grav/Common/Service/StreamsServiceProvider.php @@ -0,0 +1,61 @@ +init($c, $locator); + + return $locator; + }; + } + + protected function init(Container $container, ResourceLocator $locator) + { + $schemes = $container['config']->get('streams.schemes'); + + if (!$schemes) { + return; + } + + // Set locator to both streams. + Stream::setLocator($locator); + ReadOnlyStream::setLocator($locator); + + $registered = stream_get_wrappers(); + + foreach ($schemes as $scheme => $config) { + if (isset($config['paths'])) { + $locator->addPath($scheme, '', $config['paths']); + } + if (isset($config['prefixes'])) { + foreach ($config['prefixes'] as $prefix => $paths) { + $locator->addPath($scheme, $prefix, $paths); + } + } + + if (in_array($scheme, $registered)) { + stream_wrapper_unregister($scheme); + } + $type = !empty($config['type']) ? $config['type'] : 'ReadOnlyStream'; + if ($type[0] != '\\') { + $type = '\\Grav\\Component\\Filesystem\\StreamWrapper\\' . $type; + } + + if (!stream_wrapper_register($scheme, $type)) { + throw new \InvalidArgumentException("Stream '{$type}' could not be initialized."); + } + } + } +} diff --git a/system/src/Grav/Common/Session/Session.php b/system/src/Grav/Common/Session/Session.php index 0fe34d7f9..2f6596cbb 100644 --- a/system/src/Grav/Common/Session/Session.php +++ b/system/src/Grav/Common/Session/Session.php @@ -79,7 +79,7 @@ public function start() $this->started = true; return $this; - } + } /** * Get session ID diff --git a/system/src/Grav/Common/Taxonomy.php b/system/src/Grav/Common/Taxonomy.php index 3143e2d4e..dadf42b7f 100644 --- a/system/src/Grav/Common/Taxonomy.php +++ b/system/src/Grav/Common/Taxonomy.php @@ -26,13 +26,15 @@ class Taxonomy { protected $taxonomy_map; + protected $grav; /** * Constructor that resets the map */ - public function __construct() + public function __construct(Grav $grav) { $this->taxonomy_map = array(); + $this->grav = $grav; } /** @@ -48,7 +50,8 @@ public function addTaxonomy(Page\Page $page, $page_taxonomy = null) $page_taxonomy = $page->taxonomy(); } - $config = Registry::get('Config'); + /** @var Config $config */ + $config = $this->grav['config']; if ($config->get('site.taxonomies') && count($page_taxonomy) > 0) { foreach ((array) $config->get('site.taxonomies') as $taxonomy) { if (isset($page_taxonomy[$taxonomy])) { diff --git a/system/src/Grav/Common/Theme.php b/system/src/Grav/Common/Theme.php index 31e5cf477..407dee282 100644 --- a/system/src/Grav/Common/Theme.php +++ b/system/src/Grav/Common/Theme.php @@ -1,6 +1,64 @@ name = $name; + + parent::__construct($grav, $config); + } + + public function configure() { + $themeConfig = Yaml::instance(THEMES_DIR . "{$this->name}/{$this->name}.yaml")->content(); + + $this->config->merge(['themes' => [$this->name => $themeConfig]]); + + /** @var ResourceLocator $locator */ + $locator = $this->grav['locator']; + + // TODO: move + $registered = stream_get_wrappers(); + $schemes = $this->config->get( + "themes.{$this->name}.streams.scheme", + ['theme' => ['paths' => ["user/themes/{$this->name}"]]] + ); + + foreach ($schemes as $scheme => $config) { + if (isset($config['paths'])) { + $locator->addPath($scheme, '', $config['paths']); + } + if (isset($config['prefixes'])) { + foreach ($config['prefixes'] as $prefix => $paths) { + $locator->addPath($scheme, $prefix, $paths); + } + } + + if (in_array($scheme, $registered)) { + stream_wrapper_unregister($scheme); + } + $type = !empty($config['type']) ? $config['type'] : 'ReadOnlyStream'; + if ($type[0] != '\\') { + $type = '\\Grav\\Component\\Filesystem\\StreamWrapper\\' . $type; + } + + if (!stream_wrapper_register($scheme, $type)) { + throw new \InvalidArgumentException("Stream '{$type}' could not be initialized."); + } + + } + } } diff --git a/system/src/Grav/Common/Themes.php b/system/src/Grav/Common/Themes.php index f21e8faac..852a34df7 100644 --- a/system/src/Grav/Common/Themes.php +++ b/system/src/Grav/Common/Themes.php @@ -11,12 +11,22 @@ */ class Themes { + /** + * @var Grav + */ + protected $grav; + + public function __construct(Grav $grav) + { + $this->grav = $grav; + } + /** * Return list of all theme data with their blueprints. * * @return array|Data\Data[] */ - static public function all() + public function all() { $list = array(); $iterator = new \DirectoryIterator(THEMES_DIR); @@ -43,7 +53,7 @@ static public function all() * @return Data\Data * @throws \RuntimeException */ - static public function get($type) + public function get($type) { if (!$type) { throw new \RuntimeException('Theme name not provided.'); @@ -76,8 +86,10 @@ static public function get($type) public function load($name = null) { + /** @var Config $config */ + $config = $this->grav['config']; + if (!$name) { - $config = Registry::get('Config'); $name = $config->get('system.pages.theme'); } @@ -89,13 +101,13 @@ public function load($name = null) $className = '\\Grav\\Theme\\' . ucfirst($name); if (class_exists($className)) { - $class = new $className; + $class = new $className($this->grav, $config, $name); } } } if (empty($class)) { - $class = new Theme; + $class = new Theme($this->grav, $config, $name); } return $class; diff --git a/system/src/Grav/Common/Twig.php b/system/src/Grav/Common/Twig.php index d694ca160..749634748 100644 --- a/system/src/Grav/Common/Twig.php +++ b/system/src/Grav/Common/Twig.php @@ -16,38 +16,28 @@ class Twig /** * @var \Twig_Environment */ - protected $twig; + public $twig; /** - * @var Grav - */ - protected $grav; - - /** - * @var Config - */ - protected $config; - - /** - * @var Uri - */ - protected $uri; - - /** - * @var Taxonomy + * @var array */ - protected $taxonomy; + public $twig_vars; /** * @var array */ - public $twig_vars; + public $twig_paths; /** * @var string */ public $template; + /** + * @var Grav + */ + protected $grav; + /** * @var \Twig_Loader_Filesystem */ @@ -58,6 +48,15 @@ class Twig */ protected $loaderArray; + + /** + * Constructor + */ + public function __construct(Grav $grav) + { + $this->grav = $grav; + } + /** * Twig initialization that sets the twig loader chain, then the environment, then extensions * and also the base set of twig vars @@ -65,58 +64,54 @@ class Twig public function init() { if (!isset($this->twig)) { + /** @var Config $config */ + $config = $this->grav['config']; - // get Grav and Config - $this->grav = Registry::get('Grav'); - $this->config = $this->grav->config; - $this->uri = Registry::get('Uri'); - $this->taxonomy = Registry::get('Taxonomy'); - - - $this->twig_paths = array(THEMES_DIR . $this->config->get('system.pages.theme') . '/templates'); - $this->grav->fireEvent('onAfterTwigTemplatesPaths'); + $this->twig_paths = array(THEMES_DIR . $config->get('system.pages.theme') . '/templates'); + $this->grav->fireEvent('onTwigTemplatePaths'); $this->loader = new \Twig_Loader_Filesystem($this->twig_paths); $this->loaderArray = new \Twig_Loader_Array(array()); $loader_chain = new \Twig_Loader_Chain(array($this->loaderArray, $this->loader)); - $params = $this->config->get('system.twig'); + $params = $config->get('system.twig'); if (!empty($params['cache'])) { $params['cache'] = CACHE_DIR; } $this->twig = new \Twig_Environment($loader_chain, $params); - $this->grav->fireEvent('onAfterTwigInit'); + $this->grav->fireEvent('onTwigInitialized'); // set default date format if set in config - if ($this->config->get('system.pages.dateformat.long')) { - $this->twig->getExtension('core')->setDateFormat($this->config->get('system.pages.dateformat.long')); + if ($config->get('system.pages.dateformat.long')) { + $this->twig->getExtension('core')->setDateFormat($config->get('system.pages.dateformat.long')); } // enable the debug extension if required - if ($this->config->get('system.twig.debug')) { + if ($config->get('system.twig.debug')) { $this->twig->addExtension(new \Twig_Extension_Debug()); } $this->twig->addExtension(new TwigExtension()); - $this->grav->fireEvent('onAfterTwigExtensions'); + $this->grav->fireEvent('onTwigExtensions'); - $baseUrlAbsolute = $this->config->get('system.base_url_absolute'); - $baseUrlRelative = $this->config->get('system.base_url_relative'); - $theme = $this->config->get('system.pages.theme'); + $baseUrlAbsolute = $config->get('system.base_url_absolute'); + $baseUrlRelative = $config->get('system.base_url_relative'); + $theme = $config->get('system.pages.theme'); $themeUrl = $baseUrlRelative .'/'. USER_PATH . basename(THEMES_DIR) .'/'. $theme; // Set some standard variables for twig $this->twig_vars = array( - 'config' => $this->config, - 'uri' => $this->uri, + 'grav_version' => GRAV_VERSION, + 'config' => $config, + 'uri' => $this->grav['uri'], 'base_dir' => rtrim(ROOT_DIR, '/'), 'base_url_absolute' => $baseUrlAbsolute, 'base_url_relative' => $baseUrlRelative, 'theme_dir' => THEMES_DIR . $theme, 'theme_url' => $themeUrl, - 'site' => $this->config->get('site'), - 'stylesheets' => array(), - 'scripts' => array(), - 'taxonomy' => $this->taxonomy, + 'site' => $config->get('site'), + 'assets' => $this->grav['assets'], + 'taxonomy' => $this->grav['taxonomy'], + 'browser' => $this->grav['browser'], ); } @@ -161,11 +156,10 @@ public function setTemplate($name, $template) */ public function processPage(Page $item, $content = null) { - $this->init(); $content = $content !== null ? $content : $item->content(); // override the twig header vars for local resolution - $this->grav->fireEvent('onAfterTwigPageVars'); + $this->grav->fireEvent('onTwigPageVariables'); $twig_vars = $this->twig_vars; $twig_vars['page'] = $item; @@ -194,10 +188,8 @@ public function processPage(Page $item, $content = null) */ public function processString($string, array $vars = array()) { - $this->init(); - // override the twig header vars for local resolution - $this->grav->fireEvent('onAfterTwigVars'); + $this->grav->fireEvent('onTwigStringVariables'); $vars += $this->twig_vars; $name = '@Var:' . $string; @@ -213,17 +205,15 @@ public function processString($string, array $vars = array()) * * @param string $format Output format (defaults to HTML). * @return string the rendered output - * @throws \Twig_Error_Loader + * @throws \RuntimeException */ public function processSite($format = null) { - $this->init(); - // set the page now its been processed - $this->grav->fireEvent('onAfterTwigSiteVars'); + $this->grav->fireEvent('onTwigSiteVariables'); $twig_vars = $this->twig_vars; - $pages = $this->grav->pages; - $page = $this->grav->page; + $pages = $this->grav['pages']; + $page = $this->grav['page']; $twig_vars['pages'] = $pages->root(); $twig_vars['page'] = $page; @@ -233,7 +223,12 @@ public function processSite($format = null) // Get Twig template layout $template = $this->template($page->template() . $ext); - $output = $this->twig->render($template, $twig_vars); + + try { + $output = $this->twig->render($template, $twig_vars); + } catch (\Twig_Error_Loader $e) { + throw new \RuntimeException('Resource not found.', 404, $e); + } return $output; } diff --git a/system/src/Grav/Common/TwigExtension.php b/system/src/Grav/Common/TwigExtension.php index 6ef8779b6..4c5a45d9c 100644 --- a/system/src/Grav/Common/TwigExtension.php +++ b/system/src/Grav/Common/TwigExtension.php @@ -1,6 +1,12 @@ base = $base; - $this->root = $base . rtrim(substr($_SERVER['PHP_SELF'], 0, strpos($_SERVER['PHP_SELF'], 'index.php')), '/'); + $this->root = $base . $root_path; $this->url = $base . $uri; $this->init(); @@ -57,7 +71,7 @@ public function init() if (strpos($bit, ':') !== false) { $param = explode(':', $bit); if (count($param) == 2) { - $this->params[$param[0]] = str_replace('%7C', '/', $param[1]); + $this->params[$param[0]] = str_replace('%7C', '/', filter_var($param[1], FILTER_SANITIZE_STRING)); } } else { $path[] = $bit; @@ -128,7 +142,7 @@ public function route($absolute = false, $domain = false) public function query($id = null) { if (isset($id)) { - return $this->query[$id]; + return filter_var($this->query[$id], FILTER_SANITIZE_STRING) ; } else { return http_build_query($this->query); } diff --git a/system/src/Grav/Common/User/Authentication.php b/system/src/Grav/Common/User/Authentication.php index 3a7af6d8f..265cac8fc 100644 --- a/system/src/Grav/Common/User/Authentication.php +++ b/system/src/Grav/Common/User/Authentication.php @@ -15,7 +15,7 @@ abstract class Authentication * @param string $password Plaintext password. * @return string|bool */ - static public function create($password) + public static function create($password) { return password_hash($password, PASSWORD_DEFAULT); } @@ -27,7 +27,7 @@ static public function create($password) * @param string $hash Hash to verify against. * @return int Returns 0 if the check fails, 1 if password matches, 2 if hash needs to be updated. */ - static public function verify($password, $hash) + public static function verify($password, $hash) { // Always accept plaintext passwords (needs an update). // FIXME: not safe to do this... diff --git a/system/src/Grav/Common/Utils.php b/system/src/Grav/Common/Utils.php index 09e564ebd..f6e9ba823 100644 --- a/system/src/Grav/Common/Utils.php +++ b/system/src/Grav/Common/Utils.php @@ -51,6 +51,7 @@ public static function mergeObjects($obj1, $obj2) * @return string */ public static function truncateHtml($text, $length = 100, $ending = '...', $exact = false, $considerHtml = true) { + $open_tags = array(); if ($considerHtml) { // if the plain text is shorter than the maximum length, return the whole text if (strlen(preg_replace('/<.*?>/', '', $text)) <= $length) { @@ -59,7 +60,6 @@ public static function truncateHtml($text, $length = 100, $ending = '...', $exac // splits all html-tags to scanable lines preg_match_all('/(<.+?>)?([^<>]*)/s', $text, $lines, PREG_SET_ORDER); $total_length = strlen($ending); - $open_tags = array(); $truncate = ''; foreach ($lines as $line_matchings) { // if there is any html-tag in this line, handle it and add it (uncounted) to the output @@ -72,7 +72,7 @@ public static function truncateHtml($text, $length = 100, $ending = '...', $exac // delete tag from $open_tags list $pos = array_search($tag_matchings[1], $open_tags); if ($pos !== false) { - unset($open_tags[$pos]); + unset($open_tags[$pos]); } // if tag is an opening tag } else if (preg_match('/^<\s*([^\s>!]+).*?>$/s', $line_matchings[1], $tag_matchings)) { @@ -102,14 +102,14 @@ public static function truncateHtml($text, $length = 100, $ending = '...', $exac } } $truncate .= substr($line_matchings[2], 0, $left+$entities_length); - // maximum lenght is reached, so get off the loop + // maximum length is reached, so get off the loop break; } else { $truncate .= $line_matchings[2]; $total_length += $content_length; } // if the maximum length is reached, get off the loop - if($total_length>= $length) { + if ($total_length >= $length) { break; } } @@ -131,7 +131,7 @@ public static function truncateHtml($text, $length = 100, $ending = '...', $exac } // add the defined ending to the text $truncate .= $ending; - if($considerHtml) { + if ($considerHtml) { // close all unclosed html-tags foreach ($open_tags as $tag) { $truncate .= ''; diff --git a/system/src/Grav/Component/ArrayTraits/ArrayAccess.php b/system/src/Grav/Component/ArrayTraits/ArrayAccess.php new file mode 100644 index 000000000..aacd1c01f --- /dev/null +++ b/system/src/Grav/Component/ArrayTraits/ArrayAccess.php @@ -0,0 +1,46 @@ +items[$offset]); + } + + /** + * @param mixed $offset + * @return mixed + */ + public function offsetGet($offset) + { + return isset($this->items[$offset]) ? $this->items[$offset] : null; + } + + /** + * @param mixed $offset + * @param mixed $value + */ + public function offsetSet($offset, $value) + { + $this->items[$offset] = $value; + } + + /** + * @param mixed $offset + */ + public function offsetUnset($offset) + { + unset($this->items[$offset]); + } +} diff --git a/system/src/Grav/Component/ArrayTraits/Constructor.php b/system/src/Grav/Component/ArrayTraits/Constructor.php new file mode 100644 index 000000000..80779c2fc --- /dev/null +++ b/system/src/Grav/Component/ArrayTraits/Constructor.php @@ -0,0 +1,21 @@ +items = $items; + } +} diff --git a/system/src/Grav/Component/ArrayTraits/Countable.php b/system/src/Grav/Component/ArrayTraits/Countable.php new file mode 100644 index 000000000..10e51410c --- /dev/null +++ b/system/src/Grav/Component/ArrayTraits/Countable.php @@ -0,0 +1,19 @@ +items); + } +} diff --git a/system/src/Grav/Component/ArrayTraits/Getters.php b/system/src/Grav/Component/ArrayTraits/Getters.php new file mode 100644 index 000000000..90da77a24 --- /dev/null +++ b/system/src/Grav/Component/ArrayTraits/Getters.php @@ -0,0 +1,50 @@ +offsetSet($offset, $value); + } + + /** + * Magic getter method + * + * @param mixed $offset Asset name value + * @return mixed Asset value + */ + public function __get($offset) + { + return $this->offsetGet($offset); + } + + /** + * Magic method to determine if the attribute is set + * + * @param mixed $offset Asset name value + * @return boolean True if the value is set + */ + public function __isset($offset) + { + return $this->offsetExists($offset); + } + + /** + * Magic method to unset the attribute + * + * @param mixed $offset The name value to unset + */ + public function __unset($offset) + { + $this->offsetUnset($offset); + } +} diff --git a/system/src/Grav/Component/DI/Container.php b/system/src/Grav/Component/DI/Container.php new file mode 100644 index 000000000..59fafe882 --- /dev/null +++ b/system/src/Grav/Component/DI/Container.php @@ -0,0 +1,6 @@ +schemes[$scheme][$prefix])) { + $list = array_merge($list, $this->schemes[$scheme][$prefix]); + } + + $this->schemes[$scheme][$prefix] = $list; + + // Sort in reverse order to get longer prefixes to be matched first. + krsort($this->schemes[$scheme]); + } + + /** + * @param $uri + * @return string|bool + */ + public function __invoke($uri) + { + return $this->find($uri, false, true); + } + + /** + * @param string $uri + * @param bool $absolute + * @return string|bool + */ + public function findResource($uri, $absolute = true) + { + return $this->find($uri, false, $absolute); + } + + /** + * @param string $uri + * @param bool $absolute + * @return array + */ + public function findResources($uri, $absolute = true) + { + return $this->find($uri, true, $absolute); + } + + /** + * @param string $uri + * @param bool $absolute + * @param bool $array + * + * @throws \InvalidArgumentException + * @return array|string|bool + */ + protected function find($uri, $array, $absolute) + { + $segments = explode('://', $uri, 2); + $file = array_pop($segments); + $scheme = array_pop($segments); + + if (!$scheme) { + $scheme = 'file'; + } + + if (!$file || $uri[0] == ':') { + throw new \InvalidArgumentException('Invalid resource URI'); + } + if (!isset($this->schemes[$scheme])) { + throw new \InvalidArgumentException("Invalid resource {$scheme}://"); + } + + $paths = $array ? [] : false; + foreach ($this->schemes[$scheme] as $prefix => $paths) { + if ($prefix && strpos($file, $prefix) !== 0) { + continue; + } + + foreach ($paths as $path) { + $filename = $path . '/' . ltrim(substr($file, strlen($prefix)), '\/'); + $lookup = ROOT_DIR . '/' . $filename; + + if (file_exists($lookup)) { + if (!$array) { + return $absolute ? $lookup : $filename; + } + $paths[] = $absolute ? $lookup : $filename; + } + } + } + + return $paths; + } +} diff --git a/system/src/Grav/Component/Filesystem/StreamWrapper/ReadOnlyStream.php b/system/src/Grav/Component/Filesystem/StreamWrapper/ReadOnlyStream.php new file mode 100644 index 000000000..dc1e88c65 --- /dev/null +++ b/system/src/Grav/Component/Filesystem/StreamWrapper/ReadOnlyStream.php @@ -0,0 +1,71 @@ +getPath($uri); + + if (!$path) { + return false; + } + + $this->handle = ($options & STREAM_REPORT_ERRORS) ? fopen($path, $mode) : @fopen($path, $mode); + + return (bool) $this->handle; + } + + public function stream_lock($operation) + { + // Disallow exclusive lock or non-blocking lock requests + if (!in_array($operation, [LOCK_SH, LOCK_UN, LOCK_SH | LOCK_NB])) { + trigger_error( + 'stream_lock() exclusive lock operations not supported for read-only stream wrappers', + E_USER_WARNING + ); + return false; + } + + return flock($this->handle, $operation); + } + + public function stream_write($data) + { + throw new \BadMethodCallException('stream_write() not supported for read-only stream wrappers'); + } + + public function unlink($uri) + { + throw new \BadMethodCallException('unlink() not supported for read-only stream wrappers'); + } + + public function rename($from_uri, $to_uri) + { + throw new \BadMethodCallException('rename() not supported for read-only stream wrappers'); + } + + public function mkdir($uri, $mode, $options) + { + throw new \BadMethodCallException('mkdir() not supported for read-only stream wrappers'); + } + + public function rmdir($uri, $options) + { + throw new \BadMethodCallException('rmdir() not supported for read-only stream wrappers'); + } +} diff --git a/system/src/Grav/Component/Filesystem/StreamWrapper/Stream.php b/system/src/Grav/Component/Filesystem/StreamWrapper/Stream.php new file mode 100644 index 000000000..a1307bf4f --- /dev/null +++ b/system/src/Grav/Component/Filesystem/StreamWrapper/Stream.php @@ -0,0 +1,232 @@ +getPath($uri, $mode); + + if (!$path) { + return false; + } + + $this->handle = ($options & STREAM_REPORT_ERRORS) ? fopen($path, $mode) : @fopen($path, $mode); + + return (bool) $this->handle; + } + + public function stream_close() + { + return fclose($this->handle); + } + + public function stream_lock($operation) + { + if (in_array($operation, [LOCK_SH, LOCK_EX, LOCK_UN, LOCK_NB])) { + return flock($this->handle, $operation); + } + + return false; + } + + public function stream_metadata($uri, $option, $value) + { + switch ($option) { + case STREAM_META_TOUCH: + list ($time, $atime) = $value; + return touch($uri, $time, $atime); + + case STREAM_META_OWNER_NAME: + case STREAM_META_OWNER: + return chown($uri, $value); + + case STREAM_META_GROUP_NAME: + case STREAM_META_GROUP: + return chgrp($uri, $value); + + case STREAM_META_ACCESS: + return chmod($uri, $value); + } + + return false; + } + + public function stream_read($count) + { + return fread($this->handle, $count); + } + + public function stream_write($data) + { + return fwrite($this->handle, $data); + } + + public function stream_eof() + { + return feof($this->handle); + } + + public function stream_seek($offset, $whence) + { + // fseek returns 0 on success and -1 on a failure. + return !fseek($this->handle, $offset, $whence); + } + + public function stream_flush() + { + return fflush($this->handle); + } + + public function stream_tell() + { + return ftell($this->handle); + } + + public function stream_stat() + { + return fstat($this->handle); + } + + public function unlink($uri) + { + $path = $this->getPath($uri); + + if (!$path) { + return false; + } + + return unlink($path); + } + + public function rename($fromUri, $toUri) + { + $fromPath = $this->getPath($fromUri); + $toPath = $this->getPath($toUri); + + if (!($fromPath && $toPath)) { + return false; + } + + return rename($fromPath, $toPath); + } + + public function mkdir($uri, $mode, $options) + { + $recursive = (bool) ($options & STREAM_MKDIR_RECURSIVE); + $path = $this->getPath($uri, $recursive ? $mode : null); + + if (!$path) { + return false; + } + + return ($options & STREAM_REPORT_ERRORS) ? mkdir($path, $mode, $recursive) : @mkdir($path, $mode, $recursive); + } + + public function rmdir($uri, $options) + { + $path = $this->getPath($uri); + + if (!$path) { + return false; + } + + return ($options & STREAM_REPORT_ERRORS) ? rmdir($path) : @rmdir($path); + } + + public function url_stat($uri, $flags) + { + $path = $this->getPath($uri); + + if (!$path) { + return false; + } + + // Suppress warnings if requested or if the file or directory does not + // exist. This is consistent with PHP's plain filesystem stream wrapper. + return ($flags & STREAM_URL_STAT_QUIET || !file_exists($path)) ? @stat($path) : stat($path); + } + + public function dir_opendir($uri, $options) + { + $path = $this->getPath($uri); + + if (!$path) { + return false; + } + + $this->handle = opendir($path); + + return (bool) $this->handle; + } + + public function dir_readdir() + { + return readdir($this->handle); + } + + public function dir_rewinddir() + { + rewinddir($this->handle); + + return true; + } + + public function dir_closedir() + { + closedir($this->handle); + + return true; + } + + protected function getPath($uri, $mode = null) + { + $path = $this->findPath($uri); + + if ($mode == null || !$path || file_exists($path)) { + return $path; + } + + if ($mode[0] == 'r') { + return false; + } + + // We are either opening a file or creating directory. + list($scheme, $target) = explode('://', $uri, 2); + + $path = $this->findPath($scheme . '://' . dirname($target)); + + if (!$path) { + return false; + } + + return $path . '/' . basename($uri); + } + + protected function findPath($uri) + { + return static::$locator ? static::$locator->findResource($uri) : false; + } +} diff --git a/system/src/Grav/Component/Filesystem/StreamWrapper/StreamInterface.php b/system/src/Grav/Component/Filesystem/StreamWrapper/StreamInterface.php new file mode 100644 index 000000000..01b9bff29 --- /dev/null +++ b/system/src/Grav/Component/Filesystem/StreamWrapper/StreamInterface.php @@ -0,0 +1,251 @@ +createDirectories($output); @@ -156,8 +151,13 @@ private function symlink($output) $to = $this->destination . $target; $output->writeln(' ' . $source . ' -> ' . $to); - @unlink ($to); - symlink ($from, $to); + + if (is_dir($to)) { + $this->rmdir($to); + } else { + @unlink($to); + } + symlink($from, $to); } } @@ -277,4 +277,21 @@ private function rcopy($src, $dest){ } } } + + private function rmdir($dir) { + $files = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS), + \RecursiveIteratorIterator::CHILD_FIRST + ); + + foreach ($files as $fileinfo) { + if ($fileinfo->isDir()) { + if (false === rmdir($fileinfo->getRealPath())) return false; + } else { + if (false === unlink($fileinfo->getRealPath())) return false; + } + } + + return rmdir($dir); + } } diff --git a/user/config/system.yaml b/user/config/system.yaml index e05c34ba0..1bf0c1516 100644 --- a/user/config/system.yaml +++ b/user/config/system.yaml @@ -3,12 +3,10 @@ home: pages: theme: antimatter + markdown_extra: false process: markdown: true twig: false - events: - page: false - twig: true cache: enabled: true @@ -23,8 +21,16 @@ twig: auto_reload: true autoescape: false +assets: + css_pipeline: false + css_minify: true + css_rewrite: true + js_pipeline: false + js_minify: true + debugger: enabled: true + strict: false max_depth: 10 log: enabled: false diff --git a/vendor/autoload.php b/vendor/autoload.php index ee668a088..9922f6973 100644 --- a/vendor/autoload.php +++ b/vendor/autoload.php @@ -4,4 +4,4 @@ require_once __DIR__ . '/composer' . '/autoload_real.php'; -return ComposerAutoloaderInit59d31b9205a8126f2a856995d42bb492::getLoader(); +return ComposerAutoloaderInitc72d8fa047c31604816395255e8b35b9::getLoader(); diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php index 6c5a47494..42d97136e 100644 --- a/vendor/composer/autoload_classmap.php +++ b/vendor/composer/autoload_classmap.php @@ -6,6 +6,236 @@ $baseDir = dirname($vendorDir); return array( + 'CSSmin' => $vendorDir . '/mrclay/minify/min/lib/CSSmin.php', + 'Doctrine\\Common\\Cache\\ApcCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/ApcCache.php', + 'Doctrine\\Common\\Cache\\ArrayCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/ArrayCache.php', + 'Doctrine\\Common\\Cache\\Cache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/Cache.php', + 'Doctrine\\Common\\Cache\\CacheProvider' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/CacheProvider.php', + 'Doctrine\\Common\\Cache\\CouchbaseCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/CouchbaseCache.php', + 'Doctrine\\Common\\Cache\\FileCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/FileCache.php', + 'Doctrine\\Common\\Cache\\FilesystemCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/FilesystemCache.php', + 'Doctrine\\Common\\Cache\\MemcacheCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/MemcacheCache.php', + 'Doctrine\\Common\\Cache\\MemcachedCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/MemcachedCache.php', + 'Doctrine\\Common\\Cache\\MongoDBCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/MongoDBCache.php', + 'Doctrine\\Common\\Cache\\PhpFileCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/PhpFileCache.php', + 'Doctrine\\Common\\Cache\\RedisCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/RedisCache.php', + 'Doctrine\\Common\\Cache\\RiakCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/RiakCache.php', + 'Doctrine\\Common\\Cache\\Version' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/Version.php', + 'Doctrine\\Common\\Cache\\WinCacheCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/WinCacheCache.php', + 'Doctrine\\Common\\Cache\\XcacheCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/XcacheCache.php', + 'Doctrine\\Common\\Cache\\ZendDataCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/ZendDataCache.php', + 'DooDigestAuth' => $vendorDir . '/mrclay/minify/min/lib/DooDigestAuth.php', + 'FirePHP' => $vendorDir . '/mrclay/minify/min/lib/FirePHP.php', + 'Grav\\Common\\Assets' => $baseDir . '/system/src/Grav/Common/Assets.php', + 'Grav\\Common\\Browser' => $baseDir . '/system/src/Grav/Browser.php', + 'Grav\\Common\\Cache' => $baseDir . '/system/src/Grav/Common/Cache.php', + 'Grav\\Common\\Config' => $baseDir . '/system/src/Grav/Common/Config.php', + 'Grav\\Common\\Data\\Blueprint' => $baseDir . '/system/src/Grav/Common/Data/Blueprint.php', + 'Grav\\Common\\Data\\Blueprints' => $baseDir . '/system/src/Grav/Common/Data/Blueprints.php', + 'Grav\\Common\\Data\\Data' => $baseDir . '/system/src/Grav/Common/Data/Data.php', + 'Grav\\Common\\Data\\DataInterface' => $baseDir . '/system/src/Grav/Common/Data/DataInterface.php', + 'Grav\\Common\\Data\\Validation' => $baseDir . '/system/src/Grav/Common/Data/Validation.php', + 'Grav\\Common\\Debugger' => $baseDir . '/system/src/Grav/Common/Debugger.php', + 'Grav\\Common\\Filesystem\\FileInterface' => $baseDir . '/system/src/Grav/Common/Filesystem/FileInterface.php', + 'Grav\\Common\\Filesystem\\File\\Config' => $baseDir . '/system/src/Grav/Common/Filesystem/File/Config.php', + 'Grav\\Common\\Filesystem\\File\\General' => $baseDir . '/system/src/Grav/Common/Filesystem/File/General.php', + 'Grav\\Common\\Filesystem\\File\\Json' => $baseDir . '/system/src/Grav/Common/Filesystem/File/Json.php', + 'Grav\\Common\\Filesystem\\File\\Log' => $baseDir . '/system/src/Grav/Common/Filesystem/File/Log.php', + 'Grav\\Common\\Filesystem\\File\\Markdown' => $baseDir . '/system/src/Grav/Common/Filesystem/File/Markdown.php', + 'Grav\\Common\\Filesystem\\File\\Yaml' => $baseDir . '/system/src/Grav/Common/Filesystem/File/Yaml.php', + 'Grav\\Common\\Filesystem\\Folder' => $baseDir . '/system/src/Grav/Common/Filesystem/Folder.php', + 'Grav\\Common\\Getters' => $baseDir . '/system/src/Grav/Common/Getters.php', + 'Grav\\Common\\Grav' => $baseDir . '/system/src/Grav/Common/Grav.php', + 'Grav\\Common\\GravTrait' => $baseDir . '/system/src/Grav/Common/GravTrait.php', + 'Grav\\Common\\Inflector' => $baseDir . '/system/src/Grav/Common/Inflector.php', + 'Grav\\Common\\Iterator' => $baseDir . '/system/src/Grav/Common/Iterator.php', + 'Grav\\Common\\Markdown\\Markdown' => $baseDir . '/system/src/Grav/Common/Markdown/Markdown.php', + 'Grav\\Common\\Markdown\\MarkdownExtra' => $baseDir . '/system/src/Grav/Common/Markdown/MarkdownExtra.php', + 'Grav\\Common\\Markdown\\MarkdownGravLinkTrait' => $baseDir . '/system/src/Grav/Common/Markdown/MarkdownGravLinkTrait.php', + 'Grav\\Common\\Page\\Collection' => $baseDir . '/system/src/Grav/Common/Page/Collection.php', + 'Grav\\Common\\Page\\Media' => $baseDir . '/system/src/Grav/Common/Page/Media.php', + 'Grav\\Common\\Page\\Medium' => $baseDir . '/system/src/Grav/Common/Page/Medium.php', + 'Grav\\Common\\Page\\Page' => $baseDir . '/system/src/Grav/Common/Page/Page.php', + 'Grav\\Common\\Page\\Pages' => $baseDir . '/system/src/Grav/Common/Page/Pages.php', + 'Grav\\Common\\Plugin' => $baseDir . '/system/src/Grav/Common/Plugin.php', + 'Grav\\Common\\Plugins' => $baseDir . '/system/src/Grav/Common/Plugins.php', + 'Grav\\Common\\Registry' => $baseDir . '/system/src/Grav/Common/Registry.php', + 'Grav\\Common\\Service\\StreamsServiceProvider' => $baseDir . '/system/src/Grav/Common/Service/StreamsServiceProvider.php', + 'Grav\\Common\\Session\\Message' => $baseDir . '/system/src/Grav/Common/Session/Message.php', + 'Grav\\Common\\Session\\Session' => $baseDir . '/system/src/Grav/Common/Session/Session.php', + 'Grav\\Common\\Taxonomy' => $baseDir . '/system/src/Grav/Common/Taxonomy.php', + 'Grav\\Common\\Theme' => $baseDir . '/system/src/Grav/Common/Theme.php', + 'Grav\\Common\\Themes' => $baseDir . '/system/src/Grav/Common/Themes.php', + 'Grav\\Common\\Twig' => $baseDir . '/system/src/Grav/Common/Twig.php', + 'Grav\\Common\\TwigExtension' => $baseDir . '/system/src/Grav/Common/TwigExtension.php', + 'Grav\\Common\\Uri' => $baseDir . '/system/src/Grav/Common/Uri.php', + 'Grav\\Common\\User\\Authentication' => $baseDir . '/system/src/Grav/Common/User/Authentication.php', + 'Grav\\Common\\User\\User' => $baseDir . '/system/src/Grav/Common/User/User.php', + 'Grav\\Common\\Utils' => $baseDir . '/system/src/Grav/Common/Utils.php', + 'Grav\\Component\\ArrayTraits\\ArrayAccess' => $baseDir . '/system/src/Grav/Component/ArrayTraits/ArrayAccess.php', + 'Grav\\Component\\ArrayTraits\\Constructor' => $baseDir . '/system/src/Grav/Component/ArrayTraits/Constructor.php', + 'Grav\\Component\\ArrayTraits\\Countable' => $baseDir . '/system/src/Grav/Component/ArrayTraits/Countable.php', + 'Grav\\Component\\ArrayTraits\\Getters' => $baseDir . '/system/src/Grav/Component/ArrayTraits/Getters.php', + 'Grav\\Component\\DI\\Container' => $baseDir . '/system/src/Grav/Component/DI/Container.php', + 'Grav\\Component\\DI\\ServiceProviderInterface' => $baseDir . '/system/src/Grav/Component/DI/ServiceProviderInterface.php', + 'Grav\\Component\\EventDispatcher\\Event' => $baseDir . '/system/src/Grav/Component/EventDispatcher/Event.php', + 'Grav\\Component\\EventDispatcher\\EventDispatcher' => $baseDir . '/system/src/Grav/Component/EventDispatcher/EventDispatcher.php', + 'Grav\\Component\\EventDispatcher\\EventSubscriberInterface' => $baseDir . '/system/src/Grav/Component/EventDispatcher/EventSubscriberInterface.php', + 'Grav\\Component\\Filesystem\\ResourceLocator' => $baseDir . '/system/src/Grav/Component/Filesystem/ResourceLocator.php', + 'Grav\\Component\\Filesystem\\StreamWrapper\\ReadOnlyStream' => $baseDir . '/system/src/Grav/Component/Filesystem/StreamWrapper/ReadOnlyStream.php', + 'Grav\\Component\\Filesystem\\StreamWrapper\\Stream' => $baseDir . '/system/src/Grav/Component/Filesystem/StreamWrapper/Stream.php', + 'Grav\\Component\\Filesystem\\StreamWrapper\\StreamInterface' => $baseDir . '/system/src/Grav/Component/Filesystem/StreamWrapper/StreamInterface.php', + 'Grav\\Console\\BackupCommand' => $baseDir . '/system/src/Grav/Console/BackupCommand.php', + 'Grav\\Console\\CleanCommand' => $baseDir . '/system/src/Grav/Console/CleanCommand.php', + 'Grav\\Console\\ClearCacheCommand' => $baseDir . '/system/src/Grav/Console/ClearCacheCommand.php', + 'Grav\\Console\\InstallCommand' => $baseDir . '/system/src/Grav/Console/InstallCommand.php', + 'Grav\\Console\\NewProjectCommand' => $baseDir . '/system/src/Grav/Console/NewProjectCommand.php', + 'Grav\\Console\\SetupCommand' => $baseDir . '/system/src/Grav/Console/SetupCommand.php', + 'Gregwar\\Cache\\Cache' => $vendorDir . '/gregwar/cache/Gregwar/Cache/Cache.php', + 'Gregwar\\Cache\\GarbageCollect' => $vendorDir . '/gregwar/cache/Gregwar/Cache/GarbageCollect.php', + 'Gregwar\\Image\\Adapter\\Adapter' => $vendorDir . '/gregwar/image/Gregwar/Image/Adapter/Adapter.php', + 'Gregwar\\Image\\Adapter\\AdapterInterface' => $vendorDir . '/gregwar/image/Gregwar/Image/Adapter/AdapterInterface.php', + 'Gregwar\\Image\\Adapter\\Common' => $vendorDir . '/gregwar/image/Gregwar/Image/Adapter/Common.php', + 'Gregwar\\Image\\Adapter\\GD' => $vendorDir . '/gregwar/image/Gregwar/Image/Adapter/GD.php', + 'Gregwar\\Image\\Adapter\\Imagick' => $vendorDir . '/gregwar/image/Gregwar/Image/Adapter/Imagick.php', + 'Gregwar\\Image\\Exceptions\\GenerationError' => $vendorDir . '/gregwar/image/Gregwar/Image/Exceptions/GenerationError.php', + 'Gregwar\\Image\\GarbageCollect' => $vendorDir . '/gregwar/image/Gregwar/Image/GarbageCollect.php', + 'Gregwar\\Image\\Image' => $vendorDir . '/gregwar/image/Gregwar/Image/Image.php', + 'Gregwar\\Image\\ImageColor' => $vendorDir . '/gregwar/image/Gregwar/Image/ImageColor.php', + 'Gregwar\\Image\\Source\\Create' => $vendorDir . '/gregwar/image/Gregwar/Image/Source/Create.php', + 'Gregwar\\Image\\Source\\Data' => $vendorDir . '/gregwar/image/Gregwar/Image/Source/Data.php', + 'Gregwar\\Image\\Source\\File' => $vendorDir . '/gregwar/image/Gregwar/Image/Source/File.php', + 'Gregwar\\Image\\Source\\Resource' => $vendorDir . '/gregwar/image/Gregwar/Image/Source/Resource.php', + 'Gregwar\\Image\\Source\\Source' => $vendorDir . '/gregwar/image/Gregwar/Image/Source/Source.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_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', + 'Parsedown' => $vendorDir . '/erusev/parsedown/Parsedown.php', + 'ParsedownExtra' => $vendorDir . '/erusev/parsedown-extra/ParsedownExtra.php', + 'Pimple\\Container' => $vendorDir . '/pimple/pimple/src/Pimple/Container.php', + 'Pimple\\ServiceProviderInterface' => $vendorDir . '/pimple/pimple/src/Pimple/ServiceProviderInterface.php', + 'Symfony\\Component\\Console\\Application' => $vendorDir . '/symfony/console/Symfony/Component/Console/Application.php', + 'Symfony\\Component\\Console\\Command\\Command' => $vendorDir . '/symfony/console/Symfony/Component/Console/Command/Command.php', + 'Symfony\\Component\\Console\\Command\\HelpCommand' => $vendorDir . '/symfony/console/Symfony/Component/Console/Command/HelpCommand.php', + 'Symfony\\Component\\Console\\Command\\ListCommand' => $vendorDir . '/symfony/console/Symfony/Component/Console/Command/ListCommand.php', + 'Symfony\\Component\\Console\\ConsoleEvents' => $vendorDir . '/symfony/console/Symfony/Component/Console/ConsoleEvents.php', + 'Symfony\\Component\\Console\\Descriptor\\ApplicationDescription' => $vendorDir . '/symfony/console/Symfony/Component/Console/Descriptor/ApplicationDescription.php', + 'Symfony\\Component\\Console\\Descriptor\\Descriptor' => $vendorDir . '/symfony/console/Symfony/Component/Console/Descriptor/Descriptor.php', + 'Symfony\\Component\\Console\\Descriptor\\DescriptorInterface' => $vendorDir . '/symfony/console/Symfony/Component/Console/Descriptor/DescriptorInterface.php', + 'Symfony\\Component\\Console\\Descriptor\\JsonDescriptor' => $vendorDir . '/symfony/console/Symfony/Component/Console/Descriptor/JsonDescriptor.php', + 'Symfony\\Component\\Console\\Descriptor\\MarkdownDescriptor' => $vendorDir . '/symfony/console/Symfony/Component/Console/Descriptor/MarkdownDescriptor.php', + 'Symfony\\Component\\Console\\Descriptor\\TextDescriptor' => $vendorDir . '/symfony/console/Symfony/Component/Console/Descriptor/TextDescriptor.php', + 'Symfony\\Component\\Console\\Descriptor\\XmlDescriptor' => $vendorDir . '/symfony/console/Symfony/Component/Console/Descriptor/XmlDescriptor.php', + 'Symfony\\Component\\Console\\Event\\ConsoleCommandEvent' => $vendorDir . '/symfony/console/Symfony/Component/Console/Event/ConsoleCommandEvent.php', + 'Symfony\\Component\\Console\\Event\\ConsoleEvent' => $vendorDir . '/symfony/console/Symfony/Component/Console/Event/ConsoleEvent.php', + 'Symfony\\Component\\Console\\Event\\ConsoleExceptionEvent' => $vendorDir . '/symfony/console/Symfony/Component/Console/Event/ConsoleExceptionEvent.php', + 'Symfony\\Component\\Console\\Event\\ConsoleTerminateEvent' => $vendorDir . '/symfony/console/Symfony/Component/Console/Event/ConsoleTerminateEvent.php', + 'Symfony\\Component\\Console\\Formatter\\OutputFormatter' => $vendorDir . '/symfony/console/Symfony/Component/Console/Formatter/OutputFormatter.php', + 'Symfony\\Component\\Console\\Formatter\\OutputFormatterInterface' => $vendorDir . '/symfony/console/Symfony/Component/Console/Formatter/OutputFormatterInterface.php', + 'Symfony\\Component\\Console\\Formatter\\OutputFormatterStyle' => $vendorDir . '/symfony/console/Symfony/Component/Console/Formatter/OutputFormatterStyle.php', + 'Symfony\\Component\\Console\\Formatter\\OutputFormatterStyleInterface' => $vendorDir . '/symfony/console/Symfony/Component/Console/Formatter/OutputFormatterStyleInterface.php', + 'Symfony\\Component\\Console\\Formatter\\OutputFormatterStyleStack' => $vendorDir . '/symfony/console/Symfony/Component/Console/Formatter/OutputFormatterStyleStack.php', + 'Symfony\\Component\\Console\\Helper\\DescriptorHelper' => $vendorDir . '/symfony/console/Symfony/Component/Console/Helper/DescriptorHelper.php', + 'Symfony\\Component\\Console\\Helper\\DialogHelper' => $vendorDir . '/symfony/console/Symfony/Component/Console/Helper/DialogHelper.php', + 'Symfony\\Component\\Console\\Helper\\FormatterHelper' => $vendorDir . '/symfony/console/Symfony/Component/Console/Helper/FormatterHelper.php', + 'Symfony\\Component\\Console\\Helper\\Helper' => $vendorDir . '/symfony/console/Symfony/Component/Console/Helper/Helper.php', + 'Symfony\\Component\\Console\\Helper\\HelperInterface' => $vendorDir . '/symfony/console/Symfony/Component/Console/Helper/HelperInterface.php', + 'Symfony\\Component\\Console\\Helper\\HelperSet' => $vendorDir . '/symfony/console/Symfony/Component/Console/Helper/HelperSet.php', + 'Symfony\\Component\\Console\\Helper\\InputAwareHelper' => $vendorDir . '/symfony/console/Symfony/Component/Console/Helper/InputAwareHelper.php', + 'Symfony\\Component\\Console\\Helper\\ProgressBar' => $vendorDir . '/symfony/console/Symfony/Component/Console/Helper/ProgressBar.php', + 'Symfony\\Component\\Console\\Helper\\ProgressHelper' => $vendorDir . '/symfony/console/Symfony/Component/Console/Helper/ProgressHelper.php', + 'Symfony\\Component\\Console\\Helper\\QuestionHelper' => $vendorDir . '/symfony/console/Symfony/Component/Console/Helper/QuestionHelper.php', + 'Symfony\\Component\\Console\\Helper\\Table' => $vendorDir . '/symfony/console/Symfony/Component/Console/Helper/Table.php', + 'Symfony\\Component\\Console\\Helper\\TableHelper' => $vendorDir . '/symfony/console/Symfony/Component/Console/Helper/TableHelper.php', + 'Symfony\\Component\\Console\\Helper\\TableSeparator' => $vendorDir . '/symfony/console/Symfony/Component/Console/Helper/TableSeparator.php', + 'Symfony\\Component\\Console\\Helper\\TableStyle' => $vendorDir . '/symfony/console/Symfony/Component/Console/Helper/TableStyle.php', + 'Symfony\\Component\\Console\\Input\\ArgvInput' => $vendorDir . '/symfony/console/Symfony/Component/Console/Input/ArgvInput.php', + 'Symfony\\Component\\Console\\Input\\ArrayInput' => $vendorDir . '/symfony/console/Symfony/Component/Console/Input/ArrayInput.php', + 'Symfony\\Component\\Console\\Input\\Input' => $vendorDir . '/symfony/console/Symfony/Component/Console/Input/Input.php', + 'Symfony\\Component\\Console\\Input\\InputArgument' => $vendorDir . '/symfony/console/Symfony/Component/Console/Input/InputArgument.php', + 'Symfony\\Component\\Console\\Input\\InputAwareInterface' => $vendorDir . '/symfony/console/Symfony/Component/Console/Input/InputAwareInterface.php', + 'Symfony\\Component\\Console\\Input\\InputDefinition' => $vendorDir . '/symfony/console/Symfony/Component/Console/Input/InputDefinition.php', + 'Symfony\\Component\\Console\\Input\\InputInterface' => $vendorDir . '/symfony/console/Symfony/Component/Console/Input/InputInterface.php', + 'Symfony\\Component\\Console\\Input\\InputOption' => $vendorDir . '/symfony/console/Symfony/Component/Console/Input/InputOption.php', + 'Symfony\\Component\\Console\\Input\\StringInput' => $vendorDir . '/symfony/console/Symfony/Component/Console/Input/StringInput.php', + 'Symfony\\Component\\Console\\Logger\\ConsoleLogger' => $vendorDir . '/symfony/console/Symfony/Component/Console/Logger/ConsoleLogger.php', + 'Symfony\\Component\\Console\\Output\\BufferedOutput' => $vendorDir . '/symfony/console/Symfony/Component/Console/Output/BufferedOutput.php', + 'Symfony\\Component\\Console\\Output\\ConsoleOutput' => $vendorDir . '/symfony/console/Symfony/Component/Console/Output/ConsoleOutput.php', + 'Symfony\\Component\\Console\\Output\\ConsoleOutputInterface' => $vendorDir . '/symfony/console/Symfony/Component/Console/Output/ConsoleOutputInterface.php', + 'Symfony\\Component\\Console\\Output\\NullOutput' => $vendorDir . '/symfony/console/Symfony/Component/Console/Output/NullOutput.php', + 'Symfony\\Component\\Console\\Output\\Output' => $vendorDir . '/symfony/console/Symfony/Component/Console/Output/Output.php', + 'Symfony\\Component\\Console\\Output\\OutputInterface' => $vendorDir . '/symfony/console/Symfony/Component/Console/Output/OutputInterface.php', + 'Symfony\\Component\\Console\\Output\\StreamOutput' => $vendorDir . '/symfony/console/Symfony/Component/Console/Output/StreamOutput.php', + 'Symfony\\Component\\Console\\Question\\ChoiceQuestion' => $vendorDir . '/symfony/console/Symfony/Component/Console/Question/ChoiceQuestion.php', + 'Symfony\\Component\\Console\\Question\\ConfirmationQuestion' => $vendorDir . '/symfony/console/Symfony/Component/Console/Question/ConfirmationQuestion.php', + 'Symfony\\Component\\Console\\Question\\Question' => $vendorDir . '/symfony/console/Symfony/Component/Console/Question/Question.php', + 'Symfony\\Component\\Console\\Shell' => $vendorDir . '/symfony/console/Symfony/Component/Console/Shell.php', + 'Symfony\\Component\\Console\\Tester\\ApplicationTester' => $vendorDir . '/symfony/console/Symfony/Component/Console/Tester/ApplicationTester.php', + 'Symfony\\Component\\Console\\Tester\\CommandTester' => $vendorDir . '/symfony/console/Symfony/Component/Console/Tester/CommandTester.php', + 'Symfony\\Component\\EventDispatcher\\ContainerAwareEventDispatcher' => $vendorDir . '/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ContainerAwareEventDispatcher.php', + 'Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcher' => $vendorDir . '/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php', + 'Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcherInterface' => $vendorDir . '/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcherInterface.php', + 'Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener' => $vendorDir . '/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Debug/WrappedListener.php', + 'Symfony\\Component\\EventDispatcher\\DependencyInjection\\RegisterListenersPass' => $vendorDir . '/symfony/event-dispatcher/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php', + 'Symfony\\Component\\EventDispatcher\\Event' => $vendorDir . '/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Event.php', + 'Symfony\\Component\\EventDispatcher\\EventDispatcher' => $vendorDir . '/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcher.php', + 'Symfony\\Component\\EventDispatcher\\EventDispatcherInterface' => $vendorDir . '/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcherInterface.php', + 'Symfony\\Component\\EventDispatcher\\EventSubscriberInterface' => $vendorDir . '/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventSubscriberInterface.php', + 'Symfony\\Component\\EventDispatcher\\GenericEvent' => $vendorDir . '/symfony/event-dispatcher/Symfony/Component/EventDispatcher/GenericEvent.php', + 'Symfony\\Component\\EventDispatcher\\ImmutableEventDispatcher' => $vendorDir . '/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ImmutableEventDispatcher.php', + 'Symfony\\Component\\Yaml\\Dumper' => $vendorDir . '/symfony/yaml/Symfony/Component/Yaml/Dumper.php', + 'Symfony\\Component\\Yaml\\Escaper' => $vendorDir . '/symfony/yaml/Symfony/Component/Yaml/Escaper.php', + 'Symfony\\Component\\Yaml\\Exception\\DumpException' => $vendorDir . '/symfony/yaml/Symfony/Component/Yaml/Exception/DumpException.php', + 'Symfony\\Component\\Yaml\\Exception\\ExceptionInterface' => $vendorDir . '/symfony/yaml/Symfony/Component/Yaml/Exception/ExceptionInterface.php', + 'Symfony\\Component\\Yaml\\Exception\\ParseException' => $vendorDir . '/symfony/yaml/Symfony/Component/Yaml/Exception/ParseException.php', + 'Symfony\\Component\\Yaml\\Exception\\RuntimeException' => $vendorDir . '/symfony/yaml/Symfony/Component/Yaml/Exception/RuntimeException.php', + 'Symfony\\Component\\Yaml\\Inline' => $vendorDir . '/symfony/yaml/Symfony/Component/Yaml/Inline.php', + 'Symfony\\Component\\Yaml\\Parser' => $vendorDir . '/symfony/yaml/Symfony/Component/Yaml/Parser.php', + 'Symfony\\Component\\Yaml\\Unescaper' => $vendorDir . '/symfony/yaml/Symfony/Component/Yaml/Unescaper.php', + 'Symfony\\Component\\Yaml\\Yaml' => $vendorDir . '/symfony/yaml/Symfony/Component/Yaml/Yaml.php', 'Tracy\\Bar' => $vendorDir . '/tracy/tracy/src/Tracy/Bar.php', 'Tracy\\BlueScreen' => $vendorDir . '/tracy/tracy/src/Tracy/BlueScreen.php', 'Tracy\\Debugger' => $vendorDir . '/tracy/tracy/src/Tracy/Debugger.php', @@ -14,7 +244,170 @@ 'Tracy\\FireLogger' => $vendorDir . '/tracy/tracy/src/Tracy/FireLogger.php', 'Tracy\\Helpers' => $vendorDir . '/tracy/tracy/src/Tracy/Helpers.php', 'Tracy\\IBarPanel' => $vendorDir . '/tracy/tracy/src/Tracy/IBarPanel.php', - 'Tracy\\ILogger' => $vendorDir . '/tracy/tracy/src/Tracy/ILogger.php', 'Tracy\\Logger' => $vendorDir . '/tracy/tracy/src/Tracy/Logger.php', 'Tracy\\OutputDebugger' => $vendorDir . '/tracy/tracy/src/Tracy/OutputDebugger.php', + 'Twig_Autoloader' => $vendorDir . '/twig/twig/lib/Twig/Autoloader.php', + 'Twig_Compiler' => $vendorDir . '/twig/twig/lib/Twig/Compiler.php', + 'Twig_CompilerInterface' => $vendorDir . '/twig/twig/lib/Twig/CompilerInterface.php', + 'Twig_Environment' => $vendorDir . '/twig/twig/lib/Twig/Environment.php', + 'Twig_Error' => $vendorDir . '/twig/twig/lib/Twig/Error.php', + 'Twig_Error_Loader' => $vendorDir . '/twig/twig/lib/Twig/Error/Loader.php', + 'Twig_Error_Runtime' => $vendorDir . '/twig/twig/lib/Twig/Error/Runtime.php', + 'Twig_Error_Syntax' => $vendorDir . '/twig/twig/lib/Twig/Error/Syntax.php', + 'Twig_ExistsLoaderInterface' => $vendorDir . '/twig/twig/lib/Twig/ExistsLoaderInterface.php', + 'Twig_ExpressionParser' => $vendorDir . '/twig/twig/lib/Twig/ExpressionParser.php', + 'Twig_Extension' => $vendorDir . '/twig/twig/lib/Twig/Extension.php', + 'Twig_ExtensionInterface' => $vendorDir . '/twig/twig/lib/Twig/ExtensionInterface.php', + 'Twig_Extension_Core' => $vendorDir . '/twig/twig/lib/Twig/Extension/Core.php', + 'Twig_Extension_Debug' => $vendorDir . '/twig/twig/lib/Twig/Extension/Debug.php', + 'Twig_Extension_Escaper' => $vendorDir . '/twig/twig/lib/Twig/Extension/Escaper.php', + 'Twig_Extension_Optimizer' => $vendorDir . '/twig/twig/lib/Twig/Extension/Optimizer.php', + 'Twig_Extension_Sandbox' => $vendorDir . '/twig/twig/lib/Twig/Extension/Sandbox.php', + 'Twig_Extension_Staging' => $vendorDir . '/twig/twig/lib/Twig/Extension/Staging.php', + 'Twig_Extension_StringLoader' => $vendorDir . '/twig/twig/lib/Twig/Extension/StringLoader.php', + 'Twig_Filter' => $vendorDir . '/twig/twig/lib/Twig/Filter.php', + 'Twig_FilterCallableInterface' => $vendorDir . '/twig/twig/lib/Twig/FilterCallableInterface.php', + 'Twig_FilterInterface' => $vendorDir . '/twig/twig/lib/Twig/FilterInterface.php', + 'Twig_Filter_Function' => $vendorDir . '/twig/twig/lib/Twig/Filter/Function.php', + 'Twig_Filter_Method' => $vendorDir . '/twig/twig/lib/Twig/Filter/Method.php', + 'Twig_Filter_Node' => $vendorDir . '/twig/twig/lib/Twig/Filter/Node.php', + 'Twig_Function' => $vendorDir . '/twig/twig/lib/Twig/Function.php', + 'Twig_FunctionCallableInterface' => $vendorDir . '/twig/twig/lib/Twig/FunctionCallableInterface.php', + 'Twig_FunctionInterface' => $vendorDir . '/twig/twig/lib/Twig/FunctionInterface.php', + 'Twig_Function_Function' => $vendorDir . '/twig/twig/lib/Twig/Function/Function.php', + 'Twig_Function_Method' => $vendorDir . '/twig/twig/lib/Twig/Function/Method.php', + 'Twig_Function_Node' => $vendorDir . '/twig/twig/lib/Twig/Function/Node.php', + 'Twig_Lexer' => $vendorDir . '/twig/twig/lib/Twig/Lexer.php', + 'Twig_LexerInterface' => $vendorDir . '/twig/twig/lib/Twig/LexerInterface.php', + 'Twig_LoaderInterface' => $vendorDir . '/twig/twig/lib/Twig/LoaderInterface.php', + 'Twig_Loader_Array' => $vendorDir . '/twig/twig/lib/Twig/Loader/Array.php', + 'Twig_Loader_Chain' => $vendorDir . '/twig/twig/lib/Twig/Loader/Chain.php', + 'Twig_Loader_Filesystem' => $vendorDir . '/twig/twig/lib/Twig/Loader/Filesystem.php', + 'Twig_Loader_String' => $vendorDir . '/twig/twig/lib/Twig/Loader/String.php', + 'Twig_Markup' => $vendorDir . '/twig/twig/lib/Twig/Markup.php', + 'Twig_Node' => $vendorDir . '/twig/twig/lib/Twig/Node.php', + 'Twig_NodeInterface' => $vendorDir . '/twig/twig/lib/Twig/NodeInterface.php', + 'Twig_NodeOutputInterface' => $vendorDir . '/twig/twig/lib/Twig/NodeOutputInterface.php', + 'Twig_NodeTraverser' => $vendorDir . '/twig/twig/lib/Twig/NodeTraverser.php', + 'Twig_NodeVisitorInterface' => $vendorDir . '/twig/twig/lib/Twig/NodeVisitorInterface.php', + 'Twig_NodeVisitor_Escaper' => $vendorDir . '/twig/twig/lib/Twig/NodeVisitor/Escaper.php', + 'Twig_NodeVisitor_Optimizer' => $vendorDir . '/twig/twig/lib/Twig/NodeVisitor/Optimizer.php', + 'Twig_NodeVisitor_SafeAnalysis' => $vendorDir . '/twig/twig/lib/Twig/NodeVisitor/SafeAnalysis.php', + 'Twig_NodeVisitor_Sandbox' => $vendorDir . '/twig/twig/lib/Twig/NodeVisitor/Sandbox.php', + 'Twig_Node_AutoEscape' => $vendorDir . '/twig/twig/lib/Twig/Node/AutoEscape.php', + 'Twig_Node_Block' => $vendorDir . '/twig/twig/lib/Twig/Node/Block.php', + 'Twig_Node_BlockReference' => $vendorDir . '/twig/twig/lib/Twig/Node/BlockReference.php', + 'Twig_Node_Body' => $vendorDir . '/twig/twig/lib/Twig/Node/Body.php', + 'Twig_Node_Do' => $vendorDir . '/twig/twig/lib/Twig/Node/Do.php', + 'Twig_Node_Embed' => $vendorDir . '/twig/twig/lib/Twig/Node/Embed.php', + 'Twig_Node_Expression' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression.php', + 'Twig_Node_Expression_Array' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Array.php', + 'Twig_Node_Expression_AssignName' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/AssignName.php', + 'Twig_Node_Expression_Binary' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary.php', + 'Twig_Node_Expression_Binary_Add' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/Add.php', + 'Twig_Node_Expression_Binary_And' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/And.php', + 'Twig_Node_Expression_Binary_BitwiseAnd' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseAnd.php', + 'Twig_Node_Expression_Binary_BitwiseOr' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseOr.php', + 'Twig_Node_Expression_Binary_BitwiseXor' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseXor.php', + 'Twig_Node_Expression_Binary_Concat' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/Concat.php', + 'Twig_Node_Expression_Binary_Div' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/Div.php', + 'Twig_Node_Expression_Binary_EndsWith' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/EndsWith.php', + 'Twig_Node_Expression_Binary_Equal' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/Equal.php', + 'Twig_Node_Expression_Binary_FloorDiv' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/FloorDiv.php', + 'Twig_Node_Expression_Binary_Greater' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/Greater.php', + 'Twig_Node_Expression_Binary_GreaterEqual' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/GreaterEqual.php', + 'Twig_Node_Expression_Binary_In' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/In.php', + 'Twig_Node_Expression_Binary_Less' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/Less.php', + 'Twig_Node_Expression_Binary_LessEqual' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/LessEqual.php', + 'Twig_Node_Expression_Binary_Matches' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/Matches.php', + 'Twig_Node_Expression_Binary_Mod' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/Mod.php', + 'Twig_Node_Expression_Binary_Mul' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/Mul.php', + 'Twig_Node_Expression_Binary_NotEqual' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/NotEqual.php', + 'Twig_Node_Expression_Binary_NotIn' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/NotIn.php', + 'Twig_Node_Expression_Binary_Or' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/Or.php', + 'Twig_Node_Expression_Binary_Power' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/Power.php', + 'Twig_Node_Expression_Binary_Range' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/Range.php', + 'Twig_Node_Expression_Binary_StartsWith' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/StartsWith.php', + 'Twig_Node_Expression_Binary_Sub' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/Sub.php', + 'Twig_Node_Expression_BlockReference' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/BlockReference.php', + 'Twig_Node_Expression_Call' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Call.php', + 'Twig_Node_Expression_Conditional' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Conditional.php', + 'Twig_Node_Expression_Constant' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Constant.php', + 'Twig_Node_Expression_ExtensionReference' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/ExtensionReference.php', + 'Twig_Node_Expression_Filter' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Filter.php', + 'Twig_Node_Expression_Filter_Default' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Filter/Default.php', + 'Twig_Node_Expression_Function' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Function.php', + 'Twig_Node_Expression_GetAttr' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/GetAttr.php', + 'Twig_Node_Expression_MethodCall' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/MethodCall.php', + 'Twig_Node_Expression_Name' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Name.php', + 'Twig_Node_Expression_Parent' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Parent.php', + 'Twig_Node_Expression_TempName' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/TempName.php', + 'Twig_Node_Expression_Test' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Test.php', + 'Twig_Node_Expression_Test_Constant' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Test/Constant.php', + 'Twig_Node_Expression_Test_Defined' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Test/Defined.php', + 'Twig_Node_Expression_Test_Divisibleby' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Test/Divisibleby.php', + 'Twig_Node_Expression_Test_Even' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Test/Even.php', + 'Twig_Node_Expression_Test_Null' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Test/Null.php', + 'Twig_Node_Expression_Test_Odd' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Test/Odd.php', + 'Twig_Node_Expression_Test_Sameas' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Test/Sameas.php', + 'Twig_Node_Expression_Unary' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Unary.php', + 'Twig_Node_Expression_Unary_Neg' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Unary/Neg.php', + 'Twig_Node_Expression_Unary_Not' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Unary/Not.php', + 'Twig_Node_Expression_Unary_Pos' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Unary/Pos.php', + 'Twig_Node_Flush' => $vendorDir . '/twig/twig/lib/Twig/Node/Flush.php', + 'Twig_Node_For' => $vendorDir . '/twig/twig/lib/Twig/Node/For.php', + 'Twig_Node_ForLoop' => $vendorDir . '/twig/twig/lib/Twig/Node/ForLoop.php', + 'Twig_Node_If' => $vendorDir . '/twig/twig/lib/Twig/Node/If.php', + 'Twig_Node_Import' => $vendorDir . '/twig/twig/lib/Twig/Node/Import.php', + 'Twig_Node_Include' => $vendorDir . '/twig/twig/lib/Twig/Node/Include.php', + 'Twig_Node_Macro' => $vendorDir . '/twig/twig/lib/Twig/Node/Macro.php', + 'Twig_Node_Module' => $vendorDir . '/twig/twig/lib/Twig/Node/Module.php', + 'Twig_Node_Print' => $vendorDir . '/twig/twig/lib/Twig/Node/Print.php', + 'Twig_Node_Sandbox' => $vendorDir . '/twig/twig/lib/Twig/Node/Sandbox.php', + 'Twig_Node_SandboxedModule' => $vendorDir . '/twig/twig/lib/Twig/Node/SandboxedModule.php', + 'Twig_Node_SandboxedPrint' => $vendorDir . '/twig/twig/lib/Twig/Node/SandboxedPrint.php', + 'Twig_Node_Set' => $vendorDir . '/twig/twig/lib/Twig/Node/Set.php', + 'Twig_Node_SetTemp' => $vendorDir . '/twig/twig/lib/Twig/Node/SetTemp.php', + 'Twig_Node_Spaceless' => $vendorDir . '/twig/twig/lib/Twig/Node/Spaceless.php', + 'Twig_Node_Text' => $vendorDir . '/twig/twig/lib/Twig/Node/Text.php', + 'Twig_Parser' => $vendorDir . '/twig/twig/lib/Twig/Parser.php', + 'Twig_ParserInterface' => $vendorDir . '/twig/twig/lib/Twig/ParserInterface.php', + 'Twig_Sandbox_SecurityError' => $vendorDir . '/twig/twig/lib/Twig/Sandbox/SecurityError.php', + 'Twig_Sandbox_SecurityPolicy' => $vendorDir . '/twig/twig/lib/Twig/Sandbox/SecurityPolicy.php', + 'Twig_Sandbox_SecurityPolicyInterface' => $vendorDir . '/twig/twig/lib/Twig/Sandbox/SecurityPolicyInterface.php', + 'Twig_SimpleFilter' => $vendorDir . '/twig/twig/lib/Twig/SimpleFilter.php', + 'Twig_SimpleFunction' => $vendorDir . '/twig/twig/lib/Twig/SimpleFunction.php', + 'Twig_Template' => $vendorDir . '/twig/twig/lib/Twig/Template.php', + 'Twig_TemplateInterface' => $vendorDir . '/twig/twig/lib/Twig/TemplateInterface.php', + 'Twig_Test' => $vendorDir . '/twig/twig/lib/Twig/Test.php', + 'Twig_TestCallableInterface' => $vendorDir . '/twig/twig/lib/Twig/TestCallableInterface.php', + 'Twig_TestInterface' => $vendorDir . '/twig/twig/lib/Twig/TestInterface.php', + 'Twig_Test_Function' => $vendorDir . '/twig/twig/lib/Twig/Test/Function.php', + 'Twig_Test_IntegrationTestCase' => $vendorDir . '/twig/twig/lib/Twig/Test/IntegrationTestCase.php', + 'Twig_Test_Method' => $vendorDir . '/twig/twig/lib/Twig/Test/Method.php', + 'Twig_Test_Node' => $vendorDir . '/twig/twig/lib/Twig/Test/Node.php', + 'Twig_Test_NodeTestCase' => $vendorDir . '/twig/twig/lib/Twig/Test/NodeTestCase.php', + 'Twig_Token' => $vendorDir . '/twig/twig/lib/Twig/Token.php', + 'Twig_TokenParser' => $vendorDir . '/twig/twig/lib/Twig/TokenParser.php', + 'Twig_TokenParserBroker' => $vendorDir . '/twig/twig/lib/Twig/TokenParserBroker.php', + 'Twig_TokenParserBrokerInterface' => $vendorDir . '/twig/twig/lib/Twig/TokenParserBrokerInterface.php', + 'Twig_TokenParserInterface' => $vendorDir . '/twig/twig/lib/Twig/TokenParserInterface.php', + 'Twig_TokenParser_AutoEscape' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/AutoEscape.php', + 'Twig_TokenParser_Block' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/Block.php', + 'Twig_TokenParser_Do' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/Do.php', + 'Twig_TokenParser_Embed' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/Embed.php', + 'Twig_TokenParser_Extends' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/Extends.php', + 'Twig_TokenParser_Filter' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/Filter.php', + 'Twig_TokenParser_Flush' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/Flush.php', + 'Twig_TokenParser_For' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/For.php', + 'Twig_TokenParser_From' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/From.php', + 'Twig_TokenParser_If' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/If.php', + 'Twig_TokenParser_Import' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/Import.php', + 'Twig_TokenParser_Include' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/Include.php', + 'Twig_TokenParser_Macro' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/Macro.php', + 'Twig_TokenParser_Sandbox' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/Sandbox.php', + 'Twig_TokenParser_Set' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/Set.php', + 'Twig_TokenParser_Spaceless' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/Spaceless.php', + 'Twig_TokenParser_Use' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/Use.php', + 'Twig_TokenStream' => $vendorDir . '/twig/twig/lib/Twig/TokenStream.php', ); diff --git a/vendor/composer/autoload_files.php b/vendor/composer/autoload_files.php index 7ce7f5241..2c8b0e2b2 100644 --- a/vendor/composer/autoload_files.php +++ b/vendor/composer/autoload_files.php @@ -8,4 +8,6 @@ return array( $vendorDir . '/tracy/tracy/src/shortcuts.php', $vendorDir . '/ircmaxell/password-compat/lib/password.php', + $vendorDir . '/donatj/phpuseragentparser/Source/UserAgentParser.php', + $baseDir . '/system/defines.php', ); diff --git a/vendor/composer/autoload_namespaces.php b/vendor/composer/autoload_namespaces.php index d55f7e8a4..2c705bd2a 100644 --- a/vendor/composer/autoload_namespaces.php +++ b/vendor/composer/autoload_namespaces.php @@ -8,7 +8,10 @@ return array( 'Twig_' => array($vendorDir . '/twig/twig/lib'), 'Symfony\\Component\\Yaml\\' => array($vendorDir . '/symfony/yaml'), + 'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'), 'Symfony\\Component\\Console\\' => array($vendorDir . '/symfony/console'), + 'Pimple' => array($vendorDir . '/pimple/pimple/src'), + 'ParsedownExtra' => array($vendorDir . '/erusev/parsedown-extra'), 'Parsedown' => array($vendorDir . '/erusev/parsedown'), 'Gregwar\\Image' => array($vendorDir . '/gregwar/image'), 'Gregwar\\Cache' => array($vendorDir . '/gregwar/cache'), diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php index b265c64a2..687a12874 100644 --- a/vendor/composer/autoload_psr4.php +++ b/vendor/composer/autoload_psr4.php @@ -6,4 +6,5 @@ $baseDir = dirname($vendorDir); return array( + 'Grav\\' => array($baseDir . '/system/src'), ); diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php index 70b9d3428..ede8c1c05 100644 --- a/vendor/composer/autoload_real.php +++ b/vendor/composer/autoload_real.php @@ -2,7 +2,7 @@ // autoload_real.php @generated by Composer -class ComposerAutoloaderInit59d31b9205a8126f2a856995d42bb492 +class ComposerAutoloaderInitc72d8fa047c31604816395255e8b35b9 { private static $loader; @@ -19,9 +19,9 @@ public static function getLoader() return self::$loader; } - spl_autoload_register(array('ComposerAutoloaderInit59d31b9205a8126f2a856995d42bb492', 'loadClassLoader'), true, true); + spl_autoload_register(array('ComposerAutoloaderInitc72d8fa047c31604816395255e8b35b9', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(); - spl_autoload_unregister(array('ComposerAutoloaderInit59d31b9205a8126f2a856995d42bb492', 'loadClassLoader')); + spl_autoload_unregister(array('ComposerAutoloaderInitc72d8fa047c31604816395255e8b35b9', 'loadClassLoader')); $map = require __DIR__ . '/autoload_namespaces.php'; foreach ($map as $namespace => $path) { @@ -42,14 +42,14 @@ public static function getLoader() $includeFiles = require __DIR__ . '/autoload_files.php'; foreach ($includeFiles as $file) { - composerRequire59d31b9205a8126f2a856995d42bb492($file); + composerRequirec72d8fa047c31604816395255e8b35b9($file); } return $loader; } } -function composerRequire59d31b9205a8126f2a856995d42bb492($file) +function composerRequirec72d8fa047c31604816395255e8b35b9($file) { require $file; } diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index 0e25e4c58..0ee0cd5f4 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -1,22 +1,22 @@ [ { "name": "erusev/parsedown", - "version": "dev-master", - "version_normalized": "9999999-dev", + "version": "1.0.1", + "version_normalized": "1.0.1.0", "source": { "type": "git", "url": "https://github.com/erusev/parsedown.git", - "reference": "e33ac1c56ea591f21b9cf2fa74356ef708d4e130" + "reference": "d24439ada0704948deef0d3eda2ea20fd8db1747" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/erusev/parsedown/zipball/e33ac1c56ea591f21b9cf2fa74356ef708d4e130", - "reference": "e33ac1c56ea591f21b9cf2fa74356ef708d4e130", + "url": "https://api.github.com/repos/erusev/parsedown/zipball/d24439ada0704948deef0d3eda2ea20fd8db1747", + "reference": "d24439ada0704948deef0d3eda2ea20fd8db1747", "shasum": "" }, - "time": "2014-06-18 09:27:25", + "time": "2014-05-21 20:20:46", "type": "library", - "installation-source": "source", + "installation-source": "dist", "autoload": { "psr-0": { "Parsedown": "" @@ -42,17 +42,17 @@ }, { "name": "doctrine/cache", - "version": "dev-master", - "version_normalized": "9999999-dev", + "version": "v1.3.0", + "version_normalized": "1.3.0.0", "source": { "type": "git", "url": "https://github.com/doctrine/cache.git", - "reference": "72121e68265cd8b37f9b69778308251f6c5133e4" + "reference": "e16d7adf45664a50fa86f515b6d5e7f670130449" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/cache/zipball/72121e68265cd8b37f9b69778308251f6c5133e4", - "reference": "72121e68265cd8b37f9b69778308251f6c5133e4", + "url": "https://api.github.com/repos/doctrine/cache/zipball/e16d7adf45664a50fa86f515b6d5e7f670130449", + "reference": "e16d7adf45664a50fa86f515b6d5e7f670130449", "shasum": "" }, "require": { @@ -65,14 +65,14 @@ "phpunit/phpunit": ">=3.7", "satooshi/php-coveralls": "~0.6" }, - "time": "2014-08-05 12:51:19", + "time": "2013-10-25 19:04:14", "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4.x-dev" + "dev-master": "1.0.x-dev" } }, - "installation-source": "source", + "installation-source": "dist", "autoload": { "psr-0": { "Doctrine\\Common\\Cache\\": "lib/" @@ -84,24 +84,29 @@ ], "authors": [ { - "name": "Roman Borschel", - "email": "roman@code-factory.org" + "name": "Jonathan Wage", + "email": "jonwage@gmail.com", + "homepage": "http://www.jwage.com/", + "role": "Creator" }, { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com", + "homepage": "http://www.instaclick.com" }, { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" + "name": "Roman Borschel", + "email": "roman@code-factory.org" }, { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" }, { "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" + "email": "schmittjoh@gmail.com", + "homepage": "https://github.com/schmittjoh", + "role": "Developer of wrapped JMSSerializerBundle" } ], "description": "Caching library offering an object-oriented API for many cache backends", @@ -112,34 +117,284 @@ ] }, { - "name": "tracy/tracy", - "version": "dev-master", - "version_normalized": "9999999-dev", + "name": "mrclay/minify", + "version": "2.2.0", + "version_normalized": "2.2.0.0", "source": { "type": "git", - "url": "https://github.com/nette/tracy.git", - "reference": "1250ac4907947b28ec66d6e00a337dadaddaad4c" + "url": "https://github.com/mrclay/minify.git", + "reference": "d245bca4987dec197d1e6d7dc117614b60ff7494" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/tracy/zipball/1250ac4907947b28ec66d6e00a337dadaddaad4c", - "reference": "1250ac4907947b28ec66d6e00a337dadaddaad4c", + "url": "https://api.github.com/repos/mrclay/minify/zipball/d245bca4987dec197d1e6d7dc117614b60ff7494", + "reference": "d245bca4987dec197d1e6d7dc117614b60ff7494", "shasum": "" }, "require": { - "php": ">=5.3.1" + "ext-pcre": "*", + "php": ">=5.2.1" + }, + "time": "2014-03-12 12:54:23", + "type": "library", + "installation-source": "dist", + "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": "pimple/pimple", + "version": "v3.0.0", + "version_normalized": "3.0.0.0", + "source": { + "type": "git", + "url": "https://github.com/fabpot/Pimple.git", + "reference": "876bf0899d01feacd2a2e83f04641e51350099ef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fabpot/Pimple/zipball/876bf0899d01feacd2a2e83f04641e51350099ef", + "reference": "876bf0899d01feacd2a2e83f04641e51350099ef", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "time": "2014-07-24 09:48:15", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Pimple": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Pimple is a simple Dependency Injection Container for PHP 5.3", + "homepage": "http://pimple.sensiolabs.org", + "keywords": [ + "container", + "dependency injection" + ] + }, + { + "name": "symfony/yaml", + "version": "v2.5.3", + "version_normalized": "2.5.3.0", + "target-dir": "Symfony/Component/Yaml", + "source": { + "type": "git", + "url": "https://github.com/symfony/Yaml.git", + "reference": "5a75366ae9ca8b4792cd0083e4ca4dff9fe96f1f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Yaml/zipball/5a75366ae9ca8b4792cd0083e4ca4dff9fe96f1f", + "reference": "5a75366ae9ca8b4792cd0083e4ca4dff9fe96f1f", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "time": "2014-08-05 09:00:40", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.5-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Symfony\\Component\\Yaml\\": "" + } + }, + "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 Yaml Component", + "homepage": "http://symfony.com" + }, + { + "name": "symfony/console", + "version": "v2.5.3", + "version_normalized": "2.5.3.0", + "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": { - "nette/tester": "~1.0" + "psr/log": "~1.0", + "symfony/event-dispatcher": "~2.1" }, - "time": "2014-08-07 17:19:48", + "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.3-dev" + "dev-master": "2.5-dev" } }, - "installation-source": "source", + "installation-source": "dist", + "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": "symfony/event-dispatcher", + "version": "v2.5.3", + "version_normalized": "2.5.3.0", + "target-dir": "Symfony/Component/EventDispatcher", + "source": { + "type": "git", + "url": "https://github.com/symfony/EventDispatcher.git", + "reference": "8faf5cc7e80fde74a650a36e60d32ce3c3e0457b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/8faf5cc7e80fde74a650a36e60d32ce3c3e0457b", + "reference": "8faf5cc7e80fde74a650a36e60d32ce3c3e0457b", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~2.0", + "symfony/dependency-injection": "~2.0", + "symfony/stopwatch": "~2.2" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "time": "2014-07-28 13:20:46", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.5-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Symfony\\Component\\EventDispatcher\\": "" + } + }, + "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 EventDispatcher Component", + "homepage": "http://symfony.com" + }, + { + "name": "tracy/tracy", + "version": "v2.2.2", + "version_normalized": "2.2.2.0", + "source": { + "type": "git", + "url": "https://github.com/nette/tracy.git", + "reference": "f5a2647c9d0174d218d626eab3952ea3a523c6e7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/tracy/zipball/f5a2647c9d0174d218d626eab3952ea3a523c6e7", + "reference": "f5a2647c9d0174d218d626eab3952ea3a523c6e7", + "shasum": "" + }, + "require": { + "php": ">=5.3.1" + }, + "require-dev": { + "nette/tester": "~1.0" + }, + "time": "2014-06-24 01:18:03", + "type": "library", + "installation-source": "dist", "autoload": { "classmap": [ "src/Tracy" @@ -174,24 +429,21 @@ }, { "name": "gregwar/cache", - "version": "v1.0.9", - "version_normalized": "1.0.9.0", + "version": "v1.0.7", + "version_normalized": "1.0.7.0", "target-dir": "Gregwar/Cache", "source": { "type": "git", "url": "https://github.com/Gregwar/Cache.git", - "reference": "514b9b469082028d094e33e4a059c863c546b14e" + "reference": "b2d0197c07cc1ccf7436e1ae7bf64f9948e02052" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Gregwar/Cache/zipball/514b9b469082028d094e33e4a059c863c546b14e", - "reference": "514b9b469082028d094e33e4a059c863c546b14e", + "url": "https://api.github.com/repos/Gregwar/Cache/zipball/b2d0197c07cc1ccf7436e1ae7bf64f9948e02052", + "reference": "b2d0197c07cc1ccf7436e1ae7bf64f9948e02052", "shasum": "" }, - "require": { - "php": ">=5.3" - }, - "time": "2014-07-05 17:42:36", + "time": "2014-02-20 20:04:58", "type": "library", "installation-source": "dist", "autoload": { @@ -220,28 +472,28 @@ }, { "name": "gregwar/image", - "version": "dev-master", - "version_normalized": "9999999-dev", + "version": "v2.0.17", + "version_normalized": "2.0.17.0", "target-dir": "Gregwar/Image", "source": { "type": "git", "url": "https://github.com/Gregwar/Image.git", - "reference": "31cf30151015d66f320011ea8646e90bb62ffb13" + "reference": "a7bba10ed0e3004c098bb7fde3a3e5bcafca8a2c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Gregwar/Image/zipball/31cf30151015d66f320011ea8646e90bb62ffb13", - "reference": "31cf30151015d66f320011ea8646e90bb62ffb13", + "url": "https://api.github.com/repos/Gregwar/Image/zipball/a7bba10ed0e3004c098bb7fde3a3e5bcafca8a2c", + "reference": "a7bba10ed0e3004c098bb7fde3a3e5bcafca8a2c", "shasum": "" }, "require": { "ext-gd": "*", - "gregwar/cache": "1.*", + "gregwar/cache": "v1.0.7", "php": ">=5.3.0" }, - "time": "2014-06-30 15:00:37", + "time": "2014-02-20 22:20:45", "type": "library", - "installation-source": "source", + "installation-source": "dist", "autoload": { "psr-0": { "Gregwar\\Image": "" @@ -265,25 +517,66 @@ "image" ] }, + { + "name": "ircmaxell/password-compat", + "version": "1.0.3", + "version_normalized": "1.0.3.0", + "source": { + "type": "git", + "url": "https://github.com/ircmaxell/password_compat.git", + "reference": "1fc1521b5e9794ea77e4eca30717be9635f1d4f4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ircmaxell/password_compat/zipball/1fc1521b5e9794ea77e4eca30717be9635f1d4f4", + "reference": "1fc1521b5e9794ea77e4eca30717be9635f1d4f4", + "shasum": "" + }, + "time": "2013-04-30 19:58:08", + "type": "library", + "installation-source": "dist", + "autoload": { + "files": [ + "lib/password.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Anthony Ferrara", + "email": "ircmaxell@ircmaxell.com", + "homepage": "http://blog.ircmaxell.com" + } + ], + "description": "A compatibility library for the proposed simplified password hashing algorithm: https://wiki.php.net/rfc/password_hash", + "homepage": "https://github.com/ircmaxell/password_compat", + "keywords": [ + "hashing", + "password" + ] + }, { "name": "twig/twig", - "version": "dev-master", - "version_normalized": "9999999-dev", + "version": "v1.16.0", + "version_normalized": "1.16.0.0", "source": { "type": "git", "url": "https://github.com/fabpot/Twig.git", - "reference": "e2d2a250d963dd5bba4ad23fc01c7282fc042946" + "reference": "8ce37115802e257a984a82d38254884085060024" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fabpot/Twig/zipball/e2d2a250d963dd5bba4ad23fc01c7282fc042946", - "reference": "e2d2a250d963dd5bba4ad23fc01c7282fc042946", + "url": "https://api.github.com/repos/fabpot/Twig/zipball/8ce37115802e257a984a82d38254884085060024", + "reference": "8ce37115802e257a984a82d38254884085060024", "shasum": "" }, "require": { "php": ">=5.2.4" }, - "time": "2014-08-06 06:46:32", + "time": "2014-07-05 12:19:05", "type": "library", "extra": { "branch-alias": { @@ -325,36 +618,33 @@ ] }, { - "name": "symfony/yaml", - "version": "2.5.x-dev", - "version_normalized": "2.5.9999999.9999999-dev", - "target-dir": "Symfony/Component/Yaml", + "name": "donatj/phpuseragentparser", + "version": "dev-master", + "version_normalized": "9999999-dev", "source": { "type": "git", - "url": "https://github.com/symfony/Yaml.git", - "reference": "5a75366ae9ca8b4792cd0083e4ca4dff9fe96f1f" + "url": "https://github.com/donatj/PhpUserAgent.git", + "reference": "abbd69a119f067e4afc3c4baf28d04646114a668" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Yaml/zipball/5a75366ae9ca8b4792cd0083e4ca4dff9fe96f1f", - "reference": "5a75366ae9ca8b4792cd0083e4ca4dff9fe96f1f", + "url": "https://api.github.com/repos/donatj/PhpUserAgent/zipball/abbd69a119f067e4afc3c4baf28d04646114a668", + "reference": "abbd69a119f067e4afc3c4baf28d04646114a668", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=5.3.0" }, - "time": "2014-08-05 09:00:40", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.5-dev" - } + "require-dev": { + "camspiers/json-pretty": "0.1.*" }, + "time": "2014-08-06 03:39:39", + "type": "library", "installation-source": "source", "autoload": { - "psr-0": { - "Symfony\\Component\\Yaml\\": "" - } + "files": [ + "Source/UserAgentParser.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -362,55 +652,44 @@ ], "authors": [ { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - }, - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Jesse G. Donat", + "email": "donatj@gmail.com", + "homepage": "http://donatstudios.com", + "role": "Developer" } ], - "description": "Symfony Yaml Component", - "homepage": "http://symfony.com" + "description": "Simple, streamlined PHP user-agent parser", + "homepage": "http://donatstudios.com/PHP-Parser-HTTP_USER_AGENT", + "keywords": [ + "parser", + "user agent", + "useragent" + ] }, { - "name": "symfony/console", - "version": "2.5.x-dev", - "version_normalized": "2.5.9999999.9999999-dev", - "target-dir": "Symfony/Component/Console", + "name": "erusev/parsedown-extra", + "version": "dev-master", + "version_normalized": "9999999-dev", "source": { "type": "git", - "url": "https://github.com/symfony/Console.git", - "reference": "cd2d1e4bac2206b337326b0140ff475fe9ad5f63" + "url": "https://github.com/erusev/parsedown-extra.git", + "reference": "7578fe28ce42e7a1fff4ba2aada3807c4c03d04b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Console/zipball/cd2d1e4bac2206b337326b0140ff475fe9ad5f63", - "reference": "cd2d1e4bac2206b337326b0140ff475fe9ad5f63", + "url": "https://api.github.com/repos/erusev/parsedown-extra/zipball/7578fe28ce42e7a1fff4ba2aada3807c4c03d04b", + "reference": "7578fe28ce42e7a1fff4ba2aada3807c4c03d04b", "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": "" + "erusev/parsedown": "~1.0" }, - "time": "2014-08-05 09:00:40", + "time": "2014-08-16 11:20:35", "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.5-dev" - } - }, "installation-source": "source", "autoload": { "psr-0": { - "Symfony\\Component\\Console\\": "" + "ParsedownExtra": "" } }, "notification-url": "https://packagist.org/downloads/", @@ -419,56 +698,18 @@ ], "authors": [ { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - }, - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - } - ], - "description": "Symfony Console Component", - "homepage": "http://symfony.com" - }, - { - "name": "ircmaxell/password-compat", - "version": "1.0.3", - "version_normalized": "1.0.3.0", - "source": { - "type": "git", - "url": "https://github.com/ircmaxell/password_compat.git", - "reference": "1fc1521b5e9794ea77e4eca30717be9635f1d4f4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ircmaxell/password_compat/zipball/1fc1521b5e9794ea77e4eca30717be9635f1d4f4", - "reference": "1fc1521b5e9794ea77e4eca30717be9635f1d4f4", - "shasum": "" - }, - "time": "2013-04-30 19:58:08", - "type": "library", - "installation-source": "dist", - "autoload": { - "files": [ - "lib/password.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Anthony Ferrara", - "email": "ircmaxell@ircmaxell.com", - "homepage": "http://blog.ircmaxell.com" + "name": "Emanuil Rusev", + "email": "hello@erusev.com", + "homepage": "http://erusev.com" } ], - "description": "A compatibility library for the proposed simplified password hashing algorithm: https://wiki.php.net/rfc/password_hash", - "homepage": "https://github.com/ircmaxell/password_compat", + "description": "An extension of Parsedown that adds support for Markdown Extra.", + "homepage": "https://github.com/erusev/parsedown-extra", "keywords": [ - "hashing", - "password" + "markdown", + "markdown extra", + "parsedown", + "parser" ] } ] diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/CacheProvider.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/CacheProvider.php index f896bc708..ce88e4922 100644 --- a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/CacheProvider.php +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/CacheProvider.php @@ -43,7 +43,7 @@ abstract class CacheProvider implements Cache /** * The namespace version. * - * @var integer|null + * @var string */ private $namespaceVersion; @@ -162,7 +162,7 @@ private function getNamespaceCacheKey() /** * Returns the namespace version. * - * @return integer + * @return string */ private function getNamespaceVersion() { @@ -189,7 +189,7 @@ private function getNamespaceVersion() * * @param string $id The id of the cache entry to fetch. * - * @return string|boolean The cached data or FALSE, if no cache entry exists for the given id. + * @return string|bool The cached data or FALSE, if no cache entry exists for the given id. */ abstract protected function doFetch($id); diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/FileCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/FileCache.php index d91d0bc6d..1aa4d7911 100644 --- a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/FileCache.php +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/FileCache.php @@ -130,7 +130,7 @@ protected function doFlush() protected function doGetStats() { $usage = 0; - foreach ($this->getIterator() as $file) { + foreach ($this->getIterator() as $name => $file) { $usage += $file->getSize(); } diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/FilesystemCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/FilesystemCache.php index 07eda8e87..23aaa37bd 100644 --- a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/FilesystemCache.php +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/FilesystemCache.php @@ -105,21 +105,9 @@ protected function doSave($id, $data, $lifeTime = 0) $filepath = pathinfo($filename, PATHINFO_DIRNAME); if ( ! is_dir($filepath)) { - if (false === @mkdir($filepath, 0777, true) && !is_dir($filepath)) { - return false; - } - } elseif ( ! is_writable($filepath)) { - return false; - } - - $tmpFile = tempnam($filepath, basename($filename)); - - if ((file_put_contents($tmpFile, $lifeTime . PHP_EOL . $data) !== false) && @rename($tmpFile, $filename)) { - @chmod($filename, 0666 & ~umask()); - - return true; + mkdir($filepath, 0777, true); } - return false; + return file_put_contents($filename, $lifeTime . PHP_EOL . $data) !== false; } } diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/Version.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/Version.php index d742fa080..e35fbcc5a 100644 --- a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/Version.php +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/Version.php @@ -21,5 +21,5 @@ class Version { - const VERSION = '1.4.0-DEV'; + const VERSION = '1.3.0'; } diff --git a/vendor/donatj/phpuseragentparser/LICENSE.md b/vendor/donatj/phpuseragentparser/LICENSE.md new file mode 100644 index 000000000..305b6996c --- /dev/null +++ b/vendor/donatj/phpuseragentparser/LICENSE.md @@ -0,0 +1,22 @@ +The MIT License +=============== + +Copyright (c) 2013 Jesse G. Donat + +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. \ No newline at end of file diff --git a/vendor/donatj/phpuseragentparser/README.md b/vendor/donatj/phpuseragentparser/README.md new file mode 100644 index 000000000..0d0210215 --- /dev/null +++ b/vendor/donatj/phpuseragentparser/README.md @@ -0,0 +1,110 @@ +# PHP User Agent Parser + +[![Latest Stable Version](https://poser.pugx.org/donatj/phpuseragentparser/v/stable.png)](https://packagist.org/packages/donatj/phpuseragentparser) [![Total Downloads](https://poser.pugx.org/donatj/phpuseragentparser/downloads.png)](https://packagist.org/packages/donatj/phpuseragentparser) [![Latest Unstable Version](https://poser.pugx.org/donatj/phpuseragentparser/v/unstable.png)](https://packagist.org/packages/donatj/phpuseragentparser) [![License](https://poser.pugx.org/donatj/phpuseragentparser/license.png)](https://packagist.org/packages/donatj/phpuseragentparser) +[![Build Status](https://travis-ci.org/donatj/PhpUserAgent.png?branch=master)](https://travis-ci.org/donatj/PhpUserAgent) +[![HHVM Status](http://hhvm.h4cc.de/badge/donatj/phpuseragentparser.png)](http://hhvm.h4cc.de/package/donatj/phpuseragentparser) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/donatj/PhpUserAgent/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/donatj/PhpUserAgent/?branch=master) + +## What It Is + +A simple, streamlined PHP user-agent parser! + +Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php + + +## Why Use This + +You have your choice in user-agent parsers. This one detects **all modern browsers** in a very light, quick, understandable fashion. +It is less than 150 lines of code, and consists of just two regular expressions! +It can also correctly identify exotic versions of IE others fail on. + +It offers 100% unit test coverage, is installable via Composer, and is very easy to use. + +## What It Doesn't Do + +### OS Versions + +User-agent strings **are not** a reliable source of OS Version! + +- Many agents simply don't send the information. +- Others provide varying levels of accuracy. +- Parsing Windows versions alone almost nearly doubles the size of the code. + +I'm much more interested in keeping this thing *tiny* and accurate than adding niché features and would rather focus on things that can be **done well**. + +All that said, there is the start of a [branch to do it](https://github.com/donatj/PhpUserAgent/tree/os_version_detection) I created for a client if you want to poke it, I update it from time to time, but frankly if you need to *reliably detect OS Version*, using user-agent isn't the way to do it. I'd go with JavaScript. + +## Requirements + + - PHP 5.3.0+ + +## Installing + +PHP User Agent is available through Packagist via Composer. + +```json +{ + "require": { + "donatj/phpuseragentparser": "*" + } +} +``` + +## Sample Usage + +```php +$ua_info = parse_user_agent(); +/* +array( + 'platform' => '[Detected Platform]', + 'browser' => '[Detected Browser]', + 'version' => '[Detected Browser Version]', +); +*/ +``` + +## Currently Detected Platforms + +- Desktop + - Windows + - Linux + - Macintosh + - Chrome OS +- Mobile + - Android + - iPhone + - iPad + - Windows Phone OS + - Kindle + - Kindle Fire + - BlackBerry + - Playbook +- Console + - Nintendo 3DS + - Nintendo Wii + - Nintendo WiiU + - PlayStation 3 + - PlayStation 4 + - PlayStation Vita + - Xbox 360 + - Xbox One + +## Currently Detected Browsers + +- Android Browser +- BlackBerry Browser +- Camino +- Kindle / Silk +- Firefox / Iceweasel +- Safari +- Internet Explorer +- IEMobile +- Chrome +- Opera +- Midori +- Lynx +- Wget +- Curl + + + +More information is available at [Donat Studios](http://donatstudios.com/PHP-Parser-HTTP_USER_AGENT). diff --git a/vendor/donatj/phpuseragentparser/Source/UserAgentParser.php b/vendor/donatj/phpuseragentparser/Source/UserAgentParser.php new file mode 100644 index 000000000..06178e42e --- /dev/null +++ b/vendor/donatj/phpuseragentparser/Source/UserAgentParser.php @@ -0,0 +1,144 @@ + + * @link https://github.com/donatj/PhpUserAgent + * @link http://donatstudios.com/PHP-Parser-HTTP_USER_AGENT + * @param string|null $u_agent User agent string to parse or null. Uses $_SERVER['HTTP_USER_AGENT'] on NULL + * @throws InvalidArgumentException on not having a proper user agent to parse. + * @return array an array with browser, version and platform keys + */ +function parse_user_agent( $u_agent = null ) { + if( is_null($u_agent) ) { + if( isset($_SERVER['HTTP_USER_AGENT']) ) { + $u_agent = $_SERVER['HTTP_USER_AGENT']; + } else { + throw new \InvalidArgumentException('parse_user_agent requires a user agent'); + } + } + + $platform = null; + $browser = null; + $version = null; + + $empty = array( 'platform' => $platform, 'browser' => $browser, 'version' => $version ); + + if( !$u_agent ) return $empty; + + if( preg_match('/\((.*?)\)/im', $u_agent, $parent_matches) ) { + + preg_match_all('/(?PBB\d+;|Android|CrOS|iPhone|iPad|Linux|Macintosh|Windows(\ Phone)?|Silk|linux-gnu|BlackBerry|PlayBook|Nintendo\ (WiiU?|3DS)|Xbox(\ One)?) + (?:\ [^;]*)? + (?:;|$)/imx', $parent_matches[1], $result, PREG_PATTERN_ORDER); + + $priority = array( 'Android', 'Xbox One', 'Xbox' ); + $result['platform'] = array_unique($result['platform']); + if( count($result['platform']) > 1 ) { + if( $keys = array_intersect($priority, $result['platform']) ) { + $platform = reset($keys); + } else { + $platform = $result['platform'][0]; + } + } elseif( isset($result['platform'][0]) ) { + $platform = $result['platform'][0]; + } + } + + if( $platform == 'linux-gnu' ) { + $platform = 'Linux'; + } elseif( $platform == 'CrOS' ) { + $platform = 'Chrome OS'; + } + + preg_match_all('%(?PCamino|Kindle(\ Fire\ Build)?|Firefox|Iceweasel|Safari|MSIE|Trident/.*rv|AppleWebKit|Chrome|IEMobile|Opera|OPR|Silk|Lynx|Midori|Version|Wget|curl|NintendoBrowser|PLAYSTATION\ (\d|Vita)+) + (?:\)?;?) + (?:(?:[:/ ])(?P[0-9A-Z.]+)|/(?:[A-Z]*))%ix', + $u_agent, $result, PREG_PATTERN_ORDER); + + + // If nothing matched, return null (to avoid undefined index errors) + if( !isset($result['browser'][0]) || !isset($result['version'][0]) ) { + return $empty; + } + + $browser = $result['browser'][0]; + $version = $result['version'][0]; + + $find = function ( $search, &$key ) use ( $result ) { + $xkey = array_search(strtolower($search), array_map('strtolower', $result['browser'])); + if( $xkey !== false ) { + $key = $xkey; + + return true; + } + + return false; + }; + + $key = 0; + if( $browser == 'Iceweasel' ) { + $browser = 'Firefox'; + } elseif( $find('Playstation Vita', $key) ) { + $platform = 'PlayStation Vita'; + $browser = 'Browser'; + } elseif( $find('Kindle Fire Build', $key) || $find('Silk', $key) ) { + $browser = $result['browser'][$key] == 'Silk' ? 'Silk' : 'Kindle'; + $platform = 'Kindle Fire'; + if( !($version = $result['version'][$key]) || !is_numeric($version[0]) ) { + $version = $result['version'][array_search('Version', $result['browser'])]; + } + } elseif( $find('NintendoBrowser', $key) || $platform == 'Nintendo 3DS' ) { + $browser = 'NintendoBrowser'; + $version = $result['version'][$key]; + } elseif( $find('Kindle', $key) ) { + $browser = $result['browser'][$key]; + $platform = 'Kindle'; + $version = $result['version'][$key]; + } elseif( $find('OPR', $key) ) { + $browser = 'Opera Next'; + $version = $result['version'][$key]; + } elseif( $find('Opera', $key) ) { + $browser = 'Opera'; + $find('Version', $key); + $version = $result['version'][$key]; + } elseif( $find('Midori', $key) ) { + $browser = 'Midori'; + $version = $result['version'][$key]; + } elseif( $browser == 'MSIE' || strpos($browser, 'Trident') !== false ) { + if( $find('IEMobile', $key) ) { + $browser = 'IEMobile'; + } else { + $browser = 'MSIE'; + $key = 0; + } + $version = $result['version'][$key]; + } elseif( $find('Chrome', $key) ) { + $browser = 'Chrome'; + $version = $result['version'][$key]; + } elseif( $browser == 'AppleWebKit' ) { + if( ($platform == 'Android' && !($key = 0)) ) { + $browser = 'Android Browser'; + } elseif( strpos($platform, 'BB') === 0 ) { + $browser = 'BlackBerry Browser'; + $platform = 'BlackBerry'; + } elseif( $platform == 'BlackBerry' || $platform == 'PlayBook' ) { + $browser = 'BlackBerry Browser'; + } elseif( $find('Safari', $key) ) { + $browser = 'Safari'; + } + + $find('Version', $key); + + $version = $result['version'][$key]; + } elseif( $key = preg_grep('/playstation \d/i', array_map('strtolower', $result['browser'])) ) { + $key = reset($key); + + $platform = 'PlayStation ' . preg_replace('/[^\d]/i', '', $key); + $browser = 'NetFront'; + } + + return array( 'platform' => $platform, 'browser' => $browser, 'version' => $version ); + +} diff --git a/vendor/erusev/parsedown-extra/LICENSE.txt b/vendor/erusev/parsedown-extra/LICENSE.txt new file mode 100644 index 000000000..baca86f5b --- /dev/null +++ b/vendor/erusev/parsedown-extra/LICENSE.txt @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013 Emanuil Rusev, erusev.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 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. \ No newline at end of file diff --git a/vendor/erusev/parsedown-extra/ParsedownExtra.php b/vendor/erusev/parsedown-extra/ParsedownExtra.php new file mode 100644 index 000000000..b6ac978dc --- /dev/null +++ b/vendor/erusev/parsedown-extra/ParsedownExtra.php @@ -0,0 +1,415 @@ +BlockTypes[':'] []= 'DefinitionList'; + + $this->DefinitionTypes['*'] []= 'Abbreviation'; + + # identify footnote definitions before reference definitions + array_unshift($this->DefinitionTypes['['], 'Footnote'); + + # identify footnote markers before before links + array_unshift($this->SpanTypes['['], 'FootnoteMarker'); + } + + # + # ~ + + function text($text) + { + $markup = parent::text($text); + + # merge consecutive dl elements + + $markup = preg_replace('/<\/dl>\s+
\s+/', '', $markup); + + # add footnotes + + if (isset($this->Definitions['Footnote'])) + { + $Element = $this->buildFootnoteElement(); + + $markup .= "\n" . $this->element($Element); + } + + return $markup; + } + + # + # Blocks + # + + # + # Atx + + protected function identifyAtx($Line) + { + $Block = parent::identifyAtx($Line); + + if (preg_match('/[ ]*'.$this->attributesPattern.'[ ]*$/', $Block['element']['text'], $matches, PREG_OFFSET_CAPTURE)) + { + $attributeString = $matches[1][0]; + + $Block['element']['attributes'] = $this->parseAttributes($attributeString); + + $Block['element']['text'] = substr($Block['element']['text'], 0, $matches[0][1]); + } + + return $Block; + } + + # + # Definition List + + protected function identifyDefinitionList($Line, $Block) + { + if (isset($Block['type'])) + { + return; + } + + $Element = array( + 'name' => 'dl', + 'handler' => 'elements', + 'text' => array(), + ); + + $terms = explode("\n", $Block['element']['text']); + + foreach ($terms as $term) + { + $Element['text'] []= array( + 'name' => 'dt', + 'handler' => 'line', + 'text' => $term, + ); + } + + $Element['text'] []= array( + 'name' => 'dd', + 'handler' => 'line', + 'text' => ltrim($Line['text'], ' :'), + ); + + $Block['element'] = $Element; + + return $Block; + } + + protected function addToDefinitionList($Line, array $Block) + { + if ($Line['text'][0] === ':') + { + $Block['element']['text'] []= array( + 'name' => 'dd', + 'handler' => 'line', + 'text' => ltrim($Line['text'], ' :'), + ); + + return $Block; + } + + if ( ! isset($Block['interrupted'])) + { + $Element = array_pop($Block['element']['text']); + + $Element['text'] .= "\n" . chop($Line['text']); + + $Block['element']['text'] []= $Element; + + return $Block; + } + } + + # + # Setext + + protected function identifySetext($Line, array $Block = null) + { + $Block = parent::identifySetext($Line, $Block); + + if (preg_match('/[ ]*'.$this->attributesPattern.'[ ]*$/', $Block['element']['text'], $matches, PREG_OFFSET_CAPTURE)) + { + $attributeString = $matches[1][0]; + + $Block['element']['attributes'] = $this->parseAttributes($attributeString); + + $Block['element']['text'] = substr($Block['element']['text'], 0, $matches[0][1]); + } + + return $Block; + } + + # + # Markup + + protected function completeMarkup($Block) + { + $DOMDocument = new DOMDocument; + + $DOMDocument->loadXML($Block['element']); + + $result = $DOMDocument->documentElement->getAttribute('markdown'); + + if ($result !== '1') + { + return $Block; + } + + $DOMDocument->documentElement->removeAttribute('markdown'); + + $index = 0; + $texts = array(); + + foreach ($DOMDocument->documentElement->childNodes as $Node) + { + if ($Node instanceof DOMText) + { + $texts [] = $this->text($Node->nodeValue); + + # replaces the text of the node with a placeholder + $Node->nodeValue = '\x1A'.$index ++; + } + } + + $markup = $DOMDocument->saveXML($DOMDocument->documentElement); + + foreach ($texts as $index => $text) + { + $markup = str_replace('\x1A'.$index, $text, $markup); + } + + $Block['element'] = $markup; + + return $Block; + } + + # + # Definitions + # + + # + # Abbreviation + + protected function identifyAbbreviation($Line) + { + if (preg_match('/^\*\[(.+?)\]:[ ]*(.+?)[ ]*$/', $Line['text'], $matches)) + { + $Abbreviation = array( + 'id' => $matches[1], + 'data' => $matches[2], + ); + + return $Abbreviation; + } + } + + # + # Footnote + + protected function identifyFootnote($Line) + { + if (preg_match('/^\[\^(.+?)\]:[ ]?(.+)$/', $Line['text'], $matches)) + { + $Footnote = array( + 'id' => $matches[1], + 'data' => array( + 'text' => $matches[2], + 'count' => null, + 'number' => null, + ), + ); + + return $Footnote; + } + } + + # + # Spans + # + + # + # Footnote Marker + + protected function identifyFootnoteMarker($Excerpt) + { + if (preg_match('/^\[\^(.+?)\]/', $Excerpt['text'], $matches)) + { + $name = $matches[1]; + + if ( ! isset($this->Definitions['Footnote'][$name])) + { + return; + } + + $this->Definitions['Footnote'][$name]['count'] ++; + + if ( ! isset($this->Definitions['Footnote'][$name]['number'])) + { + $this->Definitions['Footnote'][$name]['number'] = ++ $this->footnoteCount; # » & + } + + $Element = array( + 'name' => 'sup', + 'attributes' => array('id' => 'fnref'.$this->Definitions['Footnote'][$name]['count'].':'.$name), + 'handler' => 'element', + 'text' => array( + 'name' => 'a', + 'attributes' => array('href' => '#fn:'.$name, 'class' => 'footnote-ref'), + 'text' => $this->Definitions['Footnote'][$name]['number'], + ), + ); + + return array( + 'extent' => strlen($matches[0]), + 'element' => $Element, + ); + } + } + + private $footnoteCount = 0; + + # + # Link + + protected function identifyLink($Excerpt) + { + $Span = parent::identifyLink($Excerpt); + + $remainder = substr($Excerpt['text'], $Span['extent']); + + if (preg_match('/^[ ]*'.$this->attributesPattern.'/', $remainder, $matches)) + { + $Span['element']['attributes'] += $this->parseAttributes($matches[1]); + + $Span['extent'] += strlen($matches[0]); + } + + return $Span; + } + + # + # ~ + + protected function readPlainText($text) + { + $text = parent::readPlainText($text); + + if (isset($this->Definitions['Abbreviation'])) + { + foreach ($this->Definitions['Abbreviation'] as $abbreviation => $phrase) + { + $text = str_replace($abbreviation, ''.$abbreviation.'', $text); + } + } + + return $text; + } + + # + # ~ + # + + protected function buildFootnoteElement() + { + $Element = array( + 'name' => 'div', + 'attributes' => array('class' => 'footnotes'), + 'handler' => 'elements', + 'text' => array( + array( + 'name' => 'hr', + ), + array( + 'name' => 'ol', + 'handler' => 'elements', + 'text' => array(), + ), + ), + ); + + usort($this->Definitions['Footnote'], function($A, $B) { + return $A['number'] - $B['number']; + }); + + foreach ($this->Definitions['Footnote'] as $name => $Data) + { + if ( ! isset($Data['number'])) + { + continue; + } + + $text = $Data['text']; + + foreach (range(1, $Data['count']) as $number) + { + $text .= ' '; + } + + $Element['text'][1]['text'] []= array( + 'name' => 'li', + 'attributes' => array('id' => 'fn:'.$name), + 'handler' => 'elements', + 'text' => array( + array( + 'name' => 'p', + 'text' => $text, + ), + ), + ); + } + + return $Element; + } + + # + # Private + # + + private function parseAttributes($attributeString) + { + $Data = array(); + + $attributes = preg_split('/[ ]+/', $attributeString, - 1, PREG_SPLIT_NO_EMPTY); + + foreach ($attributes as $attribute) + { + if ($attribute[0] === '#') + { + $Data['id'] = substr($attribute, 1); + } + else # "." + { + $classes []= substr($attribute, 1); + } + } + + if (isset($classes)) + { + $Data['class'] = implode(' ', $classes); + } + + return $Data; + } + + private $attributesPattern = '{((?:[#.][-\w]+[ ]*)+)}'; +} diff --git a/vendor/erusev/parsedown-extra/README.md b/vendor/erusev/parsedown-extra/README.md new file mode 100644 index 000000000..931ea2f9c --- /dev/null +++ b/vendor/erusev/parsedown-extra/README.md @@ -0,0 +1,17 @@ +## Parsedown Extra + +An extension of [Parsedown](http://parsedown.org) that adds support for [Markdown Extra](http://en.wikipedia.org/wiki/Markdown_Extra). + +[[ demo ]](http://parsedown.org/demo?extra=1) + +### Installation + +Include both `Parsedown.php` and `ParsedownExtra.php` or install [the composer package](https://packagist.org/packages/erusev/parsedown-extra). + +### Example + +``` php +$Instance = new ParsedownExtra(); + +echo $Instance->text('Hello _Parsedown Extra_!'); # prints:

Hello Parsedown Extra!

+``` diff --git a/vendor/erusev/parsedown/README.md b/vendor/erusev/parsedown/README.md index f1676f4d2..a67b3ec9e 100644 --- a/vendor/erusev/parsedown/README.md +++ b/vendor/erusev/parsedown/README.md @@ -13,7 +13,6 @@ Better [Markdown](http://en.wikipedia.org/wiki/Markdown) parser for PHP. * [Tested](https://travis-ci.org/erusev/parsedown) in PHP 5.2, 5.3, 5.4, 5.5, 5.6 and [hhvm](http://www.hhvm.com/) * Extensible * [Markdown Extra extension](https://github.com/erusev/parsedown-extra) new -* [JavaScript port](https://github.com/hkdobrev/parsedown.js) under development new ### Installation diff --git a/vendor/gregwar/cache/Gregwar/Cache/Cache.php b/vendor/gregwar/cache/Gregwar/Cache/Cache.php index db65ccaac..2dfb2b0cd 100644 --- a/vendor/gregwar/cache/Gregwar/Cache/Cache.php +++ b/vendor/gregwar/cache/Gregwar/Cache/Cache.php @@ -35,7 +35,7 @@ class Cache */ public function __construct($cacheDirectory = 'cache') { - $this->cacheDirectory = $cacheDirectory; + $this->cacheDirectory = $cacheDirectory; } /** @@ -45,9 +45,9 @@ public function __construct($cacheDirectory = 'cache') */ public function setCacheDirectory($cacheDirectory) { - $this->cacheDirectory = $cacheDirectory; + $this->cacheDirectory = $cacheDirectory; - return $this; + return $this; } /** @@ -57,7 +57,7 @@ public function setCacheDirectory($cacheDirectory) */ public function getCacheDirectory() { - return $this->cacheDirectory; + return $this->cacheDirectory; } /** @@ -112,24 +112,24 @@ protected function mkdir($directory) */ public function getCacheFile($filename, $actual = false, $mkdir = false) { - $path = array(); + $path = array(); - // Getting the length of the filename before the extension - $parts = explode('.', $filename); - $len = strlen($parts[0]); + // Getting the length of the filename before the extension + $parts = explode('.', $filename); + $len = strlen($parts[0]); - for ($i=0; $iprefixSize); $i++) { - $path[] = $filename[$i]; + for ($i=0; $iprefixSize); $i++) { + $path[] = $filename[$i]; } - $path = implode('/', $path); + $path = implode('/', $path); $actualDir = $this->getActualCacheDirectory() . '/' . $path; if ($mkdir && !is_dir($actualDir)) { - mkdir($actualDir, 0755, true); - } + mkdir($actualDir, 0755, true); + } - $path .= '/' . $filename; + $path .= '/' . $filename; if ($actual) { return $this->getActualCacheDirectory() . '/' . $path; @@ -148,20 +148,20 @@ protected function checkConditions($cacheFile, array $conditions = array()) { // Implicit condition: the cache file should exist if (!file_exists($cacheFile)) { - return false; - } + return false; + } - foreach ($conditions as $type => $value) { - switch ($type) { - case 'maxage': + foreach ($conditions as $type => $value) { + switch ($type) { + case 'maxage': case 'max-age': - // Return false if the file is older than $value + // Return false if the file is older than $value $age = time() - filectime($cacheFile); if ($age > $value) { return false; } - break; - case 'younger-than': + break; + case 'younger-than': case 'youngerthan': // Return false if the file is older than the file $value, or the files $value $check = function($filename) use ($cacheFile) { @@ -179,13 +179,13 @@ protected function checkConditions($cacheFile, array $conditions = array()) } } } - break; - default: - throw new \Exception('Cache condition '.$type.' not supported'); - } - } + break; + default: + throw new \Exception('Cache condition '.$type.' not supported'); + } + } - return true; + return true; } /** @@ -199,7 +199,7 @@ public function exists($filename, array $conditions = array()) { $cacheFile = $this->getCacheFile($filename, true); - return $this->checkConditions($cacheFile, $conditions); + return $this->checkConditions($cacheFile, $conditions); } /** @@ -215,7 +215,7 @@ public function check($filename, array $conditions = array()) */ public function set($filename, $contents = '') { - $cacheFile = $this->getCacheFile($filename, true, true); + $cacheFile = $this->getCacheFile($filename, true, true); file_put_contents($cacheFile, $contents); @@ -235,11 +235,11 @@ public function write($filename, $contents = '') */ public function get($filename, array $conditions = array()) { - if ($this->exists($filename, $conditions)) { - return file_get_contents($this->getCacheFile($filename, true)); - } else { - return null; - } + if ($this->exists($filename, $conditions)) { + return file_get_contents($this->getCacheFile($filename, true)); + } else { + return null; + } } /** @@ -259,12 +259,8 @@ protected function isRemote($file) * @param $file returns the cache file or the file contents * @param $actual returns the actual cache file */ - public function getOrCreate($filename, array $conditions = array(), $function, $file = false, $actual = false) + public function getOrCreate($filename, array $conditions = array(), \Closure $function, $file = false, $actual = false) { - if (!is_callable($function)) { - throw new InvalidArgumentException('The argument $function should be callable'); - } - $cacheFile = $this->getCacheFile($filename, true, true); $data = null; @@ -272,7 +268,7 @@ public function getOrCreate($filename, array $conditions = array(), $function, $ $data = file_get_contents($cacheFile); } else { @unlink($cacheFile); - $data = call_user_func($function, $cacheFile); + $data = $function($cacheFile); // Test if the closure wrote the file or if it returned the data if (!file_exists($cacheFile)) { @@ -288,7 +284,7 @@ public function getOrCreate($filename, array $conditions = array(), $function, $ /** * Alias to getOrCreate with $file = true */ - public function getOrCreateFile($filename, array $conditions = array(), $function, $actual = false) + public function getOrCreateFile($filename, array $conditions = array(), \Closure $function, $actual = false) { return $this->getOrCreate($filename, $conditions, $function, true, $actual); } diff --git a/vendor/gregwar/image/Gregwar/Image/Image.php b/vendor/gregwar/image/Gregwar/Image/Image.php index f43f3a7ad..44ab9c449 100644 --- a/vendor/gregwar/image/Gregwar/Image/Image.php +++ b/vendor/gregwar/image/Gregwar/Image/Image.php @@ -682,7 +682,7 @@ public function inline($type = 'jpg', $quality = 80) */ public static function open($file = '') { - return new static($file); + return new self($file); } /** @@ -690,7 +690,7 @@ public static function open($file = '') */ public static function create($width, $height) { - return new static(null, $width, $height); + return new self(null, $width, $height); } /** @@ -698,7 +698,7 @@ public static function create($width, $height) */ public static function fromData($data) { - $image = new static(); + $image = new self(); $image->setData($data); return $image; @@ -709,7 +709,7 @@ public static function fromData($data) */ public static function fromResource($resource) { - $image = new static(); + $image = new self(); $image->setResource($resource); return $image; diff --git a/vendor/mrclay/minify/HISTORY.txt b/vendor/mrclay/minify/HISTORY.txt new file mode 100644 index 000000000..aaba5acfe --- /dev/null +++ b/vendor/mrclay/minify/HISTORY.txt @@ -0,0 +1,138 @@ +Minify Release History + +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. diff --git a/vendor/mrclay/minify/LICENSE.txt b/vendor/mrclay/minify/LICENSE.txt new file mode 100644 index 000000000..8f008adb5 --- /dev/null +++ b/vendor/mrclay/minify/LICENSE.txt @@ -0,0 +1,26 @@ +Copyright (c) 2008 Ryan Grove +Copyright (c) 2008 Steve Clay +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. diff --git a/vendor/mrclay/minify/MIN.txt b/vendor/mrclay/minify/MIN.txt new file mode 100644 index 000000000..5aaf0fb8c --- /dev/null +++ b/vendor/mrclay/minify/MIN.txt @@ -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.: + +"; + +$cssUri = Minify_getUri(array( + '//css/styles1.css' + ,'//css/styles2.css' +)); // a list of files +echo ""; + + +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: + + $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.: + + '/min.php')); +echo ""; + + +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 diff --git a/vendor/mrclay/minify/README.txt b/vendor/mrclay/minify/README.txt new file mode 100644 index 000000000..54b0690f8 --- /dev/null +++ b/vendor/mrclay/minify/README.txt @@ -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. + diff --git a/vendor/mrclay/minify/UPGRADING.txt b/vendor/mrclay/minify/UPGRADING.txt new file mode 100644 index 000000000..647b9cc31 --- /dev/null +++ b/vendor/mrclay/minify/UPGRADING.txt @@ -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 \ No newline at end of file diff --git a/vendor/mrclay/minify/min/lib/CSSmin.php b/vendor/mrclay/minify/min/lib/CSSmin.php new file mode 100644 index 000000000..e85f23eb5 --- /dev/null +++ b/vendor/mrclay/minify/min/lib/CSSmin.php @@ -0,0 +1,775 @@ +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('/((? -9.0 to -9 + $css = preg_replace('/((?\+\(\)\]\~\=,])/', '$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; + } +} \ No newline at end of file diff --git a/vendor/mrclay/minify/min/lib/DooDigestAuth.php b/vendor/mrclay/minify/min/lib/DooDigestAuth.php new file mode 100644 index 000000000..69bc4ed4d --- /dev/null +++ b/vendor/mrclay/minify/min/lib/DooDigestAuth.php @@ -0,0 +1,121 @@ + + * @link http://www.doophp.com/ + * @copyright Copyright © 2009 Leng Sheng Hong + * @license http://www.doophp.com/license + */ + +/** + * Handles HTTP digest authentication + * + *

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.

+ * RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L] + * + *

This class is tested under Apache 2.2 and Cherokee web server. It should work in both mod_php and cgi mode.

+ * + * @author Leng Sheng Hong + * @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. + * + *

HTTP Digest Authentication doesn't work with PHP in CGI mode, + * you have to add this into your .htaccess RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]

+ * + * @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(""); + 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(""); + 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(""); + 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; + } + + +} diff --git a/vendor/mrclay/minify/min/lib/FirePHP.php b/vendor/mrclay/minify/min/lib/FirePHP.php new file mode 100644 index 000000000..d301a641a --- /dev/null +++ b/vendor/mrclay/minify/min/lib/FirePHP.php @@ -0,0 +1,1370 @@ + + * @license http://www.opensource.org/licenses/bsd-license.php + * @package FirePHP + */ + + +/** + * Sends the given data to the FirePHP Firefox Extension. + * The data can be displayed in the Firebug Console or in the + * "Server" request tab. + * + * For more information see: http://www.firephp.org/ + * + * @copyright Copyright (C) 2007-2008 Christoph Dorn + * @author Christoph Dorn + * @license http://www.opensource.org/licenses/bsd-license.php + * @package FirePHP + */ +class FirePHP { + + /** + * FirePHP version + * + * @var string + */ + const VERSION = '0.2.0'; + + /** + * Firebug LOG level + * + * Logs a message to firebug console. + * + * @var string + */ + const LOG = 'LOG'; + + /** + * Firebug INFO level + * + * Logs a message to firebug console and displays an info icon before the message. + * + * @var string + */ + const INFO = 'INFO'; + + /** + * Firebug WARN level + * + * Logs a message to firebug console, displays an warning icon before the message and colors the line turquoise. + * + * @var string + */ + const WARN = 'WARN'; + + /** + * Firebug ERROR level + * + * Logs a message to firebug console, displays an error icon before the message and colors the line yellow. Also increments the firebug error count. + * + * @var string + */ + const ERROR = 'ERROR'; + + /** + * Dumps a variable to firebug's server panel + * + * @var string + */ + const DUMP = 'DUMP'; + + /** + * Displays a stack trace in firebug console + * + * @var string + */ + const TRACE = 'TRACE'; + + /** + * Displays an exception in firebug console + * + * Increments the firebug error count. + * + * @var string + */ + const EXCEPTION = 'EXCEPTION'; + + /** + * Displays an table in firebug console + * + * @var string + */ + const TABLE = 'TABLE'; + + /** + * Starts a group in firebug console + * + * @var string + */ + const GROUP_START = 'GROUP_START'; + + /** + * Ends a group in firebug console + * + * @var string + */ + const GROUP_END = 'GROUP_END'; + + /** + * Singleton instance of FirePHP + * + * @var FirePHP + */ + protected static $instance = null; + + /** + * Wildfire protocol message index + * + * @var int + */ + protected $messageIndex = 1; + + /** + * Options for the library + * + * @var array + */ + protected $options = array(); + + /** + * Filters used to exclude object members when encoding + * + * @var array + */ + protected $objectFilters = array(); + + /** + * A stack of objects used to detect recursion during object encoding + * + * @var object + */ + protected $objectStack = array(); + + /** + * Flag to enable/disable logging + * + * @var boolean + */ + protected $enabled = true; + + /** + * The object constructor + */ + function __construct() { + $this->options['maxObjectDepth'] = 10; + $this->options['maxArrayDepth'] = 20; + $this->options['useNativeJsonEncode'] = true; + $this->options['includeLineNumbers'] = true; + } + + /** + * When the object gets serialized only include specific object members. + * + * @return array + */ + public function __sleep() { + return array('options','objectFilters','enabled'); + } + + /** + * Gets singleton instance of FirePHP + * + * @param boolean $AutoCreate + * @return FirePHP + */ + public static function getInstance($AutoCreate=false) { + if($AutoCreate===true && !self::$instance) { + self::init(); + } + return self::$instance; + } + + /** + * Creates FirePHP object and stores it for singleton access + * + * @return FirePHP + */ + public static function init() { + return self::$instance = new self(); + } + + /** + * Enable and disable logging to Firebug + * + * @param boolean $Enabled TRUE to enable, FALSE to disable + * @return void + */ + public function setEnabled($Enabled) { + $this->enabled = $Enabled; + } + + /** + * Check if logging is enabled + * + * @return boolean TRUE if enabled + */ + public function getEnabled() { + return $this->enabled; + } + + /** + * Specify a filter to be used when encoding an object + * + * Filters are used to exclude object members. + * + * @param string $Class The class name of the object + * @param array $Filter An array or members to exclude + * @return void + */ + public function setObjectFilter($Class, $Filter) { + $this->objectFilters[$Class] = $Filter; + } + + /** + * Set some options for the library + * + * Options: + * - maxObjectDepth: The maximum depth to traverse objects (default: 10) + * - maxArrayDepth: The maximum depth to traverse arrays (default: 20) + * - useNativeJsonEncode: If true will use json_encode() (default: true) + * - includeLineNumbers: If true will include line numbers and filenames (default: true) + * + * @param array $Options The options to be set + * @return void + */ + public function setOptions($Options) { + $this->options = array_merge($this->options,$Options); + } + + /** + * Register FirePHP as your error handler + * + * Will throw exceptions for each php error. + */ + public function registerErrorHandler() + { + //NOTE: The following errors will not be caught by this error handler: + // E_ERROR, E_PARSE, E_CORE_ERROR, + // E_CORE_WARNING, E_COMPILE_ERROR, + // E_COMPILE_WARNING, E_STRICT + + set_error_handler(array($this,'errorHandler')); + } + + /** + * FirePHP's error handler + * + * Throws exception for each php error that will occur. + * + * @param int $errno + * @param string $errstr + * @param string $errfile + * @param int $errline + * @param array $errcontext + */ + public function errorHandler($errno, $errstr, $errfile, $errline, $errcontext) + { + // Don't throw exception if error reporting is switched off + if (error_reporting() == 0) { + return; + } + // Only throw exceptions for errors we are asking for + if (error_reporting() & $errno) { + throw new ErrorException($errstr, 0, $errno, $errfile, $errline); + } + } + + /** + * Register FirePHP as your exception handler + */ + public function registerExceptionHandler() + { + set_exception_handler(array($this,'exceptionHandler')); + } + + /** + * FirePHP's exception handler + * + * Logs all exceptions to your firebug console and then stops the script. + * + * @param Exception $Exception + * @throws Exception + */ + function exceptionHandler($Exception) { + $this->fb($Exception); + } + + /** + * Set custom processor url for FirePHP + * + * @param string $URL + */ + public function setProcessorUrl($URL) + { + $this->setHeader('X-FirePHP-ProcessorURL', $URL); + } + + /** + * Set custom renderer url for FirePHP + * + * @param string $URL + */ + public function setRendererUrl($URL) + { + $this->setHeader('X-FirePHP-RendererURL', $URL); + } + + /** + * Start a group for following messages + * + * @param string $Name + * @return true + * @throws Exception + */ + public function group($Name) { + return $this->fb(null, $Name, FirePHP::GROUP_START); + } + + /** + * Ends a group you have started before + * + * @return true + * @throws Exception + */ + public function groupEnd() { + return $this->fb(null, null, FirePHP::GROUP_END); + } + + /** + * Log object with label to firebug console + * + * @see FirePHP::LOG + * @param mixes $Object + * @param string $Label + * @return true + * @throws Exception + */ + public function log($Object, $Label=null) { + return $this->fb($Object, $Label, FirePHP::LOG); + } + + /** + * Log object with label to firebug console + * + * @see FirePHP::INFO + * @param mixes $Object + * @param string $Label + * @return true + * @throws Exception + */ + public function info($Object, $Label=null) { + return $this->fb($Object, $Label, FirePHP::INFO); + } + + /** + * Log object with label to firebug console + * + * @see FirePHP::WARN + * @param mixes $Object + * @param string $Label + * @return true + * @throws Exception + */ + public function warn($Object, $Label=null) { + return $this->fb($Object, $Label, FirePHP::WARN); + } + + /** + * Log object with label to firebug console + * + * @see FirePHP::ERROR + * @param mixes $Object + * @param string $Label + * @return true + * @throws Exception + */ + public function error($Object, $Label=null) { + return $this->fb($Object, $Label, FirePHP::ERROR); + } + + /** + * Dumps key and variable to firebug server panel + * + * @see FirePHP::DUMP + * @param string $Key + * @param mixed $Variable + * @return true + * @throws Exception + */ + public function dump($Key, $Variable) { + return $this->fb($Variable, $Key, FirePHP::DUMP); + } + + /** + * Log a trace in the firebug console + * + * @see FirePHP::TRACE + * @param string $Label + * @return true + * @throws Exception + */ + public function trace($Label) { + return $this->fb($Label, FirePHP::TRACE); + } + + /** + * Log a table in the firebug console + * + * @see FirePHP::TABLE + * @param string $Label + * @param string $Table + * @return true + * @throws Exception + */ + public function table($Label, $Table) { + return $this->fb($Table, $Label, FirePHP::TABLE); + } + + /** + * Check if FirePHP is installed on client + * + * @return boolean + */ + public function detectClientExtension() { + /* Check if FirePHP is installed on client */ + if(!@preg_match_all('/\sFirePHP\/([\.|\d]*)\s?/si',$this->getUserAgent(),$m) || + !version_compare($m[1][0],'0.0.6','>=')) { + return false; + } + return true; + } + + /** + * Log varible to Firebug + * + * @see http://www.firephp.org/Wiki/Reference/Fb + * @param mixed $Object The variable to be logged + * @return true Return TRUE if message was added to headers, FALSE otherwise + * @throws Exception + */ + public function fb($Object) { + + if(!$this->enabled) { + return false; + } + + if (headers_sent($filename, $linenum)) { + throw $this->newException('Headers already sent in '.$filename.' on line '.$linenum.'. Cannot send log data to FirePHP. You must have Output Buffering enabled via ob_start() or output_buffering ini directive.'); + } + + $Type = null; + $Label = null; + + if(func_num_args()==1) { + } else + if(func_num_args()==2) { + switch(func_get_arg(1)) { + case self::LOG: + case self::INFO: + case self::WARN: + case self::ERROR: + case self::DUMP: + case self::TRACE: + case self::EXCEPTION: + case self::TABLE: + case self::GROUP_START: + case self::GROUP_END: + $Type = func_get_arg(1); + break; + default: + $Label = func_get_arg(1); + break; + } + } else + if(func_num_args()==3) { + $Type = func_get_arg(2); + $Label = func_get_arg(1); + } else { + throw $this->newException('Wrong number of arguments to fb() function!'); + } + + + if(!$this->detectClientExtension()) { + return false; + } + + $meta = array(); + $skipFinalObjectEncode = false; + + if($Object instanceof Exception) { + + $meta['file'] = $this->_escapeTraceFile($Object->getFile()); + $meta['line'] = $Object->getLine(); + + $trace = $Object->getTrace(); + if($Object instanceof ErrorException + && isset($trace[0]['function']) + && $trace[0]['function']=='errorHandler' + && isset($trace[0]['class']) + && $trace[0]['class']=='FirePHP') { + + $severity = false; + switch($Object->getSeverity()) { + case E_WARNING: $severity = 'E_WARNING'; break; + case E_NOTICE: $severity = 'E_NOTICE'; break; + case E_USER_ERROR: $severity = 'E_USER_ERROR'; break; + case E_USER_WARNING: $severity = 'E_USER_WARNING'; break; + case E_USER_NOTICE: $severity = 'E_USER_NOTICE'; break; + case E_STRICT: $severity = 'E_STRICT'; break; + case E_RECOVERABLE_ERROR: $severity = 'E_RECOVERABLE_ERROR'; break; + case E_DEPRECATED: $severity = 'E_DEPRECATED'; break; + case E_USER_DEPRECATED: $severity = 'E_USER_DEPRECATED'; break; + } + + $Object = array('Class'=>get_class($Object), + 'Message'=>$severity.': '.$Object->getMessage(), + 'File'=>$this->_escapeTraceFile($Object->getFile()), + 'Line'=>$Object->getLine(), + 'Type'=>'trigger', + 'Trace'=>$this->_escapeTrace(array_splice($trace,2))); + $skipFinalObjectEncode = true; + } else { + $Object = array('Class'=>get_class($Object), + 'Message'=>$Object->getMessage(), + 'File'=>$this->_escapeTraceFile($Object->getFile()), + 'Line'=>$Object->getLine(), + 'Type'=>'throw', + 'Trace'=>$this->_escapeTrace($trace)); + $skipFinalObjectEncode = true; + } + $Type = self::EXCEPTION; + + } else + if($Type==self::TRACE) { + + $trace = debug_backtrace(); + if(!$trace) return false; + for( $i=0 ; $i_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php' + || substr($this->_standardizePath($trace[$i]['file']),-29,29)=='FirePHPCore/FirePHP.class.php')) { + /* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */ + } else + if(isset($trace[$i]['class']) + && isset($trace[$i+1]['file']) + && $trace[$i]['class']=='FirePHP' + && substr($this->_standardizePath($trace[$i+1]['file']),-18,18)=='FirePHPCore/fb.php') { + /* Skip fb() */ + } else + if($trace[$i]['function']=='fb' + || $trace[$i]['function']=='trace' + || $trace[$i]['function']=='send') { + $Object = array('Class'=>isset($trace[$i]['class'])?$trace[$i]['class']:'', + 'Type'=>isset($trace[$i]['type'])?$trace[$i]['type']:'', + 'Function'=>isset($trace[$i]['function'])?$trace[$i]['function']:'', + 'Message'=>$trace[$i]['args'][0], + 'File'=>isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):'', + 'Line'=>isset($trace[$i]['line'])?$trace[$i]['line']:'', + 'Args'=>isset($trace[$i]['args'])?$this->encodeObject($trace[$i]['args']):'', + 'Trace'=>$this->_escapeTrace(array_splice($trace,$i+1))); + + $skipFinalObjectEncode = true; + $meta['file'] = isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):''; + $meta['line'] = isset($trace[$i]['line'])?$trace[$i]['line']:''; + break; + } + } + + } else + if($Type==self::TABLE) { + + if(isset($Object[0]) && is_string($Object[0])) { + $Object[1] = $this->encodeTable($Object[1]); + } else { + $Object = $this->encodeTable($Object); + } + + $skipFinalObjectEncode = true; + + } else { + if($Type===null) { + $Type = self::LOG; + } + } + + if($this->options['includeLineNumbers']) { + if(!isset($meta['file']) || !isset($meta['line'])) { + + $trace = debug_backtrace(); + for( $i=0 ; $trace && $i_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php' + || substr($this->_standardizePath($trace[$i]['file']),-29,29)=='FirePHPCore/FirePHP.class.php')) { + /* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */ + } else + if(isset($trace[$i]['class']) + && isset($trace[$i+1]['file']) + && $trace[$i]['class']=='FirePHP' + && substr($this->_standardizePath($trace[$i+1]['file']),-18,18)=='FirePHPCore/fb.php') { + /* Skip fb() */ + } else + if(isset($trace[$i]['file']) + && substr($this->_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php') { + /* Skip FB::fb() */ + } else { + $meta['file'] = isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):''; + $meta['line'] = isset($trace[$i]['line'])?$trace[$i]['line']:''; + break; + } + } + + } + } else { + unset($meta['file']); + unset($meta['line']); + } + + $this->setHeader('X-Wf-Protocol-1','http://meta.wildfirehq.org/Protocol/JsonStream/0.2'); + $this->setHeader('X-Wf-1-Plugin-1','http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/'.self::VERSION); + + $structure_index = 1; + if($Type==self::DUMP) { + $structure_index = 2; + $this->setHeader('X-Wf-1-Structure-2','http://meta.firephp.org/Wildfire/Structure/FirePHP/Dump/0.1'); + } else { + $this->setHeader('X-Wf-1-Structure-1','http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1'); + } + + if($Type==self::DUMP) { + $msg = '{"'.$Label.'":'.$this->jsonEncode($Object, $skipFinalObjectEncode).'}'; + } else { + $msg_meta = array('Type'=>$Type); + if($Label!==null) { + $msg_meta['Label'] = $Label; + } + if(isset($meta['file'])) { + $msg_meta['File'] = $meta['file']; + } + if(isset($meta['line'])) { + $msg_meta['Line'] = $meta['line']; + } + $msg = '['.$this->jsonEncode($msg_meta).','.$this->jsonEncode($Object, $skipFinalObjectEncode).']'; + } + + $parts = explode("\n",chunk_split($msg, 5000, "\n")); + + for( $i=0 ; $i2) { + // Message needs to be split into multiple parts + $this->setHeader('X-Wf-1-'.$structure_index.'-'.'1-'.$this->messageIndex, + (($i==0)?strlen($msg):'') + . '|' . $part . '|' + . (($isetHeader('X-Wf-1-'.$structure_index.'-'.'1-'.$this->messageIndex, + strlen($part) . '|' . $part . '|'); + } + + $this->messageIndex++; + + if ($this->messageIndex > 99999) { + throw new Exception('Maximum number (99,999) of messages reached!'); + } + } + } + + $this->setHeader('X-Wf-1-Index',$this->messageIndex-1); + + return true; + } + + /** + * Standardizes path for windows systems. + * + * @param string $Path + * @return string + */ + protected function _standardizePath($Path) { + return preg_replace('/\\\\+/','/',$Path); + } + + /** + * Escape trace path for windows systems + * + * @param array $Trace + * @return array + */ + protected function _escapeTrace($Trace) { + if(!$Trace) return $Trace; + for( $i=0 ; $i_escapeTraceFile($Trace[$i]['file']); + } + if(isset($Trace[$i]['args'])) { + $Trace[$i]['args'] = $this->encodeObject($Trace[$i]['args']); + } + } + return $Trace; + } + + /** + * Escape file information of trace for windows systems + * + * @param string $File + * @return string + */ + protected function _escapeTraceFile($File) { + /* Check if we have a windows filepath */ + if(strpos($File,'\\')) { + /* First strip down to single \ */ + + $file = preg_replace('/\\\\+/','\\',$File); + + return $file; + } + return $File; + } + + /** + * Send header + * + * @param string $Name + * @param string_type $Value + */ + protected function setHeader($Name, $Value) { + return header($Name.': '.$Value); + } + + /** + * Get user agent + * + * @return string|false + */ + protected function getUserAgent() { + if(!isset($_SERVER['HTTP_USER_AGENT'])) return false; + return $_SERVER['HTTP_USER_AGENT']; + } + + /** + * Returns a new exception + * + * @param string $Message + * @return Exception + */ + protected function newException($Message) { + return new Exception($Message); + } + + /** + * Encode an object into a JSON string + * + * Uses PHP's jeson_encode() if available + * + * @param object $Object The object to be encoded + * @return string The JSON string + */ + protected function jsonEncode($Object, $skipObjectEncode=false) + { + if(!$skipObjectEncode) { + $Object = $this->encodeObject($Object); + } + + if(function_exists('json_encode') + && $this->options['useNativeJsonEncode']!=false) { + + return json_encode($Object); + } else { + return $this->json_encode($Object); + } + } + + /** + * Encodes a table by encoding each row and column with encodeObject() + * + * @param array $Table The table to be encoded + * @return array + */ + protected function encodeTable($Table) { + if(!$Table) return $Table; + for( $i=0 ; $iencodeObject($Table[$i][$j]); + } + } + } + return $Table; + } + + /** + * Encodes an object including members with + * protected and private visibility + * + * @param Object $Object The object to be encoded + * @param int $Depth The current traversal depth + * @return array All members of the object + */ + protected function encodeObject($Object, $ObjectDepth = 1, $ArrayDepth = 1) + { + $return = array(); + + if (is_object($Object)) { + + if ($ObjectDepth > $this->options['maxObjectDepth']) { + return '** Max Object Depth ('.$this->options['maxObjectDepth'].') **'; + } + + foreach ($this->objectStack as $refVal) { + if ($refVal === $Object) { + return '** Recursion ('.get_class($Object).') **'; + } + } + array_push($this->objectStack, $Object); + + $return['__className'] = $class = get_class($Object); + + $reflectionClass = new ReflectionClass($class); + $properties = array(); + foreach( $reflectionClass->getProperties() as $property) { + $properties[$property->getName()] = $property; + } + + $members = (array)$Object; + + foreach( $properties as $raw_name => $property ) { + + $name = $raw_name; + if($property->isStatic()) { + $name = 'static:'.$name; + } + if($property->isPublic()) { + $name = 'public:'.$name; + } else + if($property->isPrivate()) { + $name = 'private:'.$name; + $raw_name = "\0".$class."\0".$raw_name; + } else + if($property->isProtected()) { + $name = 'protected:'.$name; + $raw_name = "\0".'*'."\0".$raw_name; + } + + if(!(isset($this->objectFilters[$class]) + && is_array($this->objectFilters[$class]) + && in_array($raw_name,$this->objectFilters[$class]))) { + + if(array_key_exists($raw_name,$members) + && !$property->isStatic()) { + + $return[$name] = $this->encodeObject($members[$raw_name], $ObjectDepth + 1, 1); + + } else { + if(method_exists($property,'setAccessible')) { + $property->setAccessible(true); + $return[$name] = $this->encodeObject($property->getValue($Object), $ObjectDepth + 1, 1); + } else + if($property->isPublic()) { + $return[$name] = $this->encodeObject($property->getValue($Object), $ObjectDepth + 1, 1); + } else { + $return[$name] = '** Need PHP 5.3 to get value **'; + } + } + } else { + $return[$name] = '** Excluded by Filter **'; + } + } + + // Include all members that are not defined in the class + // but exist in the object + foreach( $members as $raw_name => $value ) { + + $name = $raw_name; + + if ($name{0} == "\0") { + $parts = explode("\0", $name); + $name = $parts[2]; + } + + if(!isset($properties[$name])) { + $name = 'undeclared:'.$name; + + if(!(isset($this->objectFilters[$class]) + && is_array($this->objectFilters[$class]) + && in_array($raw_name,$this->objectFilters[$class]))) { + + $return[$name] = $this->encodeObject($value, $ObjectDepth + 1, 1); + } else { + $return[$name] = '** Excluded by Filter **'; + } + } + } + + array_pop($this->objectStack); + + } elseif (is_array($Object)) { + + if ($ArrayDepth > $this->options['maxArrayDepth']) { + return '** Max Array Depth ('.$this->options['maxArrayDepth'].') **'; + } + + foreach ($Object as $key => $val) { + + // Encoding the $GLOBALS PHP array causes an infinite loop + // if the recursion is not reset here as it contains + // a reference to itself. This is the only way I have come up + // with to stop infinite recursion in this case. + if($key=='GLOBALS' + && is_array($val) + && array_key_exists('GLOBALS',$val)) { + $val['GLOBALS'] = '** Recursion (GLOBALS) **'; + } + + $return[$key] = $this->encodeObject($val, 1, $ArrayDepth + 1); + } + } else { + if(self::is_utf8($Object)) { + return $Object; + } else { + return utf8_encode($Object); + } + } + return $return; + } + + /** + * Returns true if $string is valid UTF-8 and false otherwise. + * + * @param mixed $str String to be tested + * @return boolean + */ + protected static function is_utf8($str) { + $c=0; $b=0; + $bits=0; + $len=strlen($str); + for($i=0; $i<$len; $i++){ + $c=ord($str[$i]); + if($c > 128){ + if(($c >= 254)) return false; + elseif($c >= 252) $bits=6; + elseif($c >= 248) $bits=5; + elseif($c >= 240) $bits=4; + elseif($c >= 224) $bits=3; + elseif($c >= 192) $bits=2; + else return false; + if(($i+$bits) > $len) return false; + while($bits > 1){ + $i++; + $b=ord($str[$i]); + if($b < 128 || $b > 191) return false; + $bits--; + } + } + } + return true; + } + + /** + * Converts to and from JSON format. + * + * JSON (JavaScript Object Notation) is a lightweight data-interchange + * format. It is easy for humans to read and write. It is easy for machines + * to parse and generate. It is based on a subset of the JavaScript + * Programming Language, Standard ECMA-262 3rd Edition - December 1999. + * This feature can also be found in Python. JSON is a text format that is + * completely language independent but uses conventions that are familiar + * to programmers of the C-family of languages, including C, C++, C#, Java, + * JavaScript, Perl, TCL, and many others. These properties make JSON an + * ideal data-interchange language. + * + * This package provides a simple encoder and decoder for JSON notation. It + * is intended for use with client-side Javascript applications that make + * use of HTTPRequest to perform server communication functions - data can + * be encoded into JSON notation for use in a client-side javascript, or + * decoded from incoming Javascript requests. JSON format is native to + * Javascript, and can be directly eval()'ed with no further parsing + * overhead + * + * All strings should be in ASCII or UTF-8 format! + * + * LICENSE: 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. + * + * THIS SOFTWARE IS PROVIDED ``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 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. + * + * @category + * @package Services_JSON + * @author Michal Migurski + * @author Matt Knapp + * @author Brett Stimmerman + * @author Christoph Dorn + * @copyright 2005 Michal Migurski + * @version CVS: $Id: JSON.php,v 1.31 2006/06/28 05:54:17 migurski Exp $ + * @license http://www.opensource.org/licenses/bsd-license.php + * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198 + */ + + + /** + * Keep a list of objects as we descend into the array so we can detect recursion. + */ + private $json_objectStack = array(); + + + /** + * convert a string from one UTF-8 char to one UTF-16 char + * + * Normally should be handled by mb_convert_encoding, but + * provides a slower PHP-only method for installations + * that lack the multibye string extension. + * + * @param string $utf8 UTF-8 character + * @return string UTF-16 character + * @access private + */ + private function json_utf82utf16($utf8) + { + // oh please oh please oh please oh please oh please + if(function_exists('mb_convert_encoding')) { + return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8'); + } + + switch(strlen($utf8)) { + case 1: + // this case should never be reached, because we are in ASCII range + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return $utf8; + + case 2: + // return a UTF-16 character from a 2-byte UTF-8 char + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0x07 & (ord($utf8{0}) >> 2)) + . chr((0xC0 & (ord($utf8{0}) << 6)) + | (0x3F & ord($utf8{1}))); + + case 3: + // return a UTF-16 character from a 3-byte UTF-8 char + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr((0xF0 & (ord($utf8{0}) << 4)) + | (0x0F & (ord($utf8{1}) >> 2))) + . chr((0xC0 & (ord($utf8{1}) << 6)) + | (0x7F & ord($utf8{2}))); + } + + // ignoring UTF-32 for now, sorry + return ''; + } + + /** + * encodes an arbitrary variable into JSON format + * + * @param mixed $var any number, boolean, string, array, or object to be encoded. + * see argument 1 to Services_JSON() above for array-parsing behavior. + * if var is a strng, note that encode() always expects it + * to be in ASCII or UTF-8 format! + * + * @return mixed JSON string representation of input var or an error if a problem occurs + * @access public + */ + private function json_encode($var) + { + + if(is_object($var)) { + if(in_array($var,$this->json_objectStack)) { + return '"** Recursion **"'; + } + } + + switch (gettype($var)) { + case 'boolean': + return $var ? 'true' : 'false'; + + case 'NULL': + return 'null'; + + case 'integer': + return (int) $var; + + case 'double': + case 'float': + return (float) $var; + + case 'string': + // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT + $ascii = ''; + $strlen_var = strlen($var); + + /* + * Iterate over every character in the string, + * escaping with a slash or encoding to UTF-8 where necessary + */ + for ($c = 0; $c < $strlen_var; ++$c) { + + $ord_var_c = ord($var{$c}); + + switch (true) { + case $ord_var_c == 0x08: + $ascii .= '\b'; + break; + case $ord_var_c == 0x09: + $ascii .= '\t'; + break; + case $ord_var_c == 0x0A: + $ascii .= '\n'; + break; + case $ord_var_c == 0x0C: + $ascii .= '\f'; + break; + case $ord_var_c == 0x0D: + $ascii .= '\r'; + break; + + case $ord_var_c == 0x22: + case $ord_var_c == 0x2F: + case $ord_var_c == 0x5C: + // double quote, slash, slosh + $ascii .= '\\'.$var{$c}; + break; + + case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)): + // characters U-00000000 - U-0000007F (same as ASCII) + $ascii .= $var{$c}; + break; + + case (($ord_var_c & 0xE0) == 0xC0): + // characters U-00000080 - U-000007FF, mask 110XXXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, ord($var{$c + 1})); + $c += 1; + $utf16 = $this->json_utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xF0) == 0xE0): + // characters U-00000800 - U-0000FFFF, mask 1110XXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2})); + $c += 2; + $utf16 = $this->json_utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xF8) == 0xF0): + // characters U-00010000 - U-001FFFFF, mask 11110XXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3})); + $c += 3; + $utf16 = $this->json_utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xFC) == 0xF8): + // characters U-00200000 - U-03FFFFFF, mask 111110XX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3}), + ord($var{$c + 4})); + $c += 4; + $utf16 = $this->json_utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xFE) == 0xFC): + // characters U-04000000 - U-7FFFFFFF, mask 1111110X + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3}), + ord($var{$c + 4}), + ord($var{$c + 5})); + $c += 5; + $utf16 = $this->json_utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + } + } + + return '"'.$ascii.'"'; + + case 'array': + /* + * As per JSON spec if any array key is not an integer + * we must treat the the whole array as an object. We + * also try to catch a sparsely populated associative + * array with numeric keys here because some JS engines + * will create an array with empty indexes up to + * max_index which can cause memory issues and because + * the keys, which may be relevant, will be remapped + * otherwise. + * + * As per the ECMA and JSON specification an object may + * have any string as a property. Unfortunately due to + * a hole in the ECMA specification if the key is a + * ECMA reserved word or starts with a digit the + * parameter is only accessible using ECMAScript's + * bracket notation. + */ + + // treat as a JSON object + if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) { + + $this->json_objectStack[] = $var; + + $properties = array_map(array($this, 'json_name_value'), + array_keys($var), + array_values($var)); + + array_pop($this->json_objectStack); + + foreach($properties as $property) { + if($property instanceof Exception) { + return $property; + } + } + + return '{' . join(',', $properties) . '}'; + } + + $this->json_objectStack[] = $var; + + // treat it like a regular array + $elements = array_map(array($this, 'json_encode'), $var); + + array_pop($this->json_objectStack); + + foreach($elements as $element) { + if($element instanceof Exception) { + return $element; + } + } + + return '[' . join(',', $elements) . ']'; + + case 'object': + $vars = self::encodeObject($var); + + $this->json_objectStack[] = $var; + + $properties = array_map(array($this, 'json_name_value'), + array_keys($vars), + array_values($vars)); + + array_pop($this->json_objectStack); + + foreach($properties as $property) { + if($property instanceof Exception) { + return $property; + } + } + + return '{' . join(',', $properties) . '}'; + + default: + return null; + } + } + + /** + * array-walking function for use in generating JSON-formatted name-value pairs + * + * @param string $name name of key to use + * @param mixed $value reference to an array element to be encoded + * + * @return string JSON-formatted name-value pair, like '"name":value' + * @access private + */ + private function json_name_value($name, $value) + { + // Encoding the $GLOBALS PHP array causes an infinite loop + // if the recursion is not reset here as it contains + // a reference to itself. This is the only way I have come up + // with to stop infinite recursion in this case. + if($name=='GLOBALS' + && is_array($value) + && array_key_exists('GLOBALS',$value)) { + $value['GLOBALS'] = '** Recursion **'; + } + + $encoded_value = $this->json_encode($value); + + if($encoded_value instanceof Exception) { + return $encoded_value; + } + + return $this->json_encode(strval($name)) . ':' . $encoded_value; + } +} diff --git a/vendor/mrclay/minify/min/lib/HTTP/ConditionalGet.php b/vendor/mrclay/minify/min/lib/HTTP/ConditionalGet.php new file mode 100644 index 000000000..93b7e75d8 --- /dev/null +++ b/vendor/mrclay/minify/min/lib/HTTP/ConditionalGet.php @@ -0,0 +1,366 @@ + + * list($updateTime, $content) = getDbUpdateAndContent(); + * $cg = new HTTP_ConditionalGet(array( + * 'lastModifiedTime' => $updateTime + * ,'isPublic' => true + * )); + * $cg->sendHeaders(); + * if ($cg->cacheIsValid) { + * exit(); + * } + * echo $content; + * + * + * E.g. Shortcut for the above + * + * HTTP_ConditionalGet::check($updateTime, true); // exits if client has cache + * echo $content; + * + * + * E.g. Content from DB with no update time: + * + * $content = getContentFromDB(); + * $cg = new HTTP_ConditionalGet(array( + * 'contentHash' => md5($content) + * )); + * $cg->sendHeaders(); + * if ($cg->cacheIsValid) { + * exit(); + * } + * echo $content; + * + * + * E.g. Static content with some static includes: + * + * // 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(); + * } + * + * @package Minify + * @subpackage HTTP + * @author Stephen Clay + */ +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: + * + * array( + * 'Cache-Control' => 'max-age=0, public' + * ,'ETag' => '"foobar"' + * ) + * + * + * @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 + * + * + * header('Expires: ' . HTTP_ConditionalGet::gmtdate($time)); + * + * + * @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; + } +} diff --git a/vendor/mrclay/minify/min/lib/HTTP/Encoder.php b/vendor/mrclay/minify/min/lib/HTTP/Encoder.php new file mode 100644 index 000000000..8f347793c --- /dev/null +++ b/vendor/mrclay/minify/min/lib/HTTP/Encoder.php @@ -0,0 +1,335 @@ + + * // Send a CSS file, compressed if possible + * $he = new HTTP_Encoder(array( + * 'content' => file_get_contents($cssFile) + * ,'type' => 'text/css' + * )); + * $he->encode(); + * $he->sendAll(); + * + * + * + * // Shortcut to encoding output + * header('Content-Type: text/css'); // needed if not HTML + * HTTP_Encoder::output($css); + * + * + * + * // Just sniff for the accepted encoding + * $encoding = HTTP_Encoder::getAcceptedEncoding(); + * + * + * 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 + */ +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. + * + * array( + * 'Content-Length' => '615' + * ,'Content-Encoding' => 'x-gzip' + * ,'Vary' => 'Accept-Encoding' + * ) + * + * + * @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; +} diff --git a/vendor/mrclay/minify/min/lib/JSMin.php b/vendor/mrclay/minify/min/lib/JSMin.php new file mode 100644 index 000000000..9840d8b33 --- /dev/null +++ b/vendor/mrclay/minify/min/lib/JSMin.php @@ -0,0 +1,449 @@ + + * $minifiedJs = JSMin::minify($js); + * + * + * 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 (PHP port) + * @author Steve Clay (modifications + cleanup) + * @author Andrea Giammarchi (spaceBeforeRegExp) + * @copyright 2002 Douglas Crockford (jsmin.c) + * @copyright 2008 Ryan Grove (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 {} diff --git a/vendor/mrclay/minify/min/lib/JSMinPlus.php b/vendor/mrclay/minify/min/lib/JSMinPlus.php new file mode 100644 index 000000000..5a3c5bdff --- /dev/null +++ b/vendor/mrclay/minify/min/lib/JSMinPlus.php @@ -0,0 +1,2086 @@ + + * + * Usage: $minified = JSMinPlus::minify($script [, $filename]) + * + * Versionlog (see also changelog.txt): + * 23-07-2011 - remove dynamic creation of OP_* and KEYWORD_* defines and declare them on top + * reduce memory footprint by minifying by block-scope + * some small byte-saving and performance improvements + * 12-05-2009 - fixed hook:colon precedence, fixed empty body in loop and if-constructs + * 18-04-2009 - fixed crashbug in PHP 5.2.9 and several other bugfixes + * 12-04-2009 - some small bugfixes and performance improvements + * 09-04-2009 - initial open sourced version 1.0 + * + * Latest version of this script: http://files.tweakers.net/jsminplus/jsminplus.zip + * + */ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Narcissus JavaScript engine. + * + * The Initial Developer of the Original Code is + * Brendan Eich . + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): Tino Zijdel + * PHP port, modifications and minifier routine are (C) 2009-2011 + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +define('TOKEN_END', 1); +define('TOKEN_NUMBER', 2); +define('TOKEN_IDENTIFIER', 3); +define('TOKEN_STRING', 4); +define('TOKEN_REGEXP', 5); +define('TOKEN_NEWLINE', 6); +define('TOKEN_CONDCOMMENT_START', 7); +define('TOKEN_CONDCOMMENT_END', 8); + +define('JS_SCRIPT', 100); +define('JS_BLOCK', 101); +define('JS_LABEL', 102); +define('JS_FOR_IN', 103); +define('JS_CALL', 104); +define('JS_NEW_WITH_ARGS', 105); +define('JS_INDEX', 106); +define('JS_ARRAY_INIT', 107); +define('JS_OBJECT_INIT', 108); +define('JS_PROPERTY_INIT', 109); +define('JS_GETTER', 110); +define('JS_SETTER', 111); +define('JS_GROUP', 112); +define('JS_LIST', 113); + +define('JS_MINIFIED', 999); + +define('DECLARED_FORM', 0); +define('EXPRESSED_FORM', 1); +define('STATEMENT_FORM', 2); + +/* Operators */ +define('OP_SEMICOLON', ';'); +define('OP_COMMA', ','); +define('OP_HOOK', '?'); +define('OP_COLON', ':'); +define('OP_OR', '||'); +define('OP_AND', '&&'); +define('OP_BITWISE_OR', '|'); +define('OP_BITWISE_XOR', '^'); +define('OP_BITWISE_AND', '&'); +define('OP_STRICT_EQ', '==='); +define('OP_EQ', '=='); +define('OP_ASSIGN', '='); +define('OP_STRICT_NE', '!=='); +define('OP_NE', '!='); +define('OP_LSH', '<<'); +define('OP_LE', '<='); +define('OP_LT', '<'); +define('OP_URSH', '>>>'); +define('OP_RSH', '>>'); +define('OP_GE', '>='); +define('OP_GT', '>'); +define('OP_INCREMENT', '++'); +define('OP_DECREMENT', '--'); +define('OP_PLUS', '+'); +define('OP_MINUS', '-'); +define('OP_MUL', '*'); +define('OP_DIV', '/'); +define('OP_MOD', '%'); +define('OP_NOT', '!'); +define('OP_BITWISE_NOT', '~'); +define('OP_DOT', '.'); +define('OP_LEFT_BRACKET', '['); +define('OP_RIGHT_BRACKET', ']'); +define('OP_LEFT_CURLY', '{'); +define('OP_RIGHT_CURLY', '}'); +define('OP_LEFT_PAREN', '('); +define('OP_RIGHT_PAREN', ')'); +define('OP_CONDCOMMENT_END', '@*/'); + +define('OP_UNARY_PLUS', 'U+'); +define('OP_UNARY_MINUS', 'U-'); + +/* Keywords */ +define('KEYWORD_BREAK', 'break'); +define('KEYWORD_CASE', 'case'); +define('KEYWORD_CATCH', 'catch'); +define('KEYWORD_CONST', 'const'); +define('KEYWORD_CONTINUE', 'continue'); +define('KEYWORD_DEBUGGER', 'debugger'); +define('KEYWORD_DEFAULT', 'default'); +define('KEYWORD_DELETE', 'delete'); +define('KEYWORD_DO', 'do'); +define('KEYWORD_ELSE', 'else'); +define('KEYWORD_ENUM', 'enum'); +define('KEYWORD_FALSE', 'false'); +define('KEYWORD_FINALLY', 'finally'); +define('KEYWORD_FOR', 'for'); +define('KEYWORD_FUNCTION', 'function'); +define('KEYWORD_IF', 'if'); +define('KEYWORD_IN', 'in'); +define('KEYWORD_INSTANCEOF', 'instanceof'); +define('KEYWORD_NEW', 'new'); +define('KEYWORD_NULL', 'null'); +define('KEYWORD_RETURN', 'return'); +define('KEYWORD_SWITCH', 'switch'); +define('KEYWORD_THIS', 'this'); +define('KEYWORD_THROW', 'throw'); +define('KEYWORD_TRUE', 'true'); +define('KEYWORD_TRY', 'try'); +define('KEYWORD_TYPEOF', 'typeof'); +define('KEYWORD_VAR', 'var'); +define('KEYWORD_VOID', 'void'); +define('KEYWORD_WHILE', 'while'); +define('KEYWORD_WITH', 'with'); + + +class JSMinPlus +{ + private $parser; + private $reserved = array( + 'break', 'case', 'catch', 'continue', 'default', 'delete', 'do', + 'else', 'finally', 'for', 'function', 'if', 'in', 'instanceof', + 'new', 'return', 'switch', 'this', 'throw', 'try', 'typeof', 'var', + 'void', 'while', 'with', + // Words reserved for future use + 'abstract', 'boolean', 'byte', 'char', 'class', 'const', 'debugger', + 'double', 'enum', 'export', 'extends', 'final', 'float', 'goto', + 'implements', 'import', 'int', 'interface', 'long', 'native', + 'package', 'private', 'protected', 'public', 'short', 'static', + 'super', 'synchronized', 'throws', 'transient', 'volatile', + // These are not reserved, but should be taken into account + // in isValidIdentifier (See jslint source code) + 'arguments', 'eval', 'true', 'false', 'Infinity', 'NaN', 'null', 'undefined' + ); + + private function __construct() + { + $this->parser = new JSParser($this); + } + + public static function minify($js, $filename='') + { + static $instance; + + // this is a singleton + if(!$instance) + $instance = new JSMinPlus(); + + return $instance->min($js, $filename); + } + + private function min($js, $filename) + { + try + { + $n = $this->parser->parse($js, $filename, 1); + return $this->parseTree($n); + } + catch(Exception $e) + { + echo $e->getMessage() . "\n"; + } + + return false; + } + + public function parseTree($n, $noBlockGrouping = false) + { + $s = ''; + + switch ($n->type) + { + case JS_MINIFIED: + $s = $n->value; + break; + + case JS_SCRIPT: + // we do nothing yet with funDecls or varDecls + $noBlockGrouping = true; + // FALL THROUGH + + case JS_BLOCK: + $childs = $n->treeNodes; + $lastType = 0; + for ($c = 0, $i = 0, $j = count($childs); $i < $j; $i++) + { + $type = $childs[$i]->type; + $t = $this->parseTree($childs[$i]); + if (strlen($t)) + { + if ($c) + { + $s = rtrim($s, ';'); + + if ($type == KEYWORD_FUNCTION && $childs[$i]->functionForm == DECLARED_FORM) + { + // put declared functions on a new line + $s .= "\n"; + } + elseif ($type == KEYWORD_VAR && $type == $lastType) + { + // mutiple var-statements can go into one + $t = ',' . substr($t, 4); + } + else + { + // add terminator + $s .= ';'; + } + } + + $s .= $t; + + $c++; + $lastType = $type; + } + } + + if ($c > 1 && !$noBlockGrouping) + { + $s = '{' . $s . '}'; + } + break; + + case KEYWORD_FUNCTION: + $s .= 'function' . ($n->name ? ' ' . $n->name : '') . '('; + $params = $n->params; + for ($i = 0, $j = count($params); $i < $j; $i++) + $s .= ($i ? ',' : '') . $params[$i]; + $s .= '){' . $this->parseTree($n->body, true) . '}'; + break; + + case KEYWORD_IF: + $s = 'if(' . $this->parseTree($n->condition) . ')'; + $thenPart = $this->parseTree($n->thenPart); + $elsePart = $n->elsePart ? $this->parseTree($n->elsePart) : null; + + // empty if-statement + if ($thenPart == '') + $thenPart = ';'; + + if ($elsePart) + { + // be carefull and always make a block out of the thenPart; could be more optimized but is a lot of trouble + if ($thenPart != ';' && $thenPart[0] != '{') + $thenPart = '{' . $thenPart . '}'; + + $s .= $thenPart . 'else'; + + // we could check for more, but that hardly ever applies so go for performance + if ($elsePart[0] != '{') + $s .= ' '; + + $s .= $elsePart; + } + else + { + $s .= $thenPart; + } + break; + + case KEYWORD_SWITCH: + $s = 'switch(' . $this->parseTree($n->discriminant) . '){'; + $cases = $n->cases; + for ($i = 0, $j = count($cases); $i < $j; $i++) + { + $case = $cases[$i]; + if ($case->type == KEYWORD_CASE) + $s .= 'case' . ($case->caseLabel->type != TOKEN_STRING ? ' ' : '') . $this->parseTree($case->caseLabel) . ':'; + else + $s .= 'default:'; + + $statement = $this->parseTree($case->statements, true); + if ($statement) + { + $s .= $statement; + // no terminator for last statement + if ($i + 1 < $j) + $s .= ';'; + } + } + $s .= '}'; + break; + + case KEYWORD_FOR: + $s = 'for(' . ($n->setup ? $this->parseTree($n->setup) : '') + . ';' . ($n->condition ? $this->parseTree($n->condition) : '') + . ';' . ($n->update ? $this->parseTree($n->update) : '') . ')'; + + $body = $this->parseTree($n->body); + if ($body == '') + $body = ';'; + + $s .= $body; + break; + + case KEYWORD_WHILE: + $s = 'while(' . $this->parseTree($n->condition) . ')'; + + $body = $this->parseTree($n->body); + if ($body == '') + $body = ';'; + + $s .= $body; + break; + + case JS_FOR_IN: + $s = 'for(' . ($n->varDecl ? $this->parseTree($n->varDecl) : $this->parseTree($n->iterator)) . ' in ' . $this->parseTree($n->object) . ')'; + + $body = $this->parseTree($n->body); + if ($body == '') + $body = ';'; + + $s .= $body; + break; + + case KEYWORD_DO: + $s = 'do{' . $this->parseTree($n->body, true) . '}while(' . $this->parseTree($n->condition) . ')'; + break; + + case KEYWORD_BREAK: + case KEYWORD_CONTINUE: + $s = $n->value . ($n->label ? ' ' . $n->label : ''); + break; + + case KEYWORD_TRY: + $s = 'try{' . $this->parseTree($n->tryBlock, true) . '}'; + $catchClauses = $n->catchClauses; + for ($i = 0, $j = count($catchClauses); $i < $j; $i++) + { + $t = $catchClauses[$i]; + $s .= 'catch(' . $t->varName . ($t->guard ? ' if ' . $this->parseTree($t->guard) : '') . '){' . $this->parseTree($t->block, true) . '}'; + } + if ($n->finallyBlock) + $s .= 'finally{' . $this->parseTree($n->finallyBlock, true) . '}'; + break; + + case KEYWORD_THROW: + case KEYWORD_RETURN: + $s = $n->type; + if ($n->value) + { + $t = $this->parseTree($n->value); + if (strlen($t)) + { + if ($this->isWordChar($t[0]) || $t[0] == '\\') + $s .= ' '; + + $s .= $t; + } + } + break; + + case KEYWORD_WITH: + $s = 'with(' . $this->parseTree($n->object) . ')' . $this->parseTree($n->body); + break; + + case KEYWORD_VAR: + case KEYWORD_CONST: + $s = $n->value . ' '; + $childs = $n->treeNodes; + for ($i = 0, $j = count($childs); $i < $j; $i++) + { + $t = $childs[$i]; + $s .= ($i ? ',' : '') . $t->name; + $u = $t->initializer; + if ($u) + $s .= '=' . $this->parseTree($u); + } + break; + + case KEYWORD_IN: + case KEYWORD_INSTANCEOF: + $left = $this->parseTree($n->treeNodes[0]); + $right = $this->parseTree($n->treeNodes[1]); + + $s = $left; + + if ($this->isWordChar(substr($left, -1))) + $s .= ' '; + + $s .= $n->type; + + if ($this->isWordChar($right[0]) || $right[0] == '\\') + $s .= ' '; + + $s .= $right; + break; + + case KEYWORD_DELETE: + case KEYWORD_TYPEOF: + $right = $this->parseTree($n->treeNodes[0]); + + $s = $n->type; + + if ($this->isWordChar($right[0]) || $right[0] == '\\') + $s .= ' '; + + $s .= $right; + break; + + case KEYWORD_VOID: + $s = 'void(' . $this->parseTree($n->treeNodes[0]) . ')'; + break; + + case KEYWORD_DEBUGGER: + throw new Exception('NOT IMPLEMENTED: DEBUGGER'); + break; + + case TOKEN_CONDCOMMENT_START: + case TOKEN_CONDCOMMENT_END: + $s = $n->value . ($n->type == TOKEN_CONDCOMMENT_START ? ' ' : ''); + $childs = $n->treeNodes; + for ($i = 0, $j = count($childs); $i < $j; $i++) + $s .= $this->parseTree($childs[$i]); + break; + + case OP_SEMICOLON: + if ($expression = $n->expression) + $s = $this->parseTree($expression); + break; + + case JS_LABEL: + $s = $n->label . ':' . $this->parseTree($n->statement); + break; + + case OP_COMMA: + $childs = $n->treeNodes; + for ($i = 0, $j = count($childs); $i < $j; $i++) + $s .= ($i ? ',' : '') . $this->parseTree($childs[$i]); + break; + + case OP_ASSIGN: + $s = $this->parseTree($n->treeNodes[0]) . $n->value . $this->parseTree($n->treeNodes[1]); + break; + + case OP_HOOK: + $s = $this->parseTree($n->treeNodes[0]) . '?' . $this->parseTree($n->treeNodes[1]) . ':' . $this->parseTree($n->treeNodes[2]); + break; + + case OP_OR: case OP_AND: + case OP_BITWISE_OR: case OP_BITWISE_XOR: case OP_BITWISE_AND: + case OP_EQ: case OP_NE: case OP_STRICT_EQ: case OP_STRICT_NE: + case OP_LT: case OP_LE: case OP_GE: case OP_GT: + case OP_LSH: case OP_RSH: case OP_URSH: + case OP_MUL: case OP_DIV: case OP_MOD: + $s = $this->parseTree($n->treeNodes[0]) . $n->type . $this->parseTree($n->treeNodes[1]); + break; + + case OP_PLUS: + case OP_MINUS: + $left = $this->parseTree($n->treeNodes[0]); + $right = $this->parseTree($n->treeNodes[1]); + + switch ($n->treeNodes[1]->type) + { + case OP_PLUS: + case OP_MINUS: + case OP_INCREMENT: + case OP_DECREMENT: + case OP_UNARY_PLUS: + case OP_UNARY_MINUS: + $s = $left . $n->type . ' ' . $right; + break; + + case TOKEN_STRING: + //combine concatted strings with same quotestyle + if ($n->type == OP_PLUS && substr($left, -1) == $right[0]) + { + $s = substr($left, 0, -1) . substr($right, 1); + break; + } + // FALL THROUGH + + default: + $s = $left . $n->type . $right; + } + break; + + case OP_NOT: + case OP_BITWISE_NOT: + case OP_UNARY_PLUS: + case OP_UNARY_MINUS: + $s = $n->value . $this->parseTree($n->treeNodes[0]); + break; + + case OP_INCREMENT: + case OP_DECREMENT: + if ($n->postfix) + $s = $this->parseTree($n->treeNodes[0]) . $n->value; + else + $s = $n->value . $this->parseTree($n->treeNodes[0]); + break; + + case OP_DOT: + $s = $this->parseTree($n->treeNodes[0]) . '.' . $this->parseTree($n->treeNodes[1]); + break; + + case JS_INDEX: + $s = $this->parseTree($n->treeNodes[0]); + // See if we can replace named index with a dot saving 3 bytes + if ( $n->treeNodes[0]->type == TOKEN_IDENTIFIER && + $n->treeNodes[1]->type == TOKEN_STRING && + $this->isValidIdentifier(substr($n->treeNodes[1]->value, 1, -1)) + ) + $s .= '.' . substr($n->treeNodes[1]->value, 1, -1); + else + $s .= '[' . $this->parseTree($n->treeNodes[1]) . ']'; + break; + + case JS_LIST: + $childs = $n->treeNodes; + for ($i = 0, $j = count($childs); $i < $j; $i++) + $s .= ($i ? ',' : '') . $this->parseTree($childs[$i]); + break; + + case JS_CALL: + $s = $this->parseTree($n->treeNodes[0]) . '(' . $this->parseTree($n->treeNodes[1]) . ')'; + break; + + case KEYWORD_NEW: + case JS_NEW_WITH_ARGS: + $s = 'new ' . $this->parseTree($n->treeNodes[0]) . '(' . ($n->type == JS_NEW_WITH_ARGS ? $this->parseTree($n->treeNodes[1]) : '') . ')'; + break; + + case JS_ARRAY_INIT: + $s = '['; + $childs = $n->treeNodes; + for ($i = 0, $j = count($childs); $i < $j; $i++) + { + $s .= ($i ? ',' : '') . $this->parseTree($childs[$i]); + } + $s .= ']'; + break; + + case JS_OBJECT_INIT: + $s = '{'; + $childs = $n->treeNodes; + for ($i = 0, $j = count($childs); $i < $j; $i++) + { + $t = $childs[$i]; + if ($i) + $s .= ','; + if ($t->type == JS_PROPERTY_INIT) + { + // Ditch the quotes when the index is a valid identifier + if ( $t->treeNodes[0]->type == TOKEN_STRING && + $this->isValidIdentifier(substr($t->treeNodes[0]->value, 1, -1)) + ) + $s .= substr($t->treeNodes[0]->value, 1, -1); + else + $s .= $t->treeNodes[0]->value; + + $s .= ':' . $this->parseTree($t->treeNodes[1]); + } + else + { + $s .= $t->type == JS_GETTER ? 'get' : 'set'; + $s .= ' ' . $t->name . '('; + $params = $t->params; + for ($i = 0, $j = count($params); $i < $j; $i++) + $s .= ($i ? ',' : '') . $params[$i]; + $s .= '){' . $this->parseTree($t->body, true) . '}'; + } + } + $s .= '}'; + break; + + case TOKEN_NUMBER: + $s = $n->value; + if (preg_match('/^([1-9]+)(0{3,})$/', $s, $m)) + $s = $m[1] . 'e' . strlen($m[2]); + break; + + case KEYWORD_NULL: case KEYWORD_THIS: case KEYWORD_TRUE: case KEYWORD_FALSE: + case TOKEN_IDENTIFIER: case TOKEN_STRING: case TOKEN_REGEXP: + $s = $n->value; + break; + + case JS_GROUP: + if (in_array( + $n->treeNodes[0]->type, + array( + JS_ARRAY_INIT, JS_OBJECT_INIT, JS_GROUP, + TOKEN_NUMBER, TOKEN_STRING, TOKEN_REGEXP, TOKEN_IDENTIFIER, + KEYWORD_NULL, KEYWORD_THIS, KEYWORD_TRUE, KEYWORD_FALSE + ) + )) + { + $s = $this->parseTree($n->treeNodes[0]); + } + else + { + $s = '(' . $this->parseTree($n->treeNodes[0]) . ')'; + } + break; + + default: + throw new Exception('UNKNOWN TOKEN TYPE: ' . $n->type); + } + + return $s; + } + + private function isValidIdentifier($string) + { + return preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', $string) && !in_array($string, $this->reserved); + } + + private function isWordChar($char) + { + return $char == '_' || $char == '$' || ctype_alnum($char); + } +} + +class JSParser +{ + private $t; + private $minifier; + + private $opPrecedence = array( + ';' => 0, + ',' => 1, + '=' => 2, '?' => 2, ':' => 2, + // The above all have to have the same precedence, see bug 330975 + '||' => 4, + '&&' => 5, + '|' => 6, + '^' => 7, + '&' => 8, + '==' => 9, '!=' => 9, '===' => 9, '!==' => 9, + '<' => 10, '<=' => 10, '>=' => 10, '>' => 10, 'in' => 10, 'instanceof' => 10, + '<<' => 11, '>>' => 11, '>>>' => 11, + '+' => 12, '-' => 12, + '*' => 13, '/' => 13, '%' => 13, + 'delete' => 14, 'void' => 14, 'typeof' => 14, + '!' => 14, '~' => 14, 'U+' => 14, 'U-' => 14, + '++' => 15, '--' => 15, + 'new' => 16, + '.' => 17, + JS_NEW_WITH_ARGS => 0, JS_INDEX => 0, JS_CALL => 0, + JS_ARRAY_INIT => 0, JS_OBJECT_INIT => 0, JS_GROUP => 0 + ); + + private $opArity = array( + ',' => -2, + '=' => 2, + '?' => 3, + '||' => 2, + '&&' => 2, + '|' => 2, + '^' => 2, + '&' => 2, + '==' => 2, '!=' => 2, '===' => 2, '!==' => 2, + '<' => 2, '<=' => 2, '>=' => 2, '>' => 2, 'in' => 2, 'instanceof' => 2, + '<<' => 2, '>>' => 2, '>>>' => 2, + '+' => 2, '-' => 2, + '*' => 2, '/' => 2, '%' => 2, + 'delete' => 1, 'void' => 1, 'typeof' => 1, + '!' => 1, '~' => 1, 'U+' => 1, 'U-' => 1, + '++' => 1, '--' => 1, + 'new' => 1, + '.' => 2, + JS_NEW_WITH_ARGS => 2, JS_INDEX => 2, JS_CALL => 2, + JS_ARRAY_INIT => 1, JS_OBJECT_INIT => 1, JS_GROUP => 1, + TOKEN_CONDCOMMENT_START => 1, TOKEN_CONDCOMMENT_END => 1 + ); + + public function __construct($minifier=null) + { + $this->minifier = $minifier; + $this->t = new JSTokenizer(); + } + + public function parse($s, $f, $l) + { + // initialize tokenizer + $this->t->init($s, $f, $l); + + $x = new JSCompilerContext(false); + $n = $this->Script($x); + if (!$this->t->isDone()) + throw $this->t->newSyntaxError('Syntax error'); + + return $n; + } + + private function Script($x) + { + $n = $this->Statements($x); + $n->type = JS_SCRIPT; + $n->funDecls = $x->funDecls; + $n->varDecls = $x->varDecls; + + // minify by scope + if ($this->minifier) + { + $n->value = $this->minifier->parseTree($n); + + // clear tree from node to save memory + $n->treeNodes = null; + $n->funDecls = null; + $n->varDecls = null; + + $n->type = JS_MINIFIED; + } + + return $n; + } + + private function Statements($x) + { + $n = new JSNode($this->t, JS_BLOCK); + array_push($x->stmtStack, $n); + + while (!$this->t->isDone() && $this->t->peek() != OP_RIGHT_CURLY) + $n->addNode($this->Statement($x)); + + array_pop($x->stmtStack); + + return $n; + } + + private function Block($x) + { + $this->t->mustMatch(OP_LEFT_CURLY); + $n = $this->Statements($x); + $this->t->mustMatch(OP_RIGHT_CURLY); + + return $n; + } + + private function Statement($x) + { + $tt = $this->t->get(); + $n2 = null; + + // Cases for statements ending in a right curly return early, avoiding the + // common semicolon insertion magic after this switch. + switch ($tt) + { + case KEYWORD_FUNCTION: + return $this->FunctionDefinition( + $x, + true, + count($x->stmtStack) > 1 ? STATEMENT_FORM : DECLARED_FORM + ); + break; + + case OP_LEFT_CURLY: + $n = $this->Statements($x); + $this->t->mustMatch(OP_RIGHT_CURLY); + return $n; + + case KEYWORD_IF: + $n = new JSNode($this->t); + $n->condition = $this->ParenExpression($x); + array_push($x->stmtStack, $n); + $n->thenPart = $this->Statement($x); + $n->elsePart = $this->t->match(KEYWORD_ELSE) ? $this->Statement($x) : null; + array_pop($x->stmtStack); + return $n; + + case KEYWORD_SWITCH: + $n = new JSNode($this->t); + $this->t->mustMatch(OP_LEFT_PAREN); + $n->discriminant = $this->Expression($x); + $this->t->mustMatch(OP_RIGHT_PAREN); + $n->cases = array(); + $n->defaultIndex = -1; + + array_push($x->stmtStack, $n); + + $this->t->mustMatch(OP_LEFT_CURLY); + + while (($tt = $this->t->get()) != OP_RIGHT_CURLY) + { + switch ($tt) + { + case KEYWORD_DEFAULT: + if ($n->defaultIndex >= 0) + throw $this->t->newSyntaxError('More than one switch default'); + // FALL THROUGH + case KEYWORD_CASE: + $n2 = new JSNode($this->t); + if ($tt == KEYWORD_DEFAULT) + $n->defaultIndex = count($n->cases); + else + $n2->caseLabel = $this->Expression($x, OP_COLON); + break; + default: + throw $this->t->newSyntaxError('Invalid switch case'); + } + + $this->t->mustMatch(OP_COLON); + $n2->statements = new JSNode($this->t, JS_BLOCK); + while (($tt = $this->t->peek()) != KEYWORD_CASE && $tt != KEYWORD_DEFAULT && $tt != OP_RIGHT_CURLY) + $n2->statements->addNode($this->Statement($x)); + + array_push($n->cases, $n2); + } + + array_pop($x->stmtStack); + return $n; + + case KEYWORD_FOR: + $n = new JSNode($this->t); + $n->isLoop = true; + $this->t->mustMatch(OP_LEFT_PAREN); + + if (($tt = $this->t->peek()) != OP_SEMICOLON) + { + $x->inForLoopInit = true; + if ($tt == KEYWORD_VAR || $tt == KEYWORD_CONST) + { + $this->t->get(); + $n2 = $this->Variables($x); + } + else + { + $n2 = $this->Expression($x); + } + $x->inForLoopInit = false; + } + + if ($n2 && $this->t->match(KEYWORD_IN)) + { + $n->type = JS_FOR_IN; + if ($n2->type == KEYWORD_VAR) + { + if (count($n2->treeNodes) != 1) + { + throw $this->t->SyntaxError( + 'Invalid for..in left-hand side', + $this->t->filename, + $n2->lineno + ); + } + + // NB: n2[0].type == IDENTIFIER and n2[0].value == n2[0].name. + $n->iterator = $n2->treeNodes[0]; + $n->varDecl = $n2; + } + else + { + $n->iterator = $n2; + $n->varDecl = null; + } + + $n->object = $this->Expression($x); + } + else + { + $n->setup = $n2 ? $n2 : null; + $this->t->mustMatch(OP_SEMICOLON); + $n->condition = $this->t->peek() == OP_SEMICOLON ? null : $this->Expression($x); + $this->t->mustMatch(OP_SEMICOLON); + $n->update = $this->t->peek() == OP_RIGHT_PAREN ? null : $this->Expression($x); + } + + $this->t->mustMatch(OP_RIGHT_PAREN); + $n->body = $this->nest($x, $n); + return $n; + + case KEYWORD_WHILE: + $n = new JSNode($this->t); + $n->isLoop = true; + $n->condition = $this->ParenExpression($x); + $n->body = $this->nest($x, $n); + return $n; + + case KEYWORD_DO: + $n = new JSNode($this->t); + $n->isLoop = true; + $n->body = $this->nest($x, $n, KEYWORD_WHILE); + $n->condition = $this->ParenExpression($x); + if (!$x->ecmaStrictMode) + { + // "; + * $link = ""; + * + * // in min.php + * Minify::serve('Groups', array( + * 'groups' => $groupSources + * ,'setExpires' => (time() + 86400 * 365) + * )); + * + * + * @package Minify + * @author Stephen Clay + */ +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 + * + * + * echo $b->uri('/site.js'); + * // outputs "/site.js?1678242" + * + * echo $b->uri('/scriptaculous.js?load=effects'); + * // outputs "/scriptaculous.js?load=effects&1678242" + * + * + * @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; + } +} diff --git a/vendor/mrclay/minify/min/lib/Minify/CSS.php b/vendor/mrclay/minify/min/lib/Minify/CSS.php new file mode 100644 index 000000000..32414551d --- /dev/null +++ b/vendor/mrclay/minify/min/lib/Minify/CSS.php @@ -0,0 +1,99 @@ + + * @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.: + * + * array('//symlink' => '/real/target/path') // unix + * array('//static' => 'D:\\staticStorage') // Windows + * + * + * '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'] + ); + } + } +} diff --git a/vendor/mrclay/minify/min/lib/Minify/CSS/Compressor.php b/vendor/mrclay/minify/min/lib/Minify/CSS/Compressor.php new file mode 100644 index 000000000..c6cdd8b7a --- /dev/null +++ b/vendor/mrclay/minify/min/lib/Minify/CSS/Compressor.php @@ -0,0 +1,249 @@ + + * @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]; + } +} diff --git a/vendor/mrclay/minify/min/lib/Minify/CSS/UriRewriter.php b/vendor/mrclay/minify/min/lib/Minify/CSS/UriRewriter.php new file mode 100644 index 000000000..50360ce7e --- /dev/null +++ b/vendor/mrclay/minify/min/lib/Minify/CSS/UriRewriter.php @@ -0,0 +1,307 @@ + + */ +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.: + * + * array('//symlink' => '/real/target/path') // unix + * array('//static' => 'D:\\staticStorage') // Windows + * + * + * @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 + * + * + * 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' + * + * + * @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.: + * + * array('/home/foo/www/not/real/path' => '/real/target/path') // unix + * array('C:\\htdocs\\not\\real' => 'D:\\real\\target\\path') // Windows + * + * + * @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})"; + } +} diff --git a/vendor/mrclay/minify/min/lib/Minify/CSSmin.php b/vendor/mrclay/minify/min/lib/Minify/CSSmin.php new file mode 100644 index 000000000..440338387 --- /dev/null +++ b/vendor/mrclay/minify/min/lib/Minify/CSSmin.php @@ -0,0 +1,85 @@ + + */ +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.: + * + * array('//symlink' => '/real/target/path') // unix + * array('//static' => 'D:\\staticStorage') // Windows + * + * + * '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'] + ); + } + } +} diff --git a/vendor/mrclay/minify/min/lib/Minify/Cache/APC.php b/vendor/mrclay/minify/min/lib/Minify/Cache/APC.php new file mode 100644 index 000000000..24ab04620 --- /dev/null +++ b/vendor/mrclay/minify/min/lib/Minify/Cache/APC.php @@ -0,0 +1,133 @@ + + * Minify::setCache(new Minify_Cache_APC()); + * + * + * @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; + } +} diff --git a/vendor/mrclay/minify/min/lib/Minify/Cache/File.php b/vendor/mrclay/minify/min/lib/Minify/Cache/File.php new file mode 100644 index 000000000..27b424a6a --- /dev/null +++ b/vendor/mrclay/minify/min/lib/Minify/Cache/File.php @@ -0,0 +1,194 @@ +_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'); + 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 + * @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 + * @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; +} diff --git a/vendor/mrclay/minify/min/lib/Minify/Cache/Memcache.php b/vendor/mrclay/minify/min/lib/Minify/Cache/Memcache.php new file mode 100644 index 000000000..72bf454b9 --- /dev/null +++ b/vendor/mrclay/minify/min/lib/Minify/Cache/Memcache.php @@ -0,0 +1,140 @@ + + * // 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(); + * } + * + **/ +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; + } +} diff --git a/vendor/mrclay/minify/min/lib/Minify/Cache/XCache.php b/vendor/mrclay/minify/min/lib/Minify/Cache/XCache.php new file mode 100644 index 000000000..3039deddc --- /dev/null +++ b/vendor/mrclay/minify/min/lib/Minify/Cache/XCache.php @@ -0,0 +1,126 @@ + + * Minify::setCache(new Minify_Cache_XCache()); + * + * + * @package Minify + * @author Elan Ruusamäe + **/ +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; + } +} diff --git a/vendor/mrclay/minify/min/lib/Minify/Cache/ZendPlatform.php b/vendor/mrclay/minify/min/lib/Minify/Cache/ZendPlatform.php new file mode 100644 index 000000000..3130d69a7 --- /dev/null +++ b/vendor/mrclay/minify/min/lib/Minify/Cache/ZendPlatform.php @@ -0,0 +1,142 @@ + + * Minify::setCache(new Minify_Cache_ZendPlatform()); + * + * + * @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; + } +} diff --git a/vendor/mrclay/minify/min/lib/Minify/ClosureCompiler.php b/vendor/mrclay/minify/min/lib/Minify/ClosureCompiler.php new file mode 100644 index 000000000..bdcdc7ee9 --- /dev/null +++ b/vendor/mrclay/minify/min/lib/Minify/ClosureCompiler.php @@ -0,0 +1,123 @@ + + * 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 + * + * + * + * @todo unit tests, $options docs + * @todo more options support (or should just passthru them all?) + * + * @package Minify + * @author Stephen Clay + * @author Elan Ruusamäe + */ +class Minify_ClosureCompiler { + + /** + * 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 + */ + public static function minify($js, $options = array()) + { + self::_prepare(); + if (! ($tmpFile = tempnam(self::$tempDir, 'cc_'))) { + throw new Exception('Minify_ClosureCompiler : could not create temp file in "'.self::$tempDir.'".'); + } + file_put_contents($tmpFile, $js); + exec(self::_getCmd($options, $tmpFile), $output, $result_code); + unlink($tmpFile); + if ($result_code != 0) { + throw new Exception('Minify_ClosureCompiler : Closure Compiler execution failed.'); + } + return implode("\n", $output); + } + + private static function _getCmd($userOptions, $tmpFile) + { + $o = array_merge( + array( + 'charset' => 'utf-8', + 'compilation_level' => 'SIMPLE_OPTIMIZATIONS', + ), + $userOptions + ); + $cmd = self::$javaExecutable . ' -jar ' . escapeshellarg(self::$jarFile) + . (preg_match('/^[\\da-zA-Z0-9\\-]+$/', $o['charset']) + ? " --charset {$o['charset']}" + : ''); + + foreach (array('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 Exception('Minify_ClosureCompiler : $jarFile('.self::$jarFile.') is not a valid file.'); + } + if (! is_readable(self::$jarFile)) { + throw new Exception('Minify_ClosureCompiler : $jarFile('.self::$jarFile.') is not readable.'); + } + if (! is_dir(self::$tempDir)) { + throw new Exception('Minify_ClosureCompiler : $tempDir('.self::$tempDir.') is not a valid direcotry.'); + } + if (! is_writable(self::$tempDir)) { + throw new Exception('Minify_ClosureCompiler : $tempDir('.self::$tempDir.') is not writable.'); + } + } +} + +/* vim:ts=4:sw=4:et */ diff --git a/vendor/mrclay/minify/min/lib/Minify/CommentPreserver.php b/vendor/mrclay/minify/min/lib/Minify/CommentPreserver.php new file mode 100644 index 000000000..7a359bf9b --- /dev/null +++ b/vendor/mrclay/minify/min/lib/Minify/CommentPreserver.php @@ -0,0 +1,89 @@ + + */ +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; + } +} diff --git a/vendor/mrclay/minify/min/lib/Minify/Controller/Base.php b/vendor/mrclay/minify/min/lib/Minify/Controller/Base.php new file mode 100644 index 000000000..5a8632903 --- /dev/null +++ b/vendor/mrclay/minify/min/lib/Minify/Controller/Base.php @@ -0,0 +1,222 @@ + + */ +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); + } +} diff --git a/vendor/mrclay/minify/min/lib/Minify/Controller/Files.php b/vendor/mrclay/minify/min/lib/Minify/Controller/Files.php new file mode 100644 index 000000000..f084cd07e --- /dev/null +++ b/vendor/mrclay/minify/min/lib/Minify/Controller/Files.php @@ -0,0 +1,76 @@ + + * Minify::serve('Files', array( + * 'files' => array( + * '//js/jquery.js' + * ,'//js/plugins.js' + * ,'/home/username/file.js' + * ) + * )); + * + * + * As a shortcut, the controller will replace "//" at the beginning + * of a filename with $_SERVER['DOCUMENT_ROOT'] . '/'. + * + * @package Minify + * @author Stephen Clay + */ +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; + } +} + diff --git a/vendor/mrclay/minify/min/lib/Minify/Controller/Groups.php b/vendor/mrclay/minify/min/lib/Minify/Controller/Groups.php new file mode 100644 index 000000000..c4c25db12 --- /dev/null +++ b/vendor/mrclay/minify/min/lib/Minify/Controller/Groups.php @@ -0,0 +1,91 @@ + + * Minify::serve('Groups', array( + * 'groups' => array( + * 'css' => array('//css/type.css', '//css/layout.css') + * ,'js' => array('//js/jquery.js', '//js/site.js') + * ) + * )); + * + * + * 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 + */ +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; + } +} + diff --git a/vendor/mrclay/minify/min/lib/Minify/Controller/MinApp.php b/vendor/mrclay/minify/min/lib/Minify/Controller/MinApp.php new file mode 100644 index 000000000..6943ee6bf --- /dev/null +++ b/vendor/mrclay/minify/min/lib/Minify/Controller/MinApp.php @@ -0,0 +1,238 @@ + + */ +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'); + } + } +} diff --git a/vendor/mrclay/minify/min/lib/Minify/Controller/Page.php b/vendor/mrclay/minify/min/lib/Minify/Controller/Page.php new file mode 100644 index 000000000..1095fb46f --- /dev/null +++ b/vendor/mrclay/minify/min/lib/Minify/Controller/Page.php @@ -0,0 +1,68 @@ + + */ +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; + } +} + diff --git a/vendor/mrclay/minify/min/lib/Minify/Controller/Version1.php b/vendor/mrclay/minify/min/lib/Minify/Controller/Version1.php new file mode 100644 index 000000000..91fcf6144 --- /dev/null +++ b/vendor/mrclay/minify/min/lib/Minify/Controller/Version1.php @@ -0,0 +1,119 @@ + + * Minify::serve('Version1'); + * + * + * @package Minify + * @author Stephen Clay + */ +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); + } + } + } +} + diff --git a/vendor/mrclay/minify/min/lib/Minify/DebugDetector.php b/vendor/mrclay/minify/min/lib/Minify/DebugDetector.php new file mode 100644 index 000000000..1def97484 --- /dev/null +++ b/vendor/mrclay/minify/min/lib/Minify/DebugDetector.php @@ -0,0 +1,26 @@ + + */ +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; + } +} diff --git a/vendor/mrclay/minify/min/lib/Minify/HTML.php b/vendor/mrclay/minify/min/lib/Minify/HTML.php new file mode 100644 index 000000000..40f730761 --- /dev/null +++ b/vendor/mrclay/minify/min/lib/Minify/HTML.php @@ -0,0 +1,255 @@ + + */ +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, '_replacementHash = 'MINIFYHTML' . md5($_SERVER['REQUEST_TIME']); + $this->_placeholders = array(); + + // replace SCRIPTs (and minify) with placeholders + $this->_html = preg_replace_callback( + '/(\\s*)]*?>)([\\s\\S]*?)<\\/script>(\\s*)/i' + ,array($this, '_removeScriptCB') + ,$this->_html); + + // replace STYLEs (and minify) with placeholders + $this->_html = preg_replace_callback( + '/\\s*]*>)([\\s\\S]*?)<\\/style>\\s*/i' + ,array($this, '_removeStyleCB') + ,$this->_html); + + // remove HTML comments (not containing IE conditional comments). + $this->_html = preg_replace_callback( + '//' + ,array($this, '_commentCB') + ,$this->_html); + + // replace PREs with placeholders + $this->_html = preg_replace_callback('/\\s*]*?>[\\s\\S]*?<\\/pre>)\\s*/i' + ,array($this, '_removePreCB') + ,$this->_html); + + // replace TEXTAREAs with placeholders + $this->_html = preg_replace_callback( + '/\\s*]*?>[\\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], '_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("_reservePlace("\\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}/**/" + : "{$openStyle}{$css}" + ); + } + + protected function _removeScriptCB($m) + { + $openScript = "_jsCleanComments) { + $js = preg_replace('/(?:^\\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}/**/{$ws2}" + : "{$ws1}{$openScript}{$js}{$ws2}" + ); + } + + protected function _removeCdata($str) + { + return (false !== strpos($str, ''), '', $str) + : $str; + } + + protected function _needsCdata($str) + { + return ($this->_isXhtml && preg_match('/(?:[<&]|\\-\\-|\\]\\]>)/', $str)); + } +} diff --git a/vendor/mrclay/minify/min/lib/Minify/HTML/Helper.php b/vendor/mrclay/minify/min/lib/Minify/HTML/Helper.php new file mode 100644 index 000000000..f92ab854c --- /dev/null +++ b/vendor/mrclay/minify/min/lib/Minify/HTML/Helper.php @@ -0,0 +1,225 @@ + + */ +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= + 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; + } +} diff --git a/vendor/mrclay/minify/min/lib/Minify/ImportProcessor.php b/vendor/mrclay/minify/min/lib/Minify/ImportProcessor.php new file mode 100644 index 000000000..bdfae547d --- /dev/null +++ b/vendor/mrclay/minify/min/lib/Minify/ImportProcessor.php @@ -0,0 +1,216 @@ + + * @author Simon Schick + */ +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; + } +} diff --git a/vendor/mrclay/minify/min/lib/Minify/JS/ClosureCompiler.php b/vendor/mrclay/minify/min/lib/Minify/JS/ClosureCompiler.php new file mode 100644 index 000000000..e067d7c85 --- /dev/null +++ b/vendor/mrclay/minify/min/lib/Minify/JS/ClosureCompiler.php @@ -0,0 +1,230 @@ + + * + * @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 {} diff --git a/vendor/mrclay/minify/min/lib/Minify/Lines.php b/vendor/mrclay/minify/min/lib/Minify/Lines.php new file mode 100644 index 000000000..e999a6c2b --- /dev/null +++ b/vendor/mrclay/minify/min/lib/Minify/Lines.php @@ -0,0 +1,143 @@ + + * @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; + } +} diff --git a/vendor/mrclay/minify/min/lib/Minify/Loader.php b/vendor/mrclay/minify/min/lib/Minify/Loader.php new file mode 100644 index 000000000..0a225c056 --- /dev/null +++ b/vendor/mrclay/minify/min/lib/Minify/Loader.php @@ -0,0 +1,28 @@ + + */ +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')); + } +} diff --git a/vendor/mrclay/minify/min/lib/Minify/Logger.php b/vendor/mrclay/minify/min/lib/Minify/Logger.php new file mode 100644 index 000000000..8eb72f452 --- /dev/null +++ b/vendor/mrclay/minify/min/lib/Minify/Logger.php @@ -0,0 +1,47 @@ + + * + * @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; +} diff --git a/vendor/mrclay/minify/min/lib/Minify/Packer.php b/vendor/mrclay/minify/min/lib/Minify/Packer.php new file mode 100644 index 000000000..949c3eef0 --- /dev/null +++ b/vendor/mrclay/minify/min/lib/Minify/Packer.php @@ -0,0 +1,37 @@ +pack()); + } +} diff --git a/vendor/mrclay/minify/min/lib/Minify/Source.php b/vendor/mrclay/minify/min/lib/Minify/Source.php new file mode 100644 index 000000000..5a85d10d0 --- /dev/null +++ b/vendor/mrclay/minify/min/lib/Minify/Source.php @@ -0,0 +1,187 @@ + + */ +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; +} + diff --git a/vendor/mrclay/minify/min/lib/Minify/YUI/CssCompressor.java b/vendor/mrclay/minify/min/lib/Minify/YUI/CssCompressor.java new file mode 100644 index 000000000..3fb497a96 --- /dev/null +++ b/vendor/mrclay/minify/min/lib/Minify/YUI/CssCompressor.java @@ -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); + } +} diff --git a/vendor/mrclay/minify/min/lib/Minify/YUI/CssCompressor.php b/vendor/mrclay/minify/min/lib/Minify/YUI/CssCompressor.php new file mode 100644 index 000000000..ae3443d44 --- /dev/null +++ b/vendor/mrclay/minify/min/lib/Minify/YUI/CssCompressor.php @@ -0,0 +1,171 @@ ++\\(\\)\\],])@", "$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]; + } + } +} \ No newline at end of file diff --git a/vendor/mrclay/minify/min/lib/Minify/YUICompressor.php b/vendor/mrclay/minify/min/lib/Minify/YUICompressor.php new file mode 100644 index 000000000..5762e890c --- /dev/null +++ b/vendor/mrclay/minify/min/lib/Minify/YUICompressor.php @@ -0,0 +1,156 @@ + + * 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) + * ); + * + * + * 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 + */ +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.'); + } + } +} + diff --git a/vendor/mrclay/minify/min/lib/MrClay/Cli.php b/vendor/mrclay/minify/min/lib/MrClay/Cli.php new file mode 100644 index 000000000..9aa8e06f2 --- /dev/null +++ b/vendor/mrclay/minify/min/lib/MrClay/Cli.php @@ -0,0 +1,384 @@ +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 + * @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); + } +} + diff --git a/vendor/mrclay/minify/min/lib/MrClay/Cli/Arg.php b/vendor/mrclay/minify/min/lib/MrClay/Cli/Arg.php new file mode 100644 index 000000000..5fa593273 --- /dev/null +++ b/vendor/mrclay/minify/min/lib/MrClay/Cli/Arg.php @@ -0,0 +1,183 @@ +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 + * @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; + } +} diff --git a/vendor/pimple/pimple/CHANGELOG b/vendor/pimple/pimple/CHANGELOG new file mode 100644 index 000000000..776a9153e --- /dev/null +++ b/vendor/pimple/pimple/CHANGELOG @@ -0,0 +1,25 @@ +* 3.0.0 (2014-07-24) + + * removed the Pimple class alias (use Pimple\Container instead) + +* 2.1.1 (2014-07-24) + + * fixed compiler warnings for the C extension + * fixed code when dealing with circular references + +* 2.1.0 (2014-06-24) + + * moved the Pimple to Pimple\Container (with a BC layer -- Pimple is now a + deprecated alias which will be removed in Pimple 3.0) + * added Pimple\ServiceProviderInterface (and Pimple::register()) + +* 2.0.0 (2014-02-10) + + * changed extend to automatically re-assign the extended service and keep it as shared or factory + (to keep BC, extend still returns the extended service) + * changed services to be shared by default (use factory() for factory + services) + +* 1.0.0 + + * initial version diff --git a/vendor/pimple/pimple/LICENSE b/vendor/pimple/pimple/LICENSE new file mode 100644 index 000000000..86b4721be --- /dev/null +++ b/vendor/pimple/pimple/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2009-2014 Fabien Potencier + +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. diff --git a/vendor/pimple/pimple/README.rst b/vendor/pimple/pimple/README.rst new file mode 100644 index 000000000..c345b6a11 --- /dev/null +++ b/vendor/pimple/pimple/README.rst @@ -0,0 +1,200 @@ +Pimple +====== + +.. caution:: + + This is the documentation for Pimple 3.x. If you are using Pimple 1.x, read + the `Pimple 1.x documentation`_. Reading the Pimple 1.x code is also a good + way to learn more about how to create a simple Dependency Injection + Container (recent versions of Pimple are more focused on performance). + +Pimple is a small Dependency Injection Container for PHP. + +Installation +------------ + +Before using Pimple in your project, add it to your ``composer.json`` file: + +.. code-block:: bash + + $ ./composer.phar require pimple/pimple ~3.0 + +Alternatively, Pimple is also available as a PHP C extension: + +.. code-block:: bash + + $ cd ext/pimple + $ phpize + $ ./configure + $ make + $ make install + +Usage +----- + +Creating a container is a matter of creating a ``Container`` instance: + +.. code-block:: php + + use Pimple\Container; + + $container = new Container(); + +As many other dependency injection containers, Pimple manages two different +kind of data: **services** and **parameters**. + +Defining Services +~~~~~~~~~~~~~~~~~ + +A service is an object that does something as part of a larger system. Examples +of services: a database connection, a templating engine, or a mailer. Almost +any **global** object can be a service. + +Services are defined by **anonymous functions** that return an instance of an +object: + +.. code-block:: php + + // define some services + $container['session_storage'] = function ($c) { + return new SessionStorage('SESSION_ID'); + }; + + $container['session'] = function ($c) { + return new Session($c['session_storage']); + }; + +Notice that the anonymous function has access to the current container +instance, allowing references to other services or parameters. + +As objects are only created when you get them, the order of the definitions +does not matter. + +Using the defined services is also very easy: + +.. code-block:: php + + // get the session object + $session = $container['session']; + + // the above call is roughly equivalent to the following code: + // $storage = new SessionStorage('SESSION_ID'); + // $session = new Session($storage); + +Defining Factory Services +~~~~~~~~~~~~~~~~~~~~~~~~~ + +By default, each time you get a service, Pimple returns the **same instance** +of it. If you want a different instance to be returned for all calls, wrap your +anonymous function with the ``factory()`` method + +.. code-block:: php + + $container['session'] = $container->factory(function ($c) { + return new Session($c['session_storage']); + }); + +Now, each call to ``$container['session']`` returns a new instance of the +session. + +Defining Parameters +~~~~~~~~~~~~~~~~~~~ + +Defining a parameter allows to ease the configuration of your container from +the outside and to store global values: + +.. code-block:: php + + // define some parameters + $container['cookie_name'] = 'SESSION_ID'; + $container['session_storage_class'] = 'SessionStorage'; + +If you change the ``session_storage`` service definition like below: + +.. code-block:: php + + $container['session_storage'] = function ($c) { + return new $c['session_storage_class']($c['cookie_name']); + }; + +You can now easily change the cookie name by overriding the +``session_storage_class`` parameter instead of redefining the service +definition. + +Protecting Parameters +~~~~~~~~~~~~~~~~~~~~~ + +Because Pimple sees anonymous functions as service definitions, you need to +wrap anonymous functions with the ``protect()`` method to store them as +parameters: + +.. code-block:: php + + $container['random_func'] = $container->protect(function () { + return rand(); + }); + +Modifying Services after Definition +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In some cases you may want to modify a service definition after it has been +defined. You can use the ``extend()`` method to define additional code to be +run on your service just after it is created: + +.. code-block:: php + + $container['session_storage'] = function ($c) { + return new $c['session_storage_class']($c['cookie_name']); + }; + + $container->extend('session_storage', function ($storage, $c) { + $storage->...(); + + return $storage; + }; + +The first argument is the name of the service to extend, the second a function +that gets access to the object instance and the container. + +Extending a Container +~~~~~~~~~~~~~~~~~~~~~ + +If you use the same libraries over and over, you might want to reuse some +services from one project to the next one; package your services into a +**provider** by implementing ``Pimple\ServiceProviderInterface``: + +.. code-block:: php + + use Pimple\Container; + + class FooProvider implements Pimple\ServiceProviderInterface + { + public function register(Container $pimple) + { + // register some services and parameters + // on $pimple + } + } + +Then, register the provider on a Container: + +.. code-block:: php + + $pimple->register(new FooProvider()); + +Fetching the Service Creation Function +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When you access an object, Pimple automatically calls the anonymous function +that you defined, which creates the service object for you. If you want to get +raw access to this function, you can use the ``raw()`` method: + +.. code-block:: php + + $container['session'] = function ($c) { + return new Session($c['session_storage']); + }; + + $sessionFunction = $container->raw('session'); + +.. _Pimple 1.x documentation: https://github.com/fabpot/Pimple/tree/1.1 diff --git a/vendor/pimple/pimple/src/Pimple/Container.php b/vendor/pimple/pimple/src/Pimple/Container.php new file mode 100644 index 000000000..26edefc9d --- /dev/null +++ b/vendor/pimple/pimple/src/Pimple/Container.php @@ -0,0 +1,281 @@ +factories = new \SplObjectStorage(); + $this->protected = new \SplObjectStorage(); + + foreach ($values as $key => $value) { + $this->offsetSet($key, $value); + } + } + + /** + * Sets a parameter or an object. + * + * Objects must be defined as Closures. + * + * Allowing any PHP callable leads to difficult to debug problems + * as function names (strings) are callable (creating a function with + * the same name as an existing parameter would break your container). + * + * @param string $id The unique identifier for the parameter or object + * @param mixed $value The value of the parameter or a closure to define an object + * @throws \RuntimeException Prevent override of a frozen service + */ + public function offsetSet($id, $value) + { + if (isset($this->frozen[$id])) { + throw new \RuntimeException(sprintf('Cannot override frozen service "%s".', $id)); + } + + $this->values[$id] = $value; + $this->keys[$id] = true; + } + + /** + * Gets a parameter or an object. + * + * @param string $id The unique identifier for the parameter or object + * + * @return mixed The value of the parameter or an object + * + * @throws \InvalidArgumentException if the identifier is not defined + */ + public function offsetGet($id) + { + if (!isset($this->keys[$id])) { + throw new \InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id)); + } + + if ( + isset($this->raw[$id]) + || !is_object($this->values[$id]) + || isset($this->protected[$this->values[$id]]) + || !method_exists($this->values[$id], '__invoke') + ) { + return $this->values[$id]; + } + + if (isset($this->factories[$this->values[$id]])) { + return $this->values[$id]($this); + } + + $raw = $this->values[$id]; + $val = $this->values[$id] = $raw($this); + $this->raw[$id] = $raw; + + $this->frozen[$id] = true; + + return $val; + } + + /** + * Checks if a parameter or an object is set. + * + * @param string $id The unique identifier for the parameter or object + * + * @return bool + */ + public function offsetExists($id) + { + return isset($this->keys[$id]); + } + + /** + * Unsets a parameter or an object. + * + * @param string $id The unique identifier for the parameter or object + */ + public function offsetUnset($id) + { + if (isset($this->keys[$id])) { + if (is_object($this->values[$id])) { + unset($this->factories[$this->values[$id]], $this->protected[$this->values[$id]]); + } + + unset($this->values[$id], $this->frozen[$id], $this->raw[$id], $this->keys[$id]); + } + } + + /** + * Marks a callable as being a factory service. + * + * @param callable $callable A service definition to be used as a factory + * + * @return callable The passed callable + * + * @throws \InvalidArgumentException Service definition has to be a closure of an invokable object + */ + public function factory($callable) + { + if (!is_object($callable) || !method_exists($callable, '__invoke')) { + throw new \InvalidArgumentException('Service definition is not a Closure or invokable object.'); + } + + $this->factories->attach($callable); + + return $callable; + } + + /** + * Protects a callable from being interpreted as a service. + * + * This is useful when you want to store a callable as a parameter. + * + * @param callable $callable A callable to protect from being evaluated + * + * @return callable The passed callable + * + * @throws \InvalidArgumentException Service definition has to be a closure of an invokable object + */ + public function protect($callable) + { + if (!is_object($callable) || !method_exists($callable, '__invoke')) { + throw new \InvalidArgumentException('Callable is not a Closure or invokable object.'); + } + + $this->protected->attach($callable); + + return $callable; + } + + /** + * Gets a parameter or the closure defining an object. + * + * @param string $id The unique identifier for the parameter or object + * + * @return mixed The value of the parameter or the closure defining an object + * + * @throws \InvalidArgumentException if the identifier is not defined + */ + public function raw($id) + { + if (!isset($this->keys[$id])) { + throw new \InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id)); + } + + if (isset($this->raw[$id])) { + return $this->raw[$id]; + } + + return $this->values[$id]; + } + + /** + * Extends an object definition. + * + * Useful when you want to extend an existing object definition, + * without necessarily loading that object. + * + * @param string $id The unique identifier for the object + * @param callable $callable A service definition to extend the original + * + * @return callable The wrapped callable + * + * @throws \InvalidArgumentException if the identifier is not defined or not a service definition + */ + public function extend($id, $callable) + { + if (!isset($this->keys[$id])) { + throw new \InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id)); + } + + if (!is_object($this->values[$id]) || !method_exists($this->values[$id], '__invoke')) { + throw new \InvalidArgumentException(sprintf('Identifier "%s" does not contain an object definition.', $id)); + } + + if (!is_object($callable) || !method_exists($callable, '__invoke')) { + throw new \InvalidArgumentException('Extension service definition is not a Closure or invokable object.'); + } + + $factory = $this->values[$id]; + + $extended = function ($c) use ($callable, $factory) { + return $callable($factory($c), $c); + }; + + if (isset($this->factories[$factory])) { + $this->factories->detach($factory); + $this->factories->attach($extended); + } + + return $this[$id] = $extended; + } + + /** + * Returns all defined value names. + * + * @return array An array of value names + */ + public function keys() + { + return array_keys($this->values); + } + + /** + * Registers a service provider. + * + * @param ServiceProviderInterface $provider A ServiceProviderInterface instance + * @param array $values An array of values that customizes the provider + * + * @return static + */ + public function register(ServiceProviderInterface $provider, array $values = array()) + { + $provider->register($this); + + foreach ($values as $key => $value) { + $this[$key] = $value; + } + + return $this; + } +} diff --git a/vendor/pimple/pimple/src/Pimple/ServiceProviderInterface.php b/vendor/pimple/pimple/src/Pimple/ServiceProviderInterface.php new file mode 100644 index 000000000..9b122bd43 --- /dev/null +++ b/vendor/pimple/pimple/src/Pimple/ServiceProviderInterface.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * Lazily loads listeners and subscribers from the dependency injection + * container + * + * @author Fabien Potencier + * @author Bernhard Schussek + * @author Jordan Alliot + */ +class ContainerAwareEventDispatcher extends EventDispatcher +{ + /** + * The container from where services are loaded + * @var ContainerInterface + */ + private $container; + + /** + * The service IDs of the event listeners and subscribers + * @var array + */ + private $listenerIds = array(); + + /** + * The services registered as listeners + * @var array + */ + private $listeners = array(); + + /** + * Constructor. + * + * @param ContainerInterface $container A ContainerInterface instance + */ + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + + /** + * Adds a service as event listener + * + * @param string $eventName Event for which the listener is added + * @param array $callback The service ID of the listener service & the method + * name that has to be called + * @param int $priority The higher this value, the earlier an event listener + * will be triggered in the chain. + * Defaults to 0. + * + * @throws \InvalidArgumentException + */ + public function addListenerService($eventName, $callback, $priority = 0) + { + if (!is_array($callback) || 2 !== count($callback)) { + throw new \InvalidArgumentException('Expected an array("service", "method") argument'); + } + + $this->listenerIds[$eventName][] = array($callback[0], $callback[1], $priority); + } + + public function removeListener($eventName, $listener) + { + $this->lazyLoad($eventName); + + if (isset($this->listeners[$eventName])) { + foreach ($this->listeners[$eventName] as $key => $l) { + foreach ($this->listenerIds[$eventName] as $i => $args) { + list($serviceId, $method, $priority) = $args; + if ($key === $serviceId.'.'.$method) { + if ($listener === array($l, $method)) { + unset($this->listeners[$eventName][$key]); + if (empty($this->listeners[$eventName])) { + unset($this->listeners[$eventName]); + } + unset($this->listenerIds[$eventName][$i]); + if (empty($this->listenerIds[$eventName])) { + unset($this->listenerIds[$eventName]); + } + } + } + } + } + } + + parent::removeListener($eventName, $listener); + } + + /** + * @see EventDispatcherInterface::hasListeners + */ + public function hasListeners($eventName = null) + { + if (null === $eventName) { + return (bool) count($this->listenerIds) || (bool) count($this->listeners); + } + + if (isset($this->listenerIds[$eventName])) { + return true; + } + + return parent::hasListeners($eventName); + } + + /** + * @see EventDispatcherInterface::getListeners + */ + public function getListeners($eventName = null) + { + if (null === $eventName) { + foreach (array_keys($this->listenerIds) as $serviceEventName) { + $this->lazyLoad($serviceEventName); + } + } else { + $this->lazyLoad($eventName); + } + + return parent::getListeners($eventName); + } + + /** + * Adds a service as event subscriber + * + * @param string $serviceId The service ID of the subscriber service + * @param string $class The service's class name (which must implement EventSubscriberInterface) + */ + public function addSubscriberService($serviceId, $class) + { + foreach ($class::getSubscribedEvents() as $eventName => $params) { + if (is_string($params)) { + $this->listenerIds[$eventName][] = array($serviceId, $params, 0); + } elseif (is_string($params[0])) { + $this->listenerIds[$eventName][] = array($serviceId, $params[0], isset($params[1]) ? $params[1] : 0); + } else { + foreach ($params as $listener) { + $this->listenerIds[$eventName][] = array($serviceId, $listener[0], isset($listener[1]) ? $listener[1] : 0); + } + } + } + } + + /** + * {@inheritdoc} + * + * Lazily loads listeners for this event from the dependency injection + * container. + * + * @throws \InvalidArgumentException if the service is not defined + */ + public function dispatch($eventName, Event $event = null) + { + $this->lazyLoad($eventName); + + return parent::dispatch($eventName, $event); + } + + public function getContainer() + { + return $this->container; + } + + /** + * Lazily loads listeners for this event from the dependency injection + * container. + * + * @param string $eventName The name of the event to dispatch. The name of + * the event is the name of the method that is + * invoked on listeners. + */ + protected function lazyLoad($eventName) + { + if (isset($this->listenerIds[$eventName])) { + foreach ($this->listenerIds[$eventName] as $args) { + list($serviceId, $method, $priority) = $args; + $listener = $this->container->get($serviceId); + + $key = $serviceId.'.'.$method; + if (!isset($this->listeners[$eventName][$key])) { + $this->addListener($eventName, array($listener, $method), $priority); + } elseif ($listener !== $this->listeners[$eventName][$key]) { + parent::removeListener($eventName, array($this->listeners[$eventName][$key], $method)); + $this->addListener($eventName, array($listener, $method), $priority); + } + + $this->listeners[$eventName][$key] = $listener; + } + } + } +} diff --git a/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php new file mode 100644 index 000000000..410226bb3 --- /dev/null +++ b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php @@ -0,0 +1,317 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Debug; + +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\Stopwatch\Stopwatch; +use Psr\Log\LoggerInterface; + +/** + * Collects some data about event listeners. + * + * This event dispatcher delegates the dispatching to another one. + * + * @author Fabien Potencier + */ +class TraceableEventDispatcher implements TraceableEventDispatcherInterface +{ + protected $logger; + protected $stopwatch; + + private $called; + private $dispatcher; + + /** + * Constructor. + * + * @param EventDispatcherInterface $dispatcher An EventDispatcherInterface instance + * @param Stopwatch $stopwatch A Stopwatch instance + * @param LoggerInterface $logger A LoggerInterface instance + */ + public function __construct(EventDispatcherInterface $dispatcher, Stopwatch $stopwatch, LoggerInterface $logger = null) + { + $this->dispatcher = $dispatcher; + $this->stopwatch = $stopwatch; + $this->logger = $logger; + $this->called = array(); + } + + /** + * {@inheritdoc} + */ + public function addListener($eventName, $listener, $priority = 0) + { + $this->dispatcher->addListener($eventName, $listener, $priority); + } + + /** + * {@inheritdoc} + */ + public function addSubscriber(EventSubscriberInterface $subscriber) + { + $this->dispatcher->addSubscriber($subscriber); + } + + /** + * {@inheritdoc} + */ + public function removeListener($eventName, $listener) + { + return $this->dispatcher->removeListener($eventName, $listener); + } + + /** + * {@inheritdoc} + */ + public function removeSubscriber(EventSubscriberInterface $subscriber) + { + return $this->dispatcher->removeSubscriber($subscriber); + } + + /** + * {@inheritdoc} + */ + public function getListeners($eventName = null) + { + return $this->dispatcher->getListeners($eventName); + } + + /** + * {@inheritdoc} + */ + public function hasListeners($eventName = null) + { + return $this->dispatcher->hasListeners($eventName); + } + + /** + * {@inheritdoc} + */ + public function dispatch($eventName, Event $event = null) + { + if (null === $event) { + $event = new Event(); + } + + $this->preProcess($eventName); + $this->preDispatch($eventName, $event); + + $e = $this->stopwatch->start($eventName, 'section'); + + $this->dispatcher->dispatch($eventName, $event); + + if ($e->isStarted()) { + $e->stop(); + } + + $this->postDispatch($eventName, $event); + $this->postProcess($eventName); + + return $event; + } + + /** + * {@inheritdoc} + */ + public function getCalledListeners() + { + $called = array(); + foreach ($this->called as $eventName => $listeners) { + foreach ($listeners as $listener) { + $info = $this->getListenerInfo($listener->getWrappedListener(), $eventName); + $called[$eventName.'.'.$info['pretty']] = $info; + } + } + + return $called; + } + + /** + * {@inheritdoc} + */ + public function getNotCalledListeners() + { + try { + $allListeners = $this->getListeners(); + } catch (\Exception $e) { + if (null !== $this->logger) { + $this->logger->info(sprintf('An exception was thrown while getting the uncalled listeners (%s)', $e->getMessage()), array('exception' => $e)); + } + + // unable to retrieve the uncalled listeners + return array(); + } + + $notCalled = array(); + foreach ($allListeners as $eventName => $listeners) { + foreach ($listeners as $listener) { + $called = false; + if (isset($this->called[$eventName])) { + foreach ($this->called[$eventName] as $l) { + if ($l->getWrappedListener() === $listener) { + $called = true; + + break; + } + } + } + + if (!$called) { + $info = $this->getListenerInfo($listener, $eventName); + $notCalled[$eventName.'.'.$info['pretty']] = $info; + } + } + } + + return $notCalled; + } + + /** + * Proxies all method calls to the original event dispatcher. + * + * @param string $method The method name + * @param array $arguments The method arguments + * + * @return mixed + */ + public function __call($method, $arguments) + { + return call_user_func_array(array($this->dispatcher, $method), $arguments); + } + + /** + * Called before dispatching the event. + * + * @param string $eventName The event name + * @param Event $event The event + */ + protected function preDispatch($eventName, Event $event) + { + } + + /** + * Called after dispatching the event. + * + * @param string $eventName The event name + * @param Event $event The event + */ + protected function postDispatch($eventName, Event $event) + { + } + + private function preProcess($eventName) + { + foreach ($this->dispatcher->getListeners($eventName) as $listener) { + $this->dispatcher->removeListener($eventName, $listener); + $info = $this->getListenerInfo($listener, $eventName); + $name = isset($info['class']) ? $info['class'] : $info['type']; + $this->dispatcher->addListener($eventName, new WrappedListener($listener, $name, $this->stopwatch)); + } + } + + private function postProcess($eventName) + { + $skipped = false; + foreach ($this->dispatcher->getListeners($eventName) as $listener) { + // Unwrap listener + $this->dispatcher->removeListener($eventName, $listener); + $this->dispatcher->addListener($eventName, $listener->getWrappedListener()); + + $info = $this->getListenerInfo($listener->getWrappedListener(), $eventName); + if ($listener->wasCalled()) { + if (null !== $this->logger) { + $this->logger->debug(sprintf('Notified event "%s" to listener "%s".', $eventName, $info['pretty'])); + } + + if (!isset($this->called[$eventName])) { + $this->called[$eventName] = new \SplObjectStorage(); + } + + $this->called[$eventName]->attach($listener); + } + + if (null !== $this->logger && $skipped) { + $this->logger->debug(sprintf('Listener "%s" was not called for event "%s".', $info['pretty'], $eventName)); + } + + if ($listener->stoppedPropagation()) { + if (null !== $this->logger) { + $this->logger->debug(sprintf('Listener "%s" stopped propagation of the event "%s".', $info['pretty'], $eventName)); + } + + $skipped = true; + } + } + } + + /** + * Returns information about the listener + * + * @param object $listener The listener + * @param string $eventName The event name + * + * @return array Information about the listener + */ + private function getListenerInfo($listener, $eventName) + { + $info = array( + 'event' => $eventName, + ); + if ($listener instanceof \Closure) { + $info += array( + 'type' => 'Closure', + 'pretty' => 'closure' + ); + } elseif (is_string($listener)) { + try { + $r = new \ReflectionFunction($listener); + $file = $r->getFileName(); + $line = $r->getStartLine(); + } catch (\ReflectionException $e) { + $file = null; + $line = null; + } + $info += array( + 'type' => 'Function', + 'function' => $listener, + 'file' => $file, + 'line' => $line, + 'pretty' => $listener, + ); + } elseif (is_array($listener) || (is_object($listener) && is_callable($listener))) { + if (!is_array($listener)) { + $listener = array($listener, '__invoke'); + } + $class = is_object($listener[0]) ? get_class($listener[0]) : $listener[0]; + try { + $r = new \ReflectionMethod($class, $listener[1]); + $file = $r->getFileName(); + $line = $r->getStartLine(); + } catch (\ReflectionException $e) { + $file = null; + $line = null; + } + $info += array( + 'type' => 'Method', + 'class' => $class, + 'method' => $listener[1], + 'file' => $file, + 'line' => $line, + 'pretty' => $class.'::'.$listener[1], + ); + } + + return $info; + } +} diff --git a/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcherInterface.php b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcherInterface.php new file mode 100644 index 000000000..5483e8150 --- /dev/null +++ b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcherInterface.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Debug; + +use Symfony\Component\EventDispatcher\EventDispatcherInterface; + +/** + * @author Fabien Potencier + */ +interface TraceableEventDispatcherInterface extends EventDispatcherInterface +{ + /** + * Gets the called listeners. + * + * @return array An array of called listeners + */ + public function getCalledListeners(); + + /** + * Gets the not called listeners. + * + * @return array An array of not called listeners + */ + public function getNotCalledListeners(); +} diff --git a/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Debug/WrappedListener.php b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Debug/WrappedListener.php new file mode 100644 index 000000000..c501662b0 --- /dev/null +++ b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Debug/WrappedListener.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Debug; + +use Symfony\Component\Stopwatch\Stopwatch; +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; + +/** + * @author Fabien Potencier + */ +class WrappedListener +{ + private $listener; + private $name; + private $called; + private $stoppedPropagation; + private $stopwatch; + + public function __construct($listener, $name, Stopwatch $stopwatch) + { + $this->listener = $listener; + $this->name = $name; + $this->stopwatch = $stopwatch; + $this->called = false; + $this->stoppedPropagation = false; + } + + public function getWrappedListener() + { + return $this->listener; + } + + public function wasCalled() + { + return $this->called; + } + + public function stoppedPropagation() + { + return $this->stoppedPropagation; + } + + public function __invoke(Event $event, $eventName, EventDispatcherInterface $dispatcher) + { + $this->called = true; + + $e = $this->stopwatch->start($this->name, 'event_listener'); + + call_user_func($this->listener, $event, $eventName, $dispatcher); + + if ($e->isStarted()) { + $e->stop(); + } + + if ($event->isPropagationStopped()) { + $this->stoppedPropagation = true; + } + } +} diff --git a/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php new file mode 100644 index 000000000..afe3ecd18 --- /dev/null +++ b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php @@ -0,0 +1,106 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\DependencyInjection; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; + +/** + * Compiler pass to register tagged services for an event dispatcher. + */ +class RegisterListenersPass implements CompilerPassInterface +{ + /** + * @var string + */ + protected $dispatcherService; + + /** + * @var string + */ + protected $listenerTag; + + /** + * @var string + */ + protected $subscriberTag; + + /** + * Constructor. + * + * @param string $dispatcherService Service name of the event dispatcher in processed container + * @param string $listenerTag Tag name used for listener + * @param string $subscriberTag Tag name used for subscribers + */ + public function __construct($dispatcherService = 'event_dispatcher', $listenerTag = 'kernel.event_listener', $subscriberTag = 'kernel.event_subscriber') + { + $this->dispatcherService = $dispatcherService; + $this->listenerTag = $listenerTag; + $this->subscriberTag = $subscriberTag; + } + + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition($this->dispatcherService) && !$container->hasAlias($this->dispatcherService)) { + return; + } + + $definition = $container->findDefinition($this->dispatcherService); + + foreach ($container->findTaggedServiceIds($this->listenerTag) as $id => $events) { + $def = $container->getDefinition($id); + if (!$def->isPublic()) { + throw new \InvalidArgumentException(sprintf('The service "%s" must be public as event listeners are lazy-loaded.', $id)); + } + + if ($def->isAbstract()) { + throw new \InvalidArgumentException(sprintf('The service "%s" must not be abstract as event listeners are lazy-loaded.', $id)); + } + + foreach ($events as $event) { + $priority = isset($event['priority']) ? $event['priority'] : 0; + + if (!isset($event['event'])) { + throw new \InvalidArgumentException(sprintf('Service "%s" must define the "event" attribute on "%s" tags.', $id, $this->listenerTag)); + } + + if (!isset($event['method'])) { + $event['method'] = 'on'.preg_replace_callback(array( + '/(?<=\b)[a-z]/i', + '/[^a-z0-9]/i', + ), function ($matches) { return strtoupper($matches[0]); }, $event['event']); + $event['method'] = preg_replace('/[^a-z0-9]/i', '', $event['method']); + } + + $definition->addMethodCall('addListenerService', array($event['event'], array($id, $event['method']), $priority)); + } + } + + foreach ($container->findTaggedServiceIds($this->subscriberTag) as $id => $attributes) { + $def = $container->getDefinition($id); + if (!$def->isPublic()) { + throw new \InvalidArgumentException(sprintf('The service "%s" must be public as event subscribers are lazy-loaded.', $id)); + } + + // We must assume that the class value has been correctly filled, even if the service is created by a factory + $class = $def->getClass(); + + $refClass = new \ReflectionClass($class); + $interface = 'Symfony\Component\EventDispatcher\EventSubscriberInterface'; + if (!$refClass->implementsInterface($interface)) { + throw new \InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, $interface)); + } + + $definition->addMethodCall('addSubscriberService', array($id, $class)); + } + } +} diff --git a/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Event.php b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Event.php new file mode 100644 index 000000000..bf792a257 --- /dev/null +++ b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Event.php @@ -0,0 +1,129 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * Event is the base class for classes containing event data. + * + * This class contains no event data. It is used by events that do not pass + * state information to an event handler when an event is raised. + * + * You can call the method stopPropagation() to abort the execution of + * further listeners in your event listener. + * + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Bernhard Schussek + * + * @api + */ +class Event +{ + /** + * @var bool Whether no further event listeners should be triggered + */ + private $propagationStopped = false; + + /** + * @var EventDispatcher Dispatcher that dispatched this event + */ + private $dispatcher; + + /** + * @var string This event's name + */ + private $name; + + /** + * Returns whether further event listeners should be triggered. + * + * @see Event::stopPropagation + * @return bool Whether propagation was already stopped for this event. + * + * @api + */ + public function isPropagationStopped() + { + return $this->propagationStopped; + } + + /** + * Stops the propagation of the event to further event listeners. + * + * If multiple event listeners are connected to the same event, no + * further event listener will be triggered once any trigger calls + * stopPropagation(). + * + * @api + */ + public function stopPropagation() + { + $this->propagationStopped = true; + } + + /** + * Stores the EventDispatcher that dispatches this Event + * + * @param EventDispatcherInterface $dispatcher + * + * @deprecated Deprecated in 2.4, to be removed in 3.0. The event dispatcher is passed to the listener call. + * + * @api + */ + public function setDispatcher(EventDispatcherInterface $dispatcher) + { + $this->dispatcher = $dispatcher; + } + + /** + * Returns the EventDispatcher that dispatches this Event + * + * @return EventDispatcherInterface + * + * @deprecated Deprecated in 2.4, to be removed in 3.0. The event dispatcher is passed to the listener call. + * + * @api + */ + public function getDispatcher() + { + return $this->dispatcher; + } + + /** + * Gets the event's name. + * + * @return string + * + * @deprecated Deprecated in 2.4, to be removed in 3.0. The event name is passed to the listener call. + * + * @api + */ + public function getName() + { + return $this->name; + } + + /** + * Sets the event's name property. + * + * @param string $name The event name. + * + * @deprecated Deprecated in 2.4, to be removed in 3.0. The event name is passed to the listener call. + * + * @api + */ + public function setName($name) + { + $this->name = $name; + } +} diff --git a/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcher.php b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcher.php new file mode 100644 index 000000000..222a93714 --- /dev/null +++ b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcher.php @@ -0,0 +1,185 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * The EventDispatcherInterface is the central point of Symfony's event listener system. + * + * Listeners are registered on the manager and events are dispatched through the + * manager. + * + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Bernhard Schussek + * @author Fabien Potencier + * @author Jordi Boggiano + * @author Jordan Alliot + * + * @api + */ +class EventDispatcher implements EventDispatcherInterface +{ + private $listeners = array(); + private $sorted = array(); + + /** + * @see EventDispatcherInterface::dispatch + * + * @api + */ + public function dispatch($eventName, Event $event = null) + { + if (null === $event) { + $event = new Event(); + } + + $event->setDispatcher($this); + $event->setName($eventName); + + if (!isset($this->listeners[$eventName])) { + return $event; + } + + $this->doDispatch($this->getListeners($eventName), $eventName, $event); + + return $event; + } + + /** + * @see EventDispatcherInterface::getListeners + */ + public function getListeners($eventName = null) + { + if (null !== $eventName) { + if (!isset($this->sorted[$eventName])) { + $this->sortListeners($eventName); + } + + return $this->sorted[$eventName]; + } + + foreach (array_keys($this->listeners) as $eventName) { + if (!isset($this->sorted[$eventName])) { + $this->sortListeners($eventName); + } + } + + return array_filter($this->sorted); + } + + /** + * @see EventDispatcherInterface::hasListeners + */ + public function hasListeners($eventName = null) + { + return (bool) count($this->getListeners($eventName)); + } + + /** + * @see EventDispatcherInterface::addListener + * + * @api + */ + public function addListener($eventName, $listener, $priority = 0) + { + $this->listeners[$eventName][$priority][] = $listener; + unset($this->sorted[$eventName]); + } + + /** + * @see EventDispatcherInterface::removeListener + */ + public function removeListener($eventName, $listener) + { + if (!isset($this->listeners[$eventName])) { + return; + } + + foreach ($this->listeners[$eventName] as $priority => $listeners) { + if (false !== ($key = array_search($listener, $listeners, true))) { + unset($this->listeners[$eventName][$priority][$key], $this->sorted[$eventName]); + } + } + } + + /** + * @see EventDispatcherInterface::addSubscriber + * + * @api + */ + public function addSubscriber(EventSubscriberInterface $subscriber) + { + foreach ($subscriber->getSubscribedEvents() as $eventName => $params) { + if (is_string($params)) { + $this->addListener($eventName, array($subscriber, $params)); + } elseif (is_string($params[0])) { + $this->addListener($eventName, array($subscriber, $params[0]), isset($params[1]) ? $params[1] : 0); + } else { + foreach ($params as $listener) { + $this->addListener($eventName, array($subscriber, $listener[0]), isset($listener[1]) ? $listener[1] : 0); + } + } + } + } + + /** + * @see EventDispatcherInterface::removeSubscriber + */ + public function removeSubscriber(EventSubscriberInterface $subscriber) + { + foreach ($subscriber->getSubscribedEvents() as $eventName => $params) { + if (is_array($params) && is_array($params[0])) { + foreach ($params as $listener) { + $this->removeListener($eventName, array($subscriber, $listener[0])); + } + } else { + $this->removeListener($eventName, array($subscriber, is_string($params) ? $params : $params[0])); + } + } + } + + /** + * Triggers the listeners of an event. + * + * This method can be overridden to add functionality that is executed + * for each listener. + * + * @param callable[] $listeners The event listeners. + * @param string $eventName The name of the event to dispatch. + * @param Event $event The event object to pass to the event handlers/listeners. + */ + protected function doDispatch($listeners, $eventName, Event $event) + { + foreach ($listeners as $listener) { + call_user_func($listener, $event, $eventName, $this); + if ($event->isPropagationStopped()) { + break; + } + } + } + + /** + * Sorts the internal list of listeners for the given event by priority. + * + * @param string $eventName The name of the event. + */ + private function sortListeners($eventName) + { + $this->sorted[$eventName] = array(); + + if (isset($this->listeners[$eventName])) { + krsort($this->listeners[$eventName]); + $this->sorted[$eventName] = call_user_func_array('array_merge', $this->listeners[$eventName]); + } + } +} diff --git a/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcherInterface.php b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcherInterface.php new file mode 100644 index 000000000..3fdbfd882 --- /dev/null +++ b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcherInterface.php @@ -0,0 +1,96 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * The EventDispatcherInterface is the central point of Symfony's event listener system. + * Listeners are registered on the manager and events are dispatched through the + * manager. + * + * @author Bernhard Schussek + * + * @api + */ +interface EventDispatcherInterface +{ + /** + * Dispatches an event to all registered listeners. + * + * @param string $eventName The name of the event to dispatch. The name of + * the event is the name of the method that is + * invoked on listeners. + * @param Event $event The event to pass to the event handlers/listeners. + * If not supplied, an empty Event instance is created. + * + * @return Event + * + * @api + */ + public function dispatch($eventName, Event $event = null); + + /** + * Adds an event listener that listens on the specified events. + * + * @param string $eventName The event to listen on + * @param callable $listener The listener + * @param int $priority The higher this value, the earlier an event + * listener will be triggered in the chain (defaults to 0) + * + * @api + */ + public function addListener($eventName, $listener, $priority = 0); + + /** + * Adds an event subscriber. + * + * The subscriber is asked for all the events he is + * interested in and added as a listener for these events. + * + * @param EventSubscriberInterface $subscriber The subscriber. + * + * @api + */ + public function addSubscriber(EventSubscriberInterface $subscriber); + + /** + * Removes an event listener from the specified events. + * + * @param string|array $eventName The event(s) to remove a listener from + * @param callable $listener The listener to remove + */ + public function removeListener($eventName, $listener); + + /** + * Removes an event subscriber. + * + * @param EventSubscriberInterface $subscriber The subscriber + */ + public function removeSubscriber(EventSubscriberInterface $subscriber); + + /** + * Gets the listeners of a specific event or all listeners. + * + * @param string $eventName The name of the event + * + * @return array The event listeners for the specified event, or all event listeners by event name + */ + public function getListeners($eventName = null); + + /** + * Checks whether an event has any registered listeners. + * + * @param string $eventName The name of the event + * + * @return bool true if the specified event has any listeners, false otherwise + */ + public function hasListeners($eventName = null); +} diff --git a/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventSubscriberInterface.php b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventSubscriberInterface.php new file mode 100644 index 000000000..080f892fd --- /dev/null +++ b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventSubscriberInterface.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * An EventSubscriber knows himself what events he is interested in. + * If an EventSubscriber is added to an EventDispatcherInterface, the manager invokes + * {@link getSubscribedEvents} and registers the subscriber as a listener for all + * returned events. + * + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Bernhard Schussek + * + * @api + */ +interface EventSubscriberInterface +{ + /** + * Returns an array of event names this subscriber wants to listen to. + * + * The array keys are event names and the value can be: + * + * * The method name to call (priority defaults to 0) + * * An array composed of the method name to call and the priority + * * An array of arrays composed of the method names to call and respective + * priorities, or 0 if unset + * + * For instance: + * + * * array('eventName' => 'methodName') + * * array('eventName' => array('methodName', $priority)) + * * array('eventName' => array(array('methodName1', $priority), array('methodName2')) + * + * @return array The event names to listen to + * + * @api + */ + public static function getSubscribedEvents(); +} diff --git a/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/GenericEvent.php b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/GenericEvent.php new file mode 100644 index 000000000..1e8c44a67 --- /dev/null +++ b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/GenericEvent.php @@ -0,0 +1,186 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * Event encapsulation class. + * + * Encapsulates events thus decoupling the observer from the subject they encapsulate. + * + * @author Drak + */ +class GenericEvent extends Event implements \ArrayAccess, \IteratorAggregate +{ + /** + * Event subject. + * + * @var mixed usually object or callable + */ + protected $subject; + + /** + * Array of arguments. + * + * @var array + */ + protected $arguments; + + /** + * Encapsulate an event with $subject and $args. + * + * @param mixed $subject The subject of the event, usually an object. + * @param array $arguments Arguments to store in the event. + */ + public function __construct($subject = null, array $arguments = array()) + { + $this->subject = $subject; + $this->arguments = $arguments; + } + + /** + * Getter for subject property. + * + * @return mixed $subject The observer subject. + */ + public function getSubject() + { + return $this->subject; + } + + /** + * Get argument by key. + * + * @param string $key Key. + * + * @throws \InvalidArgumentException If key is not found. + * + * @return mixed Contents of array key. + */ + public function getArgument($key) + { + if ($this->hasArgument($key)) { + return $this->arguments[$key]; + } + + throw new \InvalidArgumentException(sprintf('%s not found in %s', $key, $this->getName())); + } + + /** + * Add argument to event. + * + * @param string $key Argument name. + * @param mixed $value Value. + * + * @return GenericEvent + */ + public function setArgument($key, $value) + { + $this->arguments[$key] = $value; + + return $this; + } + + /** + * Getter for all arguments. + * + * @return array + */ + public function getArguments() + { + return $this->arguments; + } + + /** + * Set args property. + * + * @param array $args Arguments. + * + * @return GenericEvent + */ + public function setArguments(array $args = array()) + { + $this->arguments = $args; + + return $this; + } + + /** + * Has argument. + * + * @param string $key Key of arguments array. + * + * @return bool + */ + public function hasArgument($key) + { + return array_key_exists($key, $this->arguments); + } + + /** + * ArrayAccess for argument getter. + * + * @param string $key Array key. + * + * @throws \InvalidArgumentException If key does not exist in $this->args. + * + * @return mixed + */ + public function offsetGet($key) + { + return $this->getArgument($key); + } + + /** + * ArrayAccess for argument setter. + * + * @param string $key Array key to set. + * @param mixed $value Value. + */ + public function offsetSet($key, $value) + { + $this->setArgument($key, $value); + } + + /** + * ArrayAccess for unset argument. + * + * @param string $key Array key. + */ + public function offsetUnset($key) + { + if ($this->hasArgument($key)) { + unset($this->arguments[$key]); + } + } + + /** + * ArrayAccess has argument. + * + * @param string $key Array key. + * + * @return bool + */ + public function offsetExists($key) + { + return $this->hasArgument($key); + } + + /** + * IteratorAggregate for iterating over the object like an array + * + * @return \ArrayIterator + */ + public function getIterator() + { + return new \ArrayIterator($this->arguments); + } +} diff --git a/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ImmutableEventDispatcher.php b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ImmutableEventDispatcher.php new file mode 100644 index 000000000..b70b81a8b --- /dev/null +++ b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ImmutableEventDispatcher.php @@ -0,0 +1,92 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * A read-only proxy for an event dispatcher. + * + * @author Bernhard Schussek + */ +class ImmutableEventDispatcher implements EventDispatcherInterface +{ + /** + * The proxied dispatcher. + * @var EventDispatcherInterface + */ + private $dispatcher; + + /** + * Creates an unmodifiable proxy for an event dispatcher. + * + * @param EventDispatcherInterface $dispatcher The proxied event dispatcher. + */ + public function __construct(EventDispatcherInterface $dispatcher) + { + $this->dispatcher = $dispatcher; + } + + /** + * {@inheritdoc} + */ + public function dispatch($eventName, Event $event = null) + { + return $this->dispatcher->dispatch($eventName, $event); + } + + /** + * {@inheritdoc} + */ + public function addListener($eventName, $listener, $priority = 0) + { + throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); + } + + /** + * {@inheritdoc} + */ + public function addSubscriber(EventSubscriberInterface $subscriber) + { + throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); + } + + /** + * {@inheritdoc} + */ + public function removeListener($eventName, $listener) + { + throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); + } + + /** + * {@inheritdoc} + */ + public function removeSubscriber(EventSubscriberInterface $subscriber) + { + throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); + } + + /** + * {@inheritdoc} + */ + public function getListeners($eventName = null) + { + return $this->dispatcher->getListeners($eventName); + } + + /** + * {@inheritdoc} + */ + public function hasListeners($eventName = null) + { + return $this->dispatcher->hasListeners($eventName); + } +} diff --git a/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/LICENSE b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/LICENSE new file mode 100644 index 000000000..0b3292cf9 --- /dev/null +++ b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2014 Fabien Potencier + +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. diff --git a/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/README.md b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/README.md new file mode 100644 index 000000000..22bf74fdc --- /dev/null +++ b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/README.md @@ -0,0 +1,25 @@ +EventDispatcher Component +========================= + +The Symfony2 EventDispatcher component implements the Mediator pattern in a +simple and effective way to make your projects truly extensible. + + use Symfony\Component\EventDispatcher\EventDispatcher; + use Symfony\Component\EventDispatcher\Event; + + $dispatcher = new EventDispatcher(); + + $dispatcher->addListener('event_name', function (Event $event) { + // ... + }); + + $dispatcher->dispatch('event_name'); + +Resources +--------- + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Component/EventDispatcher/ + $ composer.phar install + $ phpunit diff --git a/vendor/tracy/tracy/readme.md b/vendor/tracy/tracy/readme.md index d0f0352a1..1564d592a 100644 --- a/vendor/tracy/tracy/readme.md +++ b/vendor/tracy/tracy/readme.md @@ -2,7 +2,7 @@ ============================================== [![Downloads this Month](https://img.shields.io/packagist/dm/tracy/tracy.svg)](https://packagist.org/packages/tracy/tracy) -[![Build Status](https://travis-ci.org/nette/tracy.svg?branch=master)](https://travis-ci.org/nette/tracy) +[![Build Status](https://travis-ci.org/nette/tracy.svg?branch=v2.2)](https://travis-ci.org/nette/tracy) Tracy library is a useful PHP everyday programmer's helper. It helps you to: diff --git a/vendor/tracy/tracy/src/Tracy/BlueScreen.php b/vendor/tracy/tracy/src/Tracy/BlueScreen.php index 4e20fe167..907bb6952 100644 --- a/vendor/tracy/tracy/src/Tracy/BlueScreen.php +++ b/vendor/tracy/tracy/src/Tracy/BlueScreen.php @@ -50,15 +50,6 @@ public function render(\Exception $exception) { $panels = $this->panels; $info = array_filter($this->info); - $source = Helpers::getSource(); - $sourceIsUrl = preg_match('#^https?://#', $source); - $title = $exception instanceof \ErrorException - ? Helpers::errorTypeToString($exception->getSeverity()) - : get_class($exception); - $skipError = $sourceIsUrl && $exception instanceof \ErrorException && !empty($exception->skippable) - ? $source . (strpos($source, '?') ? '&' : '?') . '_tracy_skip_error' - : NULL; - require __DIR__ . '/templates/bluescreen.phtml'; } diff --git a/vendor/tracy/tracy/src/Tracy/Debugger.php b/vendor/tracy/tracy/src/Tracy/Debugger.php index 551d7f47a..b91016871 100644 --- a/vendor/tracy/tracy/src/Tracy/Debugger.php +++ b/vendor/tracy/tracy/src/Tracy/Debugger.php @@ -14,37 +14,31 @@ /** * Debugger: displays and logs errors. * + * Behavior is determined by two factors: mode & output + * - modes: production / development + * - output: HTML / AJAX / CLI / other (e.g. XML) + * * @author David Grudl */ class Debugger { - /** server modes {@link Debugger::enable()} */ - const DEVELOPMENT = FALSE, - PRODUCTION = TRUE, - DETECT = NULL; - /** @var string */ - public static $version = '2.3-dev'; + public static $version = '2.2.2'; /** @var bool in production mode is suppressed any debugging output */ public static $productionMode = self::DETECT; - /** @var bool {@link Debugger::enable()} */ - private static $enabled = FALSE; - - /** @var bool prevent double rendering */ - private static $done; - - /********************* errors and exceptions reporting ****************d*g**/ + /** @var int timestamp with microseconds of the start of the request */ + public static $time; - /** @var bool|int determines whether any error will cause immediate death; if integer that it's matched against error severity */ - public static $strictMode = FALSE; + /** @var string requested URI or command line */ + public static $source; - /** @var bool disables the @ (shut-up) operator so that notices and warnings are no longer hidden */ - public static $scream = FALSE; + /** @var string URI pattern mask to open editor */ + public static $editor = 'editor://open/?file=%file&line=%line'; - /** @var array of callables specifies the functions that are automatically called after fatal error */ - public static $onFatalError = array(); + /** @var string command to open browser (use 'start ""' in Windows) */ + public static $browser; /********************* Debugger::dump() ****************d*g**/ @@ -57,53 +51,80 @@ class Debugger /** @var bool display location? {@link Debugger::dump()} */ public static $showLocation = FALSE; + /********************* errors and exceptions reporting ****************d*g**/ + + /** server modes {@link Debugger::enable()} */ + const DEVELOPMENT = FALSE, + PRODUCTION = TRUE, + DETECT = NULL; + + /** @var BlueScreen */ + private static $blueScreen; + + /** @var bool|int determines whether any error will cause immediate death; if integer that it's matched against error severity */ + public static $strictMode = FALSE; // $immediateDeath + + /** @var bool disables the @ (shut-up) operator so that notices and warnings are no longer hidden */ + public static $scream = FALSE; + + /** @var array of callables specifies the functions that are automatically called after fatal error */ + public static $onFatalError = array(); + + /** @var bool {@link Debugger::enable()} */ + private static $enabled = FALSE; + + /** @internal */ + public static $errorTypes = array( + E_ERROR => 'Fatal Error', + E_USER_ERROR => 'User Error', + E_RECOVERABLE_ERROR => 'Recoverable Error', + E_CORE_ERROR => 'Core Error', + E_COMPILE_ERROR => 'Compile Error', + E_PARSE => 'Parse Error', + E_WARNING => 'Warning', + E_CORE_WARNING => 'Core Warning', + E_COMPILE_WARNING => 'Compile Warning', + E_USER_WARNING => 'User Warning', + E_NOTICE => 'Notice', + E_USER_NOTICE => 'User Notice', + E_STRICT => 'Strict standards', + E_DEPRECATED => 'Deprecated', + E_USER_DEPRECATED => 'User Deprecated', + ); + /********************* logging ****************d*g**/ + /** @var Logger */ + private static $logger; + + /** @var FireLogger */ + private static $fireLogger; + /** @var string name of the directory where errors should be logged */ public static $logDirectory; - /** @var int log bluescreen in production mode for this error severity */ - public static $logSeverity = 0; - /** @var string|array email(s) to which send error notifications */ public static $email; - /** {@link Debugger::log()} and {@link Debugger::fireLog()} */ - const DEBUG = ILogger::DEBUG, - INFO = ILogger::INFO, - WARNING = ILogger::WARNING, - ERROR = ILogger::ERROR, - EXCEPTION = ILogger::EXCEPTION, - CRITICAL = ILogger::CRITICAL; - - /********************* misc ****************d*g**/ - - /** @var int timestamp with microseconds of the start of the request */ - public static $time; - /** @deprecated */ - public static $source; - - /** @var string URI pattern mask to open editor */ - public static $editor = 'editor://open/?file=%file&line=%line'; + public static $mailer = array('Tracy\Logger', 'defaultMailer'); - /** @var string command to open browser (use 'start ""' in Windows) */ - public static $browser; + /** @deprecated */ + public static $emailSnooze = 172800; - /********************* services ****************d*g**/ + /** {@link Debugger::log()} and {@link Debugger::fireLog()} */ + const DEBUG = 'debug', + INFO = 'info', + WARNING = 'warning', + ERROR = 'error', + EXCEPTION = 'exception', + CRITICAL = 'critical'; - /** @var BlueScreen */ - private static $blueScreen; + /********************* debug bar ****************d*g**/ /** @var Bar */ private static $bar; - /** @var ILogger */ - private static $logger; - - /** @var ILogger */ - private static $fireLogger; - /** * Static class - cannot be instantiated. @@ -116,14 +137,22 @@ final public function __construct() /** * Enables displaying or logging errors and exceptions. - * @param mixed production, development mode, autodetection or IP address(es) whitelist. - * @param string error log directory; enables logging in production mode, FALSE means that logging is disabled - * @param string administrator email; enables email sending in production mode + * @param mixed production, development mode, autodetection or IP address(es) whitelist. + * @param string error log directory; enables logging in production mode, FALSE means that logging is disabled + * @param string administrator email; enables email sending in production mode * @return void */ public static function enable($mode = NULL, $logDirectory = NULL, $email = NULL) { + self::$enabled = TRUE; self::$time = isset($_SERVER['REQUEST_TIME_FLOAT']) ? $_SERVER['REQUEST_TIME_FLOAT'] : microtime(TRUE); + if (isset($_SERVER['REQUEST_URI'])) { + self::$source = (!empty($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'], 'off') ? 'https://' : 'http://') + . (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '') + . $_SERVER['REQUEST_URI']; + } else { + self::$source = empty($_SERVER['argv']) ? 'CLI' : 'CLI: ' . implode(' ', $_SERVER['argv']); + } error_reporting(E_ALL | E_STRICT); // production/development mode detection @@ -143,14 +172,15 @@ public static function enable($mode = NULL, $logDirectory = NULL, $email = NULL) if ($email !== NULL) { self::$email = $email; } - if ($logDirectory !== NULL) { - self::$logDirectory = $logDirectory; + if (is_string($logDirectory)) { + self::$logDirectory = realpath($logDirectory); + if (self::$logDirectory === FALSE) { + self::_exceptionHandler(new \RuntimeException("Log directory is not found or is not directory.")); + } + } elseif ($logDirectory === FALSE) { + self::$logDirectory = NULL; } if (self::$logDirectory) { - if (!is_dir(self::$logDirectory) || !preg_match('#([a-z]:)?[/\\\\]#Ai', self::$logDirectory)) { - self::$logDirectory = NULL; - self::exceptionHandler(new \RuntimeException('Logging directory not found or is not absolute path.')); - } ini_set('error_log', self::$logDirectory . '/php_error.log'); } @@ -161,25 +191,97 @@ public static function enable($mode = NULL, $logDirectory = NULL, $email = NULL) ini_set('log_errors', FALSE); } elseif (ini_get('display_errors') != !self::$productionMode && ini_get('display_errors') !== (self::$productionMode ? 'stderr' : 'stdout')) { // intentionally == - self::exceptionHandler(new \RuntimeException("Unable to set 'display_errors' because function ini_set() is disabled.")); + self::_exceptionHandler(new \RuntimeException("Unable to set 'display_errors' because function ini_set() is disabled.")); } - if (!self::$enabled) { - register_shutdown_function(array(__CLASS__, 'shutdownHandler')); - set_exception_handler(array(__CLASS__, 'exceptionHandler')); - set_error_handler(array(__CLASS__, 'errorHandler')); + register_shutdown_function(array(__CLASS__, '_shutdownHandler')); + set_exception_handler(array(__CLASS__, '_exceptionHandler')); + set_error_handler(array(__CLASS__, '_errorHandler')); + + foreach (array('Tracy\Bar', 'Tracy\BlueScreen', 'Tracy\DefaultBarPanel', 'Tracy\Dumper', + 'Tracy\FireLogger', 'Tracy\Helpers', 'Tracy\Logger', ) as $class) { + class_exists($class); + } + } - foreach (array('Tracy\Bar', 'Tracy\BlueScreen', 'Tracy\DefaultBarPanel', 'Tracy\Dumper', - 'Tracy\FireLogger', 'Tracy\Helpers', 'Tracy\Logger', ) as $class) { - class_exists($class); - } - self::$enabled = TRUE; + /** + * @return BlueScreen + */ + public static function getBlueScreen() + { + if (!self::$blueScreen) { + self::$blueScreen = new BlueScreen; + self::$blueScreen->info = array( + 'PHP ' . PHP_VERSION, + isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : NULL, + 'Tracy ' . self::$version, + ); } + return self::$blueScreen; } /** + * @return Bar + */ + public static function getBar() + { + if (!self::$bar) { + self::$bar = new Bar; + self::$bar->addPanel(new DefaultBarPanel('time')); + self::$bar->addPanel(new DefaultBarPanel('memory')); + self::$bar->addPanel(new DefaultBarPanel('errors'), __CLASS__ . ':errors'); // filled by _errorHandler() + self::$bar->addPanel(new DefaultBarPanel('dumps'), __CLASS__ . ':dumps'); // filled by barDump() + self::$bar->info = array( + 'PHP ' . PHP_VERSION, + isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : NULL, + 'Tracy ' . self::$version, + ); + } + return self::$bar; + } + + + /** + * @return void + */ + public static function setLogger($logger) + { + self::$logger = $logger; + } + + + /** + * @return Logger + */ + public static function getLogger() + { + if (!self::$logger) { + self::$logger = new Logger; + self::$logger->directory = & self::$logDirectory; + self::$logger->email = & self::$email; + self::$logger->mailer = & self::$mailer; + self::$logger->emailSnooze = & self::$emailSnooze; + } + return self::$logger; + } + + + /** + * @return FireLogger + */ + public static function getFireLogger() + { + if (!self::$fireLogger) { + self::$fireLogger = new FireLogger; + } + return self::$fireLogger; + } + + + /** + * Is Debug enabled? * @return bool */ public static function isEnabled() @@ -188,20 +290,81 @@ public static function isEnabled() } + /** + * Logs message or exception to file (if not disabled) and sends email notification (if enabled). + * @param string|Exception + * @param int one of constant Debugger::INFO, WARNING, ERROR (sends email), EXCEPTION (sends email), CRITICAL (sends email) + * @return string logged error filename + */ + public static function log($message, $priority = self::INFO) + { + if (!self::$logDirectory) { + return; + } + + $exceptionFilename = NULL; + if ($message instanceof \Exception) { + $exception = $message; + while ($exception) { + $tmp[] = ($exception instanceof ErrorException + ? 'Fatal error: ' . $exception->getMessage() + : get_class($exception) . ': ' . $exception->getMessage()) + . ' in ' . $exception->getFile() . ':' . $exception->getLine(); + $exception = $exception->getPrevious(); + } + $exception = $message; + $message = implode($tmp, "\ncaused by "); + + $hash = md5(preg_replace('~(Resource id #)\d+~', '$1', $exception)); + $exceptionFilename = 'exception-' . @date('Y-m-d-H-i-s') . "-$hash.html"; + foreach (new \DirectoryIterator(self::$logDirectory) as $entry) { + if (strpos($entry, $hash)) { + $exceptionFilename = $entry; + $saved = TRUE; + break; + } + } + } elseif (!is_string($message)) { + $message = Dumper::toText($message); + } + + if ($exceptionFilename) { + $exceptionFilename = self::$logDirectory . '/' . $exceptionFilename; + if (empty($saved) && $logHandle = @fopen($exceptionFilename, 'w')) { + ob_start(); // double buffer prevents sending HTTP headers in some PHP + ob_start(function($buffer) use ($logHandle) { fwrite($logHandle, $buffer); }, 4096); + self::getBlueScreen()->render($exception); + ob_end_flush(); + ob_end_clean(); + fclose($logHandle); + } + } + + self::getLogger()->log(array( + @date('[Y-m-d H-i-s]'), + trim($message), + self::$source ? ' @ ' . self::$source : NULL, + $exceptionFilename ? ' @@ ' . basename($exceptionFilename) : NULL + ), $priority); + + return $exceptionFilename ? strtr($exceptionFilename, '\\/', DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR) : NULL; + } + + /** * Shutdown handler to catch fatal errors and execute of the planned activities. * @return void * @internal */ - public static function shutdownHandler() + public static function _shutdownHandler() { - if (self::$done) { + if (!self::$enabled) { return; } $error = error_get_last(); - if (in_array($error['type'], array(E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE, E_RECOVERABLE_ERROR, E_USER_ERROR), TRUE)) { - self::exceptionHandler(Helpers::fixStack(new ErrorException($error['message'], 0, $error['type'], $error['file'], $error['line'])), FALSE); + if (in_array($error['type'], array(E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE), TRUE)) { + self::_exceptionHandler(Helpers::fixStack(new ErrorException($error['message'], 0, $error['type'], $error['file'], $error['line'])), FALSE); } elseif (!connection_aborted() && !self::$productionMode && self::isHtmlMode()) { self::getBar()->render(); @@ -211,15 +374,16 @@ public static function shutdownHandler() /** * Handler to catch uncaught exception. + * @param \Exception * @return void * @internal */ - public static function exceptionHandler(\Exception $exception, $exit = TRUE) + public static function _exceptionHandler(\Exception $exception, $exit = TRUE) { - if (self::$done) { + if (!self::$enabled) { return; } - self::$done = TRUE; + self::$enabled = FALSE; // prevent double rendering if (!headers_sent()) { $protocol = isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.1'; @@ -281,11 +445,16 @@ public static function exceptionHandler(\Exception $exception, $exit = TRUE) /** * Handler to catch warnings and notices. + * @param int level of the error raised + * @param string error message + * @param string file that the error was raised in + * @param int line number the error was raised at + * @param array an array of variables that existed in the scope the error was triggered in * @return bool FALSE to call normal error handler, NULL otherwise * @throws ErrorException * @internal */ - public static function errorHandler($severity, $message, $file, $line, $context) + public static function _errorHandler($severity, $message, $file, $line, $context) { if (self::$scream) { error_reporting(E_ALL | E_STRICT); @@ -296,7 +465,7 @@ public static function errorHandler($severity, $message, $file, $line, $context) $previous = isset($context['e']) && $context['e'] instanceof \Exception ? $context['e'] : NULL; $e = new ErrorException($message, 0, $severity, $file, $line, $previous); $e->context = $context; - self::exceptionHandler($e); + self::_exceptionHandler($e); } $e = new ErrorException($message, 0, $severity, $file, $line); @@ -306,22 +475,13 @@ public static function errorHandler($severity, $message, $file, $line, $context) } elseif (($severity & error_reporting()) !== $severity) { return FALSE; // calls normal error handler to fill-in error_get_last() - } elseif (($severity & self::$logSeverity) === $severity) { + } elseif (!self::$productionMode && (is_bool(self::$strictMode) ? self::$strictMode : ((self::$strictMode & $severity) === $severity))) { $e = new ErrorException($message, 0, $severity, $file, $line); $e->context = $context; - self::log($e, self::ERROR); - return NULL; - - } elseif (!self::$productionMode && !isset($_GET['_tracy_skip_error']) - && (is_bool(self::$strictMode) ? self::$strictMode : ((self::$strictMode & $severity) === $severity)) - ) { - $e = new ErrorException($message, 0, $severity, $file, $line); - $e->context = $context; - $e->skippable = TRUE; - self::exceptionHandler($e); + self::_exceptionHandler($e); } - $message = 'PHP ' . Helpers::errorTypeToString($severity) . ": $message"; + $message = 'PHP ' . (isset(self::$errorTypes[$severity]) ? self::$errorTypes[$severity] : 'Unknown error') . ": $message"; $count = & self::getBar()->getPanel(__CLASS__ . ':errors')->data["$file|$line|$message"]; if ($count++) { // repeated error @@ -338,89 +498,6 @@ public static function errorHandler($severity, $message, $file, $line, $context) } - private static function isHtmlMode() - { - return empty($_SERVER['HTTP_X_REQUESTED_WITH']) - && PHP_SAPI !== 'cli' - && !preg_match('#^Content-Type: (?!text/html)#im', implode("\n", headers_list())); - } - - - /********************* services ****************d*g**/ - - - /** - * @return BlueScreen - */ - public static function getBlueScreen() - { - if (!self::$blueScreen) { - self::$blueScreen = new BlueScreen; - self::$blueScreen->info = array( - 'PHP ' . PHP_VERSION, - isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : NULL, - 'Tracy ' . self::$version, - ); - } - return self::$blueScreen; - } - - - /** - * @return Bar - */ - public static function getBar() - { - if (!self::$bar) { - self::$bar = new Bar; - self::$bar->addPanel(new DefaultBarPanel('time')); - self::$bar->addPanel(new DefaultBarPanel('memory')); - self::$bar->addPanel(new DefaultBarPanel('errors'), __CLASS__ . ':errors'); // filled by errorHandler() - self::$bar->info = array( - 'PHP ' . PHP_VERSION, - isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : NULL, - 'Tracy ' . self::$version, - ); - } - return self::$bar; - } - - - /** - * @return void - */ - public static function setLogger(ILogger $logger) - { - self::$logger = $logger; - } - - - /** - * @return ILogger - */ - public static function getLogger() - { - if (!self::$logger) { - self::$logger = new Logger(self::$logDirectory, self::$email, self::getBlueScreen()); - self::$logger->directory = & self::$logDirectory; // back compatiblity - self::$logger->email = & self::$email; - } - return self::$logger; - } - - - /** - * @return ILogger - */ - public static function getFireLogger() - { - if (!self::$fireLogger) { - self::$fireLogger = new FireLogger; - } - return self::$fireLogger; - } - - /********************* useful tools ****************d*g**/ @@ -479,11 +556,7 @@ public static function timer($name = NULL) public static function barDump($var, $title = NULL, array $options = NULL) { if (!self::$productionMode) { - static $panel; - if (!$panel) { - self::getBar()->addPanel($panel = new DefaultBarPanel('dumps')); - } - $panel->data[] = array('title' => $title, 'dump' => Dumper::toHtml($var, (array) $options + array( + self::getBar()->getPanel(__CLASS__ . ':dumps')->data[] = array('title' => $title, 'dump' => Dumper::toHtml($var, (array) $options + array( Dumper::DEPTH => self::$maxDepth, Dumper::TRUNCATE => self::$maxLen, Dumper::LOCATION => self::$showLocation, @@ -493,19 +566,6 @@ public static function barDump($var, $title = NULL, array $options = NULL) } - /** - * Logs message or exception to file. - * @param string|Exception - * @return string logged error filename - */ - public static function log($message, $priority = ILogger::INFO) - { - if (self::getLogger()->directory) { - return self::getLogger()->log($message, $priority); - } - } - - /** * Sends message to FireLogger console. * @param mixed message to log @@ -518,4 +578,12 @@ public static function fireLog($message) } } + + private static function isHtmlMode() + { + return empty($_SERVER['HTTP_X_REQUESTED_WITH']) + && PHP_SAPI !== 'cli' + && !preg_match('#^Content-Type: (?!text/html)#im', implode("\n", headers_list())); + } + } diff --git a/vendor/tracy/tracy/src/Tracy/Dumper.php b/vendor/tracy/tracy/src/Tracy/Dumper.php index 098958b37..cdd8dbc5a 100644 --- a/vendor/tracy/tracy/src/Tracy/Dumper.php +++ b/vendor/tracy/tracy/src/Tracy/Dumper.php @@ -21,12 +21,7 @@ class Dumper TRUNCATE = 'truncate', // how truncate long strings? (defaults to 150) COLLAPSE = 'collapse', // always collapse? (defaults to false) COLLAPSE_COUNT = 'collapsecount', // how big array/object are collapsed? (defaults to 7) - LOCATION = 'location'; // show location string? (defaults to 0) - - const - LOCATION_SOURCE = 1, // shows where dump was called - LOCATION_LINK = 2, // appends clickable anchor - LOCATION_CLASS = 4; // shows where class is defined + LOCATION = 'location'; // show location string? (defaults to false) /** @var array */ public static $terminalColors = array( @@ -78,15 +73,13 @@ public static function toHtml($var, array $options = NULL) self::TRUNCATE => 150, self::COLLAPSE => FALSE, self::COLLAPSE_COUNT => 7, + self::LOCATION => FALSE, ); - $loc = & $options[self::LOCATION]; - $loc = $loc === TRUE ? ~0 : (int) $loc; - - list($file, $line, $code) = $loc ? self::findLocation() : NULL; + list($file, $line, $code) = $options[self::LOCATION] ? self::findLocation() : NULL; return '
' : '>')
+			. ($file ? Helpers::createHtml(' title="%in file % on line %" data-tracy-href="%"', "$code\n", $file, $line, Helpers::editorUri($file, $line)) . '>' : '>')
 			. self::dumpVar($var, $options)
-			. ($file && $loc & self::LOCATION_LINK ? 'in ' . Helpers::editorLink($file, $line) . '' : '')
+			. ($file ? 'in ' . Helpers::editorLink($file, $line) . '' : '')
 			. "
\n"; } @@ -223,17 +216,12 @@ private static function dumpObject(& $var, $options, $level) $fields = (array) $var; } - $editor = NULL; - if ($options[self::LOCATION] & self::LOCATION_CLASS) { - $rc = $var instanceof \Closure ? new \ReflectionFunction($var) : new \ReflectionClass($var); - $editor = Helpers::editorUri($rc->getFileName(), $rc->getStartLine()); - } + static $list = array(); + $rc = $var instanceof \Closure ? new \ReflectionFunction($var) : new \ReflectionClass($var); $out = 'getFileName(), $rc->getStartLine(), $editor) : '') + . ($options[self::LOCATION] && ($editor = Helpers::editorUri($rc->getFileName(), $rc->getStartLine())) ? ' data-tracy-href="' . htmlspecialchars($editor) . '"' : '') . '>' . get_class($var) . ' #' . substr(md5(spl_object_hash($var)), 0, 4) . ''; - static $list = array(); - if (empty($fields)) { return $out . "\n"; diff --git a/vendor/tracy/tracy/src/Tracy/FireLogger.php b/vendor/tracy/tracy/src/Tracy/FireLogger.php index e09176a8a..0ab114458 100644 --- a/vendor/tracy/tracy/src/Tracy/FireLogger.php +++ b/vendor/tracy/tracy/src/Tracy/FireLogger.php @@ -16,16 +16,15 @@ * @see http://firelogger.binaryage.com * @author David Grudl */ -class FireLogger implements ILogger +class FireLogger { - /** @var int */ - public $maxDepth = 3; + const DEBUG = 'debug', + INFO = 'info', + WARNING = 'warning', + ERROR = 'error', + CRITICAL = 'critical'; - /** @var int */ - public $maxLenght = 150; - - /** @var array */ - private $payload = array('logs' => array()); + private static $payload = array('logs' => array()); /** @@ -33,7 +32,7 @@ class FireLogger implements ILogger * @param mixed * @return bool was successful? */ - public function log($message, $priority = self::DEBUG) + public static function log($message, $priority = self::DEBUG) { if (!isset($_SERVER['HTTP_X_FIRELOGGER']) || headers_sent()) { return FALSE; @@ -42,7 +41,7 @@ public function log($message, $priority = self::DEBUG) $item = array( 'name' => 'PHP', 'level' => $priority, - 'order' => count($this->payload['logs']), + 'order' => count(self::$payload['logs']), 'time' => str_pad(number_format((microtime(TRUE) - Debugger::$time) * 1000, 1, '.', ' '), 8, '0', STR_PAD_LEFT) . ' ms', 'template' => '', 'message' => '', @@ -58,7 +57,7 @@ public function log($message, $priority = self::DEBUG) $e = array_shift($args); $trace = $e->getTrace(); if (isset($trace[0]['class']) && $trace[0]['class'] === 'Tracy\Debugger' - && ($trace[0]['function'] === 'shutdownHandler' || $trace[0]['function'] === 'errorHandler') + && ($trace[0]['function'] === '_shutdownHandler' || $trace[0]['function'] === '_errorHandler') ) { unset($trace[0]); } @@ -101,8 +100,8 @@ public function log($message, $priority = self::DEBUG) $item['args'] = $args; - $this->payload['logs'][] = $this->jsonDump($item, -1); - foreach (str_split(base64_encode(@json_encode($this->payload)), 4990) as $k => $v) { // intentionally @ + self::$payload['logs'][] = self::jsonDump($item, -1); + foreach (str_split(base64_encode(@json_encode(self::$payload)), 4990) as $k => $v) { // intentionally @ header("FireLogger-de11e-$k:$v"); } return TRUE; @@ -115,14 +114,14 @@ public function log($message, $priority = self::DEBUG) * @param int current recursion level * @return string */ - private function jsonDump(& $var, $level = 0) + private static function jsonDump(& $var, $level = 0) { if (is_bool($var) || is_null($var) || is_int($var) || is_float($var)) { return $var; } elseif (is_string($var)) { - if ($this->maxLenght && strlen($var) > $this->maxLenght) { - $var = substr($var, 0, $this->maxLenght) . " \xE2\x80\xA6 "; + if (Debugger::$maxLen && strlen($var) > Debugger::$maxLen) { + $var = substr($var, 0, Debugger::$maxLen) . " \xE2\x80\xA6 "; } return Helpers::fixEncoding($var); @@ -134,12 +133,12 @@ private function jsonDump(& $var, $level = 0) if (isset($var[$marker])) { return "\xE2\x80\xA6RECURSION\xE2\x80\xA6"; - } elseif ($level < $this->maxDepth || !$this->maxDepth) { + } elseif ($level < Debugger::$maxDepth || !Debugger::$maxDepth) { $var[$marker] = TRUE; $res = array(); foreach ($var as $k => & $v) { if ($k !== $marker) { - $res[$this->jsonDump($k)] = $this->jsonDump($v, $level + 1); + $res[self::jsonDump($k)] = self::jsonDump($v, $level + 1); } } unset($var[$marker]); @@ -155,14 +154,14 @@ private function jsonDump(& $var, $level = 0) if (in_array($var, $list, TRUE)) { return "\xE2\x80\xA6RECURSION\xE2\x80\xA6"; - } elseif ($level < $this->maxDepth || !$this->maxDepth) { + } elseif ($level < Debugger::$maxDepth || !Debugger::$maxDepth) { $list[] = $var; $res = array("\x00" => '(object) ' . get_class($var)); foreach ($arr as $k => & $v) { if ($k[0] === "\x00") { $k = substr($k, strrpos($k, "\x00") + 1); } - $res[$this->jsonDump($k)] = $this->jsonDump($v, $level + 1); + $res[self::jsonDump($k)] = self::jsonDump($v, $level + 1); } array_pop($list); return $res; diff --git a/vendor/tracy/tracy/src/Tracy/Helpers.php b/vendor/tracy/tracy/src/Tracy/Helpers.php index d94f2ccd4..56f24e563 100644 --- a/vendor/tracy/tracy/src/Tracy/Helpers.php +++ b/vendor/tracy/tracy/src/Tracy/Helpers.php @@ -30,7 +30,7 @@ public static function editorLink($file, $line) if (substr($dir, 0, strlen($base)) === $base) { $dir = '...' . substr($dir, strlen($base)); } - return self::formatHtml('%%%', + return self::createHtml('%%%', $editor, "$file:$line", rtrim($dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR, @@ -38,7 +38,7 @@ public static function editorLink($file, $line) $line ? ":$line" : '' ); } else { - return self::formatHtml('%', $file . ($line ? ":$line" : '')); + return self::createHtml('%', $file . ($line ? ":$line" : '')); } } @@ -55,7 +55,7 @@ public static function editorUri($file, $line) } - public static function formatHtml($mask) + public static function createHtml($mask) { $args = func_get_args(); return preg_replace_callback('#%#', function() use (& $args, & $count) { @@ -79,7 +79,6 @@ public static function findTrace(array $trace, $method, & $index = NULL) } - /** @internal */ public static function fixStack($exception) { if (function_exists('xdebug_get_function_stack')) { @@ -105,7 +104,11 @@ public static function fixStack($exception) } - /** @internal */ + /** + * Returns correctly UTF-8 encoded string. + * @param string byte stream to fix + * @return string + */ public static function fixEncoding($s) { if (PHP_VERSION_ID < 50400) { @@ -115,41 +118,4 @@ public static function fixEncoding($s) } } - - /** @internal */ - public static function errorTypeToString($type) - { - $types = array( - E_ERROR => 'Fatal Error', - E_USER_ERROR => 'User Error', - E_RECOVERABLE_ERROR => 'Recoverable Error', - E_CORE_ERROR => 'Core Error', - E_COMPILE_ERROR => 'Compile Error', - E_PARSE => 'Parse Error', - E_WARNING => 'Warning', - E_CORE_WARNING => 'Core Warning', - E_COMPILE_WARNING => 'Compile Warning', - E_USER_WARNING => 'User Warning', - E_NOTICE => 'Notice', - E_USER_NOTICE => 'User Notice', - E_STRICT => 'Strict standards', - E_DEPRECATED => 'Deprecated', - E_USER_DEPRECATED => 'User Deprecated', - ); - return isset($types[$type]) ? $types[$type] : 'Unknown error'; - } - - - /** @internal */ - public static function getSource() - { - if (isset($_SERVER['REQUEST_URI'])) { - return (!empty($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'], 'off') ? 'https://' : 'http://') - . (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '') - . $_SERVER['REQUEST_URI']; - } else { - return empty($_SERVER['argv']) ? 'CLI' : 'CLI: ' . implode(' ', $_SERVER['argv']); - } - } - } diff --git a/vendor/tracy/tracy/src/Tracy/ILogger.php b/vendor/tracy/tracy/src/Tracy/ILogger.php deleted file mode 100644 index 562f26b0a..000000000 --- a/vendor/tracy/tracy/src/Tracy/ILogger.php +++ /dev/null @@ -1,27 +0,0 @@ -directory = $directory; - $this->email = $email; - $this->blueScreen = $blueScreen; - $this->mailer = array($this, 'defaultMailer'); - } + /** @var string|array email or emails to which send error notifications */ + public $email; /** * Logs message or exception to file and sends email notification. - * @param string|Exception - * @param int one of constant ILogger::INFO, WARNING, ERROR (sends email), EXCEPTION (sends email), CRITICAL (sends email) - * @return string logged error filename + * @param string|array + * @param int one of constant Debugger::INFO, WARNING, ERROR (sends email), EXCEPTION (sends email), CRITICAL (sends email) + * @return void */ - public function log($value, $priority = self::INFO) + public function log($message, $priority = NULL) { if (!is_dir($this->directory)) { throw new \RuntimeException("Directory '$this->directory' is not found or is not directory."); } - $exceptionFile = $value instanceof \Exception ? $this->logException($value) : NULL; - $message = $this->formatMessage($value); + if (is_array($message)) { + $message = implode(' ', $message); + } + $message = preg_replace('#\s*\r?\n\s*#', ' ', trim($message)); $file = $this->directory . '/' . strtolower($priority ?: self::INFO) . '.log'; - if (!@file_put_contents($file, $message . PHP_EOL, FILE_APPEND | LOCK_EX)) { throw new \RuntimeException("Unable to write to log file '$file'. Is directory writable?"); } @@ -68,64 +64,6 @@ public function log($value, $priority = self::INFO) ) { call_user_func($this->mailer, $message, implode(', ', (array) $this->email)); } - - return $exceptionFile; - } - - - /** - * @return string - */ - private function formatMessage($value, $exceptionFile = NULL) - { - if ($value instanceof \Exception) { - while ($value) { - $tmp[] = ($value instanceof \ErrorException ? 'Fatal error: ' . $value->getMessage() : get_class($value) . ': ' . $value->getMessage()) - . ' in ' . $value->getFile() . ':' . $value->getLine(); - $value = $value->getPrevious(); - } - $value = implode($tmp, "\ncaused by "); - - } elseif (!is_string($value)) { - $value = Dumper::toText($value); - } - - $value = trim(preg_replace('#\s*\r?\n\s*#', ' ', $value)); - - return implode(' ', array( - @date('[Y-m-d H-i-s]'), - $value, - ' @ ' . Helpers::getSource(), - $exceptionFile ? ' @@ ' . basename($exceptionFile) : NULL - )); - } - - - /** - * @return string logged error filename - */ - private function logException(\Exception $exception) - { - $dir = strtr($this->directory . '/', '\\/', DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR); - $hash = md5(preg_replace('~(Resource id #)\d+~', '$1', $exception)); - foreach (new \DirectoryIterator($this->directory) as $file) { - if (strpos($file, $hash)) { - return $dir . $file; - } - } - - $file = $dir . 'exception-' . @date('Y-m-d-H-i-s') . "-$hash.html"; - if ($handle = @fopen($file, 'w')) { - ob_start(); // double buffer prevents sending HTTP headers in some PHP - ob_start(function($buffer) use ($handle) { fwrite($handle, $buffer); }, 4096); - $bs = $this->blueScreen ?: new BlueScreen; - $bs->render($exception); - ob_end_flush(); - ob_end_clean(); - fclose($handle); - } - - return $file; } @@ -134,9 +72,8 @@ private function logException(\Exception $exception) * @param string * @param string * @return void - * @internal */ - public function defaultMailer($message, $email) + public static function defaultMailer($message, $email) { $host = preg_replace('#[^\w.-]+#', '', isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : php_uname('n')); $parts = str_replace( diff --git a/vendor/tracy/tracy/src/Tracy/OutputDebugger.php b/vendor/tracy/tracy/src/Tracy/OutputDebugger.php index 5f60dbbe7..f47abe94b 100644 --- a/vendor/tracy/tracy/src/Tracy/OutputDebugger.php +++ b/vendor/tracy/tracy/src/Tracy/OutputDebugger.php @@ -42,7 +42,6 @@ public function start() } - /** @internal */ public function handler($s, $phase) { $trace = debug_backtrace(FALSE); diff --git a/vendor/tracy/tracy/src/Tracy/templates/bar.phtml b/vendor/tracy/tracy/src/Tracy/templates/bar.phtml index e3b85cfe7..4b39dc4e5 100644 --- a/vendor/tracy/tracy/src/Tracy/templates/bar.phtml +++ b/vendor/tracy/tracy/src/Tracy/templates/bar.phtml @@ -32,6 +32,13 @@ use Tracy; + + +
@@ -48,9 +47,7 @@ $counter = 0;

getCode() ? ' #' . $exception->getCode() : '') ?>

-

getMessage(), ENT_IGNORE) ?> - getMessage())) ?>" rel="noreferrer">search► - skip error►

+

getMessage(), ENT_IGNORE) ?> getMessage())) ?>" id="tracyBsSearch" rel="noreferrer">search►

getPrevious()): ?> @@ -104,7 +101,7 @@ $counter = 0; - +

Call stack

@@ -120,7 +117,7 @@ $counter = 0; inner-code - " class="tracy-toggle">source  + " class="tracy-toggle tracy-collapsed">source  " ?> @@ -131,11 +128,11 @@ $counter = 0;

-
id="tracyBsSrc">
+
id="tracyBsSrc">
-
">
+
">
@@ -150,7 +147,7 @@ $counter = 0; } foreach ($row['args'] as $k => $v) { echo '', htmlspecialchars(isset($params[$k]) ? '$' . $params[$k]->name : "#$k"), ''; - echo Dumper::toHtml($v, array(Dumper::LOCATION => Dumper::LOCATION_CLASS)); + echo Dumper::toHtml($v); echo "\n"; } ?> @@ -173,7 +170,7 @@ $counter = 0; context as $k => $v) { - echo '\n"; + echo '\n"; } ?>
$', htmlspecialchars($k), '', Dumper::toHtml($v, array(Dumper::LOCATION => Dumper::LOCATION_CLASS)), "
$', htmlspecialchars($k), '', Dumper::toHtml($v), "
@@ -219,7 +216,7 @@ $counter = 0; $v) echo '\n"; + foreach ($_SESSION as $k => $v) echo '\n"; ?>
', htmlspecialchars($k), '', $k === '__NF' ? 'Nette Session' : Dumper::toHtml($v, array(Dumper::LOCATION => Dumper::LOCATION_CLASS)), "
', htmlspecialchars($k), '', $k === '__NF' ? 'Nette Session' : Dumper::toHtml($v), "
@@ -231,7 +228,7 @@ $counter = 0;
$v) echo '\n"; + foreach ($_SESSION['__NF']['DATA'] as $k => $v) echo '\n"; ?>
', htmlspecialchars($k), '', Dumper::toHtml($v, array(Dumper::LOCATION => Dumper::LOCATION_CLASS)), "
', htmlspecialchars($k), '', Dumper::toHtml($v), "
@@ -333,8 +330,12 @@ $counter = 0;
    -
  • Report generated at
  • -
  • +
  • Report generated at
  • + +
  • + +
  • +
@@ -364,15 +365,6 @@ $counter = 0; bs.onclick = function(e) { e = e || window.event; - - if (e.ctrlKey) { - for (var link = e.target; link && (!link.getAttribute || !link.getAttribute('data-tracy-href')); link = link.parentNode) {} - if (link) { - location.href = link.getAttribute('data-tracy-href'); - return false; - } - } - if (e.shiftKey || e.altKey || e.ctrlKey || e.metaKey) { return; } diff --git a/vendor/tracy/tracy/src/tracy.php b/vendor/tracy/tracy/src/tracy.php index dcd29100a..f77af928e 100644 --- a/vendor/tracy/tracy/src/tracy.php +++ b/vendor/tracy/tracy/src/tracy.php @@ -1,7 +1,7 @@ raw('array('); $first = true; - foreach ($value as $key => $v) { + foreach ($value as $key => $value) { if (!$first) { $this->raw(', '); } $first = false; $this->repr($key); $this->raw(' => '); - $this->repr($v); + $this->repr($value); } $this->raw(')'); } else { @@ -267,9 +267,4 @@ public function outdent($step = 1) return $this; } - - public function getVarName() - { - return sprintf('__internal_%s', hash('sha256', uniqid(mt_rand(), true), false)); - } } diff --git a/vendor/twig/twig/lib/Twig/Environment.php b/vendor/twig/twig/lib/Twig/Environment.php index f1fec00fc..d3ba9be91 100644 --- a/vendor/twig/twig/lib/Twig/Environment.php +++ b/vendor/twig/twig/lib/Twig/Environment.php @@ -16,7 +16,7 @@ */ class Twig_Environment { - const VERSION = '1.16.1-DEV'; + const VERSION = '1.16.0'; protected $charset; protected $loader; diff --git a/vendor/twig/twig/lib/Twig/ExpressionParser.php b/vendor/twig/twig/lib/Twig/ExpressionParser.php index f685bad8e..01594f710 100644 --- a/vendor/twig/twig/lib/Twig/ExpressionParser.php +++ b/vendor/twig/twig/lib/Twig/ExpressionParser.php @@ -318,7 +318,7 @@ public function getFunctionNode($name, $line) throw new Twig_Error_Syntax('The "attribute" function takes at least two arguments (the variable and the attributes)', $line, $this->parser->getFilename()); } - return new Twig_Node_Expression_GetAttr($args->getNode(0), $args->getNode(1), count($args) > 2 ? $args->getNode(2) : null, Twig_Template::ANY_CALL, $line); + return new Twig_Node_Expression_GetAttr($args->getNode(0), $args->getNode(1), count($args) > 2 ? $args->getNode(2) : new Twig_Node_Expression_Array(array(), $line), Twig_Template::ANY_CALL, $line); default: if (null !== $alias = $this->parser->getImportedSymbol('function', $name)) { $arguments = new Twig_Node_Expression_Array(array(), $line); diff --git a/vendor/twig/twig/lib/Twig/Extension/Core.php b/vendor/twig/twig/lib/Twig/Extension/Core.php index ef3657698..750ef0ff8 100644 --- a/vendor/twig/twig/lib/Twig/Extension/Core.php +++ b/vendor/twig/twig/lib/Twig/Extension/Core.php @@ -672,7 +672,7 @@ function _twig_markup2string(&$value) function twig_array_merge($arr1, $arr2) { if (!is_array($arr1) || !is_array($arr2)) { - throw new Twig_Error_Runtime(sprintf('The merge filter only works with arrays or hashes; %s and %s given.', gettype($arr1), gettype($arr2))); + throw new Twig_Error_Runtime('The merge filter only works with arrays or hashes.'); } return array_merge($arr1, $arr2); @@ -702,7 +702,7 @@ function twig_slice(Twig_Environment $env, $item, $start, $length = null, $prese $item = (string) $item; if (function_exists('mb_get_info') && null !== $charset = $env->getCharset()) { - return (string) mb_substr($item, $start, null === $length ? mb_strlen($item, $charset) - $start : $length, $charset); + return mb_substr($item, $start, null === $length ? mb_strlen($item, $charset) - $start : $length, $charset); } return null === $length ? substr($item, $start) : substr($item, $start, $length); @@ -944,7 +944,7 @@ function twig_escape_filter(Twig_Environment $env, $string, $strategy = 'html', static $htmlspecialcharsCharsets; if (null === $htmlspecialcharsCharsets) { - if (defined('HHVM_VERSION')) { + if ('hiphop' === substr(PHP_VERSION, -6)) { $htmlspecialcharsCharsets = array('utf-8' => true, 'UTF-8' => true); } else { $htmlspecialcharsCharsets = array( diff --git a/vendor/twig/twig/lib/Twig/Node/AutoEscape.php b/vendor/twig/twig/lib/Twig/Node/AutoEscape.php index fcabf9033..8f190e0bd 100644 --- a/vendor/twig/twig/lib/Twig/Node/AutoEscape.php +++ b/vendor/twig/twig/lib/Twig/Node/AutoEscape.php @@ -30,7 +30,7 @@ public function __construct($value, Twig_NodeInterface $body, $lineno, $tag = 'a /** * Compiles the node to PHP. * - * @param Twig_Compiler $compiler A Twig_Compiler instance + * @param Twig_Compiler A Twig_Compiler instance */ public function compile(Twig_Compiler $compiler) { diff --git a/vendor/twig/twig/lib/Twig/Node/Block.php b/vendor/twig/twig/lib/Twig/Node/Block.php index 989e4a0ca..50eb67ed8 100644 --- a/vendor/twig/twig/lib/Twig/Node/Block.php +++ b/vendor/twig/twig/lib/Twig/Node/Block.php @@ -25,7 +25,7 @@ public function __construct($name, Twig_NodeInterface $body, $lineno, $tag = nul /** * Compiles the node to PHP. * - * @param Twig_Compiler $compiler A Twig_Compiler instance + * @param Twig_Compiler A Twig_Compiler instance */ public function compile(Twig_Compiler $compiler) { diff --git a/vendor/twig/twig/lib/Twig/Node/BlockReference.php b/vendor/twig/twig/lib/Twig/Node/BlockReference.php index a05ea045b..013e369eb 100644 --- a/vendor/twig/twig/lib/Twig/Node/BlockReference.php +++ b/vendor/twig/twig/lib/Twig/Node/BlockReference.php @@ -25,7 +25,7 @@ public function __construct($name, $lineno, $tag = null) /** * Compiles the node to PHP. * - * @param Twig_Compiler $compiler A Twig_Compiler instance + * @param Twig_Compiler A Twig_Compiler instance */ public function compile(Twig_Compiler $compiler) { diff --git a/vendor/twig/twig/lib/Twig/Node/Do.php b/vendor/twig/twig/lib/Twig/Node/Do.php index 9981bc16b..c528066b2 100644 --- a/vendor/twig/twig/lib/Twig/Node/Do.php +++ b/vendor/twig/twig/lib/Twig/Node/Do.php @@ -24,7 +24,7 @@ public function __construct(Twig_Node_Expression $expr, $lineno, $tag = null) /** * Compiles the node to PHP. * - * @param Twig_Compiler $compiler A Twig_Compiler instance + * @param Twig_Compiler A Twig_Compiler instance */ public function compile(Twig_Compiler $compiler) { diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Array.php b/vendor/twig/twig/lib/Twig/Node/Expression/Array.php index 6cf7ca144..1da785fe4 100644 --- a/vendor/twig/twig/lib/Twig/Node/Expression/Array.php +++ b/vendor/twig/twig/lib/Twig/Node/Expression/Array.php @@ -63,7 +63,7 @@ public function addElement(Twig_Node_Expression $value, Twig_Node_Expression $ke /** * Compiles the node to PHP. * - * @param Twig_Compiler $compiler A Twig_Compiler instance + * @param Twig_Compiler A Twig_Compiler instance */ public function compile(Twig_Compiler $compiler) { diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/AssignName.php b/vendor/twig/twig/lib/Twig/Node/Expression/AssignName.php index 4d5dbdb90..2ddea78cf 100644 --- a/vendor/twig/twig/lib/Twig/Node/Expression/AssignName.php +++ b/vendor/twig/twig/lib/Twig/Node/Expression/AssignName.php @@ -15,7 +15,7 @@ class Twig_Node_Expression_AssignName extends Twig_Node_Expression_Name /** * Compiles the node to PHP. * - * @param Twig_Compiler $compiler A Twig_Compiler instance + * @param Twig_Compiler A Twig_Compiler instance */ public function compile(Twig_Compiler $compiler) { diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Binary.php b/vendor/twig/twig/lib/Twig/Node/Expression/Binary.php index 5c383d155..9dd5de2c8 100644 --- a/vendor/twig/twig/lib/Twig/Node/Expression/Binary.php +++ b/vendor/twig/twig/lib/Twig/Node/Expression/Binary.php @@ -19,7 +19,7 @@ public function __construct(Twig_NodeInterface $left, Twig_NodeInterface $right, /** * Compiles the node to PHP. * - * @param Twig_Compiler $compiler A Twig_Compiler instance + * @param Twig_Compiler A Twig_Compiler instance */ public function compile(Twig_Compiler $compiler) { diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Binary/EndsWith.php b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/EndsWith.php index 93b3b96f0..5de6c72d2 100644 --- a/vendor/twig/twig/lib/Twig/Node/Expression/Binary/EndsWith.php +++ b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/EndsWith.php @@ -12,14 +12,14 @@ class Twig_Node_Expression_Binary_EndsWith extends Twig_Node_Expression_Binary { public function compile(Twig_Compiler $compiler) { - $left = $compiler->getVarName(); - $right = $compiler->getVarName(); $compiler - ->raw(sprintf('(is_string($%s = ', $left)) + ->raw('(0 === substr_compare(') ->subcompile($this->getNode('left')) - ->raw(sprintf(') && is_string($%s = ', $right)) + ->raw(', ') ->subcompile($this->getNode('right')) - ->raw(sprintf(') && (\'\' === $%2$s || $%2$s === substr($%1$s, -strlen($%2$s))))', $left, $right)) + ->raw(', -strlen(') + ->subcompile($this->getNode('right')) + ->raw(')))') ; } diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Binary/FloorDiv.php b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/FloorDiv.php index d3518b558..7fbd0556f 100644 --- a/vendor/twig/twig/lib/Twig/Node/Expression/Binary/FloorDiv.php +++ b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/FloorDiv.php @@ -13,7 +13,7 @@ class Twig_Node_Expression_Binary_FloorDiv extends Twig_Node_Expression_Binary /** * Compiles the node to PHP. * - * @param Twig_Compiler $compiler A Twig_Compiler instance + * @param Twig_Compiler A Twig_Compiler instance */ public function compile(Twig_Compiler $compiler) { diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Binary/In.php b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/In.php index 1d485b61c..788f9377a 100644 --- a/vendor/twig/twig/lib/Twig/Node/Expression/Binary/In.php +++ b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/In.php @@ -13,7 +13,7 @@ class Twig_Node_Expression_Binary_In extends Twig_Node_Expression_Binary /** * Compiles the node to PHP. * - * @param Twig_Compiler $compiler A Twig_Compiler instance + * @param Twig_Compiler A Twig_Compiler instance */ public function compile(Twig_Compiler $compiler) { diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Binary/NotIn.php b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/NotIn.php index 8f215f1c1..f347b7b6e 100644 --- a/vendor/twig/twig/lib/Twig/Node/Expression/Binary/NotIn.php +++ b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/NotIn.php @@ -13,7 +13,7 @@ class Twig_Node_Expression_Binary_NotIn extends Twig_Node_Expression_Binary /** * Compiles the node to PHP. * - * @param Twig_Compiler $compiler A Twig_Compiler instance + * @param Twig_Compiler A Twig_Compiler instance */ public function compile(Twig_Compiler $compiler) { diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Power.php b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Power.php index 6cd3a217c..b2c590405 100644 --- a/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Power.php +++ b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Power.php @@ -13,7 +13,7 @@ class Twig_Node_Expression_Binary_Power extends Twig_Node_Expression_Binary /** * Compiles the node to PHP. * - * @param Twig_Compiler $compiler A Twig_Compiler instance + * @param Twig_Compiler A Twig_Compiler instance */ public function compile(Twig_Compiler $compiler) { diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Range.php b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Range.php index fc102fed3..bea4f2a60 100644 --- a/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Range.php +++ b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Range.php @@ -13,7 +13,7 @@ class Twig_Node_Expression_Binary_Range extends Twig_Node_Expression_Binary /** * Compiles the node to PHP. * - * @param Twig_Compiler $compiler A Twig_Compiler instance + * @param Twig_Compiler A Twig_Compiler instance */ public function compile(Twig_Compiler $compiler) { diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Binary/StartsWith.php b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/StartsWith.php index d2e30d66e..eb8c107cb 100644 --- a/vendor/twig/twig/lib/Twig/Node/Expression/Binary/StartsWith.php +++ b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/StartsWith.php @@ -12,14 +12,12 @@ class Twig_Node_Expression_Binary_StartsWith extends Twig_Node_Expression_Binary { public function compile(Twig_Compiler $compiler) { - $left = $compiler->getVarName(); - $right = $compiler->getVarName(); $compiler - ->raw(sprintf('(is_string($%s = ', $left)) + ->raw('(0 === strpos(') ->subcompile($this->getNode('left')) - ->raw(sprintf(') && is_string($%s = ', $right)) + ->raw(', ') ->subcompile($this->getNode('right')) - ->raw(sprintf(') && (\'\' === $%2$s || 0 === strpos($%1$s, $%2$s)))', $left, $right)) + ->raw('))') ; } diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/BlockReference.php b/vendor/twig/twig/lib/Twig/Node/Expression/BlockReference.php index 4ddb2cf44..647196eb5 100644 --- a/vendor/twig/twig/lib/Twig/Node/Expression/BlockReference.php +++ b/vendor/twig/twig/lib/Twig/Node/Expression/BlockReference.php @@ -25,7 +25,7 @@ public function __construct(Twig_NodeInterface $name, $asString = false, $lineno /** * Compiles the node to PHP. * - * @param Twig_Compiler $compiler A Twig_Compiler instance + * @param Twig_Compiler A Twig_Compiler instance */ public function compile(Twig_Compiler $compiler) { diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/ExtensionReference.php b/vendor/twig/twig/lib/Twig/Node/Expression/ExtensionReference.php index db06abb0a..00ac6701f 100644 --- a/vendor/twig/twig/lib/Twig/Node/Expression/ExtensionReference.php +++ b/vendor/twig/twig/lib/Twig/Node/Expression/ExtensionReference.php @@ -24,7 +24,7 @@ public function __construct($name, $lineno, $tag = null) /** * Compiles the node to PHP. * - * @param Twig_Compiler $compiler A Twig_Compiler instance + * @param Twig_Compiler A Twig_Compiler instance */ public function compile(Twig_Compiler $compiler) { diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/GetAttr.php b/vendor/twig/twig/lib/Twig/Node/Expression/GetAttr.php index 6ce61111c..55d9fcc32 100644 --- a/vendor/twig/twig/lib/Twig/Node/Expression/GetAttr.php +++ b/vendor/twig/twig/lib/Twig/Node/Expression/GetAttr.php @@ -11,7 +11,7 @@ */ class Twig_Node_Expression_GetAttr extends Twig_Node_Expression { - public function __construct(Twig_Node_Expression $node, Twig_Node_Expression $attribute, Twig_Node_Expression $arguments = null, $type, $lineno) + public function __construct(Twig_Node_Expression $node, Twig_Node_Expression $attribute, Twig_Node_Expression_Array $arguments, $type, $lineno) { parent::__construct(array('node' => $node, 'attribute' => $attribute, 'arguments' => $arguments), array('type' => $type, 'is_defined_test' => false, 'ignore_strict_check' => false, 'disable_c_ext' => false), $lineno); } @@ -32,30 +32,20 @@ public function compile(Twig_Compiler $compiler) $compiler->raw(', ')->subcompile($this->getNode('attribute')); - // only generate optional arguments when needed (to make generated code more readable) - $needFourth = $this->getAttribute('ignore_strict_check'); - $needThird = $needFourth || $this->getAttribute('is_defined_test'); - $needSecond = $needThird || Twig_Template::ANY_CALL !== $this->getAttribute('type'); - $needFirst = $needSecond || null !== $this->getNode('arguments'); - - if ($needFirst) { - if (null !== $this->getNode('arguments')) { - $compiler->raw(', ')->subcompile($this->getNode('arguments')); - } else { - $compiler->raw(', array()'); - } - } + if (count($this->getNode('arguments')) || Twig_Template::ANY_CALL !== $this->getAttribute('type') || $this->getAttribute('is_defined_test') || $this->getAttribute('ignore_strict_check')) { + $compiler->raw(', ')->subcompile($this->getNode('arguments')); - if ($needSecond) { - $compiler->raw(', ')->repr($this->getAttribute('type')); - } + if (Twig_Template::ANY_CALL !== $this->getAttribute('type') || $this->getAttribute('is_defined_test') || $this->getAttribute('ignore_strict_check')) { + $compiler->raw(', ')->repr($this->getAttribute('type')); + } - if ($needThird) { - $compiler->raw(', ')->repr($this->getAttribute('is_defined_test')); - } + if ($this->getAttribute('is_defined_test') || $this->getAttribute('ignore_strict_check')) { + $compiler->raw(', '.($this->getAttribute('is_defined_test') ? 'true' : 'false')); + } - if ($needFourth) { - $compiler->raw(', ')->repr($this->getAttribute('ignore_strict_check')); + if ($this->getAttribute('ignore_strict_check')) { + $compiler->raw(', '.($this->getAttribute('ignore_strict_check') ? 'true' : 'false')); + } } $compiler->raw(')'); diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Parent.php b/vendor/twig/twig/lib/Twig/Node/Expression/Parent.php index a22ce0386..dcf618c04 100644 --- a/vendor/twig/twig/lib/Twig/Node/Expression/Parent.php +++ b/vendor/twig/twig/lib/Twig/Node/Expression/Parent.php @@ -25,7 +25,7 @@ public function __construct($name, $lineno, $tag = null) /** * Compiles the node to PHP. * - * @param Twig_Compiler $compiler A Twig_Compiler instance + * @param Twig_Compiler A Twig_Compiler instance */ public function compile(Twig_Compiler $compiler) { diff --git a/vendor/twig/twig/lib/Twig/Node/Flush.php b/vendor/twig/twig/lib/Twig/Node/Flush.php index 20d6aab43..0467ddcef 100644 --- a/vendor/twig/twig/lib/Twig/Node/Flush.php +++ b/vendor/twig/twig/lib/Twig/Node/Flush.php @@ -24,7 +24,7 @@ public function __construct($lineno, $tag) /** * Compiles the node to PHP. * - * @param Twig_Compiler $compiler A Twig_Compiler instance + * @param Twig_Compiler A Twig_Compiler instance */ public function compile(Twig_Compiler $compiler) { diff --git a/vendor/twig/twig/lib/Twig/Node/For.php b/vendor/twig/twig/lib/Twig/Node/For.php index c54a23cc9..d1ff371da 100644 --- a/vendor/twig/twig/lib/Twig/Node/For.php +++ b/vendor/twig/twig/lib/Twig/Node/For.php @@ -33,7 +33,7 @@ public function __construct(Twig_Node_Expression_AssignName $keyTarget, Twig_Nod /** * Compiles the node to PHP. * - * @param Twig_Compiler $compiler A Twig_Compiler instance + * @param Twig_Compiler A Twig_Compiler instance */ public function compile(Twig_Compiler $compiler) { diff --git a/vendor/twig/twig/lib/Twig/Node/ForLoop.php b/vendor/twig/twig/lib/Twig/Node/ForLoop.php index d330283e7..b8841583d 100644 --- a/vendor/twig/twig/lib/Twig/Node/ForLoop.php +++ b/vendor/twig/twig/lib/Twig/Node/ForLoop.php @@ -24,7 +24,7 @@ public function __construct($lineno, $tag = null) /** * Compiles the node to PHP. * - * @param Twig_Compiler $compiler A Twig_Compiler instance + * @param Twig_Compiler A Twig_Compiler instance */ public function compile(Twig_Compiler $compiler) { diff --git a/vendor/twig/twig/lib/Twig/Node/If.php b/vendor/twig/twig/lib/Twig/Node/If.php index 980274e5c..b42d10755 100644 --- a/vendor/twig/twig/lib/Twig/Node/If.php +++ b/vendor/twig/twig/lib/Twig/Node/If.php @@ -25,7 +25,7 @@ public function __construct(Twig_NodeInterface $tests, Twig_NodeInterface $else /** * Compiles the node to PHP. * - * @param Twig_Compiler $compiler A Twig_Compiler instance + * @param Twig_Compiler A Twig_Compiler instance */ public function compile(Twig_Compiler $compiler) { diff --git a/vendor/twig/twig/lib/Twig/Node/Import.php b/vendor/twig/twig/lib/Twig/Node/Import.php index 230665541..99efc0911 100644 --- a/vendor/twig/twig/lib/Twig/Node/Import.php +++ b/vendor/twig/twig/lib/Twig/Node/Import.php @@ -24,7 +24,7 @@ public function __construct(Twig_Node_Expression $expr, Twig_Node_Expression $va /** * Compiles the node to PHP. * - * @param Twig_Compiler $compiler A Twig_Compiler instance + * @param Twig_Compiler A Twig_Compiler instance */ public function compile(Twig_Compiler $compiler) { diff --git a/vendor/twig/twig/lib/Twig/Node/Include.php b/vendor/twig/twig/lib/Twig/Node/Include.php index 06548884a..860aedfe2 100644 --- a/vendor/twig/twig/lib/Twig/Node/Include.php +++ b/vendor/twig/twig/lib/Twig/Node/Include.php @@ -25,7 +25,7 @@ public function __construct(Twig_Node_Expression $expr, Twig_Node_Expression $va /** * Compiles the node to PHP. * - * @param Twig_Compiler $compiler A Twig_Compiler instance + * @param Twig_Compiler A Twig_Compiler instance */ public function compile(Twig_Compiler $compiler) { @@ -60,26 +60,40 @@ public function compile(Twig_Compiler $compiler) protected function addGetTemplate(Twig_Compiler $compiler) { - $method = $this->getNode('expr') instanceof Twig_Node_Expression_Constant ? 'loadTemplate' : 'resolveTemplate'; - $compiler - ->write(sprintf('$this->env->%s(', $method)) - ->subcompile($this->getNode('expr')) - ->raw(')') - ; + if ($this->getNode('expr') instanceof Twig_Node_Expression_Constant) { + $compiler + ->write("\$this->env->loadTemplate(") + ->subcompile($this->getNode('expr')) + ->raw(")") + ; + } else { + $compiler + ->write("\$template = \$this->env->resolveTemplate(") + ->subcompile($this->getNode('expr')) + ->raw(");\n") + ->write('$template') + ; + } } protected function addTemplateArguments(Twig_Compiler $compiler) { - if (null === $this->getNode('variables')) { - $compiler->raw(false === $this->getAttribute('only') ? '$context' : 'array()'); - } elseif (false === $this->getAttribute('only')) { - $compiler - ->raw('array_merge($context, ') - ->subcompile($this->getNode('variables')) - ->raw(')') - ; + if (false === $this->getAttribute('only')) { + if (null === $this->getNode('variables')) { + $compiler->raw('$context'); + } else { + $compiler + ->raw('array_merge($context, ') + ->subcompile($this->getNode('variables')) + ->raw(')') + ; + } } else { - $compiler->subcompile($this->getNode('variables')); + if (null === $this->getNode('variables')) { + $compiler->raw('array()'); + } else { + $compiler->subcompile($this->getNode('variables')); + } } } } diff --git a/vendor/twig/twig/lib/Twig/Node/Macro.php b/vendor/twig/twig/lib/Twig/Node/Macro.php index 47c2915f7..89910618b 100644 --- a/vendor/twig/twig/lib/Twig/Node/Macro.php +++ b/vendor/twig/twig/lib/Twig/Node/Macro.php @@ -24,7 +24,7 @@ public function __construct($name, Twig_NodeInterface $body, Twig_NodeInterface /** * Compiles the node to PHP. * - * @param Twig_Compiler $compiler A Twig_Compiler instance + * @param Twig_Compiler A Twig_Compiler instance */ public function compile(Twig_Compiler $compiler) { diff --git a/vendor/twig/twig/lib/Twig/Node/Module.php b/vendor/twig/twig/lib/Twig/Node/Module.php index 9f66b28d5..3f8017572 100644 --- a/vendor/twig/twig/lib/Twig/Node/Module.php +++ b/vendor/twig/twig/lib/Twig/Node/Module.php @@ -31,7 +31,7 @@ public function setIndex($index) /** * Compiles the node to PHP. * - * @param Twig_Compiler $compiler A Twig_Compiler instance + * @param Twig_Compiler A Twig_Compiler instance */ public function compile(Twig_Compiler $compiler) { diff --git a/vendor/twig/twig/lib/Twig/Node/Print.php b/vendor/twig/twig/lib/Twig/Node/Print.php index 42635361a..b0c41d1d9 100644 --- a/vendor/twig/twig/lib/Twig/Node/Print.php +++ b/vendor/twig/twig/lib/Twig/Node/Print.php @@ -25,7 +25,7 @@ public function __construct(Twig_Node_Expression $expr, $lineno, $tag = null) /** * Compiles the node to PHP. * - * @param Twig_Compiler $compiler A Twig_Compiler instance + * @param Twig_Compiler A Twig_Compiler instance */ public function compile(Twig_Compiler $compiler) { diff --git a/vendor/twig/twig/lib/Twig/Node/Sandbox.php b/vendor/twig/twig/lib/Twig/Node/Sandbox.php index 8ca772bcd..8cf3ed44f 100644 --- a/vendor/twig/twig/lib/Twig/Node/Sandbox.php +++ b/vendor/twig/twig/lib/Twig/Node/Sandbox.php @@ -24,7 +24,7 @@ public function __construct(Twig_NodeInterface $body, $lineno, $tag = null) /** * Compiles the node to PHP. * - * @param Twig_Compiler $compiler A Twig_Compiler instance + * @param Twig_Compiler A Twig_Compiler instance */ public function compile(Twig_Compiler $compiler) { diff --git a/vendor/twig/twig/lib/Twig/Node/SandboxedPrint.php b/vendor/twig/twig/lib/Twig/Node/SandboxedPrint.php index 91872cccc..73dfaa963 100644 --- a/vendor/twig/twig/lib/Twig/Node/SandboxedPrint.php +++ b/vendor/twig/twig/lib/Twig/Node/SandboxedPrint.php @@ -29,7 +29,7 @@ public function __construct(Twig_Node_Expression $expr, $lineno, $tag = null) /** * Compiles the node to PHP. * - * @param Twig_Compiler $compiler A Twig_Compiler instance + * @param Twig_Compiler A Twig_Compiler instance */ public function compile(Twig_Compiler $compiler) { diff --git a/vendor/twig/twig/lib/Twig/Node/Set.php b/vendor/twig/twig/lib/Twig/Node/Set.php index 407d14734..4c9c16ce2 100644 --- a/vendor/twig/twig/lib/Twig/Node/Set.php +++ b/vendor/twig/twig/lib/Twig/Node/Set.php @@ -39,7 +39,7 @@ public function __construct($capture, Twig_NodeInterface $names, Twig_NodeInterf /** * Compiles the node to PHP. * - * @param Twig_Compiler $compiler A Twig_Compiler instance + * @param Twig_Compiler A Twig_Compiler instance */ public function compile(Twig_Compiler $compiler) { diff --git a/vendor/twig/twig/lib/Twig/Node/Spaceless.php b/vendor/twig/twig/lib/Twig/Node/Spaceless.php index 1478c59ac..7555fa0f1 100644 --- a/vendor/twig/twig/lib/Twig/Node/Spaceless.php +++ b/vendor/twig/twig/lib/Twig/Node/Spaceless.php @@ -26,7 +26,7 @@ public function __construct(Twig_NodeInterface $body, $lineno, $tag = 'spaceless /** * Compiles the node to PHP. * - * @param Twig_Compiler $compiler A Twig_Compiler instance + * @param Twig_Compiler A Twig_Compiler instance */ public function compile(Twig_Compiler $compiler) { diff --git a/vendor/twig/twig/lib/Twig/Node/Text.php b/vendor/twig/twig/lib/Twig/Node/Text.php index 6863604e5..21bdcea14 100644 --- a/vendor/twig/twig/lib/Twig/Node/Text.php +++ b/vendor/twig/twig/lib/Twig/Node/Text.php @@ -25,7 +25,7 @@ public function __construct($data, $lineno) /** * Compiles the node to PHP. * - * @param Twig_Compiler $compiler A Twig_Compiler instance + * @param Twig_Compiler A Twig_Compiler instance */ public function compile(Twig_Compiler $compiler) { diff --git a/vendor/twig/twig/lib/Twig/NodeInterface.php b/vendor/twig/twig/lib/Twig/NodeInterface.php index 8077349b3..43afd0df0 100644 --- a/vendor/twig/twig/lib/Twig/NodeInterface.php +++ b/vendor/twig/twig/lib/Twig/NodeInterface.php @@ -21,7 +21,7 @@ interface Twig_NodeInterface extends Countable, IteratorAggregate /** * Compiles the node to PHP. * - * @param Twig_Compiler $compiler A Twig_Compiler instance + * @param Twig_Compiler A Twig_Compiler instance */ public function compile(Twig_Compiler $compiler); diff --git a/vendor/twig/twig/lib/Twig/Template.php b/vendor/twig/twig/lib/Twig/Template.php index 63910dacc..1e23e8b4f 100644 --- a/vendor/twig/twig/lib/Twig/Template.php +++ b/vendor/twig/twig/lib/Twig/Template.php @@ -379,11 +379,7 @@ protected function getAttribute($object, $item, array $arguments = array(), $typ } elseif (is_object($object)) { $message = sprintf('Impossible to access a key "%s" on an object of class "%s" that does not implement ArrayAccess interface', $item, get_class($object)); } elseif (is_array($object)) { - if (empty($object)) { - $message = sprintf('Key "%s" does not exist as the array is empty', $arrayItem); - } else { - $message = sprintf('Key "%s" for array with keys "%s" does not exist', $arrayItem, implode(', ', array_keys($object))); - } + $message = sprintf('Key "%s" for array with keys "%s" does not exist', $arrayItem, implode(', ', array_keys($object))); } elseif (Twig_Template::ARRAY_CALL === $type) { $message = sprintf('Impossible to access a key ("%s") on a %s variable ("%s")', $item, gettype($object), $object); } else { diff --git a/web.config b/web.config index 8083c163f..72a041f63 100755 --- a/web.config +++ b/web.config @@ -3,6 +3,7 @@ +