diff --git a/sparks/assets/1.5.1/.gitignore b/sparks/assets/1.5.1/.gitignore new file mode 100644 index 0000000..496ee2c --- /dev/null +++ b/sparks/assets/1.5.1/.gitignore @@ -0,0 +1 @@ +.DS_Store \ No newline at end of file diff --git a/sparks/assets/1.5.1/README.markdown b/sparks/assets/1.5.1/README.markdown new file mode 100644 index 0000000..e47df1e --- /dev/null +++ b/sparks/assets/1.5.1/README.markdown @@ -0,0 +1,98 @@ +# Simple Assets Library + +A simple assets library that has the ability to combine and minify your JavaScript and CSS assets. +Additionally there's a LessCSS compiler and a CoffeeScript compiler. + +## Third Party Libraries + +The libraries JSMin, CSSMin, LessPHP and CoffeeScript-PHP are all created by third parties, but they're included in this package for convenience. + +## Requirements + +1. PHP 5.3+ +2. CodeIgniter 2.1 +3. Directory structure for the assets files, with a writeable cache directory + +## Documentation + +Set all your preferences in the config file (assets directories, options to minify and combine). +Now you can use the helper methods in your views like this: + + + + +You can load the javascript files from the CDN using: + + + +There's also a method for clearing all cached files: + + + +The default configuration assumes your assets directory is in the root of your project. Be sure to set the permissions for the cache directory so it can be writeable. + +Note about "freeze" option: This basicly tells the lib not to scan the files and folders for new and changed files, but to pull all the info from the info.cache file. This speeds up the whole process a little bit. Useful for apps with a bigger load in production. + +### LESS / CoffeeScript + +Files with extensions .less and .coffee will automatically be processed through appropriate libraries. + +### Groups + +There's also a possibility to define groups of assets. This can be useful when for e.g. you want separate scripts in you page header, and others in the footer. This can be accomplished like this: + + + + +The same thing will work with CSS files. You can use this to show groups of CSS files for specific pages: + + + + +### Importing CSS files (@import) + +Including files via *@import* should work just fine, just be sure to use proper paths. Example of a stylesheet would be something like this: + + @import "bootstrap/bootstrap.less"; + @import "libs/fancybox.css"; + + body { background: #f2f2f2; } + + … + +Just keep in mind when using *@import* that those files are not scanned for changes and the cache wont be cleared in case you change a file that is included via *@import*. + +### Images + +A helper is also provided to display images from the directory setup in the config. + + + +You can also generate the img tag directly using a similar syntax as in the CodeIgniter HTML helper. + + 'Logo')); ?> + +### Overriding CI base_url +By default Assets uses codeIgniter's `$config['base_url']` config to determine the URL for your assets. However this can be overwritten by defining the following configuration item: +```php +$config['assets']['base_url'] = 'https://example.com'; +``` +This will allow you to define your assets on a seperate static domain, or specify `https` for assets seperately from your CI application. + +## Frameworks / Libraries + +The library has been tested with Twitter Bootstrap 2.0.1 and HTML5 Boilerplate 3.0. It wont work with the latest Bootstrap 2.0.2 because of a problem in LessPHP. I hope this will be fixed soon. And if you happen to use the library with a different framework (bootstrap), give me a shout and I'll put it on this list. So here it is: + +* Twitter Bootstrap 2.0.1 (2.0.2 not working yet) LESS +* HTML5 Boilerplate 3.0 + +## Directory structure example + + /application + /assets + /cache + /css + /images + /js + /sparks + /system diff --git a/sparks/assets/1.5.1/config/assets.php b/sparks/assets/1.5.1/config/assets.php new file mode 100644 index 0000000..6b1a797 --- /dev/null +++ b/sparks/assets/1.5.1/config/assets.php @@ -0,0 +1,48 @@ + array( + 'default_version' => '1.7.2', + 'path' => 'https://ajax.googleapis.com/ajax/libs/jquery/%version%/jquery.min.js', + ), + 'jqueryui' => array( + 'default_version' => '1.8.18', + 'path' => 'https://ajax.googleapis.com/ajax/libs/jqueryui/%version%/jquery-ui.min.js', + ), + 'jquery-validate' => array( + 'default_version' => '1.9', + 'path' => 'http://ajax.aspnetcdn.com/ajax/jquery.validate/%version%/jquery.validate.min.js', + ), + 'jquery-mobile' => array( + 'default_version' => '1.0.1', + 'path' => 'http://ajax.aspnetcdn.com/ajax/jquery.mobile/%version%/jquery.mobile-%version%.min.js', + ), + 'jquery-mobile-css' => array( + 'default_version' => '1.0.1', + 'path' => 'http://ajax.aspnetcdn.com/ajax/jquery.mobile/%version%/jquery.mobile-%version%.min.css', + ), + 'jquery-cycle' => array( + 'default_version' => '2.99', + 'path' => 'http://ajax.aspnetcdn.com/ajax/jquery.cycle/%version%/jquery.cycle.all.min.js', + ), + 'chrome-frame' => array( + 'default_version' => '1.0.2', + 'path' => 'https://ajax.googleapis.com/ajax/libs/chrome-frame/%version%/CFInstall.min.js', + ), + 'ext-core' => array( + 'default_version' => '3.1.0', + 'path' => 'https://ajax.googleapis.com/ajax/libs/ext-core/%version%/ext-core.js', + ), + 'dojo' => array( + 'default_version' => '1.7.2', + 'path' => 'https://ajax.googleapis.com/ajax/libs/dojo/%version%/dojo/dojo.js', + ), + 'mootools' => array( + 'default_version' => '1.4.5', + 'path' => 'https://ajax.googleapis.com/ajax/libs/mootools/%version%/mootools-yui-compressed.js', + ), + 'prototype' => array( + 'default_version' => '1.7.0.0', + 'path' => 'https://ajax.googleapis.com/ajax/libs/prototype/%version%/prototype.js', + ), + 'scriptaculous' => array( + 'default_version' => '1.9.0', + 'path' => 'https://ajax.googleapis.com/ajax/libs/scriptaculous/%version%/scriptaculous.js', + ), + 'swfobject' => array( + 'default_version' => '2.2', + 'path' => 'https://ajax.googleapis.com/ajax/libs/swfobject/%version%/swfobject.js', + ), + 'webfont' => array( + 'default_version' => '1.0.26', + 'path' => 'https://ajax.googleapis.com/ajax/libs/webfont/%version%/webfont.js', + ), +); \ No newline at end of file diff --git a/sparks/assets/1.5.1/config/assets_cssmin.php b/sparks/assets/1.5.1/config/assets_cssmin.php new file mode 100644 index 0000000..ddcff2c --- /dev/null +++ b/sparks/assets/1.5.1/config/assets_cssmin.php @@ -0,0 +1,32 @@ + false, + "RemoveComments" => true, + "RemoveEmptyRulesets" => true, + "RemoveEmptyAtBlocks" => true, + "ConvertLevel3AtKeyframes" => false, + "ConvertLevel3Properties" => false, + "Variables" => true, + "RemoveLastDelarationSemiColon" => true, +); + +$config['assets_cssmin_plugins'] = array( + "Variables" => true, + "ConvertFontWeight" => false, + "ConvertHslColors" => false, + "ConvertRgbColors" => false, + "ConvertNamedColors" => false, + "CompressColorValues" => false, + "CompressUnitValues" => false, + "CompressExpressionValues" => false +); \ No newline at end of file diff --git a/sparks/assets/1.5.1/config/autoload.php b/sparks/assets/1.5.1/config/autoload.php new file mode 100644 index 0000000..a3d2dd5 --- /dev/null +++ b/sparks/assets/1.5.1/config/autoload.php @@ -0,0 +1,6 @@ + + * @copyright Copyright (c) 2012, Boris Strahija, http://creolab.hr + * @version 1.5.0 + */ + + +function assets_css($assets = null, $attributes = null) +{ + Assets::css($assets, $attributes); +} + + +function assets_css_group($group = null, $assets = null, $attributes = null) +{ + Assets::css_group($group, $assets, $attributes); +} + + +function assets_js($assets = null) +{ + Assets::js($assets); +} + + +function assets_js_group($group = null, $assets = null) +{ + Assets::js_group($group, $assets); +} + + +function assets_url($path = null) +{ + return Assets::url($path); +} + + +function assets_img($path = null, $tag = false, $properties = null) +{ + return Assets::img($path, $tag, $properties); +} + + +function assets_cdn($assets = null) +{ + Assets::cdn($assets); +} + + +function assets_conditional($condition = null, $string = null) +{ + Assets::conditional($condition, $string); +} + + +function clear_assets_cache($type = null) +{ + Assets::clear_cache($type); +} + + +/* End of file assets_helper.php */ \ No newline at end of file diff --git a/sparks/assets/1.5.1/libraries/assets.php b/sparks/assets/1.5.1/libraries/assets.php new file mode 100644 index 0000000..f24ee3b --- /dev/null +++ b/sparks/assets/1.5.1/libraries/assets.php @@ -0,0 +1,1261 @@ + + * @copyright Copyright (c) 2012, Boris Strahija, http://creolab.hr + * @version 1.5.1 + */ + +define('ASSETS_VERSION', '1.5.1'); + + +class Assets { + + protected static $_ci; + protected static $_less; + protected static $_cache_info; + protected static $_cache_info_file = 'info.cache'; + protected static $_enable_benchmark = false; + + + // All the assets go in here + private static $_assets = array('js' => array(), 'css' => array()); + + + // Prefixes and groups + public static $group; + public static $default_group = array( + 'css' => '__def_css__', + 'js' => '__def_js__', + ); + + + // Paths and folders + public static $assets_dir, $base_path, $base_url; + public static $js_dir, $js_path, $js_url; + public static $css_dir, $css_path, $css_url; + public static $img_dir, $img_path, $img_url; + public static $cache_dir, $cache_path, $cache_url; + + + // Config + public static $minify = false; // Minify all + public static $minify_js = true; + public static $minify_css = true; + public static $auto_clear_cache = false; // Automaticly clear all cache before creating new cache files + public static $auto_clear_css_cache = false; // Or clear just cached CSS files + public static $auto_clear_js_cache = false; // Or just cached JS files + public static $html5 = true; // Use HTML5 tags + public static $enable_less = true; // Enable LESS CSS parser + public static $enable_coffeescript = true; // Enable CoffeeScript parser + public static $freeze = false; // Disable all processing once the assets are cached (for production) + + // CssMin config + public static $cssmin_plugins = array(); + public static $cssmin_filters = array(); + + // Flags + public static $auto_cleared_css_cache = false; + public static $auto_cleared_js_cache = false; + private static $_cssmin_loaded = false; + private static $_jsmin_loaded = false; + private static $_less_loaded = false; + private static $_coffeescript_loaded = false; + + + /* ------------------------------------------------------------------------------------------ */ + + /** + * Display CSS tags + * @param array $files + * @return string + */ + public static function css($files = null, $attributes = null) + { + self::init(); + + // Start benchmark + if (self::$_enable_benchmark) self::$_ci->benchmark->mark("Assets::css()_start"); + + self::$group = null; + // Add to process container + self::_add_assets($files, null, 'css', $attributes); + + // And process it + if (self::$_cache_info and self::$freeze) {} + else { self::_process('css'); } + + // End benchmark + if (self::$_enable_benchmark) self::$_ci->benchmark->mark("Assets::css()_end"); + + // Tags + return self::_generate_tags('css'); + } + + + /* ------------------------------------------------------------------------------------------ */ + + /** + * Display a group of CSS tags + * @param string $group + * @param array $files + * @return string + */ + public static function css_group($group = null, $files = null, $attributes = null) + { + self::$group = $group; + + self::init(); + + // Start benchmark + if (self::$_enable_benchmark) self::$_ci->benchmark->mark("Assets::css_group(".$group.")_start"); + + // Add to process container + self::_add_assets($files, $group, 'css', $attributes); + + // And process it + if (self::$_cache_info and self::$freeze) {} + else { self::_process('css', $group); } + + // End benchmark + if (self::$_enable_benchmark) self::$_ci->benchmark->mark("Assets::css_group(".$group.")_end"); + + // Tags + return self::_generate_tags('css'); + } + + + /* ------------------------------------------------------------------------------------------ */ + + /** + * Display JS tags + * @param array $files + * @return string + */ + public static function js($files = null) + { + self::$group = null; + + self::init(); + + // Start benchmark + if (self::$_enable_benchmark) self::$_ci->benchmark->mark("Assets::js()_start"); + + // Add to process container + self::_add_assets($files, null, 'js'); + + // And process it + if (self::$_cache_info and self::$freeze) {} + else { self::_process('js'); } + + // End benchmark + if (self::$_enable_benchmark) self::$_ci->benchmark->mark("Assets::js()_end"); + + // Tags + return self::_generate_tags('js'); + } + + + /* ------------------------------------------------------------------------------------------ */ + + /** + * Display a group of JS tags + * @param string $group + * @param array $files + * @return string + */ + public static function js_group($group = null, $files = null) + { + self::$group = $group; + + self::init(); + + // Start benchmark + if (self::$_enable_benchmark) self::$_ci->benchmark->mark("Assets::js_group(".$group.")_start"); + + // Add to process container + self::_add_assets($files, $group, 'js'); + + // And process it + if (self::$_cache_info and self::$freeze) {} + else { self::_process('js', $group); } + + // End benchmark + if (self::$_enable_benchmark) self::$_ci->benchmark->mark("Assets::js_group(".$group.")_end"); + + // Tags + return self::_generate_tags('js'); + } + + + /* ------------------------------------------------------------------------------------------ */ + + /** + * Simply return html tags with CDN paths + * @param array $assets + * @return string + */ + public static function cdn($assets = null, $echo = true) + { + if ($assets) + { + // Start benchmark + if (self::$_enable_benchmark) self::$_ci->benchmark->mark("Assets::cdn()_start"); + + self::init(); + + $html = ''; + + foreach ($assets as $asset) + { + // Get asset from config + if (is_array($asset)) + { + $cdn_asset = self::$_ci->config->item($asset[0], 'assets_cdn'); + if ($cdn_asset) $version = $asset[1]; + } + else + { + $cdn_asset = self::$_ci->config->item($asset, 'assets_cdn'); + if ($cdn_asset) $version = $cdn_asset['default_version']; + } + + // Check if asset ok + if (isset($cdn_asset) and isset($version) and $cdn_asset and $version) + { + $cdn_path = str_replace('%version%', $version, $cdn_asset['path']); + $html .= self::tag($cdn_path, 'js', false); + } + } + + // End benchmark + if (self::$_enable_benchmark) self::$_ci->benchmark->mark("Assets::cdn()_end"); + + if ($echo) echo $html; + else return $html; + } + } + + + /* ------------------------------------------------------------------------------------------ */ + + /** + * Place string in IE conditional comments + * @param string $condition + * @param string $string + */ + public static function conditional($condition = null, $string = null) + { + if ($condition and $string) + { + // Start benchmark + if (self::$_enable_benchmark) self::$_ci->benchmark->mark("Assets::conditional()_start"); + + echo ''; + + // End benchmark + if (self::$_enable_benchmark) self::$_ci->benchmark->mark("Assets::conditional()_end"); + } + } + + + + + /* ------------------------------------------------------------------------------------------ */ + + /** + * Add assets to list for processing + * @param array $assets + * @param string $group + * @param string $type + */ + private static function _add_assets($assets = null, $group = null, $type = null, $attributes = null) + { + // Start benchmark + if (self::$_enable_benchmark) self::$_ci->benchmark->mark("Assets::add-assets()_start"); + + if ( ! $group) $group = self::$default_group[$type]; + + // Set last modified time to 0 + self::$_assets[$type][$group]['last_modified'] = 0; + self::$_assets[$type][$group]['last_modified_human'] = "0000-00-00 00:00:00"; + + // Attributes + self::$_assets[$type][$group]['attributes'] = $attributes; + + // Prepare some vars + self::$_assets[$type][$group]['combined'] = ''; + self::$_assets[$type][$group]['output'] = ''; + + // List of active files in group + if (isset(self::$_cache_info->{$type}->{$group}->file_list)) self::$_assets[$type][$group]['file_list'] = self::$_cache_info->{$type}->{$group}->file_list; + else self::$_assets[$type][$group]['file_list'] = array(); + + // Add assets to list + foreach ($assets as $asset) + { + // Add file + $tmp_asset = array('file' => $asset); + + // Determine path + if ($type === 'css') $file_path = reduce_double_slashes(self::$css_path.'/'.$asset); + elseif ($type === 'js') $file_path = reduce_double_slashes(self::$js_path.'/'.$asset); + $tmp_asset['path'] = $file_path; + + // Modified time + $tmp_asset['modified'] = filemtime(realpath($file_path)); + if ($tmp_asset['modified'] > self::$_assets[$type][$group]['last_modified']) + { + self::$_assets[$type][$group]['last_modified'] = $tmp_asset['modified']; + self::$_assets[$type][$group]['last_modified_human'] = date('Y-m-d H:i:s', (int) $tmp_asset['modified']); + } + + // Add to container + self::$_assets[$type][$group]['src'][] = $tmp_asset; + self::$_assets[$type][$group]['file_list'] = (array) self::$_assets[$type][$group]['file_list']; + self::$_assets[$type][$group]['file_list'][] = $tmp_asset['file']; + } + + self::$_assets[$type][$group]['file_list'] = array_unique(self::$_assets[$type][$group]['file_list']); + + // End benchmark + if (self::$_enable_benchmark) self::$_ci->benchmark->mark("Assets::add-assets()_end"); + } + + + /* ------------------------------------------------------------------------------------------ */ + + /** + * Process assets in group + * @param string $type + * @param string $group + */ + private static function _process($type = null, $group = null) + { + if ( ! $group) $group = self::$default_group[$type]; + + // Start benchmark + if (self::$_enable_benchmark) self::$_ci->benchmark->mark("Assets::process(".$type.", ".$group.")_start"); + + // Last modified date + if ( ! isset(self::$_cache_info->{$type}->{$group})) $last_modified = 0; + else $last_modified = self::last_modified($type, $group); + + // Create a cache filename + if ($group !== self::$default_group[$type]) $file_prefix = $group.'.'; + else $file_prefix = ''; + $file_name = self::$_assets[$type][$group]['cache_file_name'] = $file_prefix.$last_modified.".".$type; + + // And check if we should process it + $file_exists = file_exists(self::$cache_path.'/'.$file_name); + + if ( ! $file_exists or ! $last_modified) + { + // Get list of assets + $assets = self::$_assets[$type][$group]; + + // Loop through all original assets + foreach ($assets['src'] as $key=>$asset) + { + // Get file contents + $contents = read_file($asset['path']); + + // Get file info + $asset['info'] = pathinfo($asset['file']); + + // Process imports in CSS files + if ($type === 'css') + { + $import_result = self::_process_imports($contents, null, $asset['file']); + $contents = $import_result['contents']; + + // Update last modified time + if ($import_result['last_modified'] > self::$_assets[$type][$group]['last_modified']) + { + self::$_assets[$type][$group]['last_modified'] = $import_result['last_modified']; + self::$_assets[$type][$group]['last_modified_human'] = date('Y-m-d H:i:s', $import_result['last_modified']); + } + + // Update imported files + self::$_assets[$type][$group]['file_list'] = array_unique(array_merge(self::$_assets[$type][$group]['file_list'], $import_result['file_list'])); + } + elseif ($type === 'js') + { + // CoffeeScript parser + if (self::$enable_coffeescript and $asset['info']['extension'] === 'coffee') + { + if ( ! self::$_coffeescript_loaded) self::_init_coffeescript(); + + CoffeeScript\Init::load(); + $contents = CoffeeScript\Compiler::compile($contents); + } + elseif ( ! self::$enable_coffeescript and $asset['info']['extension'] === 'coffee') + { + $contents = ''; + } + + // Minify JS + if (self::$minify_js) + { + self::_init_jsmin(); + $contents = trim(JSMin::minify($contents)); + } + } + + // Or add to combine var (if we're combining) + self::$_assets[$type][$group]['combined'] .= "\n".$contents; + } + + // New file name + $file_name = self::$_assets[$type][$group]['cache_file_name'] = $file_prefix.self::$_assets[$type][$group]['last_modified'].".".$type; + + // Now minify/less if we choose so + if ($type === 'css') + { + $output = self::$_assets[$type][$group]['combined']; + + // Less + if (self::$enable_less and ! self::$freeze) + { + self::_init_less(); + $output = self::$_less->parse($output); + } + + // Minify CSS + if (self::$minify_css and ! self::$freeze) + { + self::_init_cssmin(); + + $output = trim(CSSMin::minify($output, self::$cssmin_filters, self::$cssmin_plugins)); + } + + // Add to output + self::$_assets[$type][$group]['output'] = $output; + unset($output); + } + elseif ($type === 'js') + { + $output = self::$_assets[$type][$group]['combined']; + + // Minify JS + if (self::$minify_js) + { + self::_init_jsmin(); + self::$_assets[$type][$group]['output'] = trim(JSMin::minify($output)); + } + + // Add to output + self::$_assets[$type][$group]['output'] = $output; + unset($output); + } + + // Once it's processed remove vars we dont need + unset(self::$_assets[$type][$group]['combined']); + + // And finnaly we create the actual cached files + self::_cache_assets($type); + } + + // Update cache info + self::_update_cache_info(); + + // End benchmark + if (self::$_enable_benchmark) self::$_ci->benchmark->mark("Assets::process(".$type.", ".$group.")_end"); + } + + + /* ------------------------------------------------------------------------------------------ */ + + /** + * Process all CSS @imports and parse URL's + * @param string $contents + * @param string $import_dir + * @return array + */ + public static function _process_imports($contents = '', $import_dir = null, $asset_location = null) + { + // Start benchmark + if (self::$_enable_benchmark) self::$_ci->benchmark->mark("Assets::process_imports()_start"); + + $last_modified = 0; + + // Process URL's + $contents = self::_process_urls($contents, $asset_location); + + // Find @import calls + $imports_exist = preg_match_all("/@import\s+(.*);/", $contents, $imports); + + // List of import files + $import_file_list = array(); + + // If @import exist add them + if ($imports_exist and is_array($imports) and isset($imports[1]) and is_array($imports[1])) + { + foreach ($imports[1] as $import_key=>$import) + { + $import_file = trim($import, " \""); + + // Is a directory set + if ($import_dir) $import_file = $import_dir.'/'.$import_file; + + // Add to list + $import_file_list[] = $import_file; + + // Path info + $import_info = pathinfo($import_file); + $import_info_dir = ($import_info['dirname'] and $import_info['dirname'] !== ".") ? $import_info['dirname'] : null; + + // Path to file + $import_file_path = reduce_double_slashes(self::$css_path.'/'.$import_file); + + // Get modified date + $import_modified = filemtime(realpath($import_file_path)); + if ($import_modified > $last_modified) $last_modified = $import_modified; + + // Get contents + $import_contents = read_file($import_file_path); + + // Nested imports? + $import_result = self::_process_imports($import_contents, $import_info_dir, $import_file); + if ($import_result) + { + $import_contents = $import_result['contents']; + + // Add to list + if ($import_result['file_list']) $import_file_list = array_unique(array_merge($import_file_list, $import_result['file_list'])); + + // Check modified time + if ($import_result['last_modified'] > $last_modified) $last_modified = $import_result['last_modified']; + } + + // And add to main contents + $contents = str_replace($imports[0][$import_key], $import_contents, $contents); + } + } + + // End benchmark + if (self::$_enable_benchmark) self::$_ci->benchmark->mark("Assets::process_imports()_end"); + + return array( + 'last_modified' => $last_modified, + 'contents' => $contents, + 'file_list' => $import_file_list, + ); + } + + + /* ------------------------------------------------------------------------------------------ */ + + /** + * Find all URL's and process em accordingly + * @param string $contents + * @param string $asset_location + * @return string + */ + private static function _process_urls($contents = null, $asset_location = null) + { + // Parse URL's + preg_match_all("/url\((\"|'|)?((.*\.(png|gif|jpg))(\"|'|))\)/Ui", $contents, $matches); + + if (isset($matches[2]) and $matches[2]) + { + // Unique URL's + if (is_array($matches[2])) $matches[2] = array_unique($matches[2]); + + // Set location for assets + if ($asset_location) $asset_location = dirname($asset_location).'/'; + else $asset_location = ''; + + foreach ($matches[2] as $match) + { + $href = trim($match, " '\""); + $new_href = ''; + $base = self::$css_url.'/'.$asset_location; + + if ( ! $href) $new_href = $base; + + // href="http://..." ==> href isn't relative + $rel_parsed = parse_url($href); + if (array_key_exists('scheme', $rel_parsed)) $new_href = $href; + + // Add an extra character so that, if it ends in a /, we don't lose the last piece. + $base_parsed = parse_url("$base "); + // if it's just server.com and no path, then put a / there. + if (!array_key_exists('path', $base_parsed)) $base_parsed = parse_url("$base/ "); + + if ( ! isset($rel_parsed['host']) or (isset($rel_parsed['host']) and isset($base_parsed['host']) and ($rel_parsed['host'] == $base_parsed['host']))) + { + // href="/ ==> throw away current path. + if ($href{0} === "/") $path = $href; + else $path = dirname($base_parsed['path']) . "/$href"; + + // bla/./bloo ==> bla/bloo + $path = preg_replace('~/\./~', '/', $path); + + // resolve /../ + // loop through all the parts, popping whenever there's a .., pushing otherwise. + $parts = array(); + foreach (explode('/', preg_replace('~/+~', '/', $path)) as $part) + { + if ($part === "..") array_pop($parts); + elseif ($part != "") $parts[] = $part; + } + + $new_href = ((array_key_exists('scheme', $base_parsed)) ? $base_parsed['scheme'] . '://' . $base_parsed['host'] : "") . "/" . implode("/", $parts); + if (substr($path, 0, 2) == '//' and substr($new_href, 0, 2) != '//' and substr($new_href, 0, 1) == '/') $new_href = '/' . $new_href; + } + + $contents = str_replace($href, $new_href, $contents); + } + } + + return $contents; + } + + + /* ------------------------------------------------------------------------------------------ */ + + /** + * Find last modified time in list of files + * @param string $type - css or js (can be null) + * @param string $group - name of group so we can fetch the file list + * @return integer + */ + public static function last_modified($type = null, $group = null) + { + $last_modified = 0; + + // Start benchmark + if (self::$_enable_benchmark) self::$_ci->benchmark->mark("Assets::last_modified(".$type.", ".$group.")_start"); + + // Get file path + if ($type === 'css') $path = self::$css_path; + elseif ($type === 'js') $path = self::$js_path; + + // Get files + $files = array(); + if ($type and ! $group or ($type and ! isset(self::$_cache_info->{$type}->{$group}))) + { + $files = directory_map($path, 1); + } + elseif ($type and $group and isset(self::$_cache_info->{$type}->{$group}->file_list)) + { + $files = self::$_cache_info->{$type}->{$group}->file_list; + } + + foreach ($files as $file) + { + if ( ! is_array($file)) + { + $file_modified = (int) @filemtime($path."/".$file); + if ($file_modified > $last_modified) $last_modified = $file_modified; + } + else + { + $subfiles = directory_map($path.'/'.$file, 1); + + foreach ($subfiles as $subfile) + { + if ( ! is_array($subfile)) + { + $subfile_modified = filemtime($path."/".$file); + if ($subfile_modified > $last_modified) $last_modified = $subfile_modified; + } + } + } + } + + // End benchmark + if (self::$_enable_benchmark) self::$_ci->benchmark->mark("Assets::last_modified(".$type.", ".$group.")_end"); + + return $last_modified; + } + + + /* ------------------------------------------------------------------------------------------ */ + + /** + * Cache all assets of this type + * @param string $type + */ + private static function _cache_assets($type = null) + { + // Start benchmark + if (self::$_enable_benchmark) self::$_ci->benchmark->mark("Assets::cache_assets()_start"); + + // Auto clear cache directory? + if ($type == 'css' and (self::$auto_clear_cache or self::$auto_clear_css_cache) and ! self::$auto_cleared_css_cache) + { + self::clear_css_cache(self::$group, false); + self::$auto_cleared_css_cache = true; + } + + if ($type == 'js' and (self::$auto_clear_cache or self::$auto_clear_js_cache) and ! self::$auto_cleared_js_cache) + { + self::clear_js_cache(self::$group, false); + self::$auto_cleared_js_cache = true; + } + + // Loop through groups + foreach (self::$_assets[$type] as $key=>$assets_group) + { + $file_path = self::$cache_path."/".$assets_group['cache_file_name']; + + write_file($file_path, $assets_group['output']); + + // Remove contents after caching + unset(self::$_assets[$type][$key]['output']); + } + + // End benchmark + if (self::$_enable_benchmark) self::$_ci->benchmark->mark("Assets::cache_assets()_end"); + } + + + /* ------------------------------------------------------------------------------------------ */ + + /** + * Get/parse contents of cache info file + * @return object + */ + private static function _get_cache_info() + { + // Start benchmark + if (self::$_enable_benchmark) self::$_ci->benchmark->mark("Assets::_get_cache_info()_start"); + + self::$_cache_info = @json_decode(read_file(self::$cache_path.'/'.self::$_cache_info_file)); + + // End benchmark + if (self::$_enable_benchmark) self::$_ci->benchmark->mark("Assets::_get_cache_info()_end"); + } + + + /* ------------------------------------------------------------------------------------------ */ + + /** + * Displays cahce info + * @return string + */ + public static function echo_cache_info() + { + echo '
'; print_r(self::$_cache_info); echo '
'; + } + + + /* ------------------------------------------------------------------------------------------ */ + + /** + * Update cache info file + */ + public static function _update_cache_info() + { + // Start benchmark + if (self::$_enable_benchmark) self::$_ci->benchmark->mark("Assets::_update_cache_info()_start"); + + // We need to update the info file + foreach (self::$_assets as $key_type=>$assets_type) + { + foreach ($assets_type as $key_group=>$assets_group) + { + // Remove output + unset(self::$_assets[$key_type][$key_group]['output']); + + // Create empty placeholders if needed + if (self::$_cache_info === null) self::$_cache_info = new stdClass; + if ( ! isset(self::$_cache_info->{$key_type})) self::$_cache_info->{$key_type} = new stdClass; + if ( ! isset(self::$_cache_info->{$key_type}->{$key_group})) self::$_cache_info->{$key_type}->{$key_group} = new stdClass; + + // Add group to info + self::$_cache_info->{$key_type}->{$key_group}->cache_file_name = $assets_group['cache_file_name']; + self::$_cache_info->{$key_type}->{$key_group}->last_modified = $assets_group['last_modified']; + self::$_cache_info->{$key_type}->{$key_group}->file_list = $assets_group['file_list']; + } + } + + write_file(self::$cache_path.'/'.self::$_cache_info_file, json_encode(self::$_cache_info)); + + // End benchmark + if (self::$_enable_benchmark) self::$_ci->benchmark->mark("Assets::_update_cache_info()_end"); + } + + + /* ------------------------------------------------------------------------------------------ */ + + /** + * Generate and output HTML tags + * @param string $type + * @param boolean $echo + * @return string + */ + private static function _generate_tags($type = null, $echo = true) + { + if ( ! self::$group) $group = self::$default_group[$type]; + else $group = self::$group; + + if (isset(self::$_assets[$type][$group])) + { + // Get list of assets + $assets_group = self::$_assets[$type][$group]; + + // Default attributes + $attributes = ''; + if ($type === 'css') + { + // Default attributes + if ( ! isset($assets_group['attributes']['rel'])) $assets_group['attributes']['rel'] = 'stylesheet'; + } + elseif ($type === 'js') + { + // Default attributes + if ( ! isset($assets_group['attributes']['charset']) and ! self::$html5) $assets_group['attributes']['charset'] = 'utf-8'; + } + + // Custom attributes + if (isset($assets_group['attributes']) and $assets_group['attributes']) + { + foreach ($assets_group['attributes'] as $att=>$val) + { + $attributes .= ' '.$att.'="'.$val.'"'; + } + } + + // File name + if ( ! isset($assets_group['cache_file_name'])) $assets_group['cache_file_name'] = self::$_cache_info->{$type}->{$group}->cache_file_name; + + // File and tag + $file = self::$cache_url.'/'.$assets_group['cache_file_name']; + $tag = self::tag($file, $type, false, $attributes); + + if ($echo) echo $tag; + else return $tag; + } + } + + + /* ------------------------------------------------------------------------------------------ */ + + /** + * Display a HTML tag + * @param string $file + * @param string $type + * @param boolean $echo + * @return string + */ + public static function tag($file = null, $type = null, $echo = true, $attributes = '') + { + if ($type === 'css') + { + if (self::$html5) $tag = ''.PHP_EOL; + else $tag = ''.PHP_EOL; + } + elseif ($type === 'js') + { + if (self::$html5) $tag = ''.PHP_EOL; + else $tag = ''.PHP_EOL; + } + + if ($echo) echo $tag; + else return $tag; + } + + + /* ------------------------------------------------------------------------------------------ */ + + /** + * Delete cached files + * @param string $type + * @param string $group + */ + public static function clear_cache($type = null, $group = null, $init = true) + { + if ($init) self::init(); + + // Get all cached files + $files = directory_map(self::$cache_path, 1); + + if ($files) + { + foreach ($files as $file) + { + if ($group) + { + + } + else + { + $file_path = reduce_double_slashes(self::$cache_path.'/'.$file); + $file_info = pathinfo($file_path); + + if ($type === 'css') + { + if (isset($file_info['extension']) and strtolower($file_info['extension']) === 'css') unlink($file_path); + } + elseif ($type === 'js') + { + if (isset($file_info['extension']) and strtolower($file_info['extension']) === 'js') unlink($file_path); + } + else + { + if (isset($file_info['extension']) and (strtolower($file_info['extension']) === 'css' or strtolower($file_info['extension']) === 'js')) unlink($file_path); + if (isset($file_info['extension']) and strtolower($file_info['extension']) === 'cache') unlink($file_path); + } + } + } + } + + self::_get_cache_info(); + } + + + /* ------------------------------------------------------------------------------------------ */ + + /** + * Delete cached CSS files + * @param string $asset_file + */ + public static function clear_css_cache($group = null, $init = true) + { + return self::clear_cache('css', $group, $init); + } + + + /* ------------------------------------------------------------------------------------------ */ + + /** + * Delete cached JS files + * @param string $asset_file + */ + public static function clear_js_cache($group = null, $init = true) + { + return self::clear_cache('js', $group, $init); + } + + + /* ------------------------------------------------------------------------------------------ */ + + public static function clear_assets() + { + self::$_assets = array('js' => array(), 'css' => array()); + } + + + + /* ------------------------------------------------------------------------------------------ */ + /* !/===> URL / Image helpers */ + /* ------------------------------------------------------------------------------------------ */ + + + /** + * Return url to asset + * @param string $path + */ + public static function url($path = null) + { + return self::$base_url.'/'.$path; + } + + + /* ------------------------------------------------------------------------------------------ */ + + /** + * Return url to image, or event an entire tag + * @param string $path + */ + public static function img($path = null, $tag = false, $properties = null) + { + self::init(); + + $img_path = self::$img_url.'/'.$path; + + // Properties + if ($properties) $properties['src'] = $img_path; + + // Tag? + if ($tag) + { + $img = img($properties, false); + + // Remove site_url if base_url is set as auto protocol + if (stripos(self::$base_url, '//') === 0) + { + $img = str_replace('src="'.base_url(), 'src="', $img); + } + + return $img; + } + else + { + return $img_path; + } + } + + + + /* ------------------------------------------------------------------------------------------ */ + /* !/===> Initialization and configuration */ + /* ------------------------------------------------------------------------------------------ */ + + + /** + * Library initialization + * @param array $cfg + */ + public static function init($cfg = null) + { + // Clean previous assets if needed + self::clear_assets(); + + if ( ! self::$_ci) + { + self::$_ci =& get_instance(); + + // Start benchmark + if (self::$_enable_benchmark) self::$_ci->benchmark->mark("Assets::init()_start"); + + // Add config to library + if ($cfg) + { + self::configure(array_merge($cfg), config_item('assets')); + } + else + { + self::configure(config_item('assets')); + } + + // Get cache info + self::_get_cache_info(); + } + + // End benchmark + if (self::$_enable_benchmark) self::$_ci->benchmark->mark("Assets::init()_end"); + } + + + /* ------------------------------------------------------------------------------------------ */ + + private static function _init_cssmin() + { + if (self::$minify_css and ! self::$freeze and ! self::$_cssmin_loaded) + { + // Start benchmark + if (self::$_enable_benchmark) self::$_ci->benchmark->mark("Assets::init_cssmin()_start"); + + // Load + if (defined('SPARKPATH')) include(reduce_double_slashes(SPARKPATH.'assets/'.ASSETS_VERSION.'/libraries/cssmin.php')); + else include(reduce_double_slashes(APPPATH.'/third_party/assets/cssmin.php')); + self::$_cssmin_loaded = true; + + // Set current dir for css min + if ( ! isset(self::$cssmin_filters['currentDir'])) self::$cssmin_filters['currentDir'] = str_replace(site_url(), '', self::$base_url).'/'; + + // End benchmark + if (self::$_enable_benchmark) self::$_ci->benchmark->mark("Assets::init_cssmin()_end"); + } + } + + + /* ------------------------------------------------------------------------------------------ */ + + private static function _init_jsmin() + { + if (self::$minify_js and ! self::$freeze and ! self::$_jsmin_loaded) + { + // Start benchmark + if (self::$_enable_benchmark) self::$_ci->benchmark->mark("Assets::init_jsmin()_start"); + + // Load + if (defined('SPARKPATH')) include(reduce_double_slashes(SPARKPATH.'assets/'.ASSETS_VERSION.'/libraries/jsmin.php')); + else include(reduce_double_slashes(APPPATH.'/third_party/assets/jsmin.php')); + self::$_jsmin_loaded = true; + + // End benchmark + if (self::$_enable_benchmark) self::$_ci->benchmark->mark("Assets::init_jsmin()_end"); + } + } + + + /* ------------------------------------------------------------------------------------------ */ + + private static function _init_less() + { + if ( ! self::$freeze and ! self::$_less_loaded) + { + // Start benchmark + if (self::$_enable_benchmark) self::$_ci->benchmark->mark("Assets::init_less()_start"); + + // Load LessPHP + if (defined('SPARKPATH')) include(reduce_double_slashes(SPARKPATH.'assets/'.ASSETS_VERSION.'/libraries/lessc.php')); + else include(reduce_double_slashes(APPPATH.'/third_party/assets/lessc.php')); + + // Initialize + self::$_less = new lessc(); + self::$_less->importDir = self::$css_path.'/'; + self::$_less_loaded = true; + + // End benchmark + if (self::$_enable_benchmark) self::$_ci->benchmark->mark("Assets::init_less()_end"); + } + } + + + /* ------------------------------------------------------------------------------------------ */ + + private static function _init_coffeescript() + { + if ( ! self::$freeze and ! self::$_coffeescript_loaded) + { + // Start benchmark + if (self::$_enable_benchmark) self::$_ci->benchmark->mark("Assets::init_coffeescript()_start"); + + // Load classes + if (defined('SPARKPATH')) include(reduce_double_slashes(SPARKPATH.'assets/'.ASSETS_VERSION.'/libraries/coffeescript/Init.php')); + else include(reduce_double_slashes(APPPATH.'/third_party/assets/coffeescript/coffeescript.php')); + + // Initialize + CoffeeScript\Init::load(); + self::$_coffeescript_loaded = true; + + // End benchmark + if (self::$_enable_benchmark) self::$_ci->benchmark->mark("Assets::init_coffeescript()_end"); + } + } + + + /* ------------------------------------------------------------------------------------------ */ + + /** + * Library configuration + * @param array $cfg + */ + public static function configure($cfg = null) + { + // Start benchmark + if (self::$_enable_benchmark) self::$_ci->benchmark->mark("Assets::configure()_start"); + + $cfg = array_merge(config_item('assets'), $cfg); + + if ($cfg and is_array($cfg)) + { + foreach ($cfg as $key=>$val) + { + self::$$key = $val; + //echo 'CONFIG: ', $key, ' :: ', $val, '
'; + } + } + + // CssMin configuration + self::$cssmin_filters = self::$_ci->config->item('assets_cssmin_filters'); + self::$cssmin_plugins = self::$_ci->config->item('assets_cssmin_plugins'); + + // Prepare all the paths and URI's + self::_paths(); + + // End benchmark + if (self::$_enable_benchmark) self::$_ci->benchmark->mark("Assets::configure()_end"); + } + + + /* ------------------------------------------------------------------------------------------ */ + + /** + * Set a different assets path + * @param string $path + */ + public static function set_path($path = null) + { + self::init(); + + if ($path) self::$assets_dir = $path; + + self::_paths(); + } + + + /* ------------------------------------------------------------------------------------------ */ + + /** + * Setup paths + */ + private static function _paths() + { + // Start benchmark + if (self::$_enable_benchmark) self::$_ci->benchmark->mark("Assets::paths()_start"); + + // Set the assets base path + self::$base_path = reduce_double_slashes(realpath(self::$assets_dir)); + + // Now set the assets base URL + if ( ! self::$base_url) self::$base_url = reduce_double_slashes(config_item('base_url').'/'.self::$assets_dir); + else self::$base_url = self::$base_url.self::$assets_dir; + + // Auto protocol + if (stripos(self::$base_url, '//') === 0) $slash = '/'; + else $slash = ''; + + // And finally the paths and URL's to the css and js assets + self::$js_path = reduce_double_slashes(self::$base_path .'/'.self::$js_dir); + self::$js_url = $slash.reduce_double_slashes(self::$base_url .'/'.self::$js_dir); + self::$css_path = reduce_double_slashes(self::$base_path .'/'.self::$css_dir); + self::$css_url = $slash.reduce_double_slashes(self::$base_url .'/'.self::$css_dir); + self::$img_path = reduce_double_slashes(self::$base_path .'/'.self::$img_dir); + self::$img_url = $slash.reduce_double_slashes(self::$base_url .'/'.self::$img_dir); + self::$cache_path = reduce_double_slashes(self::$base_path .'/'.self::$cache_dir); + self::$cache_url = $slash.reduce_double_slashes(self::$base_url .'/'.self::$cache_dir); + + if ( ! self::$freeze) + { + // Check if all directories exist + if ( ! is_dir(self::$js_path)) + { + if ( ! @mkdir(self::$js_path, 0755)) exit('Error with JS directory.'); + } + + if ( ! is_dir(self::$css_path)) + { + if ( ! @mkdir(self::$css_path, 0755)) exit('Error with CSS directory.'); + } + + if ( ! is_dir(self::$cache_path)) + { + if ( ! @mkdir(self::$cache_path, 0777)) exit('Error with CACHE directory.'); + } + + // Try to make the cache direcory writable + if (is_dir(self::$cache_path) and ! is_really_writable(self::$cache_path)) + { + @chmod(self::$cache_path, 0777); + } + + // If it's still not writable throw error + if ( ! is_dir(self::$cache_path) or ! is_really_writable(self::$cache_path)) + { + exit('Error with CACHE directory.'); + } + } + + // End benchmark + if (self::$_enable_benchmark) self::$_ci->benchmark->mark("Assets::paths()_end"); + } + + /* ------------------------------------------------------------------------------------------ */ + +} + + +/* End of file assets.php */ \ No newline at end of file diff --git a/sparks/assets/1.5.1/libraries/coffeescript/Compiler.php b/sparks/assets/1.5.1/libraries/coffeescript/Compiler.php new file mode 100755 index 0000000..544855c --- /dev/null +++ b/sparks/assets/1.5.1/libraries/coffeescript/Compiler.php @@ -0,0 +1,76 @@ + The source file, for debugging (formatted into error messages) + * 'header' => Add a header to the generated source (default: TRUE) + * 'rewrite' => Enable rewriting token stream (debugging) + * 'tokens' => Reference to token stream (debugging) + * 'trace' => File to write parser trace to (debugging) + * + * @param string The source CoffeeScript code + * @param array Options (see above) + * + * @return string The resulting JavaScript (if there were no errors) + */ + static function compile($code, $options = array()) + { + $lexer = new Lexer($code, $options); + + if (isset($options['filename'])) + { + Parser::$FILE = $options['filename']; + } + + if (isset($options['tokens'])) + { + $tokens = & $options['tokens']; + } + + if (isset($options['trace'])) + { + Parser::Trace(fopen($options['trace'], 'w', TRUE), '> '); + } + + try + { + $parser = new Parser(); + + foreach (($tokens = $lexer->tokenize()) as $token) + { + $parser->parse($token); + } + + $js = $parser->parse(NULL)->compile($options); + } + catch (\Exception $e) + { + throw new Error("In {$options['filename']}, ".$e->getMessage()); + } + + if ( ! isset($options['header']) || $options['header']) + { + $js = '// Generated by CoffeeScript PHP ' . VERSION . "\n" . $js; + } + + return $js; + } + +} + +?> diff --git a/sparks/assets/1.5.1/libraries/coffeescript/Error.php b/sparks/assets/1.5.1/libraries/coffeescript/Error.php new file mode 100755 index 0000000..785f86f --- /dev/null +++ b/sparks/assets/1.5.1/libraries/coffeescript/Error.php @@ -0,0 +1,15 @@ +message = $message; + } +} + +?> diff --git a/sparks/assets/1.5.1/libraries/coffeescript/Helpers.php b/sparks/assets/1.5.1/libraries/coffeescript/Helpers.php new file mode 100755 index 0000000..9ca2ff2 --- /dev/null +++ b/sparks/assets/1.5.1/libraries/coffeescript/Helpers.php @@ -0,0 +1,97 @@ + $v) + { + if ($v) + { + $compacted[] = $v; + } + } + + return $compacted; + } + + static function del( & $obj, $key) + { + $val = NULL; + + if (isset($obj[$key])) + { + $val = $obj[$key]; + unset($obj[$key]); + } + + return $val; + } + + static function extend($obj, $properties) + { + foreach ($properties as $k => $v) + { + $obj->{$k} = $v; + } + + return $obj; + } + + static function flatten(array $array) + { + $flattened = array(); + + foreach ($array as $k => $v) + { + if (is_array($v)) + { + $flattened = array_merge($flattened, flatten($v)); + } + else + { + $flattened[] = $v; + } + } + + return $flattened; + } + + static function & last( & $array, $back = 0) + { + static $NULL; + + $i = count($array) - $back - 1; + + if (isset($array[$i])) + { + return $array[$i]; + } + else + { + // Make sure $NULL is really NULL. + $NULL = NULL; + + return $NULL; + } + } + + + /** + * Wrap a primitive with an object, so that properties can be attached to it + * like in JavaScript. + */ + static function wrap($v) + { + return new Value($v); + } + +} + +?> diff --git a/sparks/assets/1.5.1/libraries/coffeescript/Init.php b/sparks/assets/1.5.1/libraries/coffeescript/Init.php new file mode 100755 index 0000000..23814a3 --- /dev/null +++ b/sparks/assets/1.5.1/libraries/coffeescript/Init.php @@ -0,0 +1,95 @@ + diff --git a/sparks/assets/1.5.1/libraries/coffeescript/Lexer.php b/sparks/assets/1.5.1/libraries/coffeescript/Lexer.php new file mode 100755 index 0000000..dfb2b8f --- /dev/null +++ b/sparks/assets/1.5.1/libraries/coffeescript/Lexer.php @@ -0,0 +1,1349 @@ + '&&', + 'or' => '||', + 'is' => '==', + 'isnt' => '!=', + 'not' => '!', + 'yes' => 'true', + 'no' => 'false', + 'on' => 'true', + 'off' => 'false' + ); + + static $COFFEE_KEYWORDS = array( + 'by', + 'loop', + 'of', + 'then', + 'undefined', + 'unless', + 'until', + 'when' + ); + + // exports.RESERVED. + static $COFFEE_RESERVED = array(); + + static $JS_KEYWORDS = array( + 'break', + 'catch', + 'class', + 'continue', + 'debugger', + 'delete', + 'do', + 'else', + 'extends', + 'false', + 'finally', + 'for', + 'if', + 'in', + 'instanceof', + 'new', + 'null', + 'this', + 'throw', + 'typeof', + 'return', + 'switch', + 'super', + 'true', + 'try', + 'while', + ); + + // RESERVED. + static $JS_RESERVED = array( + '__bind', + '__extends', + '__hasProp', + '__indexOf', + '__slice', + 'case', + 'const', + 'default', + 'enum', + 'export', + 'function', + 'implements', + 'import', + 'interface', + 'let', + 'native', + 'package', + 'protected', + 'private', + 'public', + 'static', + 'var', + 'void', + 'with', + 'yield', + ); + + static $STRICT_PROSCRIBED = array('arguments', 'eval'); + + static $JS_FORBIDDEN = array(); + + static $CODE = '/^[-=]>/'; + static $COMMENT = '/^###([^#][\s\S]*?)(?:###[^\n\S]*|(?:###)?$)|^(?:\s*#(?!##[^#]).*)+/'; + static $HEREDOC = '/^("""|\'\'\')([\s\S]*?)(?:\n[^\n\S]*)?\1/'; + static $HEREDOC_INDENT = '/\n+([^\n\S]*)/'; + static $HEREDOC_ILLEGAL = '%\*/%'; + static $HEREGEX = '%^/{3}([\s\S]+?)/{3}([imgy]{0,4})(?!\w)%'; + static $HEREGEX_OMIT = '/\s+(?:#.*)?/'; + static $IDENTIFIER = '/^([$A-Za-z_\x7f-\x{ffff}][$\w\x7f-\x{ffff}]*)([^\n\S]*:(?!:))?/u'; + static $JSTOKEN = '/^`[^\\\\`]*(?:\\\\.[^\\\\`]*)*`/'; + static $LINE_CONTINUER = '/^\s*(?:,|\??\.(?![.\d])|::)/'; + static $MULTI_DENT = '/^(?:\n[^\n\S]*)+/'; + static $MULTILINER = '/\n/'; + static $NUMBER = '/^0b[01]+|^0o[0-7]+|^0x[\da-f]+|^\d*\.?\d+(?:e[+-]?\d+)?/i'; + static $OPERATOR = '#^(?:[-=]>|[-+*/%<>&|^!?=]=|>>>=?|([-+:])\1|([&|<>])\2=?|\?\.|\.{2,3})#'; + static $REGEX = '%^(/(?![\s=])[^[/\n\\\\]*(?:(?:\\\\[\s\S]|\[[^\]\n\\\\]*(?:\\\\[\s\S][^\]\n\\\\]*)*\])[^[/\n\\\\]*)*/)([imgy]{0,4})(?!\w)%'; + static $SIMPLESTR = '/^\'[^\\\\\']*(?:\\\\.[^\\\\\']*)*\'/i'; + static $TRAILING_SPACES = '/\s+$/'; + static $WHITESPACE = '/^[^\n\S]+/'; + + static $BOOL = array('TRUE', 'FALSE', 'NULL', 'UNDEFINED'); + static $CALLABLE = array('IDENTIFIER', 'STRING', 'REGEX', ')', ']', '}', '?', '::', '@', 'THIS', 'SUPER'); + static $COMPARE = array('==', '!=', '<', '>', '<=', '>='); + static $COMPOUND_ASSIGN = array('-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?=', '<<=', '>>=', '>>>=', '&=', '^=', '|=' ); + static $INDEXABLE = array('NUMBER', 'BOOL'); + static $LINE_BREAK = array('INDENT', 'OUTDENT', 'TERMINATOR'); + static $LOGIC = array('&&', '||', '&', '|', '^'); + static $MATH = array('*', '/', '%'); + static $NOT_REGEX = array('NUMBER', 'REGEX', 'BOOL', '++', '--', ']'); + static $NOT_SPACED_REGEX = array(')', '}', 'THIS', 'IDENTIFIER', 'STRING'); + static $RELATION = array('IN', 'OF', 'INSTANCEOF'); + static $SHIFT = array('<<', '>>', '>>>'); + static $UNARY = array('!', '~', 'NEW', 'TYPEOF', 'DELETE', 'DO'); + + static $INVERSES = array(); + + static $initialized = FALSE; + + /** + * Initialize some static variables (called at the end of this file). + */ + static function init() + { + if (self::$initialized) return; + + self::$initialized = TRUE; + + self::$COFFEE_KEYWORDS = array_merge(self::$COFFEE_KEYWORDS, array_keys(self::$COFFEE_ALIASES)); + self::$COFFEE_RESERVED = array_merge(self::$JS_RESERVED, self::$JS_KEYWORDS, self::$COFFEE_KEYWORDS, self::$STRICT_PROSCRIBED); + self::$JS_FORBIDDEN = array_merge(self::$JS_KEYWORDS, self::$JS_RESERVED, self::$STRICT_PROSCRIBED); + self::$INDEXABLE = array_merge(self::$CALLABLE, self::$INDEXABLE); + self::$NOT_SPACED_REGEX = array_merge(self::$NOT_REGEX, self::$NOT_SPACED_REGEX); + + Rewriter::init(); + + self::$INVERSES = Rewriter::$INVERSES; + } + + /** + * In Jison, token tags can be represented simply using strings, whereas with + * ParserGenerator (a port of Lemon) we're stuck using numeric constants for + * everything. + * + * This static function maps those string representations to their numeric constants, + * making it easier to port directly from the CoffeeScript source. + */ + static function t($name) + { + static $map = array( + '.' => 'ACCESSOR', + '[' => 'ARRAY_START', + ']' => 'ARRAY_END', + '@' => 'AT_SIGN', + '=>' => 'BOUND_FUNC', + ':' => 'COLON', + ',' => 'COMMA', + '--' => 'DECREMENT', + '=' => 'EQUALS', + '?' => 'EXISTENTIAL', + '?.' => 'EXISTENTIAL_ACCESSOR', + '->' => 'FUNC', + '++' => 'INCREMENT', + '&' => 'LOGIC', + '&&' => 'LOGIC', + '||' => 'LOGIC', + '-' => 'MINUS', + '{' => 'OBJECT_START', + '}' => 'OBJECT_END', + '(' => 'PAREN_START', + ')' => 'PAREN_END', + '+' => 'PLUS', + '::' => 'PROTOTYPE', + '...' => 'RANGE_EXCLUSIVE', + '..' => 'RANGE_INCLUSIVE', + ); + + if (is_array($name) || (func_num_args() > 1 && $name = func_get_args())) + { + $tags = array(); + + foreach ($name as $v) + { + $tags[] = t($v); + } + + return $tags; + } + + $name = 'CoffeeScript\Parser::YY_'.(isset($map[$name]) ? $map[$name] : $name); + + // Don't return the original name if there's no matching constant, in some + // cases intermediate token types are created and the value returned by this + // static function still needs to be unique. + return defined($name) ? constant($name) : $name; + } + + /** + * Change a CoffeeScript PHP token tag to it's equivalent canonical form (the + * form used in the JavaScript version). + * + * This static function is used for testing purposes only. + */ + static function t_canonical($token) + { + static $map = array( + 'ACCESSOR' => '.', + + // These are separate from INDEX_START and INDEX_END. + 'ARRAY_START' => '[', + 'ARRAY_END' => ']', + + 'AT_SIGN' => '@', + 'BOUND_FUNC' => '=>', + 'COLON' => ':', + 'COMMA' => ',', + 'DECREMENT' => '--', + 'EQUALS' => '=', + 'EXISTENTIAL' => '?', + 'EXISTENTIAL_ACCESSOR' => '?.', + 'FUNC' => '->', + 'INCREMENT' => '++', + 'MINUS' => '-', + 'OBJECT_START' => '{', + 'OBJECT_END' => '}', + + // These are separate from CALL_START and CALL_END. + 'PAREN_START' => '(', + 'PAREN_END' => ')', + + 'PLUS' => '+', + 'PROTOTYPE' => '::', + 'RANGE_EXCLUSIVE' => '...', + 'RANGE_INCLUSIVE' => '..' + ); + + if (is_array($token)) + { + if (is_array($token[0])) + { + foreach ($token as & $t) + { + $t = t_canonical($t); + } + } + else + { + // Single token. + $token[0] = t_canonical($token[0]); + + if (is_object($token[1])) + { + $str = "< {$token[1]} "; + + foreach ($token[1] as $k => $v) + { + if ($k !== 'v' && $v) + { + $str.= $k.' '; + } + } + + $token[1] = $str.'>'; + } + } + + return $token; + } + else if (is_numeric($token)) + { + $token = substr(Parser::tokenName($token), 3); + } + else if (is_string($token)) + { + // The token type isn't known to the parser, so t() returned a unique + // string to use instead. + $token = substr($token, strlen('CoffeeScript\Parser::YY_')); + } + + return isset($map[$token]) ? $map[$token] : $token; + } + + + function __construct($code, $options) + { + self::init(); + + if (preg_match(self::$WHITESPACE, $code)) + { + $code = "\n{$code}"; + } + + $code = preg_replace(self::$TRAILING_SPACES, '', str_replace("\r", '', $code)); + + $options = array_merge(array( + 'indent' => 0, + 'index' => 0, + 'line' => 0, + 'rewrite' => TRUE + ), + $options); + + $this->code = $code; + $this->chunk = $code; + $this->ends = array(); + $this->indent = 0; + $this->indents = array(); + $this->indebt = 0; + $this->index = $options['index']; + $this->length = strlen($this->code); + $this->line = $options['line']; + $this->outdebt = 0; + $this->options = $options; + $this->tokens = array(); + } + + function balanced_string($str, $end) + { + $continue_count = 0; + + $stack = array($end); + $prev = NULL; + + $len = strlen($str); + + for ($i = 1; $i < $len; $i++) + { + if ($continue_count) + { + --$continue_count; + continue; + } + + switch ($letter = $str{$i}) + { + case '\\': + ++$continue_count; + continue 2; + + case $end: + array_pop($stack); + + if (count($stack) === 0) + { + return substr($str, 0, $i + 1); + } + + $end = $stack[count($stack) - 1]; + continue 2; + } + + if ($end === '}' && ($letter === '"' || $letter === "'")) + { + $stack[] = $end = $letter; + } + else if ($end === '}' && $letter === '/' && (preg_match(self::$HEREGEX, substr($str, $i), $match) || preg_match(self::$REGEX, substr($str, $i), $match))) + { + $continue_count += strlen($match[0]) - 1; + } + else if ($end === '}' && $letter === '{') + { + $stack[] = $end = '}'; + } + else if ($end === '"' && $prev === '#' && $letter === '{') + { + $stack[] = $end = '}'; + } + + $prev = $letter; + } + + $this->error('missing '.array_pop($stack).', starting'); + } + + function close_indentation() + { + $this->outdent_token($this->indent); + } + + function comment_token() + { + if ( ! preg_match(self::$COMMENT, $this->chunk, $match)) + { + return 0; + } + + $comment = $match[0]; + + if (isset($match[1]) && ($here = $match[1])) + { + $this->token('HERECOMMENT', $this->sanitize_heredoc($here, array( + 'herecomment' => TRUE, + 'indent' => str_pad('', $this->indent) + ))); + } + + $this->line += substr_count($comment, "\n"); + + return strlen($comment); + } + + function error($message) + { + throw new SyntaxError($message.' on line '.($this->line + 1)); + } + + function escape_lines($str, $heredoc = NULL) + { + return preg_replace(self::$MULTILINER, $heredoc ? '\\n' : '', $str); + } + + function heredoc_token() + { + if ( ! preg_match(self::$HEREDOC, $this->chunk, $match)) + { + return 0; + } + + $heredoc = $match[0]; + $quote = $heredoc{0}; + $doc = $this->sanitize_heredoc($match[2], array('quote' => $quote, 'indent' => NULL)); + + if ($quote === '"' && strpos($doc, '#{') !== FALSE) + { + $this->interpolate_string($doc, array('heredoc' => TRUE)); + } + else + { + $this->token('STRING', $this->make_string($doc, $quote, TRUE)); + } + + $this->line += substr_count($heredoc, "\n"); + + return strlen($heredoc); + } + + function heregex_token($match) + { + list($heregex, $body, $flags) = $match; + + if (strpos($body, '#{') === FALSE) + { + $re = preg_replace(self::$HEREGEX_OMIT, '', $body); + $re = preg_replace('/\//', '\\/', $re); + + if (preg_match('/^\*/', $re)) + { + $this->error('regular expressions cannot begin with `*`'); + } + + $this->token('REGEX', '/'.($re ? $re : '(?:)').'/'.$flags); + + return strlen($heregex); + } + + $this->token('IDENTIFIER', 'RegExp'); + $this->tokens[] = array(t('CALL_START'), '('); + + $tokens = array(); + + foreach ($this->interpolate_string($body, array('regex' => TRUE)) as $token) + { + list($tag, $value) = $token; + + if ($tag === 'TOKENS') + { + $tokens = array_merge($tokens, (array) $value); + } + else + { + if ( ! ($value = preg_replace(self::$HEREGEX_OMIT, '', $value))) + { + continue; + } + + $value = preg_replace('/\\\\/', '\\\\\\\\', $value); + $tokens[] = array(t('STRING'), $this->make_string($value, '"', TRUE)); + } + + $tokens[] = array(t('+'), '+'); + } + + array_pop($tokens); + + if ( ! (isset($tokens[0]) && $tokens[0][0] === 'STRING')) + { + array_push($this->tokens, array(t('STRING'), '""'), array(t('+'), '+')); + } + + $this->tokens = array_merge($this->tokens, $tokens); + + if ($flags) + { + array_push($this->tokens, array(t(','), ','), array(t('STRING'), "\"{$flags}\"")); + } + + $this->token(')', ')'); + + return strlen($heregex); + } + + function identifier_token() + { + if ( ! preg_match(self::$IDENTIFIER, $this->chunk, $match)) + { + return 0; + } + + list($input, $id) = $match; + + $colon = isset($match[2]) ? $match[2] : NULL; + + if ($id === 'own' && $this->tag() === t('FOR')) + { + $this->token('OWN', $id); + + return strlen($id); + } + + $forced_identifier = $colon || ($prev = last($this->tokens)) && + (in_array($prev[0], t('.', '?.', '::')) || + ( ! (isset($prev['spaced']) && $prev['spaced']) && $prev[0] === t('@'))); + + $tag = 'IDENTIFIER'; + + if ( ! $forced_identifier and (in_array($id, self::$JS_KEYWORDS) || in_array($id, self::$COFFEE_KEYWORDS))) + { + $tag = strtoupper($id); + + if ($tag === 'WHEN' && in_array($this->tag(), t(self::$LINE_BREAK))) + { + $tag = 'LEADING_WHEN'; + } + else if ($tag === 'FOR') + { + $this->seen_for = TRUE; + } + else if ($tag === 'UNLESS') + { + $tag = 'IF'; + } + else if (in_array($tag, self::$UNARY)) + { + $tag = 'UNARY'; + } + else if (in_array($tag, self::$RELATION)) + { + if ($tag !== 'INSTANCEOF' && (isset($this->seen_for) && $this->seen_for)) + { + $tag = 'FOR'.$tag; + $this->seen_for = FALSE; + } + else + { + $tag = 'RELATION'; + + if ($this->value() === '!') + { + array_pop($this->tokens); + $id = '!'. $id; + } + } + } + } + + if (in_array($id, self::$JS_FORBIDDEN, TRUE)) + { + if ($forced_identifier) + { + $id = wrap($id); + $id->reserved = TRUE; + + $tag = 'IDENTIFIER'; + } + else if (in_array($id, self::$JS_RESERVED, TRUE)) + { + $this->error("reserved word $id"); + } + } + + if ( ! $forced_identifier) + { + if (isset(self::$COFFEE_ALIASES[$id])) + { + $id = self::$COFFEE_ALIASES[$id]; + } + + $map = array( + 'UNARY' => array('!'), + 'COMPARE' => array('==', '!='), + 'LOGIC' => array('&&', '||'), + 'BOOL' => array('true', 'false', 'null', 'undefined'), + 'STATEMENT' => array('break', 'continue') + ); + + foreach ($map as $k => $v) + { + if (in_array($id, $v)) + { + $tag = $k; + break; + } + } + } + + $this->token($tag, $id); + + if ($colon) + { + $this->token(':', ':'); + } + + return strlen($input); + } + + function interpolate_string($str, array $options = array()) // #{0} + { + $options = array_merge(array( + 'heredoc' => '', + 'regex' => NULL + ), + $options); + + $tokens = array(); + $pi = 0; + $i = -1; + + while ( isset($str{++$i}) ) + { + $letter = $str{$i}; + + if ($letter === '\\') + { + $i++; + continue; + } + + if ( ! ($letter === '#' && (substr($str, $i + 1, 1) === '{') && + ($expr = $this->balanced_string(substr($str, $i + 1), '}'))) ) + { + continue; + } + + if ($pi < $i) + { + $tokens[] = array('NEOSTRING', substr($str, $pi, $i - $pi)); + } + + $inner = substr($expr, 1, -1); + + if (strlen($inner)) + { + $lexer = new Lexer($inner, array( + 'line' => $this->line, + 'rewrite' => FALSE, + )); + + $nested = $lexer->tokenize(); + + array_pop($nested); + + if (isset($nested[0]) && $nested[0][0] === t('TERMINATOR')) + { + array_shift($nested); + } + + if ( ($length = count($nested)) ) + { + if ($length > 1) + { + array_unshift($nested, array(t('('), '(', $this->line)); + $nested[] = array(t(')'), ')', $this->line); + } + + $tokens[] = array('TOKENS', $nested); + } + } + + $i += strlen($expr); + $pi = $i + 1; + } + + if ($i > $pi && $pi < strlen($str)) + { + $tokens[] = array('NEOSTRING', substr($str, $pi)); + } + + if ($options['regex']) + { + return $tokens; + } + + if ( ! count($tokens)) + { + return $this->token('STRING', '""'); + } + + if ( ! ($tokens[0][0] === 'NEOSTRING')) + { + array_unshift($tokens, array('', '')); + } + + if ( ($interpolated = count($tokens) > 1) ) + { + $this->token('(', '('); + } + + for ($i = 0; $i < count($tokens); $i++) + { + list($tag, $value) = $tokens[$i]; + + if ($i) + { + $this->token('+', '+'); + } + + if ($tag === 'TOKENS') + { + $this->tokens = array_merge($this->tokens, $value); + } + else + { + $this->token('STRING', $this->make_string($value, '"', $options['heredoc'])); + } + } + + if ($interpolated) + { + $this->token(')', ')'); + } + + return $tokens; + } + + function js_token() + { + if ( ! ($this->chunk{0} === '`' && preg_match(self::$JSTOKEN, $this->chunk, $match))) + { + return 0; + } + + $this->token('JS', substr($script = $match[0], 1, -1)); + + return strlen($script); + } + + function line_token() + { + if ( ! preg_match(self::$MULTI_DENT, $this->chunk, $match)) + { + return 0; + } + + $indent = $match[0]; + $this->line += substr_count($indent, "\n"); + $this->seen_for = FALSE; + + // $prev = & last($this->tokens, 1); + $size = strlen($indent) - 1 - strrpos($indent, "\n"); + + $no_newlines = $this->unfinished(); + + if (($size - $this->indebt) === $this->indent) + { + if ($no_newlines) + { + $this->suppress_newlines(); + } + else + { + $this->newline_token(); + } + + return strlen($indent); + } + + if ($size > $this->indent) + { + if ($no_newlines) + { + $this->indebt = $size - $this->indent; + $this->suppress_newlines(); + + return strlen($indent); + } + + $diff = $size - $this->indent + $this->outdebt; + + $this->token('INDENT', $diff); + $this->indents[] = $diff; + $this->ends[] = 'OUTDENT'; + $this->outdebt = $this->indebt = 0; + } + else + { + $this->indebt = 0; + $this->outdent_token($this->indent - $size, $no_newlines); + } + + $this->indent = $size; + + return strlen($indent); + } + + function literal_token() + { + if (preg_match(self::$OPERATOR, $this->chunk, $match)) + { + list($value) = $match; + + if (preg_match(self::$CODE, $value)) + { + $this->tag_parameters(); + } + } + else + { + $value = $this->chunk{0}; + } + + $tag = t($value); + $prev = & last($this->tokens); + + if ($value === '=' && $prev) + { + if ( ! (isset($prev[1]->reserved) && $prev[1]->reserved) && in_array(''.$prev[1], self::$JS_FORBIDDEN)) + { + $this->error('reserved word "'.$this->value().'" can\'t be assigned'); + } + + if (in_array($prev[1], array('||', '&&'))) + { + $prev[0] = t('COMPOUND_ASSIGN'); + $prev[1] .= '='; + + return 1; + } + } + + if ($value === ';') + { + $this->seen_for = FALSE; + $tag = t('TERMINATOR'); + } + else if (in_array($value, self::$MATH)) + { + $tag = t('MATH'); + } + else if (in_array($value, self::$COMPARE)) + { + $tag = t('COMPARE'); + } + else if (in_array($value, self::$COMPOUND_ASSIGN)) + { + $tag = t('COMPOUND_ASSIGN'); + } + else if (in_array($value, self::$UNARY)) + { + $tag = t('UNARY'); + } + else if (in_array($value, self::$SHIFT)) + { + $tag = t('SHIFT'); + } + else if (in_array($value, self::$LOGIC) || $value === '?' && (isset($prev['spaced']) && $prev['spaced'])) + { + $tag = t('LOGIC'); + } + else if ($prev && ! (isset($prev['spaced']) && $prev['spaced'])) + { + if ($value === '(' && in_array($prev[0], t(self::$CALLABLE))) + { + if ($prev[0] === t('?')) + { + $prev[0] = t('FUNC_EXIST'); + } + + $tag = t('CALL_START'); + } + else if ($value === '[' && in_array($prev[0], t(self::$INDEXABLE))) + { + $tag = t('INDEX_START'); + + if ($prev[0] === t('?')) + { + $prev[0] = t('INDEX_SOAK'); + } + } + } + + if (in_array($value, array('(', '{', '['))) + { + $this->ends[] = self::$INVERSES[$value]; + } + else if (in_array($value, array(')', '}', ']'))) + { + $this->pair($value); + } + + $this->token($tag, $value); + + return strlen($value); + } + + function make_string($body, $quote, $heredoc = NULL) + { + if (!strlen($body)) + { + return $quote.$quote; + } + + $body = preg_replace_callback('/\\\\([\s\S])/', function($match) use ($quote) + { + $contents = $match[1]; + + if (in_array($contents, array("\n", $quote))) + { + return $contents; + } + + return $match[0]; + }, + $body); + + $body = preg_replace('/'.$quote.'/', '\\\\$0', $body); + + return $quote.$this->escape_lines($body, $heredoc).$quote; + } + + function newline_token() + { + while ($this->value() === ';') + { + array_pop($this->tokens); + } + + if ($this->tag() !== t('TERMINATOR')) + { + $this->token('TERMINATOR', "\n"); + } + } + + function number_token() + { + if ( ! preg_match(self::$NUMBER, $this->chunk, $match)) + { + return 0; + } + + $number = $match[0]; + + if (preg_match('/^0[BOX]/', $number)) + { + $this->error("radix prefix '$number' must be lowercase"); + } + else if (preg_match('/E/', $number) && ! preg_match('/^0x/', $number)) + { + $this->error("exponential notation '$number' must be indicated with a lowercase 'e'"); + } + else if (preg_match('/^0\d*[89]/', $number)) + { + $this->error("decimal literal '$number' must not be prefixed with '0'"); + } + else if (preg_match('/^0\d+/', $number)) + { + $this->error("octal literal '$number' must be prefixed with 0o"); + } + + $lexed_length = strlen($number); + + if (preg_match('/^0o([0-7]+)/', $number, $octal_literal)) + { + $number = '0x'.base_convert(intval($octal_literal[1], 8), 8, 16); + } + + if (preg_match('/^0b([01]+)/', $number, $binary_literal)) + { + $number = '0x'.base_convert(intval($binary_literal[1], 2), 2, 16); + } + + $this->token('NUMBER', $number); + + return $lexed_length; + } + + function outdent_token($move_out, $no_newlines = FALSE) + { + while ($move_out > 0) + { + $len = count($this->indents) - 1; + + if ( ! isset($this->indents[$len])) + { + $move_out = 0; + } + else if ($this->indents[$len] === $this->outdebt) + { + $move_out -= $this->outdebt; + $this->outdebt = 0; + } + else if ($this->indents[$len] < $this->outdebt) + { + $this->outdebt -= $this->indents[$len]; + $move_out -= $this->indents[$len]; + } + else + { + $dent = array_pop($this->indents) - $this->outdebt; + $move_out -= $dent; + $this->outdebt = 0; + $this->pair('OUTDENT'); + $this->token('OUTDENT', $dent); + } + } + + if (isset($dent) && $dent) + { + $this->outdebt -= $move_out; + } + + while ($this->value() == ';') + { + array_pop($this->tokens); + } + + if ( ! ($this->tag() === t('TERMINATOR') || $no_newlines)) + { + $this->token('TERMINATOR', "\n"); + } + + return $this; + } + + function pair($tag) + { + if ( ! ($tag === ($wanted = last($this->ends)))) + { + if ($wanted !== 'OUTDENT') + { + $this->error("unmateched $tag"); + } + + $this->indent -= $size = last($this->indents); + $this->outdent_token($size, TRUE); + + return $this->pair($tag); + } + + return array_pop($this->ends); + } + + function regex_token() + { + if ($this->chunk{0} !== '/') + { + return 0; + } + + if (preg_match(self::$HEREGEX, $this->chunk, $match)) + { + $length = $this->heregex_token($match); + $this->line += substr_count($match[0], "\n"); + + return $length; + } + + $prev = last($this->tokens); + + if ($prev) + { + if (in_array($prev[0], t((isset($prev['spaced']) && $prev['spaced']) ? + self::$NOT_REGEX : self::$NOT_SPACED_REGEX))) + { + return 0; + } + } + + if ( ! preg_match(self::$REGEX, $this->chunk, $match)) + { + return 0; + } + + list($match, $regex, $flags) = $match; + + if (substr($regex, 0, -1) === '/*') + { + $this->error('regular expressions cannot begin with `*`'); + } + + $regex = $regex === '//' ? '/(?:)/' : $regex; + + $this->token('REGEX', "{$regex}{$flags}"); + + return strlen($match); + } + + function sanitize_heredoc($doc, array $options) + { + $herecomment = isset($options['herecomment']) ? $options['herecomment'] : NULL; + $indent = isset($options['indent']) ? $options['indent'] : NULL; + + if ($herecomment) + { + if (preg_match(self::$HEREDOC_ILLEGAL, $doc)) + { + $this->error('block comment cannot contain "*/*, starting'); + } + + if ( ! strpos($doc, "\n")) + { + return $doc; + } + } + else + { + $offset = 0; + + while (preg_match(self::$HEREDOC_INDENT, $doc, $match, PREG_OFFSET_CAPTURE, $offset)) + { + $attempt = $match[1][0]; + $offset = strlen($match[0][0]) + $match[0][1]; + + if ( is_null($indent) || (strlen($indent) > strlen($attempt) && strlen($attempt) > 0)) + { + $indent = $attempt; + } + } + } + + if ($indent) + { + $doc = preg_replace('/\n'.$indent.'/', "\n", $doc); + } + + if ( ! $herecomment) + { + $doc = preg_replace('/^\n/', '', $doc); + } + + return $doc; + } + + function string_token() + { + switch ($this->chunk{0}) + { + case "'": + if ( ! preg_match(self::$SIMPLESTR, $this->chunk, $match)) + { + return 0; + } + + $this->token('STRING', preg_replace(self::$MULTILINER, "\\\n", $string = $match[0])); + break; + + case '"': + if ( ! ($string = $this->balanced_string($this->chunk, '"'))) + { + return 0; + } + + if (strpos($string, '#{', 1) > 0) + { + $this->interpolate_string(substr($string, 1, -1)); + } + else + { + $this->token('STRING', $this->escape_lines($string)); + } + + break; + + default: + return 0; + } + + if (preg_match('/^(?:\\\\.|[^\\\\])*\\\\[0-7]/', $string, $octal_esc)) + { + $this->error("octal escape sequences $string are not allowed"); + } + + $this->line += substr_count($string, "\n"); + + return strlen($string); + } + + function suppress_newlines() + { + if ($this->value() === '\\') + { + array_pop($this->tokens); + } + } + + function tag($index = 0, $tag = NULL) + { + $token = & last($this->tokens, $index); + + if ( ! is_null($tag)) + { + $token[0] = $tag; + } + + return $token[0]; + } + + function tag_parameters() + { + if ($this->tag() !== t(')')) + { + return $this; + } + + $stack = array(); + $tokens = &$this->tokens; + + $i = count($tokens); + $tokens[--$i][0] = t('PARAM_END'); + + while ( ($tok = &$tokens[--$i]) ) + { + if ($tok[0] === t(')')) + { + $stack[] = $tok; + } + else if (in_array($tok[0], t('(', 'CALL_START'))) + { + if (count($stack)) + { + array_pop($stack); + } + else if ($tok[0] === t('(')) + { + $tok[0] = t('PARAM_START'); + return $this; + } + else + { + return $this; + } + } + } + + return $this; + } + + function token($tag, $value = NULL) + { + if ( ! is_numeric($tag)) + { + $tag = t($tag); + } + + $token = array($tag, $value, $this->line); + + return ($this->tokens[] = $token); + } + + function tokenize() + { + while ( ($this->chunk = substr($this->code, $this->index)) !== FALSE ) + { + $types = array('identifier', 'comment', 'whitespace', 'line', 'heredoc', + 'string', 'number', 'regex', 'js', 'literal'); + + foreach ($types as $type) + { + if ( ($d = $this->{$type.'_token'}()) ) + { + $this->index += $d; + break; + } + } + } + + $this->close_indentation(); + + if (($tag = array_pop($this->ends)) !== NULL) + { + $this->error('missing '.t_canonical($tag)); + } + + if ($this->options['rewrite']) + { + $rewriter = new Rewriter($this->tokens); + $this->tokens = $rewriter->rewrite(); + } + + return $this->tokens; + } + + function value($index = 0, $value = NULL) + { + $token = & last($this->tokens, $index); + + if ( ! is_null($value)) + { + $token[1] = $value; + } + + return $token[1]; + } + + function unfinished() + { + return + preg_match(self::$LINE_CONTINUER, $this->chunk) || + in_array($this->tag(), t('\\', '.', '?.', 'UNARY', 'MATH', '+', '-', 'SHIFT', 'RELATION', + 'COMPARE', 'LOGIC', 'THROW', 'EXTENDS')); + } + + function whitespace_token() + { + if ( ! (preg_match(self::$WHITESPACE, $this->chunk, $match) || ($nline = ($this->chunk{0} === "\n")))) + { + return 0; + } + + $prev = & last($this->tokens); + + if ($prev) + { + $prev[$match ? 'spaced' : 'newLine'] = TRUE; + } + + return $match ? strlen($match[0]) : 0; + } +} + +?> diff --git a/sparks/assets/1.5.1/libraries/coffeescript/Nodes.php b/sparks/assets/1.5.1/libraries/coffeescript/Nodes.php new file mode 100755 index 0000000..bbcb2bf --- /dev/null +++ b/sparks/assets/1.5.1/libraries/coffeescript/Nodes.php @@ -0,0 +1,105 @@ +{$name}) && $parent->{$name} && $ifn = $parent->{$name}->unfold_soak($options))) + { + return NULL; + } + + $parent->{$name} = $ifn->body; + $ifn->body = yy('Value', $parent); + + return $ifn; + } + + static function utility($name) + { + Scope::$root->assign($ref = "__$name", call_user_func(array(__CLASS__, "utility_$name"))); + + return $ref; + } + + static function utility_bind() + { + return 'function(fn, me){ return function(){ return fn.apply(me, arguments); }; }'; + } + + static function utility_extends() + { + return 'function(child, parent) { ' + . 'for (var key in parent) { ' + . 'if ('.self::utility('hasProp').'.call(parent, key)) ' + . 'child[key] = parent[key]; ' + . '} ' + . 'function ctor() { ' + . 'this.constructor = child; ' + . '} ' + . 'ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; ' + . 'return child; ' + . '}'; + } + + static function utility_hasProp() + { + return '{}.hasOwnProperty'; + } + + static function utility_indexOf() + { + return '[].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }'; + } + + static function utility_slice() + { + return '[].slice'; + } + + /** + * Since PHP can't return values from __construct, and some of the node + * classes rely heavily on this feature in JavaScript, we use this function + * instead of 'new'. + */ + static function yy($type) + { + $args = func_get_args(); + array_shift($args); + + $type = __NAMESPACE__.'\yy_'.$type; + + $inst = new $type; + $inst = call_user_func_array(array($inst, 'constructor'), $args); + + return $inst; + } + +} + +?> diff --git a/sparks/assets/1.5.1/libraries/coffeescript/Parser.php b/sparks/assets/1.5.1/libraries/coffeescript/Parser.php new file mode 100755 index 0000000..5e176b8 --- /dev/null +++ b/sparks/assets/1.5.1/libraries/coffeescript/Parser.php @@ -0,0 +1,3330 @@ +string = $s->string; + $this->metadata = $s->metadata; + } else { + $this->string = (string) $s; + if ($m instanceof yyToken) { + $this->metadata = $m->metadata; + } elseif (is_array($m)) { + $this->metadata = $m; + } + } + } + + function __toString() + { + return $this->string; + } + + function offsetExists($offset) + { + return isset($this->metadata[$offset]); + } + + function offsetGet($offset) + { + return $this->metadata[$offset]; + } + + function offsetSet($offset, $value) + { + if ($offset === null) { + if (isset($value[0])) { + $x = ($value instanceof yyToken) ? + $value->metadata : $value; + $this->metadata = array_merge($this->metadata, $x); + return; + } + $offset = count($this->metadata); + } + if ($value === null) { + return; + } + if ($value instanceof yyToken) { + if ($value->metadata) { + $this->metadata[$offset] = $value->metadata; + } + } elseif ($value) { + $this->metadata[$offset] = $value; + } + } + + function offsetUnset($offset) + { + unset($this->metadata[$offset]); + } +} + +/** The following structure represents a single element of the + * parser's stack. Information stored includes: + * + * + The state number for the parser at this level of the stack. + * + * + The value of the token stored at this level of the stack. + * (In other words, the "major" token.) + * + * + The semantic value stored at this level of the stack. This is + * the information used by the action routines in the grammar. + * It is sometimes called the "minor" token. + */ +class yyStackEntry +{ + public $stateno; /* The state-number */ + public $major; /* The major token value. This is the code + ** number for the token at this stack level */ + public $minor; /* The user-supplied minor token value. This + ** is the value of the token */ +}; + +// code external to the class is included here + +// declare_class is output here +#line 2 "/var/www/coffeescript-php/grammar.y" + class Parser #line 102 "/var/www/coffeescript-php/grammar.php" +{ + static $LINE = 0; + static $FILE = 'unknown'; + +/* First off, code is included which follows the "include_class" declaration +** in the input file. */ + +/* Next is all token values, as class constants +*/ +/* +** These constants (all generated automatically by the parser generator) +** specify the various kinds of tokens (terminals) that the parser +** understands. +** +** Each symbol here is a terminal symbol in the grammar. +*/ + const YY_POST_IF = 1; + const YY_IF = 2; + const YY_ELSE = 3; + const YY_FOR = 4; + const YY_WHILE = 5; + const YY_UNTIL = 6; + const YY_LOOP = 7; + const YY_SUPER = 8; + const YY_CLASS = 9; + const YY_FORIN = 10; + const YY_FOROF = 11; + const YY_BY = 12; + const YY_WHEN = 13; + const YY_EQUALS = 14; + const YY_COLON = 15; + const YY_COMPOUND_ASSIGN = 16; + const YY_RETURN = 17; + const YY_THROW = 18; + const YY_EXTENDS = 19; + const YY_INDENT = 20; + const YY_OUTDENT = 21; + const YY_LOGIC = 22; + const YY_COMPARE = 23; + const YY_RELATION = 24; + const YY_SHIFT = 25; + const YY_PLUS = 26; + const YY_MINUS = 27; + const YY_MATH = 28; + const YY_UNARY = 29; + const YY_EXISTENTIAL = 30; + const YY_INCREMENT = 31; + const YY_DECREMENT = 32; + const YY_CALL_START = 33; + const YY_CALL_END = 34; + const YY_ACCESSOR = 35; + const YY_EXISTENTIAL_ACCESSOR = 36; + const YY_PROTOTYPE = 37; + const YY_TERMINATOR = 38; + const YY_STATEMENT = 39; + const YY_IDENTIFIER = 40; + const YY_NUMBER = 41; + const YY_STRING = 42; + const YY_JS = 43; + const YY_REGEX = 44; + const YY_DEBUGGER = 45; + const YY_BOOL = 46; + const YY_HERECOMMENT = 47; + const YY_PARAM_START = 48; + const YY_PARAM_END = 49; + const YY_FUNC = 50; + const YY_BOUND_FUNC = 51; + const YY_COMMA = 52; + const YY_RANGE_EXCLUSIVE = 53; + const YY_INDEX_START = 54; + const YY_INDEX_END = 55; + const YY_INDEX_SOAK = 56; + const YY_OBJECT_START = 57; + const YY_OBJECT_END = 58; + const YY_FUNC_EXIST = 59; + const YY_THIS = 60; + const YY_AT_SIGN = 61; + const YY_ARRAY_START = 62; + const YY_ARRAY_END = 63; + const YY_RANGE_INCLUSIVE = 64; + const YY_TRY = 65; + const YY_FINALLY = 66; + const YY_CATCH = 67; + const YY_PAREN_START = 68; + const YY_PAREN_END = 69; + const YY_OWN = 70; + const YY_SWITCH = 71; + const YY_LEADING_WHEN = 72; + const YY_NO_ACTION = 512; + const YY_ACCEPT_ACTION = 511; + const YY_ERROR_ACTION = 510; + +/* Next are that tables used to determine what action to take based on the +** current state and lookahead token. These tables are used to implement +** functions that take a state number and lookahead value and return an +** action integer. +** +** Suppose the action integer is N. Then the action is determined as +** follows +** +** 0 <= N < self::YYNSTATE Shift N. That is, +** push the lookahead +** token onto the stack +** and goto state N. +** +** self::YYNSTATE <= N < self::YYNSTATE+self::YYNRULE Reduce by rule N-YYNSTATE. +** +** N == self::YYNSTATE+self::YYNRULE A syntax error has occurred. +** +** N == self::YYNSTATE+self::YYNRULE+1 The parser accepts its +** input. (and concludes parsing) +** +** N == self::YYNSTATE+self::YYNRULE+2 No such action. Denotes unused +** slots in the yy_action[] table. +** +** The action table is constructed as a single large static array $yy_action. +** Given state S and lookahead X, the action is computed as +** +** self::$yy_action[self::$yy_shift_ofst[S] + X ] +** +** If the index value self::$yy_shift_ofst[S]+X is out of range or if the value +** self::$yy_lookahead[self::$yy_shift_ofst[S]+X] is not equal to X or if +** self::$yy_shift_ofst[S] is equal to self::YY_SHIFT_USE_DFLT, it means that +** the action is not in the table and that self::$yy_default[S] should be used instead. +** +** The formula above is for computing the action when the lookahead is +** a terminal symbol. If the lookahead is a non-terminal (as occurs after +** a reduce action) then the static $yy_reduce_ofst array is used in place of +** the static $yy_shift_ofst array and self::YY_REDUCE_USE_DFLT is used in place of +** self::YY_SHIFT_USE_DFLT. +** +** The following are the tables generated in this section: +** +** self::$yy_action A single table containing all actions. +** self::$yy_lookahead A table containing the lookahead for each entry in +** yy_action. Used to detect hash collisions. +** self::$yy_shift_ofst For each state, the offset into self::$yy_action for +** shifting terminals. +** self::$yy_reduce_ofst For each state, the offset into self::$yy_action for +** shifting non-terminals after a reduce. +** self::$yy_default Default action for each state. +*/ + const YY_SZ_ACTTAB = 4586; +static public $yy_action = array( + /* 0 */ 511, 181, 186, 235, 90, 118, 254, 255, 111, 109, + /* 10 */ 266, 267, 264, 263, 259, 260, 261, 262, 246, 245, + /* 20 */ 237, 232, 300, 178, 37, 26, 238, 20, 158, 309, + /* 30 */ 35, 19, 269, 299, 4, 164, 132, 301, 298, 297, + /* 40 */ 39, 21, 36, 22, 32, 34, 20, 225, 309, 226, + /* 50 */ 135, 154, 291, 147, 135, 32, 34, 20, 8, 309, + /* 60 */ 183, 70, 117, 254, 255, 111, 109, 266, 267, 264, + /* 70 */ 263, 259, 260, 261, 262, 246, 245, 237, 232, 300, + /* 80 */ 178, 130, 13, 238, 66, 158, 251, 230, 231, 269, + /* 90 */ 299, 287, 164, 234, 301, 298, 297, 21, 36, 22, + /* 100 */ 32, 34, 20, 139, 309, 283, 241, 144, 154, 291, + /* 110 */ 147, 135, 243, 306, 244, 135, 8, 183, 69, 117, + /* 120 */ 254, 255, 111, 109, 266, 267, 264, 263, 259, 260, + /* 130 */ 261, 262, 246, 245, 237, 232, 300, 178, 151, 152, + /* 140 */ 238, 1, 158, 179, 310, 200, 269, 299, 52, 164, + /* 150 */ 251, 301, 298, 297, 16, 185, 222, 45, 201, 195, + /* 160 */ 13, 44, 197, 180, 199, 154, 291, 147, 135, 268, + /* 170 */ 239, 159, 40, 169, 183, 77, 117, 254, 255, 111, + /* 180 */ 109, 266, 267, 264, 263, 259, 260, 261, 262, 246, + /* 190 */ 245, 237, 232, 300, 178, 129, 272, 238, 65, 158, + /* 200 */ 128, 272, 292, 269, 299, 287, 164, 305, 301, 298, + /* 210 */ 297, 202, 36, 22, 32, 34, 20, 138, 309, 283, + /* 220 */ 240, 236, 154, 291, 147, 135, 279, 75, 293, 307, + /* 230 */ 131, 183, 77, 117, 254, 255, 111, 109, 266, 267, + /* 240 */ 264, 263, 259, 260, 261, 262, 246, 245, 237, 232, + /* 250 */ 300, 178, 205, 190, 238, 242, 158, 150, 221, 277, + /* 260 */ 269, 299, 287, 164, 219, 301, 298, 297, 214, 173, + /* 270 */ 204, 208, 13, 207, 139, 225, 283, 226, 135, 154, + /* 280 */ 291, 147, 135, 275, 13, 206, 198, 271, 183, 77, + /* 290 */ 117, 254, 255, 111, 109, 266, 267, 264, 263, 259, + /* 300 */ 260, 261, 262, 246, 245, 237, 232, 300, 178, 211, + /* 310 */ 184, 238, 302, 158, 280, 15, 165, 269, 299, 287, + /* 320 */ 164, 17, 301, 298, 297, 22, 32, 34, 20, 207, + /* 330 */ 309, 140, 225, 283, 226, 135, 154, 291, 147, 135, + /* 340 */ 296, 206, 198, 227, 247, 183, 77, 117, 254, 255, + /* 350 */ 111, 109, 266, 267, 264, 263, 259, 260, 261, 262, + /* 360 */ 246, 245, 237, 232, 300, 178, 220, 248, 238, 55, + /* 370 */ 158, 9, 166, 167, 269, 299, 287, 164, 170, 301, + /* 380 */ 298, 297, 381, 5, 381, 381, 381, 8, 145, 225, + /* 390 */ 283, 226, 135, 154, 291, 147, 135, 203, 187, 176, + /* 400 */ 14, 11, 183, 381, 309, 381, 304, 175, 381, 235, + /* 410 */ 90, 118, 254, 255, 111, 109, 266, 267, 264, 263, + /* 420 */ 259, 260, 261, 262, 246, 245, 237, 232, 300, 178, + /* 430 */ 295, 258, 238, 270, 158, 278, 376, 149, 269, 299, + /* 440 */ 168, 164, 276, 301, 298, 297, 56, 189, 67, 47, + /* 450 */ 24, 12, 160, 58, 376, 134, 249, 154, 291, 147, + /* 460 */ 135, 57, 25, 182, 6, 308, 183, 265, 376, 177, + /* 470 */ 42, 43, 333, 50, 333, 59, 60, 28, 333, 376, + /* 480 */ 67, 47, 24, 256, 251, 230, 231, 233, 229, 228, + /* 490 */ 224, 234, 63, 333, 240, 236, 27, 333, 61, 215, + /* 500 */ 225, 62, 226, 135, 303, 153, 3, 288, 333, 148, + /* 510 */ 5, 284, 7, 333, 207, 18, 64, 333, 56, 333, + /* 520 */ 67, 47, 24, 12, 160, 58, 206, 198, 11, 333, + /* 530 */ 333, 212, 217, 57, 25, 333, 53, 333, 333, 333, + /* 540 */ 333, 333, 42, 43, 333, 50, 333, 59, 60, 333, + /* 550 */ 218, 184, 5, 285, 54, 256, 251, 230, 231, 233, + /* 560 */ 229, 228, 224, 234, 63, 333, 240, 236, 51, 333, + /* 570 */ 11, 5, 225, 62, 226, 135, 303, 153, 3, 333, + /* 580 */ 225, 148, 226, 135, 7, 333, 333, 18, 172, 11, + /* 590 */ 235, 90, 118, 254, 255, 111, 109, 266, 267, 264, + /* 600 */ 263, 259, 260, 261, 262, 246, 245, 237, 232, 300, + /* 610 */ 178, 333, 333, 238, 286, 158, 333, 61, 333, 269, + /* 620 */ 299, 333, 164, 333, 301, 298, 297, 56, 333, 67, + /* 630 */ 47, 24, 12, 160, 58, 64, 251, 333, 154, 291, + /* 640 */ 147, 135, 57, 25, 333, 6, 333, 183, 333, 333, + /* 650 */ 333, 42, 43, 62, 50, 193, 59, 60, 2, 210, + /* 660 */ 333, 251, 333, 333, 256, 251, 230, 231, 233, 229, + /* 670 */ 228, 224, 234, 63, 333, 240, 236, 333, 62, 333, + /* 680 */ 333, 333, 62, 3, 252, 303, 153, 3, 250, 333, + /* 690 */ 148, 68, 155, 7, 333, 171, 18, 235, 90, 118, + /* 700 */ 254, 255, 111, 109, 266, 267, 264, 263, 259, 260, + /* 710 */ 261, 262, 246, 245, 237, 232, 300, 178, 333, 333, + /* 720 */ 238, 333, 158, 333, 333, 333, 269, 299, 333, 164, + /* 730 */ 333, 301, 298, 297, 56, 333, 67, 47, 24, 12, + /* 740 */ 160, 58, 333, 333, 333, 154, 291, 147, 135, 57, + /* 750 */ 25, 333, 257, 333, 183, 333, 250, 333, 42, 43, + /* 760 */ 142, 50, 333, 59, 60, 333, 333, 333, 141, 333, + /* 770 */ 333, 256, 251, 230, 231, 233, 229, 228, 224, 234, + /* 780 */ 63, 333, 240, 236, 333, 213, 273, 333, 333, 62, + /* 790 */ 333, 333, 303, 153, 3, 333, 311, 148, 333, 333, + /* 800 */ 7, 333, 333, 18, 333, 77, 117, 254, 255, 111, + /* 810 */ 109, 266, 267, 264, 263, 259, 260, 261, 262, 246, + /* 820 */ 245, 237, 232, 300, 178, 333, 333, 238, 333, 158, + /* 830 */ 333, 333, 38, 269, 299, 287, 164, 14, 301, 298, + /* 840 */ 297, 333, 39, 21, 36, 22, 32, 34, 20, 290, + /* 850 */ 309, 333, 154, 291, 147, 135, 333, 333, 333, 333, + /* 860 */ 333, 183, 77, 117, 254, 255, 111, 109, 266, 267, + /* 870 */ 264, 263, 259, 260, 261, 262, 246, 245, 237, 232, + /* 880 */ 300, 178, 333, 333, 238, 333, 158, 333, 333, 333, + /* 890 */ 269, 299, 287, 164, 333, 301, 298, 297, 333, 39, + /* 900 */ 21, 36, 22, 32, 34, 20, 282, 309, 333, 154, + /* 910 */ 291, 147, 135, 221, 333, 333, 333, 333, 183, 219, + /* 920 */ 174, 333, 333, 216, 173, 204, 208, 253, 90, 118, + /* 930 */ 254, 255, 111, 109, 266, 267, 264, 263, 259, 260, + /* 940 */ 261, 262, 246, 245, 237, 232, 300, 178, 333, 333, + /* 950 */ 238, 333, 158, 333, 333, 31, 269, 299, 333, 164, + /* 960 */ 333, 301, 298, 297, 39, 21, 36, 22, 32, 34, + /* 970 */ 20, 333, 309, 333, 333, 154, 291, 147, 135, 333, + /* 980 */ 333, 333, 333, 281, 183, 94, 117, 254, 255, 111, + /* 990 */ 109, 266, 267, 264, 263, 259, 260, 261, 262, 246, + /* 1000 */ 245, 237, 232, 300, 178, 333, 333, 238, 333, 158, + /* 1010 */ 333, 333, 333, 269, 299, 333, 164, 333, 301, 298, + /* 1020 */ 297, 56, 333, 67, 47, 24, 12, 160, 58, 333, + /* 1030 */ 333, 333, 154, 291, 147, 135, 57, 25, 333, 48, + /* 1040 */ 333, 183, 333, 333, 333, 42, 43, 333, 50, 333, + /* 1050 */ 59, 60, 333, 333, 333, 333, 333, 333, 256, 251, + /* 1060 */ 230, 231, 233, 229, 228, 224, 234, 63, 333, 240, + /* 1070 */ 236, 143, 333, 333, 333, 333, 62, 333, 333, 303, + /* 1080 */ 153, 3, 333, 333, 148, 333, 333, 7, 333, 223, + /* 1090 */ 18, 333, 333, 56, 333, 67, 47, 24, 12, 160, + /* 1100 */ 58, 333, 333, 416, 333, 137, 136, 146, 57, 25, + /* 1110 */ 333, 125, 333, 333, 333, 333, 333, 42, 43, 333, + /* 1120 */ 50, 333, 59, 60, 4, 333, 132, 333, 251, 294, + /* 1130 */ 256, 251, 230, 231, 233, 229, 228, 224, 234, 63, + /* 1140 */ 14, 240, 236, 333, 333, 62, 333, 333, 62, 144, + /* 1150 */ 2, 303, 153, 3, 333, 333, 148, 333, 333, 7, + /* 1160 */ 333, 333, 18, 333, 333, 56, 333, 67, 47, 24, + /* 1170 */ 12, 160, 58, 333, 333, 333, 333, 137, 136, 146, + /* 1180 */ 57, 25, 333, 33, 333, 333, 333, 333, 333, 42, + /* 1190 */ 43, 333, 50, 333, 59, 60, 4, 333, 132, 333, + /* 1200 */ 333, 294, 256, 251, 230, 231, 233, 229, 228, 224, + /* 1210 */ 234, 63, 333, 240, 236, 333, 333, 333, 333, 333, + /* 1220 */ 62, 333, 333, 303, 153, 3, 333, 333, 148, 333, + /* 1230 */ 333, 7, 333, 333, 18, 89, 117, 254, 255, 111, + /* 1240 */ 109, 266, 267, 264, 263, 259, 260, 261, 262, 246, + /* 1250 */ 245, 237, 232, 300, 178, 333, 333, 238, 333, 158, + /* 1260 */ 333, 333, 333, 269, 299, 333, 164, 333, 301, 298, + /* 1270 */ 297, 56, 333, 67, 47, 24, 12, 160, 58, 333, + /* 1280 */ 133, 333, 154, 291, 147, 135, 57, 25, 333, 8, + /* 1290 */ 333, 183, 333, 333, 333, 42, 43, 333, 50, 333, + /* 1300 */ 59, 60, 333, 333, 333, 333, 333, 333, 256, 251, + /* 1310 */ 230, 231, 233, 229, 228, 224, 234, 63, 333, 240, + /* 1320 */ 236, 333, 333, 333, 333, 333, 62, 333, 333, 303, + /* 1330 */ 153, 3, 333, 333, 148, 333, 333, 7, 333, 333, + /* 1340 */ 18, 333, 333, 56, 333, 67, 47, 24, 12, 160, + /* 1350 */ 58, 333, 333, 333, 333, 333, 333, 333, 57, 25, + /* 1360 */ 333, 333, 188, 333, 333, 333, 333, 42, 43, 333, + /* 1370 */ 50, 333, 59, 60, 333, 333, 333, 333, 333, 333, + /* 1380 */ 256, 251, 230, 231, 233, 229, 228, 224, 234, 63, + /* 1390 */ 333, 240, 236, 333, 333, 333, 333, 333, 62, 333, + /* 1400 */ 333, 303, 153, 3, 333, 333, 148, 333, 333, 7, + /* 1410 */ 333, 333, 18, 333, 333, 56, 333, 67, 47, 24, + /* 1420 */ 12, 160, 58, 333, 333, 333, 333, 333, 333, 333, + /* 1430 */ 57, 25, 333, 10, 333, 333, 333, 333, 333, 42, + /* 1440 */ 43, 333, 50, 333, 59, 60, 333, 333, 333, 333, + /* 1450 */ 333, 333, 256, 251, 230, 231, 233, 229, 228, 224, + /* 1460 */ 234, 63, 333, 240, 236, 333, 333, 333, 333, 333, + /* 1470 */ 62, 333, 333, 303, 153, 3, 333, 333, 148, 333, + /* 1480 */ 333, 7, 333, 333, 18, 333, 333, 56, 333, 67, + /* 1490 */ 47, 24, 12, 160, 58, 333, 333, 333, 333, 333, + /* 1500 */ 333, 333, 57, 25, 333, 6, 333, 333, 333, 333, + /* 1510 */ 333, 42, 43, 333, 50, 333, 59, 60, 333, 333, + /* 1520 */ 333, 333, 333, 333, 256, 251, 230, 231, 233, 229, + /* 1530 */ 228, 224, 234, 63, 333, 240, 236, 333, 333, 333, + /* 1540 */ 333, 333, 62, 333, 333, 303, 153, 3, 333, 333, + /* 1550 */ 148, 333, 333, 7, 333, 333, 18, 121, 117, 254, + /* 1560 */ 255, 111, 109, 266, 267, 264, 263, 259, 260, 261, + /* 1570 */ 262, 246, 245, 237, 232, 300, 178, 333, 333, 238, + /* 1580 */ 333, 158, 333, 333, 333, 269, 299, 333, 164, 333, + /* 1590 */ 301, 298, 297, 56, 333, 67, 47, 24, 12, 160, + /* 1600 */ 58, 333, 333, 333, 154, 291, 147, 135, 57, 25, + /* 1610 */ 333, 333, 333, 183, 333, 333, 333, 42, 43, 333, + /* 1620 */ 50, 333, 59, 60, 333, 333, 333, 333, 333, 333, + /* 1630 */ 256, 251, 230, 231, 233, 229, 228, 224, 234, 63, + /* 1640 */ 333, 240, 236, 333, 333, 333, 333, 333, 62, 333, + /* 1650 */ 333, 303, 153, 3, 333, 333, 148, 333, 333, 7, + /* 1660 */ 333, 333, 18, 333, 120, 117, 254, 255, 111, 109, + /* 1670 */ 266, 267, 264, 263, 259, 260, 261, 262, 246, 245, + /* 1680 */ 237, 232, 300, 178, 333, 333, 238, 333, 158, 333, + /* 1690 */ 333, 29, 269, 299, 333, 164, 333, 301, 298, 297, + /* 1700 */ 39, 21, 36, 22, 32, 34, 20, 333, 309, 333, + /* 1710 */ 333, 154, 291, 147, 135, 333, 333, 333, 333, 333, + /* 1720 */ 183, 82, 117, 254, 255, 111, 109, 266, 267, 264, + /* 1730 */ 263, 259, 260, 261, 262, 246, 245, 237, 232, 300, + /* 1740 */ 178, 333, 333, 238, 200, 158, 333, 333, 333, 269, + /* 1750 */ 299, 333, 164, 333, 301, 298, 297, 201, 195, 333, + /* 1760 */ 333, 191, 180, 199, 333, 333, 333, 333, 154, 291, + /* 1770 */ 147, 135, 333, 333, 333, 333, 333, 183, 101, 117, + /* 1780 */ 254, 255, 111, 109, 266, 267, 264, 263, 259, 260, + /* 1790 */ 261, 262, 246, 245, 237, 232, 300, 178, 333, 333, + /* 1800 */ 238, 333, 158, 333, 333, 333, 269, 299, 333, 164, + /* 1810 */ 333, 301, 298, 297, 333, 333, 333, 333, 333, 333, + /* 1820 */ 333, 333, 333, 333, 333, 154, 291, 147, 135, 333, + /* 1830 */ 333, 333, 333, 333, 183, 95, 117, 254, 255, 111, + /* 1840 */ 109, 266, 267, 264, 263, 259, 260, 261, 262, 246, + /* 1850 */ 245, 237, 232, 300, 178, 333, 333, 238, 333, 158, + /* 1860 */ 333, 333, 333, 269, 299, 333, 164, 333, 301, 298, + /* 1870 */ 297, 333, 333, 333, 333, 333, 333, 333, 333, 333, + /* 1880 */ 333, 333, 154, 291, 147, 135, 333, 333, 333, 333, + /* 1890 */ 333, 183, 96, 117, 254, 255, 111, 109, 266, 267, + /* 1900 */ 264, 263, 259, 260, 261, 262, 246, 245, 237, 232, + /* 1910 */ 300, 178, 333, 333, 238, 333, 158, 333, 333, 333, + /* 1920 */ 269, 299, 333, 164, 333, 301, 298, 297, 333, 333, + /* 1930 */ 333, 333, 333, 333, 333, 333, 333, 333, 333, 154, + /* 1940 */ 291, 147, 135, 333, 333, 333, 333, 333, 183, 100, + /* 1950 */ 117, 254, 255, 111, 109, 266, 267, 264, 263, 259, + /* 1960 */ 260, 261, 262, 246, 245, 237, 232, 300, 178, 333, + /* 1970 */ 333, 238, 333, 158, 333, 333, 333, 269, 299, 333, + /* 1980 */ 164, 333, 301, 298, 297, 333, 333, 333, 333, 333, + /* 1990 */ 333, 333, 333, 333, 333, 333, 154, 291, 147, 135, + /* 2000 */ 333, 333, 333, 333, 333, 183, 115, 117, 254, 255, + /* 2010 */ 111, 109, 266, 267, 264, 263, 259, 260, 261, 262, + /* 2020 */ 246, 245, 237, 232, 300, 178, 333, 333, 238, 333, + /* 2030 */ 158, 333, 333, 333, 269, 299, 333, 164, 333, 301, + /* 2040 */ 298, 297, 333, 333, 333, 333, 333, 333, 333, 333, + /* 2050 */ 333, 333, 333, 154, 291, 147, 135, 333, 333, 333, + /* 2060 */ 333, 333, 183, 110, 117, 254, 255, 111, 109, 266, + /* 2070 */ 267, 264, 263, 259, 260, 261, 262, 246, 245, 237, + /* 2080 */ 232, 300, 178, 333, 333, 238, 333, 158, 333, 333, + /* 2090 */ 333, 269, 299, 333, 164, 333, 301, 298, 297, 333, + /* 2100 */ 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, + /* 2110 */ 154, 291, 147, 135, 333, 333, 333, 333, 333, 183, + /* 2120 */ 84, 117, 254, 255, 111, 109, 266, 267, 264, 263, + /* 2130 */ 259, 260, 261, 262, 246, 245, 237, 232, 300, 178, + /* 2140 */ 333, 333, 238, 333, 158, 333, 333, 333, 269, 299, + /* 2150 */ 333, 164, 333, 301, 298, 297, 333, 333, 333, 333, + /* 2160 */ 333, 333, 333, 333, 333, 333, 333, 154, 291, 147, + /* 2170 */ 135, 333, 333, 333, 333, 333, 183, 102, 117, 254, + /* 2180 */ 255, 111, 109, 266, 267, 264, 263, 259, 260, 261, + /* 2190 */ 262, 246, 245, 237, 232, 300, 178, 333, 333, 238, + /* 2200 */ 333, 158, 333, 333, 333, 269, 299, 333, 164, 333, + /* 2210 */ 301, 298, 297, 333, 333, 333, 333, 333, 333, 333, + /* 2220 */ 333, 333, 333, 333, 154, 291, 147, 135, 333, 333, + /* 2230 */ 333, 333, 333, 183, 81, 117, 254, 255, 111, 109, + /* 2240 */ 266, 267, 264, 263, 259, 260, 261, 262, 246, 245, + /* 2250 */ 237, 232, 300, 178, 333, 333, 238, 333, 158, 333, + /* 2260 */ 333, 333, 269, 299, 333, 164, 333, 301, 298, 297, + /* 2270 */ 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, + /* 2280 */ 333, 154, 291, 147, 135, 333, 333, 333, 333, 333, + /* 2290 */ 183, 72, 117, 254, 255, 111, 109, 266, 267, 264, + /* 2300 */ 263, 259, 260, 261, 262, 246, 245, 237, 232, 300, + /* 2310 */ 178, 333, 333, 238, 333, 158, 333, 333, 333, 269, + /* 2320 */ 299, 333, 164, 333, 301, 298, 297, 333, 333, 333, + /* 2330 */ 333, 333, 333, 333, 333, 333, 333, 333, 154, 291, + /* 2340 */ 147, 135, 333, 333, 333, 333, 333, 183, 104, 117, + /* 2350 */ 254, 255, 111, 109, 266, 267, 264, 263, 259, 260, + /* 2360 */ 261, 262, 246, 245, 237, 232, 300, 178, 333, 333, + /* 2370 */ 238, 333, 158, 333, 333, 333, 269, 299, 333, 164, + /* 2380 */ 333, 301, 298, 297, 333, 333, 333, 333, 333, 333, + /* 2390 */ 333, 333, 333, 333, 333, 154, 291, 147, 135, 333, + /* 2400 */ 333, 333, 333, 333, 183, 71, 117, 254, 255, 111, + /* 2410 */ 109, 266, 267, 264, 263, 259, 260, 261, 262, 246, + /* 2420 */ 245, 237, 232, 300, 178, 333, 333, 238, 333, 158, + /* 2430 */ 333, 333, 333, 269, 299, 333, 164, 333, 301, 298, + /* 2440 */ 297, 333, 333, 333, 333, 333, 333, 333, 333, 333, + /* 2450 */ 333, 333, 154, 291, 147, 135, 333, 333, 333, 333, + /* 2460 */ 333, 183, 83, 117, 254, 255, 111, 109, 266, 267, + /* 2470 */ 264, 263, 259, 260, 261, 262, 246, 245, 237, 232, + /* 2480 */ 300, 178, 333, 333, 238, 333, 158, 333, 333, 333, + /* 2490 */ 269, 299, 333, 164, 333, 301, 298, 297, 333, 333, + /* 2500 */ 333, 333, 333, 333, 333, 333, 333, 333, 333, 154, + /* 2510 */ 291, 147, 135, 333, 333, 333, 333, 333, 183, 76, + /* 2520 */ 117, 254, 255, 111, 109, 266, 267, 264, 263, 259, + /* 2530 */ 260, 261, 262, 246, 245, 237, 232, 300, 178, 333, + /* 2540 */ 333, 238, 333, 158, 333, 333, 333, 269, 299, 333, + /* 2550 */ 164, 333, 301, 298, 297, 333, 333, 333, 333, 333, + /* 2560 */ 333, 333, 333, 333, 333, 333, 154, 291, 147, 135, + /* 2570 */ 333, 333, 333, 333, 333, 183, 93, 117, 254, 255, + /* 2580 */ 111, 109, 266, 267, 264, 263, 259, 260, 261, 262, + /* 2590 */ 246, 245, 237, 232, 300, 178, 333, 333, 238, 333, + /* 2600 */ 158, 333, 333, 333, 269, 299, 333, 164, 333, 301, + /* 2610 */ 298, 297, 333, 333, 333, 333, 333, 333, 333, 333, + /* 2620 */ 333, 333, 333, 154, 291, 147, 135, 333, 333, 333, + /* 2630 */ 333, 333, 183, 85, 117, 254, 255, 111, 109, 266, + /* 2640 */ 267, 264, 263, 259, 260, 261, 262, 246, 245, 237, + /* 2650 */ 232, 300, 178, 333, 333, 238, 333, 158, 333, 333, + /* 2660 */ 333, 269, 299, 333, 164, 333, 301, 298, 297, 333, + /* 2670 */ 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, + /* 2680 */ 154, 291, 147, 135, 333, 333, 333, 333, 333, 183, + /* 2690 */ 124, 117, 254, 255, 111, 109, 266, 267, 264, 263, + /* 2700 */ 259, 260, 261, 262, 246, 245, 237, 232, 300, 178, + /* 2710 */ 333, 333, 238, 333, 158, 333, 333, 333, 269, 299, + /* 2720 */ 333, 164, 333, 301, 298, 297, 333, 333, 333, 333, + /* 2730 */ 333, 333, 333, 333, 333, 333, 333, 154, 291, 147, + /* 2740 */ 135, 333, 333, 333, 333, 333, 183, 88, 117, 254, + /* 2750 */ 255, 111, 109, 266, 267, 264, 263, 259, 260, 261, + /* 2760 */ 262, 246, 245, 237, 232, 300, 178, 333, 333, 238, + /* 2770 */ 333, 158, 333, 333, 333, 269, 299, 333, 164, 333, + /* 2780 */ 301, 298, 297, 333, 333, 333, 333, 333, 333, 333, + /* 2790 */ 333, 333, 333, 333, 154, 291, 147, 135, 333, 333, + /* 2800 */ 333, 333, 333, 183, 73, 117, 254, 255, 111, 109, + /* 2810 */ 266, 267, 264, 263, 259, 260, 261, 262, 246, 245, + /* 2820 */ 237, 232, 300, 178, 333, 333, 238, 333, 158, 333, + /* 2830 */ 333, 333, 269, 299, 333, 164, 333, 301, 298, 297, + /* 2840 */ 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, + /* 2850 */ 333, 154, 291, 147, 135, 333, 333, 333, 333, 333, + /* 2860 */ 183, 114, 117, 254, 255, 111, 109, 266, 267, 264, + /* 2870 */ 263, 259, 260, 261, 262, 246, 245, 237, 232, 300, + /* 2880 */ 178, 333, 333, 238, 333, 158, 333, 333, 333, 269, + /* 2890 */ 299, 333, 164, 333, 301, 298, 297, 333, 333, 333, + /* 2900 */ 333, 333, 333, 333, 333, 333, 333, 333, 154, 291, + /* 2910 */ 147, 135, 333, 333, 333, 333, 333, 183, 123, 117, + /* 2920 */ 254, 255, 111, 109, 266, 267, 264, 263, 259, 260, + /* 2930 */ 261, 262, 246, 245, 237, 232, 300, 178, 333, 333, + /* 2940 */ 238, 333, 158, 333, 333, 333, 269, 299, 333, 164, + /* 2950 */ 333, 301, 298, 297, 333, 333, 333, 333, 333, 333, + /* 2960 */ 333, 333, 333, 333, 333, 154, 291, 147, 135, 333, + /* 2970 */ 333, 333, 333, 333, 183, 98, 117, 254, 255, 111, + /* 2980 */ 109, 266, 267, 264, 263, 259, 260, 261, 262, 246, + /* 2990 */ 245, 237, 232, 300, 178, 333, 333, 238, 333, 158, + /* 3000 */ 333, 333, 333, 269, 299, 333, 164, 333, 301, 298, + /* 3010 */ 297, 333, 333, 333, 333, 333, 333, 333, 333, 333, + /* 3020 */ 333, 333, 154, 291, 147, 135, 333, 333, 333, 333, + /* 3030 */ 333, 183, 87, 117, 254, 255, 111, 109, 266, 267, + /* 3040 */ 264, 263, 259, 260, 261, 262, 246, 245, 237, 232, + /* 3050 */ 300, 178, 333, 333, 238, 333, 158, 333, 333, 333, + /* 3060 */ 269, 299, 333, 164, 333, 301, 298, 297, 333, 333, + /* 3070 */ 333, 333, 333, 333, 333, 333, 333, 333, 333, 154, + /* 3080 */ 291, 147, 135, 333, 333, 333, 333, 333, 183, 74, + /* 3090 */ 117, 254, 255, 111, 109, 266, 267, 264, 263, 259, + /* 3100 */ 260, 261, 262, 246, 245, 237, 232, 300, 178, 333, + /* 3110 */ 333, 238, 333, 158, 333, 333, 333, 269, 299, 333, + /* 3120 */ 164, 333, 301, 298, 297, 333, 333, 333, 333, 333, + /* 3130 */ 333, 333, 333, 333, 333, 333, 154, 291, 147, 135, + /* 3140 */ 333, 333, 333, 333, 333, 183, 97, 117, 254, 255, + /* 3150 */ 111, 109, 266, 267, 264, 263, 259, 260, 261, 262, + /* 3160 */ 246, 245, 237, 232, 300, 178, 333, 333, 238, 333, + /* 3170 */ 158, 333, 333, 333, 269, 299, 333, 164, 333, 301, + /* 3180 */ 298, 297, 333, 333, 333, 333, 333, 333, 333, 333, + /* 3190 */ 333, 333, 333, 154, 291, 147, 135, 333, 333, 333, + /* 3200 */ 333, 333, 183, 108, 117, 254, 255, 111, 109, 266, + /* 3210 */ 267, 264, 263, 259, 260, 261, 262, 246, 245, 237, + /* 3220 */ 232, 300, 178, 333, 333, 238, 333, 158, 333, 333, + /* 3230 */ 333, 269, 299, 333, 164, 333, 301, 298, 297, 333, + /* 3240 */ 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, + /* 3250 */ 154, 291, 147, 135, 333, 333, 333, 333, 333, 183, + /* 3260 */ 78, 117, 254, 255, 111, 109, 266, 267, 264, 263, + /* 3270 */ 259, 260, 261, 262, 246, 245, 237, 232, 300, 178, + /* 3280 */ 333, 333, 238, 333, 158, 333, 333, 333, 269, 299, + /* 3290 */ 333, 164, 333, 301, 298, 297, 333, 333, 333, 333, + /* 3300 */ 333, 333, 333, 333, 333, 333, 333, 154, 291, 147, + /* 3310 */ 135, 333, 333, 333, 333, 333, 183, 92, 117, 254, + /* 3320 */ 255, 111, 109, 266, 267, 264, 263, 259, 260, 261, + /* 3330 */ 262, 246, 245, 237, 232, 300, 178, 333, 333, 238, + /* 3340 */ 333, 158, 333, 333, 333, 269, 299, 333, 164, 333, + /* 3350 */ 301, 298, 297, 333, 333, 333, 333, 333, 333, 333, + /* 3360 */ 333, 333, 333, 333, 154, 291, 147, 135, 333, 333, + /* 3370 */ 333, 333, 333, 183, 105, 117, 254, 255, 111, 109, + /* 3380 */ 266, 267, 264, 263, 259, 260, 261, 262, 246, 245, + /* 3390 */ 237, 232, 300, 178, 333, 333, 238, 333, 158, 333, + /* 3400 */ 333, 333, 269, 299, 333, 164, 333, 301, 298, 297, + /* 3410 */ 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, + /* 3420 */ 333, 154, 291, 147, 135, 333, 333, 333, 333, 333, + /* 3430 */ 183, 116, 117, 254, 255, 111, 109, 266, 267, 264, + /* 3440 */ 263, 259, 260, 261, 262, 246, 245, 237, 232, 300, + /* 3450 */ 178, 333, 333, 238, 333, 158, 333, 333, 333, 269, + /* 3460 */ 299, 333, 164, 333, 301, 298, 297, 333, 333, 333, + /* 3470 */ 333, 333, 333, 333, 333, 333, 333, 333, 154, 291, + /* 3480 */ 147, 135, 333, 333, 333, 333, 333, 183, 103, 117, + /* 3490 */ 254, 255, 111, 109, 266, 267, 264, 263, 259, 260, + /* 3500 */ 261, 262, 246, 245, 237, 232, 300, 178, 333, 333, + /* 3510 */ 238, 333, 158, 333, 333, 333, 269, 299, 333, 164, + /* 3520 */ 333, 301, 298, 297, 333, 333, 333, 333, 333, 333, + /* 3530 */ 333, 333, 333, 333, 333, 154, 291, 147, 135, 333, + /* 3540 */ 333, 333, 333, 333, 183, 91, 117, 254, 255, 111, + /* 3550 */ 109, 266, 267, 264, 263, 259, 260, 261, 262, 246, + /* 3560 */ 245, 237, 232, 300, 178, 333, 333, 238, 333, 158, + /* 3570 */ 333, 333, 333, 269, 299, 333, 164, 333, 301, 298, + /* 3580 */ 297, 333, 333, 333, 333, 333, 333, 333, 333, 333, + /* 3590 */ 333, 333, 154, 291, 147, 135, 333, 333, 333, 333, + /* 3600 */ 333, 183, 122, 117, 254, 255, 111, 109, 266, 267, + /* 3610 */ 264, 263, 259, 260, 261, 262, 246, 245, 237, 232, + /* 3620 */ 300, 178, 333, 333, 238, 333, 158, 333, 333, 333, + /* 3630 */ 269, 299, 333, 164, 333, 301, 298, 297, 333, 333, + /* 3640 */ 333, 333, 333, 333, 333, 333, 333, 333, 333, 154, + /* 3650 */ 291, 147, 135, 333, 333, 333, 333, 333, 183, 79, + /* 3660 */ 117, 254, 255, 111, 109, 266, 267, 264, 263, 259, + /* 3670 */ 260, 261, 262, 246, 245, 237, 232, 300, 178, 333, + /* 3680 */ 333, 238, 333, 158, 333, 333, 333, 269, 299, 333, + /* 3690 */ 164, 333, 301, 298, 297, 333, 333, 333, 333, 333, + /* 3700 */ 333, 333, 333, 333, 333, 333, 154, 291, 147, 135, + /* 3710 */ 333, 333, 333, 333, 333, 183, 119, 117, 254, 255, + /* 3720 */ 111, 109, 266, 267, 264, 263, 259, 260, 261, 262, + /* 3730 */ 246, 245, 237, 232, 300, 178, 333, 333, 238, 333, + /* 3740 */ 158, 333, 333, 333, 269, 299, 333, 164, 333, 301, + /* 3750 */ 298, 297, 333, 333, 333, 333, 333, 333, 333, 333, + /* 3760 */ 333, 333, 333, 154, 291, 147, 135, 333, 333, 333, + /* 3770 */ 333, 333, 183, 99, 117, 254, 255, 111, 109, 266, + /* 3780 */ 267, 264, 263, 259, 260, 261, 262, 246, 245, 237, + /* 3790 */ 232, 300, 178, 333, 333, 238, 333, 158, 333, 333, + /* 3800 */ 333, 269, 299, 333, 164, 333, 301, 298, 297, 333, + /* 3810 */ 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, + /* 3820 */ 154, 291, 147, 135, 333, 333, 333, 333, 333, 183, + /* 3830 */ 106, 117, 254, 255, 111, 109, 266, 267, 264, 263, + /* 3840 */ 259, 260, 261, 262, 246, 245, 237, 232, 300, 178, + /* 3850 */ 333, 333, 238, 333, 158, 333, 333, 333, 269, 299, + /* 3860 */ 333, 164, 333, 301, 298, 297, 333, 333, 333, 333, + /* 3870 */ 333, 333, 333, 333, 333, 333, 333, 154, 291, 147, + /* 3880 */ 135, 333, 333, 333, 333, 333, 183, 80, 117, 254, + /* 3890 */ 255, 111, 109, 266, 267, 264, 263, 259, 260, 261, + /* 3900 */ 262, 246, 245, 237, 232, 300, 178, 333, 333, 238, + /* 3910 */ 333, 158, 333, 333, 333, 269, 299, 333, 164, 333, + /* 3920 */ 301, 298, 297, 333, 333, 333, 333, 333, 333, 333, + /* 3930 */ 333, 333, 333, 333, 154, 291, 147, 135, 333, 333, + /* 3940 */ 333, 333, 333, 183, 107, 117, 254, 255, 111, 109, + /* 3950 */ 266, 267, 264, 263, 259, 260, 261, 262, 246, 245, + /* 3960 */ 237, 232, 300, 178, 333, 333, 238, 333, 158, 333, + /* 3970 */ 333, 333, 269, 299, 333, 164, 333, 301, 298, 297, + /* 3980 */ 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, + /* 3990 */ 333, 154, 291, 147, 135, 333, 333, 333, 333, 333, + /* 4000 */ 183, 86, 117, 254, 255, 111, 109, 266, 267, 264, + /* 4010 */ 263, 259, 260, 261, 262, 246, 245, 237, 232, 300, + /* 4020 */ 178, 333, 333, 238, 333, 158, 333, 333, 333, 269, + /* 4030 */ 299, 333, 164, 333, 301, 298, 297, 56, 333, 333, + /* 4040 */ 333, 333, 12, 160, 58, 333, 333, 333, 154, 291, + /* 4050 */ 147, 135, 57, 25, 333, 333, 333, 183, 333, 333, + /* 4060 */ 333, 42, 43, 333, 50, 333, 59, 60, 333, 333, + /* 4070 */ 333, 333, 333, 333, 256, 251, 230, 231, 233, 229, + /* 4080 */ 228, 224, 234, 63, 333, 240, 236, 160, 333, 333, + /* 4090 */ 333, 333, 62, 333, 333, 303, 153, 3, 46, 8, + /* 4100 */ 148, 333, 333, 7, 333, 30, 18, 333, 67, 47, + /* 4110 */ 24, 333, 333, 333, 333, 333, 333, 333, 333, 251, + /* 4120 */ 230, 231, 233, 229, 228, 224, 39, 21, 36, 22, + /* 4130 */ 32, 34, 20, 333, 309, 333, 62, 333, 333, 303, + /* 4140 */ 153, 3, 333, 313, 333, 333, 333, 7, 333, 112, + /* 4150 */ 113, 333, 333, 333, 333, 333, 333, 163, 333, 333, + /* 4160 */ 333, 237, 232, 300, 274, 333, 30, 238, 311, 67, + /* 4170 */ 47, 24, 333, 269, 299, 333, 127, 333, 301, 298, + /* 4180 */ 297, 30, 333, 333, 67, 47, 24, 39, 21, 36, + /* 4190 */ 22, 32, 34, 20, 333, 309, 333, 333, 333, 333, + /* 4200 */ 8, 333, 39, 21, 36, 22, 32, 34, 20, 333, + /* 4210 */ 309, 333, 30, 333, 333, 67, 47, 24, 213, 333, + /* 4220 */ 333, 333, 333, 333, 333, 333, 333, 333, 333, 311, + /* 4230 */ 333, 333, 209, 39, 21, 36, 22, 32, 34, 20, + /* 4240 */ 30, 309, 333, 67, 47, 24, 333, 333, 333, 333, + /* 4250 */ 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, + /* 4260 */ 196, 39, 21, 36, 22, 32, 34, 20, 30, 309, + /* 4270 */ 333, 67, 47, 24, 333, 333, 333, 333, 333, 333, + /* 4280 */ 333, 333, 333, 333, 333, 333, 333, 333, 333, 39, + /* 4290 */ 21, 36, 22, 32, 34, 20, 30, 309, 333, 67, + /* 4300 */ 47, 24, 333, 333, 333, 333, 333, 333, 333, 333, + /* 4310 */ 333, 333, 333, 333, 333, 333, 194, 39, 21, 36, + /* 4320 */ 22, 32, 34, 20, 30, 309, 333, 67, 47, 24, + /* 4330 */ 312, 333, 333, 160, 333, 333, 333, 333, 333, 333, + /* 4340 */ 333, 333, 333, 126, 333, 39, 21, 36, 22, 32, + /* 4350 */ 34, 20, 30, 309, 333, 67, 47, 24, 333, 333, + /* 4360 */ 333, 333, 333, 333, 333, 251, 230, 231, 233, 229, + /* 4370 */ 228, 224, 333, 39, 21, 36, 22, 32, 34, 20, + /* 4380 */ 333, 309, 62, 112, 113, 303, 153, 3, 333, 333, + /* 4390 */ 333, 333, 333, 7, 333, 237, 232, 300, 274, 333, + /* 4400 */ 333, 238, 333, 333, 289, 333, 333, 269, 299, 333, + /* 4410 */ 161, 333, 301, 298, 297, 333, 112, 113, 333, 333, + /* 4420 */ 333, 333, 333, 333, 333, 333, 333, 333, 237, 232, + /* 4430 */ 300, 274, 30, 333, 238, 67, 47, 24, 333, 333, + /* 4440 */ 269, 299, 333, 162, 333, 301, 298, 297, 67, 47, + /* 4450 */ 24, 333, 333, 39, 21, 36, 22, 32, 34, 20, + /* 4460 */ 333, 309, 333, 333, 8, 333, 39, 21, 36, 22, + /* 4470 */ 32, 34, 20, 333, 309, 67, 47, 24, 333, 333, + /* 4480 */ 333, 333, 333, 333, 49, 333, 333, 333, 333, 333, + /* 4490 */ 333, 333, 333, 39, 21, 36, 22, 32, 34, 20, + /* 4500 */ 333, 309, 67, 47, 24, 333, 333, 333, 333, 333, + /* 4510 */ 333, 41, 333, 23, 8, 333, 67, 47, 24, 333, + /* 4520 */ 39, 21, 36, 22, 32, 34, 20, 381, 309, 381, + /* 4530 */ 381, 381, 333, 333, 39, 21, 36, 22, 32, 34, + /* 4540 */ 20, 200, 309, 333, 333, 333, 333, 333, 381, 200, + /* 4550 */ 381, 333, 333, 381, 201, 195, 333, 333, 192, 180, + /* 4560 */ 199, 333, 201, 195, 333, 333, 192, 180, 199, 333, + /* 4570 */ 333, 333, 333, 333, 333, 333, 333, 156, 333, 333, + /* 4580 */ 333, 333, 333, 333, 333, 157, + ); + static public $yy_lookahead = array( + /* 0 */ 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + /* 10 */ 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, + /* 20 */ 94, 95, 96, 97, 10, 11, 100, 28, 102, 30, + /* 30 */ 12, 13, 106, 107, 54, 109, 56, 111, 112, 113, + /* 40 */ 22, 23, 24, 25, 26, 27, 28, 125, 30, 127, + /* 50 */ 128, 125, 126, 127, 128, 26, 27, 28, 20, 30, + /* 60 */ 134, 78, 79, 80, 81, 82, 83, 84, 85, 86, + /* 70 */ 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, + /* 80 */ 97, 49, 38, 100, 52, 102, 40, 41, 42, 106, + /* 90 */ 107, 108, 109, 47, 111, 112, 113, 23, 24, 25, + /* 100 */ 26, 27, 28, 120, 30, 122, 76, 61, 125, 126, + /* 110 */ 127, 128, 125, 69, 127, 128, 20, 134, 78, 79, + /* 120 */ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + /* 130 */ 90, 91, 92, 93, 94, 95, 96, 97, 66, 67, + /* 140 */ 100, 33, 102, 21, 94, 81, 106, 107, 52, 109, + /* 150 */ 40, 111, 112, 113, 16, 115, 116, 19, 94, 95, + /* 160 */ 38, 121, 98, 99, 100, 125, 126, 127, 128, 31, + /* 170 */ 32, 94, 14, 103, 134, 78, 79, 80, 81, 82, + /* 180 */ 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, + /* 190 */ 93, 94, 95, 96, 97, 132, 133, 100, 52, 102, + /* 200 */ 132, 133, 76, 106, 107, 108, 109, 76, 111, 112, + /* 210 */ 113, 53, 24, 25, 26, 27, 28, 120, 30, 122, + /* 220 */ 50, 51, 125, 126, 127, 128, 76, 52, 119, 69, + /* 230 */ 3, 134, 78, 79, 80, 81, 82, 83, 84, 85, + /* 240 */ 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + /* 250 */ 96, 97, 55, 38, 100, 21, 102, 66, 94, 21, + /* 260 */ 106, 107, 108, 109, 100, 111, 112, 113, 104, 105, + /* 270 */ 106, 107, 38, 94, 120, 125, 122, 127, 128, 125, + /* 280 */ 126, 127, 128, 76, 38, 106, 107, 38, 134, 78, + /* 290 */ 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, + /* 300 */ 89, 90, 91, 92, 93, 94, 95, 96, 97, 130, + /* 310 */ 131, 100, 119, 102, 21, 14, 103, 106, 107, 108, + /* 320 */ 109, 15, 111, 112, 113, 25, 26, 27, 28, 94, + /* 330 */ 30, 120, 125, 122, 127, 128, 125, 126, 127, 128, + /* 340 */ 76, 106, 107, 129, 94, 134, 78, 79, 80, 81, + /* 350 */ 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, + /* 360 */ 92, 93, 94, 95, 96, 97, 131, 94, 100, 2, + /* 370 */ 102, 52, 103, 103, 106, 107, 108, 109, 103, 111, + /* 380 */ 112, 113, 33, 20, 35, 36, 37, 20, 120, 125, + /* 390 */ 122, 127, 128, 125, 126, 127, 128, 34, 124, 76, + /* 400 */ 72, 38, 134, 54, 30, 56, 76, 75, 59, 77, + /* 410 */ 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, + /* 420 */ 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + /* 430 */ 76, 114, 100, 76, 102, 133, 20, 102, 106, 107, + /* 440 */ 103, 109, 76, 111, 112, 113, 2, 76, 4, 5, + /* 450 */ 6, 7, 8, 9, 38, 76, 94, 125, 126, 127, + /* 460 */ 128, 17, 18, 76, 20, 76, 134, 119, 52, 76, + /* 470 */ 26, 27, 135, 29, 135, 31, 32, 1, 135, 63, + /* 480 */ 4, 5, 6, 39, 40, 41, 42, 43, 44, 45, + /* 490 */ 46, 47, 48, 135, 50, 51, 121, 135, 20, 21, + /* 500 */ 125, 57, 127, 128, 60, 61, 62, 63, 135, 65, + /* 510 */ 20, 21, 68, 135, 94, 71, 38, 135, 2, 135, + /* 520 */ 4, 5, 6, 7, 8, 9, 106, 107, 38, 135, + /* 530 */ 135, 76, 112, 17, 18, 135, 20, 135, 135, 135, + /* 540 */ 135, 135, 26, 27, 135, 29, 135, 31, 32, 135, + /* 550 */ 130, 131, 20, 21, 38, 39, 40, 41, 42, 43, + /* 560 */ 44, 45, 46, 47, 48, 135, 50, 51, 121, 135, + /* 570 */ 38, 20, 125, 57, 127, 128, 60, 61, 62, 135, + /* 580 */ 125, 65, 127, 128, 68, 135, 135, 71, 75, 38, + /* 590 */ 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + /* 600 */ 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, + /* 610 */ 97, 135, 135, 100, 63, 102, 135, 20, 135, 106, + /* 620 */ 107, 135, 109, 135, 111, 112, 113, 2, 135, 4, + /* 630 */ 5, 6, 7, 8, 9, 38, 40, 135, 125, 126, + /* 640 */ 127, 128, 17, 18, 135, 20, 135, 134, 135, 135, + /* 650 */ 135, 26, 27, 57, 29, 58, 31, 32, 62, 34, + /* 660 */ 135, 40, 135, 135, 39, 40, 41, 42, 43, 44, + /* 670 */ 45, 46, 47, 48, 135, 50, 51, 135, 57, 135, + /* 680 */ 135, 135, 57, 62, 110, 60, 61, 62, 114, 135, + /* 690 */ 65, 70, 118, 68, 135, 75, 71, 77, 78, 79, + /* 700 */ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + /* 710 */ 90, 91, 92, 93, 94, 95, 96, 97, 135, 135, + /* 720 */ 100, 135, 102, 135, 135, 135, 106, 107, 135, 109, + /* 730 */ 135, 111, 112, 113, 2, 135, 4, 5, 6, 7, + /* 740 */ 8, 9, 135, 135, 135, 125, 126, 127, 128, 17, + /* 750 */ 18, 135, 110, 135, 134, 135, 114, 135, 26, 27, + /* 760 */ 118, 29, 135, 31, 32, 135, 135, 135, 3, 135, + /* 770 */ 135, 39, 40, 41, 42, 43, 44, 45, 46, 47, + /* 780 */ 48, 135, 50, 51, 135, 53, 21, 135, 135, 57, + /* 790 */ 135, 135, 60, 61, 62, 135, 64, 65, 135, 135, + /* 800 */ 68, 135, 135, 71, 135, 78, 79, 80, 81, 82, + /* 810 */ 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, + /* 820 */ 93, 94, 95, 96, 97, 135, 135, 100, 135, 102, + /* 830 */ 135, 135, 12, 106, 107, 108, 109, 72, 111, 112, + /* 840 */ 113, 135, 22, 23, 24, 25, 26, 27, 28, 122, + /* 850 */ 30, 135, 125, 126, 127, 128, 135, 135, 135, 135, + /* 860 */ 135, 134, 78, 79, 80, 81, 82, 83, 84, 85, + /* 870 */ 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + /* 880 */ 96, 97, 135, 135, 100, 135, 102, 135, 135, 135, + /* 890 */ 106, 107, 108, 109, 135, 111, 112, 113, 135, 22, + /* 900 */ 23, 24, 25, 26, 27, 28, 122, 30, 135, 125, + /* 910 */ 126, 127, 128, 94, 135, 135, 135, 135, 134, 100, + /* 920 */ 101, 135, 135, 104, 105, 106, 107, 77, 78, 79, + /* 930 */ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + /* 940 */ 90, 91, 92, 93, 94, 95, 96, 97, 135, 135, + /* 950 */ 100, 135, 102, 135, 135, 13, 106, 107, 135, 109, + /* 960 */ 135, 111, 112, 113, 22, 23, 24, 25, 26, 27, + /* 970 */ 28, 135, 30, 135, 135, 125, 126, 127, 128, 135, + /* 980 */ 135, 135, 135, 76, 134, 78, 79, 80, 81, 82, + /* 990 */ 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, + /* 1000 */ 93, 94, 95, 96, 97, 135, 135, 100, 135, 102, + /* 1010 */ 135, 135, 135, 106, 107, 135, 109, 135, 111, 112, + /* 1020 */ 113, 2, 135, 4, 5, 6, 7, 8, 9, 135, + /* 1030 */ 135, 135, 125, 126, 127, 128, 17, 18, 135, 20, + /* 1040 */ 135, 134, 135, 135, 135, 26, 27, 135, 29, 135, + /* 1050 */ 31, 32, 135, 135, 135, 135, 135, 135, 39, 40, + /* 1060 */ 41, 42, 43, 44, 45, 46, 47, 48, 135, 50, + /* 1070 */ 51, 3, 135, 135, 135, 135, 57, 135, 135, 60, + /* 1080 */ 61, 62, 135, 135, 65, 135, 135, 68, 135, 21, + /* 1090 */ 71, 135, 135, 2, 135, 4, 5, 6, 7, 8, + /* 1100 */ 9, 135, 135, 33, 135, 35, 36, 37, 17, 18, + /* 1110 */ 135, 20, 135, 135, 135, 135, 135, 26, 27, 135, + /* 1120 */ 29, 135, 31, 32, 54, 135, 56, 135, 40, 59, + /* 1130 */ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + /* 1140 */ 72, 50, 51, 135, 135, 57, 135, 135, 57, 61, + /* 1150 */ 62, 60, 61, 62, 135, 135, 65, 135, 135, 68, + /* 1160 */ 135, 135, 71, 135, 135, 2, 135, 4, 5, 6, + /* 1170 */ 7, 8, 9, 135, 135, 135, 135, 35, 36, 37, + /* 1180 */ 17, 18, 135, 20, 135, 135, 135, 135, 135, 26, + /* 1190 */ 27, 135, 29, 135, 31, 32, 54, 135, 56, 135, + /* 1200 */ 135, 59, 39, 40, 41, 42, 43, 44, 45, 46, + /* 1210 */ 47, 48, 135, 50, 51, 135, 135, 135, 135, 135, + /* 1220 */ 57, 135, 135, 60, 61, 62, 135, 135, 65, 135, + /* 1230 */ 135, 68, 135, 135, 71, 78, 79, 80, 81, 82, + /* 1240 */ 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, + /* 1250 */ 93, 94, 95, 96, 97, 135, 135, 100, 135, 102, + /* 1260 */ 135, 135, 135, 106, 107, 135, 109, 135, 111, 112, + /* 1270 */ 113, 2, 135, 4, 5, 6, 7, 8, 9, 135, + /* 1280 */ 123, 135, 125, 126, 127, 128, 17, 18, 135, 20, + /* 1290 */ 135, 134, 135, 135, 135, 26, 27, 135, 29, 135, + /* 1300 */ 31, 32, 135, 135, 135, 135, 135, 135, 39, 40, + /* 1310 */ 41, 42, 43, 44, 45, 46, 47, 48, 135, 50, + /* 1320 */ 51, 135, 135, 135, 135, 135, 57, 135, 135, 60, + /* 1330 */ 61, 62, 135, 135, 65, 135, 135, 68, 135, 135, + /* 1340 */ 71, 135, 135, 2, 135, 4, 5, 6, 7, 8, + /* 1350 */ 9, 135, 135, 135, 135, 135, 135, 135, 17, 18, + /* 1360 */ 135, 135, 21, 135, 135, 135, 135, 26, 27, 135, + /* 1370 */ 29, 135, 31, 32, 135, 135, 135, 135, 135, 135, + /* 1380 */ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + /* 1390 */ 135, 50, 51, 135, 135, 135, 135, 135, 57, 135, + /* 1400 */ 135, 60, 61, 62, 135, 135, 65, 135, 135, 68, + /* 1410 */ 135, 135, 71, 135, 135, 2, 135, 4, 5, 6, + /* 1420 */ 7, 8, 9, 135, 135, 135, 135, 135, 135, 135, + /* 1430 */ 17, 18, 135, 20, 135, 135, 135, 135, 135, 26, + /* 1440 */ 27, 135, 29, 135, 31, 32, 135, 135, 135, 135, + /* 1450 */ 135, 135, 39, 40, 41, 42, 43, 44, 45, 46, + /* 1460 */ 47, 48, 135, 50, 51, 135, 135, 135, 135, 135, + /* 1470 */ 57, 135, 135, 60, 61, 62, 135, 135, 65, 135, + /* 1480 */ 135, 68, 135, 135, 71, 135, 135, 2, 135, 4, + /* 1490 */ 5, 6, 7, 8, 9, 135, 135, 135, 135, 135, + /* 1500 */ 135, 135, 17, 18, 135, 20, 135, 135, 135, 135, + /* 1510 */ 135, 26, 27, 135, 29, 135, 31, 32, 135, 135, + /* 1520 */ 135, 135, 135, 135, 39, 40, 41, 42, 43, 44, + /* 1530 */ 45, 46, 47, 48, 135, 50, 51, 135, 135, 135, + /* 1540 */ 135, 135, 57, 135, 135, 60, 61, 62, 135, 135, + /* 1550 */ 65, 135, 135, 68, 135, 135, 71, 78, 79, 80, + /* 1560 */ 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, + /* 1570 */ 91, 92, 93, 94, 95, 96, 97, 135, 135, 100, + /* 1580 */ 135, 102, 135, 135, 135, 106, 107, 135, 109, 135, + /* 1590 */ 111, 112, 113, 2, 135, 4, 5, 6, 7, 8, + /* 1600 */ 9, 135, 135, 135, 125, 126, 127, 128, 17, 18, + /* 1610 */ 135, 135, 135, 134, 135, 135, 135, 26, 27, 135, + /* 1620 */ 29, 135, 31, 32, 135, 135, 135, 135, 135, 135, + /* 1630 */ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + /* 1640 */ 135, 50, 51, 135, 135, 135, 135, 135, 57, 135, + /* 1650 */ 135, 60, 61, 62, 135, 135, 65, 135, 135, 68, + /* 1660 */ 135, 135, 71, 135, 78, 79, 80, 81, 82, 83, + /* 1670 */ 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, + /* 1680 */ 94, 95, 96, 97, 135, 135, 100, 135, 102, 135, + /* 1690 */ 135, 13, 106, 107, 135, 109, 135, 111, 112, 113, + /* 1700 */ 22, 23, 24, 25, 26, 27, 28, 135, 30, 135, + /* 1710 */ 135, 125, 126, 127, 128, 135, 135, 135, 135, 135, + /* 1720 */ 134, 78, 79, 80, 81, 82, 83, 84, 85, 86, + /* 1730 */ 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, + /* 1740 */ 97, 135, 135, 100, 81, 102, 135, 135, 135, 106, + /* 1750 */ 107, 135, 109, 135, 111, 112, 113, 94, 95, 135, + /* 1760 */ 135, 98, 99, 100, 135, 135, 135, 135, 125, 126, + /* 1770 */ 127, 128, 135, 135, 135, 135, 135, 134, 78, 79, + /* 1780 */ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + /* 1790 */ 90, 91, 92, 93, 94, 95, 96, 97, 135, 135, + /* 1800 */ 100, 135, 102, 135, 135, 135, 106, 107, 135, 109, + /* 1810 */ 135, 111, 112, 113, 135, 135, 135, 135, 135, 135, + /* 1820 */ 135, 135, 135, 135, 135, 125, 126, 127, 128, 135, + /* 1830 */ 135, 135, 135, 135, 134, 78, 79, 80, 81, 82, + /* 1840 */ 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, + /* 1850 */ 93, 94, 95, 96, 97, 135, 135, 100, 135, 102, + /* 1860 */ 135, 135, 135, 106, 107, 135, 109, 135, 111, 112, + /* 1870 */ 113, 135, 135, 135, 135, 135, 135, 135, 135, 135, + /* 1880 */ 135, 135, 125, 126, 127, 128, 135, 135, 135, 135, + /* 1890 */ 135, 134, 78, 79, 80, 81, 82, 83, 84, 85, + /* 1900 */ 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + /* 1910 */ 96, 97, 135, 135, 100, 135, 102, 135, 135, 135, + /* 1920 */ 106, 107, 135, 109, 135, 111, 112, 113, 135, 135, + /* 1930 */ 135, 135, 135, 135, 135, 135, 135, 135, 135, 125, + /* 1940 */ 126, 127, 128, 135, 135, 135, 135, 135, 134, 78, + /* 1950 */ 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, + /* 1960 */ 89, 90, 91, 92, 93, 94, 95, 96, 97, 135, + /* 1970 */ 135, 100, 135, 102, 135, 135, 135, 106, 107, 135, + /* 1980 */ 109, 135, 111, 112, 113, 135, 135, 135, 135, 135, + /* 1990 */ 135, 135, 135, 135, 135, 135, 125, 126, 127, 128, + /* 2000 */ 135, 135, 135, 135, 135, 134, 78, 79, 80, 81, + /* 2010 */ 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, + /* 2020 */ 92, 93, 94, 95, 96, 97, 135, 135, 100, 135, + /* 2030 */ 102, 135, 135, 135, 106, 107, 135, 109, 135, 111, + /* 2040 */ 112, 113, 135, 135, 135, 135, 135, 135, 135, 135, + /* 2050 */ 135, 135, 135, 125, 126, 127, 128, 135, 135, 135, + /* 2060 */ 135, 135, 134, 78, 79, 80, 81, 82, 83, 84, + /* 2070 */ 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + /* 2080 */ 95, 96, 97, 135, 135, 100, 135, 102, 135, 135, + /* 2090 */ 135, 106, 107, 135, 109, 135, 111, 112, 113, 135, + /* 2100 */ 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, + /* 2110 */ 125, 126, 127, 128, 135, 135, 135, 135, 135, 134, + /* 2120 */ 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, + /* 2130 */ 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + /* 2140 */ 135, 135, 100, 135, 102, 135, 135, 135, 106, 107, + /* 2150 */ 135, 109, 135, 111, 112, 113, 135, 135, 135, 135, + /* 2160 */ 135, 135, 135, 135, 135, 135, 135, 125, 126, 127, + /* 2170 */ 128, 135, 135, 135, 135, 135, 134, 78, 79, 80, + /* 2180 */ 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, + /* 2190 */ 91, 92, 93, 94, 95, 96, 97, 135, 135, 100, + /* 2200 */ 135, 102, 135, 135, 135, 106, 107, 135, 109, 135, + /* 2210 */ 111, 112, 113, 135, 135, 135, 135, 135, 135, 135, + /* 2220 */ 135, 135, 135, 135, 125, 126, 127, 128, 135, 135, + /* 2230 */ 135, 135, 135, 134, 78, 79, 80, 81, 82, 83, + /* 2240 */ 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, + /* 2250 */ 94, 95, 96, 97, 135, 135, 100, 135, 102, 135, + /* 2260 */ 135, 135, 106, 107, 135, 109, 135, 111, 112, 113, + /* 2270 */ 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, + /* 2280 */ 135, 125, 126, 127, 128, 135, 135, 135, 135, 135, + /* 2290 */ 134, 78, 79, 80, 81, 82, 83, 84, 85, 86, + /* 2300 */ 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, + /* 2310 */ 97, 135, 135, 100, 135, 102, 135, 135, 135, 106, + /* 2320 */ 107, 135, 109, 135, 111, 112, 113, 135, 135, 135, + /* 2330 */ 135, 135, 135, 135, 135, 135, 135, 135, 125, 126, + /* 2340 */ 127, 128, 135, 135, 135, 135, 135, 134, 78, 79, + /* 2350 */ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + /* 2360 */ 90, 91, 92, 93, 94, 95, 96, 97, 135, 135, + /* 2370 */ 100, 135, 102, 135, 135, 135, 106, 107, 135, 109, + /* 2380 */ 135, 111, 112, 113, 135, 135, 135, 135, 135, 135, + /* 2390 */ 135, 135, 135, 135, 135, 125, 126, 127, 128, 135, + /* 2400 */ 135, 135, 135, 135, 134, 78, 79, 80, 81, 82, + /* 2410 */ 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, + /* 2420 */ 93, 94, 95, 96, 97, 135, 135, 100, 135, 102, + /* 2430 */ 135, 135, 135, 106, 107, 135, 109, 135, 111, 112, + /* 2440 */ 113, 135, 135, 135, 135, 135, 135, 135, 135, 135, + /* 2450 */ 135, 135, 125, 126, 127, 128, 135, 135, 135, 135, + /* 2460 */ 135, 134, 78, 79, 80, 81, 82, 83, 84, 85, + /* 2470 */ 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + /* 2480 */ 96, 97, 135, 135, 100, 135, 102, 135, 135, 135, + /* 2490 */ 106, 107, 135, 109, 135, 111, 112, 113, 135, 135, + /* 2500 */ 135, 135, 135, 135, 135, 135, 135, 135, 135, 125, + /* 2510 */ 126, 127, 128, 135, 135, 135, 135, 135, 134, 78, + /* 2520 */ 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, + /* 2530 */ 89, 90, 91, 92, 93, 94, 95, 96, 97, 135, + /* 2540 */ 135, 100, 135, 102, 135, 135, 135, 106, 107, 135, + /* 2550 */ 109, 135, 111, 112, 113, 135, 135, 135, 135, 135, + /* 2560 */ 135, 135, 135, 135, 135, 135, 125, 126, 127, 128, + /* 2570 */ 135, 135, 135, 135, 135, 134, 78, 79, 80, 81, + /* 2580 */ 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, + /* 2590 */ 92, 93, 94, 95, 96, 97, 135, 135, 100, 135, + /* 2600 */ 102, 135, 135, 135, 106, 107, 135, 109, 135, 111, + /* 2610 */ 112, 113, 135, 135, 135, 135, 135, 135, 135, 135, + /* 2620 */ 135, 135, 135, 125, 126, 127, 128, 135, 135, 135, + /* 2630 */ 135, 135, 134, 78, 79, 80, 81, 82, 83, 84, + /* 2640 */ 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + /* 2650 */ 95, 96, 97, 135, 135, 100, 135, 102, 135, 135, + /* 2660 */ 135, 106, 107, 135, 109, 135, 111, 112, 113, 135, + /* 2670 */ 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, + /* 2680 */ 125, 126, 127, 128, 135, 135, 135, 135, 135, 134, + /* 2690 */ 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, + /* 2700 */ 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + /* 2710 */ 135, 135, 100, 135, 102, 135, 135, 135, 106, 107, + /* 2720 */ 135, 109, 135, 111, 112, 113, 135, 135, 135, 135, + /* 2730 */ 135, 135, 135, 135, 135, 135, 135, 125, 126, 127, + /* 2740 */ 128, 135, 135, 135, 135, 135, 134, 78, 79, 80, + /* 2750 */ 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, + /* 2760 */ 91, 92, 93, 94, 95, 96, 97, 135, 135, 100, + /* 2770 */ 135, 102, 135, 135, 135, 106, 107, 135, 109, 135, + /* 2780 */ 111, 112, 113, 135, 135, 135, 135, 135, 135, 135, + /* 2790 */ 135, 135, 135, 135, 125, 126, 127, 128, 135, 135, + /* 2800 */ 135, 135, 135, 134, 78, 79, 80, 81, 82, 83, + /* 2810 */ 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, + /* 2820 */ 94, 95, 96, 97, 135, 135, 100, 135, 102, 135, + /* 2830 */ 135, 135, 106, 107, 135, 109, 135, 111, 112, 113, + /* 2840 */ 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, + /* 2850 */ 135, 125, 126, 127, 128, 135, 135, 135, 135, 135, + /* 2860 */ 134, 78, 79, 80, 81, 82, 83, 84, 85, 86, + /* 2870 */ 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, + /* 2880 */ 97, 135, 135, 100, 135, 102, 135, 135, 135, 106, + /* 2890 */ 107, 135, 109, 135, 111, 112, 113, 135, 135, 135, + /* 2900 */ 135, 135, 135, 135, 135, 135, 135, 135, 125, 126, + /* 2910 */ 127, 128, 135, 135, 135, 135, 135, 134, 78, 79, + /* 2920 */ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + /* 2930 */ 90, 91, 92, 93, 94, 95, 96, 97, 135, 135, + /* 2940 */ 100, 135, 102, 135, 135, 135, 106, 107, 135, 109, + /* 2950 */ 135, 111, 112, 113, 135, 135, 135, 135, 135, 135, + /* 2960 */ 135, 135, 135, 135, 135, 125, 126, 127, 128, 135, + /* 2970 */ 135, 135, 135, 135, 134, 78, 79, 80, 81, 82, + /* 2980 */ 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, + /* 2990 */ 93, 94, 95, 96, 97, 135, 135, 100, 135, 102, + /* 3000 */ 135, 135, 135, 106, 107, 135, 109, 135, 111, 112, + /* 3010 */ 113, 135, 135, 135, 135, 135, 135, 135, 135, 135, + /* 3020 */ 135, 135, 125, 126, 127, 128, 135, 135, 135, 135, + /* 3030 */ 135, 134, 78, 79, 80, 81, 82, 83, 84, 85, + /* 3040 */ 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + /* 3050 */ 96, 97, 135, 135, 100, 135, 102, 135, 135, 135, + /* 3060 */ 106, 107, 135, 109, 135, 111, 112, 113, 135, 135, + /* 3070 */ 135, 135, 135, 135, 135, 135, 135, 135, 135, 125, + /* 3080 */ 126, 127, 128, 135, 135, 135, 135, 135, 134, 78, + /* 3090 */ 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, + /* 3100 */ 89, 90, 91, 92, 93, 94, 95, 96, 97, 135, + /* 3110 */ 135, 100, 135, 102, 135, 135, 135, 106, 107, 135, + /* 3120 */ 109, 135, 111, 112, 113, 135, 135, 135, 135, 135, + /* 3130 */ 135, 135, 135, 135, 135, 135, 125, 126, 127, 128, + /* 3140 */ 135, 135, 135, 135, 135, 134, 78, 79, 80, 81, + /* 3150 */ 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, + /* 3160 */ 92, 93, 94, 95, 96, 97, 135, 135, 100, 135, + /* 3170 */ 102, 135, 135, 135, 106, 107, 135, 109, 135, 111, + /* 3180 */ 112, 113, 135, 135, 135, 135, 135, 135, 135, 135, + /* 3190 */ 135, 135, 135, 125, 126, 127, 128, 135, 135, 135, + /* 3200 */ 135, 135, 134, 78, 79, 80, 81, 82, 83, 84, + /* 3210 */ 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + /* 3220 */ 95, 96, 97, 135, 135, 100, 135, 102, 135, 135, + /* 3230 */ 135, 106, 107, 135, 109, 135, 111, 112, 113, 135, + /* 3240 */ 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, + /* 3250 */ 125, 126, 127, 128, 135, 135, 135, 135, 135, 134, + /* 3260 */ 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, + /* 3270 */ 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + /* 3280 */ 135, 135, 100, 135, 102, 135, 135, 135, 106, 107, + /* 3290 */ 135, 109, 135, 111, 112, 113, 135, 135, 135, 135, + /* 3300 */ 135, 135, 135, 135, 135, 135, 135, 125, 126, 127, + /* 3310 */ 128, 135, 135, 135, 135, 135, 134, 78, 79, 80, + /* 3320 */ 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, + /* 3330 */ 91, 92, 93, 94, 95, 96, 97, 135, 135, 100, + /* 3340 */ 135, 102, 135, 135, 135, 106, 107, 135, 109, 135, + /* 3350 */ 111, 112, 113, 135, 135, 135, 135, 135, 135, 135, + /* 3360 */ 135, 135, 135, 135, 125, 126, 127, 128, 135, 135, + /* 3370 */ 135, 135, 135, 134, 78, 79, 80, 81, 82, 83, + /* 3380 */ 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, + /* 3390 */ 94, 95, 96, 97, 135, 135, 100, 135, 102, 135, + /* 3400 */ 135, 135, 106, 107, 135, 109, 135, 111, 112, 113, + /* 3410 */ 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, + /* 3420 */ 135, 125, 126, 127, 128, 135, 135, 135, 135, 135, + /* 3430 */ 134, 78, 79, 80, 81, 82, 83, 84, 85, 86, + /* 3440 */ 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, + /* 3450 */ 97, 135, 135, 100, 135, 102, 135, 135, 135, 106, + /* 3460 */ 107, 135, 109, 135, 111, 112, 113, 135, 135, 135, + /* 3470 */ 135, 135, 135, 135, 135, 135, 135, 135, 125, 126, + /* 3480 */ 127, 128, 135, 135, 135, 135, 135, 134, 78, 79, + /* 3490 */ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + /* 3500 */ 90, 91, 92, 93, 94, 95, 96, 97, 135, 135, + /* 3510 */ 100, 135, 102, 135, 135, 135, 106, 107, 135, 109, + /* 3520 */ 135, 111, 112, 113, 135, 135, 135, 135, 135, 135, + /* 3530 */ 135, 135, 135, 135, 135, 125, 126, 127, 128, 135, + /* 3540 */ 135, 135, 135, 135, 134, 78, 79, 80, 81, 82, + /* 3550 */ 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, + /* 3560 */ 93, 94, 95, 96, 97, 135, 135, 100, 135, 102, + /* 3570 */ 135, 135, 135, 106, 107, 135, 109, 135, 111, 112, + /* 3580 */ 113, 135, 135, 135, 135, 135, 135, 135, 135, 135, + /* 3590 */ 135, 135, 125, 126, 127, 128, 135, 135, 135, 135, + /* 3600 */ 135, 134, 78, 79, 80, 81, 82, 83, 84, 85, + /* 3610 */ 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + /* 3620 */ 96, 97, 135, 135, 100, 135, 102, 135, 135, 135, + /* 3630 */ 106, 107, 135, 109, 135, 111, 112, 113, 135, 135, + /* 3640 */ 135, 135, 135, 135, 135, 135, 135, 135, 135, 125, + /* 3650 */ 126, 127, 128, 135, 135, 135, 135, 135, 134, 78, + /* 3660 */ 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, + /* 3670 */ 89, 90, 91, 92, 93, 94, 95, 96, 97, 135, + /* 3680 */ 135, 100, 135, 102, 135, 135, 135, 106, 107, 135, + /* 3690 */ 109, 135, 111, 112, 113, 135, 135, 135, 135, 135, + /* 3700 */ 135, 135, 135, 135, 135, 135, 125, 126, 127, 128, + /* 3710 */ 135, 135, 135, 135, 135, 134, 78, 79, 80, 81, + /* 3720 */ 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, + /* 3730 */ 92, 93, 94, 95, 96, 97, 135, 135, 100, 135, + /* 3740 */ 102, 135, 135, 135, 106, 107, 135, 109, 135, 111, + /* 3750 */ 112, 113, 135, 135, 135, 135, 135, 135, 135, 135, + /* 3760 */ 135, 135, 135, 125, 126, 127, 128, 135, 135, 135, + /* 3770 */ 135, 135, 134, 78, 79, 80, 81, 82, 83, 84, + /* 3780 */ 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + /* 3790 */ 95, 96, 97, 135, 135, 100, 135, 102, 135, 135, + /* 3800 */ 135, 106, 107, 135, 109, 135, 111, 112, 113, 135, + /* 3810 */ 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, + /* 3820 */ 125, 126, 127, 128, 135, 135, 135, 135, 135, 134, + /* 3830 */ 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, + /* 3840 */ 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + /* 3850 */ 135, 135, 100, 135, 102, 135, 135, 135, 106, 107, + /* 3860 */ 135, 109, 135, 111, 112, 113, 135, 135, 135, 135, + /* 3870 */ 135, 135, 135, 135, 135, 135, 135, 125, 126, 127, + /* 3880 */ 128, 135, 135, 135, 135, 135, 134, 78, 79, 80, + /* 3890 */ 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, + /* 3900 */ 91, 92, 93, 94, 95, 96, 97, 135, 135, 100, + /* 3910 */ 135, 102, 135, 135, 135, 106, 107, 135, 109, 135, + /* 3920 */ 111, 112, 113, 135, 135, 135, 135, 135, 135, 135, + /* 3930 */ 135, 135, 135, 135, 125, 126, 127, 128, 135, 135, + /* 3940 */ 135, 135, 135, 134, 78, 79, 80, 81, 82, 83, + /* 3950 */ 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, + /* 3960 */ 94, 95, 96, 97, 135, 135, 100, 135, 102, 135, + /* 3970 */ 135, 135, 106, 107, 135, 109, 135, 111, 112, 113, + /* 3980 */ 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, + /* 3990 */ 135, 125, 126, 127, 128, 135, 135, 135, 135, 135, + /* 4000 */ 134, 78, 79, 80, 81, 82, 83, 84, 85, 86, + /* 4010 */ 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, + /* 4020 */ 97, 135, 135, 100, 135, 102, 135, 135, 135, 106, + /* 4030 */ 107, 135, 109, 135, 111, 112, 113, 2, 135, 135, + /* 4040 */ 135, 135, 7, 8, 9, 135, 135, 135, 125, 126, + /* 4050 */ 127, 128, 17, 18, 135, 135, 135, 134, 135, 135, + /* 4060 */ 135, 26, 27, 135, 29, 135, 31, 32, 135, 135, + /* 4070 */ 135, 135, 135, 135, 39, 40, 41, 42, 43, 44, + /* 4080 */ 45, 46, 47, 48, 135, 50, 51, 8, 135, 135, + /* 4090 */ 135, 135, 57, 135, 135, 60, 61, 62, 19, 20, + /* 4100 */ 65, 135, 135, 68, 135, 1, 71, 135, 4, 5, + /* 4110 */ 6, 135, 135, 135, 135, 135, 135, 135, 135, 40, + /* 4120 */ 41, 42, 43, 44, 45, 46, 22, 23, 24, 25, + /* 4130 */ 26, 27, 28, 135, 30, 135, 57, 135, 135, 60, + /* 4140 */ 61, 62, 135, 76, 135, 135, 135, 68, 135, 82, + /* 4150 */ 83, 135, 135, 135, 135, 135, 135, 53, 135, 135, + /* 4160 */ 135, 94, 95, 96, 97, 135, 1, 100, 64, 4, + /* 4170 */ 5, 6, 135, 106, 107, 135, 109, 135, 111, 112, + /* 4180 */ 113, 1, 135, 135, 4, 5, 6, 22, 23, 24, + /* 4190 */ 25, 26, 27, 28, 135, 30, 135, 135, 135, 135, + /* 4200 */ 20, 135, 22, 23, 24, 25, 26, 27, 28, 135, + /* 4210 */ 30, 135, 1, 135, 135, 4, 5, 6, 53, 135, + /* 4220 */ 135, 135, 135, 135, 135, 135, 135, 135, 135, 64, + /* 4230 */ 135, 135, 21, 22, 23, 24, 25, 26, 27, 28, + /* 4240 */ 1, 30, 135, 4, 5, 6, 135, 135, 135, 135, + /* 4250 */ 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, + /* 4260 */ 21, 22, 23, 24, 25, 26, 27, 28, 1, 30, + /* 4270 */ 135, 4, 5, 6, 135, 135, 135, 135, 135, 135, + /* 4280 */ 135, 135, 135, 135, 135, 135, 135, 135, 135, 22, + /* 4290 */ 23, 24, 25, 26, 27, 28, 1, 30, 135, 4, + /* 4300 */ 5, 6, 135, 135, 135, 135, 135, 135, 135, 135, + /* 4310 */ 135, 135, 135, 135, 135, 135, 21, 22, 23, 24, + /* 4320 */ 25, 26, 27, 28, 1, 30, 135, 4, 5, 6, + /* 4330 */ 63, 135, 135, 8, 135, 135, 135, 135, 135, 135, + /* 4340 */ 135, 135, 135, 20, 135, 22, 23, 24, 25, 26, + /* 4350 */ 27, 28, 1, 30, 135, 4, 5, 6, 135, 135, + /* 4360 */ 135, 135, 135, 135, 135, 40, 41, 42, 43, 44, + /* 4370 */ 45, 46, 135, 22, 23, 24, 25, 26, 27, 28, + /* 4380 */ 135, 30, 57, 82, 83, 60, 61, 62, 135, 135, + /* 4390 */ 135, 135, 135, 68, 135, 94, 95, 96, 97, 135, + /* 4400 */ 135, 100, 135, 135, 53, 135, 135, 106, 107, 135, + /* 4410 */ 109, 135, 111, 112, 113, 135, 82, 83, 135, 135, + /* 4420 */ 135, 135, 135, 135, 135, 135, 135, 135, 94, 95, + /* 4430 */ 96, 97, 1, 135, 100, 4, 5, 6, 135, 135, + /* 4440 */ 106, 107, 135, 109, 135, 111, 112, 113, 4, 5, + /* 4450 */ 6, 135, 135, 22, 23, 24, 25, 26, 27, 28, + /* 4460 */ 135, 30, 135, 135, 20, 135, 22, 23, 24, 25, + /* 4470 */ 26, 27, 28, 135, 30, 4, 5, 6, 135, 135, + /* 4480 */ 135, 135, 135, 135, 13, 135, 135, 135, 135, 135, + /* 4490 */ 135, 135, 135, 22, 23, 24, 25, 26, 27, 28, + /* 4500 */ 135, 30, 4, 5, 6, 135, 135, 135, 135, 135, + /* 4510 */ 135, 13, 135, 19, 20, 135, 4, 5, 6, 135, + /* 4520 */ 22, 23, 24, 25, 26, 27, 28, 33, 30, 35, + /* 4530 */ 36, 37, 135, 135, 22, 23, 24, 25, 26, 27, + /* 4540 */ 28, 81, 30, 135, 135, 135, 135, 135, 54, 81, + /* 4550 */ 56, 135, 135, 59, 94, 95, 135, 135, 98, 99, + /* 4560 */ 100, 135, 94, 95, 135, 135, 98, 99, 100, 135, + /* 4570 */ 135, 135, 135, 135, 135, 135, 135, 117, 135, 135, + /* 4580 */ 135, 135, 135, 135, 135, 117, +); + const YY_SHIFT_USE_DFLT = -21; + const YY_SHIFT_MAX = 187; + static public $yy_shift_ofst = array( + /* 0 */ 1269, 625, 444, 444, 732, 1485, 1485, 1413, 1341, 1591, + /* 10 */ 1591, 1591, 1269, 1591, 1591, 516, 1019, 1163, 1091, 1591, + /* 20 */ 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, + /* 30 */ 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, + /* 40 */ 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, + /* 50 */ 1591, 1591, 1591, 1591, 1591, 1591, 1591, 4035, 4079, 4325, + /* 60 */ 4325, 46, 46, 1088, 46, 46, 1088, 621, 596, 4165, + /* 70 */ 4104, 4180, 4180, 4444, 4444, 596, 4239, 4351, 4323, 4295, + /* 80 */ 4267, 4211, 4431, 4431, 4431, 4431, 4431, 4498, 4471, 4431, + /* 90 */ 4431, 4431, 4431, 4512, 4512, 4512, 18, 820, 942, 1678, + /* 100 */ 877, 877, 877, 877, 877, 877, 877, 877, 877, 1070, + /* 110 */ 74, 1070, 1142, 1142, 188, 300, 29, 476, 476, -1, + /* 120 */ -1, -1, -1, 374, 374, 328, 328, 4494, 1068, 765, + /* 130 */ 170, 367, -20, 96, 72, 14, 110, 110, 319, 319, + /* 140 */ 319, 38, 108, 38, 110, 319, 110, 38, 38, 38, + /* 150 */ 38, 38, 110, 110, 38, 108, 146, 146, 38, 38, + /* 160 */ 108, 349, 349, 416, 138, 597, 532, 490, 363, 478, + /* 170 */ 551, 122, 234, 158, 32, 44, 249, 293, 301, 160, + /* 180 */ 306, 246, 238, 227, 175, 197, 215, 191, +); + const YY_REDUCE_USE_DFLT = -79; + const YY_REDUCE_MAX = 160; + static public $yy_reduce_ofst = array( + /* 0 */ -74, 268, 154, -17, 40, 211, 97, 332, 513, 727, + /* 10 */ 620, 784, 907, 850, 1157, 3125, 3296, 3239, 3182, 3068, + /* 20 */ 2840, 2783, 3353, 3011, 2954, 3752, 3695, 3809, 3923, 3866, + /* 30 */ 3467, 3410, 3524, 3581, 3638, 2897, 1928, 1814, 1871, 1985, + /* 40 */ 2042, 1757, 1586, 1479, 1643, 1700, 2726, 2669, 2441, 2498, + /* 50 */ 2612, 2555, 2384, 2156, 2099, 2213, 2327, 2270, 4067, 4334, + /* 60 */ 4301, 4468, 4460, 819, 64, 1663, 164, 420, 179, 447, + /* 70 */ 375, 150, 207, 455, 264, 235, -78, -78, -78, -78, + /* 80 */ -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, + /* 90 */ -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, + /* 100 */ -78, -78, -78, -78, -78, -78, -78, -78, -78, 642, + /* 110 */ -78, 574, 574, 642, -78, -78, -78, -13, -13, -78, + /* 120 */ -78, -78, -78, -78, -78, 63, 68, 354, 302, 302, + /* 130 */ 335, 357, 317, 323, 274, 214, 273, 250, 269, 275, + /* 140 */ 270, 393, 348, 387, 50, 337, 362, 366, 379, 371, + /* 150 */ 389, 330, 77, 50, 126, 109, 213, 70, 30, 131, + /* 160 */ 193, +); + static public $yyExpectedTokens = array( + /* 0 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 20, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 1 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 20, 26, 27, 29, 31, 32, 34, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 2 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 20, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 63, 65, 68, 71, ), + /* 3 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 20, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 63, 65, 68, 71, ), + /* 4 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 53, 57, 60, 61, 62, 64, 65, 68, 71, ), + /* 5 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 20, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 6 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 20, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 7 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 20, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 8 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 21, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 9 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 10 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 11 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 12 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 20, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 13 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 14 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 15 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 20, 26, 27, 29, 31, 32, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 16 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 20, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 17 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 20, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 18 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 20, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 19 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 20 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 21 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 22 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 23 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 24 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 25 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 26 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 27 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 28 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 29 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 30 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 31 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 32 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 33 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 34 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 35 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 36 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 37 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 38 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 39 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 40 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 41 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 42 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 43 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 44 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 45 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 46 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 47 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 48 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 49 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 50 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 51 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 52 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 53 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 54 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 55 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 56 */ array(2, 4, 5, 6, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 57 */ array(2, 7, 8, 9, 17, 18, 26, 27, 29, 31, 32, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 57, 60, 61, 62, 65, 68, 71, ), + /* 58 */ array(8, 19, 20, 40, 41, 42, 43, 44, 45, 46, 57, 60, 61, 62, 68, ), + /* 59 */ array(8, 40, 41, 42, 43, 44, 45, 46, 57, 60, 61, 62, 68, ), + /* 60 */ array(8, 40, 41, 42, 43, 44, 45, 46, 57, 60, 61, 62, 68, ), + /* 61 */ array(40, 41, 42, 47, 61, ), + /* 62 */ array(40, 41, 42, 47, 61, ), + /* 63 */ array(40, 57, 61, 62, ), + /* 64 */ array(40, 41, 42, 47, 61, ), + /* 65 */ array(40, 41, 42, 47, 61, ), + /* 66 */ array(40, 57, 61, 62, ), + /* 67 */ array(40, 57, 62, 70, ), + /* 68 */ array(40, 57, 62, ), + /* 69 */ array(1, 4, 5, 6, 22, 23, 24, 25, 26, 27, 28, 30, 53, 64, ), + /* 70 */ array(1, 4, 5, 6, 22, 23, 24, 25, 26, 27, 28, 30, 53, 64, ), + /* 71 */ array(1, 4, 5, 6, 20, 22, 23, 24, 25, 26, 27, 28, 30, ), + /* 72 */ array(1, 4, 5, 6, 20, 22, 23, 24, 25, 26, 27, 28, 30, ), + /* 73 */ array(4, 5, 6, 20, 22, 23, 24, 25, 26, 27, 28, 30, ), + /* 74 */ array(4, 5, 6, 20, 22, 23, 24, 25, 26, 27, 28, 30, ), + /* 75 */ array(40, 57, 62, ), + /* 76 */ array(1, 4, 5, 6, 21, 22, 23, 24, 25, 26, 27, 28, 30, ), + /* 77 */ array(1, 4, 5, 6, 22, 23, 24, 25, 26, 27, 28, 30, 53, ), + /* 78 */ array(1, 4, 5, 6, 20, 22, 23, 24, 25, 26, 27, 28, 30, ), + /* 79 */ array(1, 4, 5, 6, 21, 22, 23, 24, 25, 26, 27, 28, 30, ), + /* 80 */ array(1, 4, 5, 6, 22, 23, 24, 25, 26, 27, 28, 30, 63, ), + /* 81 */ array(1, 4, 5, 6, 21, 22, 23, 24, 25, 26, 27, 28, 30, ), + /* 82 */ array(1, 4, 5, 6, 22, 23, 24, 25, 26, 27, 28, 30, ), + /* 83 */ array(1, 4, 5, 6, 22, 23, 24, 25, 26, 27, 28, 30, ), + /* 84 */ array(1, 4, 5, 6, 22, 23, 24, 25, 26, 27, 28, 30, ), + /* 85 */ array(1, 4, 5, 6, 22, 23, 24, 25, 26, 27, 28, 30, ), + /* 86 */ array(1, 4, 5, 6, 22, 23, 24, 25, 26, 27, 28, 30, ), + /* 87 */ array(4, 5, 6, 13, 22, 23, 24, 25, 26, 27, 28, 30, ), + /* 88 */ array(4, 5, 6, 13, 22, 23, 24, 25, 26, 27, 28, 30, ), + /* 89 */ array(1, 4, 5, 6, 22, 23, 24, 25, 26, 27, 28, 30, ), + /* 90 */ array(1, 4, 5, 6, 22, 23, 24, 25, 26, 27, 28, 30, ), + /* 91 */ array(1, 4, 5, 6, 22, 23, 24, 25, 26, 27, 28, 30, ), + /* 92 */ array(1, 4, 5, 6, 22, 23, 24, 25, 26, 27, 28, 30, ), + /* 93 */ array(4, 5, 6, 22, 23, 24, 25, 26, 27, 28, 30, ), + /* 94 */ array(4, 5, 6, 22, 23, 24, 25, 26, 27, 28, 30, ), + /* 95 */ array(4, 5, 6, 22, 23, 24, 25, 26, 27, 28, 30, ), + /* 96 */ array(12, 13, 22, 23, 24, 25, 26, 27, 28, 30, ), + /* 97 */ array(12, 22, 23, 24, 25, 26, 27, 28, 30, ), + /* 98 */ array(13, 22, 23, 24, 25, 26, 27, 28, 30, ), + /* 99 */ array(13, 22, 23, 24, 25, 26, 27, 28, 30, ), + /* 100 */ array(22, 23, 24, 25, 26, 27, 28, 30, ), + /* 101 */ array(22, 23, 24, 25, 26, 27, 28, 30, ), + /* 102 */ array(22, 23, 24, 25, 26, 27, 28, 30, ), + /* 103 */ array(22, 23, 24, 25, 26, 27, 28, 30, ), + /* 104 */ array(22, 23, 24, 25, 26, 27, 28, 30, ), + /* 105 */ array(22, 23, 24, 25, 26, 27, 28, 30, ), + /* 106 */ array(22, 23, 24, 25, 26, 27, 28, 30, ), + /* 107 */ array(22, 23, 24, 25, 26, 27, 28, 30, ), + /* 108 */ array(22, 23, 24, 25, 26, 27, 28, 30, ), + /* 109 */ array(33, 35, 36, 37, 54, 56, 59, ), + /* 110 */ array(23, 24, 25, 26, 27, 28, 30, ), + /* 111 */ array(33, 35, 36, 37, 54, 56, 59, ), + /* 112 */ array(35, 36, 37, 54, 56, 59, ), + /* 113 */ array(35, 36, 37, 54, 56, 59, ), + /* 114 */ array(24, 25, 26, 27, 28, 30, ), + /* 115 */ array(25, 26, 27, 28, 30, ), + /* 116 */ array(26, 27, 28, 30, ), + /* 117 */ array(1, 4, 5, 6, ), + /* 118 */ array(1, 4, 5, 6, ), + /* 119 */ array(28, 30, ), + /* 120 */ array(28, 30, ), + /* 121 */ array(28, 30, ), + /* 122 */ array(28, 30, ), + /* 123 */ array(30, ), + /* 124 */ array(30, ), + /* 125 */ array(72, ), + /* 126 */ array(72, ), + /* 127 */ array(19, 20, 33, 35, 36, 37, 54, 56, 59, ), + /* 128 */ array(3, 21, 72, ), + /* 129 */ array(3, 21, 72, ), + /* 130 */ array(50, 51, ), + /* 131 */ array(2, 20, ), + /* 132 */ array(54, 56, ), + /* 133 */ array(20, 52, ), + /* 134 */ array(66, 67, ), + /* 135 */ array(10, 11, ), + /* 136 */ array(40, ), + /* 137 */ array(40, ), + /* 138 */ array(52, ), + /* 139 */ array(52, ), + /* 140 */ array(52, ), + /* 141 */ array(20, ), + /* 142 */ array(33, ), + /* 143 */ array(20, ), + /* 144 */ array(40, ), + /* 145 */ array(52, ), + /* 146 */ array(40, ), + /* 147 */ array(20, ), + /* 148 */ array(20, ), + /* 149 */ array(20, ), + /* 150 */ array(20, ), + /* 151 */ array(20, ), + /* 152 */ array(40, ), + /* 153 */ array(40, ), + /* 154 */ array(20, ), + /* 155 */ array(33, ), + /* 156 */ array(52, ), + /* 157 */ array(52, ), + /* 158 */ array(20, ), + /* 159 */ array(20, ), + /* 160 */ array(33, ), + /* 161 */ array(33, 35, 36, 37, 54, 56, 59, ), + /* 162 */ array(33, 35, 36, 37, 54, 56, 59, ), + /* 163 */ array(20, 38, 52, 63, ), + /* 164 */ array(16, 19, 31, 32, ), + /* 165 */ array(20, 38, 58, ), + /* 166 */ array(20, 21, 38, ), + /* 167 */ array(20, 21, 38, ), + /* 168 */ array(20, 34, 38, ), + /* 169 */ array(20, 21, 38, ), + /* 170 */ array(20, 38, 63, ), + /* 171 */ array(21, 38, ), + /* 172 */ array(21, 38, ), + /* 173 */ array(14, 53, ), + /* 174 */ array(49, 52, ), + /* 175 */ array(38, 69, ), + /* 176 */ array(38, ), + /* 177 */ array(21, ), + /* 178 */ array(14, ), + /* 179 */ array(69, ), + /* 180 */ array(15, ), + /* 181 */ array(38, ), + /* 182 */ array(21, ), + /* 183 */ array(3, ), + /* 184 */ array(52, ), + /* 185 */ array(55, ), + /* 186 */ array(38, ), + /* 187 */ array(66, ), + /* 188 */ array(), + /* 189 */ array(), + /* 190 */ array(), + /* 191 */ array(), + /* 192 */ array(), + /* 193 */ array(), + /* 194 */ array(), + /* 195 */ array(), + /* 196 */ array(), + /* 197 */ array(), + /* 198 */ array(), + /* 199 */ array(), + /* 200 */ array(), + /* 201 */ array(), + /* 202 */ array(), + /* 203 */ array(), + /* 204 */ array(), + /* 205 */ array(), + /* 206 */ array(), + /* 207 */ array(), + /* 208 */ array(), + /* 209 */ array(), + /* 210 */ array(), + /* 211 */ array(), + /* 212 */ array(), + /* 213 */ array(), + /* 214 */ array(), + /* 215 */ array(), + /* 216 */ array(), + /* 217 */ array(), + /* 218 */ array(), + /* 219 */ array(), + /* 220 */ array(), + /* 221 */ array(), + /* 222 */ array(), + /* 223 */ array(), + /* 224 */ array(), + /* 225 */ array(), + /* 226 */ array(), + /* 227 */ array(), + /* 228 */ array(), + /* 229 */ array(), + /* 230 */ array(), + /* 231 */ array(), + /* 232 */ array(), + /* 233 */ array(), + /* 234 */ array(), + /* 235 */ array(), + /* 236 */ array(), + /* 237 */ array(), + /* 238 */ array(), + /* 239 */ array(), + /* 240 */ array(), + /* 241 */ array(), + /* 242 */ array(), + /* 243 */ array(), + /* 244 */ array(), + /* 245 */ array(), + /* 246 */ array(), + /* 247 */ array(), + /* 248 */ array(), + /* 249 */ array(), + /* 250 */ array(), + /* 251 */ array(), + /* 252 */ array(), + /* 253 */ array(), + /* 254 */ array(), + /* 255 */ array(), + /* 256 */ array(), + /* 257 */ array(), + /* 258 */ array(), + /* 259 */ array(), + /* 260 */ array(), + /* 261 */ array(), + /* 262 */ array(), + /* 263 */ array(), + /* 264 */ array(), + /* 265 */ array(), + /* 266 */ array(), + /* 267 */ array(), + /* 268 */ array(), + /* 269 */ array(), + /* 270 */ array(), + /* 271 */ array(), + /* 272 */ array(), + /* 273 */ array(), + /* 274 */ array(), + /* 275 */ array(), + /* 276 */ array(), + /* 277 */ array(), + /* 278 */ array(), + /* 279 */ array(), + /* 280 */ array(), + /* 281 */ array(), + /* 282 */ array(), + /* 283 */ array(), + /* 284 */ array(), + /* 285 */ array(), + /* 286 */ array(), + /* 287 */ array(), + /* 288 */ array(), + /* 289 */ array(), + /* 290 */ array(), + /* 291 */ array(), + /* 292 */ array(), + /* 293 */ array(), + /* 294 */ array(), + /* 295 */ array(), + /* 296 */ array(), + /* 297 */ array(), + /* 298 */ array(), + /* 299 */ array(), + /* 300 */ array(), + /* 301 */ array(), + /* 302 */ array(), + /* 303 */ array(), + /* 304 */ array(), + /* 305 */ array(), + /* 306 */ array(), + /* 307 */ array(), + /* 308 */ array(), + /* 309 */ array(), + /* 310 */ array(), + /* 311 */ array(), + /* 312 */ array(), + /* 313 */ array(), +); + static public $yy_default = array( + /* 0 */ 314, 510, 510, 510, 510, 510, 510, 510, 510, 365, + /* 10 */ 510, 510, 510, 319, 510, 510, 510, 510, 510, 510, + /* 20 */ 510, 510, 510, 510, 510, 510, 510, 510, 510, 510, + /* 30 */ 510, 510, 510, 510, 510, 510, 510, 510, 510, 510, + /* 40 */ 510, 510, 510, 510, 431, 510, 510, 510, 510, 510, + /* 50 */ 510, 429, 510, 510, 510, 510, 510, 358, 404, 510, + /* 60 */ 510, 399, 399, 366, 510, 365, 510, 510, 510, 396, + /* 70 */ 437, 510, 510, 406, 410, 510, 510, 437, 510, 510, + /* 80 */ 510, 510, 430, 440, 371, 428, 490, 451, 449, 439, + /* 90 */ 320, 491, 351, 450, 458, 452, 471, 473, 475, 472, + /* 100 */ 476, 509, 348, 477, 357, 507, 446, 474, 347, 326, + /* 110 */ 505, 325, 416, 416, 504, 506, 503, 510, 321, 501, + /* 120 */ 494, 493, 500, 502, 492, 510, 510, 408, 510, 510, + /* 130 */ 510, 510, 510, 510, 441, 510, 510, 510, 364, 364, + /* 140 */ 364, 510, 510, 510, 510, 364, 392, 510, 510, 510, + /* 150 */ 510, 510, 510, 421, 510, 510, 364, 364, 510, 510, + /* 160 */ 414, 495, 496, 426, 381, 510, 510, 510, 510, 510, + /* 170 */ 510, 510, 510, 369, 510, 510, 484, 510, 384, 510, + /* 180 */ 350, 315, 510, 488, 469, 510, 510, 442, 337, 360, + /* 190 */ 316, 401, 400, 398, 352, 355, 508, 402, 468, 356, + /* 200 */ 353, 354, 370, 419, 374, 394, 467, 466, 375, 349, + /* 210 */ 418, 465, 407, 426, 368, 403, 367, 462, 464, 373, + /* 220 */ 470, 372, 397, 478, 346, 455, 460, 463, 345, 344, + /* 230 */ 340, 341, 342, 343, 359, 317, 363, 377, 380, 497, + /* 240 */ 362, 361, 338, 454, 459, 336, 335, 389, 390, 391, + /* 250 */ 393, 339, 378, 318, 322, 323, 324, 379, 395, 331, + /* 260 */ 332, 333, 334, 330, 329, 413, 327, 328, 498, 382, + /* 270 */ 489, 485, 482, 480, 384, 487, 461, 479, 483, 486, + /* 280 */ 481, 457, 434, 432, 436, 435, 424, 438, 423, 376, + /* 290 */ 433, 456, 453, 412, 417, 409, 411, 388, 387, 383, + /* 300 */ 385, 386, 415, 420, 443, 445, 447, 448, 444, 499, + /* 310 */ 422, 425, 427, 405, +); +/* The next thing included is series of defines which control +** various aspects of the generated parser. +** self::YYNOCODE is a number which corresponds +** to no legal terminal or nonterminal number. This +** number is used to fill in empty slots of the hash +** table. +** self::YYFALLBACK If defined, this indicates that one or more tokens +** have fall-back values which should be used if the +** original value of the token will not parse. +** self::YYSTACKDEPTH is the maximum depth of the parser's stack. +** self::YYNSTATE the combined number of states. +** self::YYNRULE the number of rules in the grammar +** self::YYERRORSYMBOL is the code number of the error symbol. If not +** defined, then do no error processing. +*/ + const YYNOCODE = 136; + const YYSTACKDEPTH = 100; + const YYNSTATE = 314; + const YYNRULE = 196; + const YYERRORSYMBOL = 73; + const YYERRSYMDT = 'yy0'; + const YYFALLBACK = 0; + /** The next table maps tokens into fallback tokens. If a construct + * like the following: + * + * %fallback ID X Y Z. + * + * appears in the grammer, then ID becomes a fallback token for X, Y, + * and Z. Whenever one of the tokens X, Y, or Z is input to the parser + * but it does not parse, the type of the token is changed to ID and + * the parse is retried before an error is thrown. + */ + static public $yyFallback = array( + ); + /** + * Turn parser tracing on by giving a stream to which to write the trace + * and a prompt to preface each trace message. Tracing is turned off + * by making either argument NULL + * + * Inputs: + * + * - A stream resource to which trace output should be written. + * If NULL, then tracing is turned off. + * - A prefix string written at the beginning of every + * line of trace output. If NULL, then tracing is + * turned off. + * + * Outputs: + * + * - None. + * @param resource + * @param string + */ + static function Trace($TraceFILE, $zTracePrompt) + { + if (!$TraceFILE) { + $zTracePrompt = 0; + } elseif (!$zTracePrompt) { + $TraceFILE = 0; + } + self::$yyTraceFILE = $TraceFILE; + self::$yyTracePrompt = $zTracePrompt; + } + + /** + * Output debug information to output (php://output stream) + */ + static function PrintTrace() + { + self::$yyTraceFILE = fopen('php://output', 'w'); + self::$yyTracePrompt = ''; + } + + /** + * @var resource|0 + */ + static public $yyTraceFILE; + /** + * String to prepend to debug output + * @var string|0 + */ + static public $yyTracePrompt; + /** + * @var int + */ + public $yyidx = -1; /* Index of top element in stack */ + /** + * @var int + */ + public $yyerrcnt; /* Shifts left before out of the error */ + /** + * @var array + */ + public $yystack = array(); /* The parser's stack */ + + /** + * For tracing shifts, the names of all terminals and nonterminals + * are required. The following table supplies these names + * @var array + */ + static public $yyTokenName = array( + '$', 'YY_POST_IF', 'YY_IF', 'YY_ELSE', + 'YY_FOR', 'YY_WHILE', 'YY_UNTIL', 'YY_LOOP', + 'YY_SUPER', 'YY_CLASS', 'YY_FORIN', 'YY_FOROF', + 'YY_BY', 'YY_WHEN', 'YY_EQUALS', 'YY_COLON', + 'YY_COMPOUND_ASSIGN', 'YY_RETURN', 'YY_THROW', 'YY_EXTENDS', + 'YY_INDENT', 'YY_OUTDENT', 'YY_LOGIC', 'YY_COMPARE', + 'YY_RELATION', 'YY_SHIFT', 'YY_PLUS', 'YY_MINUS', + 'YY_MATH', 'YY_UNARY', 'YY_EXISTENTIAL', 'YY_INCREMENT', + 'YY_DECREMENT', 'YY_CALL_START', 'YY_CALL_END', 'YY_ACCESSOR', + 'YY_EXISTENTIAL_ACCESSOR', 'YY_PROTOTYPE', 'YY_TERMINATOR', 'YY_STATEMENT', + 'YY_IDENTIFIER', 'YY_NUMBER', 'YY_STRING', 'YY_JS', + 'YY_REGEX', 'YY_DEBUGGER', 'YY_BOOL', 'YY_HERECOMMENT', + 'YY_PARAM_START', 'YY_PARAM_END', 'YY_FUNC', 'YY_BOUND_FUNC', + 'YY_COMMA', 'YY_RANGE_EXCLUSIVE', 'YY_INDEX_START', 'YY_INDEX_END', + 'YY_INDEX_SOAK', 'YY_OBJECT_START', 'YY_OBJECT_END', 'YY_FUNC_EXIST', + 'YY_THIS', 'YY_AT_SIGN', 'YY_ARRAY_START', 'YY_ARRAY_END', + 'YY_RANGE_INCLUSIVE', 'YY_TRY', 'YY_FINALLY', 'YY_CATCH', + 'YY_PAREN_START', 'YY_PAREN_END', 'YY_OWN', 'YY_SWITCH', + 'YY_LEADING_WHEN', 'error', 'root', 'body', + 'block', 'line', 'expression', 'statement', + 'return', 'comment', 'value', 'invocation', + 'code', 'operation', 'assign', 'if', + 'try', 'while', 'for', 'switch', + 'class', 'throw', 'identifier', 'alphanumeric', + 'literal', 'assignable', 'assignObj', 'objAssignable', + 'thisProperty', 'paramList', 'funcGlyph', 'optComma', + 'param', 'paramVar', 'array', 'object', + 'splat', 'simpleAssignable', 'accessor', 'parenthetical', + 'range', 'this', 'index', 'indexValue', + 'slice', 'assignList', 'optFuncExist', 'arguments', + 'argList', 'rangeDots', 'arg', 'simpleArgs', + 'catch', 'whileSource', 'loop', 'forBody', + 'forStart', 'forSource', 'forVariables', 'forValue', + 'whens', 'when', 'ifBlock', + ); + + /** + * For tracing reduce actions, the names of all rules are required. + * @var array + */ + static public $yyRuleName = array( + /* 0 */ "root ::=", + /* 1 */ "root ::= body", + /* 2 */ "root ::= block YY_TERMINATOR", + /* 3 */ "body ::= line", + /* 4 */ "body ::= body YY_TERMINATOR line", + /* 5 */ "body ::= body YY_TERMINATOR", + /* 6 */ "line ::= expression", + /* 7 */ "line ::= statement", + /* 8 */ "statement ::= return", + /* 9 */ "statement ::= comment", + /* 10 */ "statement ::= YY_STATEMENT", + /* 11 */ "expression ::= value", + /* 12 */ "expression ::= invocation", + /* 13 */ "expression ::= code", + /* 14 */ "expression ::= operation", + /* 15 */ "expression ::= assign", + /* 16 */ "expression ::= if", + /* 17 */ "expression ::= try", + /* 18 */ "expression ::= while", + /* 19 */ "expression ::= for", + /* 20 */ "expression ::= switch", + /* 21 */ "expression ::= class", + /* 22 */ "expression ::= throw", + /* 23 */ "block ::= YY_INDENT YY_OUTDENT", + /* 24 */ "block ::= YY_INDENT body YY_OUTDENT", + /* 25 */ "identifier ::= YY_IDENTIFIER", + /* 26 */ "alphanumeric ::= YY_NUMBER", + /* 27 */ "alphanumeric ::= YY_STRING", + /* 28 */ "literal ::= alphanumeric", + /* 29 */ "literal ::= YY_JS", + /* 30 */ "literal ::= YY_REGEX", + /* 31 */ "literal ::= YY_DEBUGGER", + /* 32 */ "literal ::= YY_BOOL", + /* 33 */ "assign ::= assignable YY_EQUALS expression", + /* 34 */ "assign ::= assignable YY_EQUALS YY_TERMINATOR expression", + /* 35 */ "assign ::= assignable YY_EQUALS YY_INDENT expression YY_OUTDENT", + /* 36 */ "assignObj ::= objAssignable", + /* 37 */ "assignObj ::= objAssignable YY_COLON expression", + /* 38 */ "assignObj ::= objAssignable YY_COLON YY_INDENT expression YY_OUTDENT", + /* 39 */ "assignObj ::= comment", + /* 40 */ "objAssignable ::= identifier", + /* 41 */ "objAssignable ::= alphanumeric", + /* 42 */ "objAssignable ::= thisProperty", + /* 43 */ "return ::= YY_RETURN expression", + /* 44 */ "return ::= YY_RETURN", + /* 45 */ "comment ::= YY_HERECOMMENT", + /* 46 */ "code ::= YY_PARAM_START paramList YY_PARAM_END funcGlyph block", + /* 47 */ "code ::= funcGlyph block", + /* 48 */ "funcGlyph ::= YY_FUNC", + /* 49 */ "funcGlyph ::= YY_BOUND_FUNC", + /* 50 */ "optComma ::=", + /* 51 */ "optComma ::= YY_COMMA", + /* 52 */ "paramList ::=", + /* 53 */ "paramList ::= param", + /* 54 */ "paramList ::= paramList YY_COMMA param", + /* 55 */ "param ::= paramVar", + /* 56 */ "param ::= paramVar YY_RANGE_EXCLUSIVE", + /* 57 */ "param ::= paramVar YY_EQUALS expression", + /* 58 */ "paramVar ::= identifier", + /* 59 */ "paramVar ::= thisProperty", + /* 60 */ "paramVar ::= array", + /* 61 */ "paramVar ::= object", + /* 62 */ "splat ::= expression YY_RANGE_EXCLUSIVE", + /* 63 */ "simpleAssignable ::= identifier", + /* 64 */ "simpleAssignable ::= value accessor", + /* 65 */ "simpleAssignable ::= invocation accessor", + /* 66 */ "simpleAssignable ::= thisProperty", + /* 67 */ "assignable ::= simpleAssignable", + /* 68 */ "assignable ::= array", + /* 69 */ "assignable ::= object", + /* 70 */ "value ::= assignable", + /* 71 */ "value ::= literal", + /* 72 */ "value ::= parenthetical", + /* 73 */ "value ::= range", + /* 74 */ "value ::= this", + /* 75 */ "accessor ::= YY_ACCESSOR identifier", + /* 76 */ "accessor ::= YY_EXISTENTIAL_ACCESSOR identifier", + /* 77 */ "accessor ::= YY_PROTOTYPE identifier", + /* 78 */ "accessor ::= YY_PROTOTYPE", + /* 79 */ "accessor ::= index", + /* 80 */ "index ::= YY_INDEX_START indexValue YY_INDEX_END", + /* 81 */ "index ::= YY_INDEX_SOAK index", + /* 82 */ "indexValue ::= expression", + /* 83 */ "indexValue ::= slice", + /* 84 */ "object ::= YY_OBJECT_START assignList optComma YY_OBJECT_END", + /* 85 */ "assignList ::=", + /* 86 */ "assignList ::= assignObj", + /* 87 */ "assignList ::= assignList YY_COMMA assignObj", + /* 88 */ "assignList ::= assignList optComma YY_TERMINATOR assignObj", + /* 89 */ "assignList ::= assignList optComma YY_INDENT assignList optComma YY_OUTDENT", + /* 90 */ "class ::= YY_CLASS", + /* 91 */ "class ::= YY_CLASS block", + /* 92 */ "class ::= YY_CLASS YY_EXTENDS expression", + /* 93 */ "class ::= YY_CLASS YY_EXTENDS expression block", + /* 94 */ "class ::= YY_CLASS simpleAssignable", + /* 95 */ "class ::= YY_CLASS simpleAssignable block", + /* 96 */ "class ::= YY_CLASS simpleAssignable YY_EXTENDS expression", + /* 97 */ "class ::= YY_CLASS simpleAssignable YY_EXTENDS expression block", + /* 98 */ "invocation ::= value optFuncExist arguments", + /* 99 */ "invocation ::= invocation optFuncExist arguments", + /* 100 */ "invocation ::= YY_SUPER", + /* 101 */ "invocation ::= YY_SUPER arguments", + /* 102 */ "optFuncExist ::=", + /* 103 */ "optFuncExist ::= YY_FUNC_EXIST", + /* 104 */ "arguments ::= YY_CALL_START YY_CALL_END", + /* 105 */ "arguments ::= YY_CALL_START argList optComma YY_CALL_END", + /* 106 */ "this ::= YY_THIS", + /* 107 */ "this ::= YY_AT_SIGN", + /* 108 */ "thisProperty ::= YY_AT_SIGN identifier", + /* 109 */ "array ::= YY_ARRAY_START YY_ARRAY_END", + /* 110 */ "array ::= YY_ARRAY_START argList optComma YY_ARRAY_END", + /* 111 */ "rangeDots ::= YY_RANGE_INCLUSIVE", + /* 112 */ "rangeDots ::= YY_RANGE_EXCLUSIVE", + /* 113 */ "range ::= YY_ARRAY_START expression rangeDots expression YY_ARRAY_END", + /* 114 */ "slice ::= expression rangeDots expression", + /* 115 */ "slice ::= expression rangeDots", + /* 116 */ "slice ::= rangeDots expression", + /* 117 */ "slice ::= rangeDots", + /* 118 */ "argList ::= arg", + /* 119 */ "argList ::= argList YY_COMMA arg", + /* 120 */ "argList ::= argList optComma YY_TERMINATOR arg", + /* 121 */ "argList ::= YY_INDENT argList optComma YY_OUTDENT", + /* 122 */ "argList ::= argList optComma YY_INDENT argList optComma YY_OUTDENT", + /* 123 */ "arg ::= expression", + /* 124 */ "arg ::= splat", + /* 125 */ "simpleArgs ::= expression", + /* 126 */ "simpleArgs ::= simpleArgs YY_COMMA expression", + /* 127 */ "try ::= YY_TRY block", + /* 128 */ "try ::= YY_TRY block catch", + /* 129 */ "try ::= YY_TRY block YY_FINALLY block", + /* 130 */ "try ::= YY_TRY block catch YY_FINALLY block", + /* 131 */ "catch ::= YY_CATCH identifier block", + /* 132 */ "throw ::= YY_THROW expression", + /* 133 */ "parenthetical ::= YY_PAREN_START body YY_PAREN_END", + /* 134 */ "parenthetical ::= YY_PAREN_START YY_INDENT body YY_OUTDENT YY_PAREN_END", + /* 135 */ "whileSource ::= YY_WHILE expression", + /* 136 */ "whileSource ::= YY_WHILE expression YY_WHEN expression", + /* 137 */ "whileSource ::= YY_UNTIL expression", + /* 138 */ "whileSource ::= YY_UNTIL expression YY_WHEN expression", + /* 139 */ "while ::= whileSource block", + /* 140 */ "while ::= statement whileSource", + /* 141 */ "while ::= expression whileSource", + /* 142 */ "while ::= loop", + /* 143 */ "loop ::= YY_LOOP block", + /* 144 */ "loop ::= YY_LOOP expression", + /* 145 */ "for ::= statement forBody", + /* 146 */ "for ::= expression forBody", + /* 147 */ "for ::= forBody block", + /* 148 */ "forBody ::= YY_FOR range", + /* 149 */ "forBody ::= forStart forSource", + /* 150 */ "forStart ::= YY_FOR forVariables", + /* 151 */ "forStart ::= YY_FOR YY_OWN forVariables", + /* 152 */ "forValue ::= identifier", + /* 153 */ "forValue ::= array", + /* 154 */ "forValue ::= object", + /* 155 */ "forVariables ::= forValue", + /* 156 */ "forVariables ::= forValue YY_COMMA forValue", + /* 157 */ "forSource ::= YY_FORIN expression", + /* 158 */ "forSource ::= YY_FOROF expression", + /* 159 */ "forSource ::= YY_FORIN expression YY_WHEN expression", + /* 160 */ "forSource ::= YY_FOROF expression YY_WHEN expression", + /* 161 */ "forSource ::= YY_FORIN expression YY_BY expression", + /* 162 */ "forSource ::= YY_FORIN expression YY_WHEN expression YY_BY expression", + /* 163 */ "forSource ::= YY_FORIN expression YY_BY expression YY_WHEN expression", + /* 164 */ "switch ::= YY_SWITCH expression YY_INDENT whens YY_OUTDENT", + /* 165 */ "switch ::= YY_SWITCH expression YY_INDENT whens YY_ELSE block YY_OUTDENT", + /* 166 */ "switch ::= YY_SWITCH YY_INDENT whens YY_OUTDENT", + /* 167 */ "switch ::= YY_SWITCH YY_INDENT whens YY_ELSE block YY_OUTDENT", + /* 168 */ "whens ::= when", + /* 169 */ "whens ::= whens when", + /* 170 */ "when ::= YY_LEADING_WHEN simpleArgs block", + /* 171 */ "when ::= YY_LEADING_WHEN simpleArgs block YY_TERMINATOR", + /* 172 */ "ifBlock ::= YY_IF expression block", + /* 173 */ "ifBlock ::= ifBlock YY_ELSE YY_IF expression block", + /* 174 */ "if ::= ifBlock", + /* 175 */ "if ::= ifBlock YY_ELSE block", + /* 176 */ "if ::= statement YY_POST_IF expression", + /* 177 */ "if ::= expression YY_POST_IF expression", + /* 178 */ "operation ::= YY_UNARY expression", + /* 179 */ "operation ::= YY_MINUS expression", + /* 180 */ "operation ::= YY_PLUS expression", + /* 181 */ "operation ::= YY_DECREMENT simpleAssignable", + /* 182 */ "operation ::= YY_INCREMENT simpleAssignable", + /* 183 */ "operation ::= simpleAssignable YY_DECREMENT", + /* 184 */ "operation ::= simpleAssignable YY_INCREMENT", + /* 185 */ "operation ::= expression YY_EXISTENTIAL", + /* 186 */ "operation ::= expression YY_PLUS expression", + /* 187 */ "operation ::= expression YY_MINUS expression", + /* 188 */ "operation ::= expression YY_MATH expression", + /* 189 */ "operation ::= expression YY_SHIFT expression", + /* 190 */ "operation ::= expression YY_COMPARE expression", + /* 191 */ "operation ::= expression YY_LOGIC expression", + /* 192 */ "operation ::= expression YY_RELATION expression", + /* 193 */ "operation ::= simpleAssignable YY_COMPOUND_ASSIGN expression", + /* 194 */ "operation ::= simpleAssignable YY_COMPOUND_ASSIGN YY_INDENT expression YY_OUTDENT", + /* 195 */ "operation ::= simpleAssignable YY_EXTENDS expression", + ); + + /** + * This function returns the symbolic name associated with a token + * value. + * @param int + * @return string + */ + static function tokenName($tokenType) + { + if ($tokenType === 0) { + return 'End of Input'; + } + if ($tokenType > 0 && $tokenType < count(self::$yyTokenName)) { + return self::$yyTokenName[$tokenType]; + } else { + return "Unknown"; + } + } + + /** + * The following function deletes the value associated with a + * symbol. The symbol can be either a terminal or nonterminal. + * @param int the symbol code + * @param mixed the symbol's value + */ + static function yy_destructor($yymajor, $yypminor) + { + switch ($yymajor) { + /* Here is inserted the actions which take place when a + ** terminal or non-terminal is destroyed. This can happen + ** when the symbol is popped from the stack during a + ** reduce or during error processing or when a parser is + ** being destroyed before it is finished parsing. + ** + ** Note: during a reduce, the only symbols destroyed are those + ** which appear on the RHS of the rule, but which are not used + ** inside the C code. + */ + default: break; /* If no destructor action specified: do nothing */ + } + } + + /** + * Pop the parser's stack once. + * + * If there is a destructor routine associated with the token which + * is popped from the stack, then call it. + * + * Return the major token number for the symbol popped. + * @param yyParser + * @return int + */ + function yy_pop_parser_stack() + { + if (!count($this->yystack)) { + return; + } + $yytos = array_pop($this->yystack); + if (self::$yyTraceFILE && $this->yyidx >= 0) { + fwrite(self::$yyTraceFILE, + self::$yyTracePrompt . 'Popping ' . self::tokenName($yytos->major) . + "\n"); + } + $yymajor = $yytos->major; + self::yy_destructor($yymajor, $yytos->minor); + $this->yyidx--; + return $yymajor; + } + + /** + * Deallocate and destroy a parser. Destructors are all called for + * all stack elements before shutting the parser down. + */ + function __destruct() + { + while ($this->yyidx >= 0) { + $this->yy_pop_parser_stack(); + } + if (is_resource(self::$yyTraceFILE)) { + fclose(self::$yyTraceFILE); + } + } + + /** + * Based on the current state and parser stack, get a list of all + * possible lookahead tokens + * @param int + * @return array + */ + function yy_get_expected_tokens($token) + { + $state = $this->yystack[$this->yyidx]->stateno; + $expected = self::$yyExpectedTokens[$state]; + if (in_array($token, self::$yyExpectedTokens[$state], true)) { + return $expected; + } + $stack = $this->yystack; + $yyidx = $this->yyidx; + do { + $yyact = $this->yy_find_shift_action($token); + if ($yyact >= self::YYNSTATE && $yyact < self::YYNSTATE + self::YYNRULE) { + // reduce action + $done = 0; + do { + if ($done++ == 100) { + $this->yyidx = $yyidx; + $this->yystack = $stack; + // too much recursion prevents proper detection + // so give up + return array_unique($expected); + } + $yyruleno = $yyact - self::YYNSTATE; + $this->yyidx -= self::$yyRuleInfo[$yyruleno]['rhs']; + $nextstate = $this->yy_find_reduce_action( + $this->yystack[$this->yyidx]->stateno, + self::$yyRuleInfo[$yyruleno]['lhs']); + if (isset(self::$yyExpectedTokens[$nextstate])) { + $expected += self::$yyExpectedTokens[$nextstate]; + if (in_array($token, + self::$yyExpectedTokens[$nextstate], true)) { + $this->yyidx = $yyidx; + $this->yystack = $stack; + return array_unique($expected); + } + } + if ($nextstate < self::YYNSTATE) { + // we need to shift a non-terminal + $this->yyidx++; + $x = new yyStackEntry; + $x->stateno = $nextstate; + $x->major = self::$yyRuleInfo[$yyruleno]['lhs']; + $this->yystack[$this->yyidx] = $x; + continue 2; + } elseif ($nextstate == self::YYNSTATE + self::YYNRULE + 1) { + $this->yyidx = $yyidx; + $this->yystack = $stack; + // the last token was just ignored, we can't accept + // by ignoring input, this is in essence ignoring a + // syntax error! + return array_unique($expected); + } elseif ($nextstate === self::YY_NO_ACTION) { + $this->yyidx = $yyidx; + $this->yystack = $stack; + // input accepted, but not shifted (I guess) + return $expected; + } else { + $yyact = $nextstate; + } + } while (true); + } + break; + } while (true); + return array_unique($expected); + } + + /** + * Based on the parser state and current parser stack, determine whether + * the lookahead token is possible. + * + * The parser will convert the token value to an error token if not. This + * catches some unusual edge cases where the parser would fail. + * @param int + * @return bool + */ + function yy_is_expected_token($token) + { + if ($token === 0) { + return true; // 0 is not part of this + } + $state = $this->yystack[$this->yyidx]->stateno; + if (in_array($token, self::$yyExpectedTokens[$state], true)) { + return true; + } + $stack = $this->yystack; + $yyidx = $this->yyidx; + do { + $yyact = $this->yy_find_shift_action($token); + if ($yyact >= self::YYNSTATE && $yyact < self::YYNSTATE + self::YYNRULE) { + // reduce action + $done = 0; + do { + if ($done++ == 100) { + $this->yyidx = $yyidx; + $this->yystack = $stack; + // too much recursion prevents proper detection + // so give up + return true; + } + $yyruleno = $yyact - self::YYNSTATE; + $this->yyidx -= self::$yyRuleInfo[$yyruleno]['rhs']; + $nextstate = $this->yy_find_reduce_action( + $this->yystack[$this->yyidx]->stateno, + self::$yyRuleInfo[$yyruleno]['lhs']); + if (isset(self::$yyExpectedTokens[$nextstate]) && + in_array($token, self::$yyExpectedTokens[$nextstate], true)) { + $this->yyidx = $yyidx; + $this->yystack = $stack; + return true; + } + if ($nextstate < self::YYNSTATE) { + // we need to shift a non-terminal + $this->yyidx++; + $x = new yyStackEntry; + $x->stateno = $nextstate; + $x->major = self::$yyRuleInfo[$yyruleno]['lhs']; + $this->yystack[$this->yyidx] = $x; + continue 2; + } elseif ($nextstate == self::YYNSTATE + self::YYNRULE + 1) { + $this->yyidx = $yyidx; + $this->yystack = $stack; + if (!$token) { + // end of input: this is valid + return true; + } + // the last token was just ignored, we can't accept + // by ignoring input, this is in essence ignoring a + // syntax error! + return false; + } elseif ($nextstate === self::YY_NO_ACTION) { + $this->yyidx = $yyidx; + $this->yystack = $stack; + // input accepted, but not shifted (I guess) + return true; + } else { + $yyact = $nextstate; + } + } while (true); + } + break; + } while (true); + $this->yyidx = $yyidx; + $this->yystack = $stack; + return true; + } + + /** + * Find the appropriate action for a parser given the terminal + * look-ahead token iLookAhead. + * + * If the look-ahead token is YYNOCODE, then check to see if the action is + * independent of the look-ahead. If it is, return the action, otherwise + * return YY_NO_ACTION. + * @param int The look-ahead token + */ + function yy_find_shift_action($iLookAhead) + { + $stateno = $this->yystack[$this->yyidx]->stateno; + + /* if ($this->yyidx < 0) return self::YY_NO_ACTION; */ + if (!isset(self::$yy_shift_ofst[$stateno])) { + // no shift actions + return self::$yy_default[$stateno]; + } + $i = self::$yy_shift_ofst[$stateno]; + if ($i === self::YY_SHIFT_USE_DFLT) { + return self::$yy_default[$stateno]; + } + if ($iLookAhead == self::YYNOCODE) { + return self::YY_NO_ACTION; + } + $i += $iLookAhead; + if ($i < 0 || $i >= self::YY_SZ_ACTTAB || + self::$yy_lookahead[$i] != $iLookAhead) { + if (count(self::$yyFallback) && $iLookAhead < count(self::$yyFallback) + && ($iFallback = self::$yyFallback[$iLookAhead]) != 0) { + if (self::$yyTraceFILE) { + fwrite(self::$yyTraceFILE, self::$yyTracePrompt . "FALLBACK " . + self::tokenName($iLookAhead) . " => " . + self::tokenName($iFallback) . "\n"); + } + return $this->yy_find_shift_action($iFallback); + } + return self::$yy_default[$stateno]; + } else { + return self::$yy_action[$i]; + } + } + + /** + * Find the appropriate action for a parser given the non-terminal + * look-ahead token $iLookAhead. + * + * If the look-ahead token is self::YYNOCODE, then check to see if the action is + * independent of the look-ahead. If it is, return the action, otherwise + * return self::YY_NO_ACTION. + * @param int Current state number + * @param int The look-ahead token + */ + function yy_find_reduce_action($stateno, $iLookAhead) + { + /* $stateno = $this->yystack[$this->yyidx]->stateno; */ + + if (!isset(self::$yy_reduce_ofst[$stateno])) { + return self::$yy_default[$stateno]; + } + $i = self::$yy_reduce_ofst[$stateno]; + if ($i == self::YY_REDUCE_USE_DFLT) { + return self::$yy_default[$stateno]; + } + if ($iLookAhead == self::YYNOCODE) { + return self::YY_NO_ACTION; + } + $i += $iLookAhead; + if ($i < 0 || $i >= self::YY_SZ_ACTTAB || + self::$yy_lookahead[$i] != $iLookAhead) { + return self::$yy_default[$stateno]; + } else { + return self::$yy_action[$i]; + } + } + + /** + * Perform a shift action. + * @param int The new state to shift in + * @param int The major token to shift in + * @param mixed the minor token to shift in + */ + function yy_shift($yyNewState, $yyMajor, $yypMinor) + { + $this->yyidx++; + if ($this->yyidx >= self::YYSTACKDEPTH) { + $this->yyidx--; + if (self::$yyTraceFILE) { + fprintf(self::$yyTraceFILE, "%sStack Overflow!\n", self::$yyTracePrompt); + } + while ($this->yyidx >= 0) { + $this->yy_pop_parser_stack(); + } + /* Here code is inserted which will execute if the parser + ** stack ever overflows */ + return; + } + $yytos = new yyStackEntry; + $yytos->stateno = $yyNewState; + $yytos->major = $yyMajor; + $yytos->minor = $yypMinor; + array_push($this->yystack, $yytos); + if (self::$yyTraceFILE && $this->yyidx > 0) { + fprintf(self::$yyTraceFILE, "%sShift %d\n", self::$yyTracePrompt, + $yyNewState); + fprintf(self::$yyTraceFILE, "%sStack:", self::$yyTracePrompt); + for ($i = 1; $i <= $this->yyidx; $i++) { + fprintf(self::$yyTraceFILE, " %s", + self::tokenName($this->yystack[$i]->major)); + } + fwrite(self::$yyTraceFILE,"\n"); + } + } + + /** + * The following table contains information about every rule that + * is used during the reduce. + * + *
+     * array(
+     *  array(
+     *   int $lhs;         Symbol on the left-hand side of the rule
+     *   int $nrhs;     Number of right-hand side symbols in the rule
+     *  ),...
+     * );
+     * 
+ */ + static public $yyRuleInfo = array( + array( 'lhs' => 74, 'rhs' => 0 ), + array( 'lhs' => 74, 'rhs' => 1 ), + array( 'lhs' => 74, 'rhs' => 2 ), + array( 'lhs' => 75, 'rhs' => 1 ), + array( 'lhs' => 75, 'rhs' => 3 ), + array( 'lhs' => 75, 'rhs' => 2 ), + array( 'lhs' => 77, 'rhs' => 1 ), + array( 'lhs' => 77, 'rhs' => 1 ), + array( 'lhs' => 79, 'rhs' => 1 ), + array( 'lhs' => 79, 'rhs' => 1 ), + array( 'lhs' => 79, 'rhs' => 1 ), + array( 'lhs' => 78, 'rhs' => 1 ), + array( 'lhs' => 78, 'rhs' => 1 ), + array( 'lhs' => 78, 'rhs' => 1 ), + array( 'lhs' => 78, 'rhs' => 1 ), + array( 'lhs' => 78, 'rhs' => 1 ), + array( 'lhs' => 78, 'rhs' => 1 ), + array( 'lhs' => 78, 'rhs' => 1 ), + array( 'lhs' => 78, 'rhs' => 1 ), + array( 'lhs' => 78, 'rhs' => 1 ), + array( 'lhs' => 78, 'rhs' => 1 ), + array( 'lhs' => 78, 'rhs' => 1 ), + array( 'lhs' => 78, 'rhs' => 1 ), + array( 'lhs' => 76, 'rhs' => 2 ), + array( 'lhs' => 76, 'rhs' => 3 ), + array( 'lhs' => 94, 'rhs' => 1 ), + array( 'lhs' => 95, 'rhs' => 1 ), + array( 'lhs' => 95, 'rhs' => 1 ), + array( 'lhs' => 96, 'rhs' => 1 ), + array( 'lhs' => 96, 'rhs' => 1 ), + array( 'lhs' => 96, 'rhs' => 1 ), + array( 'lhs' => 96, 'rhs' => 1 ), + array( 'lhs' => 96, 'rhs' => 1 ), + array( 'lhs' => 86, 'rhs' => 3 ), + array( 'lhs' => 86, 'rhs' => 4 ), + array( 'lhs' => 86, 'rhs' => 5 ), + array( 'lhs' => 98, 'rhs' => 1 ), + array( 'lhs' => 98, 'rhs' => 3 ), + array( 'lhs' => 98, 'rhs' => 5 ), + array( 'lhs' => 98, 'rhs' => 1 ), + array( 'lhs' => 99, 'rhs' => 1 ), + array( 'lhs' => 99, 'rhs' => 1 ), + array( 'lhs' => 99, 'rhs' => 1 ), + array( 'lhs' => 80, 'rhs' => 2 ), + array( 'lhs' => 80, 'rhs' => 1 ), + array( 'lhs' => 81, 'rhs' => 1 ), + array( 'lhs' => 84, 'rhs' => 5 ), + array( 'lhs' => 84, 'rhs' => 2 ), + array( 'lhs' => 102, 'rhs' => 1 ), + array( 'lhs' => 102, 'rhs' => 1 ), + array( 'lhs' => 103, 'rhs' => 0 ), + array( 'lhs' => 103, 'rhs' => 1 ), + array( 'lhs' => 101, 'rhs' => 0 ), + array( 'lhs' => 101, 'rhs' => 1 ), + array( 'lhs' => 101, 'rhs' => 3 ), + array( 'lhs' => 104, 'rhs' => 1 ), + array( 'lhs' => 104, 'rhs' => 2 ), + array( 'lhs' => 104, 'rhs' => 3 ), + array( 'lhs' => 105, 'rhs' => 1 ), + array( 'lhs' => 105, 'rhs' => 1 ), + array( 'lhs' => 105, 'rhs' => 1 ), + array( 'lhs' => 105, 'rhs' => 1 ), + array( 'lhs' => 108, 'rhs' => 2 ), + array( 'lhs' => 109, 'rhs' => 1 ), + array( 'lhs' => 109, 'rhs' => 2 ), + array( 'lhs' => 109, 'rhs' => 2 ), + array( 'lhs' => 109, 'rhs' => 1 ), + array( 'lhs' => 97, 'rhs' => 1 ), + array( 'lhs' => 97, 'rhs' => 1 ), + array( 'lhs' => 97, 'rhs' => 1 ), + array( 'lhs' => 82, 'rhs' => 1 ), + array( 'lhs' => 82, 'rhs' => 1 ), + array( 'lhs' => 82, 'rhs' => 1 ), + array( 'lhs' => 82, 'rhs' => 1 ), + array( 'lhs' => 82, 'rhs' => 1 ), + array( 'lhs' => 110, 'rhs' => 2 ), + array( 'lhs' => 110, 'rhs' => 2 ), + array( 'lhs' => 110, 'rhs' => 2 ), + array( 'lhs' => 110, 'rhs' => 1 ), + array( 'lhs' => 110, 'rhs' => 1 ), + array( 'lhs' => 114, 'rhs' => 3 ), + array( 'lhs' => 114, 'rhs' => 2 ), + array( 'lhs' => 115, 'rhs' => 1 ), + array( 'lhs' => 115, 'rhs' => 1 ), + array( 'lhs' => 107, 'rhs' => 4 ), + array( 'lhs' => 117, 'rhs' => 0 ), + array( 'lhs' => 117, 'rhs' => 1 ), + array( 'lhs' => 117, 'rhs' => 3 ), + array( 'lhs' => 117, 'rhs' => 4 ), + array( 'lhs' => 117, 'rhs' => 6 ), + array( 'lhs' => 92, 'rhs' => 1 ), + array( 'lhs' => 92, 'rhs' => 2 ), + array( 'lhs' => 92, 'rhs' => 3 ), + array( 'lhs' => 92, 'rhs' => 4 ), + array( 'lhs' => 92, 'rhs' => 2 ), + array( 'lhs' => 92, 'rhs' => 3 ), + array( 'lhs' => 92, 'rhs' => 4 ), + array( 'lhs' => 92, 'rhs' => 5 ), + array( 'lhs' => 83, 'rhs' => 3 ), + array( 'lhs' => 83, 'rhs' => 3 ), + array( 'lhs' => 83, 'rhs' => 1 ), + array( 'lhs' => 83, 'rhs' => 2 ), + array( 'lhs' => 118, 'rhs' => 0 ), + array( 'lhs' => 118, 'rhs' => 1 ), + array( 'lhs' => 119, 'rhs' => 2 ), + array( 'lhs' => 119, 'rhs' => 4 ), + array( 'lhs' => 113, 'rhs' => 1 ), + array( 'lhs' => 113, 'rhs' => 1 ), + array( 'lhs' => 100, 'rhs' => 2 ), + array( 'lhs' => 106, 'rhs' => 2 ), + array( 'lhs' => 106, 'rhs' => 4 ), + array( 'lhs' => 121, 'rhs' => 1 ), + array( 'lhs' => 121, 'rhs' => 1 ), + array( 'lhs' => 112, 'rhs' => 5 ), + array( 'lhs' => 116, 'rhs' => 3 ), + array( 'lhs' => 116, 'rhs' => 2 ), + array( 'lhs' => 116, 'rhs' => 2 ), + array( 'lhs' => 116, 'rhs' => 1 ), + array( 'lhs' => 120, 'rhs' => 1 ), + array( 'lhs' => 120, 'rhs' => 3 ), + array( 'lhs' => 120, 'rhs' => 4 ), + array( 'lhs' => 120, 'rhs' => 4 ), + array( 'lhs' => 120, 'rhs' => 6 ), + array( 'lhs' => 122, 'rhs' => 1 ), + array( 'lhs' => 122, 'rhs' => 1 ), + array( 'lhs' => 123, 'rhs' => 1 ), + array( 'lhs' => 123, 'rhs' => 3 ), + array( 'lhs' => 88, 'rhs' => 2 ), + array( 'lhs' => 88, 'rhs' => 3 ), + array( 'lhs' => 88, 'rhs' => 4 ), + array( 'lhs' => 88, 'rhs' => 5 ), + array( 'lhs' => 124, 'rhs' => 3 ), + array( 'lhs' => 93, 'rhs' => 2 ), + array( 'lhs' => 111, 'rhs' => 3 ), + array( 'lhs' => 111, 'rhs' => 5 ), + array( 'lhs' => 125, 'rhs' => 2 ), + array( 'lhs' => 125, 'rhs' => 4 ), + array( 'lhs' => 125, 'rhs' => 2 ), + array( 'lhs' => 125, 'rhs' => 4 ), + array( 'lhs' => 89, 'rhs' => 2 ), + array( 'lhs' => 89, 'rhs' => 2 ), + array( 'lhs' => 89, 'rhs' => 2 ), + array( 'lhs' => 89, 'rhs' => 1 ), + array( 'lhs' => 126, 'rhs' => 2 ), + array( 'lhs' => 126, 'rhs' => 2 ), + array( 'lhs' => 90, 'rhs' => 2 ), + array( 'lhs' => 90, 'rhs' => 2 ), + array( 'lhs' => 90, 'rhs' => 2 ), + array( 'lhs' => 127, 'rhs' => 2 ), + array( 'lhs' => 127, 'rhs' => 2 ), + array( 'lhs' => 128, 'rhs' => 2 ), + array( 'lhs' => 128, 'rhs' => 3 ), + array( 'lhs' => 131, 'rhs' => 1 ), + array( 'lhs' => 131, 'rhs' => 1 ), + array( 'lhs' => 131, 'rhs' => 1 ), + array( 'lhs' => 130, 'rhs' => 1 ), + array( 'lhs' => 130, 'rhs' => 3 ), + array( 'lhs' => 129, 'rhs' => 2 ), + array( 'lhs' => 129, 'rhs' => 2 ), + array( 'lhs' => 129, 'rhs' => 4 ), + array( 'lhs' => 129, 'rhs' => 4 ), + array( 'lhs' => 129, 'rhs' => 4 ), + array( 'lhs' => 129, 'rhs' => 6 ), + array( 'lhs' => 129, 'rhs' => 6 ), + array( 'lhs' => 91, 'rhs' => 5 ), + array( 'lhs' => 91, 'rhs' => 7 ), + array( 'lhs' => 91, 'rhs' => 4 ), + array( 'lhs' => 91, 'rhs' => 6 ), + array( 'lhs' => 132, 'rhs' => 1 ), + array( 'lhs' => 132, 'rhs' => 2 ), + array( 'lhs' => 133, 'rhs' => 3 ), + array( 'lhs' => 133, 'rhs' => 4 ), + array( 'lhs' => 134, 'rhs' => 3 ), + array( 'lhs' => 134, 'rhs' => 5 ), + array( 'lhs' => 87, 'rhs' => 1 ), + array( 'lhs' => 87, 'rhs' => 3 ), + array( 'lhs' => 87, 'rhs' => 3 ), + array( 'lhs' => 87, 'rhs' => 3 ), + array( 'lhs' => 85, 'rhs' => 2 ), + array( 'lhs' => 85, 'rhs' => 2 ), + array( 'lhs' => 85, 'rhs' => 2 ), + array( 'lhs' => 85, 'rhs' => 2 ), + array( 'lhs' => 85, 'rhs' => 2 ), + array( 'lhs' => 85, 'rhs' => 2 ), + array( 'lhs' => 85, 'rhs' => 2 ), + array( 'lhs' => 85, 'rhs' => 2 ), + array( 'lhs' => 85, 'rhs' => 3 ), + array( 'lhs' => 85, 'rhs' => 3 ), + array( 'lhs' => 85, 'rhs' => 3 ), + array( 'lhs' => 85, 'rhs' => 3 ), + array( 'lhs' => 85, 'rhs' => 3 ), + array( 'lhs' => 85, 'rhs' => 3 ), + array( 'lhs' => 85, 'rhs' => 3 ), + array( 'lhs' => 85, 'rhs' => 3 ), + array( 'lhs' => 85, 'rhs' => 5 ), + array( 'lhs' => 85, 'rhs' => 3 ), + ); + + /** + * The following table contains a mapping of reduce action to method name + * that handles the reduction. + * + * If a rule is not set, it has no handler. + */ + static public $yyReduceMap = array( + 0 => 0, + 23 => 0, + 1 => 1, + 6 => 1, + 7 => 1, + 8 => 1, + 9 => 1, + 11 => 1, + 12 => 1, + 13 => 1, + 14 => 1, + 15 => 1, + 16 => 1, + 17 => 1, + 18 => 1, + 19 => 1, + 20 => 1, + 21 => 1, + 22 => 1, + 28 => 1, + 39 => 1, + 40 => 1, + 41 => 1, + 42 => 1, + 51 => 1, + 58 => 1, + 59 => 1, + 60 => 1, + 61 => 1, + 66 => 1, + 67 => 1, + 70 => 1, + 74 => 1, + 79 => 1, + 123 => 1, + 124 => 1, + 125 => 1, + 142 => 1, + 150 => 1, + 152 => 1, + 168 => 1, + 174 => 1, + 2 => 2, + 5 => 2, + 24 => 2, + 80 => 2, + 3 => 3, + 4 => 4, + 10 => 10, + 25 => 10, + 26 => 10, + 27 => 10, + 29 => 10, + 30 => 10, + 31 => 10, + 32 => 32, + 33 => 33, + 34 => 34, + 35 => 35, + 36 => 36, + 63 => 36, + 68 => 36, + 69 => 36, + 71 => 36, + 72 => 36, + 73 => 36, + 153 => 36, + 154 => 36, + 37 => 37, + 38 => 38, + 43 => 43, + 44 => 44, + 45 => 45, + 46 => 46, + 47 => 47, + 48 => 48, + 49 => 49, + 50 => 50, + 52 => 52, + 85 => 52, + 104 => 52, + 53 => 53, + 86 => 53, + 118 => 53, + 155 => 53, + 54 => 54, + 119 => 54, + 55 => 55, + 56 => 56, + 57 => 57, + 62 => 62, + 64 => 64, + 65 => 65, + 75 => 75, + 76 => 76, + 77 => 77, + 78 => 78, + 81 => 81, + 82 => 82, + 83 => 83, + 84 => 84, + 87 => 87, + 88 => 88, + 89 => 89, + 122 => 89, + 90 => 90, + 91 => 91, + 92 => 92, + 93 => 93, + 94 => 94, + 95 => 95, + 96 => 96, + 97 => 97, + 98 => 98, + 99 => 98, + 100 => 100, + 101 => 101, + 102 => 102, + 103 => 103, + 105 => 105, + 121 => 105, + 106 => 106, + 107 => 106, + 108 => 108, + 109 => 109, + 110 => 110, + 111 => 111, + 112 => 112, + 113 => 113, + 114 => 114, + 115 => 115, + 116 => 116, + 117 => 117, + 120 => 120, + 126 => 126, + 156 => 126, + 127 => 127, + 128 => 128, + 129 => 129, + 130 => 130, + 131 => 131, + 132 => 132, + 133 => 133, + 134 => 134, + 135 => 135, + 136 => 136, + 137 => 137, + 138 => 138, + 139 => 139, + 140 => 140, + 141 => 140, + 143 => 143, + 144 => 144, + 145 => 145, + 146 => 145, + 147 => 147, + 148 => 148, + 149 => 149, + 151 => 151, + 157 => 157, + 158 => 158, + 159 => 159, + 160 => 160, + 161 => 161, + 162 => 162, + 163 => 163, + 164 => 164, + 165 => 165, + 166 => 166, + 167 => 167, + 169 => 169, + 170 => 170, + 171 => 171, + 172 => 172, + 173 => 173, + 175 => 175, + 176 => 176, + 177 => 176, + 178 => 178, + 181 => 178, + 182 => 178, + 179 => 179, + 180 => 179, + 183 => 183, + 184 => 183, + 185 => 185, + 186 => 186, + 187 => 186, + 188 => 186, + 189 => 186, + 190 => 186, + 191 => 186, + 192 => 192, + 193 => 193, + 194 => 194, + 195 => 195, + ); + /* Beginning here are the reduction cases. A typical example + ** follows: + ** #line + ** function yy_r0($yymsp){ ... } // User supplied code + ** #line + */ +#line 28 "/var/www/coffeescript-php/grammar.y" + function yy_r0(){ $this->_retvalue = yy('Block'); } +#line 2676 "/var/www/coffeescript-php/grammar.php" +#line 29 "/var/www/coffeescript-php/grammar.y" + function yy_r1(){ $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; } +#line 2679 "/var/www/coffeescript-php/grammar.php" +#line 30 "/var/www/coffeescript-php/grammar.y" + function yy_r2(){ $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor; } +#line 2682 "/var/www/coffeescript-php/grammar.php" +#line 32 "/var/www/coffeescript-php/grammar.y" + function yy_r3(){ $this->_retvalue = yy_Block::wrap(array($this->yystack[$this->yyidx + 0]->minor)); } +#line 2685 "/var/www/coffeescript-php/grammar.php" +#line 33 "/var/www/coffeescript-php/grammar.y" + function yy_r4(){ $this->_retvalue = $this->yystack[$this->yyidx + -2]->minor->push($this->yystack[$this->yyidx + 0]->minor); } +#line 2688 "/var/www/coffeescript-php/grammar.php" +#line 41 "/var/www/coffeescript-php/grammar.y" + function yy_r10(){ $this->_retvalue = yy('Literal', $this->yystack[$this->yyidx + 0]->minor); } +#line 2691 "/var/www/coffeescript-php/grammar.php" +#line 69 "/var/www/coffeescript-php/grammar.y" + function yy_r32(){ + $val = yy('Literal', $this->yystack[$this->yyidx + 0]->minor); + $val->is_undefined = $this->yystack[$this->yyidx + 0]->minor === 'undefined'; + $this->_retvalue = $val; + } +#line 2698 "/var/www/coffeescript-php/grammar.php" +#line 75 "/var/www/coffeescript-php/grammar.y" + function yy_r33(){ $this->_retvalue = yy('Assign', $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor); } +#line 2701 "/var/www/coffeescript-php/grammar.php" +#line 76 "/var/www/coffeescript-php/grammar.y" + function yy_r34(){ $this->_retvalue = yy('Assign', $this->yystack[$this->yyidx + -3]->minor, $this->yystack[$this->yyidx + 0]->minor); } +#line 2704 "/var/www/coffeescript-php/grammar.php" +#line 77 "/var/www/coffeescript-php/grammar.y" + function yy_r35(){ $this->_retvalue = yy('Assign', $this->yystack[$this->yyidx + -4]->minor, $this->yystack[$this->yyidx + -1]->minor); } +#line 2707 "/var/www/coffeescript-php/grammar.php" +#line 79 "/var/www/coffeescript-php/grammar.y" + function yy_r36(){ $this->_retvalue = yy('Value', $this->yystack[$this->yyidx + 0]->minor); } +#line 2710 "/var/www/coffeescript-php/grammar.php" +#line 80 "/var/www/coffeescript-php/grammar.y" + function yy_r37(){ $this->_retvalue = yy('Assign', yy('Value', $this->yystack[$this->yyidx + -2]->minor), $this->yystack[$this->yyidx + 0]->minor, 'object'); } +#line 2713 "/var/www/coffeescript-php/grammar.php" +#line 81 "/var/www/coffeescript-php/grammar.y" + function yy_r38(){ $this->_retvalue = yy('Assign', yy('Value', $this->yystack[$this->yyidx + -4]->minor), $this->yystack[$this->yyidx + -1]->minor, 'object'); } +#line 2716 "/var/www/coffeescript-php/grammar.php" +#line 88 "/var/www/coffeescript-php/grammar.y" + function yy_r43(){ $this->_retvalue = yy('Return', $this->yystack[$this->yyidx + 0]->minor); } +#line 2719 "/var/www/coffeescript-php/grammar.php" +#line 89 "/var/www/coffeescript-php/grammar.y" + function yy_r44(){ $this->_retvalue = yy('Return'); } +#line 2722 "/var/www/coffeescript-php/grammar.php" +#line 91 "/var/www/coffeescript-php/grammar.y" + function yy_r45(){ $this->_retvalue = yy('Comment', $this->yystack[$this->yyidx + 0]->minor); } +#line 2725 "/var/www/coffeescript-php/grammar.php" +#line 93 "/var/www/coffeescript-php/grammar.y" + function yy_r46(){ $this->_retvalue = yy('Code', $this->yystack[$this->yyidx + -3]->minor, $this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor); } +#line 2728 "/var/www/coffeescript-php/grammar.php" +#line 94 "/var/www/coffeescript-php/grammar.y" + function yy_r47(){ $this->_retvalue = yy('Code', array(), $this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor); } +#line 2731 "/var/www/coffeescript-php/grammar.php" +#line 96 "/var/www/coffeescript-php/grammar.y" + function yy_r48(){ $this->_retvalue = 'func'; } +#line 2734 "/var/www/coffeescript-php/grammar.php" +#line 97 "/var/www/coffeescript-php/grammar.y" + function yy_r49(){ $this->_retvalue = 'boundfunc'; } +#line 2737 "/var/www/coffeescript-php/grammar.php" +#line 99 "/var/www/coffeescript-php/grammar.y" + function yy_r50(){ $this->_retvalue = ''; } +#line 2740 "/var/www/coffeescript-php/grammar.php" +#line 102 "/var/www/coffeescript-php/grammar.y" + function yy_r52(){ $this->_retvalue = array(); } +#line 2743 "/var/www/coffeescript-php/grammar.php" +#line 103 "/var/www/coffeescript-php/grammar.y" + function yy_r53(){ $this->_retvalue = array($this->yystack[$this->yyidx + 0]->minor); } +#line 2746 "/var/www/coffeescript-php/grammar.php" +#line 104 "/var/www/coffeescript-php/grammar.y" + function yy_r54(){ $this->_retvalue = array_merge($this->yystack[$this->yyidx + -2]->minor, array($this->yystack[$this->yyidx + 0]->minor)); } +#line 2749 "/var/www/coffeescript-php/grammar.php" +#line 106 "/var/www/coffeescript-php/grammar.y" + function yy_r55(){ $this->_retvalue = yy('Param', $this->yystack[$this->yyidx + 0]->minor); } +#line 2752 "/var/www/coffeescript-php/grammar.php" +#line 107 "/var/www/coffeescript-php/grammar.y" + function yy_r56(){ $this->_retvalue = yy('Param', $this->yystack[$this->yyidx + -1]->minor, NULL, TRUE); } +#line 2755 "/var/www/coffeescript-php/grammar.php" +#line 108 "/var/www/coffeescript-php/grammar.y" + function yy_r57(){ $this->_retvalue = yy('Param', $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor); } +#line 2758 "/var/www/coffeescript-php/grammar.php" +#line 115 "/var/www/coffeescript-php/grammar.y" + function yy_r62(){ $this->_retvalue = yy('Splat', $this->yystack[$this->yyidx + -1]->minor); } +#line 2761 "/var/www/coffeescript-php/grammar.php" +#line 118 "/var/www/coffeescript-php/grammar.y" + function yy_r64(){ $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor->add($this->yystack[$this->yyidx + 0]->minor); } +#line 2764 "/var/www/coffeescript-php/grammar.php" +#line 119 "/var/www/coffeescript-php/grammar.y" + function yy_r65(){ $this->_retvalue = yy('Value', $this->yystack[$this->yyidx + -1]->minor, is_object($this->yystack[$this->yyidx + 0]->minor) ? array($this->yystack[$this->yyidx + 0]->minor) : (array) $this->yystack[$this->yyidx + 0]->minor); } +#line 2767 "/var/www/coffeescript-php/grammar.php" +#line 132 "/var/www/coffeescript-php/grammar.y" + function yy_r75(){ $this->_retvalue = yy('Access', $this->yystack[$this->yyidx + 0]->minor); } +#line 2770 "/var/www/coffeescript-php/grammar.php" +#line 133 "/var/www/coffeescript-php/grammar.y" + function yy_r76(){ $this->_retvalue = yy('Access', $this->yystack[$this->yyidx + 0]->minor, 'soak'); } +#line 2773 "/var/www/coffeescript-php/grammar.php" +#line 134 "/var/www/coffeescript-php/grammar.y" + function yy_r77(){ $this->_retvalue = array( yy('Access', yy('Literal', 'prototype')), yy('Access', $this->yystack[$this->yyidx + 0]->minor) ); } +#line 2776 "/var/www/coffeescript-php/grammar.php" +#line 135 "/var/www/coffeescript-php/grammar.y" + function yy_r78(){ $this->_retvalue = yy('Access', yy('Literal', 'prototype')); } +#line 2779 "/var/www/coffeescript-php/grammar.php" +#line 139 "/var/www/coffeescript-php/grammar.y" + function yy_r81(){ $this->_retvalue = extend($this->yystack[$this->yyidx + 0]->minor, array('soak' => TRUE)); } +#line 2782 "/var/www/coffeescript-php/grammar.php" +#line 141 "/var/www/coffeescript-php/grammar.y" + function yy_r82(){ $this->_retvalue = yy('Index', $this->yystack[$this->yyidx + 0]->minor); } +#line 2785 "/var/www/coffeescript-php/grammar.php" +#line 142 "/var/www/coffeescript-php/grammar.y" + function yy_r83(){ $this->_retvalue = yy('Slice', $this->yystack[$this->yyidx + 0]->minor); } +#line 2788 "/var/www/coffeescript-php/grammar.php" +#line 144 "/var/www/coffeescript-php/grammar.y" + function yy_r84(){ + $object_start = $this->yystack[$this->yyidx - 3]->minor; + $generated = isset($object_start->generated) ? $object_start->generated : FALSE; + + $this->_retvalue = yy('Obj', $this->yystack[$this->yyidx + -2]->minor, $generated); + } +#line 2796 "/var/www/coffeescript-php/grammar.php" +#line 153 "/var/www/coffeescript-php/grammar.y" + function yy_r87(){ $this->yystack[$this->yyidx + -2]->minor[] = $this->yystack[$this->yyidx + 0]->minor; $this->_retvalue = $this->yystack[$this->yyidx + -2]->minor; } +#line 2799 "/var/www/coffeescript-php/grammar.php" +#line 154 "/var/www/coffeescript-php/grammar.y" + function yy_r88(){ $this->yystack[$this->yyidx + -3]->minor[] = $this->yystack[$this->yyidx + 0]->minor; $this->_retvalue = $this->yystack[$this->yyidx + -3]->minor; } +#line 2802 "/var/www/coffeescript-php/grammar.php" +#line 155 "/var/www/coffeescript-php/grammar.y" + function yy_r89(){ $this->_retvalue = array_merge($this->yystack[$this->yyidx + -5]->minor, $this->yystack[$this->yyidx + -2]->minor); } +#line 2805 "/var/www/coffeescript-php/grammar.php" +#line 157 "/var/www/coffeescript-php/grammar.y" + function yy_r90(){ $this->_retvalue = yy('Class'); } +#line 2808 "/var/www/coffeescript-php/grammar.php" +#line 158 "/var/www/coffeescript-php/grammar.y" + function yy_r91(){ $this->_retvalue = yy('Class', NULL, NULL, $this->yystack[$this->yyidx + 0]->minor); } +#line 2811 "/var/www/coffeescript-php/grammar.php" +#line 159 "/var/www/coffeescript-php/grammar.y" + function yy_r92(){ $this->_retvalue = yy('Class', NULL, $this->yystack[$this->yyidx + 0]->minor); } +#line 2814 "/var/www/coffeescript-php/grammar.php" +#line 160 "/var/www/coffeescript-php/grammar.y" + function yy_r93(){ $this->_retvalue = yy('Class', NULL, $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor); } +#line 2817 "/var/www/coffeescript-php/grammar.php" +#line 161 "/var/www/coffeescript-php/grammar.y" + function yy_r94(){ $this->_retvalue = yy('Class', $this->yystack[$this->yyidx + 0]->minor); } +#line 2820 "/var/www/coffeescript-php/grammar.php" +#line 162 "/var/www/coffeescript-php/grammar.y" + function yy_r95(){ $this->_retvalue = yy('Class', $this->yystack[$this->yyidx + -1]->minor, NULL, $this->yystack[$this->yyidx + 0]->minor); } +#line 2823 "/var/www/coffeescript-php/grammar.php" +#line 163 "/var/www/coffeescript-php/grammar.y" + function yy_r96(){ $this->_retvalue = yy('Class', $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor); } +#line 2826 "/var/www/coffeescript-php/grammar.php" +#line 164 "/var/www/coffeescript-php/grammar.y" + function yy_r97(){ $this->_retvalue = yy('Class', $this->yystack[$this->yyidx + -3]->minor, $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor); } +#line 2829 "/var/www/coffeescript-php/grammar.php" +#line 166 "/var/www/coffeescript-php/grammar.y" + function yy_r98(){ $this->_retvalue = yy('Call', $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor); } +#line 2832 "/var/www/coffeescript-php/grammar.php" +#line 168 "/var/www/coffeescript-php/grammar.y" + function yy_r100(){ $this->_retvalue = yy('Call', 'super', array(yy('Splat', yy('Literal', 'arguments')))); } +#line 2835 "/var/www/coffeescript-php/grammar.php" +#line 169 "/var/www/coffeescript-php/grammar.y" + function yy_r101(){ $this->_retvalue = yy('Call', 'super', $this->yystack[$this->yyidx + 0]->minor); } +#line 2838 "/var/www/coffeescript-php/grammar.php" +#line 171 "/var/www/coffeescript-php/grammar.y" + function yy_r102(){ $this->_retvalue = FALSE; } +#line 2841 "/var/www/coffeescript-php/grammar.php" +#line 172 "/var/www/coffeescript-php/grammar.y" + function yy_r103(){ $this->_retvalue = TRUE; } +#line 2844 "/var/www/coffeescript-php/grammar.php" +#line 175 "/var/www/coffeescript-php/grammar.y" + function yy_r105(){ $this->_retvalue = $this->yystack[$this->yyidx + -2]->minor; } +#line 2847 "/var/www/coffeescript-php/grammar.php" +#line 177 "/var/www/coffeescript-php/grammar.y" + function yy_r106(){ $this->_retvalue = yy('Value', yy('Literal', 'this')); } +#line 2850 "/var/www/coffeescript-php/grammar.php" +#line 180 "/var/www/coffeescript-php/grammar.y" + function yy_r108(){ $this->_retvalue = yy('Value', yy('Literal', 'this'), array(yy('Access', $this->yystack[$this->yyidx + 0]->minor)), 'this'); } +#line 2853 "/var/www/coffeescript-php/grammar.php" +#line 182 "/var/www/coffeescript-php/grammar.y" + function yy_r109(){ $this->_retvalue = yy('Arr', array()); } +#line 2856 "/var/www/coffeescript-php/grammar.php" +#line 183 "/var/www/coffeescript-php/grammar.y" + function yy_r110(){ $this->_retvalue = yy('Arr', $this->yystack[$this->yyidx + -2]->minor); } +#line 2859 "/var/www/coffeescript-php/grammar.php" +#line 185 "/var/www/coffeescript-php/grammar.y" + function yy_r111(){ $this->_retvalue = 'inclusive'; } +#line 2862 "/var/www/coffeescript-php/grammar.php" +#line 186 "/var/www/coffeescript-php/grammar.y" + function yy_r112(){ $this->_retvalue = 'exclusive'; } +#line 2865 "/var/www/coffeescript-php/grammar.php" +#line 188 "/var/www/coffeescript-php/grammar.y" + function yy_r113(){ $this->_retvalue = yy('Range', $this->yystack[$this->yyidx + -3]->minor, $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + -2]->minor); } +#line 2868 "/var/www/coffeescript-php/grammar.php" +#line 190 "/var/www/coffeescript-php/grammar.y" + function yy_r114(){ $this->_retvalue = yy('Range', $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor); } +#line 2871 "/var/www/coffeescript-php/grammar.php" +#line 191 "/var/www/coffeescript-php/grammar.y" + function yy_r115(){ $this->_retvalue = yy('Range', $this->yystack[$this->yyidx + -1]->minor, NULL, $this->yystack[$this->yyidx + 0]->minor); } +#line 2874 "/var/www/coffeescript-php/grammar.php" +#line 192 "/var/www/coffeescript-php/grammar.y" + function yy_r116(){ $this->_retvalue = yy('Range', NULL, $this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor); } +#line 2877 "/var/www/coffeescript-php/grammar.php" +#line 193 "/var/www/coffeescript-php/grammar.y" + function yy_r117(){ $this->_retvalue = yy('Range', NULL, NULL, $this->yystack[$this->yyidx + 0]->minor); } +#line 2880 "/var/www/coffeescript-php/grammar.php" +#line 197 "/var/www/coffeescript-php/grammar.y" + function yy_r120(){ $this->_retvalue = array_merge($this->yystack[$this->yyidx + -3]->minor, array($this->yystack[$this->yyidx + 0]->minor)); } +#line 2883 "/var/www/coffeescript-php/grammar.php" +#line 205 "/var/www/coffeescript-php/grammar.y" + function yy_r126(){ $this->_retvalue = array($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor); } +#line 2886 "/var/www/coffeescript-php/grammar.php" +#line 207 "/var/www/coffeescript-php/grammar.y" + function yy_r127(){ $this->_retvalue = yy('Try', $this->yystack[$this->yyidx + 0]->minor); } +#line 2889 "/var/www/coffeescript-php/grammar.php" +#line 208 "/var/www/coffeescript-php/grammar.y" + function yy_r128(){ $this->_retvalue = yy('Try', $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor[0], $this->yystack[$this->yyidx + 0]->minor[1]); } +#line 2892 "/var/www/coffeescript-php/grammar.php" +#line 209 "/var/www/coffeescript-php/grammar.y" + function yy_r129(){ $this->_retvalue = yy('Try', $this->yystack[$this->yyidx + -2]->minor, NULL, NULL, $this->yystack[$this->yyidx + 0]->minor); } +#line 2895 "/var/www/coffeescript-php/grammar.php" +#line 210 "/var/www/coffeescript-php/grammar.y" + function yy_r130(){ $this->_retvalue = yy('Try', $this->yystack[$this->yyidx + -3]->minor, $this->yystack[$this->yyidx + -2]->minor[0], $this->yystack[$this->yyidx + -2]->minor[1], $this->yystack[$this->yyidx + 0]->minor); } +#line 2898 "/var/www/coffeescript-php/grammar.php" +#line 212 "/var/www/coffeescript-php/grammar.y" + function yy_r131(){ $this->_retvalue = array($this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor); } +#line 2901 "/var/www/coffeescript-php/grammar.php" +#line 214 "/var/www/coffeescript-php/grammar.y" + function yy_r132(){ $this->_retvalue = yy('Throw', $this->yystack[$this->yyidx + 0]->minor); } +#line 2904 "/var/www/coffeescript-php/grammar.php" +#line 216 "/var/www/coffeescript-php/grammar.y" + function yy_r133(){ $this->_retvalue = yy('Parens', $this->yystack[$this->yyidx + -1]->minor); } +#line 2907 "/var/www/coffeescript-php/grammar.php" +#line 217 "/var/www/coffeescript-php/grammar.y" + function yy_r134(){ $this->_retvalue = yy('Parens', $this->yystack[$this->yyidx + -2]->minor); } +#line 2910 "/var/www/coffeescript-php/grammar.php" +#line 219 "/var/www/coffeescript-php/grammar.y" + function yy_r135(){ $this->_retvalue = yy('While', $this->yystack[$this->yyidx + 0]->minor); } +#line 2913 "/var/www/coffeescript-php/grammar.php" +#line 220 "/var/www/coffeescript-php/grammar.y" + function yy_r136(){ $this->_retvalue = yy('While', $this->yystack[$this->yyidx + -2]->minor, array('guard' => $this->yystack[$this->yyidx + 0]->minor)); } +#line 2916 "/var/www/coffeescript-php/grammar.php" +#line 221 "/var/www/coffeescript-php/grammar.y" + function yy_r137(){ $this->_retvalue = yy('While', $this->yystack[$this->yyidx + 0]->minor, array('invert' => TRUE)); } +#line 2919 "/var/www/coffeescript-php/grammar.php" +#line 222 "/var/www/coffeescript-php/grammar.y" + function yy_r138(){ $this->_retvalue = yy('While', $this->yystack[$this->yyidx + -2]->minor, array('invert' => TRUE, 'guard' => $this->yystack[$this->yyidx + 0]->minor)); } +#line 2922 "/var/www/coffeescript-php/grammar.php" +#line 224 "/var/www/coffeescript-php/grammar.y" + function yy_r139(){ $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor->add_body($this->yystack[$this->yyidx + 0]->minor); } +#line 2925 "/var/www/coffeescript-php/grammar.php" +#line 225 "/var/www/coffeescript-php/grammar.y" + function yy_r140(){ $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor->add_body(yy_Block::wrap(array($this->yystack[$this->yyidx + -1]->minor))); } +#line 2928 "/var/www/coffeescript-php/grammar.php" +#line 229 "/var/www/coffeescript-php/grammar.y" + function yy_r143(){ $this->_retvalue = yy('While', yy('Literal', 'true')); $this->_retvalue->add_body($this->yystack[$this->yyidx + 0]->minor); } +#line 2931 "/var/www/coffeescript-php/grammar.php" +#line 230 "/var/www/coffeescript-php/grammar.y" + function yy_r144(){ $this->_retvalue = yy('While', yy('Literal', 'true')); $this->_retvalue->add_body(yy_Block::wrap($this->yystack[$this->yyidx + 0]->minor)); } +#line 2934 "/var/www/coffeescript-php/grammar.php" +#line 232 "/var/www/coffeescript-php/grammar.y" + function yy_r145(){ $this->_retvalue = yy('For', $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor); } +#line 2937 "/var/www/coffeescript-php/grammar.php" +#line 234 "/var/www/coffeescript-php/grammar.y" + function yy_r147(){ $this->_retvalue = yy('For', $this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor); } +#line 2940 "/var/www/coffeescript-php/grammar.php" +#line 236 "/var/www/coffeescript-php/grammar.y" + function yy_r148(){ $this->_retvalue = array('source' => yy('Value', $this->yystack[$this->yyidx + 0]->minor)); } +#line 2943 "/var/www/coffeescript-php/grammar.php" +#line 237 "/var/www/coffeescript-php/grammar.y" + function yy_r149(){ $this->yystack[$this->yyidx + 0]->minor['own'] = isset($this->yystack[$this->yyidx + -1]->minor['own']) ? $this->yystack[$this->yyidx + -1]->minor['own'] : NULL; $this->yystack[$this->yyidx + 0]->minor['name'] = $this->yystack[$this->yyidx + -1]->minor[0]; $this->yystack[$this->yyidx + 0]->minor['index'] = isset($this->yystack[$this->yyidx + -1]->minor[1]) ? $this->yystack[$this->yyidx + -1]->minor[1] : NULL; $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; } +#line 2946 "/var/www/coffeescript-php/grammar.php" +#line 240 "/var/www/coffeescript-php/grammar.y" + function yy_r151(){ $this->yystack[$this->yyidx + 0]->minor['own'] = TRUE; $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; } +#line 2949 "/var/www/coffeescript-php/grammar.php" +#line 249 "/var/www/coffeescript-php/grammar.y" + function yy_r157(){ $this->_retvalue = array('source' => $this->yystack[$this->yyidx + 0]->minor); } +#line 2952 "/var/www/coffeescript-php/grammar.php" +#line 250 "/var/www/coffeescript-php/grammar.y" + function yy_r158(){ $this->_retvalue = array('source' => $this->yystack[$this->yyidx + 0]->minor, 'object' => TRUE); } +#line 2955 "/var/www/coffeescript-php/grammar.php" +#line 251 "/var/www/coffeescript-php/grammar.y" + function yy_r159(){ $this->_retvalue = array('source' => $this->yystack[$this->yyidx + -2]->minor, 'guard' => $this->yystack[$this->yyidx + 0]->minor); } +#line 2958 "/var/www/coffeescript-php/grammar.php" +#line 252 "/var/www/coffeescript-php/grammar.y" + function yy_r160(){ $this->_retvalue = array('source' => $this->yystack[$this->yyidx + -2]->minor, 'guard' => $this->yystack[$this->yyidx + 0]->minor, 'object' => TRUE); } +#line 2961 "/var/www/coffeescript-php/grammar.php" +#line 253 "/var/www/coffeescript-php/grammar.y" + function yy_r161(){ $this->_retvalue = array('source' => $this->yystack[$this->yyidx + -2]->minor, 'step' => $this->yystack[$this->yyidx + 0]->minor); } +#line 2964 "/var/www/coffeescript-php/grammar.php" +#line 254 "/var/www/coffeescript-php/grammar.y" + function yy_r162(){ $this->_retvalue = array('source' => $this->yystack[$this->yyidx + -4]->minor, 'guard' => $this->yystack[$this->yyidx + -2]->minor, 'step' => $this->yystack[$this->yyidx + 0]->minor); } +#line 2967 "/var/www/coffeescript-php/grammar.php" +#line 255 "/var/www/coffeescript-php/grammar.y" + function yy_r163(){ $this->_retvalue = array('source' => $this->yystack[$this->yyidx + -4]->minor, 'step' => $this->yystack[$this->yyidx + -2]->minor, 'guard' => $this->yystack[$this->yyidx + 0]->minor); } +#line 2970 "/var/www/coffeescript-php/grammar.php" +#line 257 "/var/www/coffeescript-php/grammar.y" + function yy_r164(){ $this->_retvalue = yy('Switch', $this->yystack[$this->yyidx + -3]->minor, $this->yystack[$this->yyidx + -1]->minor); } +#line 2973 "/var/www/coffeescript-php/grammar.php" +#line 258 "/var/www/coffeescript-php/grammar.y" + function yy_r165(){ $this->_retvalue = yy('Switch', $this->yystack[$this->yyidx + -5]->minor, $this->yystack[$this->yyidx + -3]->minor, $this->yystack[$this->yyidx + -1]->minor); } +#line 2976 "/var/www/coffeescript-php/grammar.php" +#line 259 "/var/www/coffeescript-php/grammar.y" + function yy_r166(){ $this->_retvalue = yy('Switch', NULL, $this->yystack[$this->yyidx + -1]->minor); } +#line 2979 "/var/www/coffeescript-php/grammar.php" +#line 260 "/var/www/coffeescript-php/grammar.y" + function yy_r167(){ $this->_retvalue = yy('Switch', NULL, $this->yystack[$this->yyidx + -3]->minor, $this->yystack[$this->yyidx + -1]->minor); } +#line 2982 "/var/www/coffeescript-php/grammar.php" +#line 263 "/var/www/coffeescript-php/grammar.y" + function yy_r169(){ $this->_retvalue = array_merge($this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor); } +#line 2985 "/var/www/coffeescript-php/grammar.php" +#line 265 "/var/www/coffeescript-php/grammar.y" + function yy_r170(){ $this->_retvalue = array(array($this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor)); } +#line 2988 "/var/www/coffeescript-php/grammar.php" +#line 266 "/var/www/coffeescript-php/grammar.y" + function yy_r171(){ $this->_retvalue = array(array($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -1]->minor)); } +#line 2991 "/var/www/coffeescript-php/grammar.php" +#line 268 "/var/www/coffeescript-php/grammar.y" + function yy_r172(){ $this->_retvalue = yy('If', $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor, array('type' => $this->yystack[$this->yyidx + -2]->minor)); } +#line 2994 "/var/www/coffeescript-php/grammar.php" +#line 269 "/var/www/coffeescript-php/grammar.y" + function yy_r173(){ $this->_retvalue = $this->yystack[$this->yyidx + -4]->minor->add_else(yy('If', $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor, array('type' => $this->yystack[$this->yyidx + -2]->minor))); } +#line 2997 "/var/www/coffeescript-php/grammar.php" +#line 272 "/var/www/coffeescript-php/grammar.y" + function yy_r175(){ $this->_retvalue = $this->yystack[$this->yyidx + -2]->minor->add_else($this->yystack[$this->yyidx + 0]->minor); } +#line 3000 "/var/www/coffeescript-php/grammar.php" +#line 273 "/var/www/coffeescript-php/grammar.y" + function yy_r176(){ $this->_retvalue = yy('If', $this->yystack[$this->yyidx + 0]->minor, yy_Block::wrap(array($this->yystack[$this->yyidx + -2]->minor)), array('type' => $this->yystack[$this->yyidx + -1]->minor, 'statement' => TRUE)); } +#line 3003 "/var/www/coffeescript-php/grammar.php" +#line 276 "/var/www/coffeescript-php/grammar.y" + function yy_r178(){ $this->_retvalue = yy('Op', $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor); } +#line 3006 "/var/www/coffeescript-php/grammar.php" +#line 277 "/var/www/coffeescript-php/grammar.y" + function yy_r179(){ $this->_retvalue = yy('Op', $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor); /* prec: 'UNARY'; */ } +#line 3009 "/var/www/coffeescript-php/grammar.php" +#line 282 "/var/www/coffeescript-php/grammar.y" + function yy_r183(){ $this->_retvalue = yy('Op', $this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor, NULL, TRUE); } +#line 3012 "/var/www/coffeescript-php/grammar.php" +#line 285 "/var/www/coffeescript-php/grammar.y" + function yy_r185(){ $this->_retvalue = yy('Existence', $this->yystack[$this->yyidx + -1]->minor); } +#line 3015 "/var/www/coffeescript-php/grammar.php" +#line 287 "/var/www/coffeescript-php/grammar.y" + function yy_r186(){ $this->_retvalue = yy('Op', $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor); } +#line 3018 "/var/www/coffeescript-php/grammar.php" +#line 294 "/var/www/coffeescript-php/grammar.y" + function yy_r192(){ + if ($this->yystack[$this->yyidx + -1]->minor{0} === '!') { + $this->_retvalue = yy('Op', substr($this->yystack[$this->yyidx + -1]->minor, 1), $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor); + $this->_retvalue = $this->_retvalue->invert(); + } + else { + $this->_retvalue = yy('Op', $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor); + } + } +#line 3029 "/var/www/coffeescript-php/grammar.php" +#line 304 "/var/www/coffeescript-php/grammar.y" + function yy_r193(){ $this->_retvalue = yy('Assign', $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor); } +#line 3032 "/var/www/coffeescript-php/grammar.php" +#line 305 "/var/www/coffeescript-php/grammar.y" + function yy_r194(){ $this->_retvalue = yy('Assign', $this->yystack[$this->yyidx + -4]->minor, $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + -3]->minor); } +#line 3035 "/var/www/coffeescript-php/grammar.php" +#line 306 "/var/www/coffeescript-php/grammar.y" + function yy_r195(){ $this->_retvalue = yy('Extends', $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor); } +#line 3038 "/var/www/coffeescript-php/grammar.php" + + /** + * placeholder for the left hand side in a reduce operation. + * + * For a parser with a rule like this: + *
+     * rule(A) ::= B. { A = 1; }
+     * 
+ * + * The parser will translate to something like: + * + * + * function yy_r0(){$this->_retvalue = 1;} + * + */ + private $_retvalue; + + /** + * Perform a reduce action and the shift that must immediately + * follow the reduce. + * + * For a rule such as: + * + *
+     * A ::= B blah C. { dosomething(); }
+     * 
+ * + * This function will first call the action, if any, ("dosomething();" in our + * example), and then it will pop three states from the stack, + * one for each entry on the right-hand side of the expression + * (B, blah, and C in our example rule), and then push the result of the action + * back on to the stack with the resulting state reduced to (as described in the .out + * file) + * @param int Number of the rule by which to reduce + */ + function yy_reduce($yyruleno) + { + //int $yygoto; /* The next state */ + //int $yyact; /* The next action */ + //mixed $yygotominor; /* The LHS of the rule reduced */ + //yyStackEntry $yymsp; /* The top of the parser's stack */ + //int $yysize; /* Amount to pop the stack */ + $yymsp = $this->yystack[$this->yyidx]; + if (self::$yyTraceFILE && $yyruleno >= 0 + && $yyruleno < count(self::$yyRuleName)) { + fprintf(self::$yyTraceFILE, "%sReduce (%d) [%s].\n", + self::$yyTracePrompt, $yyruleno, + self::$yyRuleName[$yyruleno]); + } + + $this->_retvalue = $yy_lefthand_side = null; + if (array_key_exists($yyruleno, self::$yyReduceMap)) { + // call the action + $this->_retvalue = null; + $this->{'yy_r' . self::$yyReduceMap[$yyruleno]}(); + $yy_lefthand_side = $this->_retvalue; + } + $yygoto = self::$yyRuleInfo[$yyruleno]['lhs']; + $yysize = self::$yyRuleInfo[$yyruleno]['rhs']; + $this->yyidx -= $yysize; + for ($i = $yysize; $i; $i--) { + // pop all of the right-hand side parameters + array_pop($this->yystack); + } + $yyact = $this->yy_find_reduce_action($this->yystack[$this->yyidx]->stateno, $yygoto); + if ($yyact < self::YYNSTATE) { + /* If we are not debugging and the reduce action popped at least + ** one element off the stack, then we can push the new element back + ** onto the stack here, and skip the stack overflow test in yy_shift(). + ** That gives a significant speed improvement. */ + if (!self::$yyTraceFILE && $yysize) { + $this->yyidx++; + $x = new yyStackEntry; + $x->stateno = $yyact; + $x->major = $yygoto; + $x->minor = $yy_lefthand_side; + $this->yystack[$this->yyidx] = $x; + } else { + $this->yy_shift($yyact, $yygoto, $yy_lefthand_side); + } + } elseif ($yyact == self::YYNSTATE + self::YYNRULE + 1) { + $this->yy_accept(); + } + } + + /** + * The following code executes when the parse fails + * + * Code from %parse_fail is inserted here + */ + function yy_parse_failed() + { + if (self::$yyTraceFILE) { + fprintf(self::$yyTraceFILE, "%sFail!\n", self::$yyTracePrompt); + } + while ($this->yyidx >= 0) { + $this->yy_pop_parser_stack(); + } + /* Here code is inserted which will be executed whenever the + ** parser fails */ + } + + /** + * The following code executes when a syntax error first occurs. + * + * %syntax_error code is inserted here + * @param int The major type of the error token + * @param mixed The minor type of the error token + */ + function yy_syntax_error($yymajor, $TOKEN) + { +#line 4 "/var/www/coffeescript-php/grammar.y" + + throw new SyntaxError( + 'unexpected '.$this->tokenName($yymajor).' in '.self::$FILE.':' + . (self::$LINE + 1).'.' + ); +#line 3157 "/var/www/coffeescript-php/grammar.php" + } + + /** + * The following is executed when the parser accepts + * + * %parse_accept code is inserted here + */ + function yy_accept() + { + if (self::$yyTraceFILE) { + fprintf(self::$yyTraceFILE, "%sAccept!\n", self::$yyTracePrompt); + } + while ($this->yyidx >= 0) { + $stack = $this->yy_pop_parser_stack(); + } + /* Here code is inserted which will be executed whenever the + ** parser accepts */ + } + + /** + * The main parser program. + * + * The first argument is the major token number. The second is + * the token value string as scanned from the input. + * + * @param int $yymajor the token number + * @param mixed $yytokenvalue the token value + * @param mixed ... any extra arguments that should be passed to handlers + * + * @return void + */ + function parse($token) + { + list($yymajor, $yytokenvalue, ) = $token ? $token : array(0, 0); + self::$LINE = isset($token[2]) ? $token[2] : -1; + +// $yyact; /* The parser action. */ +// $yyendofinput; /* True if we are at the end of input */ + $yyerrorhit = 0; /* True if yymajor has invoked an error */ + + /* (re)initialize the parser, if necessary */ + if ($this->yyidx === null || $this->yyidx < 0) { + /* if ($yymajor == 0) return; // not sure why this was here... */ + $this->yyidx = 0; + $this->yyerrcnt = -1; + $x = new yyStackEntry; + $x->stateno = 0; + $x->major = 0; + $this->yystack = array(); + array_push($this->yystack, $x); + } + $yyendofinput = ($yymajor==0); + + if (self::$yyTraceFILE) { + fprintf( + self::$yyTraceFILE, + "%sInput %s\n", + self::$yyTracePrompt, + self::tokenName($yymajor) + ); + } + + do { + $yyact = $this->yy_find_shift_action($yymajor); + if ($yymajor < self::YYERRORSYMBOL + && !$this->yy_is_expected_token($yymajor) + ) { + // force a syntax error + $yyact = self::YY_ERROR_ACTION; + } + if ($yyact < self::YYNSTATE) { + $this->yy_shift($yyact, $yymajor, $yytokenvalue); + $this->yyerrcnt--; + if ($yyendofinput && $this->yyidx >= 0) { + $yymajor = 0; + } else { + $yymajor = self::YYNOCODE; + } + } elseif ($yyact < self::YYNSTATE + self::YYNRULE) { + $this->yy_reduce($yyact - self::YYNSTATE); + } elseif ($yyact == self::YY_ERROR_ACTION) { + if (self::$yyTraceFILE) { + fprintf( + self::$yyTraceFILE, + "%sSyntax Error!\n", + self::$yyTracePrompt + ); + } + if (self::YYERRORSYMBOL) { + /* A syntax error has occurred. + ** The response to an error depends upon whether or not the + ** grammar defines an error token "ERROR". + ** + ** This is what we do if the grammar does define ERROR: + ** + ** * Call the %syntax_error function. + ** + ** * Begin popping the stack until we enter a state where + ** it is legal to shift the error symbol, then shift + ** the error symbol. + ** + ** * Set the error count to three. + ** + ** * Begin accepting and shifting new tokens. No new error + ** processing will occur until three tokens have been + ** shifted successfully. + ** + */ + if ($this->yyerrcnt < 0) { + $this->yy_syntax_error($yymajor, $yytokenvalue); + } + $yymx = $this->yystack[$this->yyidx]->major; + if ($yymx == self::YYERRORSYMBOL || $yyerrorhit ) { + if (self::$yyTraceFILE) { + fprintf( + self::$yyTraceFILE, + "%sDiscard input token %s\n", + self::$yyTracePrompt, + self::tokenName($yymajor) + ); + } + $this->yy_destructor($yymajor, $yytokenvalue); + $yymajor = self::YYNOCODE; + } else { + while ($this->yyidx >= 0 + && $yymx != self::YYERRORSYMBOL + && ($yyact = $this->yy_find_shift_action(self::YYERRORSYMBOL)) >= self::YYNSTATE + ) { + $this->yy_pop_parser_stack(); + } + if ($this->yyidx < 0 || $yymajor==0) { + $this->yy_destructor($yymajor, $yytokenvalue); + $this->yy_parse_failed(); + $yymajor = self::YYNOCODE; + } elseif ($yymx != self::YYERRORSYMBOL) { + $u2 = 0; + $this->yy_shift($yyact, self::YYERRORSYMBOL, $u2); + } + } + $this->yyerrcnt = 3; + $yyerrorhit = 1; + } else { + /* YYERRORSYMBOL is not defined */ + /* This is what we do if the grammar does not define ERROR: + ** + ** * Report an error message, and throw away the input token. + ** + ** * If the input token is $, then fail the parse. + ** + ** As before, subsequent error messages are suppressed until + ** three input tokens have been successfully shifted. + */ + if ($this->yyerrcnt <= 0) { + $this->yy_syntax_error($yymajor, $yytokenvalue); + } + $this->yyerrcnt = 3; + $this->yy_destructor($yymajor, $yytokenvalue); + if ($yyendofinput) { + $this->yy_parse_failed(); + } + $yymajor = self::YYNOCODE; + } + } else { + $this->yy_accept(); + $yymajor = self::YYNOCODE; + } + } while ($yymajor != self::YYNOCODE && $this->yyidx >= 0); + + if ($token === NULL) + { + return $this->_retvalue; + } + } +} diff --git a/sparks/assets/1.5.1/libraries/coffeescript/Rewriter.php b/sparks/assets/1.5.1/libraries/coffeescript/Rewriter.php new file mode 100755 index 0000000..1c03a0d --- /dev/null +++ b/sparks/assets/1.5.1/libraries/coffeescript/Rewriter.php @@ -0,0 +1,552 @@ +', '=>', '[', '(', '{', '--', '++' + ); + + static $IMPLICIT_UNSPACED_CALL = array('+', '-'); + + static $IMPLICIT_BLOCK = array('->', '=>', '{', '[', ','); + + static $IMPLICIT_END = array('POST_IF', 'FOR', 'WHILE', 'UNTIL', 'WHEN', 'BY', 'LOOP', 'TERMINATOR'); + + static $SINGLE_LINERS = array('ELSE', '->', '=>', 'TRY', 'FINALLY', 'THEN'); + static $SINGLE_CLOSERS = array('TERMINATOR', 'CATCH', 'FINALLY', 'ELSE', 'OUTDENT', 'LEADING_WHEN'); + + static $LINEBREAKS = array('TERMINATOR', 'INDENT', 'OUTDENT'); + + static $initialized = FALSE; + + static function init() + { + if (self::$initialized) return; + + self::$initialized = TRUE; + + foreach (self::$BALANCED_PAIRS as $pair) + { + list($left, $rite) = $pair; + + self::$EXPRESSION_START[] = self::$INVERSES[$rite] = $left; + self::$EXPRESSION_END[] = self::$INVERSES[$left] = $rite; + } + + self::$EXPRESSION_CLOSE = array_merge(self::$EXPRESSION_CLOSE, self::$EXPRESSION_END); + } + + function __construct($tokens) + { + self::init(); + + $this->tokens = $tokens; + } + + function add_implicit_braces() + { + $stack = array(); + $start = NULL; + $starts_line = NULL; + $same_line = TRUE; + $start_indent = 0; + + $self = $this; + + $condition = function( & $token, $i) use ( & $self, & $same_line, & $starts_line) + { + $list = array(); + + for ($j = 0; $j < 3; $j++) + { + $k = ($i + 1) + $j; + $list[$j] = isset($self->tokens[$k]) ? $self->tokens[$k] : array(NULL, NULL); + } + + list($one, $two, $three) = $list; + + if ($one[0] === t('HERECOMMENT')) + { + return FALSE; + } + + $tag = $token[0]; + + if (in_array($tag, t(Rewriter::$LINEBREAKS))) + { + $same_line = FALSE; + } + + return + ( (in_array($tag, t('TERMINATOR', 'OUTDENT')) || (in_array($tag, t(Rewriter::$IMPLICIT_END)) && $same_line)) && + ( ( ! $starts_line && $self->tag($i - 1) !== t(',')) || + ! ($two[0] === t(':') || $one[0] === t('@') && $three[0] === t(':'))) ) || + ($tag === t(',') && + ! in_array($one[0], t('IDENTIFIER', 'NUMBER', 'STRING', '@', 'TERMINATOR', 'OUTDENT')) ); + }; + + $action = function( & $token, $i) use ( & $self) + { + $tok = $self->generate(t('}'), '}', $token[2]); + array_splice($self->tokens, $i, 0, array($tok)); + }; + + $this->scan_tokens(function( & $token, $i, & $tokens) use (& $self, & $stack, & $start, & $start_indent, & $condition, & $action, & $starts_line, & $same_line) + { + if (in_array(($tag = $token[0]), t(Rewriter::$EXPRESSION_START))) + { + $stack[] = array( ($tag === t('INDENT') && $self->tag($i - 1) === t('{')) ? t('{') : $tag, $i ); + return 1; + } + + if (in_array($tag, t(Rewriter::$EXPRESSION_END))) + { + $start = array_pop($stack); + return 1; + } + + $len = count($stack) - 1; + + if ( ! ($tag === t(':') && (($ago = $self->tag($i - 2)) === t(':') || ( ! isset($stack[$len]) || $stack[$len][0] !== t('{'))) )) + { + return 1; + } + + $same_line = TRUE; + + $stack[] = array(t('{')); + $idx = (isset($ago) && $ago === t('@')) ? $i - 2 : $i - 1; + + while ($self->tag($idx - 2) === t('HERECOMMENT')) + { + $idx -= 2; + } + + $prev_tag = $self->tag($idx - 1); + + $starts_line = ! $prev_tag || in_array($prev_tag, t(Rewriter::$LINEBREAKS)); + + $value = wrap('{'); + $value->generated = TRUE; + + $tok = $self->generate(t('{'), $value, $token[2]); + + array_splice($tokens, $idx, 0, array($tok)); + + $self->detect_end($i + 2, $condition, $action); + + return 2; + }); + } + + function add_implicit_indentation() + { + $self = $this; + + $starter = $indent = $outdent = NULL; + + $condition = function($token, $i) use ( & $starter) + { + return $token[1] !== ';' && in_array($token[0], t(Rewriter::$SINGLE_CLOSERS)) && ! ($token[0] === t('ELSE') && ! in_array($starter, t('IF', 'THEN'))); + }; + + $action = function($token, $i) use ( & $self, & $outdent) + { + if ($outdent !== NULL) + { + array_splice($self->tokens, $self->tag($i - 1) === t(',') ? $i - 1 : $i, 0, array($outdent)); + } + }; + + $this->scan_tokens(function( & $token, $i, & $tokens) use ( & $action, & $condition, & $self, & $indent, & $outdent, & $starter) + { + $tag = $token[0]; + + if ($tag === t('TERMINATOR') && $self->tag($i + 1) === t('THEN')) + { + array_splice($tokens, $i, 1); + return 0; + } + + if ($tag === t('ELSE') && $self->tag($i - 1) !== t('OUTDENT')) + { + array_splice($tokens, $i, 0, $self->indentation($token)); + return 2; + } + + if ($tag === t('CATCH') && in_array($self->tag($i + 2), t('OUTDENT', 'TERMINATOR', 'FINALLY'))) + { + array_splice($tokens, $i + 2, 0, $self->indentation($token)); + return 4; + } + + if (in_array($tag, t(Rewriter::$SINGLE_LINERS)) && $self->tag($i + 1) !== t('INDENT') && + ! ($tag === t('ELSE') && $self->tag($i + 1) === t('IF'))) + { + $starter = $tag; + list($indent, $outdent) = $self->indentation($token, TRUE); + + if ($starter === t('THEN')) + { + $indent['fromThen'] = TRUE; + } + + array_splice($tokens, $i + 1, 0, array($indent)); + + $self->detect_end($i + 2, $condition, $action); + + if ($tag === t('THEN')) + { + array_splice($tokens, $i, 1); + } + + return 1; + } + + return 1; + }); + } + + function add_implicit_parentheses() + { + $no_call = $seen_single = $seen_control = FALSE; + $self = $this; + + $condition = function( & $token, $i) use ( & $self, & $seen_single, & $seen_control, & $no_call) + { + $tag = $token[0]; + + if ( ! $seen_single && (isset($token['fromThen']) && $token['fromThen'])) + { + return TRUE; + } + + if (in_array($tag, t('IF', 'ELSE', 'CATCH', '->', '=>', 'CLASS'))) + { + $seen_single = TRUE; + } + + if (in_array($tag, t('IF', 'ELSE', 'SWITCH', 'TRY', '='))) + { + $seen_control = TRUE; + } + + if (in_array($tag, t('.', '?.', '::')) && $self->tag($i - 1) === t('OUTDENT')) + { + return TRUE; + } + + return ! (isset($token['generated']) && $token['generated']) && $self->tag($i - 1) !== t(',') && + (in_array($tag, t(Rewriter::$IMPLICIT_END)) || ($tag === t('INDENT') && ! $seen_control)) && + ($tag !== t('INDENT') || + ( ! in_array($self->tag($i - 2), t('CLASS', 'EXTENDS')) && ! in_array($self->tag($i - 1), t(Rewriter::$IMPLICIT_BLOCK)) && + ! (($post = isset($self->tokens[$i + 1]) ? $self->tokens[$i + 1] : NULL) && (isset($post['generated']) && $post['generated']) && $post[0] === t('{')))); + }; + + $action = function( & $token, $i) use ( & $self) + { + array_splice($self->tokens, $i, 0, array($self->generate(t('CALL_END'), ')', isset($token[2]) ? $token[2] : NULL))); + }; + + $this->scan_tokens(function( & $token, $i, & $tokens) use ( & $condition, & $action, & $no_call, & $self, & $seen_control, & $seen_single ) + { + $tag = $token[0]; + + if (in_array($tag, t('CLASS', 'IF', 'FOR', 'WHILE'))) + { + $no_call = TRUE; + } + + $prev = NULL; + + if (isset($tokens[$i - 1])) + { + $prev = & $tokens[$i - 1]; + } + + $current = $tokens[$i]; + $next = isset($tokens[$i + 1]) ? $tokens[$i + 1] : NULL; + + $call_object = ! $no_call && $tag === t('INDENT') && + $next && (isset($next['generated']) && $next['generated']) && $next[0] === t('{') && + $prev && in_array($prev[0], t(Rewriter::$IMPLICIT_FUNC)); + + $seen_single = FALSE; + $seen_control = FALSE; + + if (in_array($tag, t(Rewriter::$LINEBREAKS))) + { + $no_call = FALSE; + } + + if ($prev && ! (isset($prev['spaced']) && $prev['spaced']) && $tag === t('?')) + { + $token['call'] = TRUE; + } + + if (isset($token['fromThen']) && $token['fromThen']) + { + return 1; + } + + if ( ! ($call_object || ($prev && (isset($prev['spaced']) && $prev['spaced'])) && + ( (isset($prev['call']) && $prev['call']) || in_array($prev[0], t(Rewriter::$IMPLICIT_FUNC)) ) && + ( in_array($tag, t(Rewriter::$IMPLICIT_CALL)) || ! ( (isset($token['spaced']) && $token['spaced']) || + (isset($token['newLine']) && $token['newLine']) ) && + in_array($tag, t(Rewriter::$IMPLICIT_UNSPACED_CALL)) ) + )) + { + return 1; + } + + array_splice($tokens, $i, 0, array($self->generate(t('CALL_START'), '(', $token[2]))); + + $self->detect_end($i + 1, $condition, $action); + + if ($prev[0] === t('?')) + { + $prev[0] = t('FUNC_EXIST'); + } + + return 2; + }); + } + + function close_open_calls() + { + $self = $this; + + $condition = function($token, $i) use ( & $self) + { + return in_array($token[0], t(')', 'CALL_END')) || $token[0] === t('OUTDENT') && + $self->tag($i - 1) === t(')'); + }; + + $action = function($token, $i) use ( & $self) + { + $self->tokens[($token[0] === t('OUTDENT') ? $i - 1 : $i)][0] = t('CALL_END'); + }; + + $this->scan_tokens(function($token, $i) use ( & $self, $condition, $action) + { + if ($token[0] === t('CALL_START')) + { + $self->detect_end($i + 1, $condition, $action); + } + + return 1; + }); + } + + function close_open_indexes() + { + $condition = function($token, $i) + { + return in_array($token[0], t(']', 'INDEX_END')); + }; + + $action = function( & $token, $i) + { + $token[0] = t('INDEX_END'); + }; + + $self = $this; + + $this->scan_tokens(function($token, $i) use ( & $self, $condition, $action) + { + if ($token[0] === t('INDEX_START')) + { + $self->detect_end($i + 1, $condition, $action); + } + + return 1; + }); + } + + function detect_end($i, $condition, $action) + { + $tokens = & $this->tokens; + $levels = 0; + + while (isset($tokens[$i])) + { + $token = & $tokens[$i]; + + if ($levels === 0 && $condition($token, $i)) + { + return $action($token, $i); + } + + if ( ! $token || $levels < 0) + { + return $action($token, $i - 1); + } + + if (in_array($token[0], t(Rewriter::$EXPRESSION_START))) + { + $levels++; + } + else if (in_array($token[0], t(Rewriter::$EXPRESSION_END))) + { + $levels--; + } + + $i++; + } + + return $i - 1; + } + + function generate($tag, $value, $line) + { + return array($tag, $value, $line, 'generated' => TRUE); + } + + function indentation($token, $implicit = FALSE) + { + $indent = array(t('INDENT'), 2, $token[2]); + $outdent = array(t('OUTDENT'), 2, $token[2]); + + if ($implicit) + { + $indent['generated'] = $outdent['generated'] = TRUE; + } + + return array($indent, $outdent); + } + + function remove_leading_newlines() + { + $key = 0; + + foreach ($this->tokens as $k => $token) + { + $key = $k; + $tag = $token[0]; + + if ($tag !== t('TERMINATOR')) + { + break; + } + } + + if ($key) + { + array_splice($this->tokens, 0, $key); + } + } + + function remove_mid_expression_newlines() + { + $self = $this; + + $this->scan_tokens(function( & $token, $i, & $tokens) use ( & $self) + { + if ( ! ($token[0] === t('TERMINATOR') && in_array($self->tag($i + 1), t(Rewriter::$EXPRESSION_CLOSE)))) + { + return 1; + } + + array_splice($tokens, $i, 1); + return 0; + }); + } + + function rewrite() + { + $this->remove_leading_newlines(); + $this->remove_mid_expression_newlines(); + $this->close_open_calls(); + $this->close_open_indexes(); + $this->add_implicit_indentation(); + $this->tag_postfix_conditionals(); + $this->add_implicit_braces(); + $this->add_implicit_parentheses(); + + return $this->tokens; + } + + function scan_tokens($block) + { + $i = 0; + + while (isset($this->tokens[$i])) + { + $i += $block($this->tokens[$i], $i, $this->tokens); + } + + return TRUE; + } + + function tag($i) + { + return isset($this->tokens[$i]) ? $this->tokens[$i][0] : NULL; + } + + function tag_postfix_conditionals() + { + $original = NULL; + + $self = $this; + + $condition = function($token, $i) + { + return in_array($token[0], t('TERMINATOR', 'INDENT')); + }; + + $action = function($token, $i) use ( & $original, & $self) + { + if ($token[0] !== t('INDENT') || ((isset($token['generated']) && $token['generated']) && ! (isset($token['fromThen']) && $token['fromThen']))) + { + $self->tokens[$original][0] = t('POST_'.t_canonical($self->tokens[$original][0])); + + // $original[0] = t('POST_'.t_canonical($original[0])); + } + }; + + $self = $this; + + $this->scan_tokens(function( & $token, $i) use ( & $original, & $condition, & $action, & $self) + { + if ( ! ($token[0] === t('IF'))) + { + return 1; + } + + $original = $i; + + // $original = & $token; + + $self->detect_end($i + 1, $condition, $action); + + return 1; + }); + } +} + +?> diff --git a/sparks/assets/1.5.1/libraries/coffeescript/Scope.php b/sparks/assets/1.5.1/libraries/coffeescript/Scope.php new file mode 100755 index 0000000..bb52c12 --- /dev/null +++ b/sparks/assets/1.5.1/libraries/coffeescript/Scope.php @@ -0,0 +1,196 @@ +parent = $parent; + $this->expressions = $expressions; + $this->method = $method; + + $this->variables = array( + array('name' => 'arguments', 'type' => 'arguments') + ); + + $this->positions = array(); + + if ( ! $this->parent) + { + self::$root = $this; + } + } + + function add($name, $type, $immediate = FALSE) + { + $name = ''.$name; + + if ($this->shared && ! $immediate) + { + return $this->parent->add($name, $type, $immediate); + } + + if (isset($this->positions[$name])) + { + $this->variables[$this->positions[$name]]['type'] = $type; + } + else + { + $this->variables[] = array('name' => $name, 'type' => $type); + $this->positions[$name] = count($this->variables) - 1; + } + } + + function assign($name, $value) + { + $this->add($name, array('value' => $value, 'assigned' => TRUE), TRUE); + $this->has_assignments = TRUE; + } + + function assigned_variables() + { + $tmp = array(); + + foreach ($this->variables as $v) + { + $type = $v['type']; + + if (is_array($type) && isset($type['assigned']) && $type['assigned']) + { + $tmp[] = "{$v['name']} = {$type['value']}"; + } + } + + return $tmp; + } + + function check($name, $immediate = FALSE) + { + $name = ''.$name; + + $found = !! $this->type($name); + + if ($found || $immediate) + { + return $found; + } + + return $this->parent ? $this->parent->check($name) : FALSE; + } + + function declared_variables() + { + $real_vars = array(); + $temp_vars = array(); + + foreach ($this->variables as $v) + { + if ($v['type'] === 'var') + { + if ($v['name']{0} === '_') + { + $temp_vars[] = $v['name']; + } + else + { + $real_vars[] = $v['name']; + } + } + } + + asort($real_vars); + asort($temp_vars); + + return array_merge($real_vars, $temp_vars); + } + + function find($name, $options = array()) + { + if ($this->check($name, $options)) + { + return TRUE; + } + + $this->add($name, 'var'); + + return FALSE; + } + + function free_variable($name, $reserve = TRUE) + { + $index = 0; + + while ($this->check(($temp = $this->temporary($name, $index)))) + { + $index++; + } + + if ($reserve) + { + $this->add($temp, 'var', TRUE); + } + + return $temp; + } + + function has_assignments() + { + return $this->has_assignments; + } + + function has_declarations() + { + return !! count($this->declared_variables()); + } + + function parameter($name) + { + if ($this->shared && $this->parent->check($name, TRUE)) + { + return; + } + + $this->add($name, 'param'); + } + + function temporary($name, $index) + { + if (strlen($name) > 1) + { + return '_'.$name.($index > 1 ? $index - 1 : ''); + } + else + { + $val = strval(base_convert($index + intval($name, 36), 10, 36)); + $val = preg_replace('/\d/', 'a', $val); + + return '_'.$val; + } + } + + function type($name) + { + foreach ($this->variables as $v) + { + if ($v['name'] === $name) + { + return $v['type']; + } + } + + return NULL; + } +} + +?> diff --git a/sparks/assets/1.5.1/libraries/coffeescript/SyntaxError.php b/sparks/assets/1.5.1/libraries/coffeescript/SyntaxError.php new file mode 100755 index 0000000..680c066 --- /dev/null +++ b/sparks/assets/1.5.1/libraries/coffeescript/SyntaxError.php @@ -0,0 +1,9 @@ + diff --git a/sparks/assets/1.5.1/libraries/coffeescript/Value.php b/sparks/assets/1.5.1/libraries/coffeescript/Value.php new file mode 100755 index 0000000..c1998ac --- /dev/null +++ b/sparks/assets/1.5.1/libraries/coffeescript/Value.php @@ -0,0 +1,20 @@ +v = $v; + } + + function __toString() + { + return $this->v; + } +} + +?> diff --git a/sparks/assets/1.5.1/libraries/coffeescript/yy/Access.php b/sparks/assets/1.5.1/libraries/coffeescript/yy/Access.php new file mode 100755 index 0000000..b014b0d --- /dev/null +++ b/sparks/assets/1.5.1/libraries/coffeescript/yy/Access.php @@ -0,0 +1,31 @@ +name = $name; + $this->name->as_key = TRUE; + + $this->soak = $tag === 'soak'; + + return $this; + } + + function compile($options, $level = NULL) + { + $name = $this->name->compile($options); + return preg_match(IDENTIFIER, $name) ? ".{$name}" : "[{$name}]"; + } + + function is_complex() + { + return FALSE; + } +} + +?> diff --git a/sparks/assets/1.5.1/libraries/coffeescript/yy/Arr.php b/sparks/assets/1.5.1/libraries/coffeescript/yy/Arr.php new file mode 100755 index 0000000..cb37656 --- /dev/null +++ b/sparks/assets/1.5.1/libraries/coffeescript/yy/Arr.php @@ -0,0 +1,69 @@ +objects = $objs ? $objs : array(); + + return $this; + } + + function assigns($name) + { + foreach ($this->objects as $obj) + { + if ($obj->assigns($name)) + { + return TRUE; + } + } + + return FALSE; + } + + function compile_node($options) + { + if ( ! count($options)) + { + return '[]'; + } + + $options['indent'] .= TAB; + $objs = $this->filter_implicit_objects($this->objects); + + if (($code = yy_Splat::compile_splatted_array($options, $objs))) + { + return $code; + } + + $code = array(); + + foreach ($objs as $obj) + { + $code[] = $obj->compile($options); + } + + $code = implode(', ', $code); + + if (strpos($code, "\n") !== FALSE) + { + return "[\n{$options['indent']}{$code}\n{$this->tab}]"; + } + else + { + return "[{$code}]"; + } + } + + function filter_implicit_objects() + { + return call_user_func_array(array(yy('Call'), __FUNCTION__), func_get_args()); + } +} + +?> diff --git a/sparks/assets/1.5.1/libraries/coffeescript/yy/Assign.php b/sparks/assets/1.5.1/libraries/coffeescript/yy/Assign.php new file mode 100755 index 0000000..73af70b --- /dev/null +++ b/sparks/assets/1.5.1/libraries/coffeescript/yy/Assign.php @@ -0,0 +1,350 @@ +variable = $variable; + $this->value = $value; + $this->context = $context; + $this->param = $options ? $options['param'] : NULL; + + $this->subpattern = isset($options['subpattern']) ? $options['subpattern'] : NULL; + + $tmp = $this->variable->unwrap_all(); + + $forbidden = in_array($name = isset($tmp->value) ? $tmp->value : NULL, Lexer::$STRICT_PROSCRIBED); + + if ($forbidden && $this->context !== 'object') + { + throw new SyntaxError("variable name may not be $name"); + } + + return $this; + } + + function assigns($name) + { + if ($this->context === 'object') + { + return $this->value->assigns($name); + } + else + { + return $this->variable->assigns($name); + } + } + + function compile_conditional($options) + { + list($left, $right) = $this->variable->cache_reference($options); + + if ( ! count($left->properties) && $left->base instanceof yy_Literal && $left->base->value !== 'this' && ! $options['scope']->check($left->base->value)) + { + throw new Error('the variable "'.$this->left->base->value.'" can\'t be assigned with '.$this->context.' because it has not been defined.'); + } + + if (strpos($this->context, '?') > -1) + { + $options['isExistentialEquals'] = TRUE; + } + + $tmp = yy('Op', substr($this->context, 0, -1), $left, yy('Assign', $right, $this->value, '=')); + + return $tmp->compile($options); + } + + function compile_node($options) + { + if (($is_value = ($this->variable instanceof yy_Value))) + { + if ($this->variable->is_array() || $this->variable->is_object()) + { + return $this->compile_pattern_match($options); + } + + if ($this->variable->is_splice()) + { + return $this->compile_splice($options); + } + + if (in_array($this->context, array('||=', '&&=', '?='), TRUE)) + { + return $this->compile_conditional($options); + } + } + + $name = $this->variable->compile($options, LEVEL_LIST); + + if ( ! $this->context) + { + if ( ! ( ($var_base = $this->variable->unwrap_all()) && $var_base->is_assignable())) + { + throw new SyntaxError('"'.$this->variable->compile($options).'" cannot be assigned.'); + } + + if ( ! (is_callable(array($var_base, 'has_properties')) && $var_base->has_properties())) + { + if ($this->param) + { + $options['scope']->add($name, 'var'); + } + else + { + $options['scope']->find($name); + } + } + } + + if ($this->value instanceof yy_Code && preg_match(METHOD_DEF, ''.$name, $match)) + { + if (isset($match[1]) && $match[1] !== '') + { + $this->value->klass = $match[1]; + } + + foreach (range(2, 5) as $i) + { + if (isset($match[$i]) && $match[$i] !== '') + { + $this->value->name = $match[$i]; + break; + } + } + } + + $val = $this->value->compile($options, LEVEL_LIST); + + if ($this->context === 'object') + { + return "{$name}: {$val}"; + } + + $val = $name.' '.($this->context ? $this->context : '=').' '.$val; + + return $options['level'] <= LEVEL_LIST ? $val : "({$val})"; + } + + function compile_pattern_match($options) + { + $top = $options['level'] === LEVEL_TOP; + $value = $this->value; + $objects = $this->variable->base->objects; + + if ( ! ($olen = count($objects))) + { + $code = $value->compile($options); + return $options['level'] >= LEVEL_OP ? "({$code})" : $code; + } + + $is_object = $this->variable->is_object(); + + if ($top && $olen === 1 && ! (($obj = $objects[0]) instanceof yy_Splat)) + { + if ($obj instanceof yy_Assign) + { + $idx = $obj->variable->base; + $obj = $obj->value; + } + else + { + if ($obj->base instanceof yy_Parens) + { + $tmp = yy('Value', $obj->unwrap_all()); + list($obj, $idx) = $tmp->cache_reference($options); + } + else + { + if ($is_object) + { + $idx = $obj->this ? $obj->properties[0]->name : $obj; + } + else + { + $idx = yy('Literal', 0); + } + } + } + + $acc = preg_match(IDENTIFIER, $idx->unwrap()->value); + $value = yy('Value', $value); + + if ($acc) + { + $value->properties[] = yy('Access', $idx); + } + else + { + $value->properties[] = yy('Index', $idx); + } + + $tmp = $obj->unwrap(); + $tmp = isset($tmp->value) ? $tmp->value : NULL; + + if (in_array($tmp, Lexer::$COFFEE_RESERVED)) + { + throw new SyntaxError('assignment to a reserved word: '.$obj->compile($options).' = '.$value->compile($options)); + } + + return yy('Assign', $obj, $value, NULL, array('param' => $this->param))->compile($options, LEVEL_TOP); + } + + $vvar = $value->compile($options, LEVEL_LIST); + $assigns = array(); + $splat = FALSE; + + if ( ! preg_match(IDENTIFIER, $vvar) || $this->variable->assigns($vvar)) + { + $assigns[] = ($ref = $options['scope']->free_variable('ref')).' = '.$vvar; + $vvar = $ref; + } + + foreach ($objects as $i => $obj) + { + $idx = $i; + + if ($is_object) + { + if ($obj instanceof yy_Assign) + { + $idx = $obj->variable->base; + $obj = $obj->value; + } + else + { + if ($obj->base instanceof yy_Parens) + { + $tmp = yy('Value', $obj->unwrap_all()); + list($obj, $idx) = $tmp->cache_reference($options); + } + else + { + $idx = $obj->this ? $obj->properties[0]->name : $obj; + } + } + } + + if ( ! $splat && ($obj instanceof yy_Splat)) + { + $name = $obj->name->unwrap()->value; + $obj = $obj->unwrap(); + + $val = "{$olen} <= {$vvar}.length ? ".utility('slice').".call({$vvar}, {$i}"; + $ivar = 'undefined'; + + if (($rest = $olen - $i - 1)) + { + $ivar = $options['scope']->free_variable('i'); + $val .= ", {$ivar} = {$vvar}.length - {$rest}) : ({$ivar} = {$i}, [])"; + } + else + { + $val .= ') : []'; + } + + $val = yy('Literal', $val); + $splat = "{$ivar}++"; + } + else + { + $name = $obj->unwrap(); + $name = isset($name->value) ? $name->value : NULL; + + if ($obj instanceof yy_Splat) + { + $obj = $obj->name->compile($options); + throw new SyntaxError("multiple splats are disallowed in an assignment: {$obj}..."); + } + + if (is_numeric($idx)) + { + $idx = yy('Literal', $splat ? $splat : $idx); + $acc = FALSE; + } + else + { + $acc = $is_object ? preg_match(IDENTIFIER, $idx->unwrap()->value) : 0; + } + + $val = yy('Value', yy('Literal', $vvar), array($acc ? yy('Access', $idx) : yy('Index', $idx))); + } + + if (isset($name) && $name && in_array($name, Lexer::$COFFEE_RESERVED)) + { + throw new SyntaxError("assignment to a reserved word: ".$obj->compile($options).' = '.$val->compile($options)); + } + + $tmp = yy('Assign', $obj, $val, NULL, array('param' => $this->param, 'subpattern' => TRUE)); + $assigns[] = $tmp->compile($options, LEVEL_TOP); + } + + if ( ! ($top || $this->subpattern)) + { + $assigns[] = $vvar; + } + + $code = implode(', ', $assigns); + + return $options['level'] < LEVEL_LIST ? $code : "({$code})"; + } + + function compile_splice($options) + { + $tmp = array_pop($this->variable->properties); + + $from = $tmp->range->from; + $to = $tmp->range->to; + $exclusive = $tmp->range->exclusive; + + $name = $this->variable->compile($options); + + list($from_decl, $from_ref) = $from ? $from->cache($options, LEVEL_OP) : array('0', '0'); + + if ($to) + { + if (($from && $from->is_simple_number()) && $to->is_simple_number()) + { + $to = intval($to->compile($options)) - intval($from_ref); + + if ( ! $exclusive) + { + $to++; + } + } + else + { + $to = $to->compile($options, LEVEL_ACCESS).' - '. $from_ref; + + if ( ! $exclusive) + { + $to .= ' + 1'; + } + } + } + else + { + $to = '9e9'; + } + + list($val_def, $val_ref) = $this->value->cache($options, LEVEL_LIST); + + $code = "[].splice.apply({$name}, [{$from_decl}, {$to}].concat({$val_def})), {$val_ref}"; + return $options['level'] > LEVEL_TOP ? "({$code})" : $code; + } + + function is_statement($options) + { + return isset($options['level']) && $options['level'] === LEVEL_TOP && $this->context && strpos($this->context, '?') > -1; + } + + function unfold_soak($options) + { + return unfold_soak($options, $this, 'variable'); + } +} + +?> diff --git a/sparks/assets/1.5.1/libraries/coffeescript/yy/Base.php b/sparks/assets/1.5.1/libraries/coffeescript/yy/Base.php new file mode 100755 index 0000000..e8e9a7b --- /dev/null +++ b/sparks/assets/1.5.1/libraries/coffeescript/yy/Base.php @@ -0,0 +1,295 @@ +to_string(); + } + + function cache($options, $level = NULL, $reused = NULL) + { + if ( ! $this->is_complex()) + { + $ref = $level ? $this->compile($options, $level) : $this; + return array($ref, $ref); + } + else + { + $ref = yy('Literal', $reused ? $reused : $options['scope']->free_variable('ref')); + $sub = yy('Assign', $ref, $this); + + if ($level) + { + return array($sub->compile($options, $level), $ref->value); + } + else + { + return array($sub, $ref); + } + } + } + + function compile($options, $level = NULL) + { + if (isset($level)) + { + $options['level'] = $level; + } + + if ( ! ($node = $this->unfold_soak($options))) + { + $node = $this; + } + + $node->tab = $options['indent']; + + if ($options['level'] === LEVEL_TOP || ! $node->is_statement($options)) + { + return $node->compile_node($options); + } + + return $node->compile_closure($options); + } + + function compile_closure($options) + { + if ($this->jumps()) + { + throw new SyntaxError('cannot use a pure statement in an expression.'); + } + + $options['sharedScope'] = TRUE; + + $closure = yy_Closure::wrap($this); + + return $closure->compile_node($options); + } + + function compile_loop_reference($options, $name) + { + $src = $tmp = $this->compile($options, LEVEL_LIST); + + if ( ! ( ($src === 0 || $src === '') || preg_match(IDENTIFIER, $src) && + $options['scope']->check($src, TRUE))) + { + $src = ($tmp = $options['scope']->free_variable($name)).' = '.$src; + } + + return array($src, $tmp); + } + + function contains($pred) + { + $contains = FALSE; + + if (is_string($pred)) + { + $tmp = __NAMESPACE__.'\\'.$pred; + + $pred = function($node) use ($tmp) + { + return call_user_func($tmp, $node); + }; + } + + $this->traverse_children(FALSE, function($node) use ( & $contains, & $pred) + { + if ($pred($node)) + { + $contains = TRUE; + return FALSE; + } + }); + + return $contains; + } + + function contains_type($type) + { + return ($this instanceof $type) || $this->contains(function($node) use ( & $type) + { + return $node instanceof $type; + }); + } + + function each_child($func) + { + if ( ! ($this->children)) + { + return $this; + } + + foreach ($this->children as $i => $attr) + { + if (isset($this->{$attr}) && $this->{$attr}) + { + foreach (flatten(array($this->{$attr})) as $i => $child) + { + if ($func($child) === FALSE) + { + break 2; + } + } + } + } + + return $this; + } + + function invert() + { + return yy('Op', '!', $this); + } + + function assigns() + { + return FALSE; + } + + function is_assignable() + { + return FALSE; + } + + function is_complex() + { + return TRUE; + } + + function is_chainable() + { + return FALSE; + } + + function is_object() + { + return FALSE; + } + + function is_statement() + { + return FALSE; + } + + function is_undefined() + { + return FALSE; + } + + function jumps() + { + return FALSE; + } + + function last_non_comment($list) + { + $i = count($list); + + while ($i--) + { + if ( ! ($list[$i] instanceof yy_Comment)) + { + return $list[$i]; + } + } + + return NULL; + } + + function make_return($res = NULL) + { + $me = $this->unwrap_all(); + + if ($res) + { + return yy('Call', yy('Literal', "{$res}.push"), array($me)); + } + else + { + return yy('Return', $me); + } + } + + function to_string($idt = '', $name = NULL) + { + if ($name === NULL) + { + $name = get_class($this); + } + + $tree = "\n{$idt}{$name}"; + + if ($this->soak) + { + $tree .= '?'; + } + + $this->each_child(function($node) use ($idt, & $tree) + { + $tree .= $node->to_string($idt.TAB); + }); + + return $tree; + } + + function traverse_children($cross_scope, $func) + { + $this->each_child(function($child) use ($cross_scope, & $func) + { + if ($func($child) === FALSE) + { + return FALSE; + } + + return $child->traverse_children($cross_scope, $func); + }); + } + + function unfold_soak($options) + { + return FALSE; + } + + function unwrap() + { + return $this; + } + + function unwrap_all() + { + $node = $this; + + while ($node !== ($node = $node->unwrap())) + { + continue; + } + + return $node; + } +} + +?> diff --git a/sparks/assets/1.5.1/libraries/coffeescript/yy/Block.php b/sparks/assets/1.5.1/libraries/coffeescript/yy/Block.php new file mode 100755 index 0000000..3b2e34d --- /dev/null +++ b/sparks/assets/1.5.1/libraries/coffeescript/yy/Block.php @@ -0,0 +1,294 @@ +expressions = compact(flatten($nodes)); + + return $this; + } + + function compile($options, $level = NULL) + { + if (isset($options['scope'])) + { + return parent::compile($options, $level); + } + else + { + return $this->compile_root($options); + } + } + + function compile_node($options) + { + $this->tab = $options['indent']; + + $top = $options['level'] === LEVEL_TOP; + $codes = array(); + + foreach ($this->expressions as $i => $node) + { + $node = $node->unwrap_all(); + $node = ($tmp = $node->unfold_soak($options)) ? $tmp : $node; + + if ($node instanceof yy_Block) + { + $codes[] = $node->compile_node($options); + } + else if ($top) + { + $node->front = TRUE; + $code = $node->compile($options); + + if ( ! $node->is_statement($options)) + { + $code = "{$this->tab}{$code};"; + + if ($node instanceof yy_Literal) + { + $code = "{$code}\n"; + } + } + + $codes[] = $code; + } + else + { + $codes[] = $node->compile($options, LEVEL_LIST); + } + } + + if ($top) + { + if (isset($this->spaced) && $this->spaced) + { + return "\n".implode("\n\n", $codes)."\n"; + } + else + { + return implode("\n", $codes); + } + } + + $code = ($tmp = implode(', ', $codes)) ? $tmp : 'void 0'; + + if (count($codes) && $options['level'] >= LEVEL_LIST) + { + return "({$code})"; + } + else + { + return $code; + } + } + + function compile_root($options) + { + $options['indent'] = isset($options['bare']) && $options['bare'] ? '' : TAB; + $options['scope'] = new Scope(NULL, $this, NULL); + $options['level'] = LEVEL_TOP; + + $this->spaced = TRUE; + $prelude = ''; + + if ( ! (isset($options['bare']) && $options['bare'])) + { + $prelude_exps = array(); + + foreach ($this->expressions as $i => $exp) + { + if ( ! ($exp->unwrap() instanceof yy_Comment)) + { + break; + } + + $prelude_exps[] = $exp; + } + + $rest = array_slice($this->expressions, count($prelude_exps)); + $this->expressions = $prelude_exps; + + if ($prelude_exps) + { + $prelude = $this->compile_node(array_merge($options, array('indent' => '')))."\n"; + } + + $this->expressions = $rest; + } + + $code = $this->compile_with_declarations($options); + + if (isset($options['bare']) && $options['bare']) + { + return $code; + } + + return "{$prelude}(function() {\n{$code}\n}).call(this);\n"; + } + + function compile_with_declarations($options) + { + $code = $post = ''; + + foreach ($this->expressions as $i => & $expr) + { + $expr = $expr->unwrap(); + + if ( ! ($expr instanceof yy_Comment || $expr instanceof yy_Literal)) + { + break; + } + } + + $options = array_merge($options, array('level' => LEVEL_TOP)); + + if ($i) + { + $rest = array_splice($this->expressions, $i, count($this->expressions)); + + list($spaced, $this->spaced) = array(isset($this->spaced) && $this->spaced, FALSE); + list($code, $this->spaced) = array($this->compile_node($options), $spaced); + + $this->expressions = $rest; + } + + $post = $this->compile_node($options); + + $scope = $options['scope']; + + if ($scope->expressions === $this) + { + $declars = $scope->has_declarations(); + $assigns = $scope->has_assignments(); + + if ($declars or $assigns) + { + if ($i) + { + $code .= "\n"; + } + + $code .= $this->tab.'var '; + + if ($declars) + { + $code .= implode(', ', $scope->declared_variables()); + } + + if ($assigns) + { + if ($declars) + { + $code .= ",\n{$this->tab}".TAB; + } + + $code .= implode(",\n{$this->tab}".TAB, $scope->assigned_variables()); + } + + $code .= ";\n"; + } + } + + return $code.$post; + } + + function is_empty() + { + return ! count($this->expressions); + } + + function is_statement($options) + { + foreach ($this->expressions as $i => $expr) + { + if ($expr->is_statement($options)) + { + return TRUE; + } + } + + return FALSE; + } + + function jumps($options = array()) + { + foreach ($this->expressions as $i => $expr) + { + if ($expr->jumps($options)) + { + return $expr; + } + } + + return FALSE; + } + + function make_return($res = NULL) + { + $len = count($this->expressions); + + while ($len--) + { + $expr = $this->expressions[$len]; + + if ( ! ($expr instanceof yy_Comment)) + { + $this->expressions[$len] = $expr->make_return($res); + + if ($expr instanceof yy_Return && ! $expr->expression) + { + return array_splice($this->expressions, $len, 1); + } + + break; + } + } + + return $this; + } + + function pop() + { + return array_pop($this->expressions); + } + + function push($node) + { + $this->expressions[] = $node; + return $this; + } + + function unshift($node) + { + array_unshift($this->expressions, $node); + return $this; + } + + function unwrap() + { + return count($this->expressions) === 1 ? $this->expressions[0] : $this; + } + + static function wrap($nodes) + { + if ( ! is_array($nodes)) + { + $nodes = array($nodes); + } + + if (count($nodes) === 1 && $nodes[0] instanceof yy_Block) + { + return $nodes[0]; + } + + return yy('Block', $nodes); + } +} + +?> diff --git a/sparks/assets/1.5.1/libraries/coffeescript/yy/Call.php b/sparks/assets/1.5.1/libraries/coffeescript/yy/Call.php new file mode 100755 index 0000000..0701145 --- /dev/null +++ b/sparks/assets/1.5.1/libraries/coffeescript/yy/Call.php @@ -0,0 +1,283 @@ +args = $args; + $this->is_new = FALSE; + $this->is_super = $variable === 'super'; + $this->variable = $this->is_super() ? NULL : $variable; + $this->soak = $soak; + + return $this; + } + + function compile_node($options) + { + if ($this->variable) + { + $this->variable->front = $this->front; + } + + if (($code = yy_Splat::compile_splatted_array($options, $this->args, TRUE))) + { + return $this->compile_splat($options, $code); + } + + $args = $this->filter_implicit_objects($this->args); + $tmp = array(); + + foreach ($args as $arg) + { + $tmp[] = $arg->compile($options, LEVEL_LIST); + } + + $args = implode(', ', $tmp); + + if ($this->is_super()) + { + return $this->super_reference($options).'.call(this'.($args ? ', '.$args : '').')'; + } + else + { + return ($this->is_new() ? 'new ' : '').$this->variable->compile($options, LEVEL_ACCESS)."({$args})"; + } + } + + function compile_super($args, $options) + { + return $this->super_reference($options).'.call(this'.(count($args) ? ', ' : '').$args.')'; + } + + function compile_splat($options, $splat_args) + { + if ($this->is_super()) + { + return $this->super_reference($options).'.apply(this, '.$splat_args.')'; + } + + if ($this->is_new()) + { + $idt = $this->tab.TAB; + + return + "(function(func, args, ctor) {\n" + . "{$idt}ctor.prototype = func.prototype;\n" + . "{$idt}var child = new ctor, result = func.apply(child, args), t = typeof result;\n" + . "{$idt}return t == \"object\" || t == \"function\" ? result || child : child;\n" + . "{$this->tab}})(".$this->variable->compile($options, LEVEL_LIST).", $splat_args, function(){})"; + } + + $base = yy('Value', $this->variable); + + if (($name = array_pop($base->properties)) && $base->is_complex()) + { + $ref = $options['scope']->free_variable('ref'); + $fun = "($ref = ".$base->compile($options, LEVEL_LIST).')'.$name->compile($options).''; + } + else + { + $fun = $base->compile($options, LEVEL_ACCESS); + $fun = preg_match(SIMPLENUM, $fun) ? "($fun)" : $fun; + + if ($name) + { + $ref = $fun; + $fun .= $name->compile($options); + } + else + { + $ref = NULL; + } + } + + $ref = $ref === NULL ? 'null' : $ref; + + return "{$fun}.apply({$ref}, {$splat_args})"; + } + + function is_new($set = NULL) + { + if ($set !== NULL) + { + $this->is_new = !! $set; + } + + return $this->is_new; + } + + function is_super() + { + return $this->is_super; + } + + function filter_implicit_objects($list) + { + $nodes = array(); + + foreach ($list as $node) + { + if ( ! ($node->is_object() && $node->base->generated)) + { + $nodes[] = $node; + continue; + } + + $obj = NULL; + + foreach ($node->base->properties as $prop) + { + if (($prop instanceof yy_Assign) || $prop instanceof yy_Comment) + { + if ( ! $obj) + { + $nodes[] = ($obj = yy('Obj', array(), TRUE)); + } + + $obj->properties[] = $prop; + } + else + { + $nodes[] = $prop; + $obj = NULL; + } + } + } + + return $nodes; + } + + function new_instance() + { + $base = isset($this->variable->base) ? $this->variable->base : $this->variable; + + if (($base instanceof yy_Call) && ! $base->is_new()) + { + $base->new_instance(); + } + else + { + $this->is_new = TRUE; + } + + return $this; + } + + function super_reference($options) + { + $method = $options['scope']->method; + + if ($method === NULL) + { + throw new SyntaxError('cannot call super outside of a function.'); + } + + $name = isset($method->name) ? $method->name : NULL; + + if ($name === NULL) + { + throw new SyntaxError('cannot call super on an anonymous function.'); + } + + if (isset($method->klass) && $method->klass) + { + $accesses = array(yy('Access', yy('Literal', '__super__'))); + + if (isset($method->static) && $method->static) + { + $accesses[] = yy('Access', yy('Literal', 'constructor')); + } + + $accesses[] = yy('Access', yy('Literal', $name)); + + return yy('Value', yy('Literal', $method->klass), $accesses)->compile($options); + } + else + { + return $name.'.__super__.constructor'; + } + } + + function unfold_soak($options) + { + if ($this->soak) + { + if ($this->variable) + { + if ($ifn = unfold_soak($options, $this, 'variable')) + { + return $ifn; + } + + $tmp = yy('Value', $this->variable); + list($left, $rite) = $tmp->cache_reference($options); + } + else + { + $left = yy('Literal', $this->super_reference($options)); + $rite = yy('Value', $left); + } + + $rite = yy('Call', $rite, $this->args); + $rite->is_new($this->is_new()); + $left = yy('Literal', 'typeof '.$left->compile($options).' === "function"'); + + return yy('If', $left, yy('Value', $rite), array('soak' => TRUE)); + } + + $call = $this; + $list = array(); + + while (TRUE) + { + if ($call->variable instanceof yy_Call) + { + $list[] = $call; + $call = $call->variable; + + continue; + } + + if ( ! ($call->variable instanceof yy_Value)) + { + break; + } + + $list[] = $call; + + if ( ! (($call = $call->variable->base) instanceof yy_Call)) + { + break; + } + } + + foreach (array_reverse($list) as $call) + { + if (isset($ifn)) + { + if ($call->variable instanceof yy_Call) + { + $call->variable = $ifn; + } + else + { + $call->variable->base = $ifn; + } + } + + $ifn = unfold_soak($options, $call, 'variable'); + } + + return isset($ifn) ? $ifn : NULL; + } +} + +?> diff --git a/sparks/assets/1.5.1/libraries/coffeescript/yy/Class.php b/sparks/assets/1.5.1/libraries/coffeescript/yy/Class.php new file mode 100755 index 0000000..12bb873 --- /dev/null +++ b/sparks/assets/1.5.1/libraries/coffeescript/yy/Class.php @@ -0,0 +1,282 @@ +variable = $variable; + $this->parent = $parent; + + $this->body = $body === NULL ? yy('Block') : $body; + $this->body->class_body = TRUE; + + $this->bound_funcs = array(); + + return $this; + } + + function add_bound_functions($options) + { + if ($this->bound_funcs) + { + foreach ($this->bound_funcs as $bvar) + { + $lhs = yy('Value', yy('Literal', 'this'), array(yy('Access', $bvar)))->compile($options); + $this->ctor->body->unshift(yy('Literal', "{$lhs} = ".utility('bind')."({$lhs}, this)")); + } + } + } + + function add_properties($node, $name, $options) + { + $props = array_slice($node->base->properties, 0); + $exprs = array(); + + while ($assign = array_shift($props)) + { + if ($assign instanceof yy_Assign) + { + $base = $assign->variable->base; + $func = $assign->value; + + $assign->context = NULL; + + if ($base->value === 'constructor') + { + if ($this->ctor) + { + throw new Error('cannot define more than one constructor in a class'); + } + + if (isset($func->bound) && $func->bound) + { + throw new Error('cannot define a constructor as a bound functions'); + } + + if ($func instanceof yy_Code) + { + $assign = $this->ctor = $func; + } + else + { + $this->external_ctor = $options['scope']->free_variable('class'); + $assign = yy('Assign', yy('Literal', $this->external_ctor), $func); + } + } + else + { + if (isset($assign->variable->this) && $assign->variable->this) + { + $func->static = TRUE; + + if (isset($func->bound) && $func->bound) + { + $func->context = $name; + } + } + else + { + $assign->variable = yy('Value', yy('Literal', $name), array( yy('Access', yy('Literal', 'prototype')), yy('Access', $base) )); + + if ($func instanceof yy_Code && isset($func->bound) && $func->bound) + { + $this->bound_funcs[] = $base; + $func->bound = FALSE; + } + } + } + } + + $exprs[] = $assign; + } + + return compact($exprs); + } + + function compile_node($options) + { + $decl = $this->determine_name(); + + $name = $decl ? $decl : '_Class'; + + if (isset($name->reserved) && $name->reserved) + { + $name = '_'.$name; + } + + $lname = yy('Literal', $name); + + $this->hoist_directive_prologue(); + $this->set_context($name); + $this->walk_body($name, $options); + $this->ensure_constructor($name); + $this->body->spaced = TRUE; + + if ( ! ($this->ctor instanceof yy_Code)) + { + array_unshift($this->body->expressions, $this->ctor); + } + + if ($decl) + { + array_unshift($this->body->expressions, yy('Assign', yy('Value', yy('Literal', $name), array(yy('Access', yy('Literal', 'name')))), yy('Literal', "'{$name}'"))); + } + + $this->body->expressions[] = $lname; + $this->body->expressions = array_merge($this->directives, $this->body->expressions); + + $this->add_bound_functions($options); + + $call = yy_Closure::wrap($this->body); + + if ($this->parent) + { + $this->super_class = yy('Literal', $options['scope']->free_variable('super', FALSE)); + array_unshift($this->body->expressions, yy('Extends', $lname, $this->super_class)); + $call->args[] = $this->parent; + + if (isset($call->variable->params)) + { + $params = & $call->variable->params; + } + else + { + $params = & $call->variable->base->params; + } + + $params[] = yy('Param', $this->super_class); + } + + $klass = yy('Parens', $call, TRUE); + + if ($this->variable) + { + $klass = yy('Assign', $this->variable, $klass); + } + + return $klass->compile($options); + } + + function determine_name() + { + if ( ! (isset($this->variable) && $this->variable)) + { + return NULL; + } + + if (($tail = last($this->variable->properties))) + { + $decl = $tail instanceof yy_Access ? $tail->name->value : NULL; + } + else + { + $decl = $this->variable->base->value; + } + + if (in_array($decl, Lexer::$STRICT_PROSCRIBED, TRUE)) + { + throw new SyntaxError("variable name may not be $decl"); + } + + $decl = $decl ? (preg_match(IDENTIFIER, $decl) ? $decl : NULL) : NULL; + + return $decl; + } + + function ensure_constructor($name) + { + if ( ! (isset($this->ctor) && $this->ctor)) + { + $this->ctor = yy('Code'); + + if ($this->parent) + { + $this->ctor->body->push(yy('Literal', "{$name}.__super__.constructor.apply(this, arguments)")); + } + + if (isset($this->external_ctor) && $this->external_ctor) + { + $this->ctor->body->push(yy('Literal', "{$this->external_ctor}.apply(this, arguments)")); + } + + $this->ctor->body->make_return(); + + array_unshift($this->body->expressions, $this->ctor); + } + + $this->ctor->ctor = $this->ctor->name = $name; + + $this->ctor->klass = NULL; + $this->ctor->no_return = TRUE; + } + + function hoist_directive_prologue() + { + $index = 0; + $expressions = $this->body->expressions; + + while (isset($expressions[$index]) && ($node = $expressions[$index]) && ( ($node instanceof yy_Comment) || ($node instanceof yy_Value) && $node->is_string() )) + { + $index++; + } + + $this->directives = array_slice($expressions, 0, $index); + } + + function set_context($name) + { + $this->body->traverse_children(FALSE, function($node) use ($name) + { + if (isset($node->class_body) && $node->class_body) + { + return FALSE; + } + + if ($node instanceof yy_Literal && ''.$node->value === 'this') + { + $node->value = $name; + } + else if ($node instanceof yy_Code) + { + $node->klass = $name; + + if ($node->bound) + { + $node->context = $name; + } + } + }); + } + + function walk_body($name, $options) + { + $self = $this; + + $this->traverse_children(FALSE, function($child) use ($name, $options, & $self) + { + if ($child instanceof yy_Class) + { + return FALSE; + } + + if ($child instanceof yy_Block) + { + foreach (($exps = $child->expressions) as $i => $node) + { + if ($node instanceof yy_Value && $node->is_object(TRUE)) + { + $exps[$i] = $self->add_properties($node, $name, $options); + } + } + + $child->expressions = $exps = flatten($exps); + } + }); + } +} + +?> diff --git a/sparks/assets/1.5.1/libraries/coffeescript/yy/Closure.php b/sparks/assets/1.5.1/libraries/coffeescript/yy/Closure.php new file mode 100755 index 0000000..5751ab0 --- /dev/null +++ b/sparks/assets/1.5.1/libraries/coffeescript/yy/Closure.php @@ -0,0 +1,49 @@ +jumps()) + { + return $expressions; + } + + $func = yy('Code', array(), yy_Block::wrap(array($expressions))); + $args = array(); + + if (($mentions_args = $expressions->contains('yy_Closure::literal_args')) || + $expressions->contains('yy_Closure::literal_this')) + { + $meth = yy('Literal', $mentions_args ? 'apply' : 'call'); + $args = array(yy('Literal', 'this')); + + if ($mentions_args) + { + $args[] = yy('Literal', 'arguments'); + } + + $func = yy('Value', $func, array(yy('Access', $meth))); + } + + $func->no_return = $no_return; + $call = yy('Call', $func, $args); + + return $statement ? yy_Block::wrap(array($call)) : $call; + } + + static function literal_args($node) + { + return ($node instanceof yy_Literal) && (''.$node->value === 'arguments') && ! $node->as_key; + } + + static function literal_this($node) + { + return (($node instanceof yy_Literal) && (''.$node->value === 'this') && ! $node->as_key) || + ($node instanceof yy_Code && $node->bound); + } +} + +?> diff --git a/sparks/assets/1.5.1/libraries/coffeescript/yy/Code.php b/sparks/assets/1.5.1/libraries/coffeescript/yy/Code.php new file mode 100755 index 0000000..a1c3d2d --- /dev/null +++ b/sparks/assets/1.5.1/libraries/coffeescript/yy/Code.php @@ -0,0 +1,200 @@ +params = $params ? $params : array(); + $this->body = $body ? $body : yy('Block'); + $this->bound = $tag === 'boundfunc'; + $this->context = $this->bound ? '_this' : NULL; + + return $this; + } + + function compile_node($options) + { + $options['scope'] = new Scope($options['scope'], $this->body, $this); + $options['scope']->shared = del($options, 'sharedScope'); + $options['indent'] .= TAB; + + unset($options['bare']); + unset($options['isExistentialEquals']); + + $params = array(); + $exprs = array(); + + foreach ($this->param_names() as $name) + { + if ( ! $options['scope']->check($name)) + { + $options['scope']->parameter($name); + } + } + + foreach ($this->params as $param) + { + if ($param->splat) + { + if (isset($param->name->value) && $param->name->value) + { + $options['scope']->add($param->name->value, 'var', TRUE); + } + + $params = array(); + + foreach ($this->params as $p) + { + $params[] = $p->as_reference($options); + } + + $splats = yy('Assign', yy('Value', yy('Arr', $params)), yy('Value', yy('Literal', 'arguments'))); + + break; + } + } + + foreach ($this->params as $param) + { + if ($param->is_complex()) + { + $val = $ref = $param->as_reference($options); + + if (isset($param->value) && $param->value) + { + $val = yy('Op', '?', $ref, $param->value); + } + + $exprs[] = yy('Assign', yy('Value', $param->name), $val, '=', array('param' => TRUE)); + } + else + { + $ref = $param; + + if ($param->value) + { + $lit = yy('Literal', $ref->name->value.' == null'); + $val = yy('Assign', yy('Value', $param->name), $param->value, '='); + + $exprs[] = yy('If', $lit, $val); + } + } + + if ( ! (isset($splats) && $splats)) + { + $params[] = $ref; + } + } + + $was_empty = $this->body->is_empty(); + + if (isset($splats) && $splats) + { + array_unshift($exprs, $splats); + } + + if ($exprs) + { + foreach (array_reverse($exprs) as $expr) + { + array_unshift($this->body->expressions, $expr); + } + } + + foreach ($params as $i => $p) + { + $options['scope']->parameter(($params[$i] = $p->compile($options))); + } + + $uniqs = array(); + + foreach ($this->param_names() as $name) + { + if (in_array($name, $uniqs)) + { + throw new SyntaxError("multiple parameters named $name"); + } + + $uniqs[] = $name; + } + + if ( ! ($was_empty || $this->no_return)) + { + $this->body->make_return(); + } + + if ($this->bound) + { + if (isset($options['scope']->parent->method->bound) && $options['scope']->parent->method->bound) + { + $this->bound = $this->context = $options['scope']->parent->method->context; + } + else if ( ! (isset($this->static) && $this->static)) + { + $options['scope']->parent->assign('_this', 'this'); + } + } + + $idt = $options['indent']; + $code = 'function'; + + if ($this->ctor) + { + $code .= ' '.$this->name; + } + + $code .= '('.implode(', ', $params).') {'; + + if ( ! $this->body->is_empty()) + { + $code .= "\n".$this->body->compile_with_declarations($options)."\n{$this->tab}"; + } + + $code .= '}'; + + if ($this->ctor) + { + return $this->tab.$code; + } + + return ($this->front || $options['level'] >= LEVEL_ACCESS) ? "({$code})" : $code; + } + + function param_names() + { + $names = array(); + + foreach ($this->params as $param) + { + $names = array_merge($names, (array) $param->names()); + } + + return $names; + } + + function is_statement() + { + return !! $this->ctor; + } + + function jumps() + { + return FALSE; + } + + function traverse_children($cross_scope, $func) + { + if ($cross_scope) + { + return parent::traverse_children($cross_scope, $func); + } + + return NULL; + } +} + +?> diff --git a/sparks/assets/1.5.1/libraries/coffeescript/yy/Comment.php b/sparks/assets/1.5.1/libraries/coffeescript/yy/Comment.php new file mode 100755 index 0000000..074ec84 --- /dev/null +++ b/sparks/assets/1.5.1/libraries/coffeescript/yy/Comment.php @@ -0,0 +1,37 @@ +comment = $comment; + + return $this; + } + + function compile_node($options, $level = NULL) + { + $code = '/*'.multident($this->comment, $this->tab)."\n{$this->tab}*/\n"; + + if ($level === LEVEL_TOP || $options['level'] === LEVEL_TOP) + { + $code = $options['indent'].$code; + } + + return $code; + } + + function is_statement() + { + return TRUE; + } + + function make_return() + { + return $this; + } +} + +?> diff --git a/sparks/assets/1.5.1/libraries/coffeescript/yy/Existence.php b/sparks/assets/1.5.1/libraries/coffeescript/yy/Existence.php new file mode 100755 index 0000000..dc6726c --- /dev/null +++ b/sparks/assets/1.5.1/libraries/coffeescript/yy/Existence.php @@ -0,0 +1,42 @@ +expression = $expression; + + return $this; + } + + function compile_node($options = array()) + { + $this->expression->front = $this->front; + $code = $this->expression->compile($options, LEVEL_OP); + + if (preg_match(IDENTIFIER, $code) && ! $options['scope']->check($code)) + { + list($cmp, $cnj) = $this->negated ? array('===', '||') : array('!==', '&&'); + + $code = "typeof {$code} {$cmp} \"undefined\" {$cnj} {$code} {$cmp} null"; + } + else + { + $code = "{$code} ".($this->negated ? '==' : '!=').' null'; + } + + return (isset($options['level']) && $options['level'] <= LEVEL_COND) ? $code : "({$code})"; + } + + function invert() + { + $this->negated = ! $this->negated; + return $this; + } +} + +?> diff --git a/sparks/assets/1.5.1/libraries/coffeescript/yy/Extends.php b/sparks/assets/1.5.1/libraries/coffeescript/yy/Extends.php new file mode 100755 index 0000000..b871927 --- /dev/null +++ b/sparks/assets/1.5.1/libraries/coffeescript/yy/Extends.php @@ -0,0 +1,26 @@ +child = $child; + $this->parent = $parent; + + return $this; + } + + function compile($options) + { + $tmp = yy('Call', yy('Value', yy('Literal', utility('extends'))), + array($this->child, $this->parent)); + + return $tmp->compile($options); + } +} + +?> diff --git a/sparks/assets/1.5.1/libraries/coffeescript/yy/For.php b/sparks/assets/1.5.1/libraries/coffeescript/yy/For.php new file mode 100755 index 0000000..d96e708 --- /dev/null +++ b/sparks/assets/1.5.1/libraries/coffeescript/yy/For.php @@ -0,0 +1,248 @@ +source = $source['source']; + $this->guard = isset($source['guard']) ? $source['guard'] : NULL; + $this->step = isset($source['step']) ? $source['step'] : NULL; + $this->name = isset($source['name']) ? $source['name'] : NULL; + $this->index = isset($source['index']) ? $source['index'] : NULL; + + $this->body = yy_Block::wrap(array($body)); + + $this->own = (isset($source['own']) && $source['own']); + $this->object = (isset($source['object']) && $source['object']); + + if ($this->object) + { + $tmp = $this->name; + $this->name = $this->index; + $this->index = $tmp; + } + + if ($this->index instanceof yy_Value) + { + throw SyntaxError('index cannot be a pattern matching expression'); + } + + $this->range = $this->source instanceof yy_Value && $this->source->base instanceof yy_Range && + ! count($this->source->properties); + + $this->pattern = $this->name instanceof yy_Value; + + if ($this->range && $this->index) + { + throw SyntaxError('indexes do not apply to range loops'); + } + + if ($this->range && $this->pattern) + { + throw SyntaxError('cannot pattern match over range loops'); + } + + $this->returns = FALSE; + + return $this; + } + + function compile_node($options) + { + $body = yy_Block::wrap(array($this->body)); + + $last_jumps = last($body->expressions); + $last_jumps = $last_jumps ? $last_jumps->jumps() : FALSE; + + if ($last_jumps && $last_jumps instanceof yy_Return) + { + $this->returns = FALSE; + } + + if ($this->range) + { + $source = $this->source->base; + } + else + { + $source = $this->source; + } + + $scope = $options['scope']; + + $name = $this->name ? $this->name->compile($options, LEVEL_LIST) : FALSE; + $index = $this->index ? $this->index->compile($options, LEVEL_LIST) : FALSE; + + if ($name && ! $this->pattern) + { + $scope->find($name, array('immediate' => TRUE)); + } + + if ($index) + { + $scope->find($index, array('immediate' => TRUE)); + } + + if ($this->returns) + { + $rvar = $scope->free_variable('results'); + } + + $ivar = $this->object ? $index : $scope->free_variable('i'); + $kvar = $this->range ? ($name ? $name : ($index ? $index : $ivar)) : ($index ? $index : $ivar); + $kvar_assign = $kvar !== $ivar ? "{$kvar} = " : ''; + + if ($this->step && ! $this->range) + { + $stepvar = $scope->free_variable('step'); + } + + if ($this->pattern) + { + $name = $ivar; + } + + $var_part = ''; + $guard_part = ''; + $def_part = ''; + + $idt1 = $this->tab.TAB; + + if ($this->range) + { + $for_part = $source->compile(array_merge($options, array('index' => $ivar, 'name' => $name, 'step' => $this->step))); + } + else + { + $svar = $this->source->compile($options, LEVEL_LIST); + + if (($name || $this->own) && ! preg_match(IDENTIFIER, $svar)) + { + $ref = $scope->free_variable('ref'); + $def_part = "{$this->tab}{$ref} = {$svar};\n"; + $svar = $ref; + } + + if ($name && ! $this->pattern) + { + $name_part = "{$name} = {$svar}[{$kvar}]"; + } + + if ( ! $this->object) + { + $lvar = $scope->free_variable('len'); + $for_var_part = "{$kvar_assign}{$ivar} = 0, {$lvar} = {$svar}.length"; + + if ($this->step) + { + $for_var_part .= ", {$stepvar} = ".$this->step->compile($options, LEVEL_OP); + } + + $step_part = $kvar_assign.($this->step ? "{$ivar} += {$stepvar}" : ($kvar !== $ivar ? "++{$ivar}" : "{$ivar}++")); + $for_part = "{$for_var_part}; {$ivar} < {$lvar}; {$step_part}"; + } + } + + if ($this->returns) + { + $result_part = "{$this->tab}{$rvar} = [];\n"; + $return_result = "\n{$this->tab}return {$rvar};"; + $body->make_return($rvar); + } + + if ($this->guard) + { + if ($body->expressions) + { + array_unshift($body->expressions, yy('If', yy('Parens', $this->guard)->invert(), yy('Literal', 'continue'))); + } + else + { + $body = yy_Block::wrap(array(yy('If', $this->guard, $body))); + } + } + + if ($this->pattern) + { + array_unshift($body->expressions, yy('Assign', $this->name, yy('Literal', "{$svar}[{$kvar}]"))); + } + + $def_part .= $this->pluck_direct_call($options, $body); + + if (isset($name_part) && $name_part) + { + $var_part = "\n{$idt1}{$name_part};"; + } + + if ($this->object) + { + $for_part = "{$kvar} in {$svar}"; + + if ($this->own) + { + $guard_part = "\n{$idt1}if (!".utility('hasProp').".call({$svar}, {$kvar})) continue;"; + } + } + + $body = $body->compile(array_merge($options, array('indent' => $idt1)), LEVEL_TOP); + + if ($body) + { + $body = "\n{$body}\n"; + } + + return + "{$def_part}" + . (isset($result_part) ? $result_part : '') + . "{$this->tab}for ({$for_part}) {{$guard_part}{$var_part}{$body}{$this->tab}}" + . (isset($return_result) ? $return_result : ''); + } + + function pluck_direct_call($options, $body) + { + $defs = ''; + + foreach ($body->expressions as $idx => $expr) + { + $expr = $expr->unwrap_all(); + + if ( ! ($expr instanceof yy_Call)) + { + continue; + } + + $val = $expr->variable->unwrap_all(); + + if ( ! ( ($val instanceof yy_Code) || + ($val instanceof yy_Value) && + (isset($val->base) && $val->base && ($val->base->unwrap_all() instanceof yy_Code) && + count($val->properties) === 1 && + isset($val->properties[0]->name) && + in_array($val->properties[0]->name['value'], array('call', 'apply'), TRUE)))) + { + continue; + } + + $fn = (isset($val->base) && $val->base) ? $val->base->unwrap_all() : $val; + $ref = yy('Literal', $options['scope']->free_variable('fn')); + $base = yy('Value', $ref); + + if (isset($val->base) && $val->base) + { + list($val->base, $base) = array($base, $val); + } + + $body->expressions[$idx] = yy('Call', $base, $expr->args); + $tmp = yy('Assign', $ref, $fn); + $defs .= $this->tab.$tmp->compile($options, LEVEL_TOP).";\n"; + } + + return $defs; + } +} + +?> diff --git a/sparks/assets/1.5.1/libraries/coffeescript/yy/If.php b/sparks/assets/1.5.1/libraries/coffeescript/yy/If.php new file mode 100755 index 0000000..8566d69 --- /dev/null +++ b/sparks/assets/1.5.1/libraries/coffeescript/yy/If.php @@ -0,0 +1,161 @@ +condition = (isset($options['type']) && $options['type'] === 'unless') ? $condition->invert() : $condition; + $this->body = $body; + $this->else_body = NULL; + $this->is_chain = FALSE; + $this->soak = isset($options['soak']) ? $options['soak'] : NULL; + + return $this; + } + + function add_else($else_body) + { + if ($this->is_chain()) + { + $this->else_body_node()->add_else($else_body); + } + else + { + $this->is_chain = $else_body instanceof yy_If; + $this->else_body = $this->ensure_block($else_body); + } + + return $this; + } + + function body_node() + { + return $this->body ? $this->body->unwrap() : NULL; + } + + function compile_node($options = array()) + { + return $this->is_statement($options) ? $this->compile_statement($options) : $this->compile_expression($options); + } + + function compile_expression($options) + { + $cond = $this->condition->compile($options, LEVEL_COND); + $body = $this->body_node()->compile($options, LEVEL_LIST); + + $alt = ($tmp = $this->else_body_node()) ? $tmp->compile($options, LEVEL_LIST) : 'void 0'; + $code = "{$cond} ? {$body} : {$alt}"; + + return (isset($options['level']) && $options['level'] > LEVEL_COND) ? "({$code})" : $code; + } + + function compile_statement($options) + { + $child = del($options, 'chainChild'); + $exeq = del($options, 'isExistentialEquals'); + + if ($exeq) + { + return yy('If', $this->condition->invert(), $this->else_body_node(), array('type' => 'if'))->compile($options); + } + + $cond = $this->condition->compile($options, LEVEL_PAREN); + $options['indent'] .= TAB; + $body = $this->ensure_block($this->body); + $if_part = "if ({$cond}) {\n".$body->compile($options)."\n{$this->tab}}"; + + if ( ! $child) + { + $if_part = $this->tab.$if_part; + } + + if ( ! $this->else_body) + { + return $if_part; + } + + $ret = $if_part.' else '; + + if ($this->is_chain()) + { + $options['indent'] = $this->tab; + $options['chainChild'] = TRUE; + + $ret .= $this->else_body->unwrap()->compile($options, LEVEL_TOP); + } + else + { + $ret .= "{\n".$this->else_body->compile($options, LEVEL_TOP)."\n{$this->tab}}"; + } + + return $ret; + } + + function else_body_node() + { + return (isset($this->else_body) && $this->else_body) ? $this->else_body->unwrap() : NULL; + } + + function ensure_block($node) + { + return $node instanceof yy_Block ? $node : yy('Block', array($node)); + } + + function is_chain() + { + return $this->is_chain; + } + + function is_statement($options = array()) + { + return (isset($options['level']) && $options['level'] === LEVEL_TOP) || + $this->body_node()->is_statement($options) || + (($tmp = $this->else_body_node()) && $tmp->is_statement($options)); + } + + function jumps($options = array()) + { + $tmp = $this->body->jumps($options); + + if ( ! $tmp && isset($this->else_body)) + { + $tmp = $this->else_body->jumps($options); + } + + return $tmp; + } + + function make_return($res = NULL) + { + if ( ! (isset($this->else_body) && $this->else_body)) + { + if ($res) + { + $this->else_body = yy('Block', array(yy('Literal', 'void 0'))); + } + } + + if ($this->body) + { + $this->body = yy('Block', array($this->body->make_return($res))); + } + + if ($this->else_body) + { + $this->else_body = yy('Block', array($this->else_body->make_return($res))); + } + + return $this; + } + + function unfold_soak() + { + return $this->soak ? $this : FALSE; + } +} + +?> diff --git a/sparks/assets/1.5.1/libraries/coffeescript/yy/In.php b/sparks/assets/1.5.1/libraries/coffeescript/yy/In.php new file mode 100755 index 0000000..ea206bc --- /dev/null +++ b/sparks/assets/1.5.1/libraries/coffeescript/yy/In.php @@ -0,0 +1,97 @@ +array = $array; + $this->object = $object; + + return $this; + } + + function compile_node($options = array()) + { + if ($this->array instanceof yy_Value && $this->array->is_array()) + { + $has_splat = FALSE; + + foreach ($this->array->base->objects as $obj) + { + if ($obj instanceof yy_Splat) + { + $has_splat = TRUE; + break; + } + } + + if ( ! $has_splat) + { + return $this->compile_or_test($options); + } + } + + return $this->compile_loop_test($options); + } + + function compile_or_test($options) + { + if ( ! $this->array->base->objects) + { + return '!!'.$this->negated; + } + + list($sub, $ref) = $this->object->cache($options, LEVEL_OP); + list($cmp, $cnj) = $this->negated ? array(' !== ', ' && ') : array(' === ', ' || '); + + $tests = array(); + + foreach ($this->array->base->objects as $i => $item) + { + $tests[] = ($i ? $ref : $sub).$cmp.$item->compile($options, LEVEL_ACCESS); + } + + if ( ! $tests) + { + // In JavaScript '' + false gives 'false', not so in PHP + return 'false'; + } + + $tests = implode($cnj, $tests); + + return (isset($options['level']) && $options['level'] < LEVEL_OP) ? $tests : "({$tests})"; + } + + function compile_loop_test($options) + { + list($sub, $ref) = $this->object->cache($options, LEVEL_LIST); + + $code = utility('indexOf').".call(".$this->array->compile($options, LEVEL_LIST).", {$ref}) " + .($this->negated ? '< 0' : '>= 0'); + + if ($sub === $ref) + { + return $code; + } + + $code = $sub.', '.$code; + return (isset($options['level']) && $options['level'] < LEVEL_LIST) ? $code : "({$code})"; + } + + function invert() + { + $this->negated = ! $this->negated; + return $this; + } + + function to_string($idt = '', $name = __CLASS__) + { + return parent::to_string($idt, $name.($this->negated ? '!' : '')); + } +} + +?> diff --git a/sparks/assets/1.5.1/libraries/coffeescript/yy/Index.php b/sparks/assets/1.5.1/libraries/coffeescript/yy/Index.php new file mode 100755 index 0000000..c8809fa --- /dev/null +++ b/sparks/assets/1.5.1/libraries/coffeescript/yy/Index.php @@ -0,0 +1,27 @@ +index = $index; + + return $this; + } + + function compile($options) + { + return '['.$this->index->compile($options, LEVEL_PAREN).']'; + } + + function is_complex() + { + return $this->index->is_complex(); + } +} + +?> diff --git a/sparks/assets/1.5.1/libraries/coffeescript/yy/Literal.php b/sparks/assets/1.5.1/libraries/coffeescript/yy/Literal.php new file mode 100755 index 0000000..f900dd7 --- /dev/null +++ b/sparks/assets/1.5.1/libraries/coffeescript/yy/Literal.php @@ -0,0 +1,96 @@ +value = $value; + + return $this; + } + + function assigns($name) + { + return $name === $this->value; + } + + function compile_node($options) + { + if ($this->is_undefined()) + { + $code = $options['level'] >= LEVEL_ACCESS ? '(void 0)' : 'void 0'; + } + else if ($this->value === 'this') + { + if ( (isset($options['scope']->method->bound) && $options['scope']->method->bound) ) + { + $code = $options['scope']->method->context; + } + else + { + $code = $this->value; + } + } + else if (isset($this->value->reserved) && $this->value->reserved) + { + $code = '"'.$this->value.'"'; + } + else + { + $code = ''.$this->value; + } + + return $this->is_statement() ? "{$this->tab}{$code};" : $code; + } + + function is_assignable() + { + return preg_match(IDENTIFIER, ''.$this->value); + } + + function is_complex() + { + return FALSE; + } + + function is_statement() + { + return in_array(''.$this->value, array('break', 'continue', 'debugger'), TRUE); + } + + function is_undefined() + { + return $this->is_undefined; + } + + function jumps($options = array()) + { + if ($this->value === 'break' && ! ( (isset($options['loop']) && $options['loop']) || (isset($options['block']) && $options['block']) )) + { + return $this; + } + + if ($this->value === 'continue' && ! (isset($options['loop']) && $options['loop'])) + { + return $this; + } + + return FALSE; + } + + function make_return() + { + return $this->is_statement() ? $this : parent::make_return(); + } + + function to_string($idt = '', $name = __CLASS__) + { + return ' "'.$this->value.'"'; + } +} + +?> diff --git a/sparks/assets/1.5.1/libraries/coffeescript/yy/Obj.php b/sparks/assets/1.5.1/libraries/coffeescript/yy/Obj.php new file mode 100755 index 0000000..c3b0b7a --- /dev/null +++ b/sparks/assets/1.5.1/libraries/coffeescript/yy/Obj.php @@ -0,0 +1,126 @@ +generated = $generated; + + $this->properties = $props ? $props : array(); + $this->objects = $this->properties; + + return $this; + } + + function assigns($name) + { + foreach ($this->properties as $prop) + { + if ($prop->assigns($name)) + { + return TRUE; + } + } + + return FALSE; + } + + function compile_node($options) + { + $props = $this->properties; + $prop_names = array(); + + foreach ($this->properties as $prop) + { + if ($prop->is_complex()) + { + $prop = isset($prop->variable) ? $prop->variable : NULL; + } + + if ($prop) + { + $prop_name = $prop->unwrap_all(); + $prop_name = isset($prop_name->value) ? $prop_name->value.'' : NULL; + + if (in_array($prop_name, $prop_names)) + { + throw new SyntaxError('multiple object literal properties named "'.$prop_name.'"'); + } + + $prop_names[] = $prop_name; + } + } + + if ( ! count($props)) + { + return ($this->front ? '({})' : '{}'); + } + + if ($this->generated) + { + foreach ($props as $node) + { + if ($node instanceof yy_Value) + { + throw new Error('cannot have an implicit value in an implicit object'); + } + } + } + + $idt = $options['indent'] .= TAB; + $last_non_com = $this->last_non_comment($this->properties); + + foreach ($props as $i => $prop) + { + if ($i === count($props) - 1) + { + $join = ''; + } + else if ($prop === $last_non_com || $prop instanceof yy_Comment) + { + $join = "\n"; + } + else + { + $join = ",\n"; + } + + $indent = $prop instanceof yy_Comment ? '' : $idt; + + if ($prop instanceof yy_Value && (isset($prop->this) && $prop->this)) + { + $prop = yy('Assign', $prop->properties[0]->name, $prop, 'object'); + } + + if ( ! ($prop instanceof yy_Comment)) + { + if ( ! ($prop instanceof yy_Assign)) + { + $prop = yy('Assign', $prop, $prop, 'object'); + } + + if (isset($prop->variable->base)) + { + $prop->variable->base->as_key = TRUE; + } + else + { + $prop->variable->as_key = TRUE; + } + } + + $props[$i] = $indent.$prop->compile($options, LEVEL_TOP).$join; + } + + $props = implode('', $props); + $obj = '{'.($props ? "\n{$props}\n{$this->tab}" : '').'}'; + + return ($this->front ? "({$obj})" : $obj); + } +} + +?> diff --git a/sparks/assets/1.5.1/libraries/coffeescript/yy/Op.php b/sparks/assets/1.5.1/libraries/coffeescript/yy/Op.php new file mode 100755 index 0000000..51b02ef --- /dev/null +++ b/sparks/assets/1.5.1/libraries/coffeescript/yy/Op.php @@ -0,0 +1,292 @@ + '===', + '!=' => '!==', + 'of' => 'in' + ); + + static $INVERSIONS = array( + '!==' => '===', + '===' => '!==' + ); + + public $children = array('first', 'second'); + + public $operator = NULL; + + public $invert = TRUE; + + function constructor($op, $first, $second = NULL, $flip = NULL) + { + if ($op === 'in') + { + return yy('In', $first, $second); + } + + if ($op === 'do') + { + return $this->generate_do($first); + } + + if ($op === 'new') + { + if ($first instanceof yy_Call && ! (isset($first->do) && $first->do) && ! (isset($first->is_new) && $first->is_new)) + { + return $first->new_instance(); + } + + if ($first instanceof yy_Code && $first->bound || (isset($first->do) && $first->do)) + { + $first = yy('Parens', $first); + } + } + + $this->operator = isset(self::$CONVERSIONS[$op]) ? self::$CONVERSIONS[$op] : $op; + $this->first = $first; + $this->second = $second; + $this->flip = !! $flip; + + return $this; + } + + function compile_chain($options) + { + $tmp = $this->first->second->cache($options); + + $this->first->second = $tmp[0]; + $shared = $tmp[1]; + + $fst = $this->first->compile($options, LEVEL_OP); + + $code = "{$fst} ".($this->invert ? '&&' : '||').' '.$shared->compile($options).' ' + .$this->operator.' '.$this->second->compile($options, LEVEL_OP); + + return "({$code})"; + } + + function compile_existence($options) + { + if ($this->first->is_complex() && $options['level'] > LEVEL_TOP) + { + $ref = yy('Literal', $options['scope']->free_variable('ref')); + $fst = yy('Parens', yy('Assign', $ref, $this->first)); + } + else + { + $fst = $this->first; + $ref = $fst; + } + + $tmp = yy('If', yy('Existence', $fst), $ref, array('type' => 'if')); + $tmp->add_else($this->second); + + return $tmp->compile($options); + } + + function compile_node($options, $level = NULL) + { + $is_chain = $this->is_chainable() && $this->first->is_chainable(); + + if ( ! $is_chain) + { + $this->first->front = $this->front; + } + + $tmp = $this->first->unwrap_all(); + $tmp = isset($tmp->value) ? $tmp->value : NULL; + + if ($this->operator === 'delete' && $options['scope']->check($tmp)) + { + throw new SyntaxError('delete operand may not be argument or var'); + } + + if (in_array($this->operator, array('--', '++')) && in_array($tmp, Lexer::$STRICT_PROSCRIBED)) + { + throw new SyntaxError('prefix increment/decrement may not have eval or arguments operand'); + } + + if ($this->is_unary()) + { + return $this->compile_unary($options); + } + + if ($is_chain) + { + return $this->compile_chain($options); + } + + if ($this->operator === '?') + { + return $this->compile_existence($options); + } + + $this->first->front = $this->front; + + $code = $this->first->compile($options, LEVEL_OP).' '.$this->operator.' ' + .$this->second->compile($options, LEVEL_OP); + + return $options['level'] <= LEVEL_OP ? $code : "({$code})"; + } + + function compile_unary($options) + { + if ($options['level'] >= LEVEL_ACCESS) + { + return yy('Parens', $this)->compile($options); + } + + $parts = array($op = $this->operator); + $plus_minus = in_array($op, array('+', '-'), TRUE); + + if (in_array($op, array('new', 'typeof', 'delete'), TRUE) || + $plus_minus && + $this->first instanceof yy_Op && $this->first->operator === $op) + { + $parts[] = ' '; + } + + if (($plus_minus && $this->first instanceof yy_Op) || ($op === 'new' && $this->first->is_statement($options))) + { + $this->first = yy('Parens', $this->first); + } + + $parts[] = $this->first->compile($options, LEVEL_OP); + + if ($this->flip) + { + $parts = array_reverse($parts); + } + + return implode('', $parts); + } + + function is_chainable() + { + return in_array($this->operator, array('<', '>', '>=', '<=', '===', '!=='), TRUE); + } + + function is_complex() + { + return ! ($this->is_unary() && in_array($this->operator, array('+', '-'))) || $this->first->is_complex(); + } + + function invert() + { + if ($this->is_chainable() && $this->first->is_chainable()) + { + $all_invertable = TRUE; + $curr = $this; + + while ($curr && (isset($curr->operator) && $curr->operator)) + { + if ($all_invertable) + { + $all_invertable = isset(self::$INVERSIONS[$curr->operator]); + } + + $curr = $curr->first; + } + + if ( ! $all_invertable) + { + return yy('Parens', $this)->invert(); + } + + $curr = $this; + + while ($curr && (isset($curr->operator) && $curr->operator)) + { + $curr->invert = ! $curr->invert; + $curr->operator = self::$INVERSIONS[$curr->operator]; + $curr = $curr->first; + } + + return $this; + } + else if (isset(self::$INVERSIONS[$this->operator]) && ($op = self::$INVERSIONS[$this->operator])) + { + $this->operator = $op; + + if ($this->first->unwrap() instanceof yy_Op) + { + $this->first->invert(); + } + + return $this; + } + else if ($this->second) + { + return yy('Parens', $this)->invert(); + } + else if ($this->operator === '!' && (($fst = $this->first->unwrap()) instanceof yy_Op) && + in_array($fst->operator, array('!', 'in', 'instanceof'), TRUE)) + { + return $fst; + } + else + { + return yy('Op', '!', $this); + } + } + + function generate_do($exp) + { + $passed_params = array(); + $func = $exp; + + if ($exp instanceof yy_Assign && ($ref = $exp->value->unwrap()) instanceof yy_Code) + { + $func = $ref; + } + + foreach ((isset($func->params) && $func->params ? $func->params : array()) as $param) + { + if (isset($param->value) && $param->value) + { + $passed_params[] = $param->value; + unset($param->value); + } + else + { + $passed_params[] = $param; + } + } + + $call = yy('Call', $exp, $passed_params); + $call->do = TRUE; + + return $call; + } + + function is_simple_number() + { + return FALSE; + } + + function is_unary() + { + return ! (isset($this->second) && $this->second); + } + + function unfold_soak($options) + { + if (in_array($this->operator, array('++', '--', 'delete'), TRUE)) + { + return unfold_soak($options, $this, 'first'); + } + + return NULL; + } + + function to_string($idt = '', $name = __CLASS__) + { + return parent::to_string($idt, $name.' '.$this->operator); + } +} + +?> diff --git a/sparks/assets/1.5.1/libraries/coffeescript/yy/Param.php b/sparks/assets/1.5.1/libraries/coffeescript/yy/Param.php new file mode 100755 index 0000000..db47bb5 --- /dev/null +++ b/sparks/assets/1.5.1/libraries/coffeescript/yy/Param.php @@ -0,0 +1,119 @@ +name = $name; + $this->value = $value; + $this->splat = $splat; + + $name = $this->name->unwrap_all(); + $name = isset($name->value) ? $name->value : NULL; + + if (in_array($name, Lexer::$STRICT_PROSCRIBED)) + { + throw new SyntaxError("parameter name \"$name\" is not allowed"); + } + + return $this; + } + + function as_reference($options) + { + if (isset($this->reference) && $this->reference) + { + return $this->reference; + } + + $node = $this->name; + + if (isset($node->this) && $node->this) + { + $node = $node->properties[0]->name; + + if (isset($this->value->reserved) && $this->value->reserved) + { + $node = yy('Literal', $options['scope']->free_variable($node->value)); + } + } + else if ($node->is_complex()) + { + $node = yy('Literal', $options['scope']->free_variable('arg')); + } + + $node = yy('Value', $node); + + if ($this->splat) + { + $node = yy('Splat', $node); + } + + return ($this->reference = $node); + } + + function compile($options, $level = NULL) + { + return $this->name->compile($options, LEVEL_LIST); + } + + function is_complex() + { + return $this->name->is_complex(); + } + + function names($name = NULL) + { + if ($name === NULL) + { + $name = $this->name; + } + + $at_param = function($obj) + { + $value = $obj->properties[0]->name; + + return isset($value->reserved) && $value->reserved ? array() : array($value); + }; + + if ($name instanceof yy_Literal) + { + return array($name->value); + } + + if ($name instanceof yy_Value) + { + return $at_param($name); + } + + $names = array(); + + foreach ($name->objects as $obj) + { + if ($obj instanceof yy_Assign) + { + $names[] = $obj->variable->base->value; + } + else if ($obj->is_array() || $obj->is_object()) + { + $names = array_merge($names, (array) $this->names($obj->base)); + } + else if (isset($obj->this) && $obj->this) + { + $names = array_merge($names, (array) $at_param($obj)); + } + else + { + $names[] = $obj->base->value; + } + } + + return $names; + } +} + +?> diff --git a/sparks/assets/1.5.1/libraries/coffeescript/yy/Parens.php b/sparks/assets/1.5.1/libraries/coffeescript/yy/Parens.php new file mode 100755 index 0000000..db4db62 --- /dev/null +++ b/sparks/assets/1.5.1/libraries/coffeescript/yy/Parens.php @@ -0,0 +1,45 @@ +body = $body; + + return $this; + } + + function compile_node($options = array()) + { + $expr = $this->body->unwrap(); + + if ($expr instanceof yy_Value && $expr->is_atomic()) + { + $expr->front = $this->front; + return $expr->compile($options); + } + + $code = $expr->compile($options, LEVEL_PAREN); + + $bare = $options['level'] < LEVEL_OP && ($expr instanceof yy_Op || $expr instanceof yy_Call || + ($expr instanceof yy_For && $expr->returns)); + + return $bare ? $code : "({$code})"; + } + + function is_complex() + { + return $this->body->is_complex(); + } + + function unwrap() + { + return $this->body; + } +} + +?> diff --git a/sparks/assets/1.5.1/libraries/coffeescript/yy/Range.php b/sparks/assets/1.5.1/libraries/coffeescript/yy/Range.php new file mode 100755 index 0000000..d941744 --- /dev/null +++ b/sparks/assets/1.5.1/libraries/coffeescript/yy/Range.php @@ -0,0 +1,217 @@ +from = $from; + $this->to = $to; + $this->exclusive = $tag === 'exclusive'; + $this->equals = $this->exclusive ? '' : '='; + + return $this; + } + + function compile_array($options) + { + if (self::check($this->from_num) && self::check($this->to_num) && abs($this->from_num - $this->to_num) <= 20) + { + $range = range($this->from_num, $this->to_num); + + if ($this->exclusive) + { + array_pop($range); + } + + return '['.implode(', ', $range).']'; + } + + $idt = $this->tab.TAB; + $i = $options['scope']->free_variable('i'); + $result = $options['scope']->free_variable('results'); + $pre = "\n{$idt}{$result} = [];"; + + if (self::check($this->from_num) && self::check($this->to_num)) + { + $options['index'] = $i; + $body = $this->compile_node($options); + } + else + { + $vars = "{$i} = {$this->from_c}".($this->to_c !== $this->to_var ? ", {$this->to_c}" : ''); + $cond = "{$this->from_var} <= {$this->to_var}"; + $body = "var {$vars}; {$cond} ? {$i} <{$this->equals} {$this->to_var} : {$i} >{$this->equals} {$this->to_var}; {$cond} ? {$i}++ : {$i}--"; + } + + $post = "{ {$result}.push({$i}); }\n{$idt}return {$result};\n{$options['indent']}"; + + $has_args = function($node) + { + return $node->contains(function($n) + { + return $n instanceof yy_Literal && $n->value === 'arguments' && ! $n->as_key(); + }); + + return FALSE; + }; + + $args = ''; + + if ($has_args($this->from) || $has_args($this->to)) + { + $args = ', arguments'; + } + + return "(function() {{$pre}\n{$idt}for ({$body}){$post}}).apply(this{$args})"; + } + + function compile_node($options) + { + if ( ! (isset($this->from_var) && $this->from_var)) + { + $this->compile_variables($options); + } + + if ( ! (isset($options['index']) && $options['index'])) + { + return $this->compile_array($options); + } + + $known = self::check($this->from_num) && self::check($this->to_num); + $idx = del($options, 'index'); + $idx_name = del($options, 'name'); + $named_index = $idx_name && $idx_name !== $idx; + + $var_part = "{$idx} = {$this->from_c}"; + + if ($this->to_c !== $this->to_var) + { + $var_part .= ", {$this->to_c}"; + } + + if (isset($this->step) && $this->step !== $this->step_var) + { + $var_part .= ", {$this->step}"; + } + + list($lt, $gt) = array("{$idx} <{$this->equals}", "{$idx} >{$this->equals}"); + + if (isset($this->step_num) && self::check($this->step_num)) + { + $cond_part = intval($this->step_num) > 0 ? "{$lt} {$this->to_var}" : "{$gt} {$this->to_var}"; + } + else if ($known) + { + list($from, $to) = array(intval($this->from_num), intval($this->to_num)); + $cond_part = $from <= $to ? "{$lt} {$to}" : "{$gt} {$to}"; + } + else + { + $cond = "{$this->from_var} <= {$this->to_var}"; + $cond_part = "{$cond} ? {$lt} {$this->to_var} : {$gt} {$this->to_var}"; + } + + if (isset($this->step_var) && $this->step_var) + { + $step_part = "{$idx} += {$this->step_var}"; + } + else if ($known) + { + if ($named_index) + { + $step_part = $from <= $to ? "++{$idx}" : "--{$idx}"; + } + else + { + $step_part = $from <= $to ? "{$idx}++" : "{$idx}--"; + } + } + else + { + if ($named_index) + { + $step_part = "{$cond} ? ++{$idx} : --{$idx}"; + } + else + { + $step_part = "{$cond} ? {$idx}++ : {$idx}--"; + } + } + + if ($named_index) + { + $var_part = "{$idx_name} = {$var_part}"; + $step_part = "{$idx_name} = {$step_part}"; + } + + return "{$var_part}; {$cond_part}; {$step_part}"; + } + + function compile_simple($options) + { + list($from, $to) = array($this->from_num, $this->to_num); + + $idx = del($options, 'index'); + $step = del($options, 'step'); + + if ($step) + { + $stepvar = $options['scope']->free_variable('step'); + } + + $var_part = "{$idx} = {$from}"; + + if ($step) + { + $var_part .= ", {$stepvar} = ".$step->compile($options); + } + + $cond_part = $from <= $to ? "{$idx} <{$this->equals} {$to}" : "{$idx} >{$this->equals} {$to}"; + + if ($step) + { + $step_part = "{$idx} += {$stepvar}"; + } + else + { + $step_part = $from <= $to ? "{$idx}++" : "{$idx}--"; + } + + return "{$var_part}; {$cond_part}; {$step_part}"; + } + + function compile_variables($options) + { + $options = array_merge($options, array('top' => TRUE)); + + list($this->from_c, $this->from_var) = $this->from->cache($options, LEVEL_LIST); + list($this->to_c, $this->to_var) = $this->to->cache($options, LEVEL_LIST); + + if ($step = del($options, 'step')) + { + list($this->step, $this->step_var) = $step->cache($options, LEVEL_LIST); + } + + list($this->from_num, $this->to_num) = array(preg_match(SIMPLENUM, $this->from_var), preg_match(SIMPLENUM, $this->to_var)); + + if (isset($this->step_var) && $this->step_var) + { + $this->step_num = preg_match(SIMPLENUM, $this->step_var); + } + } +} + +?> diff --git a/sparks/assets/1.5.1/libraries/coffeescript/yy/Return.php b/sparks/assets/1.5.1/libraries/coffeescript/yy/Return.php new file mode 100755 index 0000000..400eabb --- /dev/null +++ b/sparks/assets/1.5.1/libraries/coffeescript/yy/Return.php @@ -0,0 +1,56 @@ +unwrap()->is_undefined())) + { + $this->expression = $expr; + } + + return $this; + } + + function compile($options, $level = NULL) + { + $expr = (isset($this->expression) && $this->expression) ? $this->expression->make_return() : NULL; + + if ($expr && ! ($expr instanceof yy_Return)) + { + $ret = $expr->compile($options, $level); + } + else + { + $ret = parent::compile($options, $level); + } + + return $ret; + } + + function compile_node($options) + { + return $this->tab.'return'.(isset($this->expression) && $this->expression ? ' '.$this->expression->compile($options, LEVEL_PAREN) : '').';'; + } + + function is_statement() + { + return TRUE; + } + + function jumps() + { + return $this; + } + + function make_return() + { + return $this; + } +} + +?> diff --git a/sparks/assets/1.5.1/libraries/coffeescript/yy/Slice.php b/sparks/assets/1.5.1/libraries/coffeescript/yy/Slice.php new file mode 100755 index 0000000..eb6c4ce --- /dev/null +++ b/sparks/assets/1.5.1/libraries/coffeescript/yy/Slice.php @@ -0,0 +1,49 @@ +range = $range; + + return $this; + } + + function compile_node($options) + { + $to = $this->range->to; + $from = $this->range->from; + + $from_str = $from ? $from->compile($options, LEVEL_PAREN) : '0'; + $compiled = $to ? $to->compile($options, LEVEL_PAREN) : ''; + + if ($to && ! ( ! $this->range->exclusive && intval($compiled) === -1)) + { + $to_str = ', '; + + if ($this->range->exclusive) + { + $to_str .= $compiled; + } + else if (preg_match(SIMPLENUM, $compiled)) + { + $to_str .= (intval($compiled) + 1); + } + else + { + $compiled = $to->compile($options, LEVEL_ACCESS); + $to_str .= "({$compiled} + 1) || 9e9"; + } + } + + return ".slice({$from_str}".(isset($to_str) ? $to_str : '').')'; + } +} + +?> diff --git a/sparks/assets/1.5.1/libraries/coffeescript/yy/Splat.php b/sparks/assets/1.5.1/libraries/coffeescript/yy/Splat.php new file mode 100755 index 0000000..83f41c3 --- /dev/null +++ b/sparks/assets/1.5.1/libraries/coffeescript/yy/Splat.php @@ -0,0 +1,100 @@ += count($list)) + { + return ''; + } + + if (count($list) === 1) + { + $code = $list[0]->compile($options, LEVEL_LIST); + + if ($apply) + { + return $code; + } + + return utility('slice').".call({$code})"; + } + + $args = array_slice($list, $index); + + foreach ($args as $i => $node) + { + $code = $node->compile($options, LEVEL_LIST); + $args[$i] = ($node instanceof yy_Splat) ? utility('slice').".call({$code})" : "[{$code}]"; + } + + if ($index === 0) + { + return $args[0].'.concat('.implode(', ', array_slice($args, 1)).')'; + } + + $base = array(); + + foreach (array_slice($list, 0, $index) as $node) + { + $base[] = $node->compile($options, LEVEL_LIST); + } + + return '['.implode(', ', $base).'].concat('.implode(', ', $args).')'; + } + + function constructor($name) + { + if (is_object($name)) + { + $this->name = $name; + } + else + { + $this->name = yy('Literal', $name); + } + + return $this; + } + + function assigns($name) + { + return $this->name->assigns($name); + } + + function compile($options) + { + if (isset($this->index) && $this->index) + { + return $this->compile_param($options); + } + else + { + return $this->name->compile($options); + } + } + + function is_assignable() + { + return TRUE; + } + + function unwrap() + { + return $this->name; + } +} + +?> diff --git a/sparks/assets/1.5.1/libraries/coffeescript/yy/Switch.php b/sparks/assets/1.5.1/libraries/coffeescript/yy/Switch.php new file mode 100755 index 0000000..6748556 --- /dev/null +++ b/sparks/assets/1.5.1/libraries/coffeescript/yy/Switch.php @@ -0,0 +1,121 @@ +subject = $subject; + $this->cases = $cases; + $this->otherwise = $otherwise; + + return $this; + } + + function compile_node($options) + { + $idt1 = $options['indent'].TAB; + $idt2 = $options['indent'] = $idt1.TAB; + + $code = $this->tab.'switch (' + .($this->subject ? $this->subject->compile($options, LEVEL_PAREN) : 'false') + .") {\n"; + + foreach ($this->cases as $i => $case) + { + list($conditions, $block) = $case; + + foreach (flatten(array($conditions)) as $cond) + { + if ( ! $this->subject) + { + $cond = $cond->invert(); + } + + $code .= $idt1.'case '.$cond->compile($options, LEVEL_PAREN).":\n"; + } + + if ($body = $block->compile($options, LEVEL_TOP)) + { + $code .= $body."\n"; + } + + if ($i === (count($this->cases) - 1) && ! $this->otherwise) + { + break; + } + + $expr = $this->last_non_comment($block->expressions); + + if ($expr instanceof yy_Return || + ($expr instanceof yy_Literal && $expr->jumps() && ''.$expr->value !== 'debugger')) + { + continue; + } + + $code .= $idt2."break;\n"; + } + + if ($this->otherwise && count($this->otherwise->expressions)) + { + $code .= $idt1."default:\n".$this->otherwise->compile($options, LEVEL_TOP)."\n"; + } + + return $code.$this->tab.'}'; + } + + function is_statement() + { + return TRUE; + } + + function jumps($options = array()) + { + if ( ! isset($options['block'])) + { + $options['block'] = TRUE; + } + + foreach ($this->cases as $case) + { + list($conds, $block) = $case; + + if ($block->jumps($options)) + { + return $block; + } + } + + if (isset($this->otherwise) && $this->otherwise) + { + return $this->otherwise->jumps($options); + } + + return FALSE; + } + + function make_return($res = NULL) + { + foreach ($this->cases as $pair) + { + $pair[1]->make_return($res); + } + + if ($res) + { + $this->otherwise = isset($this->otherwise) && $this->otherwise ? $this->otherwise : yy('Block', array(yy('Literal', 'void 0'))); + } + + if (isset($this->otherwise) && $this->otherwise) + { + $this->otherwise->make_return(); + } + + return $this; + } +} + +?> diff --git a/sparks/assets/1.5.1/libraries/coffeescript/yy/Throw.php b/sparks/assets/1.5.1/libraries/coffeescript/yy/Throw.php new file mode 100755 index 0000000..3a2cf39 --- /dev/null +++ b/sparks/assets/1.5.1/libraries/coffeescript/yy/Throw.php @@ -0,0 +1,37 @@ +expression = $expression; + + return $this; + } + + function compile_node($options = array()) + { + return $this->tab.'throw '.$this->expression->compile($options).';'; + } + + function is_statement() + { + return TRUE; + } + + function jumps() + { + return FALSE; + } + + function make_return() + { + return $this; + } +} + +?> diff --git a/sparks/assets/1.5.1/libraries/coffeescript/yy/Try.php b/sparks/assets/1.5.1/libraries/coffeescript/yy/Try.php new file mode 100755 index 0000000..fd35fbe --- /dev/null +++ b/sparks/assets/1.5.1/libraries/coffeescript/yy/Try.php @@ -0,0 +1,79 @@ +attempt = $attempt; + $this->error = $error; + $this->recovery = $recovery; + $this->ensure = $ensure; + + return $this; + } + + function compile_node($options = array()) + { + $options['indent'] .= TAB; + $error_part = $this->error ? ' ('.$this->error->compile($options).') ' : ' '; + $try_part = $this->attempt->compile($options, LEVEL_TOP); + $catch_part = ''; + + if ($this->recovery) + { + if (in_array($this->error, Lexer::$STRICT_PROSCRIBED)) + { + throw new SyntaxError('catch variable may not be "'.$this->error->value.'"'); + } + + if ( ! $options['scope']->check($this->error->value)) + { + $options['scope']->add($this->error->value, 'param'); + } + + $catch_part = " catch{$error_part}{\n".$this->recovery->compile($options, LEVEL_TOP)."\n{$this->tab}}"; + } + else if ( ! ($this->ensure || $this->recovery)) + { + $catch_part = ' catch (_error) {}'; + } + + $ensure_part = isset($this->ensure) && $this->ensure ? " finally {\n".$this->ensure->compile($options, LEVEL_TOP)."\n{$this->tab}}" : ''; + + return + "{$this->tab}try {\n" + . $try_part."\n" + . "{$this->tab}}{$catch_part}{$ensure_part}"; + } + + function is_statement() + { + return TRUE; + } + + function jumps($options = array()) + { + return $this->attempt->jumps($options) || (isset($this->recovery) && $this->recovery->jumps($options)); + } + + function make_return($res) + { + if ($this->attempt) + { + $this->attempt = $this->attempt->make_return($res); + } + + if ($this->recovery) + { + $this->recovery = $this->recovery->make_return($res); + } + + return $this; + } +} + +?> diff --git a/sparks/assets/1.5.1/libraries/coffeescript/yy/Value.php b/sparks/assets/1.5.1/libraries/coffeescript/yy/Value.php new file mode 100755 index 0000000..f0014a8 --- /dev/null +++ b/sparks/assets/1.5.1/libraries/coffeescript/yy/Value.php @@ -0,0 +1,210 @@ +base = $base; + $this->properties = $props ? $props : array(); + + if ($tag) + { + $this->{$tag} = TRUE; + } + + return $this; + } + + function add($prop) + { + $this->properties = array_merge($this->properties, is_object($prop) ? array($prop) : (array) $prop); + + return $this; + } + + function assigns($name) + { + return ! count($this->properties) && $this->base->assigns($name); + } + + function cache_reference($options) + { + $name = last($this->properties); + + if (count($this->properties) < 2 && ! $this->base->is_complex() && ! ($name && $name->is_complex())) + { + return array($this, $this); + } + + $base = yy('Value', $this->base, array_slice($this->properties, 0, -1)); + $bref = NULL; + + if ($base->is_complex()) + { + $bref = yy('Literal', $options['scope']->free_variable('base')); + $base = yy('Value', yy('Parens', yy('Assign', $bref, $base))); + } + + if ( ! $name) + { + return array($base, $bref); + } + + if ($name->is_complex()) + { + $nref = yy('Literal', $options['scope']->free_variable('name')); + $name = yy('Index', yy('Assign', $nref, $name->index)); + $nref = yy('Index', $nref); + } + + $base->add($name); + + return array($base, yy('Value', isset($bref) ? $bref : $base->base, array(isset($nref) ? $nref : $name))); + } + + function compile_node($options) + { + $this->base->front = $this->front; + $props = $this->properties; + + $code = $this->base->compile($options, count($props) ? LEVEL_ACCESS : NULL); + + if ( (($this->base instanceof yy_Parens) || count($props)) && preg_match(SIMPLENUM, $code)) + { + $code = $code.'.'; + } + + foreach ($props as $prop) + { + $code .= $prop->compile($options); + } + + return $code; + } + + function has_properties() + { + return !! count($this->properties); + } + + function is_array() + { + return ! count($this->properties) && $this->base instanceof yy_Arr; + } + + function is_assignable() + { + return $this->has_properties() || $this->base->is_assignable(); + } + + function is_atomic() + { + foreach (array_merge($this->properties, array($this->base)) as $node) + { + if ((isset($node->soak) && $node->soak) || $node instanceof yy_Call) + { + return FALSE; + } + } + + return TRUE; + } + + function is_complex() + { + return $this->has_properties() || $this->base->is_complex(); + } + + function is_object($only_generated = FALSE) + { + if (count($this->properties)) + { + return FALSE; + } + + return ($this->base instanceof yy_Obj) && ( ! $only_generated || $this->base->generated); + } + + function is_simple_number() + { + return ($this->base instanceof yy_Literal) && preg_match(SIMPLENUM, ''.$this->base->value); + } + + function is_splice() + { + return last($this->properties) instanceof yy_Slice; + } + + function is_string() + { + return ($this->base instanceof yy_Literal) && preg_match(IS_STRING, ''.$this->base->value); + } + + function is_statement($options) + { + return ! count($this->properties) && $this->base->is_statement($options); + } + + function jumps($options = array()) + { + return ! count($this->properties) && $this->base->jumps($options); + } + + function unfold_soak($options) + { + if (isset($this->unfolded_soak)) + { + return $this->unfolded_soak; + } + + if (($ifn = $this->base->unfold_soak($options))) + { + $ifn->body->properties = array_merge($ifn->body->properties, $this->properties); + $result = $ifn; + } + else + { + foreach ($this->properties as $i => $prop) + { + if (isset($prop->soak) && $prop->soak) + { + $prop->soak = FALSE; + + $fst = yy('Value', $this->base, array_slice($this->properties, 0, $i)); + $snd = yy('Value', $this->base, array_slice($this->properties, $i)); + + if ($fst->is_complex()) + { + $ref = yy('Literal', $options['scope']->free_variable('ref')); + $fst = yy('Parens', yy('Assign', $ref, $fst)); + $snd->base = $ref; + } + + $result = yy('If', yy('Existence', $fst), $snd, array('soak' => TRUE)); + + break; + } + } + } + + $this->unfolded_soak = isset($result) ? $result : FALSE; + + return $this->unfolded_soak; + } + + function unwrap() + { + return count($this->properties) ? $this : $this->base; + } +} + +?> diff --git a/sparks/assets/1.5.1/libraries/coffeescript/yy/While.php b/sparks/assets/1.5.1/libraries/coffeescript/yy/While.php new file mode 100755 index 0000000..d764356 --- /dev/null +++ b/sparks/assets/1.5.1/libraries/coffeescript/yy/While.php @@ -0,0 +1,110 @@ +condition = (isset($options['invert']) && $options['invert']) ? + $condition->invert() : $condition; + + $this->guard = isset($options['guard']) ? $options['guard'] : NULL; + + return $this; + } + + function add_body($body) + { + $this->body = $body; + return $this; + } + + function compile_node($options) + { + $options['indent'] .= TAB; + $set = ''; + $body = $this->body; + + if ($body->is_empty()) + { + $body = ''; + } + else + { + if ($this->returns) + { + $body->make_return($rvar = $options['scope']->free_variable('results')); + $set = "{$this->tab}{$rvar} = [];\n"; + } + + if ($this->guard) + { + if ($body->expressions) + { + array_unshift($body->expressions, yy('If', yy('Parens', $this->guard)->invert(), yy('Literal', 'continue'))); + } + else + { + $body = yy_Block::wrap(array(yy('If', $this->guard, $body))); + } + } + + $body = "\n".$body->compile($options, LEVEL_TOP)."\n{$this->tab}"; + } + + $code = $set.$this->tab.'while ('.$this->condition->compile($options, LEVEL_PAREN).") {{$body}}"; + + if ($this->returns) + { + $code .= "\n{$this->tab}return {$rvar};"; + } + + return $code; + } + + function is_statement() + { + return TRUE; + } + + function jumps() + { + $expressions = isset($this->body->expressions) ? $this->body->expressions : array(); + + if ( ! count($expressions)) + { + return FALSE; + } + + foreach ($expressions as $node) + { + if ($node->jumps(array('loop' => TRUE))) + { + return $node; + } + } + + return FALSE; + } + + function make_return($res = NULL) + { + if ($res) + { + return parent::make_return($res); + } + else + { + $this->returns = ! $this->jumps(array('loop' => TRUE)); + } + + return $this; + } +} + +?> diff --git a/sparks/assets/1.5.1/libraries/cssmin.php b/sparks/assets/1.5.1/libraries/cssmin.php new file mode 100644 index 0000000..5da2cba --- /dev/null +++ b/sparks/assets/1.5.1/libraries/cssmin.php @@ -0,0 +1,5081 @@ + + * + * 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. + * -- + * + * @package CssMin + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +/** + * Abstract definition of a CSS token class. + * + * Every token has to extend this class. + * + * @package CssMin/Tokens + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +abstract class aCssToken + { + /** + * Returns the token as string. + * + * @return string + */ + abstract public function __toString(); + } + +/** + * Abstract definition of a for a ruleset start token. + * + * @package CssMin/Tokens + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +abstract class aCssRulesetStartToken extends aCssToken + { + + } + +/** + * Abstract definition of a for ruleset end token. + * + * @package CssMin/Tokens + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +abstract class aCssRulesetEndToken extends aCssToken + { + /** + * Implements {@link aCssToken::__toString()}. + * + * @return string + */ + public function __toString() + { + return "}"; + } + } + +/** + * Abstract definition of a parser plugin. + * + * Every parser plugin have to extend this class. A parser plugin contains the logic to parse one or aspects of a + * stylesheet. + * + * @package CssMin/Parser/Plugins + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +abstract class aCssParserPlugin + { + /** + * Plugin configuration. + * + * @var array + */ + protected $configuration = array(); + /** + * The CssParser of the plugin. + * + * @var CssParser + */ + protected $parser = null; + /** + * Plugin buffer. + * + * @var string + */ + protected $buffer = ""; + /** + * Constructor. + * + * @param CssParser $parser The CssParser object of this plugin. + * @param array $configuration Plugin configuration [optional] + * @return void + */ + public function __construct(CssParser $parser, array $configuration = null) + { + $this->configuration = $configuration; + $this->parser = $parser; + } + /** + * Returns the array of chars triggering the parser plugin. + * + * @return array + */ + abstract public function getTriggerChars(); + /** + * Returns the array of states triggering the parser plugin or FALSE if every state will trigger the parser plugin. + * + * @return array + */ + abstract public function getTriggerStates(); + /** + * Parser routine of the plugin. + * + * @param integer $index Current index + * @param string $char Current char + * @param string $previousChar Previous char + * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing + */ + abstract public function parse($index, $char, $previousChar, $state); + } + +/** + * Abstract definition of a minifier plugin class. + * + * Minifier plugin process the parsed tokens one by one to apply changes to the token. Every minifier plugin has to + * extend this class. + * + * @package CssMin/Minifier/Plugins + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +abstract class aCssMinifierPlugin + { + /** + * Plugin configuration. + * + * @var array + */ + protected $configuration = array(); + /** + * The CssMinifier of the plugin. + * + * @var CssMinifier + */ + protected $minifier = null; + /** + * Constructor. + * + * @param CssMinifier $minifier The CssMinifier object of this plugin. + * @param array $configuration Plugin configuration [optional] + * @return void + */ + public function __construct(CssMinifier $minifier, array $configuration = array()) + { + $this->configuration = $configuration; + $this->minifier = $minifier; + } + /** + * Apply the plugin to the token. + * + * @param aCssToken $token Token to process + * @return boolean Return TRUE to break the processing of this token; FALSE to continue + */ + abstract public function apply(aCssToken &$token); + /** + * -- + * + * @return array + */ + abstract public function getTriggerTokens(); + } + +/** + * Abstract definition of a minifier filter class. + * + * Minifier filters allows a pre-processing of the parsed token to add, edit or delete tokens. Every minifier filter + * has to extend this class. + * + * @package CssMin/Minifier/Filters + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +abstract class aCssMinifierFilter + { + /** + * Filter configuration. + * + * @var array + */ + protected $configuration = array(); + /** + * The CssMinifier of the filter. + * + * @var CssMinifier + */ + protected $minifier = null; + /** + * Constructor. + * + * @param CssMinifier $minifier The CssMinifier object of this plugin. + * @param array $configuration Filter configuration [optional] + * @return void + */ + public function __construct(CssMinifier $minifier, array $configuration = array()) + { + $this->configuration = $configuration; + $this->minifier = $minifier; + } + /** + * Filter the tokens. + * + * @param array $tokens Array of objects of type aCssToken + * @return integer Count of added, changed or removed tokens; a return value large than 0 will rebuild the array + */ + abstract public function apply(array &$tokens); + } + +/** + * Abstract formatter definition. + * + * Every formatter have to extend this class. + * + * @package CssMin/Formatter + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +abstract class aCssFormatter + { + /** + * Indent string. + * + * @var string + */ + protected $indent = " "; + /** + * Declaration padding. + * + * @var integer + */ + protected $padding = 0; + /** + * Tokens. + * + * @var array + */ + protected $tokens = array(); + /** + * Constructor. + * + * @param array $tokens Array of CssToken + * @param string $indent Indent string [optional] + * @param integer $padding Declaration value padding [optional] + */ + public function __construct(array $tokens, $indent = null, $padding = null) + { + $this->tokens = $tokens; + $this->indent = !is_null($indent) ? $indent : $this->indent; + $this->padding = !is_null($padding) ? $padding : $this->padding; + } + /** + * Returns the array of aCssToken as formatted string. + * + * @return string + */ + abstract public function __toString(); + } + +/** + * Abstract definition of a ruleset declaration token. + * + * @package CssMin/Tokens + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +abstract class aCssDeclarationToken extends aCssToken + { + /** + * Is the declaration flagged as important? + * + * @var boolean + */ + public $IsImportant = false; + /** + * Is the declaration flagged as last one of the ruleset? + * + * @var boolean + */ + public $IsLast = false; + /** + * Property name of the declaration. + * + * @var string + */ + public $Property = ""; + /** + * Value of the declaration. + * + * @var string + */ + public $Value = ""; + /** + * Set the properties of the @font-face declaration. + * + * @param string $property Property of the declaration + * @param string $value Value of the declaration + * @param boolean $isImportant Is the !important flag is set? + * @param boolean $IsLast Is the declaration the last one of the block? + * @return void + */ + public function __construct($property, $value, $isImportant = false, $isLast = false) + { + $this->Property = $property; + $this->Value = $value; + $this->IsImportant = $isImportant; + $this->IsLast = $isLast; + } + /** + * Implements {@link aCssToken::__toString()}. + * + * @return string + */ + public function __toString() + { + return $this->Property . ":" . $this->Value . ($this->IsImportant ? " !important" : "") . ($this->IsLast ? "" : ";"); + } + } + +/** + * Abstract definition of a for at-rule block start token. + * + * @package CssMin/Tokens + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +abstract class aCssAtBlockStartToken extends aCssToken + { + + } + +/** + * Abstract definition of a for at-rule block end token. + * + * @package CssMin/Tokens + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +abstract class aCssAtBlockEndToken extends aCssToken + { + /** + * Implements {@link aCssToken::__toString()}. + * + * @return string + */ + public function __toString() + { + return "}"; + } + } + +/** + * {@link aCssFromatter Formatter} returning the CSS source in {@link http://goo.gl/etzLs Whitesmiths indent style}. + * + * @package CssMin/Formatter + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssWhitesmithsFormatter extends aCssFormatter + { + /** + * Implements {@link aCssFormatter::__toString()}. + * + * @return string + */ + public function __toString() + { + $r = array(); + $level = 0; + for ($i = 0, $l = count($this->tokens); $i < $l; $i++) + { + $token = $this->tokens[$i]; + $class = get_class($token); + $indent = str_repeat($this->indent, $level); + if ($class === "CssCommentToken") + { + $lines = array_map("trim", explode("\n", $token->Comment)); + for ($ii = 0, $ll = count($lines); $ii < $ll; $ii++) + { + $r[] = $indent . (substr($lines[$ii], 0, 1) == "*" ? " " : "") . $lines[$ii]; + } + } + elseif ($class === "CssAtCharsetToken") + { + $r[] = $indent . "@charset " . $token->Charset . ";"; + } + elseif ($class === "CssAtFontFaceStartToken") + { + $r[] = $indent . "@font-face"; + $r[] = $this->indent . $indent . "{"; + $level++; + } + elseif ($class === "CssAtImportToken") + { + $r[] = $indent . "@import " . $token->Import . " " . implode(", ", $token->MediaTypes) . ";"; + } + elseif ($class === "CssAtKeyframesStartToken") + { + $r[] = $indent . "@keyframes \"" . $token->Name . "\""; + $r[] = $this->indent . $indent . "{"; + $level++; + } + elseif ($class === "CssAtMediaStartToken") + { + $r[] = $indent . "@media " . implode(", ", $token->MediaTypes); + $r[] = $this->indent . $indent . "{"; + $level++; + } + elseif ($class === "CssAtPageStartToken") + { + $r[] = $indent . "@page"; + $r[] = $this->indent . $indent . "{"; + $level++; + } + elseif ($class === "CssAtVariablesStartToken") + { + $r[] = $indent . "@variables " . implode(", ", $token->MediaTypes); + $r[] = $this->indent . $indent . "{"; + $level++; + } + elseif ($class === "CssRulesetStartToken" || $class === "CssAtKeyframesRulesetStartToken") + { + $r[] = $indent . implode(", ", $token->Selectors); + $r[] = $this->indent . $indent . "{"; + $level++; + } + elseif ($class == "CssAtFontFaceDeclarationToken" + || $class === "CssAtKeyframesRulesetDeclarationToken" + || $class === "CssAtPageDeclarationToken" + || $class == "CssAtVariablesDeclarationToken" + || $class === "CssRulesetDeclarationToken" + ) + { + $declaration = $indent . $token->Property . ": "; + if ($this->padding) + { + $declaration = str_pad($declaration, $this->padding, " ", STR_PAD_RIGHT); + } + $r[] = $declaration . $token->Value . ($token->IsImportant ? " !important" : "") . ";"; + } + elseif ($class === "CssAtFontFaceEndToken" + || $class === "CssAtMediaEndToken" + || $class === "CssAtKeyframesEndToken" + || $class === "CssAtKeyframesRulesetEndToken" + || $class === "CssAtPageEndToken" + || $class === "CssAtVariablesEndToken" + || $class === "CssRulesetEndToken" + ) + { + $r[] = $indent . "}"; + $level--; + } + } + return implode("\n", $r); + } + } + +/** + * This {@link aCssMinifierPlugin} will process var-statement and sets the declaration value to the variable value. + * + * This plugin only apply the variable values. The variable values itself will get parsed by the + * {@link CssVariablesMinifierFilter}. + * + * Example: + * + * @variables + * { + * defaultColor: black; + * } + * color: var(defaultColor); + * + * + * Will get converted to: + * + * color:black; + * + * + * @package CssMin/Minifier/Plugins + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssVariablesMinifierPlugin extends aCssMinifierPlugin + { + /** + * Regular expression matching a value. + * + * @var string + */ + private $reMatch = "/var\((.+)\)/iSU"; + /** + * Parsed variables. + * + * @var array + */ + private $variables = null; + /** + * Returns the variables. + * + * @return array + */ + public function getVariables() + { + return $this->variables; + } + /** + * Implements {@link aCssMinifierPlugin::minify()}. + * + * @param aCssToken $token Token to process + * @return boolean Return TRUE to break the processing of this token; FALSE to continue + */ + public function apply(aCssToken &$token) + { + if (stripos($token->Value, "var") !== false && preg_match_all($this->reMatch, $token->Value, $m)) + { + $mediaTypes = $token->MediaTypes; + if (!in_array("all", $mediaTypes)) + { + $mediaTypes[] = "all"; + } + for ($i = 0, $l = count($m[0]); $i < $l; $i++) + { + $variable = trim($m[1][$i]); + foreach ($mediaTypes as $mediaType) + { + if (isset($this->variables[$mediaType], $this->variables[$mediaType][$variable])) + { + // Variable value found => set the declaration value to the variable value and return + $token->Value = str_replace($m[0][$i], $this->variables[$mediaType][$variable], $token->Value); + continue 2; + } + } + // If no value was found trigger an error and replace the token with a CssNullToken + CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": No value found for variable " . $variable . " in media types " . implode(", ", $mediaTypes) . "", (string) $token)); + $token = new CssNullToken(); + return true; + } + } + return false; + } + /** + * Implements {@link aMinifierPlugin::getTriggerTokens()} + * + * @return array + */ + public function getTriggerTokens() + { + return array + ( + "CssAtFontFaceDeclarationToken", + "CssAtPageDeclarationToken", + "CssRulesetDeclarationToken" + ); + } + /** + * Sets the variables. + * + * @param array $variables Variables to set + * @return void + */ + public function setVariables(array $variables) + { + $this->variables = $variables; + } + } + +/** + * This {@link aCssMinifierFilter minifier filter} will parse the variable declarations out of @variables at-rule + * blocks. The variables will get store in the {@link CssVariablesMinifierPlugin} that will apply the variables to + * declaration. + * + * @package CssMin/Minifier/Filters + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssVariablesMinifierFilter extends aCssMinifierFilter + { + /** + * Implements {@link aCssMinifierFilter::filter()}. + * + * @param array $tokens Array of objects of type aCssToken + * @return integer Count of added, changed or removed tokens; a return value large than 0 will rebuild the array + */ + public function apply(array &$tokens) + { + $variables = array(); + $defaultMediaTypes = array("all"); + $mediaTypes = array(); + $remove = array(); + for($i = 0, $l = count($tokens); $i < $l; $i++) + { + // @variables at-rule block found + if (get_class($tokens[$i]) === "CssAtVariablesStartToken") + { + $remove[] = $i; + $mediaTypes = (count($tokens[$i]->MediaTypes) == 0 ? $defaultMediaTypes : $tokens[$i]->MediaTypes); + foreach ($mediaTypes as $mediaType) + { + if (!isset($variables[$mediaType])) + { + $variables[$mediaType] = array(); + } + } + // Read the variable declaration tokens + for($i = $i; $i < $l; $i++) + { + // Found a variable declaration => read the variable values + if (get_class($tokens[$i]) === "CssAtVariablesDeclarationToken") + { + foreach ($mediaTypes as $mediaType) + { + $variables[$mediaType][$tokens[$i]->Property] = $tokens[$i]->Value; + } + $remove[] = $i; + } + // Found the variables end token => break; + elseif (get_class($tokens[$i]) === "CssAtVariablesEndToken") + { + $remove[] = $i; + break; + } + } + } + } + // Variables in @variables at-rule blocks + foreach($variables as $mediaType => $null) + { + foreach($variables[$mediaType] as $variable => $value) + { + // If a var() statement in a variable value found... + if (stripos($value, "var") !== false && preg_match_all("/var\((.+)\)/iSU", $value, $m)) + { + // ... then replace the var() statement with the variable values. + for ($i = 0, $l = count($m[0]); $i < $l; $i++) + { + $variables[$mediaType][$variable] = str_replace($m[0][$i], (isset($variables[$mediaType][$m[1][$i]]) ? $variables[$mediaType][$m[1][$i]] : ""), $variables[$mediaType][$variable]); + } + } + } + } + // Remove the complete @variables at-rule block + foreach ($remove as $i) + { + $tokens[$i] = null; + } + if (!($plugin = $this->minifier->getPlugin("CssVariablesMinifierPlugin"))) + { + CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": The plugin CssVariablesMinifierPlugin was not found but is required for " . __CLASS__ . "")); + } + else + { + $plugin->setVariables($variables); + } + return count($remove); + } + } + +/** + * {@link aCssParserPlugin Parser plugin} for preserve parsing url() values. + * + * This plugin return no {@link aCssToken CssToken} but ensures that url() values will get parsed properly. + * + * @package CssMin/Parser/Plugins + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssUrlParserPlugin extends aCssParserPlugin + { + /** + * Implements {@link aCssParserPlugin::getTriggerChars()}. + * + * @return array + */ + public function getTriggerChars() + { + return array("(", ")"); + } + /** + * Implements {@link aCssParserPlugin::getTriggerStates()}. + * + * @return array + */ + public function getTriggerStates() + { + return false; + } + /** + * Implements {@link aCssParserPlugin::parse()}. + * + * @param integer $index Current index + * @param string $char Current char + * @param string $previousChar Previous char + * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing + */ + public function parse($index, $char, $previousChar, $state) + { + // Start of string + if ($char === "(" && strtolower(substr($this->parser->getSource(), $index - 3, 4)) === "url(" && $state !== "T_URL") + { + $this->parser->pushState("T_URL"); + $this->parser->setExclusive(__CLASS__); + } + // Escaped LF in url => remove escape backslash and LF + elseif ($char === "\n" && $previousChar === "\\" && $state === "T_URL") + { + $this->parser->setBuffer(substr($this->parser->getBuffer(), 0, -2)); + } + // Parse error: Unescaped LF in string literal + elseif ($char === "\n" && $previousChar !== "\\" && $state === "T_URL") + { + $line = $this->parser->getBuffer(); + $this->parser->setBuffer(substr($this->parser->getBuffer(), 0, -1) . ")"); // Replace the LF with the url string delimiter + $this->parser->popState(); + $this->parser->unsetExclusive(); + CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Unterminated string literal", $line . "_")); + } + // End of string + elseif ($char === ")" && $state === "T_URL") + { + $this->parser->popState(); + $this->parser->unsetExclusive(); + } + else + { + return false; + } + return true; + } + } + +/** + * {@link aCssParserPlugin Parser plugin} for preserve parsing string values. + * + * This plugin return no {@link aCssToken CssToken} but ensures that string values will get parsed properly. + * + * @package CssMin/Parser/Plugins + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssStringParserPlugin extends aCssParserPlugin + { + /** + * Current string delimiter char. + * + * @var string + */ + private $delimiterChar = null; + /** + * Implements {@link aCssParserPlugin::getTriggerChars()}. + * + * @return array + */ + public function getTriggerChars() + { + return array("\"", "'", "\n"); + } + /** + * Implements {@link aCssParserPlugin::getTriggerStates()}. + * + * @return array + */ + public function getTriggerStates() + { + return false; + } + /** + * Implements {@link aCssParserPlugin::parse()}. + * + * @param integer $index Current index + * @param string $char Current char + * @param string $previousChar Previous char + * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing + */ + public function parse($index, $char, $previousChar, $state) + { + // Start of string + if (($char === "\"" || $char === "'") && $state !== "T_STRING") + { + $this->delimiterChar = $char; + $this->parser->pushState("T_STRING"); + $this->parser->setExclusive(__CLASS__); + } + // Escaped LF in string => remove escape backslash and LF + elseif ($char === "\n" && $previousChar === "\\" && $state === "T_STRING") + { + $this->parser->setBuffer(substr($this->parser->getBuffer(), 0, -2)); + } + // Parse error: Unescaped LF in string literal + elseif ($char === "\n" && $previousChar !== "\\" && $state === "T_STRING") + { + $line = $this->parser->getBuffer(); + $this->parser->popState(); + $this->parser->unsetExclusive(); + $this->parser->setBuffer(substr($this->parser->getBuffer(), 0, -1) . $this->delimiterChar); // Replace the LF with the current string char + CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Unterminated string literal", $line . "_")); + $this->delimiterChar = null; + } + // End of string + elseif ($char === $this->delimiterChar && $state === "T_STRING") + { + // If the Previous char is a escape char count the amount of the previous escape chars. If the amount of + // escape chars is uneven do not end the string + if ($previousChar == "\\") + { + $source = $this->parser->getSource(); + $c = 1; + $i = $index - 2; + while (substr($source, $i, 1) === "\\") + { + $c++; $i--; + } + if ($c % 2) + { + return false; + } + } + $this->parser->popState(); + $this->parser->unsetExclusive(); + $this->delimiterChar = null; + } + else + { + return false; + } + return true; + } + } + +/** + * This {@link aCssMinifierFilter minifier filter} sorts the ruleset declarations of a ruleset by name. + * + * @package CssMin/Minifier/Filters + * @link http://code.google.com/p/cssmin/ + * @author Rowan Beentje + * @copyright Rowan Beentje + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssSortRulesetPropertiesMinifierFilter extends aCssMinifierFilter + { + /** + * Implements {@link aCssMinifierFilter::filter()}. + * + * @param array $tokens Array of objects of type aCssToken + * @return integer Count of added, changed or removed tokens; a return value larger than 0 will rebuild the array + */ + public function apply(array &$tokens) + { + $r = 0; + for ($i = 0, $l = count($tokens); $i < $l; $i++) + { + // Only look for ruleset start rules + if (get_class($tokens[$i]) !== "CssRulesetStartToken") { continue; } + // Look for the corresponding ruleset end + $endIndex = false; + for ($ii = $i + 1; $ii < $l; $ii++) + { + if (get_class($tokens[$ii]) !== "CssRulesetEndToken") { continue; } + $endIndex = $ii; + break; + } + if (!$endIndex) { break; } + $startIndex = $i; + $i = $endIndex; + // Skip if there's only one token in this ruleset + if ($endIndex - $startIndex <= 2) { continue; } + // Ensure that everything between the start and end is a declaration token, for safety + for ($ii = $startIndex + 1; $ii < $endIndex; $ii++) + { + if (get_class($tokens[$ii]) !== "CssRulesetDeclarationToken") { continue(2); } + } + $declarations = array_slice($tokens, $startIndex + 1, $endIndex - $startIndex - 1); + // Check whether a sort is required + $sortRequired = $lastPropertyName = false; + foreach ($declarations as $declaration) + { + if ($lastPropertyName) + { + if (strcmp($lastPropertyName, $declaration->Property) > 0) + { + $sortRequired = true; + break; + } + } + $lastPropertyName = $declaration->Property; + } + if (!$sortRequired) { continue; } + // Arrange the declarations alphabetically by name + usort($declarations, array(__CLASS__, "userDefinedSort1")); + // Update "IsLast" property + for ($ii = 0, $ll = count($declarations) - 1; $ii <= $ll; $ii++) + { + if ($ii == $ll) + { + $declarations[$ii]->IsLast = true; + } + else + { + $declarations[$ii]->IsLast = false; + } + } + // Splice back into the array. + array_splice($tokens, $startIndex + 1, $endIndex - $startIndex - 1, $declarations); + $r += $endIndex - $startIndex - 1; + } + return $r; + } + /** + * User defined sort function. + * + * @return integer + */ + public static function userDefinedSort1($a, $b) + { + return strcmp($a->Property, $b->Property); + } + } + +/** + * This {@link aCssToken CSS token} represents the start of a ruleset. + * + * @package CssMin/Tokens + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssRulesetStartToken extends aCssRulesetStartToken + { + /** + * Array of selectors. + * + * @var array + */ + public $Selectors = array(); + /** + * Set the properties of a ruleset token. + * + * @param array $selectors Selectors of the ruleset + * @return void + */ + public function __construct(array $selectors = array()) + { + $this->Selectors = $selectors; + } + /** + * Implements {@link aCssToken::__toString()}. + * + * @return string + */ + public function __toString() + { + return implode(",", $this->Selectors) . "{"; + } + } + +/** + * {@link aCssParserPlugin Parser plugin} for parsing ruleset block with including declarations. + * + * Found rulesets will add a {@link CssRulesetStartToken} and {@link CssRulesetEndToken} to the + * parser; including declarations as {@link CssRulesetDeclarationToken}. + * + * @package CssMin/Parser/Plugins + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssRulesetParserPlugin extends aCssParserPlugin + { + /** + * Implements {@link aCssParserPlugin::getTriggerChars()}. + * + * @return array + */ + public function getTriggerChars() + { + return array(",", "{", "}", ":", ";"); + } + /** + * Implements {@link aCssParserPlugin::getTriggerStates()}. + * + * @return array + */ + public function getTriggerStates() + { + return array("T_DOCUMENT", "T_AT_MEDIA", "T_RULESET::SELECTORS", "T_RULESET", "T_RULESET_DECLARATION"); + } + /** + * Selectors. + * + * @var array + */ + private $selectors = array(); + /** + * Implements {@link aCssParserPlugin::parse()}. + * + * @param integer $index Current index + * @param string $char Current char + * @param string $previousChar Previous char + * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing + */ + public function parse($index, $char, $previousChar, $state) + { + // Start of Ruleset and selectors + if ($char === "," && ($state === "T_DOCUMENT" || $state === "T_AT_MEDIA" || $state === "T_RULESET::SELECTORS")) + { + if ($state !== "T_RULESET::SELECTORS") + { + $this->parser->pushState("T_RULESET::SELECTORS"); + } + $this->selectors[] = $this->parser->getAndClearBuffer(",{"); + } + // End of selectors and start of declarations + elseif ($char === "{" && ($state === "T_DOCUMENT" || $state === "T_AT_MEDIA" || $state === "T_RULESET::SELECTORS")) + { + if ($this->parser->getBuffer() !== "") + { + $this->selectors[] = $this->parser->getAndClearBuffer(",{"); + if ($state == "T_RULESET::SELECTORS") + { + $this->parser->popState(); + } + $this->parser->pushState("T_RULESET"); + $this->parser->appendToken(new CssRulesetStartToken($this->selectors)); + $this->selectors = array(); + } + } + // Start of declaration + elseif ($char === ":" && $state === "T_RULESET") + { + $this->parser->pushState("T_RULESET_DECLARATION"); + $this->buffer = $this->parser->getAndClearBuffer(":;", true); + } + // Unterminated ruleset declaration + elseif ($char === ":" && $state === "T_RULESET_DECLARATION") + { + // Ignore Internet Explorer filter declarations + if ($this->buffer === "filter") + { + return false; + } + CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Unterminated declaration", $this->buffer . ":" . $this->parser->getBuffer() . "_")); + } + // End of declaration + elseif (($char === ";" || $char === "}") && $state === "T_RULESET_DECLARATION") + { + $value = $this->parser->getAndClearBuffer(";}"); + if (strtolower(substr($value, -10, 10)) === "!important") + { + $value = trim(substr($value, 0, -10)); + $isImportant = true; + } + else + { + $isImportant = false; + } + $this->parser->popState(); + $this->parser->appendToken(new CssRulesetDeclarationToken($this->buffer, $value, $this->parser->getMediaTypes(), $isImportant)); + // Declaration ends with a right curly brace; so we have to end the ruleset + if ($char === "}") + { + $this->parser->appendToken(new CssRulesetEndToken()); + $this->parser->popState(); + } + $this->buffer = ""; + } + // End of ruleset + elseif ($char === "}" && $state === "T_RULESET") + { + $this->parser->popState(); + $this->parser->clearBuffer(); + $this->parser->appendToken(new CssRulesetEndToken()); + $this->buffer = ""; + $this->selectors = array(); + } + else + { + return false; + } + return true; + } + } + +/** + * This {@link aCssToken CSS token} represents the end of a ruleset. + * + * @package CssMin/Tokens + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssRulesetEndToken extends aCssRulesetEndToken + { + + } + +/** + * This {@link aCssToken CSS token} represents a ruleset declaration. + * + * @package CssMin/Tokens + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssRulesetDeclarationToken extends aCssDeclarationToken + { + /** + * Media types of the declaration. + * + * @var array + */ + public $MediaTypes = array("all"); + /** + * Set the properties of a ddocument- or at-rule @media level declaration. + * + * @param string $property Property of the declaration + * @param string $value Value of the declaration + * @param mixed $mediaTypes Media types of the declaration + * @param boolean $isImportant Is the !important flag is set + * @param boolean $isLast Is the declaration the last one of the ruleset + * @return void + */ + public function __construct($property, $value, $mediaTypes = null, $isImportant = false, $isLast = false) + { + parent::__construct($property, $value, $isImportant, $isLast); + $this->MediaTypes = $mediaTypes ? $mediaTypes : array("all"); + } + } + +/** + * This {@link aCssMinifierFilter minifier filter} sets the IsLast property of any last declaration in a ruleset, + * @font-face at-rule or @page at-rule block. If the property IsLast is TRUE the decrations will get stringified + * without tailing semicolon. + * + * @package CssMin/Minifier/Filters + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssRemoveLastDelarationSemiColonMinifierFilter extends aCssMinifierFilter + { + /** + * Implements {@link aCssMinifierFilter::filter()}. + * + * @param array $tokens Array of objects of type aCssToken + * @return integer Count of added, changed or removed tokens; a return value large than 0 will rebuild the array + */ + public function apply(array &$tokens) + { + for ($i = 0, $l = count($tokens); $i < $l; $i++) + { + $current = get_class($tokens[$i]); + $next = isset($tokens[$i+1]) ? get_class($tokens[$i+1]) : false; + if (($current === "CssRulesetDeclarationToken" && $next === "CssRulesetEndToken") || + ($current === "CssAtFontFaceDeclarationToken" && $next === "CssAtFontFaceEndToken") || + ($current === "CssAtPageDeclarationToken" && $next === "CssAtPageEndToken")) + { + $tokens[$i]->IsLast = true; + } + } + return 0; + } + } + +/** + * This {@link aCssMinifierFilter minifier filter} will remove any empty rulesets (including @keyframes at-rule block + * rulesets). + * + * @package CssMin/Minifier/Filters + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssRemoveEmptyRulesetsMinifierFilter extends aCssMinifierFilter + { + /** + * Implements {@link aCssMinifierFilter::filter()}. + * + * @param array $tokens Array of objects of type aCssToken + * @return integer Count of added, changed or removed tokens; a return value large than 0 will rebuild the array + */ + public function apply(array &$tokens) + { + $r = 0; + for ($i = 0, $l = count($tokens); $i < $l; $i++) + { + $current = get_class($tokens[$i]); + $next = isset($tokens[$i + 1]) ? get_class($tokens[$i + 1]) : false; + if (($current === "CssRulesetStartToken" && $next === "CssRulesetEndToken") || + ($current === "CssAtKeyframesRulesetStartToken" && $next === "CssAtKeyframesRulesetEndToken" && !array_intersect(array("from", "0%", "to", "100%"), array_map("strtolower", $tokens[$i]->Selectors))) + ) + { + $tokens[$i] = null; + $tokens[$i + 1] = null; + $i++; + $r = $r + 2; + } + } + return $r; + } + } + +/** + * This {@link aCssMinifierFilter minifier filter} will remove any empty @font-face, @keyframes, @media and @page + * at-rule blocks. + * + * @package CssMin/Minifier/Filters + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssRemoveEmptyAtBlocksMinifierFilter extends aCssMinifierFilter + { + /** + * Implements {@link aCssMinifierFilter::filter()}. + * + * @param array $tokens Array of objects of type aCssToken + * @return integer Count of added, changed or removed tokens; a return value large than 0 will rebuild the array + */ + public function apply(array &$tokens) + { + $r = 0; + for ($i = 0, $l = count($tokens); $i < $l; $i++) + { + $current = get_class($tokens[$i]); + $next = isset($tokens[$i + 1]) ? get_class($tokens[$i + 1]) : false; + if (($current === "CssAtFontFaceStartToken" && $next === "CssAtFontFaceEndToken") || + ($current === "CssAtKeyframesStartToken" && $next === "CssAtKeyframesEndToken") || + ($current === "CssAtPageStartToken" && $next === "CssAtPageEndToken") || + ($current === "CssAtMediaStartToken" && $next === "CssAtMediaEndToken")) + { + $tokens[$i] = null; + $tokens[$i + 1] = null; + $i++; + $r = $r + 2; + } + } + return $r; + } + } + +/** + * This {@link aCssMinifierFilter minifier filter} will remove any comments from the array of parsed tokens. + * + * @package CssMin/Minifier/Filters + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssRemoveCommentsMinifierFilter extends aCssMinifierFilter + { + /** + * Implements {@link aCssMinifierFilter::filter()}. + * + * @param array $tokens Array of objects of type aCssToken + * @return integer Count of added, changed or removed tokens; a return value large than 0 will rebuild the array + */ + public function apply(array &$tokens) + { + $r = 0; + for ($i = 0, $l = count($tokens); $i < $l; $i++) + { + if (get_class($tokens[$i]) === "CssCommentToken") + { + $tokens[$i] = null; + $r++; + } + } + return $r; + } + } + +/** + * CSS Parser. + * + * @package CssMin/Parser + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssParser + { + /** + * Parse buffer. + * + * @var string + */ + private $buffer = ""; + /** + * {@link aCssParserPlugin Plugins}. + * + * @var array + */ + private $plugins = array(); + /** + * Source to parse. + * + * @var string + */ + private $source = ""; + /** + * Current state. + * + * @var integer + */ + private $state = "T_DOCUMENT"; + /** + * Exclusive state. + * + * @var string + */ + private $stateExclusive = false; + /** + * Media types state. + * + * @var mixed + */ + private $stateMediaTypes = false; + /** + * State stack. + * + * @var array + */ + private $states = array("T_DOCUMENT"); + /** + * Parsed tokens. + * + * @var array + */ + private $tokens = array(); + /** + * Constructer. + * + * Create instances of the used {@link aCssParserPlugin plugins}. + * + * @param string $source CSS source [optional] + * @param array $plugins Plugin configuration [optional] + * @return void + */ + public function __construct($source = null, array $plugins = null) + { + $plugins = array_merge(array + ( + "Comment" => true, + "String" => true, + "Url" => true, + "Expression" => true, + "Ruleset" => true, + "AtCharset" => true, + "AtFontFace" => true, + "AtImport" => true, + "AtKeyframes" => true, + "AtMedia" => true, + "AtPage" => true, + "AtVariables" => true + ), is_array($plugins) ? $plugins : array()); + // Create plugin instances + foreach ($plugins as $name => $config) + { + if ($config !== false) + { + $class = "Css" . $name . "ParserPlugin"; + $config = is_array($config) ? $config : array(); + if (class_exists($class)) + { + $this->plugins[] = new $class($this, $config); + } + else + { + CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": The plugin " . $name . " with the class name " . $class . " was not found")); + } + } + } + if (!is_null($source)) + { + $this->parse($source); + } + } + /** + * Append a token to the array of tokens. + * + * @param aCssToken $token Token to append + * @return void + */ + public function appendToken(aCssToken $token) + { + $this->tokens[] = $token; + } + /** + * Clears the current buffer. + * + * @return void + */ + public function clearBuffer() + { + $this->buffer = ""; + } + /** + * Returns and clear the current buffer. + * + * @param string $trim Chars to use to trim the returned buffer + * @param boolean $tolower if TRUE the returned buffer will get converted to lower case + * @return string + */ + public function getAndClearBuffer($trim = "", $tolower = false) + { + $r = $this->getBuffer($trim, $tolower); + $this->buffer = ""; + return $r; + } + /** + * Returns the current buffer. + * + * @param string $trim Chars to use to trim the returned buffer + * @param boolean $tolower if TRUE the returned buffer will get converted to lower case + * @return string + */ + public function getBuffer($trim = "", $tolower = false) + { + $r = $this->buffer; + if ($trim) + { + $r = trim($r, " \t\n\r\0\x0B" . $trim); + } + if ($tolower) + { + $r = strtolower($r); + } + return $r; + } + /** + * Returns the current media types state. + * + * @return array + */ + public function getMediaTypes() + { + return $this->stateMediaTypes; + } + /** + * Returns the CSS source. + * + * @return string + */ + public function getSource() + { + return $this->source; + } + /** + * Returns the current state. + * + * @return integer The current state + */ + public function getState() + { + return $this->state; + } + /** + * Returns a plugin by class name. + * + * @param string $name Class name of the plugin + * @return aCssParserPlugin + */ + public function getPlugin($class) + { + static $index = null; + if (is_null($index)) + { + $index = array(); + for ($i = 0, $l = count($this->plugins); $i < $l; $i++) + { + $index[get_class($this->plugins[$i])] = $i; + } + } + return isset($index[$class]) ? $this->plugins[$index[$class]] : false; + } + /** + * Returns the parsed tokens. + * + * @return array + */ + public function getTokens() + { + return $this->tokens; + } + /** + * Returns if the current state equals the passed state. + * + * @param integer $state State to compare with the current state + * @return boolean TRUE is the state equals to the passed state; FALSE if not + */ + public function isState($state) + { + return ($this->state == $state); + } + /** + * Parse the CSS source and return a array with parsed tokens. + * + * @param string $source CSS source + * @return array Array with tokens + */ + public function parse($source) + { + // Reset + $this->source = ""; + $this->tokens = array(); + // Create a global and plugin lookup table for trigger chars; set array of plugins as local variable and create + // several helper variables for plugin handling + $globalTriggerChars = ""; + $plugins = $this->plugins; + $pluginCount = count($plugins); + $pluginIndex = array(); + $pluginTriggerStates = array(); + $pluginTriggerChars = array(); + for ($i = 0, $l = count($plugins); $i < $l; $i++) + { + $tPluginClassName = get_class($plugins[$i]); + $pluginTriggerChars[$i] = implode("", $plugins[$i]->getTriggerChars()); + $tPluginTriggerStates = $plugins[$i]->getTriggerStates(); + $pluginTriggerStates[$i] = $tPluginTriggerStates === false ? false : "|" . implode("|", $tPluginTriggerStates) . "|"; + $pluginIndex[$tPluginClassName] = $i; + for ($ii = 0, $ll = strlen($pluginTriggerChars[$i]); $ii < $ll; $ii++) + { + $c = substr($pluginTriggerChars[$i], $ii, 1); + if (strpos($globalTriggerChars, $c) === false) + { + $globalTriggerChars .= $c; + } + } + } + // Normalise line endings + $source = str_replace("\r\n", "\n", $source); // Windows to Unix line endings + $source = str_replace("\r", "\n", $source); // Mac to Unix line endings + $this->source = $source; + // Variables + $buffer = &$this->buffer; + $exclusive = &$this->stateExclusive; + $state = &$this->state; + $c = $p = null; + // -- + for ($i = 0, $l = strlen($source); $i < $l; $i++) + { + // Set the current Char + $c = $source[$i]; // Is faster than: $c = substr($source, $i, 1); + // Normalize and filter double whitespace characters + if ($exclusive === false) + { + if ($c === "\n" || $c === "\t") + { + $c = " "; + } + if ($c === " " && $p === " ") + { + continue; + } + } + $buffer .= $c; + // Extended processing only if the current char is a global trigger char + if (strpos($globalTriggerChars, $c) !== false) + { + // Exclusive state is set; process with the exclusive plugin + if ($exclusive) + { + $tPluginIndex = $pluginIndex[$exclusive]; + if (strpos($pluginTriggerChars[$tPluginIndex], $c) !== false && ($pluginTriggerStates[$tPluginIndex] === false || strpos($pluginTriggerStates[$tPluginIndex], $state) !== false)) + { + $r = $plugins[$tPluginIndex]->parse($i, $c, $p, $state); + // Return value is TRUE => continue with next char + if ($r === true) + { + continue; + } + // Return value is numeric => set new index and continue with next char + elseif ($r !== false && $r != $i) + { + $i = $r; + continue; + } + } + } + // Else iterate through the plugins + else + { + $triggerState = "|" . $state . "|"; + for ($ii = 0, $ll = $pluginCount; $ii < $ll; $ii++) + { + // Only process if the current char is one of the plugin trigger chars + if (strpos($pluginTriggerChars[$ii], $c) !== false && ($pluginTriggerStates[$ii] === false || strpos($pluginTriggerStates[$ii], $triggerState) !== false)) + { + // Process with the plugin + $r = $plugins[$ii]->parse($i, $c, $p, $state); + // Return value is TRUE => break the plugin loop and and continue with next char + if ($r === true) + { + break; + } + // Return value is numeric => set new index, break the plugin loop and and continue with next char + elseif ($r !== false && $r != $i) + { + $i = $r; + break; + } + } + } + } + } + $p = $c; // Set the parent char + } + return $this->tokens; + } + /** + * Remove the last state of the state stack and return the removed stack value. + * + * @return integer Removed state value + */ + public function popState() + { + $r = array_pop($this->states); + $this->state = $this->states[count($this->states) - 1]; + return $r; + } + /** + * Adds a new state onto the state stack. + * + * @param integer $state State to add onto the state stack. + * @return integer The index of the added state in the state stacks + */ + public function pushState($state) + { + $r = array_push($this->states, $state); + $this->state = $this->states[count($this->states) - 1]; + return $r; + } + /** + * Sets/restores the buffer. + * + * @param string $buffer Buffer to set + * @return void + */ + public function setBuffer($buffer) + { + $this->buffer = $buffer; + } + /** + * Set the exclusive state. + * + * @param string $exclusive Exclusive state + * @return void + */ + public function setExclusive($exclusive) + { + $this->stateExclusive = $exclusive; + } + /** + * Set the media types state. + * + * @param array $mediaTypes Media types state + * @return void + */ + public function setMediaTypes(array $mediaTypes) + { + $this->stateMediaTypes = $mediaTypes; + } + /** + * Sets the current state in the state stack; equals to {@link CssParser::popState()} + {@link CssParser::pushState()}. + * + * @param integer $state State to set + * @return integer + */ + public function setState($state) + { + $r = array_pop($this->states); + array_push($this->states, $state); + $this->state = $this->states[count($this->states) - 1]; + return $r; + } + /** + * Removes the exclusive state. + * + * @return void + */ + public function unsetExclusive() + { + $this->stateExclusive = false; + } + /** + * Removes the media types state. + * + * @return void + */ + public function unsetMediaTypes() + { + $this->stateMediaTypes = false; + } + } + +/** + * {@link aCssFromatter Formatter} returning the CSS source in {@link http://goo.gl/j4XdU OTBS indent style} (The One True Brace Style). + * + * @package CssMin/Formatter + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssOtbsFormatter extends aCssFormatter + { + /** + * Implements {@link aCssFormatter::__toString()}. + * + * @return string + */ + public function __toString() + { + $r = array(); + $level = 0; + for ($i = 0, $l = count($this->tokens); $i < $l; $i++) + { + $token = $this->tokens[$i]; + $class = get_class($token); + $indent = str_repeat($this->indent, $level); + if ($class === "CssCommentToken") + { + $lines = array_map("trim", explode("\n", $token->Comment)); + for ($ii = 0, $ll = count($lines); $ii < $ll; $ii++) + { + $r[] = $indent . (substr($lines[$ii], 0, 1) == "*" ? " " : "") . $lines[$ii]; + } + } + elseif ($class === "CssAtCharsetToken") + { + $r[] = $indent . "@charset " . $token->Charset . ";"; + } + elseif ($class === "CssAtFontFaceStartToken") + { + $r[] = $indent . "@font-face {"; + $level++; + } + elseif ($class === "CssAtImportToken") + { + $r[] = $indent . "@import " . $token->Import . " " . implode(", ", $token->MediaTypes) . ";"; + } + elseif ($class === "CssAtKeyframesStartToken") + { + $r[] = $indent . "@keyframes \"" . $token->Name . "\" {"; + $level++; + } + elseif ($class === "CssAtMediaStartToken") + { + $r[] = $indent . "@media " . implode(", ", $token->MediaTypes) . " {"; + $level++; + } + elseif ($class === "CssAtPageStartToken") + { + $r[] = $indent . "@page {"; + $level++; + } + elseif ($class === "CssAtVariablesStartToken") + { + $r[] = $indent . "@variables " . implode(", ", $token->MediaTypes) . " {"; + $level++; + } + elseif ($class === "CssRulesetStartToken" || $class === "CssAtKeyframesRulesetStartToken") + { + $r[] = $indent . implode(", ", $token->Selectors) . " {"; + $level++; + } + elseif ($class == "CssAtFontFaceDeclarationToken" + || $class === "CssAtKeyframesRulesetDeclarationToken" + || $class === "CssAtPageDeclarationToken" + || $class == "CssAtVariablesDeclarationToken" + || $class === "CssRulesetDeclarationToken" + ) + { + $declaration = $indent . $token->Property . ": "; + if ($this->padding) + { + $declaration = str_pad($declaration, $this->padding, " ", STR_PAD_RIGHT); + } + $r[] = $declaration . $token->Value . ($token->IsImportant ? " !important" : "") . ";"; + } + elseif ($class === "CssAtFontFaceEndToken" + || $class === "CssAtMediaEndToken" + || $class === "CssAtKeyframesEndToken" + || $class === "CssAtKeyframesRulesetEndToken" + || $class === "CssAtPageEndToken" + || $class === "CssAtVariablesEndToken" + || $class === "CssRulesetEndToken" + ) + { + $level--; + $r[] = str_repeat($indent, $level) . "}"; + } + } + return implode("\n", $r); + } + } + +/** + * This {@link aCssToken CSS token} is a utility token that extends {@link aNullToken} and returns only a empty string. + * + * @package CssMin/Tokens + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssNullToken extends aCssToken + { + /** + * Implements {@link aCssToken::__toString()}. + * + * @return string + */ + public function __toString() + { + return ""; + } + } + +/** + * CSS Minifier. + * + * @package CssMin/Minifier + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssMinifier + { + /** + * {@link aCssMinifierFilter Filters}. + * + * @var array + */ + private $filters = array(); + /** + * {@link aCssMinifierPlugin Plugins}. + * + * @var array + */ + private $plugins = array(); + /** + * Minified source. + * + * @var string + */ + private $minified = ""; + /** + * Constructer. + * + * Creates instances of {@link aCssMinifierFilter filters} and {@link aCssMinifierPlugin plugins}. + * + * @param string $source CSS source [optional] + * @param array $filters Filter configuration [optional] + * @param array $plugins Plugin configuration [optional] + * @return void + */ + public function __construct($source = null, array $filters = null, array $plugins = null) + { + $filters = array_merge(array + ( + "ImportImports" => false, + "RemoveComments" => true, + "RemoveEmptyRulesets" => true, + "RemoveEmptyAtBlocks" => true, + "ConvertLevel3Properties" => false, + "ConvertLevel3AtKeyframes" => false, + "Variables" => true, + "RemoveLastDelarationSemiColon" => true + ), is_array($filters) ? $filters : array()); + $plugins = array_merge(array + ( + "Variables" => true, + "ConvertFontWeight" => false, + "ConvertHslColors" => false, + "ConvertRgbColors" => false, + "ConvertNamedColors" => false, + "CompressColorValues" => false, + "CompressUnitValues" => false, + "CompressExpressionValues" => false + ), is_array($plugins) ? $plugins : array()); + // Filters + foreach ($filters as $name => $config) + { + if ($config !== false) + { + $class = "Css" . $name . "MinifierFilter"; + $config = is_array($config) ? $config : array(); + if (class_exists($class)) + { + $this->filters[] = new $class($this, $config); + } + else + { + CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": The filter " . $name . " with the class name " . $class . " was not found")); + } + } + } + // Plugins + foreach ($plugins as $name => $config) + { + if ($config !== false) + { + $class = "Css" . $name . "MinifierPlugin"; + $config = is_array($config) ? $config : array(); + if (class_exists($class)) + { + $this->plugins[] = new $class($this, $config); + } + else + { + CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": The plugin " . $name . " with the class name " . $class . " was not found")); + } + } + } + // -- + if (!is_null($source)) + { + $this->minify($source); + } + } + /** + * Returns the minified Source. + * + * @return string + */ + public function getMinified() + { + return $this->minified; + } + /** + * Returns a plugin by class name. + * + * @param string $name Class name of the plugin + * @return aCssMinifierPlugin + */ + public function getPlugin($class) + { + static $index = null; + if (is_null($index)) + { + $index = array(); + for ($i = 0, $l = count($this->plugins); $i < $l; $i++) + { + $index[get_class($this->plugins[$i])] = $i; + } + } + return isset($index[$class]) ? $this->plugins[$index[$class]] : false; + } + /** + * Minifies the CSS source. + * + * @param string $source CSS source + * @return string + */ + public function minify($source) + { + // Variables + $r = ""; + $parser = new CssParser($source); + $tokens = $parser->getTokens(); + $filters = $this->filters; + $filterCount = count($this->filters); + $plugins = $this->plugins; + $pluginCount = count($plugins); + $pluginIndex = array(); + $pluginTriggerTokens = array(); + $globalTriggerTokens = array(); + for ($i = 0, $l = count($plugins); $i < $l; $i++) + { + $tPluginClassName = get_class($plugins[$i]); + $pluginTriggerTokens[$i] = $plugins[$i]->getTriggerTokens(); + foreach ($pluginTriggerTokens[$i] as $v) + { + if (!in_array($v, $globalTriggerTokens)) + { + $globalTriggerTokens[] = $v; + } + } + $pluginTriggerTokens[$i] = "|" . implode("|", $pluginTriggerTokens[$i]) . "|"; + $pluginIndex[$tPluginClassName] = $i; + } + $globalTriggerTokens = "|" . implode("|", $globalTriggerTokens) . "|"; + /* + * Apply filters + */ + for($i = 0; $i < $filterCount; $i++) + { + // Apply the filter; if the return value is larger than 0... + if ($filters[$i]->apply($tokens) > 0) + { + // ...then filter null values and rebuild the token array + $tokens = array_values(array_filter($tokens)); + } + } + $tokenCount = count($tokens); + /* + * Apply plugins + */ + for($i = 0; $i < $tokenCount; $i++) + { + $triggerToken = "|" . get_class($tokens[$i]) . "|"; + if (strpos($globalTriggerTokens, $triggerToken) !== false) + { + for($ii = 0; $ii < $pluginCount; $ii++) + { + if (strpos($pluginTriggerTokens[$ii], $triggerToken) !== false || $pluginTriggerTokens[$ii] === false) + { + // Apply the plugin; if the return value is TRUE continue to the next token + if ($plugins[$ii]->apply($tokens[$i]) === true) + { + continue 2; + } + } + } + } + } + // Stringify the tokens + for($i = 0; $i < $tokenCount; $i++) + { + $r .= (string) $tokens[$i]; + } + $this->minified = $r; + return $r; + } + } + +/** + * CssMin - A (simple) css minifier with benefits + * + * -- + * Copyright (c) 2011 Joe Scylla + * + * 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. + * -- + * + * @package CssMin + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssMin + { + /** + * Index of classes + * + * @var array + */ + private static $classIndex = array(); + /** + * Parse/minify errors + * + * @var array + */ + private static $errors = array(); + /** + * Verbose output. + * + * @var boolean + */ + private static $isVerbose = false; + /** + * {@link http://goo.gl/JrW54 Autoload} function of CssMin. + * + * @param string $class Name of the class + * @return void + */ + public static function autoload($class) + { + if (isset(self::$classIndex[$class])) + { + require(self::$classIndex[$class]); + } + } + /** + * Return errors + * + * @return array of {CssError}. + */ + public static function getErrors() + { + return self::$errors; + } + /** + * Returns if there were errors. + * + * @return boolean + */ + public static function hasErrors() + { + return count(self::$errors) > 0; + } + /** + * Initialises CssMin. + * + * @return void + */ + public static function initialise() + { + // Create the class index for autoloading or including + $paths = array(dirname(__FILE__)); + while (list($i, $path) = each($paths)) + { + $subDirectorys = glob($path . "*", GLOB_MARK | GLOB_ONLYDIR | GLOB_NOSORT); + if (is_array($subDirectorys)) + { + foreach ($subDirectorys as $subDirectory) + { + $paths[] = $subDirectory; + } + } + $files = glob($path . "*.php", 0); + if (is_array($files)) + { + foreach ($files as $file) + { + $class = substr(basename($file), 0, -4); + self::$classIndex[$class] = $file; + } + } + } + krsort(self::$classIndex); + // Only use autoloading if spl_autoload_register() is available and no __autoload() is defined (because + // __autoload() breaks if spl_autoload_register() is used. + if (function_exists("spl_autoload_register") && !is_callable("__autoload")) + { + spl_autoload_register(array(__CLASS__, "autoload")); + } + // Otherwise include all class files + else + { + foreach (self::$classIndex as $class => $file) + { + if (!class_exists($class)) + { + require_once($file); + } + } + } + } + /** + * Minifies CSS source. + * + * @param string $source CSS source + * @param array $filters Filter configuration [optional] + * @param array $plugins Plugin configuration [optional] + * @return string Minified CSS + */ + public static function minify($source, array $filters = null, array $plugins = null) + { + self::$errors = array(); + $minifier = new CssMinifier($source, $filters, $plugins); + return $minifier->getMinified(); + } + /** + * Parse the CSS source. + * + * @param string $source CSS source + * @param array $plugins Plugin configuration [optional] + * @return array Array of aCssToken + */ + public static function parse($source, array $plugins = null) + { + self::$errors = array(); + $parser = new CssParser($source, $plugins); + return $parser->getTokens(); + } + /** + * -- + * + * @param boolean $to + * @return boolean + */ + public static function setVerbose($to) + { + self::$isVerbose = (boolean) $to; + return self::$isVerbose; + } + /** + * -- + * + * @param CssError $error + * @return void + */ + public static function triggerError(CssError $error) + { + self::$errors[] = $error; + if (self::$isVerbose) + { + trigger_error((string) $error, E_USER_WARNING); + } + } + } +// Initialises CssMin +CssMin::initialise(); + +/** + * This {@link aCssMinifierFilter minifier filter} import external css files defined with the @import at-rule into the + * current stylesheet. + * + * @package CssMin/Minifier/Filters + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssImportImportsMinifierFilter extends aCssMinifierFilter + { + /** + * Array with already imported external stylesheets. + * + * @var array + */ + private $imported = array(); + /** + * Implements {@link aCssMinifierFilter::filter()}. + * + * @param array $tokens Array of objects of type aCssToken + * @return integer Count of added, changed or removed tokens; a return value large than 0 will rebuild the array + */ + public function apply(array &$tokens) + { + if (!isset($this->configuration["BasePath"]) || !is_dir($this->configuration["BasePath"])) + { + CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Base path " . ($this->configuration["BasePath"] ? $this->configuration["BasePath"] : "null"). " is not a directory")); + return 0; + } + for ($i = 0, $l = count($tokens); $i < $l; $i++) + { + if (get_class($tokens[$i]) === "CssAtImportToken") + { + $import = $this->configuration["BasePath"] . "/" . $tokens[$i]->Import; + // Import file was not found/is not a file + if (!is_file($import)) + { + CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Import file " . $import. " was not found.", (string) $tokens[$i])); + } + // Import file already imported; remove this @import at-rule to prevent recursions + elseif (in_array($import, $this->imported)) + { + CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Import file " . $import. " was already imported.", (string) $tokens[$i])); + $tokens[$i] = null; + } + else + { + $this->imported[] = $import; + $parser = new CssParser(file_get_contents($import)); + $import = $parser->getTokens(); + // The @import at-rule has media types defined requiring special handling + if (count($tokens[$i]->MediaTypes) > 0 && !(count($tokens[$i]->MediaTypes) == 1 && $tokens[$i]->MediaTypes[0] == "all")) + { + $blocks = array(); + /* + * Filter or set media types of @import at-rule or remove the @import at-rule if no media type is matching the parent @import at-rule + */ + for($ii = 0, $ll = count($import); $ii < $ll; $ii++) + { + if (get_class($import[$ii]) === "CssAtImportToken") + { + // @import at-rule defines no media type or only the "all" media type; set the media types to the one defined in the parent @import at-rule + if (count($import[$ii]->MediaTypes) == 0 || (count($import[$ii]->MediaTypes) == 1 && $import[$ii]->MediaTypes[0] == "all")) + { + $import[$ii]->MediaTypes = $tokens[$i]->MediaTypes; + } + // @import at-rule defineds one or more media types; filter out media types not matching with the parent @import at-rule + elseif (count($import[$ii]->MediaTypes > 0)) + { + foreach ($import[$ii]->MediaTypes as $index => $mediaType) + { + if (!in_array($mediaType, $tokens[$i]->MediaTypes)) + { + unset($import[$ii]->MediaTypes[$index]); + } + } + $import[$ii]->MediaTypes = array_values($import[$ii]->MediaTypes); + // If there are no media types left in the @import at-rule remove the @import at-rule + if (count($import[$ii]->MediaTypes) == 0) + { + $import[$ii] = null; + } + } + } + } + /* + * Remove media types of @media at-rule block not defined in the @import at-rule + */ + for($ii = 0, $ll = count($import); $ii < $ll; $ii++) + { + if (get_class($import[$ii]) === "CssAtMediaStartToken") + { + foreach ($import[$ii]->MediaTypes as $index => $mediaType) + { + if (!in_array($mediaType, $tokens[$i]->MediaTypes)) + { + unset($import[$ii]->MediaTypes[$index]); + } + $import[$ii]->MediaTypes = array_values($import[$ii]->MediaTypes); + } + } + } + /* + * If no media types left of the @media at-rule block remove the complete block + */ + for($ii = 0, $ll = count($import); $ii < $ll; $ii++) + { + if (get_class($import[$ii]) === "CssAtMediaStartToken") + { + if (count($import[$ii]->MediaTypes) === 0) + { + for ($iii = $ii; $iii < $ll; $iii++) + { + if (get_class($import[$iii]) === "CssAtMediaEndToken") + { + break; + } + } + if (get_class($import[$iii]) === "CssAtMediaEndToken") + { + array_splice($import, $ii, $iii - $ii + 1, array()); + $ll = count($import); + } + } + } + } + /* + * If the media types of the @media at-rule equals the media types defined in the @import + * at-rule remove the CssAtMediaStartToken and CssAtMediaEndToken token + */ + for($ii = 0, $ll = count($import); $ii < $ll; $ii++) + { + if (get_class($import[$ii]) === "CssAtMediaStartToken" && count(array_diff($tokens[$i]->MediaTypes, $import[$ii]->MediaTypes)) === 0) + { + for ($iii = $ii; $iii < $ll; $iii++) + { + if (get_class($import[$iii]) == "CssAtMediaEndToken") + { + break; + } + } + if (get_class($import[$iii]) == "CssAtMediaEndToken") + { + unset($import[$ii]); + unset($import[$iii]); + $import = array_values($import); + $ll = count($import); + } + } + } + /** + * Extract CssAtImportToken and CssAtCharsetToken tokens + */ + for($ii = 0, $ll = count($import); $ii < $ll; $ii++) + { + $class = get_class($import[$ii]); + if ($class === "CssAtImportToken" || $class === "CssAtCharsetToken") + { + $blocks = array_merge($blocks, array_splice($import, $ii, 1, array())); + $ll = count($import); + } + } + /* + * Extract the @font-face, @media and @page at-rule block + */ + for($ii = 0, $ll = count($import); $ii < $ll; $ii++) + { + $class = get_class($import[$ii]); + if ($class === "CssAtFontFaceStartToken" || $class === "CssAtMediaStartToken" || $class === "CssAtPageStartToken" || $class === "CssAtVariablesStartToken") + { + for ($iii = $ii; $iii < $ll; $iii++) + { + $class = get_class($import[$iii]); + if ($class === "CssAtFontFaceEndToken" || $class === "CssAtMediaEndToken" || $class === "CssAtPageEndToken" || $class === "CssAtVariablesEndToken") + { + break; + } + } + $class = get_class($import[$iii]); + if (isset($import[$iii]) && ($class === "CssAtFontFaceEndToken" || $class === "CssAtMediaEndToken" || $class === "CssAtPageEndToken" || $class === "CssAtVariablesEndToken")) + { + $blocks = array_merge($blocks, array_splice($import, $ii, $iii - $ii + 1, array())); + $ll = count($import); + } + } + } + // Create the import array with extracted tokens and the rulesets wrapped into a @media at-rule block + $import = array_merge($blocks, array(new CssAtMediaStartToken($tokens[$i]->MediaTypes)), $import, array(new CssAtMediaEndToken())); + } + // Insert the imported tokens + array_splice($tokens, $i, 1, $import); + // Modify parameters of the for-loop + $i--; + $l = count($tokens); + } + } + } + } + } + +/** + * {@link aCssParserPlugin Parser plugin} for preserve parsing expression() declaration values. + * + * This plugin return no {@link aCssToken CssToken} but ensures that expression() declaration values will get parsed + * properly. + * + * @package CssMin/Parser/Plugins + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssExpressionParserPlugin extends aCssParserPlugin + { + /** + * Count of left braces. + * + * @var integer + */ + private $leftBraces = 0; + /** + * Count of right braces. + * + * @var integer + */ + private $rightBraces = 0; + /** + * Implements {@link aCssParserPlugin::getTriggerChars()}. + * + * @return array + */ + public function getTriggerChars() + { + return array("(", ")", ";", "}"); + } + /** + * Implements {@link aCssParserPlugin::getTriggerStates()}. + * + * @return array + */ + public function getTriggerStates() + { + return false; + } + /** + * Implements {@link aCssParserPlugin::parse()}. + * + * @param integer $index Current index + * @param string $char Current char + * @param string $previousChar Previous char + * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing + */ + public function parse($index, $char, $previousChar, $state) + { + // Start of expression + if ($char === "(" && strtolower(substr($this->parser->getSource(), $index - 10, 11)) === "expression(" && $state !== "T_EXPRESSION") + { + $this->parser->pushState("T_EXPRESSION"); + $this->leftBraces++; + } + // Count left braces + elseif ($char === "(" && $state === "T_EXPRESSION") + { + $this->leftBraces++; + } + // Count right braces + elseif ($char === ")" && $state === "T_EXPRESSION") + { + $this->rightBraces++; + } + // Possible end of expression; if left and right braces are equal the expressen ends + elseif (($char === ";" || $char === "}") && $state === "T_EXPRESSION" && $this->leftBraces === $this->rightBraces) + { + $this->leftBraces = $this->rightBraces = 0; + $this->parser->popState(); + return $index - 1; + } + else + { + return false; + } + return true; + } + } + +/** + * CSS Error. + * + * @package CssMin + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssError + { + /** + * File. + * + * @var string + */ + public $File = ""; + /** + * Line. + * + * @var integer + */ + public $Line = 0; + /** + * Error message. + * + * @var string + */ + public $Message = ""; + /** + * Source. + * + * @var string + */ + public $Source = ""; + /** + * Constructor triggering the error. + * + * @param string $message Error message + * @param string $source Corresponding line [optional] + * @return void + */ + public function __construct($file, $line, $message, $source = "") + { + $this->File = $file; + $this->Line = $line; + $this->Message = $message; + $this->Source = $source; + } + /** + * Returns the error as formatted string. + * + * @return string + */ + public function __toString() + { + return $this->Message . ($this->Source ? ":
" . $this->Source . "": "") . "
in file " . $this->File . " at line " . $this->Line; + } + } + +/** + * This {@link aCssMinifierPlugin} will convert a color value in rgb notation to hexadecimal notation. + * + * Example: + * + * color: rgb(200,60%,5); + * + * + * Will get converted to: + * + * color:#c89905; + * + * + * @package CssMin/Minifier/Plugins + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssConvertRgbColorsMinifierPlugin extends aCssMinifierPlugin + { + /** + * Regular expression matching the value. + * + * @var string + */ + private $reMatch = "/rgb\s*\(\s*([0-9%]+)\s*,\s*([0-9%]+)\s*,\s*([0-9%]+)\s*\)/iS"; + /** + * Implements {@link aCssMinifierPlugin::minify()}. + * + * @param aCssToken $token Token to process + * @return boolean Return TRUE to break the processing of this token; FALSE to continue + */ + public function apply(aCssToken &$token) + { + if (stripos($token->Value, "rgb") !== false && preg_match($this->reMatch, $token->Value, $m)) + { + for ($i = 1, $l = count($m); $i < $l; $i++) + { + if (strpos("%", $m[$i]) !== false) + { + $m[$i] = substr($m[$i], 0, -1); + $m[$i] = (int) (256 * ($m[$i] / 100)); + } + $m[$i] = str_pad(dechex($m[$i]), 2, "0", STR_PAD_LEFT); + } + $token->Value = str_replace($m[0], "#" . $m[1] . $m[2] . $m[3], $token->Value); + } + return false; + } + /** + * Implements {@link aMinifierPlugin::getTriggerTokens()} + * + * @return array + */ + public function getTriggerTokens() + { + return array + ( + "CssAtFontFaceDeclarationToken", + "CssAtPageDeclarationToken", + "CssRulesetDeclarationToken" + ); + } + } + +/** + * This {@link aCssMinifierPlugin} will convert named color values to hexadecimal notation. + * + * Example: + * + * color: black; + * border: 1px solid indigo; + * + * + * Will get converted to: + * + * color:#000; + * border:1px solid #4b0082; + * + * + * @package CssMin/Minifier/Plugins + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssConvertNamedColorsMinifierPlugin extends aCssMinifierPlugin + { + + /** + * Regular expression matching the value. + * + * @var string + */ + private $reMatch = null; + /** + * Regular expression replacing the value. + * + * @var string + */ + private $reReplace = "\"\${1}\" . \$this->transformation[strtolower(\"\${2}\")] . \"\${3}\""; + /** + * Transformation table used by the {@link CssConvertNamedColorsMinifierPlugin::$reReplace replace regular expression}. + * + * @var array + */ + private $transformation = array + ( + "aliceblue" => "#f0f8ff", + "antiquewhite" => "#faebd7", + "aqua" => "#0ff", + "aquamarine" => "#7fffd4", + "azure" => "#f0ffff", + "beige" => "#f5f5dc", + "black" => "#000", + "blue" => "#00f", + "blueviolet" => "#8a2be2", + "brown" => "#a52a2a", + "burlywood" => "#deb887", + "cadetblue" => "#5f9ea0", + "chartreuse" => "#7fff00", + "chocolate" => "#d2691e", + "coral" => "#ff7f50", + "cornflowerblue" => "#6495ed", + "cornsilk" => "#fff8dc", + "crimson" => "#dc143c", + "darkblue" => "#00008b", + "darkcyan" => "#008b8b", + "darkgoldenrod" => "#b8860b", + "darkgray" => "#a9a9a9", + "darkgreen" => "#006400", + "darkkhaki" => "#bdb76b", + "darkmagenta" => "#8b008b", + "darkolivegreen" => "#556b2f", + "darkorange" => "#ff8c00", + "darkorchid" => "#9932cc", + "darkred" => "#8b0000", + "darksalmon" => "#e9967a", + "darkseagreen" => "#8fbc8f", + "darkslateblue" => "#483d8b", + "darkslategray" => "#2f4f4f", + "darkturquoise" => "#00ced1", + "darkviolet" => "#9400d3", + "deeppink" => "#ff1493", + "deepskyblue" => "#00bfff", + "dimgray" => "#696969", + "dodgerblue" => "#1e90ff", + "firebrick" => "#b22222", + "floralwhite" => "#fffaf0", + "forestgreen" => "#228b22", + "fuchsia" => "#f0f", + "gainsboro" => "#dcdcdc", + "ghostwhite" => "#f8f8ff", + "gold" => "#ffd700", + "goldenrod" => "#daa520", + "gray" => "#808080", + "green" => "#008000", + "greenyellow" => "#adff2f", + "honeydew" => "#f0fff0", + "hotpink" => "#ff69b4", + "indianred" => "#cd5c5c", + "indigo" => "#4b0082", + "ivory" => "#fffff0", + "khaki" => "#f0e68c", + "lavender" => "#e6e6fa", + "lavenderblush" => "#fff0f5", + "lawngreen" => "#7cfc00", + "lemonchiffon" => "#fffacd", + "lightblue" => "#add8e6", + "lightcoral" => "#f08080", + "lightcyan" => "#e0ffff", + "lightgoldenrodyellow" => "#fafad2", + "lightgreen" => "#90ee90", + "lightgrey" => "#d3d3d3", + "lightpink" => "#ffb6c1", + "lightsalmon" => "#ffa07a", + "lightseagreen" => "#20b2aa", + "lightskyblue" => "#87cefa", + "lightslategray" => "#789", + "lightsteelblue" => "#b0c4de", + "lightyellow" => "#ffffe0", + "lime" => "#0f0", + "limegreen" => "#32cd32", + "linen" => "#faf0e6", + "maroon" => "#800000", + "mediumaquamarine" => "#66cdaa", + "mediumblue" => "#0000cd", + "mediumorchid" => "#ba55d3", + "mediumpurple" => "#9370db", + "mediumseagreen" => "#3cb371", + "mediumslateblue" => "#7b68ee", + "mediumspringgreen" => "#00fa9a", + "mediumturquoise" => "#48d1cc", + "mediumvioletred" => "#c71585", + "midnightblue" => "#191970", + "mintcream" => "#f5fffa", + "mistyrose" => "#ffe4e1", + "moccasin" => "#ffe4b5", + "navajowhite" => "#ffdead", + "navy" => "#000080", + "oldlace" => "#fdf5e6", + "olive" => "#808000", + "olivedrab" => "#6b8e23", + "orange" => "#ffa500", + "orangered" => "#ff4500", + "orchid" => "#da70d6", + "palegoldenrod" => "#eee8aa", + "palegreen" => "#98fb98", + "paleturquoise" => "#afeeee", + "palevioletred" => "#db7093", + "papayawhip" => "#ffefd5", + "peachpuff" => "#ffdab9", + "peru" => "#cd853f", + "pink" => "#ffc0cb", + "plum" => "#dda0dd", + "powderblue" => "#b0e0e6", + "purple" => "#800080", + "red" => "#f00", + "rosybrown" => "#bc8f8f", + "royalblue" => "#4169e1", + "saddlebrown" => "#8b4513", + "salmon" => "#fa8072", + "sandybrown" => "#f4a460", + "seagreen" => "#2e8b57", + "seashell" => "#fff5ee", + "sienna" => "#a0522d", + "silver" => "#c0c0c0", + "skyblue" => "#87ceeb", + "slateblue" => "#6a5acd", + "slategray" => "#708090", + "snow" => "#fffafa", + "springgreen" => "#00ff7f", + "steelblue" => "#4682b4", + "tan" => "#d2b48c", + "teal" => "#008080", + "thistle" => "#d8bfd8", + "tomato" => "#ff6347", + "turquoise" => "#40e0d0", + "violet" => "#ee82ee", + "wheat" => "#f5deb3", + "white" => "#fff", + "whitesmoke" => "#f5f5f5", + "yellow" => "#ff0", + "yellowgreen" => "#9acd32" + ); + /** + * Overwrites {@link aCssMinifierPlugin::__construct()}. + * + * The constructor will create the {@link CssConvertNamedColorsMinifierPlugin::$reReplace replace regular expression} + * based on the {@link CssConvertNamedColorsMinifierPlugin::$transformation transformation table}. + * + * @param CssMinifier $minifier The CssMinifier object of this plugin. + * @param array $configuration Plugin configuration [optional] + * @return void + */ + public function __construct(CssMinifier $minifier, array $configuration = array()) + { + $this->reMatch = "/(^|\s)+(" . implode("|", array_keys($this->transformation)) . ")(\s|$)+/eiS"; + parent::__construct($minifier, $configuration); + } + /** + * Implements {@link aCssMinifierPlugin::minify()}. + * + * @param aCssToken $token Token to process + * @return boolean Return TRUE to break the processing of this token; FALSE to continue + */ + public function apply(aCssToken &$token) + { + $lcValue = strtolower($token->Value); + // Declaration value equals a value in the transformation table => simple replace + if (isset($this->transformation[$lcValue])) + { + $token->Value = $this->transformation[$lcValue]; + } + // Declaration value contains a value in the transformation table => regular expression replace + elseif (preg_match($this->reMatch, $token->Value)) + { + $token->Value = preg_replace($this->reMatch, $this->reReplace, $token->Value); + } + return false; + } + /** + * Implements {@link aMinifierPlugin::getTriggerTokens()} + * + * @return array + */ + public function getTriggerTokens() + { + return array + ( + "CssAtFontFaceDeclarationToken", + "CssAtPageDeclarationToken", + "CssRulesetDeclarationToken" + ); + } + } + +/** + * This {@link aCssMinifierFilter minifier filter} triggers on CSS Level 3 properties and will add declaration tokens + * with browser-specific properties. + * + * @package CssMin/Minifier/Filters + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssConvertLevel3PropertiesMinifierFilter extends aCssMinifierFilter + { + /** + * Css property transformations table. Used to convert CSS3 and proprietary properties to the browser-specific + * counterparts. + * + * @var array + */ + private $transformations = array + ( + // Property Array(Mozilla, Webkit, Opera, Internet Explorer); NULL values are placeholders and will get ignored + "animation" => array(null, "-webkit-animation", null, null), + "animation-delay" => array(null, "-webkit-animation-delay", null, null), + "animation-direction" => array(null, "-webkit-animation-direction", null, null), + "animation-duration" => array(null, "-webkit-animation-duration", null, null), + "animation-fill-mode" => array(null, "-webkit-animation-fill-mode", null, null), + "animation-iteration-count" => array(null, "-webkit-animation-iteration-count", null, null), + "animation-name" => array(null, "-webkit-animation-name", null, null), + "animation-play-state" => array(null, "-webkit-animation-play-state", null, null), + "animation-timing-function" => array(null, "-webkit-animation-timing-function", null, null), + "appearance" => array("-moz-appearance", "-webkit-appearance", null, null), + "backface-visibility" => array(null, "-webkit-backface-visibility", null, null), + "background-clip" => array(null, "-webkit-background-clip", null, null), + "background-composite" => array(null, "-webkit-background-composite", null, null), + "background-inline-policy" => array("-moz-background-inline-policy", null, null, null), + "background-origin" => array(null, "-webkit-background-origin", null, null), + "background-position-x" => array(null, null, null, "-ms-background-position-x"), + "background-position-y" => array(null, null, null, "-ms-background-position-y"), + "background-size" => array(null, "-webkit-background-size", null, null), + "behavior" => array(null, null, null, "-ms-behavior"), + "binding" => array("-moz-binding", null, null, null), + "border-after" => array(null, "-webkit-border-after", null, null), + "border-after-color" => array(null, "-webkit-border-after-color", null, null), + "border-after-style" => array(null, "-webkit-border-after-style", null, null), + "border-after-width" => array(null, "-webkit-border-after-width", null, null), + "border-before" => array(null, "-webkit-border-before", null, null), + "border-before-color" => array(null, "-webkit-border-before-color", null, null), + "border-before-style" => array(null, "-webkit-border-before-style", null, null), + "border-before-width" => array(null, "-webkit-border-before-width", null, null), + "border-border-bottom-colors" => array("-moz-border-bottom-colors", null, null, null), + "border-bottom-left-radius" => array("-moz-border-radius-bottomleft", "-webkit-border-bottom-left-radius", null, null), + "border-bottom-right-radius" => array("-moz-border-radius-bottomright", "-webkit-border-bottom-right-radius", null, null), + "border-end" => array("-moz-border-end", "-webkit-border-end", null, null), + "border-end-color" => array("-moz-border-end-color", "-webkit-border-end-color", null, null), + "border-end-style" => array("-moz-border-end-style", "-webkit-border-end-style", null, null), + "border-end-width" => array("-moz-border-end-width", "-webkit-border-end-width", null, null), + "border-fit" => array(null, "-webkit-border-fit", null, null), + "border-horizontal-spacing" => array(null, "-webkit-border-horizontal-spacing", null, null), + "border-image" => array("-moz-border-image", "-webkit-border-image", null, null), + "border-left-colors" => array("-moz-border-left-colors", null, null, null), + "border-radius" => array("-moz-border-radius", "-webkit-border-radius", null, null), + "border-border-right-colors" => array("-moz-border-right-colors", null, null, null), + "border-start" => array("-moz-border-start", "-webkit-border-start", null, null), + "border-start-color" => array("-moz-border-start-color", "-webkit-border-start-color", null, null), + "border-start-style" => array("-moz-border-start-style", "-webkit-border-start-style", null, null), + "border-start-width" => array("-moz-border-start-width", "-webkit-border-start-width", null, null), + "border-top-colors" => array("-moz-border-top-colors", null, null, null), + "border-top-left-radius" => array("-moz-border-radius-topleft", "-webkit-border-top-left-radius", null, null), + "border-top-right-radius" => array("-moz-border-radius-topright", "-webkit-border-top-right-radius", null, null), + "border-vertical-spacing" => array(null, "-webkit-border-vertical-spacing", null, null), + "box-align" => array("-moz-box-align", "-webkit-box-align", null, null), + "box-direction" => array("-moz-box-direction", "-webkit-box-direction", null, null), + "box-flex" => array("-moz-box-flex", "-webkit-box-flex", null, null), + "box-flex-group" => array(null, "-webkit-box-flex-group", null, null), + "box-flex-lines" => array(null, "-webkit-box-flex-lines", null, null), + "box-ordinal-group" => array("-moz-box-ordinal-group", "-webkit-box-ordinal-group", null, null), + "box-orient" => array("-moz-box-orient", "-webkit-box-orient", null, null), + "box-pack" => array("-moz-box-pack", "-webkit-box-pack", null, null), + "box-reflect" => array(null, "-webkit-box-reflect", null, null), + "box-shadow" => array("-moz-box-shadow", "-webkit-box-shadow", null, null), + "box-sizing" => array("-moz-box-sizing", null, null, null), + "color-correction" => array(null, "-webkit-color-correction", null, null), + "column-break-after" => array(null, "-webkit-column-break-after", null, null), + "column-break-before" => array(null, "-webkit-column-break-before", null, null), + "column-break-inside" => array(null, "-webkit-column-break-inside", null, null), + "column-count" => array("-moz-column-count", "-webkit-column-count", null, null), + "column-gap" => array("-moz-column-gap", "-webkit-column-gap", null, null), + "column-rule" => array("-moz-column-rule", "-webkit-column-rule", null, null), + "column-rule-color" => array("-moz-column-rule-color", "-webkit-column-rule-color", null, null), + "column-rule-style" => array("-moz-column-rule-style", "-webkit-column-rule-style", null, null), + "column-rule-width" => array("-moz-column-rule-width", "-webkit-column-rule-width", null, null), + "column-span" => array(null, "-webkit-column-span", null, null), + "column-width" => array("-moz-column-width", "-webkit-column-width", null, null), + "columns" => array(null, "-webkit-columns", null, null), + "filter" => array(__CLASS__, "filter"), + "float-edge" => array("-moz-float-edge", null, null, null), + "font-feature-settings" => array("-moz-font-feature-settings", null, null, null), + "font-language-override" => array("-moz-font-language-override", null, null, null), + "font-size-delta" => array(null, "-webkit-font-size-delta", null, null), + "font-smoothing" => array(null, "-webkit-font-smoothing", null, null), + "force-broken-image-icon" => array("-moz-force-broken-image-icon", null, null, null), + "highlight" => array(null, "-webkit-highlight", null, null), + "hyphenate-character" => array(null, "-webkit-hyphenate-character", null, null), + "hyphenate-locale" => array(null, "-webkit-hyphenate-locale", null, null), + "hyphens" => array(null, "-webkit-hyphens", null, null), + "force-broken-image-icon" => array("-moz-image-region", null, null, null), + "ime-mode" => array(null, null, null, "-ms-ime-mode"), + "interpolation-mode" => array(null, null, null, "-ms-interpolation-mode"), + "layout-flow" => array(null, null, null, "-ms-layout-flow"), + "layout-grid" => array(null, null, null, "-ms-layout-grid"), + "layout-grid-char" => array(null, null, null, "-ms-layout-grid-char"), + "layout-grid-line" => array(null, null, null, "-ms-layout-grid-line"), + "layout-grid-mode" => array(null, null, null, "-ms-layout-grid-mode"), + "layout-grid-type" => array(null, null, null, "-ms-layout-grid-type"), + "line-break" => array(null, "-webkit-line-break", null, "-ms-line-break"), + "line-clamp" => array(null, "-webkit-line-clamp", null, null), + "line-grid-mode" => array(null, null, null, "-ms-line-grid-mode"), + "logical-height" => array(null, "-webkit-logical-height", null, null), + "logical-width" => array(null, "-webkit-logical-width", null, null), + "margin-after" => array(null, "-webkit-margin-after", null, null), + "margin-after-collapse" => array(null, "-webkit-margin-after-collapse", null, null), + "margin-before" => array(null, "-webkit-margin-before", null, null), + "margin-before-collapse" => array(null, "-webkit-margin-before-collapse", null, null), + "margin-bottom-collapse" => array(null, "-webkit-margin-bottom-collapse", null, null), + "margin-collapse" => array(null, "-webkit-margin-collapse", null, null), + "margin-end" => array("-moz-margin-end", "-webkit-margin-end", null, null), + "margin-start" => array("-moz-margin-start", "-webkit-margin-start", null, null), + "margin-top-collapse" => array(null, "-webkit-margin-top-collapse", null, null), + "marquee " => array(null, "-webkit-marquee", null, null), + "marquee-direction" => array(null, "-webkit-marquee-direction", null, null), + "marquee-increment" => array(null, "-webkit-marquee-increment", null, null), + "marquee-repetition" => array(null, "-webkit-marquee-repetition", null, null), + "marquee-speed" => array(null, "-webkit-marquee-speed", null, null), + "marquee-style" => array(null, "-webkit-marquee-style", null, null), + "mask" => array(null, "-webkit-mask", null, null), + "mask-attachment" => array(null, "-webkit-mask-attachment", null, null), + "mask-box-image" => array(null, "-webkit-mask-box-image", null, null), + "mask-clip" => array(null, "-webkit-mask-clip", null, null), + "mask-composite" => array(null, "-webkit-mask-composite", null, null), + "mask-image" => array(null, "-webkit-mask-image", null, null), + "mask-origin" => array(null, "-webkit-mask-origin", null, null), + "mask-position" => array(null, "-webkit-mask-position", null, null), + "mask-position-x" => array(null, "-webkit-mask-position-x", null, null), + "mask-position-y" => array(null, "-webkit-mask-position-y", null, null), + "mask-repeat" => array(null, "-webkit-mask-repeat", null, null), + "mask-repeat-x" => array(null, "-webkit-mask-repeat-x", null, null), + "mask-repeat-y" => array(null, "-webkit-mask-repeat-y", null, null), + "mask-size" => array(null, "-webkit-mask-size", null, null), + "match-nearest-mail-blockquote-color" => array(null, "-webkit-match-nearest-mail-blockquote-color", null, null), + "max-logical-height" => array(null, "-webkit-max-logical-height", null, null), + "max-logical-width" => array(null, "-webkit-max-logical-width", null, null), + "min-logical-height" => array(null, "-webkit-min-logical-height", null, null), + "min-logical-width" => array(null, "-webkit-min-logical-width", null, null), + "object-fit" => array(null, null, "-o-object-fit", null), + "object-position" => array(null, null, "-o-object-position", null), + "opacity" => array(__CLASS__, "opacity"), + "outline-radius" => array("-moz-outline-radius", null, null, null), + "outline-bottom-left-radius" => array("-moz-outline-radius-bottomleft", null, null, null), + "outline-bottom-right-radius" => array("-moz-outline-radius-bottomright", null, null, null), + "outline-top-left-radius" => array("-moz-outline-radius-topleft", null, null, null), + "outline-top-right-radius" => array("-moz-outline-radius-topright", null, null, null), + "padding-after" => array(null, "-webkit-padding-after", null, null), + "padding-before" => array(null, "-webkit-padding-before", null, null), + "padding-end" => array("-moz-padding-end", "-webkit-padding-end", null, null), + "padding-start" => array("-moz-padding-start", "-webkit-padding-start", null, null), + "perspective" => array(null, "-webkit-perspective", null, null), + "perspective-origin" => array(null, "-webkit-perspective-origin", null, null), + "perspective-origin-x" => array(null, "-webkit-perspective-origin-x", null, null), + "perspective-origin-y" => array(null, "-webkit-perspective-origin-y", null, null), + "rtl-ordering" => array(null, "-webkit-rtl-ordering", null, null), + "scrollbar-3dlight-color" => array(null, null, null, "-ms-scrollbar-3dlight-color"), + "scrollbar-arrow-color" => array(null, null, null, "-ms-scrollbar-arrow-color"), + "scrollbar-base-color" => array(null, null, null, "-ms-scrollbar-base-color"), + "scrollbar-darkshadow-color" => array(null, null, null, "-ms-scrollbar-darkshadow-color"), + "scrollbar-face-color" => array(null, null, null, "-ms-scrollbar-face-color"), + "scrollbar-highlight-color" => array(null, null, null, "-ms-scrollbar-highlight-color"), + "scrollbar-shadow-color" => array(null, null, null, "-ms-scrollbar-shadow-color"), + "scrollbar-track-color" => array(null, null, null, "-ms-scrollbar-track-color"), + "stack-sizing" => array("-moz-stack-sizing", null, null, null), + "svg-shadow" => array(null, "-webkit-svg-shadow", null, null), + "tab-size" => array("-moz-tab-size", null, "-o-tab-size", null), + "table-baseline" => array(null, null, "-o-table-baseline", null), + "text-align-last" => array(null, null, null, "-ms-text-align-last"), + "text-autospace" => array(null, null, null, "-ms-text-autospace"), + "text-combine" => array(null, "-webkit-text-combine", null, null), + "text-decorations-in-effect" => array(null, "-webkit-text-decorations-in-effect", null, null), + "text-emphasis" => array(null, "-webkit-text-emphasis", null, null), + "text-emphasis-color" => array(null, "-webkit-text-emphasis-color", null, null), + "text-emphasis-position" => array(null, "-webkit-text-emphasis-position", null, null), + "text-emphasis-style" => array(null, "-webkit-text-emphasis-style", null, null), + "text-fill-color" => array(null, "-webkit-text-fill-color", null, null), + "text-justify" => array(null, null, null, "-ms-text-justify"), + "text-kashida-space" => array(null, null, null, "-ms-text-kashida-space"), + "text-overflow" => array(null, null, "-o-text-overflow", "-ms-text-overflow"), + "text-security" => array(null, "-webkit-text-security", null, null), + "text-size-adjust" => array(null, "-webkit-text-size-adjust", null, "-ms-text-size-adjust"), + "text-stroke" => array(null, "-webkit-text-stroke", null, null), + "text-stroke-color" => array(null, "-webkit-text-stroke-color", null, null), + "text-stroke-width" => array(null, "-webkit-text-stroke-width", null, null), + "text-underline-position" => array(null, null, null, "-ms-text-underline-position"), + "transform" => array("-moz-transform", "-webkit-transform", "-o-transform", null), + "transform-origin" => array("-moz-transform-origin", "-webkit-transform-origin", "-o-transform-origin", null), + "transform-origin-x" => array(null, "-webkit-transform-origin-x", null, null), + "transform-origin-y" => array(null, "-webkit-transform-origin-y", null, null), + "transform-origin-z" => array(null, "-webkit-transform-origin-z", null, null), + "transform-style" => array(null, "-webkit-transform-style", null, null), + "transition" => array("-moz-transition", "-webkit-transition", "-o-transition", null), + "transition-delay" => array("-moz-transition-delay", "-webkit-transition-delay", "-o-transition-delay", null), + "transition-duration" => array("-moz-transition-duration", "-webkit-transition-duration", "-o-transition-duration", null), + "transition-property" => array("-moz-transition-property", "-webkit-transition-property", "-o-transition-property", null), + "transition-timing-function" => array("-moz-transition-timing-function", "-webkit-transition-timing-function", "-o-transition-timing-function", null), + "user-drag" => array(null, "-webkit-user-drag", null, null), + "user-focus" => array("-moz-user-focus", null, null, null), + "user-input" => array("-moz-user-input", null, null, null), + "user-modify" => array("-moz-user-modify", "-webkit-user-modify", null, null), + "user-select" => array("-moz-user-select", "-webkit-user-select", null, null), + "white-space" => array(__CLASS__, "whiteSpace"), + "window-shadow" => array("-moz-window-shadow", null, null, null), + "word-break" => array(null, null, null, "-ms-word-break"), + "word-wrap" => array(null, null, null, "-ms-word-wrap"), + "writing-mode" => array(null, "-webkit-writing-mode", null, "-ms-writing-mode"), + "zoom" => array(null, null, null, "-ms-zoom") + ); + /** + * Implements {@link aCssMinifierFilter::filter()}. + * + * @param array $tokens Array of objects of type aCssToken + * @return integer Count of added, changed or removed tokens; a return value large than 0 will rebuild the array + */ + public function apply(array &$tokens) + { + $r = 0; + $transformations = &$this->transformations; + for ($i = 0, $l = count($tokens); $i < $l; $i++) + { + if (get_class($tokens[$i]) === "CssRulesetDeclarationToken") + { + $tProperty = $tokens[$i]->Property; + if (isset($transformations[$tProperty])) + { + $result = array(); + if (is_callable($transformations[$tProperty])) + { + $result = call_user_func_array($transformations[$tProperty], array($tokens[$i])); + if (!is_array($result) && is_object($result)) + { + $result = array($result); + } + } + else + { + $tValue = $tokens[$i]->Value; + $tMediaTypes = $tokens[$i]->MediaTypes; + foreach ($transformations[$tProperty] as $property) + { + if ($property !== null) + { + $result[] = new CssRulesetDeclarationToken($property, $tValue, $tMediaTypes); + } + } + } + if (count($result) > 0) + { + array_splice($tokens, $i + 1, 0, $result); + $i += count($result); + $l += count($result); + } + } + } + } + return $r; + } + /** + * Transforms the Internet Explorer specific declaration property "filter" to Internet Explorer 8+ compatible + * declaratiopn property "-ms-filter". + * + * @param aCssToken $token + * @return array + */ + private static function filter($token) + { + $r = array + ( + new CssRulesetDeclarationToken("-ms-filter", "\"" . $token->Value . "\"", $token->MediaTypes), + ); + return $r; + } + /** + * Transforms "opacity: {value}" into browser specific counterparts. + * + * @param aCssToken $token + * @return array + */ + private static function opacity($token) + { + // Calculate the value for Internet Explorer filter statement + $ieValue = (int) ((float) $token->Value * 100); + $r = array + ( + // Internet Explorer >= 8 + new CssRulesetDeclarationToken("-ms-filter", "\"alpha(opacity=" . $ieValue . ")\"", $token->MediaTypes), + // Internet Explorer >= 4 <= 7 + new CssRulesetDeclarationToken("filter", "alpha(opacity=" . $ieValue . ")", $token->MediaTypes), + new CssRulesetDeclarationToken("zoom", "1", $token->MediaTypes) + ); + return $r; + } + /** + * Transforms "white-space: pre-wrap" into browser specific counterparts. + * + * @param aCssToken $token + * @return array + */ + private static function whiteSpace($token) + { + if (strtolower($token->Value) === "pre-wrap") + { + $r = array + ( + // Firefox < 3 + new CssRulesetDeclarationToken("white-space", "-moz-pre-wrap", $token->MediaTypes), + // Webkit + new CssRulesetDeclarationToken("white-space", "-webkit-pre-wrap", $token->MediaTypes), + // Opera >= 4 <= 6 + new CssRulesetDeclarationToken("white-space", "-pre-wrap", $token->MediaTypes), + // Opera >= 7 + new CssRulesetDeclarationToken("white-space", "-o-pre-wrap", $token->MediaTypes), + // Internet Explorer >= 5.5 + new CssRulesetDeclarationToken("word-wrap", "break-word", $token->MediaTypes) + ); + return $r; + } + else + { + return array(); + } + } + } + +/** + * This {@link aCssMinifierFilter minifier filter} will convert @keyframes at-rule block to browser specific counterparts. + * + * @package CssMin/Minifier/Filters + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssConvertLevel3AtKeyframesMinifierFilter extends aCssMinifierFilter + { + /** + * Implements {@link aCssMinifierFilter::filter()}. + * + * @param array $tokens Array of objects of type aCssToken + * @return integer Count of added, changed or removed tokens; a return value larger than 0 will rebuild the array + */ + public function apply(array &$tokens) + { + $r = 0; + $transformations = array("-moz-keyframes", "-webkit-keyframes"); + for ($i = 0, $l = count($tokens); $i < $l; $i++) + { + if (get_class($tokens[$i]) === "CssAtKeyframesStartToken") + { + for ($ii = $i; $ii < $l; $ii++) + { + if (get_class($tokens[$ii]) === "CssAtKeyframesEndToken") + { + break; + } + } + if (get_class($tokens[$ii]) === "CssAtKeyframesEndToken") + { + $add = array(); + $source = array(); + for ($iii = $i; $iii <= $ii; $iii++) + { + $source[] = clone($tokens[$iii]); + } + foreach ($transformations as $transformation) + { + $t = array(); + foreach ($source as $token) + { + $t[] = clone($token); + } + $t[0]->AtRuleName = $transformation; + $add = array_merge($add, $t); + } + if (isset($this->configuration["RemoveSource"]) && $this->configuration["RemoveSource"] === true) + { + array_splice($tokens, $i, $ii - $i + 1, $add); + } + else + { + array_splice($tokens, $ii + 1, 0, $add); + } + $l = count($tokens); + $i = $ii + count($add); + $r += count($add); + } + } + } + return $r; + } + } + +/** + * This {@link aCssMinifierPlugin} will convert a color value in hsl notation to hexadecimal notation. + * + * Example: + * + * color: hsl(232,36%,48%); + * + * + * Will get converted to: + * + * color:#4e5aa7; + * + * + * @package CssMin/Minifier/Plugins + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssConvertHslColorsMinifierPlugin extends aCssMinifierPlugin + { + /** + * Regular expression matching the value. + * + * @var string + */ + private $reMatch = "/^hsl\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*%\s*,\s*([0-9]+)\s*%\s*\)/iS"; + /** + * Implements {@link aCssMinifierPlugin::minify()}. + * + * @param aCssToken $token Token to process + * @return boolean Return TRUE to break the processing of this token; FALSE to continue + */ + public function apply(aCssToken &$token) + { + if (stripos($token->Value, "hsl") !== false && preg_match($this->reMatch, $token->Value, $m)) + { + $token->Value = str_replace($m[0], $this->hsl2hex($m[1], $m[2], $m[3]), $token->Value); + } + return false; + } + /** + * Implements {@link aMinifierPlugin::getTriggerTokens()} + * + * @return array + */ + public function getTriggerTokens() + { + return array + ( + "CssAtFontFaceDeclarationToken", + "CssAtPageDeclarationToken", + "CssRulesetDeclarationToken" + ); + } + /** + * Convert a HSL value to hexadecimal notation. + * + * Based on: {@link http://www.easyrgb.com/index.php?X=MATH&H=19#text19}. + * + * @param integer $hue Hue + * @param integer $saturation Saturation + * @param integer $lightness Lightnesss + * @return string + */ + private function hsl2hex($hue, $saturation, $lightness) + { + $hue = $hue / 360; + $saturation = $saturation / 100; + $lightness = $lightness / 100; + if ($saturation == 0) + { + $red = $lightness * 255; + $green = $lightness * 255; + $blue = $lightness * 255; + } + else + { + if ($lightness < 0.5 ) + { + $v2 = $lightness * (1 + $saturation); + } + else + { + $v2 = ($lightness + $saturation) - ($saturation * $lightness); + } + $v1 = 2 * $lightness - $v2; + $red = 255 * self::hue2rgb($v1, $v2, $hue + (1 / 3)); + $green = 255 * self::hue2rgb($v1, $v2, $hue); + $blue = 255 * self::hue2rgb($v1, $v2, $hue - (1 / 3)); + } + return "#" . str_pad(dechex(round($red)), 2, "0", STR_PAD_LEFT) . str_pad(dechex(round($green)), 2, "0", STR_PAD_LEFT) . str_pad(dechex(round($blue)), 2, "0", STR_PAD_LEFT); + } + /** + * Apply hue to a rgb color value. + * + * @param integer $v1 Value 1 + * @param integer $v2 Value 2 + * @param integer $hue Hue + * @return integer + */ + private function hue2rgb($v1, $v2, $hue) + { + if ($hue < 0) + { + $hue += 1; + } + if ($hue > 1) + { + $hue -= 1; + } + if ((6 * $hue) < 1) + { + return ($v1 + ($v2 - $v1) * 6 * $hue); + } + if ((2 * $hue) < 1) + { + return ($v2); + } + if ((3 * $hue) < 2) + { + return ($v1 + ($v2 - $v1) * (( 2 / 3) - $hue) * 6); + } + return $v1; + } + } + +/** + * This {@link aCssMinifierPlugin} will convert the font-weight values normal and bold to their numeric notation. + * + * Example: + * + * font-weight: normal; + * font: bold 11px monospace; + * + * + * Will get converted to: + * + * font-weight:400; + * font:700 11px monospace; + * + * + * @package CssMin/Minifier/Pluginsn + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssConvertFontWeightMinifierPlugin extends aCssMinifierPlugin + { + /** + * Array of included declaration properties this plugin will process; others declaration properties will get + * ignored. + * + * @var array + */ + private $include = array + ( + "font", + "font-weight" + ); + /** + * Regular expression matching the value. + * + * @var string + */ + private $reMatch = null; + /** + * Regular expression replace the value. + * + * @var string + */ + private $reReplace = "\"\${1}\" . \$this->transformation[\"\${2}\"] . \"\${3}\""; + /** + * Transformation table used by the {@link CssConvertFontWeightMinifierPlugin::$reReplace replace regular expression}. + * + * @var array + */ + private $transformation = array + ( + "normal" => "400", + "bold" => "700" + ); + /** + * Overwrites {@link aCssMinifierPlugin::__construct()}. + * + * The constructor will create the {@link CssConvertFontWeightMinifierPlugin::$reReplace replace regular expression} + * based on the {@link CssConvertFontWeightMinifierPlugin::$transformation transformation table}. + * + * @param CssMinifier $minifier The CssMinifier object of this plugin. + * @return void + */ + public function __construct(CssMinifier $minifier) + { + $this->reMatch = "/(^|\s)+(" . implode("|", array_keys($this->transformation)). ")(\s|$)+/eiS"; + parent::__construct($minifier); + } + /** + * Implements {@link aCssMinifierPlugin::minify()}. + * + * @param aCssToken $token Token to process + * @return boolean Return TRUE to break the processing of this token; FALSE to continue + */ + public function apply(aCssToken &$token) + { + if (in_array($token->Property, $this->include) && preg_match($this->reMatch, $token->Value, $m)) + { + $token->Value = preg_replace($this->reMatch, $this->reReplace, $token->Value); + } + return false; + } + /** + * Implements {@link aMinifierPlugin::getTriggerTokens()} + * + * @return array + */ + public function getTriggerTokens() + { + return array + ( + "CssAtFontFaceDeclarationToken", + "CssAtPageDeclarationToken", + "CssRulesetDeclarationToken" + ); + } + } + +/** + * This {@link aCssMinifierPlugin} will compress several unit values to their short notations. Examples: + * + * + * padding: 0.5em; + * border: 0px; + * margin: 0 0 0 0; + * + * + * Will get compressed to: + * + * + * padding:.5px; + * border:0; + * margin:0; + * + * + * -- + * + * @package CssMin/Minifier/Plugins + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssCompressUnitValuesMinifierPlugin extends aCssMinifierPlugin + { + /** + * Regular expression used for matching and replacing unit values. + * + * @var array + */ + private $re = array + ( + "/(^| |-)0\.([0-9]+?)(0+)?(%|em|ex|px|in|cm|mm|pt|pc)/iS" => "\${1}.\${2}\${4}", + "/(^| )-?(\.?)0(%|em|ex|px|in|cm|mm|pt|pc)/iS" => "\${1}0", + "/(^0\s0\s0\s0)|(^0\s0\s0$)|(^0\s0$)/iS" => "0" + ); + /** + * Regular expression matching the value. + * + * @var string + */ + private $reMatch = "/(^| |-)0\.([0-9]+?)(0+)?(%|em|ex|px|in|cm|mm|pt|pc)|(^| )-?(\.?)0(%|em|ex|px|in|cm|mm|pt|pc)|(^0\s0\s0\s0$)|(^0\s0\s0$)|(^0\s0$)/iS"; + /** + * Implements {@link aCssMinifierPlugin::minify()}. + * + * @param aCssToken $token Token to process + * @return boolean Return TRUE to break the processing of this token; FALSE to continue + */ + public function apply(aCssToken &$token) + { + if (preg_match($this->reMatch, $token->Value)) + { + foreach ($this->re as $reMatch => $reReplace) + { + $token->Value = preg_replace($reMatch, $reReplace, $token->Value); + } + } + return false; + } + /** + * Implements {@link aMinifierPlugin::getTriggerTokens()} + * + * @return array + */ + public function getTriggerTokens() + { + return array + ( + "CssAtFontFaceDeclarationToken", + "CssAtPageDeclarationToken", + "CssRulesetDeclarationToken" + ); + } + } + +/** + * This {@link aCssMinifierPlugin} compress the content of expresssion() declaration values. + * + * For compression of expressions {@link https://github.com/rgrove/jsmin-php/ JSMin} will get used. JSMin have to be + * already included or loadable via {@link http://goo.gl/JrW54 PHP autoloading}. + * + * @package CssMin/Minifier/Plugins + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssCompressExpressionValuesMinifierPlugin extends aCssMinifierPlugin + { + /** + * Implements {@link aCssMinifierPlugin::minify()}. + * + * @param aCssToken $token Token to process + * @return boolean Return TRUE to break the processing of this token; FALSE to continue + */ + public function apply(aCssToken &$token) + { + if (class_exists("JSMin") && stripos($token->Value, "expression(") !== false) + { + $value = $token->Value; + $value = substr($token->Value, stripos($token->Value, "expression(") + 10); + $value = trim(JSMin::minify($value)); + $token->Value = "expression(" . $value . ")"; + } + return false; + } + /** + * Implements {@link aMinifierPlugin::getTriggerTokens()} + * + * @return array + */ + public function getTriggerTokens() + { + return array + ( + "CssAtFontFaceDeclarationToken", + "CssAtPageDeclarationToken", + "CssRulesetDeclarationToken" + ); + } + } + +/** + * This {@link aCssMinifierPlugin} will convert hexadecimal color value with 6 chars to their 3 char hexadecimal + * notation (if possible). + * + * Example: + * + * color: #aabbcc; + * + * + * Will get converted to: + * + * color:#abc; + * + * + * @package CssMin/Minifier/Plugins + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssCompressColorValuesMinifierPlugin extends aCssMinifierPlugin + { + /** + * Regular expression matching 6 char hexadecimal color values. + * + * @var string + */ + private $reMatch = "/\#([0-9a-f]{6})/iS"; + /** + * Implements {@link aCssMinifierPlugin::minify()}. + * + * @param aCssToken $token Token to process + * @return boolean Return TRUE to break the processing of this token; FALSE to continue + */ + public function apply(aCssToken &$token) + { + if (strpos($token->Value, "#") !== false && preg_match($this->reMatch, $token->Value, $m)) + { + $value = strtolower($m[1]); + if ($value[0] == $value[1] && $value[2] == $value[3] && $value[4] == $value[5]) + { + $token->Value = str_replace($m[0], "#" . $value[0] . $value[2] . $value[4], $token->Value); + } + } + return false; + } + /** + * Implements {@link aMinifierPlugin::getTriggerTokens()} + * + * @return array + */ + public function getTriggerTokens() + { + return array + ( + "CssAtFontFaceDeclarationToken", + "CssAtPageDeclarationToken", + "CssRulesetDeclarationToken" + ); + } + } + +/** + * This {@link aCssToken CSS token} represents a CSS comment. + * + * @package CssMin/Tokens + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssCommentToken extends aCssToken + { + /** + * Comment as Text. + * + * @var string + */ + public $Comment = ""; + /** + * Set the properties of a comment token. + * + * @param string $comment Comment including comment delimiters + * @return void + */ + public function __construct($comment) + { + $this->Comment = $comment; + } + /** + * Implements {@link aCssToken::__toString()}. + * + * @return string + */ + public function __toString() + { + return $this->Comment; + } + } + +/** + * {@link aCssParserPlugin Parser plugin} for parsing comments. + * + * Adds a {@link CssCommentToken} to the parser if a comment was found. + * + * @package CssMin/Parser/Plugins + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssCommentParserPlugin extends aCssParserPlugin + { + /** + * Implements {@link aCssParserPlugin::getTriggerChars()}. + * + * @return array + */ + public function getTriggerChars() + { + return array("*", "/"); + } + /** + * Implements {@link aCssParserPlugin::getTriggerStates()}. + * + * @return array + */ + public function getTriggerStates() + { + return false; + } + /** + * Stored buffer for restore. + * + * @var string + */ + private $restoreBuffer = ""; + /** + * Implements {@link aCssParserPlugin::parse()}. + * + * @param integer $index Current index + * @param string $char Current char + * @param string $previousChar Previous char + * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing + */ + public function parse($index, $char, $previousChar, $state) + { + if ($char === "*" && $previousChar === "/" && $state !== "T_COMMENT") + { + $this->parser->pushState("T_COMMENT"); + $this->parser->setExclusive(__CLASS__); + $this->restoreBuffer = substr($this->parser->getAndClearBuffer(), 0, -2); + } + elseif ($char === "/" && $previousChar === "*" && $state === "T_COMMENT") + { + $this->parser->popState(); + $this->parser->unsetExclusive(); + $this->parser->appendToken(new CssCommentToken("/*" . $this->parser->getAndClearBuffer())); + $this->parser->setBuffer($this->restoreBuffer); + } + else + { + return false; + } + return true; + } + } + +/** + * This {@link aCssToken CSS token} represents the start of a @variables at-rule block. + * + * @package CssMin/Tokens + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssAtVariablesStartToken extends aCssAtBlockStartToken + { + /** + * Media types of the @variables at-rule block. + * + * @var array + */ + public $MediaTypes = array(); + /** + * Set the properties of a @variables at-rule token. + * + * @param array $mediaTypes Media types + * @return void + */ + public function __construct($mediaTypes = null) + { + $this->MediaTypes = $mediaTypes ? $mediaTypes : array("all"); + } + /** + * Implements {@link aCssToken::__toString()}. + * + * @return string + */ + public function __toString() + { + return ""; + } + } + +/** + * {@link aCssParserPlugin Parser plugin} for parsing @variables at-rule block with including declarations. + * + * Found @variables at-rule blocks will add a {@link CssAtVariablesStartToken} and {@link CssAtVariablesEndToken} to the + * parser; including declarations as {@link CssAtVariablesDeclarationToken}. + * + * @package CssMin/Parser/Plugins + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssAtVariablesParserPlugin extends aCssParserPlugin + { + /** + * Implements {@link aCssParserPlugin::getTriggerChars()}. + * + * @return array + */ + public function getTriggerChars() + { + return array("@", "{", "}", ":", ";"); + } + /** + * Implements {@link aCssParserPlugin::getTriggerStates()}. + * + * @return array + */ + public function getTriggerStates() + { + return array("T_DOCUMENT", "T_AT_VARIABLES::PREPARE", "T_AT_VARIABLES", "T_AT_VARIABLES_DECLARATION"); + } + /** + * Implements {@link aCssParserPlugin::parse()}. + * + * @param integer $index Current index + * @param string $char Current char + * @param string $previousChar Previous char + * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing + */ + public function parse($index, $char, $previousChar, $state) + { + // Start of @variables at-rule block + if ($char === "@" && $state === "T_DOCUMENT" && strtolower(substr($this->parser->getSource(), $index, 10)) === "@variables") + { + $this->parser->pushState("T_AT_VARIABLES::PREPARE"); + $this->parser->clearBuffer(); + return $index + 10; + } + // Start of @variables declarations + elseif ($char === "{" && $state === "T_AT_VARIABLES::PREPARE") + { + $this->parser->setState("T_AT_VARIABLES"); + $mediaTypes = array_filter(array_map("trim", explode(",", $this->parser->getAndClearBuffer("{")))); + $this->parser->appendToken(new CssAtVariablesStartToken($mediaTypes)); + } + // Start of @variables declaration + if ($char === ":" && $state === "T_AT_VARIABLES") + { + $this->buffer = $this->parser->getAndClearBuffer(":"); + $this->parser->pushState("T_AT_VARIABLES_DECLARATION"); + } + // Unterminated @variables declaration + elseif ($char === ":" && $state === "T_AT_VARIABLES_DECLARATION") + { + // Ignore Internet Explorer filter declarations + if ($this->buffer === "filter") + { + return false; + } + CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Unterminated @variables declaration", $this->buffer . ":" . $this->parser->getBuffer() . "_")); + } + // End of @variables declaration + elseif (($char === ";" || $char === "}") && $state === "T_AT_VARIABLES_DECLARATION") + { + $value = $this->parser->getAndClearBuffer(";}"); + if (strtolower(substr($value, -10, 10)) === "!important") + { + $value = trim(substr($value, 0, -10)); + $isImportant = true; + } + else + { + $isImportant = false; + } + $this->parser->popState(); + $this->parser->appendToken(new CssAtVariablesDeclarationToken($this->buffer, $value, $isImportant)); + $this->buffer = ""; + } + // End of @variables at-rule block + elseif ($char === "}" && $state === "T_AT_VARIABLES") + { + $this->parser->popState(); + $this->parser->clearBuffer(); + $this->parser->appendToken(new CssAtVariablesEndToken()); + } + else + { + return false; + } + return true; + } + } + +/** + * This {@link aCssToken CSS token} represents the end of a @variables at-rule block. + * + * @package CssMin/Tokens + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssAtVariablesEndToken extends aCssAtBlockEndToken + { + /** + * Implements {@link aCssToken::__toString()}. + * + * @return string + */ + public function __toString() + { + return ""; + } + } + +/** + * This {@link aCssToken CSS token} represents a declaration of a @variables at-rule block. + * + * @package CssMin/Tokens + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssAtVariablesDeclarationToken extends aCssDeclarationToken + { + /** + * Implements {@link aCssToken::__toString()}. + * + * @return string + */ + public function __toString() + { + return ""; + } + } + +/** +* This {@link aCssToken CSS token} represents the start of a @page at-rule block. + * + * @package CssMin/Tokens + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssAtPageStartToken extends aCssAtBlockStartToken + { + /** + * Selector. + * + * @var string + */ + public $Selector = ""; + /** + * Sets the properties of the @page at-rule. + * + * @param string $selector Selector + * @return void + */ + public function __construct($selector = "") + { + $this->Selector = $selector; + } + /** + * Implements {@link aCssToken::__toString()}. + * + * @return string + */ + public function __toString() + { + return "@page" . ($this->Selector ? " " . $this->Selector : "") . "{"; + } + } + +/** + * {@link aCssParserPlugin Parser plugin} for parsing @page at-rule block with including declarations. + * + * Found @page at-rule blocks will add a {@link CssAtPageStartToken} and {@link CssAtPageEndToken} to the + * parser; including declarations as {@link CssAtPageDeclarationToken}. + * + * @package CssMin/Parser/Plugins + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssAtPageParserPlugin extends aCssParserPlugin + { + /** + * Implements {@link aCssParserPlugin::getTriggerChars()}. + * + * @return array + */ + public function getTriggerChars() + { + return array("@", "{", "}", ":", ";"); + } + /** + * Implements {@link aCssParserPlugin::getTriggerStates()}. + * + * @return array + */ + public function getTriggerStates() + { + return array("T_DOCUMENT", "T_AT_PAGE::SELECTOR", "T_AT_PAGE", "T_AT_PAGE_DECLARATION"); + } + /** + * Implements {@link aCssParserPlugin::parse()}. + * + * @param integer $index Current index + * @param string $char Current char + * @param string $previousChar Previous char + * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing + */ + public function parse($index, $char, $previousChar, $state) + { + // Start of @page at-rule block + if ($char === "@" && $state === "T_DOCUMENT" && strtolower(substr($this->parser->getSource(), $index, 5)) === "@page") + { + $this->parser->pushState("T_AT_PAGE::SELECTOR"); + $this->parser->clearBuffer(); + return $index + 5; + } + // Start of @page declarations + elseif ($char === "{" && $state === "T_AT_PAGE::SELECTOR") + { + $selector = $this->parser->getAndClearBuffer("{"); + $this->parser->setState("T_AT_PAGE"); + $this->parser->clearBuffer(); + $this->parser->appendToken(new CssAtPageStartToken($selector)); + } + // Start of @page declaration + elseif ($char === ":" && $state === "T_AT_PAGE") + { + $this->parser->pushState("T_AT_PAGE_DECLARATION"); + $this->buffer = $this->parser->getAndClearBuffer(":", true); + } + // Unterminated @font-face declaration + elseif ($char === ":" && $state === "T_AT_PAGE_DECLARATION") + { + // Ignore Internet Explorer filter declarations + if ($this->buffer === "filter") + { + return false; + } + CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Unterminated @page declaration", $this->buffer . ":" . $this->parser->getBuffer() . "_")); + } + // End of @page declaration + elseif (($char === ";" || $char === "}") && $state == "T_AT_PAGE_DECLARATION") + { + $value = $this->parser->getAndClearBuffer(";}"); + if (strtolower(substr($value, -10, 10)) == "!important") + { + $value = trim(substr($value, 0, -10)); + $isImportant = true; + } + else + { + $isImportant = false; + } + $this->parser->popState(); + $this->parser->appendToken(new CssAtPageDeclarationToken($this->buffer, $value, $isImportant)); + // -- + if ($char === "}") + { + $this->parser->popState(); + $this->parser->appendToken(new CssAtPageEndToken()); + } + $this->buffer = ""; + } + // End of @page at-rule block + elseif ($char === "}" && $state === "T_AT_PAGE") + { + $this->parser->popState(); + $this->parser->clearBuffer(); + $this->parser->appendToken(new CssAtPageEndToken()); + } + else + { + return false; + } + return true; + } + } + +/** + * This {@link aCssToken CSS token} represents the end of a @page at-rule block. + * + * @package CssMin/Tokens + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssAtPageEndToken extends aCssAtBlockEndToken + { + + } + +/** + * This {@link aCssToken CSS token} represents a declaration of a @page at-rule block. + * + * @package CssMin/Tokens + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssAtPageDeclarationToken extends aCssDeclarationToken + { + + } + +/** + * This {@link aCssToken CSS token} represents the start of a @media at-rule block. + * + * @package CssMin/Tokens + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssAtMediaStartToken extends aCssAtBlockStartToken + { + /** + * Sets the properties of the @media at-rule. + * + * @param array $mediaTypes Media types + * @return void + */ + public function __construct(array $mediaTypes = array()) + { + $this->MediaTypes = $mediaTypes; + } + /** + * Implements {@link aCssToken::__toString()}. + * + * @return string + */ + public function __toString() + { + return "@media " . implode(",", $this->MediaTypes) . "{"; + } + } + +/** + * {@link aCssParserPlugin Parser plugin} for parsing @media at-rule block. + * + * Found @media at-rule blocks will add a {@link CssAtMediaStartToken} and {@link CssAtMediaEndToken} to the parser. + * This plugin will also set the the current media types using {@link CssParser::setMediaTypes()} and + * {@link CssParser::unsetMediaTypes()}. + * + * @package CssMin/Parser/Plugins + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssAtMediaParserPlugin extends aCssParserPlugin + { + /** + * Implements {@link aCssParserPlugin::getTriggerChars()}. + * + * @return array + */ + public function getTriggerChars() + { + return array("@", "{", "}"); + } + /** + * Implements {@link aCssParserPlugin::getTriggerStates()}. + * + * @return array + */ + public function getTriggerStates() + { + return array("T_DOCUMENT", "T_AT_MEDIA::PREPARE", "T_AT_MEDIA"); + } + /** + * Implements {@link aCssParserPlugin::parse()}. + * + * @param integer $index Current index + * @param string $char Current char + * @param string $previousChar Previous char + * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing + */ + public function parse($index, $char, $previousChar, $state) + { + if ($char === "@" && $state === "T_DOCUMENT" && strtolower(substr($this->parser->getSource(), $index, 6)) === "@media") + { + $this->parser->pushState("T_AT_MEDIA::PREPARE"); + $this->parser->clearBuffer(); + return $index + 6; + } + elseif ($char === "{" && $state === "T_AT_MEDIA::PREPARE") + { + $mediaTypes = array_filter(array_map("trim", explode(",", $this->parser->getAndClearBuffer("{")))); + $this->parser->setMediaTypes($mediaTypes); + $this->parser->setState("T_AT_MEDIA"); + $this->parser->appendToken(new CssAtMediaStartToken($mediaTypes)); + } + elseif ($char === "}" && $state === "T_AT_MEDIA") + { + $this->parser->appendToken(new CssAtMediaEndToken()); + $this->parser->clearBuffer(); + $this->parser->unsetMediaTypes(); + $this->parser->popState(); + } + else + { + return false; + } + return true; + } + } + +/** + * This {@link aCssToken CSS token} represents the end of a @media at-rule block. + * + * @package CssMin/Tokens + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssAtMediaEndToken extends aCssAtBlockEndToken + { + + } + +/** + * This {@link aCssToken CSS token} represents the start of a @keyframes at-rule block. + * + * @package CssMin/Tokens + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssAtKeyframesStartToken extends aCssAtBlockStartToken + { + /** + * Name of the at-rule. + * + * @var string + */ + public $AtRuleName = "keyframes"; + /** + * Name + * + * @var string + */ + public $Name = ""; + /** + * Sets the properties of the @page at-rule. + * + * @param string $selector Selector + * @return void + */ + public function __construct($name, $atRuleName = null) + { + $this->Name = $name; + if (!is_null($atRuleName)) + { + $this->AtRuleName = $atRuleName; + } + } + /** + * Implements {@link aCssToken::__toString()}. + * + * @return string + */ + public function __toString() + { + return "@" . $this->AtRuleName . " \"" . $this->Name . "\"{"; + } + } + +/** + * This {@link aCssToken CSS token} represents the start of a ruleset of a @keyframes at-rule block. + * + * @package CssMin/Tokens + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssAtKeyframesRulesetStartToken extends aCssRulesetStartToken + { + /** + * Array of selectors. + * + * @var array + */ + public $Selectors = array(); + /** + * Set the properties of a ruleset token. + * + * @param array $selectors Selectors of the ruleset + * @return void + */ + public function __construct(array $selectors = array()) + { + $this->Selectors = $selectors; + } + /** + * Implements {@link aCssToken::__toString()}. + * + * @return string + */ + public function __toString() + { + return implode(",", $this->Selectors) . "{"; + } + } + +/** + * This {@link aCssToken CSS token} represents the end of a ruleset of a @keyframes at-rule block. + * + * @package CssMin/Tokens + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssAtKeyframesRulesetEndToken extends aCssRulesetEndToken + { + + } + +/** + * This {@link aCssToken CSS token} represents a ruleset declaration of a @keyframes at-rule block. + * + * @package CssMin/Tokens + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssAtKeyframesRulesetDeclarationToken extends aCssDeclarationToken + { + + } + +/** + * {@link aCssParserPlugin Parser plugin} for parsing @keyframes at-rule blocks, rulesets and declarations. + * + * @package CssMin/Parser/Plugins + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssAtKeyframesParserPlugin extends aCssParserPlugin + { + /** + * @var string Keyword + */ + private $atRuleName = ""; + /** + * Selectors. + * + * @var array + */ + private $selectors = array(); + /** + * Implements {@link aCssParserPlugin::getTriggerChars()}. + * + * @return array + */ + public function getTriggerChars() + { + return array("@", "{", "}", ":", ",", ";"); + } + /** + * Implements {@link aCssParserPlugin::getTriggerStates()}. + * + * @return array + */ + public function getTriggerStates() + { + return array("T_DOCUMENT", "T_AT_KEYFRAMES::NAME", "T_AT_KEYFRAMES", "T_AT_KEYFRAMES_RULESETS", "T_AT_KEYFRAMES_RULESET", "T_AT_KEYFRAMES_RULESET_DECLARATION"); + } + /** + * Implements {@link aCssParserPlugin::parse()}. + * + * @param integer $index Current index + * @param string $char Current char + * @param string $previousChar Previous char + * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing + */ + public function parse($index, $char, $previousChar, $state) + { + // Start of @keyframes at-rule block + if ($char === "@" && $state === "T_DOCUMENT" && strtolower(substr($this->parser->getSource(), $index, 10)) === "@keyframes") + { + $this->atRuleName = "keyframes"; + $this->parser->pushState("T_AT_KEYFRAMES::NAME"); + $this->parser->clearBuffer(); + return $index + 10; + } + // Start of @keyframes at-rule block (@-moz-keyframes) + elseif ($char === "@" && $state === "T_DOCUMENT" && strtolower(substr($this->parser->getSource(), $index, 15)) === "@-moz-keyframes") + { + $this->atRuleName = "-moz-keyframes"; + $this->parser->pushState("T_AT_KEYFRAMES::NAME"); + $this->parser->clearBuffer(); + return $index + 15; + } + // Start of @keyframes at-rule block (@-webkit-keyframes) + elseif ($char === "@" && $state === "T_DOCUMENT" && strtolower(substr($this->parser->getSource(), $index, 18)) === "@-webkit-keyframes") + { + $this->atRuleName = "-webkit-keyframes"; + $this->parser->pushState("T_AT_KEYFRAMES::NAME"); + $this->parser->clearBuffer(); + return $index + 18; + } + // Start of @keyframes rulesets + elseif ($char === "{" && $state === "T_AT_KEYFRAMES::NAME") + { + $name = $this->parser->getAndClearBuffer("{\"'"); + $this->parser->setState("T_AT_KEYFRAMES_RULESETS"); + $this->parser->clearBuffer(); + $this->parser->appendToken(new CssAtKeyframesStartToken($name, $this->atRuleName)); + } + // Start of @keyframe ruleset and selectors + if ($char === "," && $state === "T_AT_KEYFRAMES_RULESETS") + { + $this->selectors[] = $this->parser->getAndClearBuffer(",{"); + } + // Start of a @keyframes ruleset + elseif ($char === "{" && $state === "T_AT_KEYFRAMES_RULESETS") + { + if ($this->parser->getBuffer() !== "") + { + $this->selectors[] = $this->parser->getAndClearBuffer(",{"); + $this->parser->pushState("T_AT_KEYFRAMES_RULESET"); + $this->parser->appendToken(new CssAtKeyframesRulesetStartToken($this->selectors)); + $this->selectors = array(); + } + } + // Start of @keyframes ruleset declaration + elseif ($char === ":" && $state === "T_AT_KEYFRAMES_RULESET") + { + $this->parser->pushState("T_AT_KEYFRAMES_RULESET_DECLARATION"); + $this->buffer = $this->parser->getAndClearBuffer(":;", true); + } + // Unterminated @keyframes ruleset declaration + elseif ($char === ":" && $state === "T_AT_KEYFRAMES_RULESET_DECLARATION") + { + // Ignore Internet Explorer filter declarations + if ($this->buffer === "filter") + { + return false; + } + CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Unterminated @keyframes ruleset declaration", $this->buffer . ":" . $this->parser->getBuffer() . "_")); + } + // End of declaration + elseif (($char === ";" || $char === "}") && $state === "T_AT_KEYFRAMES_RULESET_DECLARATION") + { + $value = $this->parser->getAndClearBuffer(";}"); + if (strtolower(substr($value, -10, 10)) === "!important") + { + $value = trim(substr($value, 0, -10)); + $isImportant = true; + } + else + { + $isImportant = false; + } + $this->parser->popState(); + $this->parser->appendToken(new CssAtKeyframesRulesetDeclarationToken($this->buffer, $value, $isImportant)); + // Declaration ends with a right curly brace; so we have to end the ruleset + if ($char === "}") + { + $this->parser->appendToken(new CssAtKeyframesRulesetEndToken()); + $this->parser->popState(); + } + $this->buffer = ""; + } + // End of @keyframes ruleset + elseif ($char === "}" && $state === "T_AT_KEYFRAMES_RULESET") + { + $this->parser->clearBuffer(); + + $this->parser->popState(); + $this->parser->appendToken(new CssAtKeyframesRulesetEndToken()); + } + // End of @keyframes rulesets + elseif ($char === "}" && $state === "T_AT_KEYFRAMES_RULESETS") + { + $this->parser->clearBuffer(); + $this->parser->popState(); + $this->parser->appendToken(new CssAtKeyframesEndToken()); + } + else + { + return false; + } + return true; + } + } + +/** + * This {@link aCssToken CSS token} represents the end of a @keyframes at-rule block. + * + * @package CssMin/Tokens + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssAtKeyframesEndToken extends aCssAtBlockEndToken + { + + } + +/** + * This {@link aCssToken CSS token} represents a @import at-rule. + * + * @package CssMin/Tokens + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1.b1 (2001-02-22) + */ +class CssAtImportToken extends aCssToken + { + /** + * Import path of the @import at-rule. + * + * @var string + */ + public $Import = ""; + /** + * Media types of the @import at-rule. + * + * @var array + */ + public $MediaTypes = array(); + /** + * Set the properties of a @import at-rule token. + * + * @param string $import Import path + * @param array $mediaTypes Media types + * @return void + */ + public function __construct($import, $mediaTypes) + { + $this->Import = $import; + $this->MediaTypes = $mediaTypes ? $mediaTypes : array(); + } + /** + * Implements {@link aCssToken::__toString()}. + * + * @return string + */ + public function __toString() + { + return "@import \"" . $this->Import . "\"" . (count($this->MediaTypes) > 0 ? " " . implode(",", $this->MediaTypes) : ""). ";"; + } + } + +/** + * {@link aCssParserPlugin Parser plugin} for parsing @import at-rule. + * + * If a @import at-rule was found this plugin will add a {@link CssAtImportToken} to the parser. + * + * @package CssMin/Parser/Plugins + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssAtImportParserPlugin extends aCssParserPlugin + { + /** + * Implements {@link aCssParserPlugin::getTriggerChars()}. + * + * @return array + */ + public function getTriggerChars() + { + return array("@", ";", ",", "\n"); + } + /** + * Implements {@link aCssParserPlugin::getTriggerStates()}. + * + * @return array + */ + public function getTriggerStates() + { + return array("T_DOCUMENT", "T_AT_IMPORT"); + } + /** + * Implements {@link aCssParserPlugin::parse()}. + * + * @param integer $index Current index + * @param string $char Current char + * @param string $previousChar Previous char + * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing + */ + public function parse($index, $char, $previousChar, $state) + { + if ($char === "@" && $state === "T_DOCUMENT" && strtolower(substr($this->parser->getSource(), $index, 7)) === "@import") + { + $this->parser->pushState("T_AT_IMPORT"); + $this->parser->clearBuffer(); + return $index + 7; + } + elseif (($char === ";" || $char === "\n") && $state === "T_AT_IMPORT") + { + $this->buffer = $this->parser->getAndClearBuffer(";"); + $pos = false; + foreach (array(")", "\"", "'") as $needle) + { + if (($pos = strrpos($this->buffer, $needle)) !== false) + { + break; + } + } + $import = substr($this->buffer, 0, $pos + 1); + if (stripos($import, "url(") === 0) + { + $import = substr($import, 4, -1); + } + $import = trim($import, " \t\n\r\0\x0B'\""); + $mediaTypes = array_filter(array_map("trim", explode(",", trim(substr($this->buffer, $pos + 1), " \t\n\r\0\x0B{")))); + if ($pos) + { + $this->parser->appendToken(new CssAtImportToken($import, $mediaTypes)); + } + else + { + CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Invalid @import at-rule syntax", $this->parser->buffer)); + } + $this->parser->popState(); + } + else + { + return false; + } + return true; + } + } + +/** + * This {@link aCssToken CSS token} represents the start of a @font-face at-rule block. + * + * @package CssMin/Tokens + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssAtFontFaceStartToken extends aCssAtBlockStartToken + { + /** + * Implements {@link aCssToken::__toString()}. + * + * @return string + */ + public function __toString() + { + return "@font-face{"; + } + } + +/** + * {@link aCssParserPlugin Parser plugin} for parsing @font-face at-rule block with including declarations. + * + * Found @font-face at-rule blocks will add a {@link CssAtFontFaceStartToken} and {@link CssAtFontFaceEndToken} to the + * parser; including declarations as {@link CssAtFontFaceDeclarationToken}. + * + * @package CssMin/Parser/Plugins + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssAtFontFaceParserPlugin extends aCssParserPlugin + { + /** + * Implements {@link aCssParserPlugin::getTriggerChars()}. + * + * @return array + */ + public function getTriggerChars() + { + return array("@", "{", "}", ":", ";"); + } + /** + * Implements {@link aCssParserPlugin::getTriggerStates()}. + * + * @return array + */ + public function getTriggerStates() + { + return array("T_DOCUMENT", "T_AT_FONT_FACE::PREPARE", "T_AT_FONT_FACE", "T_AT_FONT_FACE_DECLARATION"); + } + /** + * Implements {@link aCssParserPlugin::parse()}. + * + * @param integer $index Current index + * @param string $char Current char + * @param string $previousChar Previous char + * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing + */ + public function parse($index, $char, $previousChar, $state) + { + // Start of @font-face at-rule block + if ($char === "@" && $state === "T_DOCUMENT" && strtolower(substr($this->parser->getSource(), $index, 10)) === "@font-face") + { + $this->parser->pushState("T_AT_FONT_FACE::PREPARE"); + $this->parser->clearBuffer(); + return $index + 10; + } + // Start of @font-face declarations + elseif ($char === "{" && $state === "T_AT_FONT_FACE::PREPARE") + { + $this->parser->setState("T_AT_FONT_FACE"); + $this->parser->clearBuffer(); + $this->parser->appendToken(new CssAtFontFaceStartToken()); + } + // Start of @font-face declaration + elseif ($char === ":" && $state === "T_AT_FONT_FACE") + { + $this->parser->pushState("T_AT_FONT_FACE_DECLARATION"); + $this->buffer = $this->parser->getAndClearBuffer(":", true); + } + // Unterminated @font-face declaration + elseif ($char === ":" && $state === "T_AT_FONT_FACE_DECLARATION") + { + // Ignore Internet Explorer filter declarations + if ($this->buffer === "filter") + { + return false; + } + CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Unterminated @font-face declaration", $this->buffer . ":" . $this->parser->getBuffer() . "_")); + } + // End of @font-face declaration + elseif (($char === ";" || $char === "}") && $state === "T_AT_FONT_FACE_DECLARATION") + { + $value = $this->parser->getAndClearBuffer(";}"); + if (strtolower(substr($value, -10, 10)) === "!important") + { + $value = trim(substr($value, 0, -10)); + $isImportant = true; + } + else + { + $isImportant = false; + } + $this->parser->popState(); + $this->parser->appendToken(new CssAtFontFaceDeclarationToken($this->buffer, $value, $isImportant)); + $this->buffer = ""; + // -- + if ($char === "}") + { + $this->parser->appendToken(new CssAtFontFaceEndToken()); + $this->parser->popState(); + } + } + // End of @font-face at-rule block + elseif ($char === "}" && $state === "T_AT_FONT_FACE") + { + $this->parser->appendToken(new CssAtFontFaceEndToken()); + $this->parser->clearBuffer(); + $this->parser->popState(); + } + else + { + return false; + } + return true; + } + } + +/** + * This {@link aCssToken CSS token} represents the end of a @font-face at-rule block. + * + * @package CssMin/Tokens + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssAtFontFaceEndToken extends aCssAtBlockEndToken + { + + } + +/** + * This {@link aCssToken CSS token} represents a declaration of a @font-face at-rule block. + * + * @package CssMin/Tokens + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssAtFontFaceDeclarationToken extends aCssDeclarationToken + { + + } + +/** + * This {@link aCssToken CSS token} represents a @charset at-rule. + * + * @package CssMin/Tokens + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssAtCharsetToken extends aCssToken + { + /** + * Charset of the @charset at-rule. + * + * @var string + */ + public $Charset = ""; + /** + * Set the properties of @charset at-rule token. + * + * @param string $charset Charset of the @charset at-rule token + * @return void + */ + public function __construct($charset) + { + $this->Charset = $charset; + } + /** + * Implements {@link aCssToken::__toString()}. + * + * @return string + */ + public function __toString() + { + return "@charset " . $this->Charset . ";"; + } + } + +/** + * {@link aCssParserPlugin Parser plugin} for parsing @charset at-rule. + * + * If a @charset at-rule was found this plugin will add a {@link CssAtCharsetToken} to the parser. + * + * @package CssMin/Parser/Plugins + * @link http://code.google.com/p/cssmin/ + * @author Joe Scylla + * @copyright 2008 - 2011 Joe Scylla + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 3.0.1 + */ +class CssAtCharsetParserPlugin extends aCssParserPlugin + { + /** + * Implements {@link aCssParserPlugin::getTriggerChars()}. + * + * @return array + */ + public function getTriggerChars() + { + return array("@", ";", "\n"); + } + /** + * Implements {@link aCssParserPlugin::getTriggerStates()}. + * + * @return array + */ + public function getTriggerStates() + { + return array("T_DOCUMENT", "T_AT_CHARSET"); + } + /** + * Implements {@link aCssParserPlugin::parse()}. + * + * @param integer $index Current index + * @param string $char Current char + * @param string $previousChar Previous char + * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing + */ + public function parse($index, $char, $previousChar, $state) + { + if ($char === "@" && $state === "T_DOCUMENT" && strtolower(substr($this->parser->getSource(), $index, 8)) === "@charset") + { + $this->parser->pushState("T_AT_CHARSET"); + $this->parser->clearBuffer(); + return $index + 8; + } + elseif (($char === ";" || $char === "\n") && $state === "T_AT_CHARSET") + { + $charset = $this->parser->getAndClearBuffer(";"); + $this->parser->popState(); + $this->parser->appendToken(new CssAtCharsetToken($charset)); + } + else + { + return false; + } + return true; + } + } + diff --git a/sparks/assets/1.5.1/libraries/jsmin.php b/sparks/assets/1.5.1/libraries/jsmin.php new file mode 100644 index 0000000..f3718db --- /dev/null +++ b/sparks/assets/1.5.1/libraries/jsmin.php @@ -0,0 +1,385 @@ + + * @copyright 2002 Douglas Crockford (jsmin.c) + * @copyright 2008 Ryan Grove (PHP port) + * @copyright 2012 Adam Goforth (Updates) + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 1.1.2 (2012-05-01) + * @link https://github.com/rgrove/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 = ''; + protected $b = ''; + protected $input = ''; + protected $inputIndex = 0; + protected $inputLength = 0; + protected $lookAhead = null; + protected $output = ''; + + // -- Public Static Methods -------------------------------------------------- + + /** + * Minify Javascript + * + * @uses __construct() + * @uses min() + * @param string $js Javascript to be minified + * @return string + */ + public static function minify($js) { + $jsmin = new JSMin($js); + return $jsmin->min(); + } + + // -- Public Instance Methods ------------------------------------------------ + + /** + * Constructor + * + * @param string $input Javascript to be minified + */ + public function __construct($input) { + $this->input = str_replace("\r\n", "\n", $input); + $this->inputLength = strlen($this->input); + } + + // -- Protected Instance Methods --------------------------------------------- + + /** + * Action -- do something! What to do is determined by the $command argument. + * + * action treats a string as a single character. Wow! + * action recognizes a regular expression if it is preceded by ( or , or =. + * + * @uses next() + * @uses get() + * @throws JSMinException If parser errors are found: + * - Unterminated string literal + * - Unterminated regular expression set in regex literal + * - Unterminated regular expression literal + * @param int $command One of class constants: + * ACTION_KEEP_A Output A. Copy B to A. Get the next B. + * ACTION_DELETE_A Copy B to A. Get the next B. (Delete A). + * ACTION_DELETE_A_B Get the next B. (Delete B). + */ + protected function action($command) { + switch($command) { + case self::ACTION_KEEP_A: + $this->output .= $this->a; + + case self::ACTION_DELETE_A: + $this->a = $this->b; + + if ($this->a === "'" || $this->a === '"') { + for (;;) { + $this->output .= $this->a; + $this->a = $this->get(); + + if ($this->a === $this->b) { + break; + } + + if (ord($this->a) <= self::ORD_LF) { + throw new JSMinException('Unterminated string literal.'); + } + + if ($this->a === '\\') { + $this->output .= $this->a; + $this->a = $this->get(); + } + } + } + + case self::ACTION_DELETE_A_B: + $this->b = $this->next(); + + if ($this->b === '/' && ( + $this->a === '(' || $this->a === ',' || $this->a === '=' || + $this->a === ':' || $this->a === '[' || $this->a === '!' || + $this->a === '&' || $this->a === '|' || $this->a === '?' || + $this->a === '{' || $this->a === '}' || $this->a === ';' || + $this->a === "\n" )) { + + $this->output .= $this->a . $this->b; + + for (;;) { + $this->a = $this->get(); + + if ($this->a === '[') { + /* + inside a regex [...] set, which MAY contain a '/' itself. Example: mootools Form.Validator near line 460: + return Form.Validator.getValidator('IsEmpty').test(element) || (/^(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]\.?){0,63}[a-z0-9!#$%&'*+/=?^_`{|}~-]@(?:(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\])$/i).test(element.get('value')); + */ + for (;;) { + $this->output .= $this->a; + $this->a = $this->get(); + + if ($this->a === ']') { + break; + } elseif ($this->a === '\\') { + $this->output .= $this->a; + $this->a = $this->get(); + } elseif (ord($this->a) <= self::ORD_LF) { + throw new JSMinException('Unterminated regular expression set in regex literal.'); + } + } + } elseif ($this->a === '/') { + break; + } elseif ($this->a === '\\') { + $this->output .= $this->a; + $this->a = $this->get(); + } elseif (ord($this->a) <= self::ORD_LF) { + throw new JSMinException('Unterminated regular expression literal.'); + } + + $this->output .= $this->a; + } + + $this->b = $this->next(); + } + } + } + + /** + * Get next char. Convert ctrl char to space. + * + * @return string|null + */ + protected function get() { + $c = $this->lookAhead; + $this->lookAhead = null; + + if ($c === null) { + if ($this->inputIndex < $this->inputLength) { + $c = substr($this->input, $this->inputIndex, 1); + $this->inputIndex += 1; + } else { + $c = null; + } + } + + if ($c === "\r") { + return "\n"; + } + + if ($c === null || $c === "\n" || ord($c) >= self::ORD_SPACE) { + return $c; + } + + return ' '; + } + + /** + * Is $c a letter, digit, underscore, dollar sign, or non-ASCII character. + * + * @return bool + */ + protected function isAlphaNum($c) { + return ord($c) > 126 || $c === '\\' || preg_match('/^[\w\$]$/', $c) === 1; + } + + /** + * Perform minification, return result + * + * @uses action() + * @uses isAlphaNum() + * @uses get() + * @uses peek() + * @return string + */ + protected function min() { + if (0 == strncmp($this->peek(), "\xef", 1)) { + $this->get(); + $this->get(); + $this->get(); + } + + $this->a = "\n"; + $this->action(self::ACTION_DELETE_A_B); + + while ($this->a !== null) { + switch ($this->a) { + case ' ': + if ($this->isAlphaNum($this->b)) { + $this->action(self::ACTION_KEEP_A); + } else { + $this->action(self::ACTION_DELETE_A); + } + break; + + case "\n": + switch ($this->b) { + case '{': + case '[': + case '(': + case '+': + case '-': + case '!': + case '~': + $this->action(self::ACTION_KEEP_A); + break; + + case ' ': + $this->action(self::ACTION_DELETE_A_B); + break; + + default: + if ($this->isAlphaNum($this->b)) { + $this->action(self::ACTION_KEEP_A); + } + else { + $this->action(self::ACTION_DELETE_A); + } + } + break; + + default: + switch ($this->b) { + case ' ': + if ($this->isAlphaNum($this->a)) { + $this->action(self::ACTION_KEEP_A); + break; + } + + $this->action(self::ACTION_DELETE_A_B); + break; + + case "\n": + switch ($this->a) { + case '}': + case ']': + case ')': + case '+': + case '-': + case '"': + case "'": + $this->action(self::ACTION_KEEP_A); + break; + + default: + if ($this->isAlphaNum($this->a)) { + $this->action(self::ACTION_KEEP_A); + } + else { + $this->action(self::ACTION_DELETE_A_B); + } + } + break; + + default: + $this->action(self::ACTION_KEEP_A); + break; + } + } + } + + return $this->output; + } + + /** + * Get the next character, skipping over comments. peek() is used to see + * if a '/' is followed by a '/' or '*'. + * + * @uses get() + * @uses peek() + * @throws JSMinException On unterminated comment. + * @return string + */ + protected function next() { + $c = $this->get(); + + if ($c === '/') { + switch($this->peek()) { + case '/': + for (;;) { + $c = $this->get(); + + if (ord($c) <= self::ORD_LF) { + return $c; + } + } + + case '*': + $this->get(); + + for (;;) { + switch($this->get()) { + case '*': + if ($this->peek() === '/') { + $this->get(); + return ' '; + } + break; + + case null: + throw new JSMinException('Unterminated comment.'); + } + } + + default: + return $c; + } + } + + return $c; + } + + /** + * Get next char. If is ctrl character, translate to a space or newline. + * + * @uses get() + * @return string|null + */ + protected function peek() { + $this->lookAhead = $this->get(); + return $this->lookAhead; + } +} + +// -- Exceptions --------------------------------------------------------------- +class JSMinException extends Exception {} diff --git a/sparks/assets/1.5.1/libraries/lessc.php b/sparks/assets/1.5.1/libraries/lessc.php new file mode 100644 index 0000000..e3ed990 --- /dev/null +++ b/sparks/assets/1.5.1/libraries/lessc.php @@ -0,0 +1,3320 @@ + + * Licensed under MIT or GPLv3, see LICENSE + */ + + +/** + * The less compiler and parser. + * + * Converting LESS to CSS is a three stage process. The incoming file is parsed + * by `lessc_parser` into a syntax tree, then it is compiled into another tree + * representing the CSS structure by `lessc`. The CSS tree is fed into a + * formatter, like `lessc_formatter` which then outputs CSS as a string. + * + * During the first compile, all values are *reduced*, which means that their + * types are brought to the lowest form before being dump as strings. This + * handles math equations, variable dereferences, and the like. + * + * The `parse` function of `lessc` is the entry point. + * + * In summary: + * + * The `lessc` class creates an intstance of the parser, feeds it LESS code, + * then transforms the resulting tree to a CSS tree. This class also holds the + * evaluation context, such as all available mixins and variables at any given + * time. + * + * The `lessc_parser` class is only concerned with parsing its input. + * + * The `lessc_formatter` takes a CSS tree, and dumps it to a formatted string, + * handling things like indentation. + */ +class lessc { + static public $VERSION = "v0.3.7"; + static protected $TRUE = array("keyword", "true"); + static protected $FALSE = array("keyword", "false"); + + protected $libFunctions = array(); + protected $registeredVars = array(); + protected $preserveComments = false; + + public $vPrefix = '@'; // prefix of abstract properties + public $mPrefix = '$'; // prefix of abstract blocks + public $parentSelector = '&'; + + public $importDisabled = false; + public $importDir = ''; + + protected $numberPrecision = null; + + // set to the parser that generated the current line when compiling + // so we know how to create error messages + protected $sourceParser = null; + protected $sourceLoc = null; + + static public $defaultValue = array("keyword", ""); + + // attempts to find the path of an import url, returns null for css files + protected function findImport($url) { + foreach ((array)$this->importDir as $dir) { + $full = $dir.(substr($dir, -1) != '/' ? '/' : '').$url; + if ($this->fileExists($file = $full.'.less') || $this->fileExists($file = $full)) { + return $file; + } + } + + return null; + } + + protected function fileExists($name) { + return is_file($name); + } + + static public function compressList($items, $delim) { + if (!isset($items[1]) && isset($items[0])) return $items[0]; + else return array('list', $delim, $items); + } + + static public function preg_quote($what) { + return preg_quote($what, '/'); + } + + // attempt to import $import into $parentBlock + // $props is the property array that will given to $parentBlock at the end + protected function mixImport($import, $parentBlock, &$props) { + list(, $url, $media) = $import; + + if (is_array($url)) { + $url = $this->compileValue($this->lib_e($this->reduce($url))); + } + + if (empty($media) && substr_compare($url, '.css', -4, 4) !== 0) { + if ($this->importDisabled) { + $props[] = array('raw', '/* import disabled */'); + return true; + } + + $realPath = $this->findImport($url); + if (!is_null($realPath)) { + $this->addParsedFile($realPath); + + $parser = $this->makeParser($realPath); + $root = $parser->parse(file_get_contents($realPath)); + $root->parent = $parentBlock; + + // handle all the imports in the new file + $pi = pathinfo($realPath); + $this->mixImports($root, $pi['dirname'].'/'); + + // bring blocks from import into current block + foreach ($root->children as $childName => $child) { + if (isset($parentBlock->children[$childName])) { + $parentBlock->children[$childName] = array_merge( + $parentBlock->children[$childName], + $child); + } else { + $parentBlock->children[$childName] = $child; + } + } + + // splice in the props + foreach ($root->props as $prop) { + // leave a reference to the file where it came from + if (isset($prop[-1]) && !is_array($prop[-1])) { + $prop[-1] = array($parser, $prop[-1]); + } + $props[] = $prop; + } + + return true; + } + } + + // fallback to regular css import + $props[] = array('raw', '@import url("'.$url.'")'.($media ? ' '.$media : '').';'); + return false; + } + + // import all imports mentioned in the block + protected function mixImports($block, $importDir = null) { + $oldImport = $this->importDir; + if ($importDir !== null) { + $this->importDir = array_merge((array)$importDir, (array)$this->importDir); + } + + $props = array(); + foreach ($block->props as $prop) { + if ($prop[0] == 'import') { + $this->mixImport($prop, $block, $props); + } else { + $props[] = $prop; + } + } + $block->props = $props; + $this->importDir = $oldImport; + } + + + /** + * Recursively compiles a block. + * + * A block is analogous to a CSS block in most cases. A single LESS document + * is encapsulated in a block when parsed, but it does not have parent tags + * so all of it's children appear on the root level when compiled. + * + * Blocks are made up of props and children. + * + * Props are property instructions, array tuples which describe an action + * to be taken, eg. write a property, set a variable, mixin a block. + * + * The children of a block are just all the blocks that are defined within. + * This is used to look up mixins when performing a mixin. + * + * Compiling the block involves pushing a fresh environment on the stack, + * and iterating through the props, compiling each one. + * + * See lessc::compileProp() + * + */ + protected function compileBlock($block) { + switch ($block->type) { + case "root": + return $this->compileRoot($block); + case null: + return $this->compileCSSBlock($block); + case "media": + return $this->compileMedia($block); + case "directive": + $name = "@" . $block->name; + if (!empty($block->value)) { + $name .= " " . $this->compileValue($this->reduce($block->value)); + } + + return $this->compileNestedBlock($block, array($name)); + default: + $this->throwError("unknown block type: $block->type\n"); + } + } + + protected function compileCSSBlock($block) { + $env = $this->pushEnv(); + + $selectors = $this->compileSelectors($block->tags); + $env->selectors = $this->multiplySelectors($selectors); + $out = $this->makeOutputBlock(null, $env->selectors); + + $this->scope->children[] = $out; + $this->compileProps($block, $out); + + $block->scope = $env; // mixins carry scope with them! + $this->popEnv(); + } + + protected function compileMedia($media) { + $env = $this->pushEnv($media); + $parentScope = $this->mediaParent($this->scope); + + $query = $this->compileMediaQuery($this->multiplyMedia($env)); + + $this->scope = $this->makeOutputBlock($media->type, array($query)); + $parentScope->children[] = $this->scope; + + $this->compileProps($media, $this->scope); + + if (count($this->scope->lines) > 0) { + $orphanSelelectors = $this->findClosestSelectors(); + if (!is_null($orphanSelelectors)) { + $orphan = $this->makeOutputBlock(null, $orphanSelelectors); + $orphan->lines = $this->scope->lines; + array_unshift($this->scope->children, $orphan); + $this->scope->lines = array(); + } + } + + $this->scope = $this->scope->parent; + $this->popEnv(); + } + + protected function mediaParent($scope) { + while (!empty($scope->parent)) { + if (!empty($scope->type) && $scope->type != "media") { + break; + } + $scope = $scope->parent; + } + + return $scope; + } + + protected function compileNestedBlock($block, $selectors) { + $this->pushEnv($block); + $this->scope = $this->makeOutputBlock($block->type, $selectors); + $this->scope->parent->children[] = $this->scope; + + $this->compileProps($block, $this->scope); + + $this->scope = $this->scope->parent; + $this->popEnv(); + } + + protected function compileRoot($root) { + $this->pushEnv(); + $this->scope = $this->makeOutputBlock($root->type); + $this->compileProps($root, $this->scope); + $this->popEnv(); + } + + protected function compileProps($block, $out) { + $this->mixImports($block); + foreach ($this->sortProps($block->props) as $prop) { + $this->compileProp($prop, $block, $out); + } + } + + protected function sortProps($props) { + $vars = array(); + $other = array(); + + foreach ($props as $prop) { + if ($prop[0] == "assign" && + substr($prop[1], 0, 1) == $this->vPrefix) { + $vars[] = $prop; + } else { + $other[] = $prop; + } + } + + return array_merge($vars, $other); + } + + protected function compileMediaQuery($queries) { + $compiledQueries = array(); + foreach ($queries as $query) { + $parts = array(); + foreach ($query as $q) { + switch ($q[0]) { + case "mediaType": + $parts[] = implode(" ", array_slice($q, 1)); + break; + case "mediaExp": + if (isset($q[2])) { + $parts[] = "($q[1]: " . + $this->compileValue($this->reduce($q[2])) . ")"; + } else { + $parts[] = "($q[1])"; + } + break; + } + } + + if (count($parts) > 0) { + $compiledQueries[] = implode(" and ", $parts); + } + } + + $out = "@media"; + if (!empty($parts)) { + $out .= " " . + implode($this->formatter->selectorSeparator, $compiledQueries); + } + return $out; + } + + protected function multiplyMedia($env, $childQueries = null) { + if (is_null($env) || + !empty($env->block->type) && $env->block->type != "media") + { + return $childQueries; + } + + // plain old block, skip + if (empty($env->block->type)) { + return $this->multiplyMedia($env->parent, $childQueries); + } + + $out = array(); + $queries = $env->block->queries; + if (is_null($childQueries)) { + $out = $queries; + } else { + foreach ($queries as $parent) { + foreach ($childQueries as $child) { + $out[] = array_merge($parent, $child); + } + } + } + + return $this->multiplyMedia($env->parent, $out); + } + + protected function expandParentSelectors(&$tag, $replace) { + $parts = explode("$&$", $tag); + $count = 0; + foreach ($parts as &$part) { + $part = str_replace($this->parentSelector, $replace, $part, $c); + $count += $c; + } + $tag = implode($this->parentSelector, $parts); + return $count; + } + + protected function findClosestSelectors() { + $env = $this->env; + $selectors = null; + while ($env !== null) { + if (isset($env->selectors)) { + $selectors = $env->selectors; + break; + } + $env = $env->parent; + } + + return $selectors; + } + + + // multiply $selectors against the nearest selectors in env + protected function multiplySelectors($selectors) { + // find parent selectors + + $parentSelectors = $this->findClosestSelectors(); + if (is_null($parentSelectors)) { + // kill parent reference in top level selector + foreach ($selectors as &$s) { + $this->expandParentSelectors($s, ""); + } + + return $selectors; + } + + $out = array(); + foreach ($parentSelectors as $parent) { + foreach ($selectors as $child) { + $count = $this->expandParentSelectors($child, $parent); + + // don't prepend the parent tag if & was used + if ($count > 0) { + $out[] = trim($child); + } else { + $out[] = trim($parent . ' ' . $child); + } + } + } + + return $out; + } + + // reduces selector expressions + protected function compileSelectors($selectors) { + $out = array(); + + foreach ($selectors as $s) { + if (is_array($s)) { + list(, $value) = $s; + $out[] = $this->compileValue($this->reduce($value)); + } else { + $out[] = $s; + } + } + + return $out; + } + + protected function eq($left, $right) { + return $left == $right; + } + + protected function patternMatch($block, $callingArgs) { + // match the guards if it has them + // any one of the groups must have all its guards pass for a match + if (!empty($block->guards)) { + $groupPassed = false; + foreach ($block->guards as $guardGroup) { + foreach ($guardGroup as $guard) { + $this->pushEnv(); + $this->zipSetArgs($block->args, $callingArgs); + + $negate = false; + if ($guard[0] == "negate") { + $guard = $guard[1]; + $negate = true; + } + + $passed = $this->reduce($guard) == self::$TRUE; + if ($negate) $passed = !$passed; + + $this->popEnv(); + + if ($passed) { + $groupPassed = true; + } else { + $groupPassed = false; + break; + } + } + + if ($groupPassed) break; + } + + if (!$groupPassed) { + return false; + } + } + + $numCalling = count($callingArgs); + + if (empty($block->args)) { + return $block->isVararg || $numCalling == 0; + } + + $i = -1; // no args + // try to match by arity or by argument literal + foreach ($block->args as $i => $arg) { + switch ($arg[0]) { + case "lit": + if (empty($callingArgs[$i]) || !$this->eq($arg[1], $callingArgs[$i])) { + return false; + } + break; + case "arg": + // no arg and no default value + if (!isset($callingArgs[$i]) && !isset($arg[2])) { + return false; + } + break; + case "rest": + $i--; // rest can be empty + break 2; + } + } + + if ($block->isVararg) { + return true; // not having enough is handled above + } else { + $numMatched = $i + 1; + // greater than becuase default values always match + return $numMatched >= $numCalling; + } + } + + protected function patternMatchAll($blocks, $callingArgs) { + $matches = null; + foreach ($blocks as $block) { + if ($this->patternMatch($block, $callingArgs)) { + $matches[] = $block; + } + } + + return $matches; + } + + // attempt to find blocks matched by path and args + protected function findBlocks($searchIn, $path, $args, $seen=array()) { + if ($searchIn == null) return null; + if (isset($seen[$searchIn->id])) return null; + $seen[$searchIn->id] = true; + + $name = $path[0]; + + if (isset($searchIn->children[$name])) { + $blocks = $searchIn->children[$name]; + if (count($path) == 1) { + $matches = $this->patternMatchAll($blocks, $args); + if (!empty($matches)) { + // This will return all blocks that match in the closest + // scope that has any matching block, like lessjs + return $matches; + } + } else { + $matches = array(); + foreach ($blocks as $subBlock) { + $subMatches = $this->findBlocks($subBlock, + array_slice($path, 1), $args, $seen); + + if (!is_null($subMatches)) { + foreach ($subMatches as $sm) { + $matches[] = $sm; + } + } + } + + return count($matches) > 0 ? $matches : null; + } + } + + if ($searchIn->parent === $searchIn) return null; + return $this->findBlocks($searchIn->parent, $path, $args, $seen); + } + + // sets all argument names in $args to either the default value + // or the one passed in through $values + protected function zipSetArgs($args, $values) { + $i = 0; + $assignedValues = array(); + foreach ($args as $a) { + if ($a[0] == "arg") { + if ($i < count($values) && !is_null($values[$i])) { + $value = $values[$i]; + } elseif (isset($a[2])) { + $value = $a[2]; + } else $value = null; + + $value = $this->reduce($value); + $this->set($a[1], $value); + $assignedValues[] = $value; + } + $i++; + } + + // check for a rest + $last = end($args); + if ($last[0] == "rest") { + $rest = array_slice($values, count($args) - 1); + $this->set($last[1], $this->reduce(array("list", " ", $rest))); + } + + $this->env->arguments = $assignedValues; + } + + // compile a prop and update $lines or $blocks appropriately + protected function compileProp($prop, $block, $out) { + // set error position context + if (isset($prop[-1])) { + if (is_array($prop[-1])) { + list($parser, $count) = $prop[-1]; + $this->sourceParser = $parser; + $this->sourceLoc = $count; + } else { + $this->sourceParser = $this->parser; + $this->sourceLoc = $prop[-1]; + } + } else { + $this->sourceLoc = -1; + } + + switch ($prop[0]) { + case 'assign': + list(, $name, $value) = $prop; + if ($name[0] == $this->vPrefix) { + $this->set($name, $value); + } else { + $out->lines[] = $this->formatter->property($name, + $this->compileValue($this->reduce($value))); + } + break; + case 'block': + list(, $child) = $prop; + $this->compileBlock($child); + break; + case 'mixin': + list(, $path, $args, $suffix) = $prop; + + $args = array_map(array($this, "reduce"), (array)$args); + $mixins = $this->findBlocks($block, $path, $args); + if ($mixins === null) { + // echo "failed to find block: ".implode(" > ", $path)."\n"; + break; // throw error here?? + } + + foreach ($mixins as $mixin) { + $haveScope = false; + if (isset($mixin->parent->scope)) { + $haveScope = true; + $mixinParentEnv = $this->pushEnv(); + $mixinParentEnv->storeParent = $mixin->parent->scope; + } + + $haveArgs = false; + if (isset($mixin->args)) { + $haveArgs = true; + $this->pushEnv(); + $this->zipSetArgs($mixin->args, $args); + } + + $oldParent = $mixin->parent; + if ($mixin != $block) $mixin->parent = $block; + + $this->mixImports($mixin); + foreach ($this->sortProps($mixin->props) as $subProp) { + if ($suffix !== null && + $subProp[0] == "assign" && + is_string($subProp[1]) && + $subProp[1]{0} != $this->vPrefix) + { + $subProp[2] = array( + 'list', ' ', + array($subProp[2], array('keyword', $suffix)) + ); + } + + $this->compileProp($subProp, $mixin, $out); + } + + $mixin->parent = $oldParent; + + if ($haveArgs) $this->popEnv(); + if ($haveScope) $this->popEnv(); + } + + break; + case 'raw': + $out->lines[] = $prop[1]; + break; + case "directive": + list(, $name, $value) = $prop; + $out->lines[] = "@$name " . $this->compileValue($this->reduce($value)).';'; + break; + case "comment": + $out->lines[] = $prop[1]; + break; + default: + $this->throwError("unknown op: {$prop[0]}\n"); + } + } + + + /** + * Compiles a primitive value into a CSS property value. + * + * Values in lessphp are typed by being wrapped in arrays, their format is + * typically: + * + * array(type, contents [, additional_contents]*) + * + * The input is expected to be reduced. This function will not work on + * things like expressions and variables. + */ + protected function compileValue($value) { + switch ($value[0]) { + case 'list': + // [1] - delimiter + // [2] - array of values + return implode($value[1], array_map(array($this, 'compileValue'), $value[2])); + case 'raw_color'; + case 'keyword': + // [1] - the keyword + return $value[1]; + case 'number': + list(, $num, $unit) = $value; + // [1] - the number + // [2] - the unit + if ($this->numberPrecision !== null) { + $num = round($num, $this->numberPrecision); + } + return $num . $unit; + case 'string': + // [1] - contents of string (includes quotes) + list(, $delim, $content) = $value; + foreach ($content as &$part) { + if (is_array($part)) { + $part = $this->compileValue($part); + } + } + return $delim . implode($content) . $delim; + case 'color': + // [1] - red component (either number or a %) + // [2] - green component + // [3] - blue component + // [4] - optional alpha component + list(, $r, $g, $b) = $value; + $r = round($r); + $g = round($g); + $b = round($b); + + if (count($value) == 5 && $value[4] != 1) { // rgba + return 'rgba('.$r.','.$g.','.$b.','.$value[4].')'; + } + + $h = sprintf("#%02x%02x%02x", $r, $g, $b); + + if (!empty($this->formatter->compressColors)) { + // Converting hex color to short notation (e.g. #003399 to #039) + if ($h[1] === $h[2] && $h[3] === $h[4] && $h[5] === $h[6]) { + $h = '#' . $h[1] . $h[3] . $h[5]; + } + } + + return $h; + + case 'function': + list(, $name, $args) = $value; + return $name.'('.$this->compileValue($args).')'; + default: // assumed to be unit + $this->throwError("unknown value type: $value[0]"); + } + } + + protected function lib_isnumber($value) { + return $this->toBool($value[0] == "number"); + } + + protected function lib_isstring($value) { + return $this->toBool($value[0] == "string"); + } + + protected function lib_iscolor($value) { + return $this->toBool($this->coerceColor($value)); + } + + protected function lib_iskeyword($value) { + return $this->toBool($value[0] == "keyword"); + } + + protected function lib_ispixel($value) { + return $this->toBool($value[0] == "number" && $value[2] == "px"); + } + + protected function lib_ispercentage($value) { + return $this->toBool($value[0] == "number" && $value[2] == "%"); + } + + protected function lib_isem($value) { + return $this->toBool($value[0] == "number" && $value[2] == "em"); + } + + protected function lib_rgbahex($color) { + $color = $this->coerceColor($color); + if (is_null($color)) + $this->throwError("color expected for rgbahex"); + + return sprintf("#%02x%02x%02x%02x", + isset($color[4]) ? $color[4]*255 : 0, + $color[1],$color[2], $color[3]); + } + + protected function lib_argb($color){ + return $this->lib_rgbahex($color); + } + + // utility func to unquote a string + protected function lib_e($arg) { + switch ($arg[0]) { + case "list": + $items = $arg[2]; + if (isset($items[0])) { + return $this->lib_e($items[0]); + } + return self::$defaultValue; + case "string": + $arg[1] = ""; + return $arg; + case "keyword": + return $arg; + default: + return array("keyword", $this->compileValue($arg)); + } + } + + protected function lib__sprintf($args) { + if ($args[0] != "list") return $args; + $values = $args[2]; + $string = array_shift($values); + $template = $this->compileValue($this->lib_e($string)); + + $i = 0; + if (preg_match_all('/%[dsa]/', $template, $m)) { + foreach ($m[0] as $match) { + $val = isset($values[$i]) ? + $this->reduce($values[$i]) : array('keyword', ''); + + // lessjs compat, renders fully expanded color, not raw color + if ($color = $this->coerceColor($val)) { + $val = $color; + } + + $i++; + $rep = $this->compileValue($this->lib_e($val)); + $template = preg_replace('/'.self::preg_quote($match).'/', + $rep, $template, 1); + } + } + + $d = $string[0] == "string" ? $string[1] : '"'; + return array("string", $d, array($template)); + } + + protected function lib_floor($arg) { + $value = $this->assertNumber($arg); + return array("number", floor($value), $arg[2]); + } + + protected function lib_ceil($arg) { + $value = $this->assertNumber($arg); + return array("number", ceil($value), $arg[2]); + } + + protected function lib_round($arg) { + $value = $this->assertNumber($arg); + return array("number", round($value), $arg[2]); + } + + /** + * Helper function to get arguments for color manipulation functions. + * takes a list that contains a color like thing and a percentage + */ + protected function colorArgs($args) { + if ($args[0] != 'list' || count($args[2]) < 2) { + return array(array('color', 0, 0, 0), 0); + } + list($color, $delta) = $args[2]; + $color = $this->assertColor($color); + $delta = floatval($delta[1]); + + return array($color, $delta); + } + + protected function lib_darken($args) { + list($color, $delta) = $this->colorArgs($args); + + $hsl = $this->toHSL($color); + $hsl[3] = $this->clamp($hsl[3] - $delta, 100); + return $this->toRGB($hsl); + } + + protected function lib_lighten($args) { + list($color, $delta) = $this->colorArgs($args); + + $hsl = $this->toHSL($color); + $hsl[3] = $this->clamp($hsl[3] + $delta, 100); + return $this->toRGB($hsl); + } + + protected function lib_saturate($args) { + list($color, $delta) = $this->colorArgs($args); + + $hsl = $this->toHSL($color); + $hsl[2] = $this->clamp($hsl[2] + $delta, 100); + return $this->toRGB($hsl); + } + + protected function lib_desaturate($args) { + list($color, $delta) = $this->colorArgs($args); + + $hsl = $this->toHSL($color); + $hsl[2] = $this->clamp($hsl[2] - $delta, 100); + return $this->toRGB($hsl); + } + + protected function lib_spin($args) { + list($color, $delta) = $this->colorArgs($args); + + $hsl = $this->toHSL($color); + + $hsl[1] = $hsl[1] + $delta % 360; + if ($hsl[1] < 0) $hsl[1] += 360; + + return $this->toRGB($hsl); + } + + protected function lib_fadeout($args) { + list($color, $delta) = $this->colorArgs($args); + $color[4] = $this->clamp((isset($color[4]) ? $color[4] : 1) - $delta/100); + return $color; + } + + protected function lib_fadein($args) { + list($color, $delta) = $this->colorArgs($args); + $color[4] = $this->clamp((isset($color[4]) ? $color[4] : 1) + $delta/100); + return $color; + } + + protected function lib_hue($color) { + $hsl = $this->toHSL($this->assertColor($color)); + return round($hsl[1]); + } + + protected function lib_saturation($color) { + $hsl = $this->toHSL($this->assertColor($color)); + return round($hsl[2]); + } + + protected function lib_lightness($color) { + $hsl = $this->toHSL($this->assertColor($color)); + return round($hsl[3]); + } + + // get the alpha of a color + // defaults to 1 for non-colors or colors without an alpha + protected function lib_alpha($value) { + if (!is_null($color = $this->coerceColor($value))) { + return isset($color[4]) ? $color[4] : 1; + } + } + + // set the alpha of the color + protected function lib_fade($args) { + list($color, $alpha) = $this->colorArgs($args); + $color[4] = $this->clamp($alpha / 100.0); + return $color; + } + + protected function lib_percentage($arg) { + $num = $this->assertNumber($arg); + return array("number", $num*100, "%"); + } + + // mixes two colors by weight + // mix(@color1, @color2, @weight); + // http://sass-lang.com/docs/yardoc/Sass/Script/Functions.html#mix-instance_method + protected function lib_mix($args) { + if ($args[0] != "list" || count($args[2]) < 3) + $this->throwError("mix expects (color1, color2, weight)"); + + list($first, $second, $weight) = $args[2]; + $first = $this->assertColor($first); + $second = $this->assertColor($second); + + $first_a = $this->lib_alpha($first); + $second_a = $this->lib_alpha($second); + $weight = $weight[1] / 100.0; + + $w = $weight * 2 - 1; + $a = $first_a - $second_a; + + $w1 = (($w * $a == -1 ? $w : ($w + $a)/(1 + $w * $a)) + 1) / 2.0; + $w2 = 1.0 - $w1; + + $new = array('color', + $w1 * $first[1] + $w2 * $second[1], + $w1 * $first[2] + $w2 * $second[2], + $w1 * $first[3] + $w2 * $second[3], + ); + + if ($first_a != 1.0 || $second_a != 1.0) { + $new[] = $first_a * $weight + $second_a * ($weight - 1); + } + + return $this->fixColor($new); + } + + protected function assertColor($value, $error = "expected color value") { + $color = $this->coerceColor($value); + if (is_null($color)) $this->throwError($error); + return $color; + } + + protected function assertNumber($value, $error = "expecting number") { + if ($value[0] == "number") return $value[1]; + $this->throwError($error); + } + + protected function toHSL($color) { + if ($color[0] == 'hsl') return $color; + + $r = $color[1] / 255; + $g = $color[2] / 255; + $b = $color[3] / 255; + + $min = min($r, $g, $b); + $max = max($r, $g, $b); + + $L = ($min + $max) / 2; + if ($min == $max) { + $S = $H = 0; + } else { + if ($L < 0.5) + $S = ($max - $min)/($max + $min); + else + $S = ($max - $min)/(2.0 - $max - $min); + + if ($r == $max) $H = ($g - $b)/($max - $min); + elseif ($g == $max) $H = 2.0 + ($b - $r)/($max - $min); + elseif ($b == $max) $H = 4.0 + ($r - $g)/($max - $min); + + } + + $out = array('hsl', + ($H < 0 ? $H + 6 : $H)*60, + $S*100, + $L*100, + ); + + if (count($color) > 4) $out[] = $color[4]; // copy alpha + return $out; + } + + protected function toRGB_helper($comp, $temp1, $temp2) { + if ($comp < 0) $comp += 1.0; + elseif ($comp > 1) $comp -= 1.0; + + if (6 * $comp < 1) return $temp1 + ($temp2 - $temp1) * 6 * $comp; + if (2 * $comp < 1) return $temp2; + if (3 * $comp < 2) return $temp1 + ($temp2 - $temp1)*((2/3) - $comp) * 6; + + return $temp1; + } + + /** + * Converts a hsl array into a color value in rgb. + * Expects H to be in range of 0 to 360, S and L in 0 to 100 + */ + protected function toRGB($color) { + if ($color == 'color') return $color; + + $H = $color[1] / 360; + $S = $color[2] / 100; + $L = $color[3] / 100; + + if ($S == 0) { + $r = $g = $b = $L; + } else { + $temp2 = $L < 0.5 ? + $L*(1.0 + $S) : + $L + $S - $L * $S; + + $temp1 = 2.0 * $L - $temp2; + + $r = $this->toRGB_helper($H + 1/3, $temp1, $temp2); + $g = $this->toRGB_helper($H, $temp1, $temp2); + $b = $this->toRGB_helper($H - 1/3, $temp1, $temp2); + } + + // $out = array('color', round($r*255), round($g*255), round($b*255)); + $out = array('color', $r*255, $g*255, $b*255); + if (count($color) > 4) $out[] = $color[4]; // copy alpha + return $out; + } + + protected function clamp($v, $max = 1, $min = 0) { + return min($max, max($min, $v)); + } + + /** + * Convert the rgb, rgba, hsl color literals of function type + * as returned by the parser into values of color type. + */ + protected function funcToColor($func) { + $fname = $func[1]; + if ($func[2][0] != 'list') return false; // need a list of arguments + $rawComponents = $func[2][2]; + + if ($fname == 'hsl' || $fname == 'hsla') { + $hsl = array('hsl'); + $i = 0; + foreach ($rawComponents as $c) { + $val = $this->reduce($c); + $val = isset($val[1]) ? floatval($val[1]) : 0; + + if ($i == 0) $clamp = 360; + elseif ($i < 3) $clamp = 100; + else $clamp = 1; + + $hsl[] = $this->clamp($val, $clamp); + $i++; + } + + while (count($hsl) < 4) $hsl[] = 0; + return $this->toRGB($hsl); + + } elseif ($fname == 'rgb' || $fname == 'rgba') { + $components = array(); + $i = 1; + foreach ($rawComponents as $c) { + $c = $this->reduce($c); + if ($i < 4) { + if ($c[0] == "number" && $c[2] == "%") { + $components[] = 255 * ($c[1] / 100); + } else { + $components[] = floatval($c[1]); + } + } elseif ($i == 4) { + if ($c[0] == "number" && $c[2] == "%") { + $components[] = 1.0 * ($c[1] / 100); + } else { + $components[] = floatval($c[1]); + } + } else break; + + $i++; + } + while (count($components) < 3) $components[] = 0; + array_unshift($components, 'color'); + return $this->fixColor($components); + } + + return false; + } + + protected function reduce($value) { + switch ($value[0]) { + case "variable": + $key = $value[1]; + if (is_array($key)) { + $key = $this->reduce($key); + $key = $this->vPrefix . $this->compileValue($this->lib_e($key)); + } + + $seen =& $this->env->seenNames; + + if (!empty($seen[$key])) { + $this->throwError("infinite loop detected: $key"); + } + + $seen[$key] = true; + $out = $this->reduce($this->get($key, self::$defaultValue)); + $seen[$key] = false; + return $out; + case "list": + foreach ($value[2] as &$item) { + $item = $this->reduce($item); + } + return $value; + case "expression": + return $this->evaluate($value); + case "string": + foreach ($value[2] as &$part) { + if (is_array($part)) { + $strip = $part[0] == "variable"; + $part = $this->reduce($part); + if ($strip) $part = $this->lib_e($part); + } + } + return $value; + case "escape": + list(,$inner) = $value; + return $this->lib_e($this->reduce($inner)); + case "function": + $color = $this->funcToColor($value); + if ($color) return $color; + + list(, $name, $args) = $value; + if ($name == "%") $name = "_sprintf"; + $f = isset($this->libFunctions[$name]) ? + $this->libFunctions[$name] : array($this, 'lib_'.$name); + + if (is_callable($f)) { + if ($args[0] == 'list') + $args = self::compressList($args[2], $args[1]); + + $ret = call_user_func($f, $this->reduce($args), $this); + + if (is_null($ret)) { + return array("string", "", array( + $name, "(", $args, ")" + )); + } + + // convert to a typed value if the result is a php primitive + if (is_numeric($ret)) $ret = array('number', $ret, ""); + elseif (!is_array($ret)) $ret = array('keyword', $ret); + + return $ret; + } + + // plain function, reduce args + $value[2] = $this->reduce($value[2]); + return $value; + case "unary": + list(, $op, $exp) = $value; + $exp = $this->reduce($exp); + + if ($exp[0] == "number") { + switch ($op) { + case "+": + return $exp; + case "-": + $exp[1] *= -1; + return $exp; + } + } + return array("string", "", array($op, $exp)); + default: + return $value; + } + } + + + // coerce a value for use in color operation + protected function coerceColor($value) { + switch($value[0]) { + case 'color': return $value; + case 'raw_color': + $c = array("color", 0, 0, 0); + $colorStr = substr($value[1], 1); + $num = hexdec($colorStr); + $width = strlen($colorStr) == 3 ? 16 : 256; + + for ($i = 3; $i > 0; $i--) { // 3 2 1 + $t = $num % $width; + $num /= $width; + + $c[$i] = $t * (256/$width) + $t * floor(16/$width); + } + + return $c; + case 'keyword': + $name = $value[1]; + if (isset(self::$cssColors[$name])) { + list($r, $g, $b) = explode(',', self::$cssColors[$name]); + return array('color', $r, $g, $b); + } + return null; + } + } + + // make something string like into a string + protected function coerceString($value) { + switch ($value[0]) { + case "string": + return $value; + case "keyword": + return array("string", "", array($value[1])); + } + return null; + } + + protected function toBool($a) { + if ($a) return self::$TRUE; + else return self::$FALSE; + } + + // evaluate an expression + protected function evaluate($exp) { + list(, $op, $left, $right, $whiteBefore, $whiteAfter) = $exp; + + $left = $this->reduce($left); + $right = $this->reduce($right); + + if ($leftColor = $this->coerceColor($left)) { + $left = $leftColor; + } + + if ($rightColor = $this->coerceColor($right)) { + $right = $rightColor; + } + + $ltype = $left[0]; + $rtype = $right[0]; + + // operators that work on all types + if ($op == "and") { + return $this->toBool($left == self::$TRUE && $right == self::$TRUE); + } + + if ($op == "=") { + return $this->toBool($this->eq($left, $right) ); + } + + if ($op == "+" && !is_null($str = $this->stringConcatenate($left, $right))) { + return $str; + } + + // type based operators + $fname = "op_${ltype}_${rtype}"; + if (is_callable(array($this, $fname))) { + $out = $this->$fname($op, $left, $right); + if (!is_null($out)) return $out; + } + + // make the expression look it did before being parsed + $paddedOp = $op; + if ($whiteBefore) $paddedOp = " " . $paddedOp; + if ($whiteAfter) $paddedOp .= " "; + + return array("string", "", array($left, $paddedOp, $right)); + } + + protected function stringConcatenate($left, $right) { + if ($strLeft = $this->coerceString($left)) { + if ($right[0] == "string") { + $right[1] = ""; + } + $strLeft[2][] = $right; + return $strLeft; + } + + if ($strRight = $this->coerceString($right)) { + array_unshift($strRight[2], $left); + return $strRight; + } + } + + + // make sure a color's components don't go out of bounds + protected function fixColor($c) { + foreach (range(1, 3) as $i) { + if ($c[$i] < 0) $c[$i] = 0; + if ($c[$i] > 255) $c[$i] = 255; + } + + return $c; + } + + protected function op_number_color($op, $lft, $rgt) { + if ($op == '+' || $op == '*') { + return $this->op_color_number($op, $rgt, $lft); + } + } + + protected function op_color_number($op, $lft, $rgt) { + if ($rgt[0] == '%') $rgt[1] /= 100; + + return $this->op_color_color($op, $lft, + array_fill(1, count($lft) - 1, $rgt[1])); + } + + protected function op_color_color($op, $left, $right) { + $out = array('color'); + $max = count($left) > count($right) ? count($left) : count($right); + foreach (range(1, $max - 1) as $i) { + $lval = isset($left[$i]) ? $left[$i] : 0; + $rval = isset($right[$i]) ? $right[$i] : 0; + switch ($op) { + case '+': + $out[] = $lval + $rval; + break; + case '-': + $out[] = $lval - $rval; + break; + case '*': + $out[] = $lval * $rval; + break; + case '%': + $out[] = $lval % $rval; + break; + case '/': + if ($rval == 0) $this->throwError("evaluate error: can't divide by zero"); + $out[] = $lval / $rval; + break; + default: + $this->throwError('evaluate error: color op number failed on op '.$op); + } + } + return $this->fixColor($out); + } + + // operator on two numbers + protected function op_number_number($op, $left, $right) { + $unit = empty($left[2]) ? $right[2] : $left[2]; + + $value = 0; + switch ($op) { + case '+': + $value = $left[1] + $right[1]; + break; + case '*': + $value = $left[1] * $right[1]; + break; + case '-': + $value = $left[1] - $right[1]; + break; + case '%': + $value = $left[1] % $right[1]; + break; + case '/': + if ($right[1] == 0) $this->throwError('parse error: divide by zero'); + $value = $left[1] / $right[1]; + break; + case '<': + return $this->toBool($left[1] < $right[1]); + case '>': + return $this->toBool($left[1] > $right[1]); + case '>=': + return $this->toBool($left[1] >= $right[1]); + case '=<': + return $this->toBool($left[1] <= $right[1]); + default: + $this->throwError('parse error: unknown number operator: '.$op); + } + + return array("number", $value, $unit); + } + + + /* environment functions */ + + protected function makeOutputBlock($type, $selectors = null) { + $b = new stdclass; + $b->lines = array(); + $b->children = array(); + $b->selectors = $selectors; + $b->type = $type; + $b->parent = $this->scope; + return $b; + } + + // the state of execution + protected function pushEnv($block = null) { + $e = new stdclass; + $e->parent = $this->env; + $e->store = array(); + $e->block = $block; + + $this->env = $e; + return $e; + } + + // pop something off the stack + protected function popEnv() { + $old = $this->env; + $this->env = $this->env->parent; + return $old; + } + + // set something in the current env + protected function set($name, $value) { + $this->env->store[$name] = $value; + } + + + // get the highest occurrence entry for a name + protected function get($name, $default=null) { + $current = $this->env; + + $isArguments = $name == $this->vPrefix . 'arguments'; + while ($current) { + if ($isArguments && isset($current->arguments)) { + return array('list', ' ', $current->arguments); + } + + if (isset($current->store[$name])) + return $current->store[$name]; + else { + $current = isset($current->storeParent) ? + $current->storeParent : $current->parent; + } + } + + return $default; + } + + // inject array of unparsed strings into environment as variables + protected function injectVariables($args) { + $this->pushEnv(); + $parser = new lessc_parser($this, __METHOD__); + foreach ($args as $name => $strValue) { + if ($name{0} != '@') $name = '@'.$name; + $parser->count = 0; + $parser->buffer = (string)$strValue; + if (!$parser->propertyValue($value)) { + throw new Exception("failed to parse passed in variable $name: $strValue"); + } + + $this->set($name, $value); + } + } + + /** + * Initialize any static state, can initialize parser for a file + * $opts isn't used yet + */ + public function __construct($fname = null) { + if ($fname !== null) { + // used for deprecated parse method + $this->_parseFile = $fname; + } + } + + public function compile($string, $name = null) { + $locale = setlocale(LC_NUMERIC, 0); + setlocale(LC_NUMERIC, "C"); + + $this->parser = $this->makeParser($name); + $root = $this->parser->parse($string); + + $this->env = null; + $this->scope = null; + + $this->formatter = $this->newFormatter(); + + if (!empty($this->registeredVars)) { + $this->injectVariables($this->registeredVars); + } + + $this->compileBlock($root); + + ob_start(); + $this->formatter->block($this->scope); + $out = ob_get_clean(); + setlocale(LC_NUMERIC, $locale); + return $out; + } + + public function compileFile($fname, $outFname = null) { + if (!is_readable($fname)) { + throw new Exception('load error: failed to find '.$fname); + } + + $pi = pathinfo($fname); + + $oldImport = $this->importDir; + + $this->importDir = (array)$this->importDir; + $this->importDir[] = $pi['dirname'].'/'; + + $this->allParsedFiles = array(); + $this->addParsedFile($fname); + + $out = $this->compile(file_get_contents($fname), $fname); + + $this->importDir = $oldImport; + + if ($outFname !== null) { + return file_put_contents($outFname, $out); + } + + return $out; + } + + // compile only if changed input has changed or output doesn't exist + public function checkedCompile($in, $out) { + if (!is_file($out) || filemtime($in) > filemtime($out)) { + $this->compileFile($in, $out); + return true; + } + return false; + } + + /** + * Execute lessphp on a .less file or a lessphp cache structure + * + * The lessphp cache structure contains information about a specific + * less file having been parsed. It can be used as a hint for future + * calls to determine whether or not a rebuild is required. + * + * The cache structure contains two important keys that may be used + * externally: + * + * compiled: The final compiled CSS + * updated: The time (in seconds) the CSS was last compiled + * + * The cache structure is a plain-ol' PHP associative array and can + * be serialized and unserialized without a hitch. + * + * @param mixed $in Input + * @param bool $force Force rebuild? + * @return array lessphp cache structure + */ + public function cachedCompile($in, $force = false) { + // assume no root + $root = null; + + if (is_string($in)) { + $root = $in; + } elseif (is_array($in) and isset($in['root'])) { + if ($force or ! isset($in['files'])) { + // If we are forcing a recompile or if for some reason the + // structure does not contain any file information we should + // specify the root to trigger a rebuild. + $root = $in['root']; + } elseif (isset($in['files']) and is_array($in['files'])) { + foreach ($in['files'] as $fname => $ftime ) { + if (!file_exists($fname) or filemtime($fname) > $ftime) { + // One of the files we knew about previously has changed + // so we should look at our incoming root again. + $root = $in['root']; + break; + } + } + } + } else { + // TODO: Throw an exception? We got neither a string nor something + // that looks like a compatible lessphp cache structure. + return null; + } + + if ($root !== null) { + // If we have a root value which means we should rebuild. + $out = array(); + $out['root'] = $root; + $out['compiled'] = $this->compileFile($root); + $out['files'] = $this->allParsedFiles(); + $out['updated'] = time(); + return $out; + } else { + // No changes, pass back the structure + // we were given initially. + return $in; + } + + } + + // parse and compile buffer + // This is deprecated + public function parse($str = null, $initialVariables = null) { + if (is_array($str)) { + $initialVariables = $str; + $str = null; + } + + $oldVars = $this->registeredVars; + if ($initialVariables !== null) { + $this->setVariables($initialVariables); + } + + if ($str == null) { + if (empty($this->_parseFile)) { + throw new exception("nothing to parse"); + } + + $out = $this->compileFile($this->_parseFile); + } else { + $out = $this->compile($str); + } + + $this->registeredVars = $oldVars; + return $out; + } + + protected function makeParser($name) { + $parser = new lessc_parser($this, $name); + $parser->writeComments = $this->preserveComments; + + return $parser; + } + + public function setFormatter($name) { + $this->formatterName = $name; + } + + protected function newFormatter() { + $className = "lessc_formatter_lessjs"; + if (!empty($this->formatterName)) { + if (!is_string($this->formatterName)) + return $this->formatterName; + $className = "lessc_formatter_$this->formatterName"; + } + + return new $className; + } + + public function setPreserveComments($preserve) { + $this->preserveComments = $preserve; + } + + public function registerFunction($name, $func) { + $this->libFunctions[$name] = $func; + } + + public function unregisterFunction($name) { + unset($this->libFunctions[$name]); + } + + public function setVariables($variables) { + $this->registeredVars = array_merge($this->registeredVars, $variables); + } + + public function unsetVariable($name) { + unset($this->registeredVars[name]); + } + + public function allParsedFiles() { + return $this->allParsedFiles; + } + + protected function addParsedFile($file) { + $this->allParsedFiles[realpath($file)] = filemtime($file); + } + + /** + * Uses the current value of $this->count to show line and line number + */ + protected function throwError($msg = null) { + if ($this->sourceLoc >= 0) { + $this->sourceParser->throwError($msg, $this->sourceLoc); + } + throw new exception($msg); + } + + // compile file $in to file $out if $in is newer than $out + // returns true when it compiles, false otherwise + public static function ccompile($in, $out, $less = null) { + if ($less === null) { + $less = new self; + } + return $less->checkedCompile($in, $out); + } + + public static function cexecute($in, $force = false, $less = null) { + if ($less === null) { + $less = new self; + } + return $less->cachedCompile($in, $force); + } + + static protected $cssColors = array( + 'aliceblue' => '240,248,255', + 'antiquewhite' => '250,235,215', + 'aqua' => '0,255,255', + 'aquamarine' => '127,255,212', + 'azure' => '240,255,255', + 'beige' => '245,245,220', + 'bisque' => '255,228,196', + 'black' => '0,0,0', + 'blanchedalmond' => '255,235,205', + 'blue' => '0,0,255', + 'blueviolet' => '138,43,226', + 'brown' => '165,42,42', + 'burlywood' => '222,184,135', + 'cadetblue' => '95,158,160', + 'chartreuse' => '127,255,0', + 'chocolate' => '210,105,30', + 'coral' => '255,127,80', + 'cornflowerblue' => '100,149,237', + 'cornsilk' => '255,248,220', + 'crimson' => '220,20,60', + 'cyan' => '0,255,255', + 'darkblue' => '0,0,139', + 'darkcyan' => '0,139,139', + 'darkgoldenrod' => '184,134,11', + 'darkgray' => '169,169,169', + 'darkgreen' => '0,100,0', + 'darkgrey' => '169,169,169', + 'darkkhaki' => '189,183,107', + 'darkmagenta' => '139,0,139', + 'darkolivegreen' => '85,107,47', + 'darkorange' => '255,140,0', + 'darkorchid' => '153,50,204', + 'darkred' => '139,0,0', + 'darksalmon' => '233,150,122', + 'darkseagreen' => '143,188,143', + 'darkslateblue' => '72,61,139', + 'darkslategray' => '47,79,79', + 'darkslategrey' => '47,79,79', + 'darkturquoise' => '0,206,209', + 'darkviolet' => '148,0,211', + 'deeppink' => '255,20,147', + 'deepskyblue' => '0,191,255', + 'dimgray' => '105,105,105', + 'dimgrey' => '105,105,105', + 'dodgerblue' => '30,144,255', + 'firebrick' => '178,34,34', + 'floralwhite' => '255,250,240', + 'forestgreen' => '34,139,34', + 'fuchsia' => '255,0,255', + 'gainsboro' => '220,220,220', + 'ghostwhite' => '248,248,255', + 'gold' => '255,215,0', + 'goldenrod' => '218,165,32', + 'gray' => '128,128,128', + 'green' => '0,128,0', + 'greenyellow' => '173,255,47', + 'grey' => '128,128,128', + 'honeydew' => '240,255,240', + 'hotpink' => '255,105,180', + 'indianred' => '205,92,92', + 'indigo' => '75,0,130', + 'ivory' => '255,255,240', + 'khaki' => '240,230,140', + 'lavender' => '230,230,250', + 'lavenderblush' => '255,240,245', + 'lawngreen' => '124,252,0', + 'lemonchiffon' => '255,250,205', + 'lightblue' => '173,216,230', + 'lightcoral' => '240,128,128', + 'lightcyan' => '224,255,255', + 'lightgoldenrodyellow' => '250,250,210', + 'lightgray' => '211,211,211', + 'lightgreen' => '144,238,144', + 'lightgrey' => '211,211,211', + 'lightpink' => '255,182,193', + 'lightsalmon' => '255,160,122', + 'lightseagreen' => '32,178,170', + 'lightskyblue' => '135,206,250', + 'lightslategray' => '119,136,153', + 'lightslategrey' => '119,136,153', + 'lightsteelblue' => '176,196,222', + 'lightyellow' => '255,255,224', + 'lime' => '0,255,0', + 'limegreen' => '50,205,50', + 'linen' => '250,240,230', + 'magenta' => '255,0,255', + 'maroon' => '128,0,0', + 'mediumaquamarine' => '102,205,170', + 'mediumblue' => '0,0,205', + 'mediumorchid' => '186,85,211', + 'mediumpurple' => '147,112,219', + 'mediumseagreen' => '60,179,113', + 'mediumslateblue' => '123,104,238', + 'mediumspringgreen' => '0,250,154', + 'mediumturquoise' => '72,209,204', + 'mediumvioletred' => '199,21,133', + 'midnightblue' => '25,25,112', + 'mintcream' => '245,255,250', + 'mistyrose' => '255,228,225', + 'moccasin' => '255,228,181', + 'navajowhite' => '255,222,173', + 'navy' => '0,0,128', + 'oldlace' => '253,245,230', + 'olive' => '128,128,0', + 'olivedrab' => '107,142,35', + 'orange' => '255,165,0', + 'orangered' => '255,69,0', + 'orchid' => '218,112,214', + 'palegoldenrod' => '238,232,170', + 'palegreen' => '152,251,152', + 'paleturquoise' => '175,238,238', + 'palevioletred' => '219,112,147', + 'papayawhip' => '255,239,213', + 'peachpuff' => '255,218,185', + 'peru' => '205,133,63', + 'pink' => '255,192,203', + 'plum' => '221,160,221', + 'powderblue' => '176,224,230', + 'purple' => '128,0,128', + 'red' => '255,0,0', + 'rosybrown' => '188,143,143', + 'royalblue' => '65,105,225', + 'saddlebrown' => '139,69,19', + 'salmon' => '250,128,114', + 'sandybrown' => '244,164,96', + 'seagreen' => '46,139,87', + 'seashell' => '255,245,238', + 'sienna' => '160,82,45', + 'silver' => '192,192,192', + 'skyblue' => '135,206,235', + 'slateblue' => '106,90,205', + 'slategray' => '112,128,144', + 'slategrey' => '112,128,144', + 'snow' => '255,250,250', + 'springgreen' => '0,255,127', + 'steelblue' => '70,130,180', + 'tan' => '210,180,140', + 'teal' => '0,128,128', + 'thistle' => '216,191,216', + 'tomato' => '255,99,71', + 'turquoise' => '64,224,208', + 'violet' => '238,130,238', + 'wheat' => '245,222,179', + 'white' => '255,255,255', + 'whitesmoke' => '245,245,245', + 'yellow' => '255,255,0', + 'yellowgreen' => '154,205,50' + ); +} + +// responsible for taking a string of LESS code and converting it into a +// syntax tree +class lessc_parser { + static protected $nextBlockId = 0; // used to uniquely identify blocks + + static protected $precedence = array( + '=<' => 0, + '>=' => 0, + '=' => 0, + '<' => 0, + '>' => 0, + + '+' => 1, + '-' => 1, + '*' => 2, + '/' => 2, + '%' => 2, + ); + + static protected $whitePattern; + static protected $commentMulti; + + static protected $commentSingle = "//"; + static protected $commentMultiLeft = "/*"; + static protected $commentMultiRight = "*/"; + + // regex string to match any of the operators + static protected $operatorString; + + // these properties will supress division unless it's inside parenthases + static protected $supressDivisionProps = + array('/border-radius$/i', '/^font$/i'); + + protected $blockDirectives = array("font-face", "keyframes", "page", "-moz-document"); + protected $lineDirectives = array("charset"); + + /** + * if we are in parens we can be more liberal with whitespace around + * operators because it must evaluate to a single value and thus is less + * ambiguous. + * + * Consider: + * property1: 10 -5; // is two numbers, 10 and -5 + * property2: (10 -5); // should evaluate to 5 + */ + protected $inParens = false; + + // caches preg escaped literals + static protected $literalCache = array(); + + public function __construct($lessc, $sourceName = null) { + $this->eatWhiteDefault = true; + // reference to less needed for vPrefix, mPrefix, and parentSelector + $this->lessc = $lessc; + + $this->sourceName = $sourceName; // name used for error messages + + $this->writeComments = false; + + if (!self::$operatorString) { + self::$operatorString = + '('.implode('|', array_map(array('lessc', 'preg_quote'), + array_keys(self::$precedence))).')'; + + $commentSingle = lessc::preg_quote(self::$commentSingle); + $commentMultiLeft = lessc::preg_quote(self::$commentMultiLeft); + $commentMultiRight = lessc::preg_quote(self::$commentMultiRight); + + self::$commentMulti = $commentMultiLeft.'.*?'.$commentMultiRight; + self::$whitePattern = '/'.$commentSingle.'[^\n]*\s*|('.self::$commentMulti.')\s*|\s+/Ais'; + } + } + + public function parse($buffer) { + $this->count = 0; + $this->line = 1; + + $this->env = null; // block stack + $this->buffer = $this->writeComments ? $buffer : $this->removeComments($buffer); + $this->pushSpecialBlock("root"); + $this->eatWhiteDefault = true; + $this->seenComments = array(); + + // trim whitespace on head + // if (preg_match('/^\s+/', $this->buffer, $m)) { + // $this->line += substr_count($m[0], "\n"); + // $this->buffer = ltrim($this->buffer); + // } + $this->whitespace(); + + // parse the entire file + $lastCount = $this->count; + while (false !== $this->parseChunk()); + + if ($this->count != strlen($this->buffer)) + $this->throwError(); + + // TODO report where the block was opened + if (!is_null($this->env->parent)) + throw new exception('parse error: unclosed block'); + + return $this->env; + } + + /** + * Parse a single chunk off the head of the buffer and append it to the + * current parse environment. + * Returns false when the buffer is empty, or when there is an error. + * + * This function is called repeatedly until the entire document is + * parsed. + * + * This parser is most similar to a recursive descent parser. Single + * functions represent discrete grammatical rules for the language, and + * they are able to capture the text that represents those rules. + * + * Consider the function lessc::keyword(). (all parse functions are + * structured the same) + * + * The function takes a single reference argument. When calling the + * function it will attempt to match a keyword on the head of the buffer. + * If it is successful, it will place the keyword in the referenced + * argument, advance the position in the buffer, and return true. If it + * fails then it won't advance the buffer and it will return false. + * + * All of these parse functions are powered by lessc::match(), which behaves + * the same way, but takes a literal regular expression. Sometimes it is + * more convenient to use match instead of creating a new function. + * + * Because of the format of the functions, to parse an entire string of + * grammatical rules, you can chain them together using &&. + * + * But, if some of the rules in the chain succeed before one fails, then + * the buffer position will be left at an invalid state. In order to + * avoid this, lessc::seek() is used to remember and set buffer positions. + * + * Before parsing a chain, use $s = $this->seek() to remember the current + * position into $s. Then if a chain fails, use $this->seek($s) to + * go back where we started. + */ + protected function parseChunk() { + if (empty($this->buffer)) return false; + $s = $this->seek(); + + // setting a property + if ($this->keyword($key) && $this->assign() && + $this->propertyValue($value, $key) && $this->end()) + { + $this->append(array('assign', $key, $value), $s); + return true; + } else { + $this->seek($s); + } + + + // look for special css blocks + if ($this->literal('@', false)) { + $this->count--; + + // media + if ($this->literal('@media')) { + if (($this->mediaQueryList($mediaQueries) || true) + && $this->literal('{')) + { + $media = $this->pushSpecialBlock("media"); + $media->queries = is_null($mediaQueries) ? array() : $mediaQueries; + return true; + } else { + $this->seek($s); + return false; + } + } + + if ($this->literal("@", false) && $this->keyword($dirName)) { + if ($this->isDirective($dirName, $this->blockDirectives)) { + if (($this->openString("{", $dirValue, null, array(";")) || true) && + $this->literal("{")) + { + $dir = $this->pushSpecialBlock("directive"); + $dir->name = $dirName; + if (isset($dirValue)) $dir->value = $dirValue; + return true; + } + } elseif ($this->isDirective($dirName, $this->lineDirectives)) { + if ($this->propertyValue($dirValue) && $this->end()) { + $this->append(array("directive", $dirName, $dirValue)); + return true; + } + } + } + + $this->seek($s); + } + + // setting a variable + if ($this->variable($var) && $this->assign() && + $this->propertyValue($value) && $this->end()) + { + $this->append(array('assign', $var, $value), $s); + return true; + } else { + $this->seek($s); + } + + if ($this->import($url, $media)) { + $this->append(array('import', $url, $media), $s); + return true; + + // don't check .css files + if (empty($media) && substr_compare($url, '.css', -4, 4) !== 0) { + if ($this->importDisabled) { + $this->append(array('raw', '/* import disabled */')); + } else { + $path = $this->findImport($url); + if (!is_null($path)) { + $this->append(array('import', $path), $s); + return true; + } + } + } + + $this->append(array('raw', '@import url("'.$url.'")'. + ($media ? ' '.$media : '').';'), $s); + return true; + } + + // opening parametric mixin + if ($this->tag($tag, true) && $this->argumentDef($args, $isVararg) && + ($this->guards($guards) || true) && + $this->literal('{')) + { + $block = $this->pushBlock($this->fixTags(array($tag))); + $block->args = $args; + $block->isVararg = $isVararg; + if (!empty($guards)) $block->guards = $guards; + return true; + } else { + $this->seek($s); + } + + // opening a simple block + if ($this->tags($tags) && $this->literal('{')) { + $tags = $this->fixTags($tags); + $this->pushBlock($tags); + return true; + } else { + $this->seek($s); + } + + // closing a block + if ($this->literal('}')) { + try { + $block = $this->pop(); + } catch (exception $e) { + $this->seek($s); + $this->throwError($e->getMessage()); + } + + $hidden = false; + if (is_null($block->type)) { + $hidden = true; + if (!isset($block->args)) { + foreach ($block->tags as $tag) { + if (!is_string($tag) || $tag{0} != $this->lessc->mPrefix) { + $hidden = false; + break; + } + } + } + + foreach ($block->tags as $tag) { + if (is_string($tag)) { + $this->env->children[$tag][] = $block; + } + } + } + + if (!$hidden) { + $this->append(array('block', $block), $s); + } + return true; + } + + // mixin + if ($this->mixinTags($tags) && + ($this->argumentValues($argv) || true) && + ($this->keyword($suffix) || true) && $this->end()) + { + $tags = $this->fixTags($tags); + $this->append(array('mixin', $tags, $argv, $suffix), $s); + return true; + } else { + $this->seek($s); + } + + // spare ; + if ($this->literal(';')) return true; + + return false; // got nothing, throw error + } + + protected function isDirective($dirname, $directives) { + // TODO: cache pattern in parser + $pattern = implode("|", + array_map(array("lessc", "preg_quote"), $directives)); + $pattern = '/^(-[a-z-]+-)?(' . $pattern . ')$/i'; + + return preg_match($pattern, $dirname); + } + + protected function fixTags($tags) { + // move @ tags out of variable namespace + foreach ($tags as &$tag) { + if ($tag{0} == $this->lessc->vPrefix) + $tag[0] = $this->lessc->mPrefix; + } + return $tags; + } + + // a list of expressions + protected function expressionList(&$exps) { + $values = array(); + + while ($this->expression($exp)) { + $values[] = $exp; + } + + if (count($values) == 0) return false; + + $exps = lessc::compressList($values, ' '); + return true; + } + + /** + * Attempt to consume an expression. + * @link http://en.wikipedia.org/wiki/Operator-precedence_parser#Pseudo-code + */ + protected function expression(&$out) { + if ($this->value($lhs)) { + $out = $this->expHelper($lhs, 0); + + // look for / shorthand + if (!empty($this->env->supressedDivision)) { + unset($this->env->supressedDivision); + $s = $this->seek(); + if ($this->literal("/") && $this->value($rhs)) { + $out = array("list", "", + array($out, array("keyword", "/"), $rhs)); + } else { + $this->seek($s); + } + } + + return true; + } + return false; + } + + /** + * recursively parse infix equation with $lhs at precedence $minP + */ + protected function expHelper($lhs, $minP) { + $this->inExp = true; + $ss = $this->seek(); + + while (true) { + $whiteBefore = isset($this->buffer[$this->count - 1]) && + ctype_space($this->buffer[$this->count - 1]); + + // If there is whitespace before the operator, then we require + // whitespace after the operator for it to be an expression + $needWhite = $whiteBefore && !$this->inParens; + + if ($this->match(self::$operatorString.($needWhite ? '\s' : ''), $m) && self::$precedence[$m[1]] >= $minP) { + if (!$this->inParens && isset($this->env->currentProperty) && $m[1] == "/" && empty($this->env->supressedDivision)) { + foreach (self::$supressDivisionProps as $pattern) { + if (preg_match($pattern, $this->env->currentProperty)) { + $this->env->supressedDivision = true; + break 2; + } + } + } + + + $whiteAfter = isset($this->buffer[$this->count - 1]) && + ctype_space($this->buffer[$this->count - 1]); + + if (!$this->value($rhs)) break; + + // peek for next operator to see what to do with rhs + if ($this->peek(self::$operatorString, $next) && self::$precedence[$next[1]] > self::$precedence[$m[1]]) { + $rhs = $this->expHelper($rhs, self::$precedence[$next[1]]); + } + + $lhs = array('expression', $m[1], $lhs, $rhs, $whiteBefore, $whiteAfter); + $ss = $this->seek(); + + continue; + } + + break; + } + + $this->seek($ss); + + return $lhs; + } + + // consume a list of values for a property + public function propertyValue(&$value, $keyName = null) { + $values = array(); + + if ($keyName !== null) $this->env->currentProperty = $keyName; + + $s = null; + while ($this->expressionList($v)) { + $values[] = $v; + $s = $this->seek(); + if (!$this->literal(',')) break; + } + + if ($s) $this->seek($s); + + if ($keyName !== null) unset($this->env->currentProperty); + + if (count($values) == 0) return false; + + $value = lessc::compressList($values, ', '); + return true; + } + + protected function parenValue(&$out) { + $s = $this->seek(); + + // speed shortcut + if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] != "(") { + return false; + } + + $inParens = $this->inParens; + if ($this->literal("(") && + ($this->inParens = true) && $this->expression($exp) && + $this->literal(")")) + { + $out = $exp; + $this->inParens = $inParens; + return true; + } else { + $this->inParens = $inParens; + $this->seek($s); + } + + return false; + } + + // a single value + protected function value(&$value) { + $s = $this->seek(); + + // speed shortcut + if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] == "-") { + // negation + if ($this->literal("-", false) && + (($this->variable($inner) && $inner = array("variable", $inner)) || + $this->unit($inner) || + $this->parenValue($inner))) + { + $value = array("unary", "-", $inner); + return true; + } else { + $this->seek($s); + } + } + + if ($this->parenValue($value)) return true; + if ($this->unit($value)) return true; + if ($this->color($value)) return true; + if ($this->func($value)) return true; + if ($this->string($value)) return true; + + if ($this->keyword($word)) { + $value = array('keyword', $word); + return true; + } + + // try a variable + if ($this->variable($var)) { + $value = array('variable', $var); + return true; + } + + // unquote string (should this work on any type? + if ($this->literal("~") && $this->string($str)) { + $value = array("escape", $str); + return true; + } else { + $this->seek($s); + } + + // css hack: \0 + if ($this->literal('\\') && $this->match('([0-9]+)', $m)) { + $value = array('keyword', '\\'.$m[1]); + return true; + } else { + $this->seek($s); + } + + return false; + } + + // an import statement + protected function import(&$url, &$media) { + $s = $this->seek(); + if (!$this->literal('@import')) return false; + + // @import "something.css" media; + // @import url("something.css") media; + // @import url(something.css) media; + + if ($this->literal('url(')) $parens = true; else $parens = false; + + if (!$this->string($url)) { + if ($parens && $this->to(')', $url)) { + $parens = false; // got em + } else { + $this->seek($s); + return false; + } + } + + if ($parens && !$this->literal(')')) { + $this->seek($s); + return false; + } + + // now the rest is media + return $this->to(';', $media, false, true); + } + + protected function mediaQueryList(&$out) { + if ($this->genericList($list, "mediaQuery", ",", false)) { + $out = $list[2]; + return true; + } + return false; + } + + protected function mediaQuery(&$out) { + $s = $this->seek(); + + $expressions = null; + $parts = array(); + + if (($this->literal("only") && ($only = true) || $this->literal("not") && ($not = true) || true) && $this->keyword($mediaType)) { + $prop = array("mediaType"); + if (isset($only)) $prop[] = "only"; + if (isset($not)) $prop[] = "not"; + $prop[] = $mediaType; + $parts[] = $prop; + } else { + $this->seek($s); + } + + + if (!empty($mediaType) && !$this->literal("and")) { + // ~ + } else { + $this->genericList($expressions, "mediaExpression", "and", false); + if (is_array($expressions)) $parts = array_merge($parts, $expressions[2]); + } + + if (count($parts) == 0) { + $this->seek($s); + return false; + } + + $out = $parts; + return true; + } + + protected function mediaExpression(&$out) { + $s = $this->seek(); + $value = null; + if ($this->literal("(") && + $this->keyword($feature) && + ($this->literal(":") && $this->expression($value) || true) && + $this->literal(")")) + { + $out = array("mediaExp", $feature); + if ($value) $out[] = $value; + return true; + } + + $this->seek($s); + return false; + } + + // an unbounded string stopped by $end + protected function openString($end, &$out, $nestingOpen=null, $rejectStrs = null) { + $oldWhite = $this->eatWhiteDefault; + $this->eatWhiteDefault = false; + + $stop = array("'", '"', "@{", $end); + $stop = array_map(array("lessc", "preg_quote"), $stop); + // $stop[] = self::$commentMulti; + + if (!is_null($rejectStrs)) { + $stop = array_merge($stop, $rejectStrs); + } + + $patt = '(.*?)('.implode("|", $stop).')'; + + $nestingLevel = 0; + + $content = array(); + while ($this->match($patt, $m, false)) { + if (!empty($m[1])) { + $content[] = $m[1]; + if ($nestingOpen) { + $nestingLevel += substr_count($m[1], $nestingOpen); + } + } + + $tok = $m[2]; + + $this->count-= strlen($tok); + if ($tok == $end) { + if ($nestingLevel == 0) { + break; + } else { + $nestingLevel--; + } + } + + if (($tok == "'" || $tok == '"') && $this->string($str)) { + $content[] = $str; + continue; + } + + if ($tok == "@{" && $this->interpolation($inter)) { + $content[] = $inter; + continue; + } + + if (in_array($tok, $rejectStrs)) { + $count = null; + break; + } + + + $content[] = $tok; + $this->count+= strlen($tok); + } + + $this->eatWhiteDefault = $oldWhite; + + if (count($content) == 0) return false; + + // trim the end + if (is_string(end($content))) { + $content[count($content) - 1] = rtrim(end($content)); + } + + $out = array("string", "", $content); + return true; + } + + protected function string(&$out) { + $s = $this->seek(); + if ($this->literal('"', false)) { + $delim = '"'; + } elseif ($this->literal("'", false)) { + $delim = "'"; + } else { + return false; + } + + $content = array(); + + // look for either ending delim , escape, or string interpolation + $patt = '([^\n]*?)(@\{|\\\\|' . + lessc::preg_quote($delim).')'; + + $oldWhite = $this->eatWhiteDefault; + $this->eatWhiteDefault = false; + + while ($this->match($patt, $m, false)) { + $content[] = $m[1]; + if ($m[2] == "@{") { + $this->count -= strlen($m[2]); + if ($this->interpolation($inter, false)) { + $content[] = $inter; + } else { + $this->count += strlen($m[2]); + $content[] = "@{"; // ignore it + } + } elseif ($m[2] == '\\') { + $content[] = $m[2]; + if ($this->literal($delim, false)) { + $content[] = $delim; + } + } else { + $this->count -= strlen($delim); + break; // delim + } + } + + $this->eatWhiteDefault = $oldWhite; + + if ($this->literal($delim)) { + $out = array("string", $delim, $content); + return true; + } + + $this->seek($s); + return false; + } + + protected function interpolation(&$out) { + $oldWhite = $this->eatWhiteDefault; + $this->eatWhiteDefault = true; + + $s = $this->seek(); + if ($this->literal("@{") && + $this->keyword($var) && + $this->literal("}", false)) + { + $out = array("variable", $this->lessc->vPrefix . $var); + $this->eatWhiteDefault = $oldWhite; + if ($this->eatWhiteDefault) $this->whitespace(); + return true; + } + + $this->eatWhiteDefault = $oldWhite; + $this->seek($s); + return false; + } + + protected function unit(&$unit) { + // speed shortcut + if (isset($this->buffer[$this->count])) { + $char = $this->buffer[$this->count]; + if (!ctype_digit($char) && $char != ".") return false; + } + + if ($this->match('([0-9]+(?:\.[0-9]*)?|\.[0-9]+)([%a-zA-Z]+)?', $m)) { + $unit = array("number", $m[1], empty($m[2]) ? "" : $m[2]); + return true; + } + return false; + } + + // a # color + protected function color(&$out) { + if ($this->match('(#(?:[0-9a-f]{8}|[0-9a-f]{6}|[0-9a-f]{3}))', $m)) { + if (strlen($m[1]) > 7) { + $out = array("string", "", array($m[1])); + } else { + $out = array("raw_color", $m[1]); + } + return true; + } + + return false; + } + + // consume a list of property values delimited by ; and wrapped in () + protected function argumentValues(&$args, $delim = ',') { + $s = $this->seek(); + if (!$this->literal('(')) return false; + + $values = array(); + while (true) { + if ($this->expressionList($value)) $values[] = $value; + if (!$this->literal($delim)) break; + else { + if ($value == null) $values[] = null; + $value = null; + } + } + + if (!$this->literal(')')) { + $this->seek($s); + return false; + } + + $args = $values; + return true; + } + + // consume an argument definition list surrounded by () + // each argument is a variable name with optional value + // or at the end a ... or a variable named followed by ... + protected function argumentDef(&$args, &$isVararg, $delim = ',') { + $s = $this->seek(); + if (!$this->literal('(')) return false; + + $values = array(); + + $isVararg = false; + while (true) { + if ($this->literal("...")) { + $isVararg = true; + break; + } + + if ($this->variable($vname)) { + $arg = array("arg", $vname); + $ss = $this->seek(); + if ($this->assign() && $this->expressionList($value)) { + $arg[] = $value; + } else { + $this->seek($ss); + if ($this->literal("...")) { + $arg[0] = "rest"; + $isVararg = true; + } + } + $values[] = $arg; + if ($isVararg) break; + continue; + } + + if ($this->value($literal)) { + $values[] = array("lit", $literal); + } + + if (!$this->literal($delim)) break; + } + + if (!$this->literal(')')) { + $this->seek($s); + return false; + } + + $args = $values; + + return true; + } + + // consume a list of tags + // this accepts a hanging delimiter + protected function tags(&$tags, $simple = false, $delim = ',') { + $tags = array(); + while ($this->tag($tt, $simple)) { + $tags[] = $tt; + if (!$this->literal($delim)) break; + } + if (count($tags) == 0) return false; + + return true; + } + + // list of tags of specifying mixin path + // optionally separated by > (lazy, accepts extra >) + protected function mixinTags(&$tags) { + $s = $this->seek(); + $tags = array(); + while ($this->tag($tt, true)) { + $tags[] = $tt; + $this->literal(">"); + } + + if (count($tags) == 0) return false; + + return true; + } + + // a bracketed value (contained within in a tag definition) + protected function tagBracket(&$value) { + // speed shortcut + if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] != "[") { + return false; + } + + $s = $this->seek(); + if ($this->literal('[') && $this->to(']', $c, true) && $this->literal(']', false)) { + $value = '['.$c.']'; + // whitespace? + if ($this->whitespace()) $value .= " "; + + // escape parent selector, (yuck) + $value = str_replace($this->lessc->parentSelector, "$&$", $value); + return true; + } + + $this->seek($s); + return false; + } + + protected function tagExpression(&$value) { + $s = $this->seek(); + if ($this->literal("(") && $this->expression($exp) && $this->literal(")")) { + $value = array('exp', $exp); + return true; + } + + $this->seek($s); + return false; + } + + // a single tag + protected function tag(&$tag, $simple = false) { + if ($simple) + $chars = '^,:;{}\][>\(\) "\''; + else + $chars = '^,;{}["\''; + + if (!$simple && $this->tagExpression($tag)) { + return true; + } + + $tag = ''; + while ($this->tagBracket($first)) $tag .= $first; + + while (true) { + if ($this->match('(['.$chars.'0-9]['.$chars.']*)', $m)) { + $tag .= $m[1]; + if ($simple) break; + + while ($this->tagBracket($brack)) $tag .= $brack; + continue; + } elseif ($this->unit($unit)) { // for keyframes + $tag .= $unit[1] . $unit[2]; + continue; + } + break; + } + + + $tag = trim($tag); + if ($tag == '') return false; + + return true; + } + + // a css function + protected function func(&$func) { + $s = $this->seek(); + + if ($this->match('(%|[\w\-_][\w\-_:\.]+|[\w_])', $m) && $this->literal('(')) { + $fname = $m[1]; + + $sPreArgs = $this->seek(); + + $args = array(); + while (true) { + $ss = $this->seek(); + // this ugly nonsense is for ie filter properties + if ($this->keyword($name) && $this->literal('=') && $this->expressionList($value)) { + $args[] = array("string", "", array($name, "=", $value)); + } else { + $this->seek($ss); + if ($this->expressionList($value)) { + $args[] = $value; + } + } + + if (!$this->literal(',')) break; + } + $args = array('list', ',', $args); + + if ($this->literal(')')) { + $func = array('function', $fname, $args); + return true; + } elseif ($fname == 'url') { + // couldn't parse and in url? treat as string + $this->seek($sPreArgs); + if ($this->openString(")", $string) && $this->literal(")")) { + $func = array('function', $fname, $string); + return true; + } + } + } + + $this->seek($s); + return false; + } + + // consume a less variable + protected function variable(&$name) { + $s = $this->seek(); + if ($this->literal($this->lessc->vPrefix, false) && + ($this->variable($sub) || $this->keyword($name))) + { + if (!empty($sub)) { + $name = array('variable', $sub); + } else { + $name = $this->lessc->vPrefix.$name; + } + return true; + } + + $name = null; + $this->seek($s); + return false; + } + + /** + * Consume an assignment operator + * Can optionally take a name that will be set to the current property name + */ + protected function assign($name = null) { + if ($name) $this->currentProperty = $name; + return $this->literal(':') || $this->literal('='); + } + + // consume a keyword + protected function keyword(&$word) { + if ($this->match('([\w_\-\*!"][\w\-_"]*)', $m)) { + $word = $m[1]; + return true; + } + return false; + } + + // consume an end of statement delimiter + protected function end() { + if ($this->literal(';')) { + return true; + } elseif ($this->count == strlen($this->buffer) || $this->buffer{$this->count} == '}') { + // if there is end of file or a closing block next then we don't need a ; + return true; + } + return false; + } + + protected function guards(&$guards) { + $s = $this->seek(); + + if (!$this->literal("when")) { + $this->seek($s); + return false; + } + + $guards = array(); + + while ($this->guardGroup($g)) { + $guards[] = $g; + if (!$this->literal(",")) break; + } + + if (count($guards) == 0) { + $guards = null; + $this->seek($s); + return false; + } + + return true; + } + + // a bunch of guards that are and'd together + // TODO rename to guardGroup + protected function guardGroup(&$guardGroup) { + $s = $this->seek(); + $guardGroup = array(); + while ($this->guard($guard)) { + $guardGroup[] = $guard; + if (!$this->literal("and")) break; + } + + if (count($guardGroup) == 0) { + $guardGroup = null; + $this->seek($s); + return false; + } + + return true; + } + + protected function guard(&$guard) { + $s = $this->seek(); + $negate = $this->literal("not"); + + if ($this->literal("(") && $this->expression($exp) && $this->literal(")")) { + $guard = $exp; + if ($negate) $guard = array("negate", $guard); + return true; + } + + $this->seek($s); + return false; + } + + /* raw parsing functions */ + + protected function literal($what, $eatWhitespace = null) { + if ($eatWhitespace === null) $eatWhitespace = $this->eatWhiteDefault; + + // shortcut on single letter + if (!$eatWhitespace && isset($this->buffer[$this->count]) && !isset($what[1])) { + if ($this->buffer[$this->count] == $what) { + $this->count++; + return true; + } + else return false; + } + + if (!isset(self::$literalCache[$what])) { + self::$literalCache[$what] = lessc::preg_quote($what); + } + + return $this->match(self::$literalCache[$what], $m, $eatWhitespace); + } + + protected function genericList(&$out, $parseItem, $delim="", $flatten=true) { + $s = $this->seek(); + $items = array(); + while ($this->$parseItem($value)) { + $items[] = $value; + if ($delim) { + if (!$this->literal($delim)) break; + } + } + + if (count($items) == 0) { + $this->seek($s); + return false; + } + + if ($flatten && count($items) == 1) { + $out = $items[0]; + } else { + $out = array("list", $delim, $items); + } + + return true; + } + + + // advance counter to next occurrence of $what + // $until - don't include $what in advance + // $allowNewline, if string, will be used as valid char set + protected function to($what, &$out, $until = false, $allowNewline = false) { + if (is_string($allowNewline)) { + $validChars = $allowNewline; + } else { + $validChars = $allowNewline ? "." : "[^\n]"; + } + if (!$this->match('('.$validChars.'*?)'.lessc::preg_quote($what), $m, !$until)) return false; + if ($until) $this->count -= strlen($what); // give back $what + $out = $m[1]; + return true; + } + + // try to match something on head of buffer + protected function match($regex, &$out, $eatWhitespace = null) { + if ($eatWhitespace === null) $eatWhitespace = $this->eatWhiteDefault; + + $r = '/'.$regex.($eatWhitespace && !$this->writeComments ? '\s*' : '').'/Ais'; + if (preg_match($r, $this->buffer, $out, null, $this->count)) { + $this->count += strlen($out[0]); + if ($eatWhitespace && $this->writeComments) $this->whitespace(); + return true; + } + return false; + } + + // match some whitespace + protected function whitespace() { + if ($this->writeComments) { + $gotWhite = false; + while (preg_match(self::$whitePattern, $this->buffer, $m, null, $this->count)) { + if (isset($m[1]) && empty($this->commentsSeen[$this->count])) { + $this->append(array("comment", $m[1])); + $this->commentsSeen[$this->count] = true; + } + $this->count += strlen($m[0]); + $gotWhite = true; + } + return $gotWhite; + } else { + $this->match("", $m); + return strlen($m[0]) > 0; + } + } + + // match something without consuming it + protected function peek($regex, &$out = null, $from=null) { + if (is_null($from)) $from = $this->count; + $r = '/'.$regex.'/Ais'; + $result = preg_match($r, $this->buffer, $out, null, $from); + + return $result; + } + + // seek to a spot in the buffer or return where we are on no argument + protected function seek($where = null) { + if ($where === null) return $this->count; + else $this->count = $where; + return true; + } + + /* misc functions */ + + public function throwError($msg = "parse error", $count = null) { + $count = is_null($count) ? $this->count : $count; + + $line = $this->line + + substr_count(substr($this->buffer, 0, $count), "\n"); + + if (!empty($this->sourceName)) { + $loc = "$this->sourceName on line $line"; + } else { + $loc = "line: $line"; + } + + // TODO this depends on $this->count + if ($this->peek("(.*?)(\n|$)", $m, $count)) { + throw new exception("$msg: failed at `$m[1]` $loc"); + } else { + throw new exception("$msg: $loc"); + } + } + + protected function pushBlock($selectors=null, $type=null) { + $b = new stdclass; + $b->parent = $this->env; + + $b->type = $type; + $b->id = self::$nextBlockId++; + + $b->isVararg = false; // TODO: kill me from here + $b->tags = $selectors; + + $b->props = array(); + $b->children = array(); + + $this->env = $b; + return $b; + } + + // push a block that doesn't multiply tags + protected function pushSpecialBlock($type) { + return $this->pushBlock(null, $type); + } + + // append a property to the current block + protected function append($prop, $pos = null) { + if ($pos !== null) $prop[-1] = $pos; + $this->env->props[] = $prop; + } + + // pop something off the stack + protected function pop() { + $old = $this->env; + $this->env = $this->env->parent; + return $old; + } + + // remove comments from $text + // todo: make it work for all functions, not just url + protected function removeComments($text) { + $look = array( + 'url(', '//', '/*', '"', "'" + ); + + $out = ''; + $min = null; + $done = false; + while (true) { + // find the next item + foreach ($look as $token) { + $pos = strpos($text, $token); + if ($pos !== false) { + if (!isset($min) || $pos < $min[1]) $min = array($token, $pos); + } + } + + if (is_null($min)) break; + + $count = $min[1]; + $skip = 0; + $newlines = 0; + switch ($min[0]) { + case 'url(': + if (preg_match('/url\(.*?\)/', $text, $m, 0, $count)) + $count += strlen($m[0]) - strlen($min[0]); + break; + case '"': + case "'": + if (preg_match('/'.$min[0].'.*?'.$min[0].'/', $text, $m, 0, $count)) + $count += strlen($m[0]) - 1; + break; + case '//': + $skip = strpos($text, "\n", $count); + if ($skip === false) $skip = strlen($text) - $count; + else $skip -= $count; + break; + case '/*': + if (preg_match('/\/\*.*?\*\//s', $text, $m, 0, $count)) { + $skip = strlen($m[0]); + $newlines = substr_count($m[0], "\n"); + } + break; + } + + if ($skip == 0) $count += strlen($min[0]); + + $out .= substr($text, 0, $count).str_repeat("\n", $newlines); + $text = substr($text, $count + $skip); + + $min = null; + } + + return $out.$text; + } + +} + +class lessc_formatter_classic { + public $indentChar = " "; + + public $break = "\n"; + public $open = " {"; + public $close = "}"; + public $selectorSeparator = ", "; + public $assignSeparator = ":"; + + public $openSingle = " { "; + public $closeSingle = " }"; + + public $disableSingle = false; + public $breakSelectors = false; + + public $compressColors = false; + + public function __construct() { + $this->indentLevel = 0; + } + + public function indentStr($n = 0) { + return str_repeat($this->indentChar, max($this->indentLevel + $n, 0)); + } + + public function property($name, $value) { + return $name . $this->assignSeparator . $value . ";"; + } + + protected function isEmpty($block) { + if (empty($block->lines)) { + foreach ($block->children as $child) { + if (!$this->isEmpty($child)) return false; + } + + return true; + } + return false; + + if (empty($block->lines) && empty($block->children)) return true; + } + + public function block($block) { + if ($this->isEmpty($block)) return; + + $inner = $pre = $this->indentStr(); + + $isSingle = !$this->disableSingle && + is_null($block->type) && count($block->lines) == 1; + + if (!empty($block->selectors)) { + $this->indentLevel++; + + if ($this->breakSelectors) { + $selectorSeparator = $this->selectorSeparator . $this->break . $pre; + } else { + $selectorSeparator = $this->selectorSeparator; + } + + echo $pre . + implode($selectorSeparator, $block->selectors); + if ($isSingle) { + echo $this->openSingle; + $inner = ""; + } else { + echo $this->open . $this->break; + $inner = $this->indentStr(); + } + + } + + if (!empty($block->lines)) { + $glue = $this->break.$inner; + echo $inner . implode($glue, $block->lines); + if (!$isSingle && !empty($block->children)) { + echo $this->break; + } + } + + foreach ($block->children as $child) { + $this->block($child); + } + + if (!empty($block->selectors)) { + if (!$isSingle && empty($block->children)) echo $this->break; + + if ($isSingle) { + echo $this->closeSingle . $this->break; + } else { + echo $pre . $this->close . $this->break; + } + + $this->indentLevel--; + } + } +} + +class lessc_formatter_compressed extends lessc_formatter_classic { + public $disableSingle = true; + public $open = "{"; + public $selectorSeparator = ","; + public $assignSeparator = ":"; + public $break = ""; + public $compressColors = true; + + public function indentStr($n = 0) { + return ""; + } +} + +class lessc_formatter_lessjs extends lessc_formatter_classic { + public $disableSingle = true; + public $breakSelectors = true; + public $assignSeparator = ": "; + public $selectorSeparator = ","; +} + diff --git a/sparks/assets/1.5.1/spark.info b/sparks/assets/1.5.1/spark.info new file mode 100644 index 0000000..099766d --- /dev/null +++ b/sparks/assets/1.5.1/spark.info @@ -0,0 +1,7 @@ +name: assets + +version: 1.5.1 + +compatibility: 2.1.0 + +tags: ["assets", "css", "javascript", "lesscss", "cofeescript"] \ No newline at end of file