From 4c7b8a57b19a82d9d9790d0e9cddfc20e0025e8f Mon Sep 17 00:00:00 2001 From: Joe Lencioni Date: Tue, 10 Apr 2012 13:17:38 -0500 Subject: [PATCH 001/421] Fixed regular expression to only strip unnecessary units on zero values for length types --- lib/Core.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Core.php b/lib/Core.php index 1dab671..3cc5cd7 100644 --- a/lib/Core.php +++ b/lib/Core.php @@ -881,7 +881,7 @@ protected static function minify ( $str ) { $replacements = array( '!\n+| (\{)!' => '$1', // Trim whitespace '!(^|[: \(,])0(\.\d+)!' => '$1$2', // Strip leading zeros on floats - '!(^|[: \(,])\.?0[a-zA-Z]{1,5}!i' => '${1}0', // Strip unnecessary units on zero values + '!(^|[: \(,])\.?0(?:e[mx]|c[hm]|rem|v[hwm]|(?:moz)?mm|in|p[tcx])!i' => '${1}0', // Strip unnecessary units on zero values for length types '!(^|\:) *(0 0 0|0 0 0 0) *(;|\})!' => '${1}0${3}', // Collapse zero lists '!(padding|margin) ?\: *0 0 *(;|\})!' => '${1}:0${2}', // Collapse zero lists continued '!\s*([>~+=])\s*!' => '$1', // Clean-up around combinators From 3841bba6045dee7f96cc2990f4842bc0a8521c54 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 11 Apr 2012 09:45:25 +0100 Subject: [PATCH 002/421] Added IO interface for alternative methods of cache management Updated Prepend.css and initial-values.ini Internal refactoring --- CHANGELOG | 5 + CssCrush.php | 4 +- Plugins.ini | 6 +- Prepend.css | 43 ++- cli.php | 26 +- lib/Color.php | 5 +- lib/Core.php | 502 +++++++++++++++--------------------- lib/Function.php | 2 +- lib/IO.php | 255 ++++++++++++++++++ lib/Importer.php | 88 ++++--- lib/Iterator.php | 63 +++++ lib/Rule.php | 282 ++++++++++++++------ lib/Util.php | 30 ++- misc/initial-values.ini | 4 +- plugins/double-colon.php | 2 +- plugins/hocus-pocus.php | 2 +- plugins/hsl-to-hex.php | 8 +- plugins/ie-clip.php | 5 +- plugins/ie-filter.php | 8 +- plugins/ie-inline-block.php | 6 +- plugins/ie-min-height.php | 4 +- plugins/ie-opacity.php | 8 +- plugins/initial.php | 4 +- plugins/rgba-fallback.php | 6 +- plugins/spiffing.php | 24 ++ 25 files changed, 916 insertions(+), 476 deletions(-) create mode 100644 lib/IO.php create mode 100644 lib/Iterator.php create mode 100644 plugins/spiffing.php diff --git a/CHANGELOG b/CHANGELOG index 3b6600d..725f770 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,8 @@ +1.4.3 +----- + + + 1.4.2 ----- Fixed bug with @import statement parsing diff --git a/CssCrush.php b/CssCrush.php index ad15d4c..0c89af5 100644 --- a/CssCrush.php +++ b/CssCrush.php @@ -4,7 +4,7 @@ * CSS Crush * Extensible CSS preprocessor * - * @version 1.4.2 + * @version 1.4.3 * @license http://www.opensource.org/licenses/mit-license.php (MIT) * @copyright Copyright 2010-2012 Pete Boere * @@ -22,6 +22,7 @@ */ require_once 'lib/Util.php'; +require_once 'lib/IO.php'; require_once 'lib/Core.php'; CssCrush::init( dirname( __FILE__ ) ); @@ -31,6 +32,7 @@ CssCrush_Function::init(); require_once 'lib/Importer.php'; +require_once 'lib/Iterator.php'; require_once 'lib/Color.php'; require_once 'lib/Hook.php'; diff --git a/Plugins.ini b/Plugins.ini index 35fa4af..55d54d9 100644 --- a/Plugins.ini +++ b/Plugins.ini @@ -30,4 +30,8 @@ plugins[] = double-colon.php plugins[] = hocus-pocus.php ; CSS3 'initial' keyword shim -plugins[] = initial.php \ No newline at end of file +plugins[] = initial.php + +; Transforms correctly-spelt Queen's English into valid CSS (http://spiffingcss.com) +; plugins[] = spiffing.php + diff --git a/Prepend.css b/Prepend.css index 2d2e2a4..3c5d93a 100644 --- a/Prepend.css +++ b/Prepend.css @@ -1,36 +1,35 @@ /*$! -Prepend.css contains library variables by default, but it could also contain reset styles such as reset.css or normalize.css that you would need prepended to every css file. +Prepend.css contains library variables by default, but it could also contain reset styles such as reset.css or normalize.css that you would want prepended to every output file. */ @define { - /*------------------------ - Font stacks - ------------------------*/ + /* Font stacks + ---------------------------------------- */ /* Serif */ - georgia: Georgia, Times, "Times New Roman", serif; - times: Times, "Times New Roman", serif; - palatino: Palatino, 'Palatino Linotype', "Hoefler Text", serif; - serif: $( times ); + baskerville: Baskerville, Times, "Times New Roman", serif; + georgia: Georgia, Times, "Times New Roman", serif; + palatino: Palatino, "Palatino Linotype", "Hoefler Text", serif; + times: Times, "Times New Roman", serif; + serif: $( times ); /* Sans-serif */ - helvetica: "Helvetica Neue", Helvetica, Arial, sans-serif; - arial: "Arial Unicode MS", Arial, Helvetica, sans-serif; - verdana: Verdana, Tahoma, Arial, sans-serif; - lucida: "Lucida Sans Unicode", "Lucida Sans", "Lucida Grande", Verdana, sans-serif; - sans-serif: $( arial ); - - /* Monospace */ - courier: "Courier New", Courier, mono; - consolas: Consolas, "Lucida Console", Monaco, "Courier New", Courier, mono; - monaco: Monaco, "Courier New", Courier, mono; - mono: $( courier ); - - /* Unicode */ - unicode: "Arial Unicode MS", Arial, "Microsoft Sans Serif", "Lucida Grande", sans-serif; + arial: Arial, "Arial Unicode MS", Helvetica, sans-serif; + gill-sans: "Gill Sans", Calibri, "Trebuchet MS", sans-serif; + helvetica: "Helvetica Neue", Helvetica, Arial, sans-serif; + lucida: "Lucida Sans Unicode", "Lucida Sans", "Lucida Grande", Verdana, sans-serif; + trebuchet-ms: "Trebuchet MS", "Lucida Sans Unicode", "Lucida Grande", "Lucida Sans", Arial, sans-serif; + verdana: Verdana, Tahoma, Arial, sans-serif; + sans-serif: $( arial ); + + /* Monospace */ + consolas: Consolas, "Lucida Console", Monaco, "Courier New", Courier, monospace; + courier: "Courier New", Courier, monospace; + monaco: Monaco, "Courier New", Courier, monospace; + monospace: $( courier ); } diff --git a/cli.php b/cli.php index 342f6ec..b57905b 100644 --- a/cli.php +++ b/cli.php @@ -7,8 +7,14 @@ require_once 'CssCrush.php'; + +// Exit status constants +define( 'STATUS_OK', 0 ); +define( 'STATUS_ERROR', 1 ); + // Get stdin input $stdin = fopen( 'php://stdin', 'r' ); +stream_set_blocking( $stdin, false ) or die ( 'Failed to disable stdin blocking' ); $stdin_contents = stream_get_contents( $stdin ); fclose( $stdin ); @@ -23,7 +29,7 @@ $required_version = 5.3; if ( $version < $required_version ) { fwrite( $stdout, "PHP version $required_version or higher is required to use this tool.\nYou are currently running PHP version $version\n\n" ); - exit( 1 ); + exit( STATUS_ERROR ); } @@ -107,7 +113,7 @@ if ( $help_flag ) { fwrite( $stdout, $help ); - exit( 1 ); + exit( STATUS_OK ); } @@ -118,8 +124,8 @@ if ( $input_file ) { if ( ! file_exists( $input_file ) ) { - fwrite( $stdout, "can't find input file\n\n" ); - exit( 0 ); + fwrite( $stdout, "Input file not found\n\n" ); + exit( STATUS_ERROR ); } $input = file_get_contents( $input_file ); } @@ -128,7 +134,7 @@ } else { fwrite( $stdout, $help ); - exit( 1 ); + exit( STATUS_OK ); } @@ -170,18 +176,20 @@ if ( $output_file ) { if ( ! @file_put_contents( $output_file, $output ) ) { - fwrite( $stdout, "Could not write to path '$output_file'\n" ); + $message = "Could not write to path '$output_file'\n"; if ( strpos( $output_file, '~' ) === 0 ) { - fwrite( $stdout, "No tilde expansion\n" ); + $message .= "Tilde expansion does not work\n"; } - exit( 0 ); + fwrite( $stdout, $message ); + exit( STATUS_ERROR ); } } else { $output .= "\n"; fwrite( $stdout, $output ); } -exit( 1 ); + +exit( STATUS_OK ); diff --git a/lib/Color.php b/lib/Color.php index c00d4e4..4df58dc 100644 --- a/lib/Color.php +++ b/lib/Color.php @@ -12,7 +12,7 @@ class csscrush_color { public static function getKeywords () { // Load the keywords if necessary if ( empty( self::$keywords ) ) { - $path = csscrush::$location . '/misc/color-keywords.ini'; + $path = csscrush::$config->location . '/misc/color-keywords.ini'; if ( $keywords = parse_ini_file( $path ) ) { foreach ( $keywords as $word => $rgb ) { $rgb = array_map( 'intval', explode( ',', $rgb ) ); @@ -162,4 +162,5 @@ public static function hexToRgb ( $hex ) { return array_map( 'hexdec', $hex ); } -} \ No newline at end of file +} + diff --git a/lib/Core.php b/lib/Core.php index 1dab671..13e1d8f 100644 --- a/lib/Core.php +++ b/lib/Core.php @@ -10,27 +10,13 @@ class csscrush { // Path information, global settings public static $config; - // The path of this script - public static $location; - - // Aliases from the aliases file - public static $aliases = array(); - public static $aliasesRaw = array(); - - // Macro function names - public static $macros = array(); - - public static $COMPILE_SUFFIX = '.crush.css'; - - // Global variable storage - protected static $globalVars = array(); + // Properties available to each process + public static $process; + public static $options; + public static $storage; + // Internal protected static $assetsLoaded = false; - - // Properties available to each 'file' process - public static $storage; - public static $compileName; - public static $options; protected static $tokenUID; // Regular expressions @@ -75,27 +61,32 @@ class csscrush { // Init called once manually post class definition public static function init ( $current_dir ) { - self::$location = $current_dir; + self::$config = new stdclass(); - self::$config = $config = new stdClass; - $config->file = '.' . __CLASS__; - $config->data = null; - $config->path = null; - $config->baseDir = null; - $config->baseURL = null; + // Path to this installation + self::$config->location = $current_dir; // Get normalized document root reference: no symlink, forward slashes, no trailing slashes - $docRoot = null; + $doc_root = null; if ( isset( $_SERVER[ 'DOCUMENT_ROOT' ] ) ) { - $docRoot = realpath( $_SERVER[ 'DOCUMENT_ROOT' ] ); + $doc_root = realpath( $_SERVER[ 'DOCUMENT_ROOT' ] ); } else { // Probably IIS $scriptname = $_SERVER[ 'SCRIPT_NAME' ]; $fullpath = realpath( basename( $scriptname ) ); - $docRoot = substr( $fullpath, 0, stripos( $fullpath, $scriptname ) ); + $doc_root = substr( $fullpath, 0, stripos( $fullpath, $scriptname ) ); } - $config->docRoot = csscrush_util::normalizeSystemPath( $docRoot ); + self::$config->docRoot = csscrush_util::normalizeSystemPath( $doc_root ); + + // Set the default IO handler + self::$config->io = 'csscrush_io'; + + // Global assets + self::$config->vars = array(); + self::$config->plugins = array(); + self::$config->aliases = array(); + self::$config->aliasesRaw = array(); // Casting to objects for ease of use self::$regex = (object) self::$regex; @@ -103,6 +94,21 @@ public static function init ( $current_dir ) { self::$regex->function = (object) self::$regex->function; } + + public static function io_call ( $method ) { + + // Fetch the argument list, shift off the first item + $args = func_get_args(); + array_shift( $args ); + + // The method address + $the_method = array( self::$config->io, $method ); + + // Return the call result + return call_user_func_array( $the_method, $args ); + } + + // Aliases and macros loader protected static function loadAssets () { @@ -113,16 +119,16 @@ protected static function loadAssets () { // Load aliases file if it exists if ( $aliases_file ) { if ( $result = @parse_ini_file( $aliases_file, true ) ) { - self::$aliasesRaw = $result; + self::$config->aliasesRaw = $result; // Value aliases require a little preprocessing - if ( isset( self::$aliasesRaw[ 'values' ] ) ) { + if ( isset( self::$config->aliasesRaw[ 'values' ] ) ) { $store = array(); - foreach ( self::$aliasesRaw[ 'values' ] as $prop_val => $aliases ) { + foreach ( self::$config->aliasesRaw[ 'values' ] as $prop_val => $aliases ) { list( $prop, $value ) = array_map( 'trim', explode( ':', $prop_val ) ); $store[ $prop ][ $value ] = $aliases; } - self::$aliasesRaw[ 'values' ] = $store; + self::$config->aliasesRaw[ 'values' ] = $store; } } else { @@ -141,8 +147,9 @@ protected static function loadAssets () { if ( $plugins_file ) { if ( $result = @parse_ini_file( $plugins_file ) ) { foreach ( $result[ 'plugins' ] as $plugin_file ) { - $path = self::$location . "/plugins/$plugin_file"; + $path = self::$config->location . "/plugins/$plugin_file"; if ( file_exists( $path ) ) { + self::$config->plugins[] = $plugin_file; require_once $path; } else { @@ -156,70 +163,32 @@ protected static function loadAssets () { } } - // Initialize config data, create config cache file if needed - protected static function loadCacheData () { - $config = self::$config; - if ( - file_exists( $config->path ) and - $config->data and - $config->data[ 'originPath' ] == $config->path - ) { - // Already loaded and config file exists in the current directory - return; - } - - $configFileExists = file_exists( $config->path ); - $configFileWritable = $configFileExists ? is_writable( $config->path ) : false; - if ( $configFileExists and $configFileWritable ) { - // Load from file - $config->data = unserialize( file_get_contents( $config->path ) ); - } - else { - // Config file may exist but not be writable (may not be visible in some ftp situations?) - if ( $configFileExists ) { - if ( ! @unlink( $config->path ) ) { - trigger_error( __METHOD__ . ": Could not delete config data file.\n", E_USER_NOTICE ); - } - } - // Create - self::log( 'Creating config data file' ); - file_put_contents( $config->path, serialize( array() ) ); - $config->data = array(); - } - } - - // Establish the hostfile directory and optionally test it's writable - protected static function setPath ( $new_dir, $write_test = true ) { + // Establish the input and output directories and optionally test if output dir writable + protected static function setPath ( $input_dir, $write_test = true ) { $config = self::$config; - $docRoot = $config->docRoot; + $process = self::$process; + $doc_root = $config->docRoot; - if ( strpos( $new_dir, $docRoot ) !== 0 ) { + if ( strpos( $input_dir, $doc_root ) !== 0 ) { // Not a system path - $new_dir = realpath( "$docRoot/$new_dir" ); + $input_dir = realpath( "$doc_root/$input_dir" ); } - $pathtest = true; - if ( ! file_exists( $new_dir ) ) { - trigger_error( __METHOD__ . ": directory '$new_dir' doesn't exist.\n", E_USER_WARNING ); - $pathtest = false; - } - else if ( $write_test and ! is_writable( $new_dir ) ) { - self::log( 'Attempting to change permissions' ); - if ( ! @chmod( $new_dir, 0755 ) ) { - trigger_error( __METHOD__ . ": directory '$new_dir' is unwritable.\n", E_USER_WARNING ); - self::log( 'Unable to update permissions' ); - $pathtest = false; - } - else { - self::log( 'Permissions updated' ); - } - } + // Store input directory + $process->inputDir = $input_dir; + $process->inputDirUrl = substr( $process->inputDir, strlen( $doc_root ) ); + + // Store reference to the output dir + $process->outputDir = csscrush::io_call( 'getOutputDir' ); + $process->outputDirUrl = substr( $process->outputDir, strlen( $doc_root ) ); - $config->path = "$new_dir/$config->file"; - $config->baseDir = $new_dir; - $config->baseURL = substr( $new_dir, strlen( $docRoot ) ); + // Test the output directory to see if it's writable + $pathtest = csscrush::io_call( 'testOutputDir', $write_test ); + + // Setup the IO handler + csscrush::io_call( 'init' ); return $pathtest; } @@ -237,23 +206,26 @@ protected static function setPath ( $new_dir, $write_test = true ) { */ public static function file ( $file, $options = null ) { - $config = self::$config; - // Reset for current process - self::reset(); + self::reset( $options ); + + $config = self::$config; + $process = self::$process; + $options = self::$options; + $doc_root = $config->docRoot; // Since we're comparing strings, we need to iron out OS differences $file = str_replace( '\\', '/', $file ); - $docRoot = $config->docRoot; - + + // Finding the system path of the input file and validating it $pathtest = true; - if ( strpos( $file, $docRoot ) === 0 ) { + if ( strpos( $file, $doc_root ) === 0 ) { // System path $pathtest = self::setPath( dirname( $file ) ); } else if ( strpos( $file, '/' ) === 0 ) { // WWW root path - $pathtest = self::setPath( dirname( $docRoot . $file ) ); + $pathtest = self::setPath( dirname( $doc_root . $file ) ); } else { // Relative path @@ -265,41 +237,38 @@ public static function file ( $file, $options = null ) { return ''; } - // Load the data of previously cached files to self::$config - self::loadCacheData(); + // Load the cache data + $process->cacheData = csscrush::io_call( 'getCacheData' ); - // Get the merged options, stored to self::$options - $options = self::getOptions( $options ); - - // Get the hostfile object - $hostfile = self::getHostfile( $file ); - - // Compiled filename we're searching for - // This can be given as an option, uses the host-filename by default - $baseCompileName = basename( $hostfile->name, '.css' ); - if ( !empty( $options[ 'output_file' ] ) ) { - $baseCompileName = basename( $options[ 'output_file' ], '.css' ); + // Get the input file object + if ( ! ( $process->input = csscrush::io_call( 'getInput', $file ) ) ) { + return ''; } - self::$compileName = $baseCompileName . self::$COMPILE_SUFFIX; - // If cache is enabled check for a valid compiled file + // Create a filename that will be used later + // Used in validateCache, and writing to filesystem + $process->outputFileName = csscrush::io_call( 'getOutputFileName' ); + if ( $options[ 'cache' ] === true ) { - $validCompliledFile = self::validateCache( $hostfile ); - if ( is_string( $validCompliledFile ) ) { - return $validCompliledFile; + + // If cache is enabled check for a valid compiled file + $valid_compliled_file = csscrush::io_call( 'validateExistingOutput' ); + + if ( is_string( $valid_compliled_file ) ) { + return $valid_compliled_file; } } // Collate hostfile and imports - $stream = csscrush_importer::hostfile( $hostfile ); + $stream = csscrush_importer::hostfile( $process->input ); // Compile $stream = self::compile( $stream ); - // Create file and return path. Return empty string on failure - if ( file_put_contents( "$config->baseDir/" . self::$compileName, $stream ) ) { - return "$config->baseURL/" . self::$compileName . - ( $options[ 'versioning' ] ? '?' . time() : '' ); + // Create file and return url. Return empty string on failure + if ( file_put_contents( "$process->outputDir/$process->outputFileName", $stream ) ) { + $timestamp = $options[ 'versioning' ] ? '?' . time() : ''; + return "$process->outputDirUrl/$process->outputFileName$timestamp"; } else { return ''; @@ -315,8 +284,11 @@ public static function file ( $file, $options = null ) { * @return string HTML link tag or error message inside HTML comment */ public static function tag ( $file, $options = null, $attributes = array() ) { + $file = self::file( $file, $options ); - if ( !empty( $file ) ) { + + if ( ! empty( $file ) ) { + // On success return the tag with any custom attributes $attributes[ 'rel' ] = "stylesheet"; $attributes[ 'href' ] = $file; @@ -324,6 +296,7 @@ public static function tag ( $file, $options = null, $attributes = array() ) { return "\n"; } else { + // Return an HTML comment with message on failure $class = __CLASS__; return "\n"; @@ -341,11 +314,14 @@ public static function tag ( $file, $options = null, $attributes = array() ) { public static function inline ( $file, $options = null, $attributes = array() ) { $file = self::file( $file, $options ); - if ( !empty( $file ) ) { + + if ( ! empty( $file ) ) { + // On success fetch the CSS text - $content = file_get_contents( self::$config->baseDir . '/' . self::$compileName ); + $content = file_get_contents( self::$process->outputDir . '/' . self::$process->outputFileName ); $tag_open = ''; $tag_close = ''; + if ( is_array( $attributes ) ) { $attr_string = csscrush_util::htmlAttributes( $attributes ); $tag_open = ""; @@ -354,6 +330,7 @@ public static function inline ( $file, $options = null, $attributes = array() ) return "$tag_open{$content}$tag_close\n"; } else { + // Return an HTML comment with message on failure $class = __CLASS__; return "\n"; @@ -368,28 +345,32 @@ public static function inline ( $file, $options = null, $attributes = array() ) * @return string CSS text */ public static function string ( $string, $options = null ) { + // Reset for current process - self::reset(); - self::getOptions( $options ); + self::reset( $options ); + + $config = self::$config; + $process = self::$process; + $options = self::$options; // Set the path context if one is given if ( isset( $options[ 'import_context' ] ) && ! empty( $options[ 'import_context' ] ) ) { self::setPath( $options[ 'import_context' ] ); } - // It's not associated with a real file so we create an 'empty' hostfile object - $hostfile = self::getHostfile(); + // It's not associated with a real file so we create an 'empty' input object + $process->input = csscrush::io_call( 'getInput' ); // Set the string on the object - $hostfile->string = $string; + $process->input->string = $string; // Import files may be ignored if ( isset( $options[ 'no_import' ] ) ) { - $hostfile->importIgnore = true; + $process->input->importIgnore = true; } // Collate imports - $stream = csscrush_importer::hostfile( $hostfile ); + $stream = csscrush_importer::hostfile( $process->input ); // Return compiled string return self::compile( $stream ); @@ -401,19 +382,22 @@ public static function string ( $string, $options = null ) { * @param mixed $var Assoc array of variable names and values, a php ini filename or null */ public static function globalVars ( $vars ) { + + $config = self::$config; + // Merge into the stack, overrides existing variables of the same name if ( is_array( $vars ) ) { - self::$globalVars = array_merge( self::$globalVars, $vars ); + $config->vars = array_merge( $config->vars, $vars ); } // Test for a file. If it is attempt to parse it elseif ( is_string( $vars ) and file_exists( $vars ) ) { if ( $result = parse_ini_file( $vars ) ) { - self::$globalVars = array_merge( self::$globalVars, $result ); + $config->vars = array_merge( $config->vars, $result ); } } // Clear the stack if the argument is explicitly null elseif ( is_null( $vars ) ) { - self::$globalVars = array(); + $config->vars = array(); } } @@ -423,26 +407,7 @@ public static function globalVars ( $vars ) { * @param string $dir System path to the directory */ public static function clearCache ( $dir = '' ) { - if ( empty( $dir ) ) { - $dir = dirname( __FILE__ ); - } - else if ( !file_exists( $dir ) ) { - return; - } - $configPath = $dir . '/' . self::$config->file; - if ( file_exists( $configPath ) ) { - unlink( $configPath ); - } - // Remove any compiled files - $suffix = self::$COMPILE_SUFFIX; - $suffixLength = strlen( $suffix ); - foreach ( scandir( $dir ) as $file ) { - if ( - strpos( $file, $suffix ) === strlen( $file ) - $suffixLength - ) { - unlink( $dir . "/{$file}" ); - } - } + return csscrush::io_call( 'clearCache', $dir ); } @@ -483,32 +448,6 @@ public static function log () { ##################### # Internal functions - protected static function getHostfile ( $file = false ) { - // May return a hostfile object associated with a real file - // Alternatively it may return a hostfile object with string input - - $config = self::$config; - - // Make basic information about the hostfile accessible - $hostfile = new stdClass; - $hostfile->name = $file ? basename( $file ) : null; - $hostfile->dir = $config->baseDir; - $hostfile->path = $file ? "$config->baseDir/$hostfile->name" : null; - - if ( $file ) { - if ( !file_exists( $hostfile->path ) ) { - // If host file is not found return an empty string - trigger_error( __METHOD__ . ": File '$hostfile->name' not found.\n", E_USER_WARNING ); - return ''; - } - else { - // Capture the modified time - $hostfile->mtime = filemtime( $hostfile->path ); - } - } - return $hostfile; - } - protected static function getBoilerplate () { $file = csscrush_util::find( 'CssCrush-local.boilerplate', 'CssCrush.boilerplate' ); @@ -549,6 +488,7 @@ protected static function getOptions ( $options ) { // Create default options for those not set $option_defaults = array( + // Minify. Set true for formatting and comments 'debug' => false, @@ -575,13 +515,12 @@ protected static function getOptions ( $options ) { 'rewrite_import_urls' => false, // Keeping track of global vars internally - '_globalVars' => self::$globalVars, + '_globalVars' => self::$config->vars, ); - self::$options = is_array( $options ) ? + return is_array( $options ) ? array_merge( $option_defaults, $options ) : $option_defaults; - return self::$options; } protected static function pruneAliases () { @@ -591,7 +530,7 @@ protected static function pruneAliases () { // For expicit 'none' argument turn off aliases if ( 'none' === $vendor ) { - self::$aliases = null; + self::$config->aliases = null; return; } @@ -605,7 +544,7 @@ protected static function pruneAliases () { $vendor = "-$vendor-"; // Loop the aliases array, filter down to the target vendor - foreach ( self::$aliases as $group_name => $group_array ) { + foreach ( self::$config->aliases as $group_name => $group_array ) { // Property/value aliases are a special case if ( 'values' === $group_name ) { foreach ( $group_array as $property => $values ) { @@ -617,7 +556,7 @@ protected static function pruneAliases () { } } } - self::$aliases[ 'values' ][ $property ][ $value ] = $result; + self::$config->aliases[ 'values' ][ $property ][ $value ] = $result; } continue; } @@ -630,14 +569,13 @@ protected static function pruneAliases () { } // Prune the whole alias keyword if there is no result if ( empty( $result ) ) { - unset( self::$aliases[ $group_name ][ $alias_keyword ] ); + unset( self::$config->aliases[ $group_name ][ $alias_keyword ] ); } else { - self::$aliases[ $group_name ][ $alias_keyword ] = $result; + self::$config->aliases[ $group_name ][ $alias_keyword ] = $result; } } } - // self::log( self::$aliases ); } protected static function calculateVariables () { @@ -646,8 +584,8 @@ protected static function calculateVariables () { // In-file variables override global variables // Runtime variables override in-file variables - self::$storage->variables = array_merge( - self::$globalVars, self::$storage->variables ); + self::$storage->variables = array_merge( self::$config->vars, self::$storage->variables ); + if ( !empty( self::$options[ 'vars' ] ) ) { self::$storage->variables = array_merge( self::$storage->variables, self::$options[ 'vars' ] ); @@ -661,8 +599,8 @@ protected static function calculateVariables () { $regex->function->var, array( 'self', 'cb_placeVariables' ), $value ); // Custom functions: - // Variable values can be escaped from function parsing with a double bang - if ( strpos( $value, '!!' ) === 0 ) { + // Variable values can be escaped from function parsing with a tilde prefix + if ( strpos( $value, '~' ) === 0 ) { $value = ltrim( $value, "!\t\r " ); } else { @@ -684,11 +622,15 @@ protected static function placeVariables ( $stream ) { return $stream; } - protected static function reset () { + protected static function reset ( $options = null ) { + // Reset properties for current process self::$tokenUID = 0; - self::$storage = new stdclass; - + + self::$process = new stdclass(); + self::$process->cacheData = array(); + + self::$storage = new stdclass(); self::$storage->tokens = (object) array( 'strings' => array(), 'comments' => array(), @@ -696,8 +638,10 @@ protected static function reset () { 'parens' => array(), ); self::$storage->variables = array(); - // Temporary storage - self::$storage->tmp = new stdclass; + self::$storage->misc = new stdclass(); + + // Load the merged options + self::$options = self::getOptions( $options ); } protected static function compile ( $stream ) { @@ -712,7 +656,7 @@ protected static function compile ( $stream ) { } // Set aliases. May be pruned if a vendor target is set - self::$aliases = self::$aliasesRaw; + self::$config->aliases = self::$config->aliasesRaw; self::pruneAliases(); // Parse variables @@ -720,7 +664,7 @@ protected static function compile ( $stream ) { // Calculate the variable stack self::calculateVariables(); - self::log( self::$storage->variables ); + // self::log( self::$storage->variables ); // Place the variables $stream = self::placeVariables( $stream ); @@ -743,6 +687,22 @@ protected static function compile ( $stream ) { // Alias at-rules (if there are any) $stream = self::aliasAtRules( $stream ); + + + $iterator = new csscrush_atrule_iterator( array( + 'input' => $stream, + 'search' => '@prefix', + 'direction' => 'reverse', + )); + + // csscrush::log( $iterator ); + // csscrush::log( count( $iterator ) ); + + foreach ( $iterator as $atrule ) { + // self::log( $value ); + } + + // print it all back $stream = self::display( $stream ); @@ -751,7 +711,7 @@ protected static function compile ( $stream ) { $stream = self::getBoilerplate() . "\n$stream"; } - self::log( self::$config->data ); + // self::log( self::$config->cacheData ); // Release memory self::$storage = null; @@ -760,7 +720,8 @@ protected static function compile ( $stream ) { } protected static function display ( $stream ) { - $minify = !self::$options[ 'debug' ]; + + $minify = ! self::$options[ 'debug' ]; $regex = self::$regex; if ( $minify ) { @@ -811,80 +772,15 @@ protected static function display ( $stream ) { return $stream; } - protected static function validateCache ( $hostfile ) { - $config = self::$config; - - // Search base directory for an existing compiled file - foreach ( scandir( $config->baseDir ) as $filename ) { - - if ( self::$compileName != $filename ) { - continue; - } - // Cached file exists - self::log( 'Cached file exists' ); - - $existingfile = new stdClass; - $existingfile->name = $filename; - $existingfile->path = "$config->baseDir/$existingfile->name"; - $existingfile->URL = "$config->baseURL/$existingfile->name"; - - // Start off with the host file then add imported files - $all_files = array( $hostfile->mtime ); - - if ( file_exists( $existingfile->path ) and isset( $config->data[ self::$compileName ] ) ) { - // File exists and has config - self::log( 'has config' ); - foreach ( $config->data[ $existingfile->name ][ 'imports' ] as $import_file ) { - // Check if this is docroot relative or hostfile relative - $root = strpos( $import_file, '/' ) === 0 ? $config->docRoot : $config->baseDir; - $import_filepath = realpath( $root ) . "/{$import_file}"; - if ( file_exists( $import_filepath ) ) { - $all_files[] = filemtime( $import_filepath ); - } - else { - // File has been moved, remove old file and skip to compile - self::log( 'Import file has been moved, removing existing file' ); - unlink( $existingfile->path ); - return false; - } - } - - $existing_options = $config->data[ $existingfile->name ][ 'options' ]; - $existing_datesum = $config->data[ $existingfile->name ][ 'datem_sum' ]; - if ( - $existing_options == self::$options and - $existing_datesum == array_sum( $all_files ) - ) { - // Files have not been modified and config is the same: return the old file - self::log( "Files and options have not been modified, returning existing - file '$existingfile->URL'" ); - return $existingfile->URL . ( self::$options[ 'versioning' ] !== false ? "?{$existing_datesum}" : '' ); - } - else { - // Remove old file and continue making a new one... - self::log( 'Files or options have been modified, removing existing file' ); - unlink( $existingfile->path ); - } - } - else if ( file_exists( $existingfile->path ) ) { - // File exists but has no config - self::log( 'File exists but no config, removing existing file' ); - unlink( $existingfile->path ); - } - return false; - - } // foreach - return false; - } - protected static function minify ( $str ) { $replacements = array( - '!\n+| (\{)!' => '$1', // Trim whitespace - '!(^|[: \(,])0(\.\d+)!' => '$1$2', // Strip leading zeros on floats - '!(^|[: \(,])\.?0[a-zA-Z]{1,5}!i' => '${1}0', // Strip unnecessary units on zero values - '!(^|\:) *(0 0 0|0 0 0 0) *(;|\})!' => '${1}0${3}', // Collapse zero lists - '!(padding|margin) ?\: *0 0 *(;|\})!' => '${1}:0${2}', // Collapse zero lists continued - '!\s*([>~+=])\s*!' => '$1', // Clean-up around combinators + '!\n+| (\{)!' => '$1', // Trim whitespace + '!(^|[: \(,])0(\.\d+)!' => '$1$2', // Strip leading zeros on floats + // '!(^|[: \(,])\.?0[a-zA-Z]{1,5}!i' => '${1}0', // Strip unnecessary units on zero values + '!(^|[: \(,])\.?0(in|[cme]m|ex|p[tcx])!i' => '${1}0', // Strip unnecessary units on zero values + '!(^|\:) *(0 0 0|0 0 0 0) *(;|\})!' => '${1}0${3}', // Collapse zero lists + '!(padding|margin) ?\: *0 0 *(;|\})!' => '${1}:0${2}', // Collapse zero lists continued + '!\s*([>~+=])\s*!' => '$1', // Clean-up around combinators '!\#([0-9a-f])\1([0-9a-f])\2([0-9a-f])\3!i' => '#$1$2$3', // Compress hex codes ); @@ -894,11 +790,11 @@ protected static function minify ( $str ) { protected static function aliasAtRules ( $stream ) { - if ( empty( self::$aliases[ 'at-rules' ] ) ) { + if ( empty( self::$config->aliases[ 'at-rules' ] ) ) { return $stream; } - $aliases = self::$aliases[ 'at-rules' ]; + $aliases = self::$config->aliases[ 'at-rules' ]; foreach ( $aliases as $at_rule => $at_rule_aliases ) { if ( @@ -915,10 +811,11 @@ protected static function aliasAtRules ( $stream ) { // Store the match position $block_start_pos = $match[0][1]; + // Capture the curly bracketed block $curly_match = csscrush_util::matchBrackets( $stream, $brackets = array( '{', '}' ), $block_start_pos ); - if ( !$curly_match ) { + if ( ! $curly_match ) { // Couldn't match the block break; } @@ -961,7 +858,7 @@ protected static function aliasAtRules ( $stream ) { $cloneRule->declarations = $new_set; // Store the clone - $replacements[] = $clone_rule_label = self::createTokenLabel( 'r' ); + $replacements[] = $clone_rule_label = self::tokenLabelCreate( 'r' ); self::$storage->tokens->rules[ $clone_rule_label ] = $cloneRule; } // Finally replace the original labels with the cloned rule labels @@ -989,17 +886,35 @@ protected static function aliasAtRules ( $stream ) { return $stream; } - public static function createTokenLabel ( $prefix, $counter = null ) { - $counter = !is_null( $counter ) ? $counter : ++self::$tokenUID; + public static function tokenLabelCreate ( $prefix ) { + $counter = ++self::$tokenUID; return "___$prefix{$counter}___"; } + public static function tokenReplace ( $string, $token_replace, $type = 'parens' ) { + + // The tokens to replace + $token_replace = (array) $token_replace; + + // Reference the token table + $token_table =& self::$storage->tokens->{ $type }; + + // Replace the tokens listed + foreach ( $token_replace as $token ) { + if ( isset( $token_table[ $token ] ) ) { + $string = str_replace( $token, $token_table[ $token ], $string ); + } + } + + return $string; + } + ############################# # preg_replace callbacks protected static function cb_extractStrings ( $match ) { - $label = csscrush::createTokenLabel( 's' ); + $label = csscrush::tokenLabelCreate( 's' ); csscrush::$storage->tokens->strings[ $label ] = $match[0]; return $label; } @@ -1018,7 +933,7 @@ protected static function cb_extractComments ( $match ) { return ''; } - $label = self::createTokenLabel( 'c' ); + $label = self::tokenLabelCreate( 'c' ); self::$storage->tokens->comments[ $label ] = $comment; return $label; @@ -1029,6 +944,7 @@ protected static function cb_restoreComments ( $match ) { } protected static function cb_extractVariables ( $match ) { + $regex = self::$regex; $block = $match[2]; @@ -1073,7 +989,7 @@ protected static function cb_placeVariables ( $match ) { protected static function cb_extractAndProcessRules ( $match ) { - $rule = new stdClass; + $rule = new stdclass(); $rule->selector_raw = $match[1]; $rule->declaration_raw = $match[2]; @@ -1082,11 +998,11 @@ protected static function cb_extractAndProcessRules ( $match ) { $rule = new csscrush_rule( $rule->selector_raw, $rule->declaration_raw ); // Only store rules with declarations - if ( !empty( $rule->declarations ) ) { + if ( ! empty( $rule->declarations ) ) { csscrush_hook::run( 'rule_prealias', $rule ); - if ( !empty( self::$aliases ) ) { + if ( ! empty( self::$config->aliases ) ) { $rule->addPropertyAliases(); $rule->addFunctionAliases(); $rule->addValueAliases(); @@ -1098,7 +1014,7 @@ protected static function cb_extractAndProcessRules ( $match ) { csscrush_hook::run( 'rule_postprocess', $rule ); - $label = self::createTokenLabel( 'r' ); + $label = self::tokenLabelCreate( 'r' ); self::$storage->tokens->rules[ $label ] = $rule; return $label . "\n"; } @@ -1112,21 +1028,25 @@ protected static function cb_restoreLiteral ( $match ) { } protected static function cb_printRule ( $match ) { - $minify = !self::$options[ 'debug' ]; + + $minify = ! self::$options[ 'debug' ]; + $whitespace = $minify ? '' : ' '; + $ruleLabel = $match[0]; - if ( !isset( self::$storage->tokens->rules[ $ruleLabel ] ) ) { + + if ( ! isset( self::$storage->tokens->rules[ $ruleLabel ] ) ) { return ''; } $rule = self::$storage->tokens->rules[ $ruleLabel ]; // Build the selector - $selectors = implode( ',', $rule->selectors ); + $selectors = implode( ",$whitespace", $rule->selectors ); // Build the block $block = array(); - $colon = $minify ? ':' : ': '; foreach ( $rule as $declaration ) { - $block[] = "{$declaration->property}$colon{$declaration->value}"; + $important = $declaration->important ? "$whitespace!important" : ''; + $block[] = "$declaration->property:{$whitespace}$declaration->value{$important}"; } // Return whole rule diff --git a/lib/Function.php b/lib/Function.php index bbfc285..368395d 100644 --- a/lib/Function.php +++ b/lib/Function.php @@ -44,7 +44,7 @@ public static function parseAndExecuteValue ( $str ) { } // No custom functions, early return - if ( !preg_match( $patt, $str ) ) { + if ( ! preg_match( $patt, $str ) ) { return $str; } diff --git a/lib/IO.php b/lib/IO.php new file mode 100644 index 0000000..be47012 --- /dev/null +++ b/lib/IO.php @@ -0,0 +1,255 @@ +cacheFileName = '.csscrush'; + $process->cacheFilePath = "$process->inputDir/$process->cacheFileName"; + } + + + public static function getInput ( $file = false ) { + + // May return a hostfile object associated with a real file + // Alternatively it may return a hostfile object with string input + + $process = csscrush::$process; + + // Make basic information about the input object accessible + $input = new stdclass(); + $input->name = $file ? basename( $file ) : null; + $input->dir = $file ? $process->inputDir : null; + $input->path = $file ? "$process->inputDir/$input->name" : null; + + if ( $file ) { + + if ( ! file_exists( $input->path ) ) { + // On failure return false with a message + trigger_error( __METHOD__ . ": File '$input->name' not found.\n", E_USER_WARNING ); + return false; + } + else { + // Capture the modified time + $input->mtime = filemtime( $input->path ); + } + } + return $input; + } + + + public static function getOutputDir () { + return csscrush::$process->inputDir; + } + + + public static function testOutputDir ( $write_test = true ) { + + $output_dir = csscrush::$process->outputDir; + $pathtest = true; + + if ( ! file_exists( $output_dir ) ) { + trigger_error( __METHOD__ . ": directory '$output_dir' doesn't exist.\n", E_USER_WARNING ); + $pathtest = false; + } + else if ( $write_test and ! is_writable( $output_dir ) ) { + csscrush::log( 'Attempting to change permissions' ); + if ( ! @chmod( $output_dir, 0755 ) ) { + trigger_error( __METHOD__ . ": directory '$output_dir' is unwritable.\n", E_USER_WARNING ); + csscrush::log( 'Unable to update permissions' ); + $pathtest = false; + } + else { + csscrush::log( 'Permissions updated' ); + } + } + return $pathtest; + } + + + public static function getOutputFileName () { + + $process = csscrush::$process; + $options = csscrush::$options; + $input = $process->input; + + $output_basename = basename( $input->name, '.css' ); + + if ( ! empty( $options[ 'output_file' ] ) ) { + $output_basename = basename( $options[ 'output_file' ], '.css' ); + } + + return "$output_basename.crush.css"; + } + + + public static function validateExistingOutput () { + + $process = csscrush::$process; + $config = csscrush::$config; + $input = $process->input; + + // Search base directory for an existing compiled file + foreach ( scandir( $process->outputDir ) as $filename ) { + + if ( $process->outputFileName != $filename ) { + continue; + } + // Cached file exists + csscrush::log( 'Cached file exists' ); + + $existingfile = new stdclass(); + $existingfile->name = $filename; + $existingfile->path = "$process->outputDir/$existingfile->name"; + $existingfile->URL = "$process->outputDirUrl/$existingfile->name"; + + // Start off with the input file then add imported files + $all_files = array( $input->mtime ); + + if ( file_exists( $existingfile->path ) and isset( $process->cacheData[ $process->outputFileName ] ) ) { + + // File exists and has config + csscrush::log( 'has config' ); + + foreach ( $process->cacheData[ $existingfile->name ][ 'imports' ] as $import_file ) { + + // Check if this is docroot relative or input dir relative + $root = strpos( $import_file, '/' ) === 0 ? $config->docRoot : $process->inputDir; + $import_filepath = realpath( $root ) . "/$import_file"; + + if ( file_exists( $import_filepath ) ) { + $all_files[] = filemtime( $import_filepath ); + } + else { + // File has been moved, remove old file and skip to compile + csscrush::log( 'Import file has been moved, removing existing file' ); + unlink( $existingfile->path ); + return false; + } + } + + $existing_options = $process->cacheData[ $existingfile->name ][ 'options' ]; + $existing_datesum = $process->cacheData[ $existingfile->name ][ 'datem_sum' ]; + + $options_unchanged = $existing_options == csscrush::$options; + $files_unchanged = $existing_datesum == array_sum( $all_files ); + + if ( $options_unchanged and $files_unchanged ) { + + // Files have not been modified and config is the same: return the old file + csscrush::log( "Files and options have not been modified, returning existing + file '$existingfile->URL'" ); + return $existingfile->URL . ( csscrush::$options[ 'versioning' ] !== false ? "?$existing_datesum" : '' ); + } + else { + // Remove old file and continue making a new one... + ! $options_unchanged && csscrush::log( 'Options have been modified' ); + ! $files_unchanged && csscrush::log( 'Files have been modified' ); + csscrush::log( 'Removing existing file' ); + + unlink( $existingfile->path ); + } + } + else if ( file_exists( $existingfile->path ) ) { + // File exists but has no config + csscrush::log( 'File exists but no config, removing existing file' ); + unlink( $existingfile->path ); + } + return false; + + } // foreach + + return false; + } + + + public static function clearCache ( $dir ) { + + if ( empty( $dir ) ) { + $dir = dirname( __FILE__ ); + } + else if ( ! file_exists( $dir ) ) { + return; + } + + $configPath = $dir . '/' . csscrush::$process->cacheFilePath; + if ( file_exists( $configPath ) ) { + unlink( $configPath ); + } + + // Remove any compiled files + $suffix = '.crush.css'; + $suffixLength = strlen( $suffix ); + + foreach ( scandir( $dir ) as $file ) { + if ( + strpos( $file, $suffix ) === strlen( $file ) - $suffixLength + ) { + unlink( $dir . "/{$file}" ); + } + } + } + + + public static function getCacheData () { + + $config = csscrush::$config; + $process = csscrush::$process; + + if ( + file_exists( $process->cacheFilePath ) and + $process->cacheData and + $process->cacheData[ 'originPath' ] == $process->cacheFilePath + ) { + // Already loaded and config file exists in the current directory + return; + } + + $cache_data_exists = file_exists( $process->cacheFilePath ); + $cache_data_file_is_writable = $cache_data_exists ? is_writable( $process->cacheFilePath ) : false; + + $cache_data = array(); + + if ( $cache_data_exists and $cache_data_file_is_writable ) { + // Load from file + $cache_data = unserialize( file_get_contents( $process->cacheFilePath ) ); + } + else { + // Config file may exist but not be writable (may not be visible in some ftp situations?) + if ( $cache_data_exists ) { + if ( ! @unlink( $process->cacheFilePath ) ) { + trigger_error( __METHOD__ . ": Could not delete config data file.\n", E_USER_NOTICE ); + } + } + // Create + csscrush::log( 'Creating cache data file' ); + file_put_contents( $process->cacheFilePath, serialize( array() ) ); + } + + return $cache_data; + } + + + public static function saveCacheData () { + + $process = csscrush::$process; + + // Need to store the current path so we can check we're using the right config path later + $process->cacheData[ 'originPath' ] = $process->cacheFilePath; + + csscrush::log( 'Saving config' ); + file_put_contents( $process->cacheFilePath, serialize( $process->cacheData ) ); + } + +} + + diff --git a/lib/Importer.php b/lib/Importer.php index e3916fe..31cb8c7 100644 --- a/lib/Importer.php +++ b/lib/Importer.php @@ -10,7 +10,7 @@ class csscrush_importer { public static function save ( $data ) { - $config = csscrush::$config; + $process = csscrush::$process; $options = csscrush::$options; // No saving if caching is disabled, return early @@ -19,21 +19,20 @@ public static function save ( $data ) { } // Write to config - $config->data[ csscrush::$compileName ] = $data; - - // Need to store the current path so we can check we're using the right config path later - $config->data[ 'originPath' ] = $config->path; + $process->cacheData[ $process->outputFileName ] = $data; // Save config changes - file_put_contents( $config->path, serialize( $config->data ) ); + csscrush::io_call( 'saveCacheData' ); } - public static function hostfile ( $hostfile ) { + public static function hostfile () { $config = csscrush::$config; + $process = csscrush::$process; $options = csscrush::$options; $regex = csscrush::$regex; + $hostfile = $process->input; // Keep track of all import file info for later logging $mtimes = array(); @@ -41,7 +40,12 @@ public static function hostfile ( $hostfile ) { // Determine input; string or file // Extract the comments then strings - $stream = isset( $hostfile->string ) ? $hostfile->string : file_get_contents( $hostfile->path ); + if ( isset( $hostfile->string ) ) { + $stream = $hostfile->string; + } + else { + $stream = file_get_contents( $hostfile->path ); + } // If there's a prepend file, prepend it if ( $prependFile = csscrush_util::find( 'Prepend-local.css', 'Prepend.css' ) ) { @@ -84,8 +88,6 @@ public static function hostfile ( $hostfile ) { $url = $import_url_token->value; } - // csscrush::log( $match ); - // Pass over absolute urls // Move the search pointer forward if ( preg_match( $regex->absoluteUrl, $url ) ) { @@ -94,7 +96,7 @@ public static function hostfile ( $hostfile ) { } // Create import object - $import = new stdClass; + $import = new stdclass(); $import->url = $url; $import->mediaContext = $mediaContext; $import->hostDir = $hostfile->dir; @@ -112,18 +114,19 @@ public static function hostfile ( $hostfile ) { // Failed to open import, just continue with the import line removed if ( ! $import->content ) { - csscrush::log( "Import file '$import->url' not found" ); + csscrush::log( "Import file '$import->url' not found at '$import->path'" ); $stream = $preStatement . $postStatement; continue; } - // Import file opened successfully so we process it: - // We need to resolve import statement urls in all imported files since - // they will be brought inline with the hostfile else { + // Import file opened successfully so we process it: + // We need to resolve import statement urls in all imported files since + // they will be brought inline with the hostfile - // Start with extracting comments in the import + // Start with extracting strings and comments in the import $import->content = csscrush::extractComments( $import->content ); + $import->content = csscrush::extractStrings( $import->content ); $import->dir = dirname( $import->url ); @@ -135,13 +138,30 @@ public static function hostfile ( $hostfile ) { // Match all @import statements in the import content // Store the replacements we might find $matchCount = preg_match_all( $regex->import, $import->content, $matchAll, - PREG_OFFSET_CAPTURE ); + PREG_OFFSET_CAPTURE | PREG_SET_ORDER ); $replacements = array(); + for ( $index = 0; $index < $matchCount; $index++ ) { - $fullMatch = $matchAll[0][ $index ][0]; - $urlMatch = $matchAll[1][ $index ][0]; + $fullMatch = $matchAll[ $index ][0][0]; + + // Url match may be at one of 2 positions + if ( $matchAll[ $index ][1][1] == -1 ) { + $urlMatch = $matchAll[ $index ][2][0]; + } + else { + $urlMatch = $matchAll[ $index ][1][0]; + } + // Url may be a string token + if ( $urlMatchToken = preg_match( $regex->token->string, $urlMatch ) ) { + // Store the token + $urlMatchToken = new csscrush_string( $urlMatch ); + // Set $urlMatch to the actual value + $urlMatch = $urlMatchToken->value; + } + + // Search and replace on the statement url $search = $urlMatch; $replace = "$import->dir/$urlMatch"; @@ -155,27 +175,35 @@ public static function hostfile ( $hostfile ) { } } - // Trim the statement and set the resolved path - $statement = trim( str_replace( $search, $replace, $fullMatch ) ); + // The full revised statement for replacement + $statement = $fullMatch; + + if ( $urlMatchToken and ! empty( $replace ) ) { + // Alter the stored token on internal hash table + $urlMatchToken->update( $replace ); + } + else { + // Trim the statement and set the resolved path + $statement = trim( str_replace( $search, $replace, $fullMatch ) ); + } // Normalise import statement to be without url() syntax: // This is so relative urls can easily be targeted later $statement = self::normalizeImportStatement( $statement ); - $replacements[ $fullMatch ] = $statement; + if ( $fullMatch !== $statement ) { + $replacements[ $fullMatch ] = $statement; + } } // If we've stored any altered @import strings then we need to apply them - if ( count( $replacements ) ) { + if ( $replacements ) { $import->content = str_replace( array_keys( $replacements ), array_values( $replacements ), $import->content ); } - // Now @import urls have been adjusted extract strings - $import->content = csscrush::extractStrings( $import->content ); - // Optionally rewrite relative url and custom function data-uri references if ( $options[ 'rewrite_import_urls' ] ) { $import->content = self::rewriteImportRelativeUrls( $import ); @@ -233,12 +261,13 @@ protected static function normalizeImportStatement ( $statement ) { protected static function resolveAbsolutePath ( $url ) { $config = csscrush::$config; + $process = csscrush::$process; if ( ! file_exists ( $config->docRoot . $url ) ) { return false; } // Move upwards '..' by the number of slashes in baseURL to get a relative path - $url = str_repeat( '../', substr_count( $config->baseURL, '/' ) ) . substr( $url, 1 ); + $url = str_repeat( '../', substr_count( $process->inputDirUrl, '/' ) ) . substr( $url, 1 ); return $url; } @@ -252,7 +281,7 @@ protected static function rewriteImportRelativeUrls ( $import ) { $hostDir = csscrush_util::normalizeSystemPath( $import->hostDir, true ); $importDir = csscrush_util::normalizeSystemPath( dirname( $import->path ), true ); - csscrush::$storage->tmp->relativeUrlPrefix = ''; + csscrush::$storage->misc->relativeUrlPrefix = ''; $url_prefix = ''; if ( $importDir === $hostDir ) { @@ -302,7 +331,7 @@ protected static function rewriteImportRelativeUrls ( $import ) { // and prepend $relative_url_prefix // Make $url_prefix accessible in callback scope - csscrush::$storage->tmp->relativeUrlPrefix = $url_prefix; + csscrush::$storage->misc->relativeUrlPrefix = $url_prefix; $url_function_patt = '! ([^a-z-]) # the preceeding character @@ -361,3 +390,4 @@ protected static function cb_rewriteImportRelativeUrl ( $match ) { } } + diff --git a/lib/Iterator.php b/lib/Iterator.php new file mode 100644 index 0000000..e836656 --- /dev/null +++ b/lib/Iterator.php @@ -0,0 +1,63 @@ +matches ); } + function next () { $this->index++; } + function rewind () { $this->index = 0; } + function valid () { return isset( $this->matches[ $this->index ] ); } + function key () { return $this->index; } + function current () { return $this->matches[ $this->index ]; } +} + + +class csscrush_atrule_iterator extends csscrush_block_iterator { + + + public function __construct ( $options ) { + + $this->options = (object) $options; + + $stream = $this->options->input; + $search = $this->options->search; + + $patt = '!' . $search . '\s?([^\{]*)\{!'; + preg_match_all( $patt, $stream, $this->matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER ); + + if ( $this->options->direction === 'reverse' ) { + $this->matches = array_reverse( $this->matches ); + } + } + + + function current () { + csscrush::log( $this->matches[ $this->index ] ); + + $match = $this->matches[ $this->index ]; + + $out = new stdclass(); + + $out->before = ''; + $out->after = ''; + $out->content = ''; + $out->arguments = ''; + + return $out; + } + +} + + +class csscrush_ruletoken_iterator extends csscrush_block_iterator { +} + + diff --git a/lib/Rule.php b/lib/Rule.php index 7e98948..92d27ef 100644 --- a/lib/Rule.php +++ b/lib/Rule.php @@ -19,10 +19,10 @@ public function __construct ( $selector_string = null, $declarations_string ) { $regex = csscrush::$regex; // Parse the selectors chunk - if ( !empty( $selector_string ) ) { + if ( ! empty( $selector_string ) ) { $selectors_match = csscrush_util::splitDelimList( $selector_string, ',' ); - $this->parens += $selectors_match->matches; + // $this->parens += $selectors_match->matches; // Remove and store comments that sit above the first selector // remove all comments between the other selectors @@ -35,85 +35,37 @@ public function __construct ( $selector_string = null, $declarations_string ) { $this->selectors = $selectors_match->list; } - // Apply any custom functions - $declarations_string = csscrush_function::parseAndExecuteValue( $declarations_string ); - // Parse the declarations chunk // Need to split safely as there are semi-colons in data-uris - $declarations_match = csscrush_util::splitDelimList( $declarations_string, ';' ); - $this->parens += $declarations_match->matches; + $declarations_match = csscrush_util::splitDelimList( $declarations_string, ';', true ); - // Parse declarations in to property/value pairs + // Split declarations in to property/value pairs foreach ( $declarations_match->list as $declaration ) { + // Strip comments around the property $declaration = preg_replace( $regex->token->comment, '', $declaration ); - // Store the property + // Extract the property part of the declaration $colonPos = strpos( $declaration, ':' ); if ( $colonPos === false ) { // If there is no colon it's malformed continue; } - - // The property name $prop = trim( substr( $declaration, 0, $colonPos ) ); - // Test for escape tilde - if ( $skip = strpos( $prop, '~' ) === 0 ) { - $prop = substr( $prop, 1 ); - } - // Store the property name - $this->addProperty( $prop ); - - // Store the property family - // Store the vendor id, if one is present - if ( preg_match( $regex->vendorPrefix, $prop, $vendor ) ) { - $family = $vendor[2]; - $vendor = $vendor[1]; - } - else { - $vendor = null; - $family = $prop; - } - // Extract the value part of the declaration $value = substr( $declaration, $colonPos + 1 ); $value = $value !== false ? trim( $value ) : $value; - if ( $value === false or $value === '' ) { - // We'll ignore declarations with empty values - continue; - } - - // Create an index of all functions in the current declaration - if ( preg_match_all( $regex->function->match, $value, $functions ) > 0 ) { - // csscrush::log( $functions ); - $out = array(); - foreach ( $functions[2] as $index => $fn_name ) { - $out[] = $fn_name; - } - $functions = array_unique( $out ); - } - else { - $functions = array(); - } - // Store the declaration - $_declaration = (object) array( - 'property' => $prop, - 'family' => $family, - 'vendor' => $vendor, - 'functions' => $functions, - 'value' => $value, - 'skip' => $skip, - ); - $this->declarations[] = $_declaration; + // Add declaration to the stack + $this->addDeclaration( $prop, $value ); } } public function addPropertyAliases () { $regex = csscrush::$regex; - $aliasedProperties =& csscrush::$aliases[ 'properties' ]; + $aliasedProperties =& csscrush::$config->aliases[ 'properties' ]; // First test for the existence of any aliased properties $intersect = array_intersect( array_keys( $aliasedProperties ), array_keys( $this->properties ) ); @@ -157,7 +109,7 @@ public function addPropertyAliases () { public function addFunctionAliases () { - $function_aliases =& csscrush::$aliases[ 'functions' ]; + $function_aliases =& csscrush::$config->aliases[ 'functions' ]; $aliased_functions = array_keys( $function_aliases ); if ( empty( $aliased_functions ) ) { @@ -239,7 +191,7 @@ public function addFunctionAliases () { public function addValueAliases () { - $aliasedValues =& csscrush::$aliases[ 'values' ]; + $aliasedValues =& csscrush::$config->aliases[ 'values' ]; // First test for the existence of any aliased properties $intersect = array_intersect( array_keys( $aliasedValues ), array_keys( $this->properties ) ); @@ -287,7 +239,7 @@ public function expandSelectors () { preg_match( '!:any(___p\d+___)!', $selector, $m ); // Parse the arguments - $expression = trim( $this->parens[ $m[1] ], '()' ); + $expression = trim( csscrush::$storage->tokens->parens[ $m[1] ], '()' ); $parts = preg_split( $reg_comma, $expression, null, PREG_SPLIT_NO_EMPTY ); $tmp = array(); @@ -339,7 +291,7 @@ public function propertyCount ( $prop ) { return 0; } - // Add property to the rule index keeping track of the count + public function addProperty ( $prop ) { if ( isset( $this->properties[ $prop ] ) ) { $this->properties[ $prop ]++; @@ -349,27 +301,199 @@ public function addProperty ( $prop ) { } } - public function createDeclaration ( $property, $value, $options = array() ) { + + public function addDeclaration ( $prop, $value ) { + + // $regex = csscrush::$regex; + // + // // Check the input + // if ( empty( $prop ) or $value === '' or $value === null ) { + // return false; + // } + // + // // Test for escape tilde + // if ( $skip = strpos( $prop, '~' ) === 0 ) { + // $prop = substr( $prop, 1 ); + // } + // + // // Store the property family + // // Store the vendor id, if one is present + // if ( preg_match( $regex->vendorPrefix, $prop, $vendor ) ) { + // $family = $vendor[2]; + // $vendor = $vendor[1]; + // } + // else { + // $vendor = null; + // $family = $prop; + // } + // + // // Check for !important keywords + // if ( ( $important = strpos( $value, '!important' ) ) !== false ) { + // $value = substr( $value, 0, $important ); + // $important = true; + // } + // + // // Ignore declarations with null css values + // if ( $value === false or $value === '' ) { + // return false; + // } + // + // // Apply custom functions + // if ( ! $skip ) { + // $value = csscrush_function::parseAndExecuteValue( $value ); + // } + // + // // Tokenize all remaining paren pairs + // $match_obj = csscrush_util::matchAllBrackets( $value ); + // $this->parens += $match_obj->matches; + // $value = $match_obj->string; + // + // + // // Create an index of all regular functions in the value + // if ( preg_match_all( $regex->function->match, $value, $functions ) > 0 ) { + // $out = array(); + // foreach ( $functions[2] as $index => $fn_name ) { + // $out[] = $fn_name; + // } + // $functions = array_unique( $out ); + // } + // else { + // $functions = array(); + // } + // + // // Store the declaration + // $_declaration = (object) array( + // 'property' => $prop, + // 'family' => $family, + // 'vendor' => $vendor, + // 'functions' => $functions, + // 'value' => $value, + // 'skip' => $skip, + // 'important' => $important, + // ); + + + + // Store the property name + $this->addProperty( $prop ); + + // Add declaration to the stack + $_declaration = new csscrush_declaration( $prop, $value ); + $this->declarations[] = $_declaration; + + return $_declaration; + } + + // + // // Get a declaration value without paren tokens + // public function getDeclarationValue ( $declaration ) { + // // $paren_keys = array_keys( $this->parens ); + // // $paren_values = array_values( $this->parens ); + // + // return csscrush::tokenReplace( $declaration->value, $token_replace ); + // + // return str_replace( $paren_keys, $paren_values, $declaration->value ); + // } + +} + + +class csscrush_declaration { + + public $property; + public $family; + public $vendor; + public $functions; + public $value; + public $skip; + public $important; + public $parenTokens; + + public function __construct ( $prop, $value ) { + + $regex = csscrush::$regex; + + // Check the input + if ( empty( $prop ) or $value === '' or $value === null ) { + return false; + } + // Test for escape tilde - if ( $skip = strpos( $property, '~' ) === 0 ) { - $property = substr( $property, 1 ); + if ( $skip = strpos( $prop, '~' ) === 0 ) { + $prop = substr( $prop, 1 ); + } + + // Store the property family + // Store the vendor id, if one is present + if ( preg_match( $regex->vendorPrefix, $prop, $vendor ) ) { + $family = $vendor[2]; + $vendor = $vendor[1]; + } + else { + $vendor = null; + $family = $prop; + } + + // Check for !important keywords + if ( ( $important = strpos( $value, '!important' ) ) !== false ) { + $value = substr( $value, 0, $important ); + $important = true; + } + + // Ignore declarations with null css values + if ( $value === false or $value === '' ) { + return false; + } + + // Apply custom functions + if ( ! $skip ) { + $value = csscrush_function::parseAndExecuteValue( $value ); } - $_declaration = array( - 'property' => $property, - 'family' => null, - 'vendor' => null, - 'value' => $value, - 'skip' => $skip, - ); - $this->addProperty( $property ); - return (object) array_merge( $_declaration, $options ); + + // Tokenize all remaining paren pairs + $match_obj = csscrush_util::matchAllBrackets( $value ); + $this->parenTokens = $match_obj->matches; + $value = $match_obj->string; + + + // Create an index of all regular functions in the value + if ( preg_match_all( $regex->function->match, $value, $functions ) > 0 ) { + $out = array(); + foreach ( $functions[2] as $index => $fn_name ) { + $out[] = $fn_name; + } + $functions = array_unique( $out ); + } + else { + $functions = array(); + } + + // // Store the declaration + // $_declaration = (object) array( + // 'property' => $prop, + // 'family' => $family, + // 'vendor' => $vendor, + // 'functions' => $functions, + // 'value' => $value, + // 'skip' => $skip, + // 'important' => $important, + // ); + + $this->property = $prop; + $this->family = $family; + $this->vendor = $vendor; + $this->functions = $functions; + $this->value = $value; + $this->skip = $skip; + $this->important = $important; } + + public function getFullValue () { - // Get a declaration value without paren tokens - public function getDeclarationValue ( $declaration ) { - $paren_keys = array_keys( $this->parens ); - $paren_values = array_values( $this->parens ); - return str_replace( $paren_keys, $paren_values, $declaration->value ); + return csscrush::tokenReplace( $this->value, $this->parenTokens ); } -} \ No newline at end of file +} + + + diff --git a/lib/Util.php b/lib/Util.php index df2be4d..a68c520 100644 --- a/lib/Util.php +++ b/lib/Util.php @@ -31,7 +31,7 @@ public static function normalizeSystemPath ( $path, $stripMsDos = false ) { public static function find () { foreach ( func_get_args() as $file ) { - $file_path = csscrush::$location . '/' . $file; + $file_path = csscrush::$config->location . '/' . $file; if ( file_exists( $file_path ) ) { return $file_path; } @@ -54,7 +54,7 @@ public static function normalizeWhiteSpace ( $str ) { public static function splitDelimList ( $str, $delim, $fold_in = false, $allow_empty = false ) { $match_obj = self::matchAllBrackets( $str ); - + // If the delimiter is one character do a simple split // Otherwise do a regex split if ( 1 === strlen( $delim ) ) { @@ -63,16 +63,21 @@ public static function splitDelimList ( $str, $delim, $fold_in = false, $allow_e else { $match_obj->list = preg_split( '!' . $delim . '!', $match_obj->string ); } - + if ( false === $allow_empty ) { $match_obj->list = array_filter( $match_obj->list ); } if ( $fold_in ) { - $match_keys = array_keys( $match_obj->matches ); - $match_values = array_values( $match_obj->matches ); + // $match_keys = array_keys( $match_obj->matches ); + // $match_values = array_values( $match_obj->matches ); + // foreach ( $match_obj->list as &$item ) { + // $item = str_replace( $match_keys, $match_values, $item ); + // } + foreach ( $match_obj->list as &$item ) { - $item = str_replace( $match_keys, $match_values, $item ); + $item = csscrush::tokenReplace( $item, $match_obj->matches ); } + } return $match_obj; } @@ -85,7 +90,7 @@ public static function matchBrackets ( $str, $brackets = array( '(', ')' ), $sea $closings = array(); $brake = 50; // Set a limit in the case of errors - $match = new stdClass; + $match = new stdclass(); $start_index = strpos( $str, $opener, $search_pos ); $close_index = strpos( $str, $closer, $search_pos ); @@ -139,7 +144,7 @@ public static function matchBrackets ( $str, $brackets = array( '(', ')' ), $sea public static function matchAllBrackets ( $str, $pair = '()', $offset = 0 ) { - $match_obj = new stdClass; + $match_obj = new stdclass(); $match_obj->string = $str; $match_obj->raw = $str; $match_obj->matches = array(); @@ -161,7 +166,7 @@ public static function matchAllBrackets ( $str, $pair = '()', $offset = 0 ) { $char = $str[ $index ]; if ( $opener === $char ) { - if ( !$inside_paren ) { + if ( ! $inside_paren ) { $paren_score = 1; $match_start = $index; } @@ -189,9 +194,10 @@ public static function matchAllBrackets ( $str, $pair = '()', $offset = 0 ) { $content = substr( $str, $start, $finish - $start ); $after = substr( $str, $finish ); - $label = csscrush::createTokenLabel( 'p' ); + $label = csscrush::tokenLabelCreate( 'p' ); $str = $before . $label . $after; - $match_obj->matches[ $label ] = $content; + // $match_obj->matches[ $label ] = $content; + $match_obj->matches[] = $label; // Parens will be folded in later csscrush::$storage->tokens->parens[ $label ] = $content; @@ -225,7 +231,7 @@ public function __construct ( $token ) { } public function update ( $newValue ) { - csscrush::$storage->tokens->strings = $newValue; + csscrush::$storage->tokens->strings[ $this->token ] = $newValue; } } diff --git a/misc/initial-values.ini b/misc/initial-values.ini index 8ef34e5..7288444 100644 --- a/misc/initial-values.ini +++ b/misc/initial-values.ini @@ -133,8 +133,8 @@ top = "auto" transform = "none" transform-style = "flat" transition = "none" -transition-delay = "0" -transition-duration = "0" +transition-delay = "0s" +transition-duration = "0s" transition-property = "none" transition-timing-function = "ease" unicode-bidi = "normal" diff --git a/plugins/double-colon.php b/plugins/double-colon.php index b480e8a..ab64003 100644 --- a/plugins/double-colon.php +++ b/plugins/double-colon.php @@ -11,7 +11,7 @@ * */ -CssCrush_Hook::add( 'rule_preprocess', 'csscrush_doublecolon' ); +csscrush_hook::add( 'rule_preprocess', 'csscrush_doublecolon' ); function csscrush_doublecolon ( $rule ) { $rule->selector_raw = preg_replace( '!::(after|before|first-letter|first-line)!', ':$1', $rule->selector_raw ); diff --git a/plugins/hocus-pocus.php b/plugins/hocus-pocus.php index 50a5593..8e8af47 100644 --- a/plugins/hocus-pocus.php +++ b/plugins/hocus-pocus.php @@ -13,7 +13,7 @@ * */ -CssCrush_Hook::add( 'rule_preprocess', 'csscrush_hocuspocus' ); +csscrush_hook::add( 'rule_preprocess', 'csscrush_hocuspocus' ); function csscrush_hocuspocus ( $rule ) { $adjustments = array( diff --git a/plugins/hsl-to-hex.php b/plugins/hsl-to-hex.php index 47d85ee..bed3d4b 100644 --- a/plugins/hsl-to-hex.php +++ b/plugins/hsl-to-hex.php @@ -10,18 +10,18 @@ * color: #6abf40 */ -CssCrush_Hook::add( 'rule_postalias', 'csscrush_hsl' ); +csscrush_hook::add( 'rule_postalias', 'csscrush_hsl' ); function csscrush_hsl ( CssCrush_Rule $rule ) { foreach ( $rule as &$declaration ) { if ( - !$declaration->skip and - ( !empty( $declaration->functions ) and in_array( 'hsl', $declaration->functions ) ) + ! $declaration->skip and + ( ! empty( $declaration->functions ) and in_array( 'hsl', $declaration->functions ) ) ) { while ( preg_match( '!hsl(___p\d+___)!', $declaration->value, $m ) ) { $full_match = $m[0]; $token = $m[1]; - $hsl = trim( $rule->parens[ $token ], '()' ); + $hsl = trim( csscrush::$storage->tokens->parens[ $token ], '()' ); $hsl = array_map( 'trim', explode( ',', $hsl ) ); $rgb = CssCrush_Color::cssHslToRgb( $hsl ); $hex = CssCrush_Color::rgbToHex( $rgb ); diff --git a/plugins/ie-clip.php b/plugins/ie-clip.php index 2316522..ed95657 100644 --- a/plugins/ie-clip.php +++ b/plugins/ie-clip.php @@ -10,7 +10,7 @@ * *clip: rect(1px 1px 1px 1px); */ -CssCrush_Hook::add( 'rule_postalias', 'csscrush_clip' ); +csscrush_hook::add( 'rule_postalias', 'csscrush_clip' ); function csscrush_clip ( CssCrush_Rule $rule ) { // Assume it's been dealt with if the property occurs more than once @@ -26,8 +26,7 @@ function csscrush_clip ( CssCrush_Rule $rule ) { ) { continue; } - $new_set[] = $rule->createDeclaration( - '*clip', str_replace( ',', ' ', $rule->getDeclarationValue( $declaration ) ) ); + $new_set[] = $rule->addDeclaration( '*clip', str_replace( ',', ' ', $declaration->getFullValue() ) ); } $rule->declarations = $new_set; } \ No newline at end of file diff --git a/plugins/ie-filter.php b/plugins/ie-filter.php index 2bcd06c..05da1fe 100644 --- a/plugins/ie-filter.php +++ b/plugins/ie-filter.php @@ -16,7 +16,7 @@ * zoom: 1; */ -CssCrush_Hook::add( 'rule_postalias', 'csscrush_filter' ); +csscrush_hook::add( 'rule_postalias', 'csscrush_filter' ); function csscrush_filter ( CssCrush_Rule $rule ) { if ( $rule->propertyCount( '-ms-filter' ) < 1 ) { @@ -44,12 +44,12 @@ function csscrush_filter ( CssCrush_Rule $rule ) { $declaration->value = implode( ',', $list ); if ( !$rule->propertyCount( 'zoom' ) ) { // Filters need hasLayout - $new_set[] = $rule->createDeclaration( 'zoom', 1 ); + $new_set[] = $rule->addDeclaration( 'zoom', 1 ); } // Quoted version for -ms-filter IE >= 8 - $new_set[] = $rule->createDeclaration( '-ms-filter', "\"$declaration->value\"" ); + $new_set[] = $rule->addDeclaration( '-ms-filter', "\"$declaration->value\"" ); // Star escaped property for IE < 8 - $new_set[] = $rule->createDeclaration( '*filter', $declaration->value ); + $new_set[] = $rule->addDeclaration( '*filter', $declaration->value ); } $rule->declarations = $new_set; } \ No newline at end of file diff --git a/plugins/ie-inline-block.php b/plugins/ie-inline-block.php index 0fbf415..9bb885e 100644 --- a/plugins/ie-inline-block.php +++ b/plugins/ie-inline-block.php @@ -11,7 +11,7 @@ * *zoom: 1; */ -CssCrush_Hook::add( 'rule_postalias', 'csscrush_display_inlineblock' ); +csscrush_hook::add( 'rule_postalias', 'csscrush_display_inlineblock' ); function csscrush_display_inlineblock ( CssCrush_Rule $rule ) { if ( $rule->propertyCount( 'display' ) < 1 ) { @@ -27,8 +27,8 @@ function csscrush_display_inlineblock ( CssCrush_Rule $rule ) { $is_display and $declaration->value !== 'inline-block' ) { continue; } - $new_set[] = $rule->createDeclaration( '*display', 'inline' ); - $new_set[] = $rule->createDeclaration( '*zoom', 1 ); + $new_set[] = $rule->addDeclaration( '*display', 'inline' ); + $new_set[] = $rule->addDeclaration( '*zoom', 1 ); } $rule->declarations = $new_set; } \ No newline at end of file diff --git a/plugins/ie-min-height.php b/plugins/ie-min-height.php index 11d3f0f..4a85ccd 100644 --- a/plugins/ie-min-height.php +++ b/plugins/ie-min-height.php @@ -10,7 +10,7 @@ * _height: 100px; */ -CssCrush_Hook::add( 'rule_postalias', 'csscrush_minheight' ); +csscrush_hook::add( 'rule_postalias', 'csscrush_minheight' ); function csscrush_minheight ( CssCrush_Rule $rule ) { if ( $rule->propertyCount( 'min-height' ) < 1 ) { @@ -24,7 +24,7 @@ function csscrush_minheight ( CssCrush_Rule $rule ) { $declaration->property !== 'min-height' ) { continue; } - $new_set[] = $rule->createDeclaration( '_height', $declaration->value ); + $new_set[] = $rule->addDeclaration( '_height', $declaration->value ); } $rule->declarations = $new_set; } \ No newline at end of file diff --git a/plugins/ie-opacity.php b/plugins/ie-opacity.php index 1504f32..69f0d2e 100755 --- a/plugins/ie-opacity.php +++ b/plugins/ie-opacity.php @@ -12,7 +12,7 @@ * zoom: 1; */ -CssCrush_Hook::add( 'rule_postalias', 'csscrush_opacity' ); +csscrush_hook::add( 'rule_postalias', 'csscrush_opacity' ); function csscrush_opacity ( CssCrush_Rule $rule ) { if ( $rule->propertyCount( 'opacity' ) < 1 ) { @@ -33,11 +33,11 @@ function csscrush_opacity ( CssCrush_Rule $rule ) { if ( !$rule->propertyCount( 'zoom' ) ) { // Filters need hasLayout - $new_set[] = $rule->createDeclaration( 'zoom', 1 ); + $new_set[] = $rule->addDeclaration( 'zoom', 1 ); } $value = "alpha(opacity=$opacity)"; - $new_set[] = $rule->createDeclaration( '-ms-filter', "\"$value\"" ); - $new_set[] = $rule->createDeclaration( '*filter', $value ); + $new_set[] = $rule->addDeclaration( '-ms-filter', "\"$value\"" ); + $new_set[] = $rule->addDeclaration( '*filter', $value ); } $rule->declarations = $new_set; } \ No newline at end of file diff --git a/plugins/initial.php b/plugins/initial.php index 12df893..3112a12 100644 --- a/plugins/initial.php +++ b/plugins/initial.php @@ -15,13 +15,13 @@ * */ -CssCrush_Hook::add( 'rule_prealias', 'csscrush_initial' ); +csscrush_hook::add( 'rule_prealias', 'csscrush_initial' ); function csscrush_initial ( CssCrush_Rule $rule ) { static $initialValues = null; if ( ! $initialValues ) { - if ( ! ( $initialValues = @parse_ini_file( CssCrush::$location . '/misc/initial-values.ini' ) ) ) { + if ( ! ( $initialValues = @parse_ini_file( CssCrush::$config->location . '/misc/initial-values.ini' ) ) ) { trigger_error( __METHOD__ . ": Initial keywords file could not be parsed.\n", E_USER_NOTICE ); return; } diff --git a/plugins/rgba-fallback.php b/plugins/rgba-fallback.php index 71da318..c5933eb 100644 --- a/plugins/rgba-fallback.php +++ b/plugins/rgba-fallback.php @@ -12,7 +12,7 @@ * background: rgba(0,0,0,.5); */ -CssCrush_Hook::add( 'rule_postalias', 'csscrush_rgba' ); +csscrush_hook::add( 'rule_postalias', 'csscrush_rgba' ); function csscrush_rgba ( CssCrush_Rule $rule ) { $props = array_keys( $rule->properties ); @@ -40,12 +40,12 @@ function csscrush_rgba ( CssCrush_Rule $rule ) { continue; } // Create rgb value from rgba - $raw_value = $rule->getDeclarationValue( $declaration ); + $raw_value = $declaration->getFullValue(); $raw_value = substr( $raw_value, 5, strlen( $raw_value ) - 1 ); list( $r, $g, $b, $a ) = explode( ',', $raw_value ); // Add rgb value to the stack, followed by rgba - $new_set[] = $rule->createDeclaration( $declaration->property, "rgb($r,$g,$b)" ); + $new_set[] = $rule->addDeclaration( $declaration->property, "rgb($r,$g,$b)" ); $new_set[] = $declaration; } $rule->declarations = $new_set; diff --git a/plugins/spiffing.php b/plugins/spiffing.php new file mode 100644 index 0000000..2a0b5d6 --- /dev/null +++ b/plugins/spiffing.php @@ -0,0 +1,24 @@ +declaration_raw = str_ireplace( $find, $replace, $rule->declaration_raw ); +} From 1045306770b280612c2eb50e47cc56c4d7e5e47d Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 8 May 2012 13:42:55 +0100 Subject: [PATCH 003/421] Bulk of changes for v1.5 New feature: Mixins New feature: Fragments New feature: Block nesting New feature: Rule inheritance / abstract rules Abstracted IO interface Added some error reporting Added spiffing.css plugin csscrush_tag method now uses media type 'all' by default Updated Aliases.ini / initial-values.ini Internal refactoring Resolved issue #23 Resolved issue #24 Resolved issue #27 Resolved issue #28 Resolved issue #29 --- Aliases.ini | 3 + CHANGELOG => CHANGELOG.txt | 20 +- CssCrush.php | 26 +- LICENSE.txt | 7 + README.md | 4 +- lib/Core.php | 530 +++++++++++++++++++++++++----------- lib/Function.php | 56 ++-- lib/IO.php | 42 ++- lib/Importer.php | 22 +- lib/Iterator.php | 63 ----- lib/Mixin.php | 268 ++++++++++++++++++ lib/Regex.php | 96 +++++++ lib/Rule.php | 421 ++++++++++++++++++---------- lib/Util.php | 111 ++++++-- misc/initial-values.ini | 1 + plugins/hsl-to-hex.php | 11 +- plugins/ie-clip.php | 10 +- plugins/ie-filter.php | 14 +- plugins/ie-inline-block.php | 12 +- plugins/ie-min-height.php | 6 +- plugins/ie-opacity.php | 12 +- plugins/initial.php | 4 +- plugins/rgba-fallback.php | 14 +- 23 files changed, 1266 insertions(+), 487 deletions(-) rename CHANGELOG => CHANGELOG.txt (86%) create mode 100644 LICENSE.txt delete mode 100644 lib/Iterator.php create mode 100644 lib/Mixin.php create mode 100644 lib/Regex.php diff --git a/Aliases.ini b/Aliases.ini index bdc4bc3..ddf1080 100644 --- a/Aliases.ini +++ b/Aliases.ini @@ -137,6 +137,9 @@ tab-size[] = -moz-tab-size tab-size[] = -o-tab-size + ; Text align last + text-align-last[] = -moz-text-align-last + ; Text decoration text-decoration-color[] = -moz-text-decoration-color text-decoration-line[] = -moz-text-decoration-line diff --git a/CHANGELOG b/CHANGELOG.txt similarity index 86% rename from CHANGELOG rename to CHANGELOG.txt index 725f770..ffde177 100644 --- a/CHANGELOG +++ b/CHANGELOG.txt @@ -1,6 +1,20 @@ -1.4.3 ------ - +1.5 +--- +New feature: Mixins +New feature: Fragments +New feature: Block nesting +New feature: Rule inheritance / abstract rules +Abstracted IO interface +Added some error reporting +Added spiffing.css plugin +csscrush_tag method now uses media type 'all' by default +Updated Aliases.ini / initial-values.ini +Internal refactoring +Resolved issue #23 +Resolved issue #24 +Resolved issue #27 +Resolved issue #28 +Resolved issue #29 1.4.2 diff --git a/CssCrush.php b/CssCrush.php index 0c89af5..39dd8e5 100644 --- a/CssCrush.php +++ b/CssCrush.php @@ -4,38 +4,28 @@ * CSS Crush * Extensible CSS preprocessor * - * @version 1.4.3 + * @version 1.5 + * @link https://github.com/peteboere/css-crush * @license http://www.opensource.org/licenses/mit-license.php (MIT) * @copyright Copyright 2010-2012 Pete Boere - * - * - * - * - * * */ require_once 'lib/Util.php'; require_once 'lib/IO.php'; require_once 'lib/Core.php'; -CssCrush::init( dirname( __FILE__ ) ); - require_once 'lib/Rule.php'; - +require_once 'lib/Mixin.php'; require_once 'lib/Function.php'; -CssCrush_Function::init(); - require_once 'lib/Importer.php'; -require_once 'lib/Iterator.php'; require_once 'lib/Color.php'; +require_once 'lib/Regex.php'; require_once 'lib/Hook.php'; +csscrush::init( dirname( __FILE__ ) ); + + + diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..cfc8a18 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,7 @@ +Copyright (c) 2010-2012 Pete Boere + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md index aebd534..857a709 100644 --- a/README.md +++ b/README.md @@ -16,11 +16,11 @@ Quick start - + Submitting bugs diff --git a/lib/Core.php b/lib/Core.php index bd01ac0..bbbb3b2 100644 --- a/lib/Core.php +++ b/lib/Core.php @@ -19,44 +19,6 @@ class csscrush { protected static $assetsLoaded = false; protected static $tokenUID; - // Regular expressions - public static $regex = array( - 'import' => '! - @import\s+ # import at-rule - (?: - url\(\s*([^\)]+)\s*\) # url function - | # or - ([_s\d]+) # string token - ) - \s*([^;]*);? # media argument - !x', - 'variables' => '!@(?:variables|define)\s*([^\{]*)\{\s*(.*?)\s*\};?!s', - 'atRule' => '!@([-a-z_]+)\s*([^\{]*)\{\s*(.*?)\s*\};?!s', - 'comment' => '!/\*(.*?)\*/!s', - 'string' => '!(\'|"|`)(?:\\1|[^\1])*?\1!', - // As an exception we treat @font-face and @page rules like standard rules - 'rule' => '! - (\n(?:[^@{}]+|@(?:font-face|page)[^{]*)) # The selector - \{([^{}]*)\} # The declaration block - !x', - 'token' => array( - 'comment' => '!___c\d+___!', - 'string' => '!___s\d+___!', - 'rule' => '!___r\d+___!', - 'paren' => '!___p\d+___!', - ), - 'function' => array( - 'var' => '!(?: - ([^a-z0-9_-]) - var\(\s*([a-z0-9_-]+)\s*\) - | - \$\(\s*([a-z0-9_-]+)\s*\) # Dollar syntax - )!ix', - 'match' => '!(^|[^a-z0-9_-])([a-z_-]+)(___p\d+___)!i', - ), - 'vendorPrefix' => '!^-([a-z]+)-([a-z-]+)!', - 'absoluteUrl' => '!^https?://!', - ); // Init called once manually post class definition public static function init ( $current_dir ) { @@ -66,18 +28,8 @@ public static function init ( $current_dir ) { // Path to this installation self::$config->location = $current_dir; - // Get normalized document root reference: no symlink, forward slashes, no trailing slashes - $doc_root = null; - if ( isset( $_SERVER[ 'DOCUMENT_ROOT' ] ) ) { - $doc_root = realpath( $_SERVER[ 'DOCUMENT_ROOT' ] ); - } - else { - // Probably IIS - $scriptname = $_SERVER[ 'SCRIPT_NAME' ]; - $fullpath = realpath( basename( $scriptname ) ); - $doc_root = substr( $fullpath, 0, stripos( $fullpath, $scriptname ) ); - } - self::$config->docRoot = csscrush_util::normalizeSystemPath( $doc_root ); + // Set the docRoot reference + self::setDocRoot(); // Set the default IO handler self::$config->io = 'csscrush_io'; @@ -88,10 +40,54 @@ public static function init ( $current_dir ) { self::$config->aliases = array(); self::$config->aliasesRaw = array(); - // Casting to objects for ease of use - self::$regex = (object) self::$regex; - self::$regex->token = (object) self::$regex->token; - self::$regex->function = (object) self::$regex->function; + // Initialise other classes + csscrush_regex::init(); + csscrush_function::init(); + csscrush_arglist::init(); + } + + + public static function setDocRoot ( $doc_root = null ) { + + // Get document_root reference + // $_SERVER['DOCUMENT_ROOT'] is unreliable in certain CGI/Apache/IIS setups + + if ( ! $doc_root ) { + + $script_filename = $_SERVER[ 'SCRIPT_FILENAME' ]; + $script_name = $_SERVER[ 'SCRIPT_NAME' ]; + + if ( $script_filename && $script_name ) { + + $len_diff = strlen( $script_filename ) - strlen( $script_name ); + + // We're comparing the two strings so normalize OS directory separators + $script_filename = str_replace( '\\', '/', $script_filename ); + $script_name = str_replace( '\\', '/', $script_name ); + + // Check $script_filename ends with $script_name + if ( substr( $script_filename, $len_diff ) === $script_name ) { + + $doc_root = realpath( substr( $script_filename, 0, $len_diff ) ); + } + } + + if ( ! $doc_root ) { + + // If doc_root is still falsy, fallback to DOCUMENT_ROOT + $doc_root = realpath( $_SERVER[ 'DOCUMENT_ROOT' ] ); + } + + if ( ! $doc_root ) { + + // If doc_root is still falsy, log an error + $error = "Could not get a document_root reference."; + csscrush::logError( $error ); + trigger_error( __METHOD__ . ": $error\n", E_USER_NOTICE ); + } + } + + self::$config->docRoot = csscrush_util::normalizeSystemPath( $doc_root ); } @@ -113,7 +109,7 @@ public static function io_call ( $method ) { protected static function loadAssets () { // Find an aliases file in the root directory - // a local file will overrides the default + // a local file overrides the default $aliases_file = csscrush_util::find( 'Aliases-local.ini', 'Aliases.ini' ); // Load aliases file if it exists @@ -140,7 +136,7 @@ protected static function loadAssets () { } // Find a plugins file in the root directory - // a local file will overrides the default + // a local file overrides the default $plugins_file = csscrush_util::find( 'Plugins-local.ini', 'Plugins.ini' ); // Load plugins @@ -179,7 +175,7 @@ protected static function setPath ( $input_dir, $write_test = true ) { // Store input directory $process->inputDir = $input_dir; $process->inputDirUrl = substr( $process->inputDir, strlen( $doc_root ) ); - + // Store reference to the output dir $process->outputDir = csscrush::io_call( 'getOutputDir' ); $process->outputDirUrl = substr( $process->outputDir, strlen( $doc_root ) ); @@ -216,7 +212,7 @@ public static function file ( $file, $options = null ) { // Since we're comparing strings, we need to iron out OS differences $file = str_replace( '\\', '/', $file ); - + // Finding the system path of the input file and validating it $pathtest = true; if ( strpos( $file, $doc_root ) === 0 ) { @@ -288,18 +284,25 @@ public static function tag ( $file, $options = null, $attributes = array() ) { $file = self::file( $file, $options ); if ( ! empty( $file ) ) { - + // On success return the tag with any custom attributes - $attributes[ 'rel' ] = "stylesheet"; + $attributes[ 'rel' ] = 'stylesheet'; $attributes[ 'href' ] = $file; + + // Should media type be forced to 'all'? + if ( ! isset( $attributes[ 'media' ] ) ) { + $attributes[ 'media' ] = 'all'; + } + $attr_string = csscrush_util::htmlAttributes( $attributes ); return "\n"; } else { - + // Return an HTML comment with message on failure $class = __CLASS__; - return "\n"; + $errors = implode( "\n", self::$process->errors ); + return "\n"; } } @@ -316,12 +319,12 @@ public static function inline ( $file, $options = null, $attributes = array() ) $file = self::file( $file, $options ); if ( ! empty( $file ) ) { - + // On success fetch the CSS text $content = file_get_contents( self::$process->outputDir . '/' . self::$process->outputFileName ); $tag_open = ''; $tag_close = ''; - + if ( is_array( $attributes ) ) { $attr_string = csscrush_util::htmlAttributes( $attributes ); $tag_open = ""; @@ -333,7 +336,8 @@ public static function inline ( $file, $options = null, $attributes = array() ) // Return an HTML comment with message on failure $class = __CLASS__; - return "\n"; + $errors = implode( "\n", self::$process->errors ); + return "\n"; } } @@ -382,15 +386,15 @@ public static function string ( $string, $options = null ) { * @param mixed $var Assoc array of variable names and values, a php ini filename or null */ public static function globalVars ( $vars ) { - + $config = self::$config; - + // Merge into the stack, overrides existing variables of the same name if ( is_array( $vars ) ) { $config->vars = array_merge( $config->vars, $vars ); } // Test for a file. If it is attempt to parse it - elseif ( is_string( $vars ) and file_exists( $vars ) ) { + elseif ( is_string( $vars ) && file_exists( $vars ) ) { if ( $result = parse_ini_file( $vars ) ) { $config->vars = array_merge( $config->vars, $result ); } @@ -444,6 +448,11 @@ public static function log () { } } + public static function logError ( $msg ) { + self::$process->errors[] = $msg; + self::log( $msg ); + } + ##################### # Internal functions @@ -452,7 +461,7 @@ protected static function getBoilerplate () { $file = csscrush_util::find( 'CssCrush-local.boilerplate', 'CssCrush.boilerplate' ); - if ( ! $file or ! self::$options[ 'boilerplate' ] ) { + if ( ! $file || ! self::$options[ 'boilerplate' ] ) { return ''; } @@ -580,8 +589,6 @@ protected static function pruneAliases () { protected static function calculateVariables () { - $regex = self::$regex; - // In-file variables override global variables // Runtime variables override in-file variables self::$storage->variables = array_merge( self::$config->vars, self::$storage->variables ); @@ -596,7 +603,7 @@ protected static function calculateVariables () { foreach ( self::$storage->variables as $name => &$value ) { // Referenced variables $value = preg_replace_callback( - $regex->function->var, array( 'self', 'cb_placeVariables' ), $value ); + csscrush_regex::$patt->varFunction, array( 'self', 'cb_placeVariables' ), $value ); // Custom functions: // Variable values can be escaped from function parsing with a tilde prefix @@ -610,13 +617,16 @@ protected static function calculateVariables () { } protected static function placeVariables ( $stream ) { + $stream = preg_replace_callback( - self::$regex->function->var, array( 'self', 'cb_placeVariables' ), $stream ); + csscrush_regex::$patt->varFunction, array( 'self', 'cb_placeVariables' ), $stream ); + // Place variables in any string tokens foreach ( self::$storage->tokens->strings as $label => &$string ) { if ( strpos( $string, '$' ) !== false ) { $string = preg_replace_callback( - self::$regex->function->var, array( 'self', 'cb_placeVariables' ), $string ); + csscrush_regex::$patt->varFunction, + array( 'self', 'cb_placeVariables' ), $string ); } } return $stream; @@ -626,31 +636,36 @@ protected static function reset ( $options = null ) { // Reset properties for current process self::$tokenUID = 0; - + self::$process = new stdclass(); self::$process->cacheData = array(); - + self::$process->mixins = array(); + self::$process->fragments = array(); + self::$process->abstracts = array(); + self::$process->errors = array(); + self::$process->selectorRelationships = array(); + self::$storage = new stdclass(); self::$storage->tokens = (object) array( - 'strings' => array(), - 'comments' => array(), - 'rules' => array(), - 'parens' => array(), + 'strings' => array(), + 'comments' => array(), + 'rules' => array(), + 'parens' => array(), + 'mixinArgs' => array(), ); self::$storage->variables = array(); self::$storage->misc = new stdclass(); - + // Load the merged options self::$options = self::getOptions( $options ); } protected static function compile ( $stream ) { - $regex = self::$regex; $options = self::$options; // Load in aliases and macros - if ( !self::$assetsLoaded ) { + if ( ! self::$assetsLoaded ) { self::loadAssets(); self::$assetsLoaded = true; } @@ -664,11 +679,16 @@ protected static function compile ( $stream ) { // Calculate the variable stack self::calculateVariables(); - // self::log( self::$storage->variables ); // Place the variables $stream = self::placeVariables( $stream ); + // Pull out the mixin declarations + $stream = self::extractMixins( $stream ); + + // Pull out the fragments + $stream = self::extractFragments( $stream ); + // Normalize whitespace $stream = csscrush_util::normalizeWhiteSpace( $stream ); @@ -684,26 +704,19 @@ protected static function compile ( $stream ) { // Rules $stream = self::extractAndProcessRules( $stream ); - // Alias at-rules (if there are any) - $stream = self::aliasAtRules( $stream ); + // Process any @-in blocks + $stream = self::prefixSelectors( $stream ); + // Main processing on the rule objects + self::processRules(); - - $iterator = new csscrush_atrule_iterator( array( - 'input' => $stream, - 'search' => '@prefix', - 'direction' => 'reverse', - )); - - // csscrush::log( $iterator ); - // csscrush::log( count( $iterator ) ); - - foreach ( $iterator as $atrule ) { - // self::log( $value ); - } + csscrush::log( csscrush::$storage->tokens->rules ); + csscrush::log( array_keys( self::$process->selectorRelationships ) ); + // Alias any @-rules + $stream = self::aliasAtRules( $stream ); - // print it all back + // Print it all back $stream = self::display( $stream ); // Add in boilerplate @@ -711,8 +724,6 @@ protected static function compile ( $stream ) { $stream = self::getBoilerplate() . "\n$stream"; } - // self::log( self::$config->cacheData ); - // Release memory self::$storage = null; @@ -722,10 +733,10 @@ protected static function compile ( $stream ) { protected static function display ( $stream ) { $minify = ! self::$options[ 'debug' ]; - $regex = self::$regex; + $regex = csscrush_regex::$patt; if ( $minify ) { - $stream = preg_replace( $regex->token->comment, '', $stream ); + $stream = csscrush_util::stripComments( $stream ); } else { // Create newlines after tokens @@ -741,7 +752,7 @@ protected static function display ( $stream ) { $stream = preg_replace( '!\n\s+!', "\n", $stream ); // Print out rules - $stream = preg_replace_callback( $regex->token->rule, array( 'self', 'cb_printRule' ), $stream ); + $stream = preg_replace_callback( $regex->ruleToken, array( 'self', 'cb_printRule' ), $stream ); // Insert parens $paren_labels = array_keys( self::$storage->tokens->parens ); @@ -774,14 +785,15 @@ protected static function display ( $stream ) { protected static function minify ( $str ) { $replacements = array( - '!\n+| (\{)!' => '$1', // Trim whitespace - '!(^|[: \(,])0(\.\d+)!' => '$1$2', // Strip leading zeros on floats - '!(^|[: \(,])\.?0(?:e[mx]|c[hm]|rem|v[hwm]|(?:moz)?mm|in|p[tcx])!i' => '${1}0', // Strip unnecessary units on zero values for length types - '!(^|\:) *(0 0 0|0 0 0 0) *(;|\})!' => '${1}0${3}', // Collapse zero lists + '!\n+| (\{)!' => '$1', // Trim whitespace + '!(^|[: \(,])0(\.\d+)!' => '$1$2', // Strip leading zeros on floats + '!(^|[: \(,])\.?0(?:e[mx]|c[hm]|rem|v[hwm]|in|p[tcx])!i' + => '${1}0', // Strip unnecessary units on zero values for length types + '!(^|\:) *(0 0 0|0 0 0 0) *(;|\})!' => '${1}0${3}', // Collapse zero lists '!(padding|margin) ?\: *0 0 *(;|\})!' => '${1}:0${2}', // Collapse zero lists continued - '!\s*([>~+=])\s*!' => '$1', // Clean-up around combinators + '!\s*([>~+=])\s*!' => '$1', // Clean-up around combinators '!\#([0-9a-f])\1([0-9a-f])\2([0-9a-f])\3!i' - => '#$1$2$3', // Compress hex codes + => '#$1$2$3', // Compress hex codes ); return preg_replace( array_keys( $replacements ), array_values( $replacements ), $str ); @@ -797,7 +809,7 @@ protected static function aliasAtRules ( $stream ) { foreach ( $aliases as $at_rule => $at_rule_aliases ) { if ( - strpos( $stream, "@$at_rule " ) === -1 or + strpos( $stream, "@$at_rule " ) === -1 || strpos( $stream, "@$at_rule{" ) === -1 ) { // Nothing to see here @@ -830,12 +842,12 @@ protected static function aliasAtRules ( $stream ) { $copy_block = str_replace( "@$at_rule", "@$alias", $original_block ); // Aliases are nearly always prefixed, capture the current vendor name - preg_match( self::$regex->vendorPrefix, $alias, $vendor ); + preg_match( csscrush_regex::$patt->vendorPrefix, $alias, $vendor ); $vendor = $vendor ? $vendor[1] : null; // Duplicate rules - if ( preg_match_all( self::$regex->token->rule, $copy_block, $copy_matches ) ) { + if ( preg_match_all( csscrush_regex::$patt->ruleToken, $copy_block, $copy_matches ) ) { $originals = array(); $replacements = array(); @@ -850,7 +862,7 @@ protected static function aliasAtRules ( $stream ) { // Filter out declarations that have different vendor context $new_set = array(); foreach ( $cloneRule as $declaration ) { - if ( !$declaration->vendor or $declaration->vendor === $vendor ) { + if ( ! $declaration->vendor || $declaration->vendor === $vendor ) { $new_set[] = $declaration; } } @@ -885,27 +897,124 @@ protected static function aliasAtRules ( $stream ) { return $stream; } + protected static function prefixSelectors ( $stream ) { + + $matches = csscrush_regex::matchAll( '@in\s+([^\{]+){', $stream, true ); + + // Move through the matches in reverse order + while ( $match = array_pop( $matches ) ) { + + list( $match_string, $match_start_pos ) = $match[0]; + $match_length = strlen( $match_string ); + + $before = substr( $stream, 0, $match_start_pos ); + + $raw_argument = trim( $match[1][0] ); + + $arguments = csscrush_util::splitDelimList( $match[1][0], ',', false, true ); + $arguments = $arguments->list; + + $curly_match = csscrush_util::matchBrackets( + $stream, $brackets = array( '{', '}' ), $match_start_pos, true ); + + if ( ! $curly_match || empty( $raw_argument ) ) { + // Couldn't match the block + $stream = $before . substr( $stream, $match_start_pos + $match_length ); + continue; + } + + // Match all the rule tokens + $rule_matches = csscrush_regex::matchAll( + csscrush_regex::$patt->ruleToken, + $curly_match->inside ); + + foreach ( $rule_matches as $rule_match ) { + + // Get the rule instance + $rule = csscrush_rule::get( $rule_match[0][0] ); + + // Set the isNested flag + $rule->isNested = true; + + // Using arguments create new selector list for the rule + $new_selector_list = array(); + + foreach ( $arguments as $arg_selector ) { + + foreach ( $rule->selectorList as $rule_selector ) { + + if ( ! $rule_selector->allowPrefix ) { + + $new_selector_list[ $rule_selector->readableValue ] = $rule_selector; + } + elseif ( strpos( $rule_selector->value, '&' ) !== false ) { + + // Ampersand is the positional symbol for where the + // prefix will be placed + + // Find and replace (only once) the ampersand + $new_value = preg_replace( + '!&!', + $arg_selector, + $rule_selector->value, + 1 ); + + // Not storing the selector as named + $new_selector_list[] = new csscrush_selector( $new_value ); + } + else { + + // Not storing the selector as named + $new_selector_list[] = new csscrush_selector( "$arg_selector {$rule_selector->value}" ); + } + } + } + $rule->selectorList = $new_selector_list; + } + + // Concatenate + $stream = $before . $curly_match->inside . $curly_match->after; + } + + return $stream; + } + public static function tokenLabelCreate ( $prefix ) { $counter = ++self::$tokenUID; return "___$prefix{$counter}___"; } - public static function tokenReplace ( $string, $token_replace, $type = 'parens' ) { + public static function processRules () { + + foreach ( self::$storage->tokens->rules as $rule ) { + + if ( ! $rule->isNested ) { + + // Associate selectors with the rule + foreach ( $rule->selectorList as $selector ) { + + // $readable_selector = $selector->makeReadableValue(); + self::$process->selectorRelationships[ $selector->readableValue ] = $rule; + } + } - // The tokens to replace - $token_replace = (array) $token_replace; + // Find previous selectors and apply the + $rule->applyExtendables(); - // Reference the token table - $token_table =& self::$storage->tokens->{ $type }; + csscrush_hook::run( 'rule_prealias', $rule ); - // Replace the tokens listed - foreach ( $token_replace as $token ) { - if ( isset( $token_table[ $token ] ) ) { - $string = str_replace( $token, $token_table[ $token ], $string ); + if ( ! empty( self::$config->aliases ) ) { + $rule->addPropertyAliases(); + $rule->addFunctionAliases(); + $rule->addValueAliases(); } + + csscrush_hook::run( 'rule_postalias', $rule ); + + $rule->expandSelectors(); + + csscrush_hook::run( 'rule_postprocess', $rule ); } - - return $string; } @@ -944,17 +1053,17 @@ protected static function cb_restoreComments ( $match ) { protected static function cb_extractVariables ( $match ) { - $regex = self::$regex; + $regex = csscrush_regex::$patt; $block = $match[2]; // Strip comment markers - $block = preg_replace( $regex->token->comment, '', $block ); + $block = csscrush_util::stripComments( $block ); // Need to split safely as there are semi-colons in data-uris $variables_match = csscrush_util::splitDelimList( $block, ';', true ); - // Loop through the pairs, restore parens + // Loop through the pairs foreach ( $variables_match->list as $var ) { $colon = strpos( $var, ':' ); if ( $colon === -1 ) { @@ -967,11 +1076,23 @@ protected static function cb_extractVariables ( $match ) { return ''; } + protected static function cb_extractMixins ( $match ) { + + $name = trim( $match[1] ); + $block = trim( $match[2] ); + + if ( ! empty( $name ) && ! empty( $block ) ) { + self::$process->mixins[ $name ] = new csscrush_mixin( $block ); + } + + return ''; + } + protected static function cb_placeVariables ( $match ) { $before_char = $match[1]; // Check for dollar shorthand - if ( empty( $match[2] ) and isset( $match[3] ) and strpos( $match[0], '$' ) !== false ) { + if ( empty( $match[2] ) && isset( $match[3] ) && strpos( $match[0], '$' ) !== false ) { $variable_name = $match[3]; } else { @@ -986,36 +1107,30 @@ protected static function cb_placeVariables ( $match ) { } } - protected static function cb_extractAndProcessRules ( $match ) { + protected static function cb_extractRules ( $match ) { $rule = new stdclass(); - $rule->selector_raw = $match[1]; - $rule->declaration_raw = $match[2]; + $rule->selector_raw = trim( $match[1] ); + $rule->declaration_raw = trim( $match[2] ); csscrush_hook::run( 'rule_preprocess', $rule ); $rule = new csscrush_rule( $rule->selector_raw, $rule->declaration_raw ); - // Only store rules with declarations - if ( ! empty( $rule->declarations ) ) { - - csscrush_hook::run( 'rule_prealias', $rule ); - - if ( ! empty( self::$config->aliases ) ) { - $rule->addPropertyAliases(); - $rule->addFunctionAliases(); - $rule->addValueAliases(); - } - - csscrush_hook::run( 'rule_postalias', $rule ); + // Store rules if they have declarations or extend arguments + if ( $rule->_declarations || $rule->extends ) { - $rule->expandSelectors(); - - csscrush_hook::run( 'rule_postprocess', $rule ); - - $label = self::tokenLabelCreate( 'r' ); + $label = $rule->label; + self::$storage->tokens->rules[ $label ] = $rule; - return $label . "\n"; + + if ( $rule->_declarations ) { + return $label . "\n"; + } + else { + // If only using extend no need to return a label + return ''; + } } else { return ''; @@ -1032,14 +1147,21 @@ protected static function cb_printRule ( $match ) { $whitespace = $minify ? '' : ' '; $ruleLabel = $match[0]; - + + // If no rule matches the label return empty string if ( ! isset( self::$storage->tokens->rules[ $ruleLabel ] ) ) { return ''; } + $rule = self::$storage->tokens->rules[ $ruleLabel ]; + // If there are no selectors associated with the rule return empty string + if ( empty( $rule->selectorList ) ) { + return ''; + } + // Build the selector - $selectors = implode( ",$whitespace", $rule->selectors ); + $selectors = implode( ",$whitespace", $rule->selectorList ); // Build the block $block = array(); @@ -1066,19 +1188,112 @@ protected static function cb_printRule ( $match ) { # Parsing methods public static function extractAndProcessRules ( $stream ) { - return preg_replace_callback( self::$regex->rule, array( 'self', 'cb_extractAndProcessRules' ), $stream ); + return preg_replace_callback( csscrush_regex::$patt->rule, array( 'self', 'cb_extractRules' ), $stream ); } public static function extractVariables ( $stream ) { - return preg_replace_callback( self::$regex->variables, array( 'self', 'cb_extractVariables' ), $stream ); + return preg_replace_callback( csscrush_regex::$patt->variables, array( 'self', 'cb_extractVariables' ), $stream ); } public static function extractComments ( $stream ) { - return preg_replace_callback( self::$regex->comment, array( 'self', 'cb_extractComments' ), $stream ); + return preg_replace_callback( csscrush_regex::$patt->comment, array( 'self', 'cb_extractComments' ), $stream ); } public static function extractStrings ( $stream ) { - return preg_replace_callback( self::$regex->string, array( 'self', 'cb_extractStrings' ), $stream ); + return preg_replace_callback( csscrush_regex::$patt->string, array( 'self', 'cb_extractStrings' ), $stream ); + } + + public static function extractMixins ( $stream ) { + return preg_replace_callback( csscrush_regex::$patt->mixin, array( 'self', 'cb_extractMixins' ), $stream ); + } + + public static function extractFragments ( $stream ) { + + $matches = csscrush_regex::matchAll( '@fragment\s+()\s*{', $stream, true ); + + // Move through the matches last to first + while ( $match = array_pop( $matches ) ) { + + list( $match_string, $match_start_pos ) = $match[0]; + $fragment_name = $match[1][0]; + + $match_length = strlen( $match_string ); + $before = substr( $stream, 0, $match_start_pos ); + + $curly_match = csscrush_util::matchBrackets( + $stream, $brackets = array( '{', '}' ), $match_start_pos, true ); + + if ( ! $curly_match ) { + // Couldn't match the block + $stream = $before . substr( $stream, $match_start_pos + $match_length ); + continue; + } + else { + // Recontruct the stream without the fragment + $stream = $before . $curly_match->after; + + // Create the fragment and store it + self::$process->fragments[ $fragment_name ] = + new csscrush_fragment( $curly_match->inside ); + + // csscrush::log( self::$process->fragments ); + } + } + + // Now find all the fragment calls + $matches = csscrush_regex::matchAll( '@fragment\s+()\s*(\(|;)', $stream, true ); + + // Move through the matches last to first + while ( $match = array_pop( $matches ) ) { + + list( $match_string, $match_start_pos ) = $match[0]; + $match_length = strlen( $match_string ); + $before = substr( $stream, 0, $match_start_pos ); + + // The matched fragment name + $fragment_name = $match[1][0]; + + // The fragment object, or null if name not present + $fragment = isset( self::$process->fragments[ $fragment_name ] ) ? self::$process->fragments[ $fragment_name ] : null; + + // Fragment may be called without any argument list + $with_arguments = $match[2][0] === '('; + + + if ( $with_arguments ) { + $paren_match = csscrush_util::matchBrackets( + $stream, $brackets = array( '(', ')' ), $match_start_pos, true ); + $after = ltrim( $paren_match->after, ';' ); + } + else { + $after = substr( $stream, $match_start_pos + $match_length ); + } + + if ( ! $fragment || ( $with_arguments && ! $paren_match ) ) { + + // Invalid fragment, or malformed argument list + $stream = $before . substr( $stream, $match_start_pos + $match_length ); + continue; + } + else { + + $args = array(); + if ( $with_arguments ) { + // Get the argument array to pass to the fragment + $args = csscrush_util::splitDelimList( $paren_match->inside, ',', true, true ); + // $args = array_map( 'trim', $args->list ); + $args = $args->list; + } + + // Execute the fragment and get the return value + $fragment_return = $fragment->call( $args ); + + // Recontruct the stream with the fragment return value + $stream = $before . $fragment_return . $after; + } + } + + return $stream; } } @@ -1107,3 +1322,4 @@ function csscrush_clearcache ( $dir = '' ) { } + diff --git a/lib/Function.php b/lib/Function.php index 368395d..ca038cd 100644 --- a/lib/Function.php +++ b/lib/Function.php @@ -16,7 +16,13 @@ class csscrush_function { public static function init () { // Set the custom function regex pattern self::$functionList = self::getFunctions(); - self::$functionPatt = '!(^|[^a-z0-9_-])(' . implode( '|', self::$functionList ) . ')?\(!i'; + self::$functionPatt = self::createFunctionMatchPatt( self::$functionList ); + } + + public static function createFunctionMatchPatt ( $list, $include_unnamed_function = true ) { + + $question = $include_unnamed_function ? '?' : ''; + return '!(^|[^a-z0-9_-])(' . implode( '|', $list ) . ')' . $question . '\(!i'; } public static function getFunctions () { @@ -34,9 +40,7 @@ public static function getFunctions () { return $fn_methods; } - public static function parseAndExecuteValue ( $str ) { - - $patt = self::$functionPatt; + public static function parseCustomFunctions ( $str, $patt, $process_callback = null ) { // No bracketed expressions, early return if ( false === strpos( $str, '(' ) ) { @@ -52,10 +56,10 @@ public static function parseAndExecuteValue ( $str ) { $str = preg_replace( '!\(([^\s])!', '( $1', $str, -1, $spacing_count ); // Find custom function matches - $match_count = preg_match_all( $patt, $str, $m, PREG_OFFSET_CAPTURE | PREG_SET_ORDER ); + $matches = csscrush_regex::matchAll( $patt, $str ); // Step through the matches from last to first - while ( $match = array_pop( $m ) ) { + while ( $match = array_pop( $matches ) ) { $offset = $match[0][1]; $before_char = $match[1][0]; @@ -97,9 +101,16 @@ public static function parseAndExecuteValue ( $str ) { $minus_before = '-' === $raw_fn_name ? '-' : ''; $result = ''; - if ( in_array( $fn_name, self::$functionList ) ) { - $fn_name_clean = str_replace( '-', '_', $fn_name ); - $result = call_user_func( array( 'self', "css_fn__$fn_name_clean" ), $content ); + + if ( ! $process_callback ) { + // If no callback reference it's a built-in + if ( in_array( $fn_name, self::$functionList ) ) { + $fn_name_clean = str_replace( '-', '_', $fn_name ); + $result = call_user_func( array( 'self', "css_fn__$fn_name_clean" ), $content ); + } + } + else { + $result = call_user_func( $process_callback, $content ); } // Join together the result @@ -115,6 +126,11 @@ public static function parseAndExecuteValue ( $str ) { } return $str; + + } + + public static function parseAndExecuteValue ( $str ) { + return self::parseCustomFunctions( $str, self::$functionPatt ); } @@ -135,10 +151,16 @@ protected static function parseMathArgs ( $input ) { } protected static function parseArgs ( $input, $allowSpaceDelim = false ) { - $args = csscrush_util::splitDelimList( $input, - ( $allowSpaceDelim ? '\s*[,\s]\s*' : ',' ), - true, true ); - return array_map( 'trim', $args->list ); + + $args = csscrush_util::splitDelimList( + $input, + ( $allowSpaceDelim ? '\s*[,\s]\s*' : ',' ), + true, + true ); + + // return array_map( 'trim', $args->list ); + + return $args->list; } protected static function colorAdjust ( $color, array $adjustments ) { @@ -148,7 +170,7 @@ protected static function colorAdjust ( $color, array $adjustments ) { // Support for Hex, RGB, RGBa and keywords // HSL and HSLa are passed over - if ( $fn_matched or array_key_exists( $color, $keywords ) ) { + if ( $fn_matched || array_key_exists( $color, $keywords ) ) { $alpha = 1; $rgb = null; @@ -237,7 +259,7 @@ public static function css_fn__math ( $input ) { $result = @eval( "return $input;" ); - return $result === false ? 0 : round( $result, 10 ); + return $result === false ? 0 : round( $result, 5 ); } public static function css_fn__percent ( $input ) { @@ -251,7 +273,7 @@ public static function css_fn__percent ( $input ) { $result = 0; // Need to check arguments or we may see divide by zero errors - if ( count( $args ) > 1 and !empty( $args[0] ) and !empty( $args[1] ) ) { + if ( count( $args ) > 1 && !empty( $args[0] ) && !empty( $args[1] ) ) { // Use bcmath if it's available for higher precision @@ -297,7 +319,7 @@ public static function css_fn__data_uri ( $input ) { $result = "url(http://github.com/$input)"; // No attempt to process absolute urls - if ( preg_match( csscrush::$regex->absoluteUrl, $input ) ) { + if ( preg_match( csscrush_regex::$patt->absoluteUrl, $input ) ) { return $result; } diff --git a/lib/IO.php b/lib/IO.php index be47012..7cd25e8 100644 --- a/lib/IO.php +++ b/lib/IO.php @@ -15,7 +15,7 @@ public static function init () { $process->cacheFileName = '.csscrush'; $process->cacheFilePath = "$process->inputDir/$process->cacheFileName"; - } + } public static function getInput ( $file = false ) { @@ -34,8 +34,11 @@ public static function getInput ( $file = false ) { if ( $file ) { if ( ! file_exists( $input->path ) ) { - // On failure return false with a message - trigger_error( __METHOD__ . ": File '$input->name' not found.\n", E_USER_WARNING ); + + // On failure return false + $error = "Input file '$input->name' not found."; + csscrush::logError( $error ); + trigger_error( __METHOD__ . ": $error\n", E_USER_WARNING ); return false; } else { @@ -56,22 +59,32 @@ public static function testOutputDir ( $write_test = true ) { $output_dir = csscrush::$process->outputDir; $pathtest = true; + $error = false; if ( ! file_exists( $output_dir ) ) { - trigger_error( __METHOD__ . ": directory '$output_dir' doesn't exist.\n", E_USER_WARNING ); + + $error = "Output directory '$output_dir' doesn't exist."; $pathtest = false; } - else if ( $write_test and ! is_writable( $output_dir ) ) { + else if ( $write_test && ! is_writable( $output_dir ) ) { + csscrush::log( 'Attempting to change permissions' ); + if ( ! @chmod( $output_dir, 0755 ) ) { - trigger_error( __METHOD__ . ": directory '$output_dir' is unwritable.\n", E_USER_WARNING ); - csscrush::log( 'Unable to update permissions' ); + + $error = "Output directory '$output_dir' is unwritable."; $pathtest = false; } else { csscrush::log( 'Permissions updated' ); } } + + if ( $error ) { + csscrush::logError( $error ); + trigger_error( __METHOD__ . ": $error\n", E_USER_WARNING ); + } + return $pathtest; } @@ -115,7 +128,7 @@ public static function validateExistingOutput () { // Start off with the input file then add imported files $all_files = array( $input->mtime ); - if ( file_exists( $existingfile->path ) and isset( $process->cacheData[ $process->outputFileName ] ) ) { + if ( file_exists( $existingfile->path ) && isset( $process->cacheData[ $process->outputFileName ] ) ) { // File exists and has config csscrush::log( 'has config' ); @@ -143,7 +156,7 @@ public static function validateExistingOutput () { $options_unchanged = $existing_options == csscrush::$options; $files_unchanged = $existing_datesum == array_sum( $all_files ); - if ( $options_unchanged and $files_unchanged ) { + if ( $options_unchanged && $files_unchanged ) { // Files have not been modified and config is the same: return the old file csscrush::log( "Files and options have not been modified, returning existing @@ -206,8 +219,8 @@ public static function getCacheData () { $process = csscrush::$process; if ( - file_exists( $process->cacheFilePath ) and - $process->cacheData and + file_exists( $process->cacheFilePath ) && + $process->cacheData && $process->cacheData[ 'originPath' ] == $process->cacheFilePath ) { // Already loaded and config file exists in the current directory @@ -219,7 +232,7 @@ public static function getCacheData () { $cache_data = array(); - if ( $cache_data_exists and $cache_data_file_is_writable ) { + if ( $cache_data_exists && $cache_data_file_is_writable ) { // Load from file $cache_data = unserialize( file_get_contents( $process->cacheFilePath ) ); } @@ -227,7 +240,10 @@ public static function getCacheData () { // Config file may exist but not be writable (may not be visible in some ftp situations?) if ( $cache_data_exists ) { if ( ! @unlink( $process->cacheFilePath ) ) { - trigger_error( __METHOD__ . ": Could not delete config data file.\n", E_USER_NOTICE ); + + $error = "Could not delete config data file."; + csscrush::logError( $error ); + trigger_error( __METHOD__ . ": $error\n", E_USER_NOTICE ); } } // Create diff --git a/lib/Importer.php b/lib/Importer.php index 31cb8c7..4e1f4c3 100644 --- a/lib/Importer.php +++ b/lib/Importer.php @@ -31,7 +31,7 @@ public static function hostfile () { $config = csscrush::$config; $process = csscrush::$process; $options = csscrush::$options; - $regex = csscrush::$regex; + $regex = csscrush_regex::$patt; $hostfile = $process->input; // Keep track of all import file info for later logging @@ -83,7 +83,7 @@ public static function hostfile () { } // Url may be a string token - if ( preg_match( $regex->token->string, $url ) ) { + if ( preg_match( $regex->stringToken, $url ) ) { $import_url_token = new csscrush_string( $url ); $url = $import_url_token->value; } @@ -147,14 +147,14 @@ public static function hostfile () { // Url match may be at one of 2 positions if ( $matchAll[ $index ][1][1] == -1 ) { - $urlMatch = $matchAll[ $index ][2][0]; + $urlMatch = $matchAll[ $index ][2][0]; } else { - $urlMatch = $matchAll[ $index ][1][0]; + $urlMatch = $matchAll[ $index ][1][0]; } // Url may be a string token - if ( $urlMatchToken = preg_match( $regex->token->string, $urlMatch ) ) { + if ( $urlMatchToken = preg_match( $regex->stringToken, $urlMatch ) ) { // Store the token $urlMatchToken = new csscrush_string( $urlMatch ); // Set $urlMatch to the actual value @@ -178,7 +178,7 @@ public static function hostfile () { // The full revised statement for replacement $statement = $fullMatch; - if ( $urlMatchToken and ! empty( $replace ) ) { + if ( $urlMatchToken && ! empty( $replace ) ) { // Alter the stored token on internal hash table $urlMatchToken->update( $replace ); } @@ -347,7 +347,7 @@ protected static function rewriteImportRelativeUrls ( $import ) { protected static function cb_rewriteImportRelativeUrl ( $match ) { - $regex = csscrush::$regex; + $regex = csscrush_regex::$patt; $storage = csscrush::$storage; // The relative url prefix @@ -357,7 +357,7 @@ protected static function cb_rewriteImportRelativeUrl ( $match ) { $url = trim( $url ); // If the url is a string token we'll need to restore it as a string token later - if ( $url_is_token = preg_match( $regex->token->string, $url ) ) { + if ( $url_is_token = preg_match( $regex->stringToken, $url ) ) { $url_token = new csscrush_string( $url ); $url = $url_token->value; @@ -368,9 +368,9 @@ protected static function cb_rewriteImportRelativeUrl ( $match ) { // $url path is absolute or begins with slash // $url is an empty string if ( - empty( $url ) or - strpos( $url, '/' ) === 0 or - strpos( $url, '$(' ) === 0 or + empty( $url ) || + strpos( $url, '/' ) === 0 || + strpos( $url, '$(' ) === 0 || preg_match( $regex->absoluteUrl, $url ) ) { // Token or not, it's ok to return the full match if $url is a root relative or absolute ref diff --git a/lib/Iterator.php b/lib/Iterator.php deleted file mode 100644 index e836656..0000000 --- a/lib/Iterator.php +++ /dev/null @@ -1,63 +0,0 @@ -matches ); } - function next () { $this->index++; } - function rewind () { $this->index = 0; } - function valid () { return isset( $this->matches[ $this->index ] ); } - function key () { return $this->index; } - function current () { return $this->matches[ $this->index ]; } -} - - -class csscrush_atrule_iterator extends csscrush_block_iterator { - - - public function __construct ( $options ) { - - $this->options = (object) $options; - - $stream = $this->options->input; - $search = $this->options->search; - - $patt = '!' . $search . '\s?([^\{]*)\{!'; - preg_match_all( $patt, $stream, $this->matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER ); - - if ( $this->options->direction === 'reverse' ) { - $this->matches = array_reverse( $this->matches ); - } - } - - - function current () { - csscrush::log( $this->matches[ $this->index ] ); - - $match = $this->matches[ $this->index ]; - - $out = new stdclass(); - - $out->before = ''; - $out->after = ''; - $out->content = ''; - $out->arguments = ''; - - return $out; - } - -} - - -class csscrush_ruletoken_iterator extends csscrush_block_iterator { -} - - diff --git a/lib/Mixin.php b/lib/Mixin.php new file mode 100644 index 0000000..e83fc12 --- /dev/null +++ b/lib/Mixin.php @@ -0,0 +1,268 @@ +arguments = new csscrush_arglist( $block ); + + // Re-assign with the parsed arguments string + $block = $this->arguments->string; + + // Need to split safely as there are semi-colons in data-uris + $declarations_match = csscrush_util::splitDelimList( $block, ';', true ); + + foreach ( $declarations_match->list as $raw_declaration ) { + + $colon = strpos( $raw_declaration, ':' ); + if ( $colon === -1 ) { + continue; + } + + // Store template declarations as arrays as they are copied by value not reference + $declaration = array(); + + $declaration['property'] = trim( substr( $raw_declaration, 0, $colon ) ); + $declaration['value'] = trim( substr( $raw_declaration, $colon + 1 ) ); + + if ( $declaration['property'] === 'mixin' ) { + + // Mixin can contain other mixins if they are available + if ( $mixin_declarations = csscrush_mixin::parseValue( $declaration['value'] ) ) { + + // Add mixin result to the stack + $this->declarationsTemplate = array_merge( $this->declarationsTemplate, $mixin_declarations ); + } + } + elseif ( ! empty( $declaration['value'] ) ) { + $this->declarationsTemplate[] = $declaration; + } + } + return ''; + } + + + public function call ( array $args ) { + + // Copy the template + $declarations = $this->declarationsTemplate; + + if ( count( $this->arguments ) ) { + + list( $find, $replace ) = $this->arguments->getSubstitutions( $args ); + + // Place the arguments + foreach ( $declarations as &$declaration ) { + $declaration['value'] = str_replace( $find, $replace, $declaration['value'] ); + } + } + + // Return mixin declarations + return $declarations; + } + + + public static function parseSingleValue ( $message ) { + + $message = ltrim( $message ); + + // e.g. mymixin( 50px, rgba(0,0,0,0), left 100% ) + + if ( preg_match( '!^[a-zA-Z0-9_-]+!', $message, $name_match ) ) { + + $mixin_name = $name_match[0]; + + if ( isset( csscrush::$process->mixins[ $mixin_name ] ) ) { + $mixin = csscrush::$process->mixins[ $mixin_name ]; + } + else { + // No mixin found with that name + return false; + } + + // Discard the name part and any enclosing parens + $message = substr( $message, strlen( $mixin_name ) ); + $message = trim( $message, ' \r\n\t()' ); + + // e.g. "value, rgba(0,0,0,0), left 100%" + + // Determine what raw arguments there are to pass to the mixin + $args = array(); + if ( $message !== '' ) { + $args = csscrush_util::splitDelimList( $message, ',', true, true ); + // $args = array_map( 'trim', $args->list ); + $args = $args->list; + } + + return $mixin->call( $args ); + } + return false; + } + + + public static function parseValue ( $message ) { + + // Call the mixin and return the list of declarations + $values = csscrush_util::splitDelimList( $message, ',', true ); + + $declarations = array(); + + foreach ( $values->list as $item ) { + + if ( $result = self::parseSingleValue( $item ) ) { + + $declarations = array_merge( $declarations, $result ); + } + } + return $declarations; + } +} + + +/** + * + * Fragment objects + * + */ + +class csscrush_fragment { + + public $template = array(); + + public $arguments; + + public function __construct ( $block ) { + + // Prepare the arguments object + $this->arguments = new csscrush_arglist( $block ); + + // Re-assign with the parsed arguments string + $this->template = $this->arguments->string; + } + + public function call ( array $args ) { + + // Copy the template + $template = $this->template; + + if ( count( $this->arguments ) ) { + + list( $find, $replace ) = $this->arguments->getSubstitutions( $args ); + $template = str_replace( $find, $replace, $template ); + } + + // Return fragment css + return $template; + } +} + + + + +/** + * + * Argument list management for mixins and fragments + * + */ + +class csscrush_arglist implements Countable { + + // Positional argument default values + public $defaults = array(); + + // The number of expected arguments + public $argCount = 0; + + // The string passed in with arg calls replaced by tokens + public $string; + + // The arg matching regex + protected static $argRegex; + + public static function init () { + + // Create regex pattern for matching 'arg' functions + self::$argRegex = csscrush_function::createFunctionMatchPatt( array( 'arg' ), false ); + } + + function __construct ( $str ) { + + // Parse all arg function calls in the passed string, callback creates default values + $this->string = csscrush_function::parseCustomFunctions( + $str, self::$argRegex, array( $this, 'store' ) ); + } + + public function store ( $raw_argument ) { + + // Match the argument index integer + if ( ! preg_match( '!^[0-9]+!', $raw_argument, $position_match ) ) { + // On failure to match an integer, return an empty string + return ''; + } + + // Get the match from the array + $position_match = $position_match[0]; + + // Store the default value + $default_value = substr( $raw_argument, strlen( $position_match ) ); + $default_value = $default_value ? ltrim( $default_value, " ,\t\n\r" ) : null; + + if ( ! is_null( $default_value ) ) { + $this->defaults[ $position_match ] = trim( $default_value ); + } + + // Update the mixin argument count + $argNumber = ( (int) $position_match ) + 1; + $this->argCount = max( $this->argCount, $argNumber ); + + // Return the argument token + return "___arg{$position_match}___"; + } + + public function getDefaultValue ( $index ) { + + return isset( $this->defaults[ $index ] ) ? $this->defaults[ $index ] : ''; + } + + public function getSubstitutions ( $args ) { + + $argIndexes = range( 0, $this->argCount-1 ); + + // Create table of substitutions + $find = array(); + $replace = array(); + + foreach ( $argIndexes as $index ) { + + $find[] = "___arg{$index}___"; + + if ( isset( $args[ $index ] ) && $args[ $index ] !== 'default' ) { + $replace[] = $args[ $index ]; + } + else { + $replace[] = $this->getDefaultValue( $index ); + } + } + + return array( $find, $replace ); + } + + public function count () { + return $this->argCount; + } +} + + diff --git a/lib/Regex.php b/lib/Regex.php new file mode 100644 index 0000000..8b304cc --- /dev/null +++ b/lib/Regex.php @@ -0,0 +1,96 @@ +name = '[a-zA-Z0-9_-]+'; + $class->notName = '[^a-zA-Z0-9_-]+'; + + // Patterns + $patt->name = '!^' . $class->name . '$!'; + $patt->notName = '!^' . $class->notName . '$!'; + + $patt->import = '! + @import\s+ # import at-rule + (?: + url\(\s*([^\)]+)\s*\) # url function + | # or + ([_s\d]+) # string token + ) + \s*([^;]*); # media argument + !x'; + + $patt->variables = '!@(?:variables|define)\s*([^\{]*)\{\s*(.*?)\s*\};?!s'; + $patt->mixin = '!@mixin\s*([^\{]*)\{\s*(.*?)\s*\};?!s'; + + $patt->abstract = csscrush_regex::create( '^@abstract\s+()', 'i' ); + + $patt->comment = '!/\*(.*?)\*/!s'; + $patt->string = '!(\'|")(?:\\1|[^\1])*?\1!'; + + // As an exception we treat some @-rules like standard rule blocks + $patt->rule = '! + (\n(?:[^@{}]+|@(?:font-face|page|abstract)[^{]*)) # The selector + \{([^{}]*)\} # The declaration block + !x'; + + // Tokens + $patt->commentToken = '!___c\d+___!'; + $patt->stringToken = '!___s\d+___!'; + $patt->ruleToken = '!___r\d+___!'; + $patt->parenToken = '!___p\d+___!'; + + // Functions + $patt->varFunction = '!(?: + ([^a-z0-9_-]) + var\(\s*([a-z0-9_-]+)\s*\) + | + \$\(\s*([a-z0-9_-]+)\s*\) # Dollar syntax + )!ix'; + $patt->function = '!(^|[^a-z0-9_-])([a-z_-]+)(___p\d+___)!i'; + + // Misc. + $patt->vendorPrefix = '!^-([a-z]+)-([a-z-]+)!'; + $patt->absoluteUrl = '!^https?://!'; + } + + + public static function create ( $pattern_template, $flags = '' ) { + + // Sugar + $pattern = str_replace( + array( '', '' ), + array( self::$class->name, self::$class->notName ), + $pattern_template ); + return '!' . $pattern . "!$flags"; + } + + + public static function matchAll ( $patt, $subject, $preprocess_patt = false, $offset = 0 ) { + + if ( $preprocess_patt ) { + // Assume case-insensitive + $patt = self::create( $patt, 'i' ); + } + + preg_match_all( $patt, $subject, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER, $offset ); + return $matches; + } +} + diff --git a/lib/Rule.php b/lib/Rule.php index 92d27ef..d1d27a3 100644 --- a/lib/Rule.php +++ b/lib/Rule.php @@ -1,38 +1,62 @@ label = csscrush::tokenLabelCreate( 'r' ); // Parse the selectors chunk if ( ! empty( $selector_string ) ) { $selectors_match = csscrush_util::splitDelimList( $selector_string, ',' ); - // $this->parens += $selectors_match->matches; // Remove and store comments that sit above the first selector // remove all comments between the other selectors - preg_match_all( $regex->token->comment, $selectors_match->list[0], $m ); + preg_match_all( $regex->commentToken, $selectors_match->list[0], $m ); $this->comments = $m[0]; - foreach ( $selectors_match->list as &$selector ) { - $selector = preg_replace( $regex->token->comment, '', $selector ); - $selector = trim( $selector ); + + // Strip any other comments then create selector instances + foreach ( $selectors_match->list as $selector ) { + + $selector = trim( csscrush_util::stripComments( $selector ) ); + + // If the selector matches an absract directive + if ( preg_match( $regex->abstract, $selector, $m ) ) { + + $abstract_name = $m[1]; + + // Link the rule to the abstract name and skip forward to declaration parsing + csscrush::$process->abstracts[ $abstract_name ] = $this; + break; + } + + $this->addSelector( new csscrush_selector( $selector ) ); } - $this->selectors = $selectors_match->list; } // Parse the declarations chunk @@ -43,7 +67,7 @@ public function __construct ( $selector_string = null, $declarations_string ) { foreach ( $declarations_match->list as $declaration ) { // Strip comments around the property - $declaration = preg_replace( $regex->token->comment, '', $declaration ); + $declaration = csscrush_util::stripComments( $declaration ); // Extract the property part of the declaration $colonPos = strpos( $declaration, ':' ); @@ -57,14 +81,70 @@ public function __construct ( $selector_string = null, $declarations_string ) { $value = substr( $declaration, $colonPos + 1 ); $value = $value !== false ? trim( $value ) : $value; - // Add declaration to the stack - $this->addDeclaration( $prop, $value ); + if ( $prop === 'mixin' ) { + + // Mixins are a special case + if ( $mixin_declarations = csscrush_mixin::parseValue( $value ) ) { + + // Add mixin declarations to the stack + while ( $mixin_declaration = array_shift( $mixin_declarations ) ) { + $this->addDeclaration( $mixin_declaration['property'], $mixin_declaration['value'] ); + } + } + } + elseif ( $prop === 'extend' ) { + + // Extend is a special case + $this->setExtendSelectors( $value ); + } + else { + + // Add declaration to the stack + $this->addDeclaration( $prop, $value ); + } } } + public function __set ( $name, $value ) { + + if ( $name === 'declarations' ) { + $this->_declarations = $value; + + // Update the table of properties + $this->updatePropertyTable(); + } + } + + public function __get ( $name ) { + + if ( $name === 'declarations' ) { + return $this->_declarations; + } + } + + public function updatePropertyTable () { + + // Create a new table of properties + $new_properties_table = array(); + + foreach ( $this as $declaration ) { + + $name = $declaration->property; + + if ( isset( $new_properties_table[ $name ] ) ) { + $new_properties_table[ $name ]++; + } + else { + $new_properties_table[ $name ] = 1; + } + } + + $this->properties = $new_properties_table; + } + public function addPropertyAliases () { - $regex = csscrush::$regex; + $regex = csscrush_regex::$patt; $aliasedProperties =& csscrush::$config->aliases[ 'properties' ]; // First test for the existence of any aliased properties @@ -78,20 +158,24 @@ public function addPropertyAliases () { foreach ( $this->declarations as $declaration ) { $prop = $declaration->property; if ( - !$declaration->skip and - isset( $aliasedProperties[ $prop ] ) + ! $declaration->skip && + isset( $aliasedProperties[ $prop ] ) ) { // There are aliases for the current property foreach ( $aliasedProperties[ $prop ] as $prop_alias ) { + if ( $this->propertyCount( $prop_alias ) ) { continue; } + // If the aliased property hasn't been set manually, we create it $copy = clone $declaration; $copy->family = $copy->property; $copy->property = $prop_alias; + // Remembering to set the vendor property $copy->vendor = null; + // Increment the property count $this->addProperty( $prop_alias ); if ( preg_match( $regex->vendorPrefix, $prop_alias, $vendor ) ) { @@ -127,8 +211,8 @@ public function addFunctionAliases () { // No functions, skip if ( - $declaration->skip or - empty( $declaration->functions ) + $declaration->skip || + empty( $declaration->functions ) ) { $new_set[] = $declaration; continue; @@ -142,7 +226,7 @@ public function addFunctionAliases () { // csscrush::log($intersect); // Loop the aliasable functions foreach ( $intersect as $fn_name ) { - + if ( $declaration->vendor ) { // If the property is vendor prefixed we use the vendor prefixed version // of the function if it exists. @@ -163,7 +247,7 @@ public function addFunctionAliases () { foreach ( $function_aliases[ $fn_name ] as $fn_alias ) { if ( - isset( $used_fn_aliases[ $declaration->family ] ) and + isset( $used_fn_aliases[ $declaration->family ] ) && in_array( $fn_alias, $used_fn_aliases[ $declaration->family ] ) ) { // If the function alias has already been applied in a vendor property @@ -229,14 +313,17 @@ public function expandSelectors () { $new_set = array(); $reg_comma = '!\s*,\s*!'; - foreach ( $this->selectors as $selector ) { - $pos = strpos( $selector, ':any___' ); + foreach ( $this->selectorList as $readableValue => $selector ) { + + $pos = strpos( $selector->value, ':any___' ); + if ( $pos !== false ) { + // Contains an :any statement so we expand $chain = array( '' ); do { if ( $pos === 0 ) { - preg_match( '!:any(___p\d+___)!', $selector, $m ); + preg_match( '!:any(___p\d+___)!', $selector->value, $m ); // Parse the arguments $expression = trim( csscrush::$storage->tokens->parens[ $m[1] ], '()' ); @@ -249,29 +336,92 @@ public function expandSelectors () { } } $chain = $tmp; - $selector = substr( $selector, strlen( $m[0] ) ); + $selector->value = substr( $selector->value, strlen( $m[0] ) ); } else { foreach ( $chain as &$row ) { - $row .= substr( $selector, 0, $pos ); + $row .= substr( $selector->value, 0, $pos ); } - $selector = substr( $selector, $pos ); + $selector->value = substr( $selector->value, $pos ); } - } while ( ( $pos = strpos( $selector, ':any___' ) ) !== false ); + } while ( ( $pos = strpos( $selector->value, ':any___' ) ) !== false ); // Finish off foreach ( $chain as &$row ) { - $new_set[] = $row . $selector; + + // Not creating a named rule association with this expanded selector + $new_set[] = new csscrush_selector( $row . $selector->value ); } + + // Store the unexpanded selector to selectorRelationships + csscrush::$process->selectorRelationships[ $readableValue ] = $this; } else { - // Nothing special - $new_set[] = $selector; + + // Nothing to expand + $new_set[ $readableValue ] = $selector; + } + + } // foreach + + $this->selectorList = $new_set; + } + + public function setExtendSelectors ( $raw_value ) { + + $abstracts = csscrush::$process->abstracts; + $selectorRelationships = csscrush::$process->selectorRelationships; + + // Pass extra argument to trim the returned list + $args = csscrush_util::splitDelimList( $raw_value, ',', true, true ); + + foreach ( $args->list as $arg ) { + + if ( preg_match( csscrush_regex::$patt->name, $arg ) ) { + + // A regular name: an element name or an abstract rule name + $this->extends[ $arg ] = true; + } + else { + + // Not a regular name: Some kind of selector so normalize it for later comparison + $readable_selector = csscrush_selector::makeReadableSelector( $arg ); + + $this->extends[ $readable_selector ] = true; } } - $this->selectors = $new_set; + csscrush::log( $this->extends ); } + public function applyExtendables () { + + $abstracts = csscrush::$process->abstracts; + $selectorRelationships = csscrush::$process->selectorRelationships; + + foreach ( $this->extends as $name => $bool ) { + + if ( isset( $abstracts[ $name ] ) ) { + + // Match found in abstract rules + $abstracts[ $name ]->addSelectors( $this->selectorList ); + } + else if ( isset( $selectorRelationships[ $name ] ) ) { + + // Match found in selectorRelationships + $selectorRelationships[ $name ]->addSelectors( $this->selectorList ); + } + } + } + + public function addSelector ( $selector ) { + + $this->selectorList[ $selector->readableValue ] = $selector; + } + + public function addSelectors ( $list ) { + + $this->selectorList = array_merge( $this->selectorList, $list ); + } ############ # IteratorAggregate @@ -285,14 +435,15 @@ public function getIterator () { # Rule API public function propertyCount ( $prop ) { + if ( array_key_exists( $prop, $this->properties ) ) { return $this->properties[ $prop ]; } return 0; } - public function addProperty ( $prop ) { + if ( isset( $this->properties[ $prop ] ) ) { $this->properties[ $prop ]++; } @@ -301,102 +452,36 @@ public function addProperty ( $prop ) { } } - public function addDeclaration ( $prop, $value ) { - // $regex = csscrush::$regex; - // - // // Check the input - // if ( empty( $prop ) or $value === '' or $value === null ) { - // return false; - // } - // - // // Test for escape tilde - // if ( $skip = strpos( $prop, '~' ) === 0 ) { - // $prop = substr( $prop, 1 ); - // } - // - // // Store the property family - // // Store the vendor id, if one is present - // if ( preg_match( $regex->vendorPrefix, $prop, $vendor ) ) { - // $family = $vendor[2]; - // $vendor = $vendor[1]; - // } - // else { - // $vendor = null; - // $family = $prop; - // } - // - // // Check for !important keywords - // if ( ( $important = strpos( $value, '!important' ) ) !== false ) { - // $value = substr( $value, 0, $important ); - // $important = true; - // } - // - // // Ignore declarations with null css values - // if ( $value === false or $value === '' ) { - // return false; - // } - // - // // Apply custom functions - // if ( ! $skip ) { - // $value = csscrush_function::parseAndExecuteValue( $value ); - // } - // - // // Tokenize all remaining paren pairs - // $match_obj = csscrush_util::matchAllBrackets( $value ); - // $this->parens += $match_obj->matches; - // $value = $match_obj->string; - // - // - // // Create an index of all regular functions in the value - // if ( preg_match_all( $regex->function->match, $value, $functions ) > 0 ) { - // $out = array(); - // foreach ( $functions[2] as $index => $fn_name ) { - // $out[] = $fn_name; - // } - // $functions = array_unique( $out ); - // } - // else { - // $functions = array(); - // } - // - // // Store the declaration - // $_declaration = (object) array( - // 'property' => $prop, - // 'family' => $family, - // 'vendor' => $vendor, - // 'functions' => $functions, - // 'value' => $value, - // 'skip' => $skip, - // 'important' => $important, - // ); - - + // Create declaration, add to the stack if it's valid + $declaration = new csscrush_declaration( $prop, $value ); - // Store the property name - $this->addProperty( $prop ); + if ( $declaration->isValid ) { - // Add declaration to the stack - $_declaration = new csscrush_declaration( $prop, $value ); - $this->declarations[] = $_declaration; + // Manually increment the property name since we're directly updating the _declarations list + $this->addProperty( $prop ); + $this->_declarations[] = $declaration; + return $declaration; + } - return $_declaration; + return false; } - // - // // Get a declaration value without paren tokens - // public function getDeclarationValue ( $declaration ) { - // // $paren_keys = array_keys( $this->parens ); - // // $paren_values = array_values( $this->parens ); - // - // return csscrush::tokenReplace( $declaration->value, $token_replace ); - // - // return str_replace( $paren_keys, $paren_values, $declaration->value ); - // } + public static function get ( $token ) { + if ( isset( csscrush::$storage->tokens->rules[ $token ] ) ) { + return csscrush::$storage->tokens->rules[ $token ]; + } + return null; + } } +/** + * + * Declaration objects + * + */ class csscrush_declaration { @@ -408,14 +493,21 @@ class csscrush_declaration { public $skip; public $important; public $parenTokens; - + + public $isValid = true; + public function __construct ( $prop, $value ) { - - $regex = csscrush::$regex; + + $regex = csscrush_regex::$patt; + + // Normalize input. Lowercase the property name + $prop = strtolower( trim( $prop ) ); + $value = trim( $value ); // Check the input - if ( empty( $prop ) or $value === '' or $value === null ) { - return false; + if ( $prop === '' || $value === '' || $value === null ) { + $this->isValid = false; + return; } // Test for escape tilde @@ -441,8 +533,9 @@ public function __construct ( $prop, $value ) { } // Ignore declarations with null css values - if ( $value === false or $value === '' ) { - return false; + if ( $value === false || $value === '' ) { + $this->isValid = false; + return; } // Apply custom functions @@ -455,9 +548,8 @@ public function __construct ( $prop, $value ) { $this->parenTokens = $match_obj->matches; $value = $match_obj->string; - // Create an index of all regular functions in the value - if ( preg_match_all( $regex->function->match, $value, $functions ) > 0 ) { + if ( preg_match_all( $regex->function, $value, $functions ) > 0 ) { $out = array(); foreach ( $functions[2] as $index => $fn_name ) { $out[] = $fn_name; @@ -468,17 +560,6 @@ public function __construct ( $prop, $value ) { $functions = array(); } - // // Store the declaration - // $_declaration = (object) array( - // 'property' => $prop, - // 'family' => $family, - // 'vendor' => $vendor, - // 'functions' => $functions, - // 'value' => $value, - // 'skip' => $skip, - // 'important' => $important, - // ); - $this->property = $prop; $this->family = $family; $this->vendor = $vendor; @@ -487,13 +568,67 @@ public function __construct ( $prop, $value ) { $this->skip = $skip; $this->important = $important; } - + public function getFullValue () { - return csscrush::tokenReplace( $this->value, $this->parenTokens ); + return csscrush_util::tokenReplace( $this->value, $this->parenTokens ); } } +/** + * + * Selector objects + * + */ + +class csscrush_selector { + + public $value; + + public $readableValue; + + public $allowPrefix = true; + + + public static function makeReadableSelector ( $selector_string ) { + + // Quick test for paren tokens + if ( strpos( $selector_string, '___p' ) !== false ) { + $selector_string = csscrush_util::tokenReplaceAll( $selector_string, 'parens' ); + } + + // Create space around combinators, then normalize whitespace + $selector_string = preg_replace( '!([>+~])!', ' $1 ', $selector_string ); + $selector_string = csscrush_util::normalizeWhiteSpace( $selector_string ); + + // Quick test for string tokens + if ( strpos( $selector_string, '___s' ) !== false ) { + $selector_string = csscrush_util::tokenReplaceAll( $selector_string, 'strings' ); + } + + return $selector_string; + } + + public function __construct ( $raw_selector, $associated_rule = null ) { + + if ( strpos( $raw_selector, '^' ) === 0 ) { + + $raw_selector = ltrim( $raw_selector, "^ \n\r\t" ); + $this->allowPrefix = false; + } + + $this->readableValue = self::makeReadableSelector( $raw_selector ); + $this->value = $raw_selector; + } + + public function __toString () { + + return $this->value; + } +} + + + diff --git a/lib/Util.php b/lib/Util.php index a68c520..064cb5a 100644 --- a/lib/Util.php +++ b/lib/Util.php @@ -7,8 +7,10 @@ class csscrush_util { + // Create html attribute string from array public static function htmlAttributes ( array $attributes ) { + $attr_string = ''; foreach ( $attributes as $name => $value ) { $value = htmlspecialchars( $value, ENT_COMPAT, 'UTF-8', false ); @@ -18,10 +20,11 @@ public static function htmlAttributes ( array $attributes ) { } - public static function normalizeSystemPath ( $path, $stripMsDos = false ) { + public static function normalizeSystemPath ( $path, $strip_ms_dos = false ) { + $path = rtrim( str_replace( '\\', '/', $path ), '/' ); - if ( $stripMsDos ) { + if ( $strip_ms_dos ) { $path = preg_replace( '!^[a-z]\:!i', '', $path ); } return $path; @@ -40,7 +43,13 @@ public static function find () { } + public static function stripComments ( $str ) { + return preg_replace( csscrush_regex::$patt->commentToken, '', $str ); + } + + public static function normalizeWhiteSpace ( $str ) { + $replacements = array( '!\s+!' => ' ', '!(\[)\s*|\s*(\])|(\()\s*|\s*(\))!' => '${1}${2}${3}${4}', // Trim internal bracket WS @@ -51,7 +60,54 @@ public static function normalizeWhiteSpace ( $str ) { } - public static function splitDelimList ( $str, $delim, $fold_in = false, $allow_empty = false ) { + public static function tokenReplace ( $string, $token_replace, $type = 'parens' ) { + + // The tokens to replace + $token_replace = (array) $token_replace; + + // Reference the token table + $token_table =& csscrush::$storage->tokens->{ $type }; + + // Replace the tokens listed + foreach ( $token_replace as $token ) { + if ( isset( $token_table[ $token ] ) ) { + $string = str_replace( $token, $token_table[ $token ], $string ); + } + } + + return $string; + } + + + public static function tokenReplaceAll ( $str, $type = 'parens' ) { + + // Only $type 'parens' or 'strings' make any sense + $token_patt = 'parenToken'; + + if ( $type === 'strings' ) { + $token_patt = 'stringToken'; + } + + // Reference the token table + $token_table =& csscrush::$storage->tokens->{ $type }; + + // Find tokens + $matches = csscrush_regex::matchAll( csscrush_regex::$patt->{ $token_patt }, $str ); + + foreach ( $matches as $m ) { + + $token = $m[0][0]; + + if ( isset( $token_table[ $token ] ) ) { + + $str = str_replace( $token, $token_table[ $token ], $str ); + } + } + return $str; + } + + + public static function splitDelimList ( $str, $delim, $fold_in = false, $trim = false ) { $match_obj = self::matchAllBrackets( $str ); @@ -64,26 +120,25 @@ public static function splitDelimList ( $str, $delim, $fold_in = false, $allow_e $match_obj->list = preg_split( '!' . $delim . '!', $match_obj->string ); } - if ( false === $allow_empty ) { - $match_obj->list = array_filter( $match_obj->list ); + if ( true === $trim ) { + $match_obj->list = array_map( 'trim', $match_obj->list ); } + + // Filter out empties + $match_obj->list = array_filter( $match_obj->list ); + if ( $fold_in ) { - // $match_keys = array_keys( $match_obj->matches ); - // $match_values = array_values( $match_obj->matches ); - // foreach ( $match_obj->list as &$item ) { - // $item = str_replace( $match_keys, $match_values, $item ); - // } - + foreach ( $match_obj->list as &$item ) { - $item = csscrush::tokenReplace( $item, $match_obj->matches ); + $item = csscrush_util::tokenReplace( $item, $match_obj->matches ); } - } return $match_obj; } - public static function matchBrackets ( $str, $brackets = array( '(', ')' ), $search_pos = 0 ) { + public static function matchBrackets ( $str, $brackets = array( '(', ')' ), + $search_pos = 0, $capture_text = false ) { list( $opener, $closer ) = $brackets; $openings = array(); @@ -96,18 +151,20 @@ public static function matchBrackets ( $str, $brackets = array( '(', ')' ), $sea $close_index = strpos( $str, $closer, $search_pos ); if ( $start_index === false ) { + return false; } if ( substr_count( $str, $opener ) !== substr_count( $str, $closer ) ) { - $sample = substr( $str, 0, 15 ); + + $sample = substr( $str, 0, 25 ); trigger_error( __METHOD__ . ": Unmatched token near '$sample'.\n", E_USER_WARNING ); return false; } while ( - ( $start_index !== false or $close_index !== false ) and $brake-- + ( $start_index !== false || $close_index !== false ) && $brake-- ) { - if ( $start_index !== false and $close_index !== false ) { + if ( $start_index !== false && $close_index !== false ) { $search_pos = min( $start_index, $close_index ); if ( $start_index < $close_index ) { $openings[] = $start_index; @@ -127,10 +184,18 @@ public static function matchBrackets ( $str, $brackets = array( '(', ')' ), $sea $search_pos += 1; // Advance if ( count( $closings ) === count( $openings ) ) { + $match->openings = $openings; + $match->start = $start = $openings[0]; $match->closings = $closings; - $match->start = $openings[0]; $match->end = $closings[ count( $closings ) - 1 ] + 1; + + if ( $capture_text ) { + // Text capturing is optional to avoid using memory when not necessary + $match->inside = substr( $str, $start + 1, $match->end - $start - 2 ); + $match->after = substr( $str, $match->end ); + } + return $match; } $start_index = strpos( $str, $opener, $search_pos ); @@ -188,6 +253,7 @@ public static function matchAllBrackets ( $str, $pair = '()', $offset = 0 ) { // Step backwards through the matches while ( $offset = array_pop( $offsets ) ) { + list( $start, $finish ) = $offset; $before = substr( $str, 0, $start ); @@ -196,7 +262,6 @@ public static function matchAllBrackets ( $str, $pair = '()', $offset = 0 ) { $label = csscrush::tokenLabelCreate( 'p' ); $str = $before . $label . $after; - // $match_obj->matches[ $label ] = $content; $match_obj->matches[] = $label; // Parens will be folded in later @@ -207,11 +272,15 @@ public static function matchAllBrackets ( $str, $pair = '()', $offset = 0 ) { return $match_obj; } - - } +/** + * + * String sugar + * + */ + class csscrush_string { public $token; diff --git a/misc/initial-values.ini b/misc/initial-values.ini index 7288444..e0b5781 100644 --- a/misc/initial-values.ini +++ b/misc/initial-values.ini @@ -122,6 +122,7 @@ right = "auto" tab-size = "8" table-layout = "auto" text-align = "inherit" +text-align-last = "auto" text-decoration = "none" text-decoration-color = "inherit" text-decoration-line = "none" diff --git a/plugins/hsl-to-hex.php b/plugins/hsl-to-hex.php index bed3d4b..dc4d761 100644 --- a/plugins/hsl-to-hex.php +++ b/plugins/hsl-to-hex.php @@ -12,19 +12,20 @@ csscrush_hook::add( 'rule_postalias', 'csscrush_hsl' ); -function csscrush_hsl ( CssCrush_Rule $rule ) { +function csscrush_hsl ( csscrush_rule $rule ) { + foreach ( $rule as &$declaration ) { if ( - ! $declaration->skip and - ( ! empty( $declaration->functions ) and in_array( 'hsl', $declaration->functions ) ) + ! $declaration->skip && + ( ! empty( $declaration->functions ) && in_array( 'hsl', $declaration->functions ) ) ) { while ( preg_match( '!hsl(___p\d+___)!', $declaration->value, $m ) ) { $full_match = $m[0]; $token = $m[1]; $hsl = trim( csscrush::$storage->tokens->parens[ $token ], '()' ); $hsl = array_map( 'trim', explode( ',', $hsl ) ); - $rgb = CssCrush_Color::cssHslToRgb( $hsl ); - $hex = CssCrush_Color::rgbToHex( $rgb ); + $rgb = csscrush_color::cssHslToRgb( $hsl ); + $hex = csscrush_color::rgbToHex( $rgb ); $declaration->value = str_replace( $full_match, $hex, $declaration->value ); } } diff --git a/plugins/ie-clip.php b/plugins/ie-clip.php index ed95657..1fca038 100644 --- a/plugins/ie-clip.php +++ b/plugins/ie-clip.php @@ -12,7 +12,8 @@ csscrush_hook::add( 'rule_postalias', 'csscrush_clip' ); -function csscrush_clip ( CssCrush_Rule $rule ) { +function csscrush_clip ( csscrush_rule $rule ) { + // Assume it's been dealt with if the property occurs more than once if ( $rule->propertyCount( 'clip' ) !== 1 ) { return; @@ -21,12 +22,13 @@ function csscrush_clip ( CssCrush_Rule $rule ) { foreach ( $rule as $declaration ) { $new_set[] = $declaration; if ( - $declaration->skip or + $declaration->skip || $declaration->property !== 'clip' ) { continue; } - $new_set[] = $rule->addDeclaration( '*clip', str_replace( ',', ' ', $declaration->getFullValue() ) ); + $new_set[] = new csscrush_declaration( '*clip', str_replace( ',', ' ', $declaration->getFullValue() ) ); } $rule->declarations = $new_set; -} \ No newline at end of file +} + diff --git a/plugins/ie-filter.php b/plugins/ie-filter.php index 05da1fe..33c8927 100644 --- a/plugins/ie-filter.php +++ b/plugins/ie-filter.php @@ -18,7 +18,7 @@ csscrush_hook::add( 'rule_postalias', 'csscrush_filter' ); -function csscrush_filter ( CssCrush_Rule $rule ) { +function csscrush_filter ( csscrush_rule $rule ) { if ( $rule->propertyCount( '-ms-filter' ) < 1 ) { return; } @@ -26,7 +26,7 @@ function csscrush_filter ( CssCrush_Rule $rule ) { $new_set = array(); foreach ( $rule as $declaration ) { if ( - $declaration->skip or + $declaration->skip || $declaration->property !== '-ms-filter' ) { $new_set[] = $declaration; @@ -35,21 +35,21 @@ function csscrush_filter ( CssCrush_Rule $rule ) { $list = array_map( 'trim', explode( ',', $declaration->value ) ); foreach ( $list as &$item ) { if ( - strpos( $item, $filter_prefix ) !== 0 and + strpos( $item, $filter_prefix ) !== 0 && strpos( $item, 'alpha' ) !== 0 // Shortcut syntax permissable on alpha ) { $item = $filter_prefix . ucfirst( $item ); } } $declaration->value = implode( ',', $list ); - if ( !$rule->propertyCount( 'zoom' ) ) { + if ( ! $rule->propertyCount( 'zoom' ) ) { // Filters need hasLayout - $new_set[] = $rule->addDeclaration( 'zoom', 1 ); + $new_set[] = new csscrush_declaration( 'zoom', 1 ); } // Quoted version for -ms-filter IE >= 8 - $new_set[] = $rule->addDeclaration( '-ms-filter', "\"$declaration->value\"" ); + $new_set[] = new csscrush_declaration( '-ms-filter', "\"$declaration->value\"" ); // Star escaped property for IE < 8 - $new_set[] = $rule->addDeclaration( '*filter', $declaration->value ); + $new_set[] = new csscrush_declaration( '*filter', $declaration->value ); } $rule->declarations = $new_set; } \ No newline at end of file diff --git a/plugins/ie-inline-block.php b/plugins/ie-inline-block.php index 9bb885e..6cebedb 100644 --- a/plugins/ie-inline-block.php +++ b/plugins/ie-inline-block.php @@ -13,7 +13,7 @@ csscrush_hook::add( 'rule_postalias', 'csscrush_display_inlineblock' ); -function csscrush_display_inlineblock ( CssCrush_Rule $rule ) { +function csscrush_display_inlineblock ( csscrush_rule $rule ) { if ( $rule->propertyCount( 'display' ) < 1 ) { return; } @@ -22,13 +22,13 @@ function csscrush_display_inlineblock ( CssCrush_Rule $rule ) { $new_set[] = $declaration; $is_display = $declaration->property === 'display'; if ( - $declaration->skip or - !$is_display or - $is_display and $declaration->value !== 'inline-block' ) { + $declaration->skip || + ! $is_display || + $is_display && $declaration->value !== 'inline-block' ) { continue; } - $new_set[] = $rule->addDeclaration( '*display', 'inline' ); - $new_set[] = $rule->addDeclaration( '*zoom', 1 ); + $new_set[] = new csscrush_declaration( '*display', 'inline' ); + $new_set[] = new csscrush_declaration( '*zoom', 1 ); } $rule->declarations = $new_set; } \ No newline at end of file diff --git a/plugins/ie-min-height.php b/plugins/ie-min-height.php index 4a85ccd..a38e7fd 100644 --- a/plugins/ie-min-height.php +++ b/plugins/ie-min-height.php @@ -12,7 +12,7 @@ csscrush_hook::add( 'rule_postalias', 'csscrush_minheight' ); -function csscrush_minheight ( CssCrush_Rule $rule ) { +function csscrush_minheight ( csscrush_rule $rule ) { if ( $rule->propertyCount( 'min-height' ) < 1 ) { return; } @@ -20,11 +20,11 @@ function csscrush_minheight ( CssCrush_Rule $rule ) { foreach ( $rule as $declaration ) { $new_set[] = $declaration; if ( - $declaration->skip or + $declaration->skip || $declaration->property !== 'min-height' ) { continue; } - $new_set[] = $rule->addDeclaration( '_height', $declaration->value ); + $new_set[] = new csscrush_declaration( '_height', $declaration->value ); } $rule->declarations = $new_set; } \ No newline at end of file diff --git a/plugins/ie-opacity.php b/plugins/ie-opacity.php index 69f0d2e..bd1d309 100755 --- a/plugins/ie-opacity.php +++ b/plugins/ie-opacity.php @@ -14,7 +14,7 @@ csscrush_hook::add( 'rule_postalias', 'csscrush_opacity' ); -function csscrush_opacity ( CssCrush_Rule $rule ) { +function csscrush_opacity ( csscrush_rule $rule ) { if ( $rule->propertyCount( 'opacity' ) < 1 ) { return; } @@ -22,7 +22,7 @@ function csscrush_opacity ( CssCrush_Rule $rule ) { foreach ( $rule as $declaration ) { $new_set[] = $declaration; if ( - $declaration->skip or + $declaration->skip || $declaration->property != 'opacity' ) { continue; @@ -31,13 +31,13 @@ function csscrush_opacity ( CssCrush_Rule $rule ) { $opacity = (float) $declaration->value; $opacity = round( $opacity * 100 ); - if ( !$rule->propertyCount( 'zoom' ) ) { + if ( ! $rule->propertyCount( 'zoom' ) ) { // Filters need hasLayout - $new_set[] = $rule->addDeclaration( 'zoom', 1 ); + $new_set[] = new csscrush_declaration( 'zoom', 1 ); } $value = "alpha(opacity=$opacity)"; - $new_set[] = $rule->addDeclaration( '-ms-filter', "\"$value\"" ); - $new_set[] = $rule->addDeclaration( '*filter', $value ); + $new_set[] = new csscrush_declaration( '-ms-filter', "\"$value\"" ); + $new_set[] = new csscrush_declaration( '*filter', $value ); } $rule->declarations = $new_set; } \ No newline at end of file diff --git a/plugins/initial.php b/plugins/initial.php index 3112a12..4e09c66 100644 --- a/plugins/initial.php +++ b/plugins/initial.php @@ -17,7 +17,7 @@ csscrush_hook::add( 'rule_prealias', 'csscrush_initial' ); -function csscrush_initial ( CssCrush_Rule $rule ) { +function csscrush_initial ( csscrush_rule $rule ) { static $initialValues = null; if ( ! $initialValues ) { @@ -28,7 +28,7 @@ function csscrush_initial ( CssCrush_Rule $rule ) { } foreach ( $rule as &$declaration ) { - if ( !$declaration->skip and 'initial' === $declaration->value ) { + if ( !$declaration->skip && 'initial' === $declaration->value ) { if ( isset( $initialValues[ $declaration->property ] ) ) { $declaration->value = $initialValues[ $declaration->property ]; } diff --git a/plugins/rgba-fallback.php b/plugins/rgba-fallback.php index c5933eb..44fc136 100644 --- a/plugins/rgba-fallback.php +++ b/plugins/rgba-fallback.php @@ -14,13 +14,15 @@ csscrush_hook::add( 'rule_postalias', 'csscrush_rgba' ); -function csscrush_rgba ( CssCrush_Rule $rule ) { + +function csscrush_rgba ( csscrush_rule $rule ) { + $props = array_keys( $rule->properties ); // Determine which properties apply $rgba_props = array(); foreach ( $props as $prop ) { - if ( $prop === 'background' or strpos( $prop, 'color' ) !== false ) { + if ( $prop === 'background' || strpos( $prop, 'color' ) !== false ) { $rgba_props[] = $prop; } } @@ -32,9 +34,9 @@ function csscrush_rgba ( CssCrush_Rule $rule ) { foreach ( $rule as $declaration ) { $is_viable = in_array( $declaration->property, $rgba_props ); if ( - $declaration->skip or - !$is_viable or - $is_viable and !preg_match( '!^rgba___p\d+___$!', $declaration->value ) + $declaration->skip || + ! $is_viable || + $is_viable && !preg_match( '!^rgba___p\d+___$!', $declaration->value ) ) { $new_set[] = $declaration; continue; @@ -45,7 +47,7 @@ function csscrush_rgba ( CssCrush_Rule $rule ) { list( $r, $g, $b, $a ) = explode( ',', $raw_value ); // Add rgb value to the stack, followed by rgba - $new_set[] = $rule->addDeclaration( $declaration->property, "rgb($r,$g,$b)" ); + $new_set[] = new csscrush_declaration( $declaration->property, "rgb($r,$g,$b)" ); $new_set[] = $declaration; } $rule->declarations = $new_set; From 4489b1cf95b5b7dc49e8ae7644ebe4c292494152 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 9 May 2012 11:35:05 +0100 Subject: [PATCH 004/421] Updated the font stacks in Prepend.css --- CHANGELOG.txt | 4 ++-- Prepend.css | 29 ++++++++++++++++++----------- lib/Importer.php | 3 ++- 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index ffde177..1faeab2 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,9 +1,9 @@ 1.5 --- +New feature: Rule inheritance / abstract rules +New feature: Block nesting New feature: Mixins New feature: Fragments -New feature: Block nesting -New feature: Rule inheritance / abstract rules Abstracted IO interface Added some error reporting Added spiffing.css plugin diff --git a/Prepend.css b/Prepend.css index 3c5d93a..465cf37 100644 --- a/Prepend.css +++ b/Prepend.css @@ -7,28 +7,35 @@ Prepend.css contains library variables by default, but it could also contain res @define { /* Font stacks + + Sources: + http://cssfontstack.com + http://www.codestyle.org ---------------------------------------- */ /* Serif */ - baskerville: Baskerville, Times, "Times New Roman", serif; + baskerville: Baskerville, "Baskerville Old Face", "Hoefler Text", Garamond, "Times New Roman", serif; georgia: Georgia, Times, "Times New Roman", serif; - palatino: Palatino, "Palatino Linotype", "Hoefler Text", serif; - times: Times, "Times New Roman", serif; - serif: $( times ); + palatino: Palatino, "Palatino Linotype", "Palatino LT STD", "Book Antiqua", Georgia, serif; + times: TimesNewRoman, "Times New Roman", Times, serif; /* Sans-serif */ - arial: Arial, "Arial Unicode MS", Helvetica, sans-serif; - gill-sans: "Gill Sans", Calibri, "Trebuchet MS", sans-serif; + arial: Arial, Helvetica, sans-serif; + arial-narrow: "Arial Narrow", Arial, sans-serif; + gill-sans: "Gill Sans", "Gill Sans MT", Calibri, sans-serif; helvetica: "Helvetica Neue", Helvetica, Arial, sans-serif; - lucida: "Lucida Sans Unicode", "Lucida Sans", "Lucida Grande", Verdana, sans-serif; + lucida: "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Verdana, sans-serif; trebuchet-ms: "Trebuchet MS", "Lucida Sans Unicode", "Lucida Grande", "Lucida Sans", Arial, sans-serif; - verdana: Verdana, Tahoma, Arial, sans-serif; - sans-serif: $( arial ); + verdana: Verdana, Geneva, sans-serif; /* Monospace */ - consolas: Consolas, "Lucida Console", Monaco, "Courier New", Courier, monospace; + consolas: Consolas, Monaco, monospace; courier: "Courier New", Courier, monospace; - monaco: Monaco, "Courier New", Courier, monospace; + monaco: Monaco, Consolas, "Lucida Console", monospace; + + /* Generic defaults */ + serif: $( times ); + sans-serif: $( arial ); monospace: $( courier ); } diff --git a/lib/Importer.php b/lib/Importer.php index 4e1f4c3..29bc1b1 100644 --- a/lib/Importer.php +++ b/lib/Importer.php @@ -114,12 +114,13 @@ public static function hostfile () { // Failed to open import, just continue with the import line removed if ( ! $import->content ) { + csscrush::log( "Import file '$import->url' not found at '$import->path'" ); $stream = $preStatement . $postStatement; continue; - } else { + // Import file opened successfully so we process it: // We need to resolve import statement urls in all imported files since // they will be brought inline with the hostfile From 3d6939011cb6b6e2f6e6e2116facb9fdc93373e9 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 9 May 2012 11:47:46 +0100 Subject: [PATCH 005/421] Version ID now automatically added to boilerplate --- CssCrush.boilerplate | 2 +- CssCrush.php | 2 +- lib/Core.php | 19 +++++++++++++++---- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/CssCrush.boilerplate b/CssCrush.boilerplate index c0ed732..f656f11 100644 --- a/CssCrush.boilerplate +++ b/CssCrush.boilerplate @@ -1,2 +1,2 @@ CSS Crush(ed) on {{datetime}} -http://github.com/peteboere/css-crush \ No newline at end of file +http://github.com/peteboere/css-crush ({{version}}) \ No newline at end of file diff --git a/CssCrush.php b/CssCrush.php index 39dd8e5..2b74ab7 100644 --- a/CssCrush.php +++ b/CssCrush.php @@ -22,7 +22,7 @@ require_once 'lib/Regex.php'; require_once 'lib/Hook.php'; -csscrush::init( dirname( __FILE__ ) ); +csscrush::init( __FILE__ ); diff --git a/lib/Core.php b/lib/Core.php index bbbb3b2..17d9f4f 100644 --- a/lib/Core.php +++ b/lib/Core.php @@ -21,12 +21,17 @@ class csscrush { // Init called once manually post class definition - public static function init ( $current_dir ) { + public static function init ( $seed_file ) { self::$config = new stdclass(); // Path to this installation - self::$config->location = $current_dir; + self::$config->location = dirname( $seed_file ); + + // Get version ID from seed file + $seed_file_contents = file_get_contents( $seed_file ); + $match_count = preg_match( '!@version\s+([\d\.]+)!', $seed_file_contents, $version_match ); + self::$config->version = $match_count ? $version_match[1] : null; // Set the docRoot reference self::setDocRoot(); @@ -468,19 +473,25 @@ protected static function getBoilerplate () { // Load the file $boilerplate = file_get_contents( $file ); - // Process any tags, currently only '{{datetime}}' is supported + // Substitute any tags if ( preg_match_all( '!\{\{([^}]+)\}\}!', $boilerplate, $boilerplate_matches ) ) { + $replacements = array(); foreach ( $boilerplate_matches[0] as $index => $tag ) { - if ( $boilerplate_matches[1][$index] === 'datetime' ) { + $tag_name = $boilerplate_matches[1][$index]; + if ( $tag_name === 'datetime' ) { $replacements[] = @date( 'Y-m-d H:i:s O' ); } + elseif ( $tag_name === 'version' ) { + $replacements[] = 'v' . csscrush::$config->version; + } else { $replacements[] = '?'; } } $boilerplate = str_replace( $boilerplate_matches[0], $replacements, $boilerplate ); } + // Pretty print $boilerplate = explode( PHP_EOL, $boilerplate ); $boilerplate = array_map( 'trim', $boilerplate ); From d3d544e9387eaef28dd4a4b6fc0219dfad754a68 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 10 May 2012 11:45:35 +0100 Subject: [PATCH 006/421] Changed 'extend' to 'extends' for readablity A few other small changes and fixes --- lib/Core.php | 16 ++++++---------- lib/Rule.php | 6 +++--- plugins/hocus-pocus.php | 5 +++-- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/lib/Core.php b/lib/Core.php index 17d9f4f..6bc5a61 100644 --- a/lib/Core.php +++ b/lib/Core.php @@ -721,7 +721,7 @@ protected static function compile ( $stream ) { // Main processing on the rule objects self::processRules(); - csscrush::log( csscrush::$storage->tokens->rules ); + // csscrush::log( csscrush::$storage->tokens->rules ); csscrush::log( array_keys( self::$process->selectorRelationships ) ); // Alias any @-rules @@ -999,14 +999,10 @@ public static function processRules () { foreach ( self::$storage->tokens->rules as $rule ) { - if ( ! $rule->isNested ) { - - // Associate selectors with the rule - foreach ( $rule->selectorList as $selector ) { - - // $readable_selector = $selector->makeReadableValue(); - self::$process->selectorRelationships[ $selector->readableValue ] = $rule; - } + // Associate selectors with the rule + foreach ( $rule->selectorList as $selector ) { + + self::$process->selectorRelationships[ $selector->readableValue ] = $rule; } // Find previous selectors and apply the @@ -1171,7 +1167,7 @@ protected static function cb_printRule ( $match ) { return ''; } - // Build the selector + // Build the selector; uses selector __toString method $selectors = implode( ",$whitespace", $rule->selectorList ); // Build the block diff --git a/lib/Rule.php b/lib/Rule.php index d1d27a3..c671b65 100644 --- a/lib/Rule.php +++ b/lib/Rule.php @@ -92,9 +92,9 @@ public function __construct ( $selector_string = null, $declarations_string ) { } } } - elseif ( $prop === 'extend' ) { + elseif ( $prop === 'extends' ) { - // Extend is a special case + // Extends is a special case $this->setExtendSelectors( $value ); } else { @@ -626,7 +626,7 @@ public function __construct ( $raw_selector, $associated_rule = null ) { public function __toString () { - return $this->value; + return $this->readableValue; } } diff --git a/plugins/hocus-pocus.php b/plugins/hocus-pocus.php index 8e8af47..8eb9603 100644 --- a/plugins/hocus-pocus.php +++ b/plugins/hocus-pocus.php @@ -16,9 +16,10 @@ csscrush_hook::add( 'rule_preprocess', 'csscrush_hocuspocus' ); function csscrush_hocuspocus ( $rule ) { + $adjustments = array( - '!:hocus([^a-z0-9_-])!' => ':any(:hover,:focus)$1', - '!:pocus([^a-z0-9_-])!' => ':any(:hover,:focus,:active)$1', + '!:hocus([^a-z0-9_-]|$)!' => ':any(:hover,:focus)$1', + '!:pocus([^a-z0-9_-]|$)!' => ':any(:hover,:focus,:active)$1', ); $rule->selector_raw = preg_replace( array_keys( $adjustments ), array_values( $adjustments ), $rule->selector_raw ); } From a8eb34ff2b88590090db91dcdb4650794ac92fd1 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 11 May 2012 09:25:36 +0100 Subject: [PATCH 007/421] Added version flag to command line application Errors now output to stderr --- cli.php | 110 +++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 81 insertions(+), 29 deletions(-) diff --git a/cli.php b/cli.php index b57905b..3889596 100644 --- a/cli.php +++ b/cli.php @@ -12,14 +12,37 @@ define( 'STATUS_OK', 0 ); define( 'STATUS_ERROR', 1 ); -// Get stdin input -$stdin = fopen( 'php://stdin', 'r' ); -stream_set_blocking( $stdin, false ) or die ( 'Failed to disable stdin blocking' ); +// Open stream handles +$stdin = fopen( 'php://stdin', 'r' ); +$stdout = fopen( 'php://stdout', 'w' ); +$stderr = fopen( 'php://stderr', 'w' ); + +// Get stdin contents +if ( ! stream_set_blocking( $stdin, false ) ) { + + stderr( 'Failed to disable stdin blocking' ); + exit( STATUS_ERROR ); +} $stdin_contents = stream_get_contents( $stdin ); fclose( $stdin ); -// Open stdout handle -$stdout = fopen( 'php://stdout', 'w' ); + +################################################################## +## Helpers + +function stderr ( $lines, $closing_newline = true ) { + global $stderr; + fwrite( $stderr, + implode( PHP_EOL, (array) $lines ) . ( $closing_newline ? PHP_EOL : '' ) + ); +} + +function stdout ( $lines, $closing_newline = true ) { + global $stdout; + fwrite( $stdout, + implode( PHP_EOL, (array) $lines ) . ( $closing_newline ? PHP_EOL : '' ) + ); +} ################################################################## @@ -27,8 +50,13 @@ $version = PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION; $required_version = 5.3; + if ( $version < $required_version ) { - fwrite( $stdout, "PHP version $required_version or higher is required to use this tool.\nYou are currently running PHP version $version\n\n" ); + + stderr( array( + "PHP version $required_version or higher is required to use this tool.", + "You are currently running PHP version $version" ) + ); exit( STATUS_ERROR ); } @@ -50,6 +78,7 @@ 'pretty', // Pretty formatting 'boilerplate', // Output boilerplate 'help', // Display help + 'version', // Display version 'vendor-target:', // Vendor target 'variables:', // Map of variable names in an http query string format ); @@ -63,21 +92,21 @@ $boilerplate = @( isset( $opts['b'] ) ?: isset( $opts['boilerplate'] ) ); $pretty = @( isset( $opts['p'] ) ?: isset( $opts['pretty'] ) ); $help_flag = @( isset( $opts['h'] ) ?: isset( $opts['help'] ) ); +$version_flag = @isset( $opts['version'] ); ################################################################## ## Help page -// $command = $argv[0] == 'csscrush' ? 'csscrush' : 'php path/to/CssCrush/cli.php'; $command = 'csscrush'; $help = <<version ); + exit( STATUS_OK ); +} + if ( $help_flag ) { - fwrite( $stdout, $help ); + + stdout( $help ); exit( STATUS_OK ); } @@ -123,17 +162,22 @@ $input = null; if ( $input_file ) { + if ( ! file_exists( $input_file ) ) { - fwrite( $stdout, "Input file not found\n\n" ); + + stdout( 'Input file not found' . PHP_EOL ); exit( STATUS_ERROR ); } $input = file_get_contents( $input_file ); } elseif ( $stdin_contents ) { + $input = $stdin_contents; } else { - fwrite( $stdout, $help ); + + // No input, just output help screen + stdout( $help ); exit( STATUS_OK ); } @@ -142,21 +186,25 @@ ## Processing $process_opts = array(); +$process_opts[ 'boilerplate' ] = $boilerplate ? true : false; +$process_opts[ 'debug' ] = $pretty ? true : false; +$process_opts[ 'rewrite_import_urls' ] = true; + if ( $vendor_target ) { + $process_opts[ 'vendor_target' ] = $vendor_target; } if ( $variables ) { + parse_str( $variables, $in_vars ); $process_opts[ 'vars' ] = $in_vars; } -$process_opts[ 'boilerplate' ] = $boilerplate ? true : false; -$process_opts[ 'debug' ] = $pretty ? true : false; -$process_opts[ 'rewrite_import_urls' ] = true; $import_context = $input_file ? dirname( realpath( $input_file ) ) : null; // If there is an import context set it to the document root if ( $import_context ) { + $old_doc_root = csscrush::$config->docRoot; csscrush::$config->docRoot = $import_context; $process_opts[ 'import_context' ] = $import_context; @@ -167,6 +215,7 @@ // Reset the document root after processing if ( $import_context ) { + csscrush::$config->docRoot = $old_doc_root; } @@ -175,26 +224,29 @@ ## Output if ( $output_file ) { + if ( ! @file_put_contents( $output_file, $output ) ) { - $message = "Could not write to path '$output_file'\n"; + + $message[] = "Could not write to path '$output_file'"; + if ( strpos( $output_file, '~' ) === 0 ) { - $message .= "Tilde expansion does not work\n"; + + $message[] = 'Tilde expansion does not work here'; } - fwrite( $stdout, $message ); + + stderr( $message ); exit( STATUS_ERROR ); } } else { - $output .= "\n"; - fwrite( $stdout, $output ); -} - -exit( STATUS_OK ); - - - + if ( csscrush::$process->errors ) { + stderr( csscrush::$process->errors ); + } + stdout( $output ); + exit( STATUS_OK ); +} From 34457d5e6fd81fc2a7543df17f3128395481365f Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 14 May 2012 12:05:29 +0100 Subject: [PATCH 008/421] Bug fixes Inheritance now recursive Fixed mixin argument parsing issue Expanded version ID to include word characters --- CssCrush.php | 2 +- lib/Core.php | 4 ++-- lib/Mixin.php | 4 ++-- lib/Rule.php | 45 ++++++++++++++++++++++++++++++++++++--------- 4 files changed, 41 insertions(+), 14 deletions(-) diff --git a/CssCrush.php b/CssCrush.php index 2b74ab7..25e33d7 100644 --- a/CssCrush.php +++ b/CssCrush.php @@ -4,7 +4,7 @@ * CSS Crush * Extensible CSS preprocessor * - * @version 1.5 + * @version 1.5-beta * @link https://github.com/peteboere/css-crush * @license http://www.opensource.org/licenses/mit-license.php (MIT) * @copyright Copyright 2010-2012 Pete Boere diff --git a/lib/Core.php b/lib/Core.php index 6bc5a61..263e010 100644 --- a/lib/Core.php +++ b/lib/Core.php @@ -30,7 +30,7 @@ public static function init ( $seed_file ) { // Get version ID from seed file $seed_file_contents = file_get_contents( $seed_file ); - $match_count = preg_match( '!@version\s+([\d\.]+)!', $seed_file_contents, $version_match ); + $match_count = preg_match( '!@version\s+([\d\.\w-]+)!', $seed_file_contents, $version_match ); self::$config->version = $match_count ? $version_match[1] : null; // Set the docRoot reference @@ -1125,7 +1125,7 @@ protected static function cb_extractRules ( $match ) { $rule = new csscrush_rule( $rule->selector_raw, $rule->declaration_raw ); // Store rules if they have declarations or extend arguments - if ( $rule->_declarations || $rule->extends ) { + if ( $rule->_declarations || $rule->extendsArgs ) { $label = $rule->label; diff --git a/lib/Mixin.php b/lib/Mixin.php index e83fc12..1fcbb4d 100644 --- a/lib/Mixin.php +++ b/lib/Mixin.php @@ -93,9 +93,9 @@ public static function parseSingleValue ( $message ) { return false; } - // Discard the name part and any enclosing parens + // Discard the name part and any wrapping parens and whitespace $message = substr( $message, strlen( $mixin_name ) ); - $message = trim( $message, ' \r\n\t()' ); + $message = preg_replace( '!^\s*\(?\s*|\s*\)?\s*$!', '', $message ); // e.g. "value, rgba(0,0,0,0), left 100%" diff --git a/lib/Rule.php b/lib/Rule.php index c671b65..08f09e5 100644 --- a/lib/Rule.php +++ b/lib/Rule.php @@ -21,6 +21,9 @@ class csscrush_rule implements IteratorAggregate { public $comments = array(); // Arugments passed in via 'extend' property + public $extendsArgs = array(); + + // Record of actual inherited rules public $extends = array(); public $_declarations = array(); @@ -352,7 +355,7 @@ public function expandSelectors () { // Not creating a named rule association with this expanded selector $new_set[] = new csscrush_selector( $row . $selector->value ); } - + // Store the unexpanded selector to selectorRelationships csscrush::$process->selectorRelationships[ $readableValue ] = $this; } @@ -361,9 +364,9 @@ public function expandSelectors () { // Nothing to expand $new_set[ $readableValue ] = $selector; } - + } // foreach - + $this->selectorList = $new_set; } @@ -380,17 +383,24 @@ public function setExtendSelectors ( $raw_value ) { if ( preg_match( csscrush_regex::$patt->name, $arg ) ) { // A regular name: an element name or an abstract rule name - $this->extends[ $arg ] = true; + $this->extendsArgs[ $arg ] = true; } else { // Not a regular name: Some kind of selector so normalize it for later comparison $readable_selector = csscrush_selector::makeReadableSelector( $arg ); - $this->extends[ $readable_selector ] = true; + $this->extendsArgs[ $readable_selector ] = true; } } - csscrush::log( $this->extends ); + csscrush::log( $this->extendsArgs ); + } + + public static function recursiveExtend ( $rule ) { + foreach ( $rule->extends as $parent_rule ) { + $parent_rule->addSelectors( $rule->selectorList ); + self::recursiveExtend( $parent_rule ); + } } public function applyExtendables () { @@ -398,17 +408,33 @@ public function applyExtendables () { $abstracts = csscrush::$process->abstracts; $selectorRelationships = csscrush::$process->selectorRelationships; - foreach ( $this->extends as $name => $bool ) { + foreach ( $this->extendsArgs as $name => $bool ) { if ( isset( $abstracts[ $name ] ) ) { + $parent_abstract_rule = $abstracts[ $name ]; + // Match found in abstract rules - $abstracts[ $name ]->addSelectors( $this->selectorList ); + $parent_abstract_rule->addSelectors( $this->selectorList ); + + // Store a reference to directly extended rules + $this->extends[] = $parent_abstract_rule; + + // Recursively inherit + self::recursiveExtend( $parent_abstract_rule ); } else if ( isset( $selectorRelationships[ $name ] ) ) { + $parent_rule = $selectorRelationships[ $name ]; + // Match found in selectorRelationships - $selectorRelationships[ $name ]->addSelectors( $this->selectorList ); + $parent_rule->addSelectors( $this->selectorList ); + + // Store a reference to directly extended rules + $this->extends[] = $parent_rule; + + // Recursively inherit + self::recursiveExtend( $parent_rule ); } } } @@ -423,6 +449,7 @@ public function addSelectors ( $list ) { $this->selectorList = array_merge( $this->selectorList, $list ); } + ############ # IteratorAggregate From f191cf87469d0eff49a90caa4d4b7a9a236e0146 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 21 May 2012 12:02:54 +0100 Subject: [PATCH 009/421] Updated aliases --- Aliases.ini | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/Aliases.ini b/Aliases.ini index ddf1080..5fda7ab 100644 --- a/Aliases.ini +++ b/Aliases.ini @@ -1,5 +1,6 @@ + ;---------------------------------------------------------------------------- -; Enable/disable aliases by commenting them (with semicolons) +; Enable/disable aliases to suit your needs ;---------------------------------------------------------------------------- ;---------------------------------------------------------------------------- @@ -11,30 +12,39 @@ animation[] = -webkit-animation animation[] = -moz-animation animation[] = -ms-animation + animation[] = -o-animation animation-delay[] = -webkit-animation-delay animation-delay[] = -moz-animation-delay animation-delay[] = -ms-animation-delay + animation-delay[] = -o-animation-delay animation-direction[] = -webkit-animation-direction animation-direction[] = -moz-animation-direction animation-direction[] = -ms-animation-direction + animation-direction[] = -o-animation-direction animation-duration[] = -webkit-animation-duration animation-duration[] = -moz-animation-duration animation-duration[] = -ms-animation-duration + animation-duration[] = -o-animation-duration animation-fill-mode[] = -webkit-animation-fill-mode animation-fill-mode[] = -moz-animation-fill-mode animation-fill-mode[] = -ms-animation-fill-mode + animation-fill-mode[] = -o-animation-fill-mode animation-iteration-count[] = -webkit-animation-iteration-count animation-iteration-count[] = -moz-animation-iteration-count animation-iteration-count[] = -ms-animation-iteration-count + animation-iteration-count[] = -o-animation-iteration-count animation-name[] = -webkit-animation-name animation-name[] = -moz-animation-name animation-name[] = -ms-animation-name + animation-name[] = -o-animation-name animation-play-state[] = -webkit-animation-play-state animation-play-state[] = -moz-animation-play-state animation-play-state[] = -ms-animation-play-state + animation-play-state[] = -o-animation-play-state animation-timing-function[] = -webkit-animation-timing-function animation-timing-function[] = -moz-animation-timing-function animation-timing-function[] = -ms-animation-timing-function + animation-timing-function[] = -o-animation-timing-function ; Backface visibility backface-visibility[] = -webkit-backface-visibility @@ -124,6 +134,13 @@ hyphens[] = -moz-hyphens hyphens[] = -ms-hyphens + ; Outline radius + outline-radius[] = -moz-outline-radius + outline-top-left-radius[] = -moz-outline-radius-topleft + outline-top-right-radius[] = -moz-outline-radius-topright + outline-bottom-left-radius[] = -moz-outline-radius-bottomleft + outline-bottom-right-radius[] = -moz-outline-radius-bottomright + ; Perspective perspective[] = -webkit-perspective perspective[] = -moz-perspective @@ -133,7 +150,6 @@ perspective-origin[] = -ms-perspective-origin ; Tab size - tab-size[] = -webkit-tab-size tab-size[] = -moz-tab-size tab-size[] = -o-tab-size @@ -150,6 +166,10 @@ transform[] = -moz-transform transform[] = -ms-transform transform[] = -o-transform + transform-origin[] = -webkit-transform-origin + transform-origin[] = -moz-transform-origin + transform-origin[] = -ms-transform-origin + transform-origin[] = -o-transform-origin transform-style[] = -webkit-transform-style transform-style[] = -moz-transform-style transform-style[] = -ms-transform-style @@ -232,8 +252,10 @@ ;-- @rule aliases [at-rules] + keyframes[] = -webkit-keyframes keyframes[] = -moz-keyframes keyframes[] = -ms-keyframes + keyframes[] = -o-keyframes From 1cad4cd1badee312437aaf85000e0b03ebc46351 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 21 May 2012 13:41:08 +0100 Subject: [PATCH 010/421] Updated version string --- CHANGELOG.txt | 2 +- CssCrush.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 1faeab2..b06a2b2 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -8,7 +8,7 @@ Abstracted IO interface Added some error reporting Added spiffing.css plugin csscrush_tag method now uses media type 'all' by default -Updated Aliases.ini / initial-values.ini +Updated alias and initial-value tables Internal refactoring Resolved issue #23 Resolved issue #24 diff --git a/CssCrush.php b/CssCrush.php index 25e33d7..2b74ab7 100644 --- a/CssCrush.php +++ b/CssCrush.php @@ -4,7 +4,7 @@ * CSS Crush * Extensible CSS preprocessor * - * @version 1.5-beta + * @version 1.5 * @link https://github.com/peteboere/css-crush * @license http://www.opensource.org/licenses/mit-license.php (MIT) * @copyright Copyright 2010-2012 Pete Boere From f7c0549d2f2e875eb8c63161f62e7a9b42f6f938 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 24 May 2012 12:07:54 +0100 Subject: [PATCH 011/421] Fixed issue with selector grouping and inheritance in combination --- CssCrush.php | 2 +- lib/Core.php | 29 ++++++++++++++--------------- lib/Rule.php | 5 +++-- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/CssCrush.php b/CssCrush.php index 2b74ab7..4c9daf3 100644 --- a/CssCrush.php +++ b/CssCrush.php @@ -4,7 +4,7 @@ * CSS Crush * Extensible CSS preprocessor * - * @version 1.5 + * @version 1.5.1-beta * @link https://github.com/peteboere/css-crush * @license http://www.opensource.org/licenses/mit-license.php (MIT) * @copyright Copyright 2010-2012 Pete Boere diff --git a/lib/Core.php b/lib/Core.php index 263e010..789cc38 100644 --- a/lib/Core.php +++ b/lib/Core.php @@ -56,7 +56,7 @@ public static function setDocRoot ( $doc_root = null ) { // Get document_root reference // $_SERVER['DOCUMENT_ROOT'] is unreliable in certain CGI/Apache/IIS setups - + if ( ! $doc_root ) { $script_filename = $_SERVER[ 'SCRIPT_FILENAME' ]; @@ -298,7 +298,6 @@ public static function tag ( $file, $options = null, $attributes = array() ) { if ( ! isset( $attributes[ 'media' ] ) ) { $attributes[ 'media' ] = 'all'; } - $attr_string = csscrush_util::htmlAttributes( $attributes ); return "\n"; } @@ -713,14 +712,14 @@ protected static function compile ( $stream ) { $stream = "\n" . str_replace( array_keys( $map ), array_values( $map ), $stream ); // Rules - $stream = self::extractAndProcessRules( $stream ); + $stream = self::extractRules( $stream ); // Process any @-in blocks $stream = self::prefixSelectors( $stream ); // Main processing on the rule objects self::processRules(); - + // csscrush::log( csscrush::$storage->tokens->rules ); csscrush::log( array_keys( self::$process->selectorRelationships ) ); @@ -943,7 +942,7 @@ protected static function prefixSelectors ( $stream ) { // Get the rule instance $rule = csscrush_rule::get( $rule_match[0][0] ); - + // Set the isNested flag $rule->isNested = true; @@ -974,7 +973,7 @@ protected static function prefixSelectors ( $stream ) { $new_selector_list[] = new csscrush_selector( $new_value ); } else { - + // Not storing the selector as named $new_selector_list[] = new csscrush_selector( "$arg_selector {$rule_selector->value}" ); } @@ -996,7 +995,7 @@ public static function tokenLabelCreate ( $prefix ) { } public static function processRules () { - + foreach ( self::$storage->tokens->rules as $rule ) { // Associate selectors with the rule @@ -1005,9 +1004,6 @@ public static function processRules () { self::$process->selectorRelationships[ $selector->readableValue ] = $rule; } - // Find previous selectors and apply the - $rule->applyExtendables(); - csscrush_hook::run( 'rule_prealias', $rule ); if ( ! empty( self::$config->aliases ) ) { @@ -1020,6 +1016,9 @@ public static function processRules () { $rule->expandSelectors(); + // Find previous selectors and apply them + $rule->applyExtendables(); + csscrush_hook::run( 'rule_postprocess', $rule ); } } @@ -1128,9 +1127,9 @@ protected static function cb_extractRules ( $match ) { if ( $rule->_declarations || $rule->extendsArgs ) { $label = $rule->label; - + self::$storage->tokens->rules[ $label ] = $rule; - + if ( $rule->_declarations ) { return $label . "\n"; } @@ -1154,7 +1153,7 @@ protected static function cb_printRule ( $match ) { $whitespace = $minify ? '' : ' '; $ruleLabel = $match[0]; - + // If no rule matches the label return empty string if ( ! isset( self::$storage->tokens->rules[ $ruleLabel ] ) ) { return ''; @@ -1194,7 +1193,7 @@ protected static function cb_printRule ( $match ) { ############ # Parsing methods - public static function extractAndProcessRules ( $stream ) { + public static function extractRules ( $stream ) { return preg_replace_callback( csscrush_regex::$patt->rule, array( 'self', 'cb_extractRules' ), $stream ); } @@ -1259,7 +1258,7 @@ public static function extractFragments ( $stream ) { // The matched fragment name $fragment_name = $match[1][0]; - + // The fragment object, or null if name not present $fragment = isset( self::$process->fragments[ $fragment_name ] ) ? self::$process->fragments[ $fragment_name ] : null; diff --git a/lib/Rule.php b/lib/Rule.php index 08f09e5..9721a29 100644 --- a/lib/Rule.php +++ b/lib/Rule.php @@ -11,7 +11,7 @@ class csscrush_rule implements IteratorAggregate { public $isNested; public $label; - public $abstractName; + // public $abstractName; public $properties = array(); @@ -393,7 +393,7 @@ public function setExtendSelectors ( $raw_value ) { $this->extendsArgs[ $readable_selector ] = true; } } - csscrush::log( $this->extendsArgs ); + // csscrush::log( $this->extendsArgs ); } public static function recursiveExtend ( $rule ) { @@ -422,6 +422,7 @@ public function applyExtendables () { // Recursively inherit self::recursiveExtend( $parent_abstract_rule ); + } else if ( isset( $selectorRelationships[ $name ] ) ) { From d48e2c4e93245c68e5b0e4627dd8ea83b10a40e2 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 25 May 2012 09:20:39 +0100 Subject: [PATCH 012/421] Extended mixins to work with abstract rules and regular rules --- CHANGELOG.txt | 6 ++++ lib/Core.php | 10 +++---- lib/Mixin.php | 82 ++++++++++++++++++++++++++++++++++++++------------- lib/Rule.php | 11 +++++++ 4 files changed, 84 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index b06a2b2..c218533 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,9 @@ +1.5.1 +----- +Extended mixins to work with abstract rules and regular rules +Fixed issue with selector grouping and inheritance in combination + + 1.5 --- New feature: Rule inheritance / abstract rules diff --git a/lib/Core.php b/lib/Core.php index 789cc38..600d0b1 100644 --- a/lib/Core.php +++ b/lib/Core.php @@ -996,13 +996,13 @@ public static function tokenLabelCreate ( $prefix ) { public static function processRules () { - foreach ( self::$storage->tokens->rules as $rule ) { + // Reset the selector relationships + self::$process->selectorRelationships = array(); - // Associate selectors with the rule - foreach ( $rule->selectorList as $selector ) { + foreach ( self::$storage->tokens->rules as $rule ) { - self::$process->selectorRelationships[ $selector->readableValue ] = $rule; - } + // Store selector relationships + $rule->indexSelectors(); csscrush_hook::run( 'rule_prealias', $rule ); diff --git a/lib/Mixin.php b/lib/Mixin.php index 1fcbb4d..e80e230 100644 --- a/lib/Mixin.php +++ b/lib/Mixin.php @@ -78,38 +78,80 @@ public function call ( array $args ) { public static function parseSingleValue ( $message ) { $message = ltrim( $message ); + $mixin = null; + $non_mixin = null; - // e.g. mymixin( 50px, rgba(0,0,0,0), left 100% ) + // e.g. + // - mymixin( 50px, rgba(0,0,0,0), left 100% ) + // - abstract-rule + // - #selector - if ( preg_match( '!^[a-zA-Z0-9_-]+!', $message, $name_match ) ) { + // Test for leading name + if ( preg_match( '!^[\w-]+!', $message, $name_match ) ) { - $mixin_name = $name_match[0]; + $name = $name_match[0]; - if ( isset( csscrush::$process->mixins[ $mixin_name ] ) ) { - $mixin = csscrush::$process->mixins[ $mixin_name ]; + if ( isset( csscrush::$process->mixins[ $name ] ) ) { + + // Mixin match + $mixin = csscrush::$process->mixins[ $name ]; } - else { - // No mixin found with that name - return false; + elseif ( isset( csscrush::$process->abstracts[ $name ] ) ) { + + // Abstract rule match + $non_mixin = csscrush::$process->abstracts[ $name ]; } + } - // Discard the name part and any wrapping parens and whitespace - $message = substr( $message, strlen( $mixin_name ) ); - $message = preg_replace( '!^\s*\(?\s*|\s*\)?\s*$!', '', $message ); + // If no mixin or abstract rule matched, look for matching selector + if ( ! $mixin && ! $non_mixin ) { - // e.g. "value, rgba(0,0,0,0), left 100%" + $selector_test = csscrush_selector::makeReadableSelector( $message ); + // csscrush::log( array_keys( csscrush::$process->selectorRelationships ) ); - // Determine what raw arguments there are to pass to the mixin - $args = array(); - if ( $message !== '' ) { - $args = csscrush_util::splitDelimList( $message, ',', true, true ); - // $args = array_map( 'trim', $args->list ); - $args = $args->list; + if ( isset( csscrush::$process->selectorRelationships[ $selector_test ] ) ) { + $non_mixin = csscrush::$process->selectorRelationships[ $selector_test ]; } + } + + // If no mixin matched, but matched alternative, use alternative + if ( ! $mixin ) { + + if ( $non_mixin ) { - return $mixin->call( $args ); + // Return expected format + $result = array(); + foreach ( $non_mixin as $declaration ) { + $result[] = array( + 'property' => $declaration->property, + 'value' => $declaration->value, + ); + } + return $result; + } + else { + + // Nothing matches + return false; + } } - return false; + + // We have a valid mixin. + // Discard the name part and any wrapping parens and whitespace + $message = substr( $message, strlen( $name ) ); + $message = preg_replace( '!^\s*\(?\s*|\s*\)?\s*$!', '', $message ); + + // e.g. "value, rgba(0,0,0,0), left 100%" + + // Determine what raw arguments there are to pass to the mixin + $args = array(); + if ( $message !== '' ) { + $args = csscrush_util::splitDelimList( $message, ',', true, true ); + // $args = array_map( 'trim', $args->list ); + $args = $args->list; + } + + return $mixin->call( $args ); } diff --git a/lib/Rule.php b/lib/Rule.php index 9721a29..4ba78a0 100644 --- a/lib/Rule.php +++ b/lib/Rule.php @@ -59,6 +59,10 @@ public function __construct ( $selector_string = null, $declarations_string ) { } $this->addSelector( new csscrush_selector( $selector ) ); + + // Store selector relationships + // - This happens twice; on first pass for mixins, second pass is for inheritance + $this->indexSelectors(); } } @@ -370,6 +374,13 @@ public function expandSelectors () { $this->selectorList = $new_set; } + public function indexSelectors () { + + foreach ( $this->selectorList as $selector ) { + csscrush::$process->selectorRelationships[ $selector->readableValue ] = $this; + } + } + public function setExtendSelectors ( $raw_value ) { $abstracts = csscrush::$process->abstracts; From d4b3b28c693ed8512f7e19b66a6490977b8657dc Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 1 Jun 2012 14:56:46 +0100 Subject: [PATCH 013/421] Inheritance now only uses the last extends property in a rule --- CssCrush.php | 2 +- lib/Rule.php | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CssCrush.php b/CssCrush.php index 4c9daf3..cd72b36 100644 --- a/CssCrush.php +++ b/CssCrush.php @@ -4,7 +4,7 @@ * CSS Crush * Extensible CSS preprocessor * - * @version 1.5.1-beta + * @version 1.5.1 * @link https://github.com/peteboere/css-crush * @license http://www.opensource.org/licenses/mit-license.php (MIT) * @copyright Copyright 2010-2012 Pete Boere diff --git a/lib/Rule.php b/lib/Rule.php index 4ba78a0..c894ae8 100644 --- a/lib/Rule.php +++ b/lib/Rule.php @@ -389,6 +389,9 @@ public function setExtendSelectors ( $raw_value ) { // Pass extra argument to trim the returned list $args = csscrush_util::splitDelimList( $raw_value, ',', true, true ); + // Reset if called earlier, last call wins by intention + $this->extendsArgs = array(); + foreach ( $args->list as $arg ) { if ( preg_match( csscrush_regex::$patt->name, $arg ) ) { From 9affb107968ba5c6d23fce1fd733153e205340ba Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 5 Jun 2012 14:53:40 +0100 Subject: [PATCH 014/421] Resolved issue #32 --- CHANGELOG.txt | 5 +++++ CssCrush.php | 2 +- lib/Function.php | 5 ++--- lib/Importer.php | 4 ++-- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index c218533..9fe2ee6 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,8 @@ +1.5.2 +----- +Resolved issue #32 + + 1.5.1 ----- Extended mixins to work with abstract rules and regular rules diff --git a/CssCrush.php b/CssCrush.php index cd72b36..2b4b658 100644 --- a/CssCrush.php +++ b/CssCrush.php @@ -4,7 +4,7 @@ * CSS Crush * Extensible CSS preprocessor * - * @version 1.5.1 + * @version 1.5.2-beta * @link https://github.com/peteboere/css-crush * @license http://www.opensource.org/licenses/mit-license.php (MIT) * @copyright Copyright 2010-2012 Pete Boere diff --git a/lib/Function.php b/lib/Function.php index ca038cd..2246789 100644 --- a/lib/Function.php +++ b/lib/Function.php @@ -328,12 +328,11 @@ public static function css_fn__data_uri ( $input ) { $file = csscrush::$config->docRoot . $input; } else { - $baseDir = csscrush::$config->baseDir; - $file = "$baseDir/$input"; + $file = csscrush::$process->inputDir . "/$input"; } // File not found - if ( !file_exists( $file ) ) { + if ( ! file_exists( $file ) ) { return $result; } diff --git a/lib/Importer.php b/lib/Importer.php index 29bc1b1..be0f00a 100644 --- a/lib/Importer.php +++ b/lib/Importer.php @@ -326,7 +326,7 @@ protected static function rewriteImportRelativeUrls ( $import ) { $url_prefix .= '/'; } - csscrush::log( 'relative_url_prefix: ' . $url_prefix ); + // csscrush::log( 'relative_url_prefix: ' . $url_prefix ); // Search for all relative url and data-uri references in the content // and prepend $relative_url_prefix @@ -352,7 +352,7 @@ protected static function cb_rewriteImportRelativeUrl ( $match ) { $storage = csscrush::$storage; // The relative url prefix - $relative_url_prefix = $storage->tmp->relativeUrlPrefix; + $relative_url_prefix = $storage->misc->relativeUrlPrefix; list( $fullMatch, $before, $function, $url ) = $match; $url = trim( $url ); From 19866563538e2576ab0a8c44b97f2116c9bec02c Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 6 Jun 2012 10:10:31 +0100 Subject: [PATCH 015/421] Set csscrush::inline method to not display boilerplate by default --- CHANGELOG.txt | 1 + lib/Core.php | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 9fe2ee6..7b97e22 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,6 +1,7 @@ 1.5.2 ----- Resolved issue #32 +csscrush::inline method now defaults to not printing a boilerplate 1.5.1 diff --git a/lib/Core.php b/lib/Core.php index 600d0b1..c28f2e0 100644 --- a/lib/Core.php +++ b/lib/Core.php @@ -320,6 +320,14 @@ public static function tag ( $file, $options = null, $attributes = array() ) { */ public static function inline ( $file, $options = null, $attributes = array() ) { + // For inline output set boilerplate to not display by default + if ( ! is_array( $options ) ) { + $options = array(); + } + if ( ! isset( $options[ 'boilerplate' ] ) ) { + $options[ 'boilerplate' ] = false; + } + $file = self::file( $file, $options ); if ( ! empty( $file ) ) { From c2f2f2816cd809c35f7e6edd29b644335091a295 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 7 Jun 2012 09:51:37 +0100 Subject: [PATCH 016/421] Update for issue #35, reducing redundant segments in rewritten paths --- lib/Importer.php | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/lib/Importer.php b/lib/Importer.php index be0f00a..3d00ba2 100644 --- a/lib/Importer.php +++ b/lib/Importer.php @@ -51,7 +51,7 @@ public static function hostfile () { if ( $prependFile = csscrush_util::find( 'Prepend-local.css', 'Prepend.css' ) ) { $stream = file_get_contents( $prependFile ) . $stream; } - + $stream = csscrush::extractComments( $stream ); $stream = csscrush::extractStrings( $stream ); @@ -145,7 +145,7 @@ public static function hostfile () { for ( $index = 0; $index < $matchCount; $index++ ) { $fullMatch = $matchAll[ $index ][0][0]; - + // Url match may be at one of 2 positions if ( $matchAll[ $index ][1][1] == -1 ) { $urlMatch = $matchAll[ $index ][2][0]; @@ -278,7 +278,7 @@ protected static function rewriteImportRelativeUrls ( $import ) { $stream = $import->content; - // We're comparing file system position so we'll + // Normalise the paths $hostDir = csscrush_util::normalizeSystemPath( $import->hostDir, true ); $importDir = csscrush_util::normalizeSystemPath( dirname( $import->path ), true ); @@ -364,6 +364,9 @@ protected static function cb_rewriteImportRelativeUrl ( $match ) { $url = $url_token->value; } + // Normalise the path + $url = csscrush_util::normalizeSystemPath( $url ); + // No rewrite if: // $url begins with a variable, e.g '$(' // $url path is absolute or begins with slash @@ -374,16 +377,22 @@ protected static function cb_rewriteImportRelativeUrl ( $match ) { strpos( $url, '$(' ) === 0 || preg_match( $regex->absoluteUrl, $url ) ) { - // Token or not, it's ok to return the full match if $url is a root relative or absolute ref + // Token or not, it's ok to return the full match + // if $url is a root relative or absolute ref return $fullMatch; } // Prepend the relative url prefix $url = $relative_url_prefix . $url; - // Restore quotes if $url was a string token + // Reduce redundant path segments (issue #35): + // e.g 'foo/../bar' => 'bar' + $url = preg_replace( '![^/.]+/\.\./!', '', $url ); + + // Restore quotes if $url was a string token, update the token and return it if ( $url_is_token ) { - $url = $url_token->quoteMark . $url . $url_token->quoteMark; + $url_token->update( $url_token->quoteMark . $url . $url_token->quoteMark ); + $url = $url_token->token; } // Reconstruct the match and return From 60ebe4e989784cbfdc1b6ebcf9a4dc170acf2ca4 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 7 Jun 2012 09:51:37 +0100 Subject: [PATCH 017/421] Update for issue #32, reducing redundant segments in rewritten paths --- lib/Importer.php | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/lib/Importer.php b/lib/Importer.php index be0f00a..3d00ba2 100644 --- a/lib/Importer.php +++ b/lib/Importer.php @@ -51,7 +51,7 @@ public static function hostfile () { if ( $prependFile = csscrush_util::find( 'Prepend-local.css', 'Prepend.css' ) ) { $stream = file_get_contents( $prependFile ) . $stream; } - + $stream = csscrush::extractComments( $stream ); $stream = csscrush::extractStrings( $stream ); @@ -145,7 +145,7 @@ public static function hostfile () { for ( $index = 0; $index < $matchCount; $index++ ) { $fullMatch = $matchAll[ $index ][0][0]; - + // Url match may be at one of 2 positions if ( $matchAll[ $index ][1][1] == -1 ) { $urlMatch = $matchAll[ $index ][2][0]; @@ -278,7 +278,7 @@ protected static function rewriteImportRelativeUrls ( $import ) { $stream = $import->content; - // We're comparing file system position so we'll + // Normalise the paths $hostDir = csscrush_util::normalizeSystemPath( $import->hostDir, true ); $importDir = csscrush_util::normalizeSystemPath( dirname( $import->path ), true ); @@ -364,6 +364,9 @@ protected static function cb_rewriteImportRelativeUrl ( $match ) { $url = $url_token->value; } + // Normalise the path + $url = csscrush_util::normalizeSystemPath( $url ); + // No rewrite if: // $url begins with a variable, e.g '$(' // $url path is absolute or begins with slash @@ -374,16 +377,22 @@ protected static function cb_rewriteImportRelativeUrl ( $match ) { strpos( $url, '$(' ) === 0 || preg_match( $regex->absoluteUrl, $url ) ) { - // Token or not, it's ok to return the full match if $url is a root relative or absolute ref + // Token or not, it's ok to return the full match + // if $url is a root relative or absolute ref return $fullMatch; } // Prepend the relative url prefix $url = $relative_url_prefix . $url; - // Restore quotes if $url was a string token + // Reduce redundant path segments (issue #35): + // e.g 'foo/../bar' => 'bar' + $url = preg_replace( '![^/.]+/\.\./!', '', $url ); + + // Restore quotes if $url was a string token, update the token and return it if ( $url_is_token ) { - $url = $url_token->quoteMark . $url . $url_token->quoteMark; + $url_token->update( $url_token->quoteMark . $url . $url_token->quoteMark ); + $url = $url_token->token; } // Reconstruct the match and return From 43dc6032470b7e3d5a8dc0f0609bfca9c9909ffd Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 8 Jun 2012 11:07:47 +0100 Subject: [PATCH 018/421] Removed aliases for IE animation, transitions and gradients --- Aliases.ini | 21 ++------------------- CHANGELOG.txt | 1 + CssCrush.php | 2 +- Plugins.ini | 6 +++++- lib/Importer.php | 2 +- 5 files changed, 10 insertions(+), 22 deletions(-) diff --git a/Aliases.ini b/Aliases.ini index 5fda7ab..9728edf 100644 --- a/Aliases.ini +++ b/Aliases.ini @@ -1,6 +1,8 @@ ;---------------------------------------------------------------------------- +; ; Enable/disable aliases to suit your needs +; ;---------------------------------------------------------------------------- ;---------------------------------------------------------------------------- @@ -11,39 +13,30 @@ ; Animations animation[] = -webkit-animation animation[] = -moz-animation - animation[] = -ms-animation animation[] = -o-animation animation-delay[] = -webkit-animation-delay animation-delay[] = -moz-animation-delay - animation-delay[] = -ms-animation-delay animation-delay[] = -o-animation-delay animation-direction[] = -webkit-animation-direction animation-direction[] = -moz-animation-direction - animation-direction[] = -ms-animation-direction animation-direction[] = -o-animation-direction animation-duration[] = -webkit-animation-duration animation-duration[] = -moz-animation-duration - animation-duration[] = -ms-animation-duration animation-duration[] = -o-animation-duration animation-fill-mode[] = -webkit-animation-fill-mode animation-fill-mode[] = -moz-animation-fill-mode - animation-fill-mode[] = -ms-animation-fill-mode animation-fill-mode[] = -o-animation-fill-mode animation-iteration-count[] = -webkit-animation-iteration-count animation-iteration-count[] = -moz-animation-iteration-count - animation-iteration-count[] = -ms-animation-iteration-count animation-iteration-count[] = -o-animation-iteration-count animation-name[] = -webkit-animation-name animation-name[] = -moz-animation-name - animation-name[] = -ms-animation-name animation-name[] = -o-animation-name animation-play-state[] = -webkit-animation-play-state animation-play-state[] = -moz-animation-play-state - animation-play-state[] = -ms-animation-play-state animation-play-state[] = -o-animation-play-state animation-timing-function[] = -webkit-animation-timing-function animation-timing-function[] = -moz-animation-timing-function - animation-timing-function[] = -ms-animation-timing-function animation-timing-function[] = -o-animation-timing-function ; Backface visibility @@ -177,23 +170,18 @@ ; Transitions transition[] = -webkit-transition transition[] = -moz-transition - transition[] = -ms-transition transition[] = -o-transition transition-delay[] = -webkit-transition-delay transition-delay[] = -moz-transition-delay - transition-delay[] = -ms-transition-delay transition-delay[] = -o-transition-delay transition-duration[] = -webkit-transition-duration transition-duration[] = -moz-transition-duration - transition-duration[] = -ms-transition-duration transition-duration[] = -o-transition-duration transition-property[] = -webkit-transition-property transition-property[] = -moz-transition-property - transition-property[] = -ms-transition-property transition-property[] = -o-transition-property transition-timing-function[] = -webkit-transition-timing-function transition-timing-function[] = -moz-transition-timing-function - transition-timing-function[] = -ms-transition-timing-function transition-timing-function[] = -o-transition-timing-function ; User select (non standard) @@ -230,21 +218,17 @@ ; Gradients linear-gradient[] = -webkit-linear-gradient linear-gradient[] = -moz-linear-gradient - linear-gradient[] = -ms-linear-gradient linear-gradient[] = -o-linear-gradient radial-gradient[] = -webkit-radial-gradient radial-gradient[] = -moz-radial-gradient - radial-gradient[] = -ms-radial-gradient radial-gradient[] = -o-radial-gradient ; Repeating gradients repeating-linear-gradient[] = -webkit-repeating-linear-gradient repeating-linear-gradient[] = -moz-repeating-linear-gradient - repeating-linear-gradient[] = -ms-repeating-linear-gradient repeating-linear-gradient[] = -o-repeating-linear-gradient repeating-radial-gradient[] = -webkit-repeating-radial-gradient repeating-radial-gradient[] = -moz-repeating-radial-gradient - repeating-radial-gradient[] = -ms-repeating-radial-gradient repeating-radial-gradient[] = -o-repeating-radial-gradient @@ -255,7 +239,6 @@ keyframes[] = -webkit-keyframes keyframes[] = -moz-keyframes - keyframes[] = -ms-keyframes keyframes[] = -o-keyframes diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 7b97e22..eb4d73c 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -2,6 +2,7 @@ ----- Resolved issue #32 csscrush::inline method now defaults to not printing a boilerplate +Updated aliases file 1.5.1 diff --git a/CssCrush.php b/CssCrush.php index 2b4b658..2d2c5c1 100644 --- a/CssCrush.php +++ b/CssCrush.php @@ -4,7 +4,7 @@ * CSS Crush * Extensible CSS preprocessor * - * @version 1.5.2-beta + * @version 1.5.2 * @link https://github.com/peteboere/css-crush * @license http://www.opensource.org/licenses/mit-license.php (MIT) * @copyright Copyright 2010-2012 Pete Boere diff --git a/Plugins.ini b/Plugins.ini index 55d54d9..4a208ce 100644 --- a/Plugins.ini +++ b/Plugins.ini @@ -1,5 +1,9 @@ + ;---------------------------------------------------------------- -; Enable/disable plugins by commenting them (with semicolons) +; +; Enable/disable plugins to suit your needs. +; See plugin docs for details: https://github.com/peteboere/css-crush/wiki/Plugins +; ;---------------------------------------------------------------- ; min-height shim for IE < 7 diff --git a/lib/Importer.php b/lib/Importer.php index 3d00ba2..ae52e84 100644 --- a/lib/Importer.php +++ b/lib/Importer.php @@ -385,7 +385,7 @@ protected static function cb_rewriteImportRelativeUrl ( $match ) { // Prepend the relative url prefix $url = $relative_url_prefix . $url; - // Reduce redundant path segments (issue #35): + // Reduce redundant path segments (issue #32): // e.g 'foo/../bar' => 'bar' $url = preg_replace( '![^/.]+/\.\./!', '', $url ); From 07d280a2fddbb3d2701d422d4aafa43080303013 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 11 Jun 2012 17:27:49 +0100 Subject: [PATCH 019/421] Added option to rewrite import urls to absolute --- CssCrush.php | 2 +- lib/Core.php | 36 ++++++++++++++++++++---------- lib/Importer.php | 20 ++++++++++------- lib/Regex.php | 1 + lib/Util.php | 57 +++++++++++++++++++++++++++++++++++------------- 5 files changed, 80 insertions(+), 36 deletions(-) diff --git a/CssCrush.php b/CssCrush.php index 2d2c5c1..2e3be98 100644 --- a/CssCrush.php +++ b/CssCrush.php @@ -4,7 +4,7 @@ * CSS Crush * Extensible CSS preprocessor * - * @version 1.5.2 + * @version 1.5.3-beta * @link https://github.com/peteboere/css-crush * @license http://www.opensource.org/licenses/mit-license.php (MIT) * @copyright Copyright 2010-2012 Pete Boere diff --git a/lib/Core.php b/lib/Core.php index c28f2e0..56ebffc 100644 --- a/lib/Core.php +++ b/lib/Core.php @@ -92,7 +92,7 @@ public static function setDocRoot ( $doc_root = null ) { } } - self::$config->docRoot = csscrush_util::normalizeSystemPath( $doc_root ); + self::$config->docRoot = csscrush_util::normalizePath( $doc_root ); } @@ -538,8 +538,8 @@ protected static function getOptions ( $options ) { 'vendor_target' => 'all', // Whether to rewrite the url references inside imported files - // This will be 'true' by default eventually - 'rewrite_import_urls' => false, + // This will probably be 'true' by default eventually + 'rewrite_import_urls' => true, // Keeping track of global vars internally '_globalVars' => self::$config->vars, @@ -670,6 +670,7 @@ protected static function reset ( $options = null ) { 'rules' => array(), 'parens' => array(), 'mixinArgs' => array(), + 'urls' => array(), ); self::$storage->variables = array(); self::$storage->misc = new stdclass(); @@ -728,8 +729,8 @@ protected static function compile ( $stream ) { // Main processing on the rule objects self::processRules(); - // csscrush::log( csscrush::$storage->tokens->rules ); - csscrush::log( array_keys( self::$process->selectorRelationships ) ); + // csscrush::log( csscrush::$storage->tokens->urls ); + // csscrush::log( array_keys( self::$process->selectorRelationships ) ); // Alias any @-rules $stream = self::aliasAtRules( $stream ); @@ -773,9 +774,7 @@ protected static function display ( $stream ) { $stream = preg_replace_callback( $regex->ruleToken, array( 'self', 'cb_printRule' ), $stream ); // Insert parens - $paren_labels = array_keys( self::$storage->tokens->parens ); - $paren_values = array_values( self::$storage->tokens->parens ); - $stream = str_replace( $paren_labels, $paren_values, $stream ); + $stream = csscrush_util::strReplaceHash( $stream, self::$storage->tokens->parens ); if ( $minify ) { $stream = self::minify( $stream ); @@ -792,10 +791,23 @@ protected static function display ( $stream ) { $stream = preg_replace( '!\n{3,}!', "\n\n", $stream ); } - // Insert literals - $string_labels = array_keys( self::$storage->tokens->strings ); - $string_values = array_values( self::$storage->tokens->strings ); - $stream = str_replace( $string_labels, $string_values, $stream ); + // Insert URLs + if ( self::$storage->tokens->urls ) { + + // Clean-up rewritten URLs + foreach ( csscrush::$storage->tokens->urls as $token => $url ) { + + // Optionally set the URLs to absolute + if ( self::$options[ 'rewrite_import_urls' ] === 'absolute' ) { + $url = self::$process->inputDirUrl . '/' . $url; + } + csscrush::$storage->tokens->urls[ $token ] = csscrush_util::cleanUpUrl( $url ); + } + $stream = csscrush_util::strReplaceHash( $stream, self::$storage->tokens->urls ); + } + + // Insert string literals + $stream = csscrush_util::strReplaceHash( $stream, self::$storage->tokens->strings ); // I think we're done return $stream; diff --git a/lib/Importer.php b/lib/Importer.php index ae52e84..8a04ed1 100644 --- a/lib/Importer.php +++ b/lib/Importer.php @@ -279,8 +279,8 @@ protected static function rewriteImportRelativeUrls ( $import ) { $stream = $import->content; // Normalise the paths - $hostDir = csscrush_util::normalizeSystemPath( $import->hostDir, true ); - $importDir = csscrush_util::normalizeSystemPath( dirname( $import->path ), true ); + $hostDir = csscrush_util::normalizePath( $import->hostDir, true ); + $importDir = csscrush_util::normalizePath( dirname( $import->path ), true ); csscrush::$storage->misc->relativeUrlPrefix = ''; $url_prefix = ''; @@ -365,7 +365,7 @@ protected static function cb_rewriteImportRelativeUrl ( $match ) { } // Normalise the path - $url = csscrush_util::normalizeSystemPath( $url ); + $url = csscrush_util::normalizePath( $url ); // No rewrite if: // $url begins with a variable, e.g '$(' @@ -385,12 +385,16 @@ protected static function cb_rewriteImportRelativeUrl ( $match ) { // Prepend the relative url prefix $url = $relative_url_prefix . $url; - // Reduce redundant path segments (issue #32): - // e.g 'foo/../bar' => 'bar' - $url = preg_replace( '![^/.]+/\.\./!', '', $url ); + // If the path comes via the css url function, we'll convert it to a url token + if ( $function == 'url' ) { - // Restore quotes if $url was a string token, update the token and return it - if ( $url_is_token ) { + $label = csscrush::tokenLabelCreate( 'u' ); + csscrush::$storage->tokens->urls[ $label ] = $url; + $url = $label; + } + elseif ( $url_is_token ) { + + // Restore quotes if $url was a string token, update the token and return it $url_token->update( $url_token->quoteMark . $url . $url_token->quoteMark ); $url = $url_token->token; } diff --git a/lib/Regex.php b/lib/Regex.php index 8b304cc..5f408c1 100644 --- a/lib/Regex.php +++ b/lib/Regex.php @@ -55,6 +55,7 @@ public static function init () { $patt->stringToken = '!___s\d+___!'; $patt->ruleToken = '!___r\d+___!'; $patt->parenToken = '!___p\d+___!'; + $patt->urlToken = '!___u\d+___!'; // Functions $patt->varFunction = '!(?: diff --git a/lib/Util.php b/lib/Util.php index 064cb5a..c437a6a 100644 --- a/lib/Util.php +++ b/lib/Util.php @@ -20,17 +20,46 @@ public static function htmlAttributes ( array $attributes ) { } - public static function normalizeSystemPath ( $path, $strip_ms_dos = false ) { + public static function normalizePath ( $path, $strip_ms_dos = false ) { + + $path = rtrim( preg_replace( '![\\/]+!', '/', $path ), '/' ); - $path = rtrim( str_replace( '\\', '/', $path ), '/' ); - if ( $strip_ms_dos ) { - $path = preg_replace( '!^[a-z]\:!i', '', $path ); + $path = preg_replace( '!^[a-z]\:!i', '', $path ); } return $path; } + public static function cleanUpUrl ( $url ) { + + // Reduce redundant path segments (issue #32): + // e.g 'foo/../bar' => 'bar' + $patt = '![^/.]+/\.\./!'; + + while ( preg_match( $patt, $url ) ) { + $url = preg_replace( $patt, '', $url ); + } + + if ( strpos( $url, '(' ) !== false || strpos( $url, ')' ) !== false ) { + $url = "\"$url\""; + } + + return $url; + } + + + public static function strReplaceHash ( $str, $map = array() ) { + + if ( ! $map ) { + return $str; + } + $labels = array_keys( $map ); + $values = array_values( $map ); + return str_replace( $labels, $values, $str ); + } + + public static function find () { foreach ( func_get_args() as $file ) { @@ -93,13 +122,13 @@ public static function tokenReplaceAll ( $str, $type = 'parens' ) { // Find tokens $matches = csscrush_regex::matchAll( csscrush_regex::$patt->{ $token_patt }, $str ); - + foreach ( $matches as $m ) { - + $token = $m[0][0]; - + if ( isset( $token_table[ $token ] ) ) { - + $str = str_replace( $token, $token_table[ $token ], $str ); } } @@ -112,7 +141,7 @@ public static function splitDelimList ( $str, $delim, $fold_in = false, $trim = $match_obj = self::matchAllBrackets( $str ); // If the delimiter is one character do a simple split - // Otherwise do a regex split + // Otherwise do a regex split if ( 1 === strlen( $delim ) ) { $match_obj->list = explode( $delim, $match_obj->string ); } @@ -126,7 +155,7 @@ public static function splitDelimList ( $str, $delim, $fold_in = false, $trim = // Filter out empties $match_obj->list = array_filter( $match_obj->list ); - + if ( $fold_in ) { foreach ( $match_obj->list as &$item ) { @@ -137,7 +166,7 @@ public static function splitDelimList ( $str, $delim, $fold_in = false, $trim = } - public static function matchBrackets ( $str, $brackets = array( '(', ')' ), + public static function matchBrackets ( $str, $brackets = array( '(', ')' ), $search_pos = 0, $capture_text = false ) { list( $opener, $closer ) = $brackets; @@ -280,7 +309,6 @@ public static function matchAllBrackets ( $str, $pair = '()', $offset = 0 ) { * String sugar * */ - class csscrush_string { public $token; @@ -292,16 +320,15 @@ class csscrush_string { public $quoteMark; public function __construct ( $token ) { - + $this->token = trim( $token ); $this->raw = csscrush::$storage->tokens->strings[ $token ]; $this->value = trim( $this->raw, '\'"' ); $this->quoteMark = $this->raw[0]; } - + public function update ( $newValue ) { csscrush::$storage->tokens->strings[ $this->token ] = $newValue; } } - From 42ffc22e17789ab3d21e04bbcc472fa4f64e7919 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 13 Jun 2012 15:02:01 +0100 Subject: [PATCH 020/421] Refactoring, cleaning-up Added incremental version detection csscrush::string method now defaults to not printing a boilerplate --- CHANGELOG.txt | 4 + CssCrush.php | 2 +- cli.php | 10 +- lib/Core.php | 12 +- lib/IO.php | 2 +- lib/Importer.php | 300 +++++++++++++++++++++++++---------------------- lib/Regex.php | 8 +- lib/Util.php | 56 ++++++++- 8 files changed, 232 insertions(+), 162 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index eb4d73c..9861583 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,7 @@ +1.5.3 +----- + + 1.5.2 ----- Resolved issue #32 diff --git a/CssCrush.php b/CssCrush.php index 2e3be98..09894b6 100644 --- a/CssCrush.php +++ b/CssCrush.php @@ -4,7 +4,7 @@ * CSS Crush * Extensible CSS preprocessor * - * @version 1.5.3-beta + * @version 1.5.3 * @link https://github.com/peteboere/css-crush * @license http://www.opensource.org/licenses/mit-license.php (MIT) * @copyright Copyright 2010-2012 Pete Boere diff --git a/cli.php b/cli.php index 3889596..8efc260 100644 --- a/cli.php +++ b/cli.php @@ -200,21 +200,21 @@ function stdout ( $lines, $closing_newline = true ) { $process_opts[ 'vars' ] = $in_vars; } -$import_context = $input_file ? dirname( realpath( $input_file ) ) : null; +$hostfile_context = $input_file ? dirname( realpath( $input_file ) ) : null; // If there is an import context set it to the document root -if ( $import_context ) { +if ( $hostfile_context ) { $old_doc_root = csscrush::$config->docRoot; - csscrush::$config->docRoot = $import_context; - $process_opts[ 'import_context' ] = $import_context; + csscrush::$config->docRoot = $hostfile_context; + $process_opts[ 'context' ] = $hostfile_context; } // Process the stream $output = csscrush::string( $input, $process_opts ); // Reset the document root after processing -if ( $import_context ) { +if ( $hostfile_context ) { csscrush::$config->docRoot = $old_doc_root; } diff --git a/lib/Core.php b/lib/Core.php index 56ebffc..53cbe34 100644 --- a/lib/Core.php +++ b/lib/Core.php @@ -31,7 +31,7 @@ public static function init ( $seed_file ) { // Get version ID from seed file $seed_file_contents = file_get_contents( $seed_file ); $match_count = preg_match( '!@version\s+([\d\.\w-]+)!', $seed_file_contents, $version_match ); - self::$config->version = $match_count ? $version_match[1] : null; + self::$config->version = $match_count ? new csscrush_version( $version_match[1] ) : null; // Set the docRoot reference self::setDocRoot(); @@ -362,6 +362,11 @@ public static function inline ( $file, $options = null, $attributes = array() ) */ public static function string ( $string, $options = null ) { + // For strings set boilerplate to not display by default + if ( ! isset( $options[ 'boilerplate' ] ) ) { + $options[ 'boilerplate' ] = false; + } + // Reset for current process self::reset( $options ); @@ -370,8 +375,8 @@ public static function string ( $string, $options = null ) { $options = self::$options; // Set the path context if one is given - if ( isset( $options[ 'import_context' ] ) && ! empty( $options[ 'import_context' ] ) ) { - self::setPath( $options[ 'import_context' ] ); + if ( isset( $options[ 'context' ] ) && ! empty( $options[ 'context' ] ) ) { + self::setPath( $options[ 'context' ] ); } // It's not associated with a real file so we create an 'empty' input object @@ -729,7 +734,6 @@ protected static function compile ( $stream ) { // Main processing on the rule objects self::processRules(); - // csscrush::log( csscrush::$storage->tokens->urls ); // csscrush::log( array_keys( self::$process->selectorRelationships ) ); // Alias any @-rules diff --git a/lib/IO.php b/lib/IO.php index 7cd25e8..6813041 100644 --- a/lib/IO.php +++ b/lib/IO.php @@ -27,8 +27,8 @@ public static function getInput ( $file = false ) { // Make basic information about the input object accessible $input = new stdclass(); + $input->dir = ! empty( $process->inputDir ) ? $process->inputDir : null; $input->name = $file ? basename( $file ) : null; - $input->dir = $file ? $process->inputDir : null; $input->path = $file ? "$process->inputDir/$input->name" : null; if ( $file ) { diff --git a/lib/Importer.php b/lib/Importer.php index 8a04ed1..92556e9 100644 --- a/lib/Importer.php +++ b/lib/Importer.php @@ -55,26 +55,41 @@ public static function hostfile () { $stream = csscrush::extractComments( $stream ); $stream = csscrush::extractStrings( $stream ); - // This may be set non-zero during the script if an absolute URL is encountered - $searchOffset = 0; + // If rewriting URLs as absolute we need to do some extra work + if ( $options[ 'rewrite_import_urls' ] === 'absolute' ) { + + // Normalize the @import statements in this case + foreach ( csscrush_regex::matchAll( $regex->import, $stream ) as $match ) { + $full_match = $match[0][0]; + $normalized_import_statement = self::normalizeImportStatement( $full_match ); + $stream = str_replace( $full_match, $normalized_import_statement, $stream ); + } + + // Convert URLs to URL tokens by setting an empty prefix + csscrush::$storage->misc->rewriteUrlPrefix = ''; + $stream = self::rewriteUrls( $stream ); + } + + // This may be set non-zero during the script if an absolute @import URL is encountered + $search_offset = 0; // Recurses until the nesting heirarchy is flattened and all files are combined - while ( preg_match( $regex->import, $stream, $match, PREG_OFFSET_CAPTURE, $searchOffset ) ) { + while ( preg_match( $regex->import, $stream, $match, PREG_OFFSET_CAPTURE, $search_offset ) ) { - $fullMatch = $match[0][0]; // Full match - $matchStart = $match[0][1]; // Full match offset - $matchEnd = $matchStart + strlen( $fullMatch ); - $preStatement = substr( $stream, 0, $matchStart ); - $postStatement = substr( $stream, $matchEnd ); + $full_match = $match[0][0]; // Full match + $match_start = $match[0][1]; // Full match offset + $match_end = $match_start + strlen( $full_match ); + $pre_statement = substr( $stream, 0, $match_start ); + $post_statement = substr( $stream, $match_end ); // If just stripping the import statements if ( isset( $hostfile->importIgnore ) ) { - $stream = $preStatement . $postStatement; + $stream = $pre_statement . $post_statement; continue; } // The media context (if specified) at position 3 in the match - $mediaContext = trim( $match[3][0] ); + $media_context = trim( $match[3][0] ); // The url may be at position 1 or 2 in the match depending on the syntax used $url = trim( $match[1][0] ); @@ -84,6 +99,7 @@ public static function hostfile () { // Url may be a string token if ( preg_match( $regex->stringToken, $url ) ) { + $import_url_token = new csscrush_string( $url ); $url = $import_url_token->value; } @@ -91,15 +107,18 @@ public static function hostfile () { // Pass over absolute urls // Move the search pointer forward if ( preg_match( $regex->absoluteUrl, $url ) ) { - $searchOffset = $matchEnd; + + $search_offset = $match_end; continue; } // Create import object $import = new stdclass(); $import->url = $url; - $import->mediaContext = $mediaContext; - $import->hostDir = $hostfile->dir; + $import->mediaContext = $media_context; + $import->hostDir = $process->inputDir; + + // csscrush::log( $import ); // Check to see if the url is root relative // Flatten import path for convenience @@ -110,118 +129,110 @@ public static function hostfile () { $import->path = realpath( "$hostfile->dir/$import->url" ); } - $import->content = @file_get_contents( $import->path ); - - // Failed to open import, just continue with the import line removed - if ( ! $import->content ) { + // Get the import contents, if unsuccessful just continue with the import line removed + if ( ! ( $import->content = @file_get_contents( $import->path ) ) ) { csscrush::log( "Import file '$import->url' not found at '$import->path'" ); - $stream = $preStatement . $postStatement; + $stream = $pre_statement . $post_statement; continue; } - else { - // Import file opened successfully so we process it: - // We need to resolve import statement urls in all imported files since - // they will be brought inline with the hostfile + // Import file opened successfully so we process it: + // - We need to resolve import statement urls in all imported files since + // they will be brought inline with the hostfile - // Start with extracting strings and comments in the import - $import->content = csscrush::extractComments( $import->content ); - $import->content = csscrush::extractStrings( $import->content ); + // Start with extracting strings and comments in the import + $import->content = csscrush::extractComments( $import->content ); + $import->content = csscrush::extractStrings( $import->content ); - $import->dir = dirname( $import->url ); + $import->dir = dirname( $import->url ); - // Store import file info for cache validation - $mtimes[] = filemtime( $import->path ); - $filenames[] = $import->url; + // Store import file info for cache validation + $mtimes[] = filemtime( $import->path ); + $filenames[] = $import->url; - // Alter all the url strings to be paths relative to the hostfile: - // Match all @import statements in the import content - // Store the replacements we might find - $matchCount = preg_match_all( $regex->import, $import->content, $matchAll, - PREG_OFFSET_CAPTURE | PREG_SET_ORDER ); - $replacements = array(); - for ( $index = 0; $index < $matchCount; $index++ ) { + // Alter all the url strings to be paths relative to the hostfile: + // - Match all @import statements in the import content + // - Store the replacements we might find + $replacements = array(); - $fullMatch = $matchAll[ $index ][0][0]; + foreach ( csscrush_regex::matchAll( $regex->import, $import->content ) as $match ) { - // Url match may be at one of 2 positions - if ( $matchAll[ $index ][1][1] == -1 ) { - $urlMatch = $matchAll[ $index ][2][0]; - } - else { - $urlMatch = $matchAll[ $index ][1][0]; - } + $full_match = $match[0][0]; - // Url may be a string token - if ( $urlMatchToken = preg_match( $regex->stringToken, $urlMatch ) ) { - // Store the token - $urlMatchToken = new csscrush_string( $urlMatch ); - // Set $urlMatch to the actual value - $urlMatch = $urlMatchToken->value; - } - - // Search and replace on the statement url - $search = $urlMatch; - $replace = "$import->dir/$urlMatch"; - - // Try to resolve absolute paths - // On failure strip the @import statement - if ( strpos( $urlMatch, '/' ) === 0 ) { - $replace = self::resolveAbsolutePath( $urlMatch ); - if ( ! $replace ) { - $search = $fullMatch; - $replace = ''; - } - } + // Url match may be at one of 2 positions + $url_match = $match[1][1] == -1 ? $match[2][0] : $match[1][0]; - // The full revised statement for replacement - $statement = $fullMatch; + // Url may be a string token + if ( $url_match_token = preg_match( $regex->stringToken, $url_match ) ) { - if ( $urlMatchToken && ! empty( $replace ) ) { - // Alter the stored token on internal hash table - $urlMatchToken->update( $replace ); - } - else { - // Trim the statement and set the resolved path - $statement = trim( str_replace( $search, $replace, $fullMatch ) ); - } + // Store the token + $url_match_token = new csscrush_string( $url_match ); - // Normalise import statement to be without url() syntax: - // This is so relative urls can easily be targeted later - $statement = self::normalizeImportStatement( $statement ); + // Set $url_match to the actual value + $url_match = $url_match_token->value; + } - if ( $fullMatch !== $statement ) { - $replacements[ $fullMatch ] = $statement; + // Search and replace on the statement url + $search = $url_match; + $replace = "$import->dir/$url_match"; + + // Try to resolve absolute paths + // On failure strip the @import statement + if ( strpos( $url_match, '/' ) === 0 ) { + $replace = self::resolveAbsolutePath( $url_match ); + if ( ! $replace ) { + $search = $full_match; + $replace = ''; } } - // If we've stored any altered @import strings then we need to apply them - if ( $replacements ) { - $import->content = str_replace( - array_keys( $replacements ), - array_values( $replacements ), - $import->content ); + // The full revised statement for replacement + $statement = $full_match; + + if ( $url_match_token && ! empty( $replace ) ) { + + // Alter the stored token on internal hash table + $url_match_token->update( $replace ); } + else { - // Optionally rewrite relative url and custom function data-uri references - if ( $options[ 'rewrite_import_urls' ] ) { - $import->content = self::rewriteImportRelativeUrls( $import ); + // Trim the statement and set the resolved path + $statement = trim( str_replace( $search, $replace, $full_match ) ); } - // Add media context if it exists - if ( $import->mediaContext ) { - $import->content = "@media $import->mediaContext {" . $import->content . '}'; + // Normalise import statement to be without url() syntax: + // - So relative urls can be targeted later + $statement = self::normalizeImportStatement( $statement ); + + if ( $full_match !== $statement ) { + $replacements[ $full_match ] = $statement; } + } - $stream = $preStatement . $import->content . $postStatement; + // If we've stored any altered @import strings then we need to apply them + if ( $replacements ) { + $import->content = csscrush_util::strReplaceHash( $import->content, $replacements ); } + // Optionally rewrite relative url and custom function data-uri references + if ( $options[ 'rewrite_import_urls' ] ) { + $import->content = self::rewriteImportUrls( $import ); + } + + // Add media context if it exists + if ( $import->mediaContext ) { + $import->content = "@media $import->mediaContext {{$import->content}}"; + } + + $stream = $pre_statement . $import->content . $post_statement; + } // End while // Save only if the hostfile object is associated with a real file if ( $hostfile->path ) { + self::save( array( 'imports' => $filenames, 'datem_sum' => array_sum( $mtimes ) + $hostfile->mtime, @@ -233,29 +244,30 @@ public static function hostfile () { } - protected static function normalizeImportStatement ( $statement ) { + protected static function normalizeImportStatement ( $import_statement ) { + + if ( preg_match( '!(\s)url\(\s*([^\)]+)\)!', $import_statement, $m ) ) { - $url_import_patt = '!^@import\s+url\(\s*!'; - if ( preg_match( $url_import_patt, $statement ) ) { - // Example matches: - // @import url(http://github.com/%22some_path_with_(parens).css") screen and ( max-width: 500px ); - // @import url(http://github.com/some_path.css); + list( $full_match, $the_space, $the_url ) = $m; + $the_url = rtrim( $the_url ); - // Trim the first part - $statement = preg_replace( $url_import_patt, '', $statement ); + if ( preg_match( csscrush_regex::$patt->stringToken, $the_url ) ) { - // 'some_path_with_(parens).css') screen and ( max-width: 500px ); - if ( preg_match( '!^([\'"])!', $statement, $m ) ) { - $statement = preg_replace( '!' . $m[1] . '\s*\)!', $m[1], $statement, 1 ); + // @import url(http://github.com/___s34___) screen and ( max-width: 500px ); + // @import url(http://github.com/___s34___); + $import_statement = str_replace( $full_match, $the_space . $the_url, $import_statement ); } - // some_path.css) screen and ( max-width: 500px ); else { - $statement = '"' . preg_replace( '!\s*\)!', '"', $statement, 1 ); + + // @import url(http://github.com/some/path/styles.css); + $string_label = csscrush::tokenLabelCreate( 's' ); + csscrush::$storage->tokens->strings[ $string_label ] = '"' . $the_url . '"'; + $import_statement = str_replace( $full_match, $the_space . $string_label, $import_statement ); } - // Pull back together - $statement = '@import ' . $statement; } - return $statement; + // csscrush::log( 'Normalised: ' . $import_statement ); + + return $import_statement; } @@ -274,28 +286,29 @@ protected static function resolveAbsolutePath ( $url ) { } - protected static function rewriteImportRelativeUrls ( $import ) { + protected static function rewriteImportUrls ( $import ) { $stream = $import->content; // Normalise the paths - $hostDir = csscrush_util::normalizePath( $import->hostDir, true ); - $importDir = csscrush_util::normalizePath( dirname( $import->path ), true ); + $host_dir = csscrush_util::normalizePath( $import->hostDir, true ); + $import_dir = csscrush_util::normalizePath( dirname( $import->path ), true ); - csscrush::$storage->misc->relativeUrlPrefix = ''; + csscrush::$storage->misc->rewriteUrlPrefix = ''; $url_prefix = ''; - if ( $importDir === $hostDir ) { + if ( $import_dir === $host_dir ) { + // Do nothing if files are in the same directory return $stream; } - elseif ( strpos( $importDir, $hostDir ) === false ) { - // Import directory is higher than the host directory + elseif ( strpos( $import_dir, $host_dir ) === false ) { + // Import directory is higher than the host directory // Split the directory paths into arrays so we can compare segment by segment - $host_segs = preg_split( '!/+!', $hostDir, null, PREG_SPLIT_NO_EMPTY ); - $import_segs = preg_split( '!/+!', $importDir, null, PREG_SPLIT_NO_EMPTY ); + $host_segs = preg_split( '!/+!', $host_dir, null, PREG_SPLIT_NO_EMPTY ); + $import_segs = preg_split( '!/+!', $import_dir, null, PREG_SPLIT_NO_EMPTY ); // Shift the segments until they are on different branches while ( @( $host_segs[0] == $import_segs[0] ) ) { @@ -311,10 +324,10 @@ protected static function rewriteImportRelativeUrls ( $import ) { } else { - // Import directory is lower than host directory + // Import directory is lower than host directory // easy, url_prefix is the difference - $url_prefix = substr( $importDir, strlen( $hostDir ) + 1 ); + $url_prefix = substr( $import_dir, strlen( $host_dir ) + 1 ); } if ( empty( $url_prefix ) ) { @@ -322,86 +335,89 @@ protected static function rewriteImportRelativeUrls ( $import ) { } // Add the directory seperator ending (if needed) - if ( $url_prefix[ strlen( $url_prefix ) - 1 ] !== '/' ) { + if ( substr( $url_prefix, -1 ) !== '/' ) { $url_prefix .= '/'; } - // csscrush::log( 'relative_url_prefix: ' . $url_prefix ); + // Make $url_prefix accessible in callback scope + csscrush::$storage->misc->rewriteUrlPrefix = $url_prefix; // Search for all relative url and data-uri references in the content // and prepend $relative_url_prefix + return self::rewriteUrls( $stream ); + } - // Make $url_prefix accessible in callback scope - csscrush::$storage->misc->relativeUrlPrefix = $url_prefix; + + protected static function rewriteUrls ( $stream ) { $url_function_patt = '! - ([^a-z-]) # the preceeding character - (data-uri|url) # the function name + ([^a-z-]) # the preceeding character + (data-uri|url) # the function name \(\s*([^\)]+)\s*\) # the url !xi'; $stream = preg_replace_callback( $url_function_patt, - array( 'self', 'cb_rewriteImportRelativeUrl' ), $stream ); + array( 'self', 'cb_rewriteUrls' ), $stream ); return $stream; } - protected static function cb_rewriteImportRelativeUrl ( $match ) { + protected static function cb_rewriteUrls ( $match ) { $regex = csscrush_regex::$patt; $storage = csscrush::$storage; // The relative url prefix - $relative_url_prefix = $storage->misc->relativeUrlPrefix; + $relative_url_prefix = $storage->misc->rewriteUrlPrefix; - list( $fullMatch, $before, $function, $url ) = $match; + list( $full_match, $before, $function, $url ) = $match; $url = trim( $url ); // If the url is a string token we'll need to restore it as a string token later - if ( $url_is_token = preg_match( $regex->stringToken, $url ) ) { + if ( $url_is_string = preg_match( $regex->stringToken, $url ) ) { - $url_token = new csscrush_string( $url ); - $url = $url_token->value; + $url_string = new csscrush_string( $url ); + $url = $url_string->value; } // Normalise the path $url = csscrush_util::normalizePath( $url ); // No rewrite if: - // $url begins with a variable, e.g '$(' - // $url path is absolute or begins with slash - // $url is an empty string + // - $url begins with a variable, e.g '$(' + // - $url path is absolute or begins with slash + // - $url is an empty string if ( empty( $url ) || strpos( $url, '/' ) === 0 || strpos( $url, '$(' ) === 0 || preg_match( $regex->absoluteUrl, $url ) ) { + // Token or not, it's ok to return the full match // if $url is a root relative or absolute ref - return $fullMatch; + return $full_match; } // Prepend the relative url prefix $url = $relative_url_prefix . $url; - // If the path comes via the css url function, we'll convert it to a url token + // If the path comes via the css url function convert it to a URL token if ( $function == 'url' ) { $label = csscrush::tokenLabelCreate( 'u' ); csscrush::$storage->tokens->urls[ $label ] = $url; $url = $label; } - elseif ( $url_is_token ) { + elseif ( $url_is_string ) { // Restore quotes if $url was a string token, update the token and return it - $url_token->update( $url_token->quoteMark . $url . $url_token->quoteMark ); - $url = $url_token->token; + $url_string->update( $url_string->quoteMark . $url . $url_string->quoteMark ); + $url = $url_string->token; } // Reconstruct the match and return return "$before$function($url)"; } - } diff --git a/lib/Regex.php b/lib/Regex.php index 5f408c1..05cee9c 100644 --- a/lib/Regex.php +++ b/lib/Regex.php @@ -38,12 +38,10 @@ public static function init () { $patt->variables = '!@(?:variables|define)\s*([^\{]*)\{\s*(.*?)\s*\};?!s'; $patt->mixin = '!@mixin\s*([^\{]*)\{\s*(.*?)\s*\};?!s'; - $patt->abstract = csscrush_regex::create( '^@abstract\s+()', 'i' ); - - $patt->comment = '!/\*(.*?)\*/!s'; - $patt->string = '!(\'|")(?:\\1|[^\1])*?\1!'; - + $patt->comment = '!/\*(.*?)\*/!sS'; + $patt->string = '!(\'|")(?:\\1|[^\1])*?\1!S'; + // As an exception we treat some @-rules like standard rule blocks $patt->rule = '! (\n(?:[^@{}]+|@(?:font-face|page|abstract)[^{]*)) # The selector diff --git a/lib/Util.php b/lib/Util.php index c437a6a..3c44582 100644 --- a/lib/Util.php +++ b/lib/Util.php @@ -312,17 +312,14 @@ public static function matchAllBrackets ( $str, $pair = '()', $offset = 0 ) { class csscrush_string { public $token; - public $value; - public $raw; - public $quoteMark; public function __construct ( $token ) { $this->token = trim( $token ); - $this->raw = csscrush::$storage->tokens->strings[ $token ]; + $this->raw = csscrush::$storage->tokens->strings[ $this->token ]; $this->value = trim( $this->raw, '\'"' ); $this->quoteMark = $this->raw[0]; } @@ -332,3 +329,54 @@ public function update ( $newValue ) { } } + +/** + * + * Version string sugar + * + */ +class csscrush_version { + + public $major; + public $minor; + public $revision; + public $extra; + + public function __construct ( $version_string ) { + + if ( ( $hyphen_pos = strpos( $version_string, '-' ) ) !== false ) { + $this->extra = substr( $version_string, $hyphen_pos + 1 ); + $version_string = substr( $version_string, 0, $hyphen_pos ); + } + + $parts = explode( '.', $version_string ); + + if ( ( $major = array_shift( $parts ) ) !== null ) { + $this->major = (int) $major; + } + if ( ( $minor = array_shift( $parts ) ) !== null ) { + $this->minor = (int) $minor; + } + if ( ( $revision = array_shift( $parts ) ) !== null ) { + $this->revision = (int) $revision; + } + } + + public function __toString () { + + $out = (string) $this->major; + + if ( ! is_null( $this->minor ) ) { + $out .= ".$this->minor"; + } + if ( ! is_null( $this->revision ) ) { + $out .= ".$this->revision"; + } + if ( ! is_null( $this->extra ) ) { + $out .= "-$this->extra"; + } + + return $out; + } +} + From a054ee7cb3f4a8e273ddedeff309a202c726846d Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 16 Jun 2012 15:16:42 +0100 Subject: [PATCH 021/421] Updated changelog --- CHANGELOG.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 9861583..71e8a9f 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,5 +1,7 @@ 1.5.3 ----- +Refactoring +Fixed some test cases 1.5.2 From 9ee4cc71f83ca24834e866929029028521b66eb1 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 21 Jun 2012 14:15:40 +0100 Subject: [PATCH 022/421] Inheritance model improved to support adoption of pseudo classes and elements Double-colon plugin moved to core Some refactoring --- CHANGELOG.txt | 6 ++ CssCrush.php | 2 +- lib/Core.php | 12 ++-- lib/Rule.php | 139 ++++++++++++++++++++++++++------------- lib/Util.php | 13 ++-- plugins/double-colon.php | 18 ----- 6 files changed, 116 insertions(+), 74 deletions(-) delete mode 100644 plugins/double-colon.php diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 71e8a9f..f5e386b 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,9 @@ +1.5.4 +----- +Inheritance model improved to support adoption of pseudo classes and elements (see wiki) +Double-colon plugin moved to core + + 1.5.3 ----- Refactoring diff --git a/CssCrush.php b/CssCrush.php index 09894b6..5b497d9 100644 --- a/CssCrush.php +++ b/CssCrush.php @@ -4,7 +4,7 @@ * CSS Crush * Extensible CSS preprocessor * - * @version 1.5.3 + * @version 1.5.4 * @link https://github.com/peteboere/css-crush * @license http://www.opensource.org/licenses/mit-license.php (MIT) * @copyright Copyright 2010-2012 Pete Boere diff --git a/lib/Core.php b/lib/Core.php index 53cbe34..9ee011e 100644 --- a/lib/Core.php +++ b/lib/Core.php @@ -437,7 +437,7 @@ public static function clearCache ( $dir = '' ) { public static $logging = false; - public static function log () { + public static function log ( $arg = null, $label = '' ) { if ( ! self::$logging ) { return; @@ -445,13 +445,15 @@ public static function log () { static $log = ''; $args = func_get_args(); - if ( !count( $args ) ) { + if ( ! count( $args ) ) { // No arguments, return the log return $log; } - else { - $arg = $args[0]; + + if ( $label ) { + $log .= "

$label

"; } + if ( is_string( $arg ) ) { $log .= $arg . '
'; } @@ -1148,7 +1150,7 @@ protected static function cb_extractRules ( $match ) { $rule = new csscrush_rule( $rule->selector_raw, $rule->declaration_raw ); // Store rules if they have declarations or extend arguments - if ( $rule->_declarations || $rule->extendsArgs ) { + if ( $rule->_declarations || $rule->extendArgs ) { $label = $rule->label; diff --git a/lib/Rule.php b/lib/Rule.php index c894ae8..4e8be06 100644 --- a/lib/Rule.php +++ b/lib/Rule.php @@ -21,10 +21,7 @@ class csscrush_rule implements IteratorAggregate { public $comments = array(); // Arugments passed in via 'extend' property - public $extendsArgs = array(); - - // Record of actual inherited rules - public $extends = array(); + public $extendArgs = array(); public $_declarations = array(); @@ -390,30 +387,11 @@ public function setExtendSelectors ( $raw_value ) { $args = csscrush_util::splitDelimList( $raw_value, ',', true, true ); // Reset if called earlier, last call wins by intention - $this->extendsArgs = array(); + $this->extendArgs = array(); foreach ( $args->list as $arg ) { - if ( preg_match( csscrush_regex::$patt->name, $arg ) ) { - - // A regular name: an element name or an abstract rule name - $this->extendsArgs[ $arg ] = true; - } - else { - - // Not a regular name: Some kind of selector so normalize it for later comparison - $readable_selector = csscrush_selector::makeReadableSelector( $arg ); - - $this->extendsArgs[ $readable_selector ] = true; - } - } - // csscrush::log( $this->extendsArgs ); - } - - public static function recursiveExtend ( $rule ) { - foreach ( $rule->extends as $parent_rule ) { - $parent_rule->addSelectors( $rule->selectorList ); - self::recursiveExtend( $parent_rule ); + $this->extendArgs[] = new csscrush_extendArg( $arg ); } } @@ -422,35 +400,60 @@ public function applyExtendables () { $abstracts = csscrush::$process->abstracts; $selectorRelationships = csscrush::$process->selectorRelationships; - foreach ( $this->extendsArgs as $name => $bool ) { + // Filter the extendArgs list to usable references + foreach ( $this->extendArgs as $index => $extend_arg ) { + + $name = $extend_arg->name; if ( isset( $abstracts[ $name ] ) ) { - $parent_abstract_rule = $abstracts[ $name ]; + $parent_rule = $abstracts[ $name ]; + $extend_arg->pointer = $parent_rule; - // Match found in abstract rules - $parent_abstract_rule->addSelectors( $this->selectorList ); + } + elseif ( isset( $selectorRelationships[ $name ] ) ) { - // Store a reference to directly extended rules - $this->extends[] = $parent_abstract_rule; + $parent_rule = $selectorRelationships[ $name ]; + $extend_arg->pointer = $parent_rule; - // Recursively inherit - self::recursiveExtend( $parent_abstract_rule ); + } + else { + // Unusable, so unset it + unset( $this->extendArgs[ $index ] ); } - else if ( isset( $selectorRelationships[ $name ] ) ) { + } - $parent_rule = $selectorRelationships[ $name ]; + // Create a stack of all parent rule args + $parent_extend_args = array(); + foreach ( $this->extendArgs as $extend_arg ) { + $parent_extend_args = array_merge( $parent_extend_args, $extend_arg->pointer->extendArgs ); + } + + // Merge this rule's extendArgs with parent extendArgs + $this->extendArgs = array_merge( $this->extendArgs, $parent_extend_args ); + + // Filter now? + + // Add this rule's selectors to all extendArgs + foreach ( $this->extendArgs as $extend_arg ) { - // Match found in selectorRelationships - $parent_rule->addSelectors( $this->selectorList ); + $ancestor = $extend_arg->pointer; - // Store a reference to directly extended rules - $this->extends[] = $parent_rule; + $extend_selectors = $this->selectorList; - // Recursively inherit - self::recursiveExtend( $parent_rule ); + // If there is a pseudo class extension create a new set accordingly + if ( $extend_arg->pseudo ) { + + $extend_selectors = array(); + foreach ( $this->selectorList as $readable => $selector ) { + $new_selector = clone $selector; + $new_readable = $new_selector->appendPseudo( $extend_arg->pseudo ); + $extend_selectors[ $new_readable ] = $new_selector; + } } + + $ancestor->addSelectors( $extend_selectors ); } } @@ -535,7 +538,6 @@ class csscrush_declaration { public $skip; public $important; public $parenTokens; - public $isValid = true; public function __construct ( $prop, $value ) { @@ -625,16 +627,12 @@ public function getFullValue () { * Selector objects * */ - class csscrush_selector { public $value; - public $readableValue; - public $allowPrefix = true; - public static function makeReadableSelector ( $selector_string ) { // Quick test for paren tokens @@ -651,6 +649,11 @@ public static function makeReadableSelector ( $selector_string ) { $selector_string = csscrush_util::tokenReplaceAll( $selector_string, 'strings' ); } + // Quick test for double-colons for backwards compat + if ( strpos( $selector_string, '::' ) !== false ) { + $selector_string = preg_replace( '!::(after|before|first-(?:letter|line))!', ':$1', $selector_string ); + } + return $selector_string; } @@ -670,7 +673,51 @@ public function __toString () { return $this->readableValue; } + + public function appendPseudo ( $pseudo ) { + + // Check to avoid doubling-up + if ( ! csscrush_util::strEndsWith( $this->readableValue, $pseudo ) ) { + + $this->readableValue .= $pseudo; + $this->value .= $pseudo; + } + return $this->readableValue; + } } +/** + * + * Extend argument objects + * + */ +class csscrush_extendArg { + + public $pointer; + public $name; + public $pseudo; + + public function __construct ( $name ) { + + $this->name = $name; + + if ( ! preg_match( csscrush_regex::$patt->name, $this->name ) ) { + + // Not a regular name: Some kind of selector so normalize it for later comparison + $this->name = csscrush_selector::makeReadableSelector( $this->name ); + + // If applying the pseudo on output store + if ( substr( $this->name, -1 ) === '!' ) { + + $this->name = rtrim( $this->name, ' !' ); + if ( preg_match( '!\:\:?[\w-]+$!', $this->name, $m ) ) { + $this->pseudo = $m[0]; + } + } + } + } +} + + diff --git a/lib/Util.php b/lib/Util.php index 3c44582..f242ca1 100644 --- a/lib/Util.php +++ b/lib/Util.php @@ -20,6 +20,12 @@ public static function htmlAttributes ( array $attributes ) { } + public static function strEndsWith ( $haystack, $needle ) { + + return substr( $haystack, -strlen( $needle ) ) === $needle; + } + + public static function normalizePath ( $path, $strip_ms_dos = false ) { $path = rtrim( preg_replace( '![\\/]+!', '/', $path ), '/' ); @@ -313,15 +319,14 @@ class csscrush_string { public $token; public $value; - public $raw; public $quoteMark; public function __construct ( $token ) { $this->token = trim( $token ); - $this->raw = csscrush::$storage->tokens->strings[ $this->token ]; - $this->value = trim( $this->raw, '\'"' ); - $this->quoteMark = $this->raw[0]; + $raw = csscrush::$storage->tokens->strings[ $this->token ]; + $this->value = trim( $raw, '\'"' ); + $this->quoteMark = $raw[0]; } public function update ( $newValue ) { diff --git a/plugins/double-colon.php b/plugins/double-colon.php deleted file mode 100644 index ab64003..0000000 --- a/plugins/double-colon.php +++ /dev/null @@ -1,18 +0,0 @@ -selector_raw = preg_replace( '!::(after|before|first-letter|first-line)!', ':$1', $rule->selector_raw ); -} From 18b5211a34f879ba10596ab5231bcc2310e3ccb2 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 22 Jun 2012 21:25:33 +0100 Subject: [PATCH 023/421] Added rule self-referencing function 'this()' and complimentary data-* properties Some refactoring --- CHANGELOG.txt | 1 + CssCrush.php | 2 +- lib/Core.php | 1 - lib/Function.php | 11 +++----- lib/Mixin.php | 16 ++--------- lib/Regex.php | 31 ++++++++++++++------- lib/Rule.php | 71 ++++++++++++++++++++++++++++++++++++++++++------ 7 files changed, 91 insertions(+), 42 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index f5e386b..02168f1 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,6 +1,7 @@ 1.5.4 ----- Inheritance model improved to support adoption of pseudo classes and elements (see wiki) +Added rule self-referencing function 'this()' and complimentary data-* properties Double-colon plugin moved to core diff --git a/CssCrush.php b/CssCrush.php index 5b497d9..60037ed 100644 --- a/CssCrush.php +++ b/CssCrush.php @@ -4,7 +4,7 @@ * CSS Crush * Extensible CSS preprocessor * - * @version 1.5.4 + * @version 1.5.4-beta * @link https://github.com/peteboere/css-crush * @license http://www.opensource.org/licenses/mit-license.php (MIT) * @copyright Copyright 2010-2012 Pete Boere diff --git a/lib/Core.php b/lib/Core.php index 9ee011e..1021409 100644 --- a/lib/Core.php +++ b/lib/Core.php @@ -48,7 +48,6 @@ public static function init ( $seed_file ) { // Initialise other classes csscrush_regex::init(); csscrush_function::init(); - csscrush_arglist::init(); } diff --git a/lib/Function.php b/lib/Function.php index 2246789..0fb2c36 100644 --- a/lib/Function.php +++ b/lib/Function.php @@ -14,15 +14,10 @@ class csscrush_function { public static $functionList; public static function init () { + // Set the custom function regex pattern self::$functionList = self::getFunctions(); - self::$functionPatt = self::createFunctionMatchPatt( self::$functionList ); - } - - public static function createFunctionMatchPatt ( $list, $include_unnamed_function = true ) { - - $question = $include_unnamed_function ? '?' : ''; - return '!(^|[^a-z0-9_-])(' . implode( '|', $list ) . ')' . $question . '\(!i'; + self::$functionPatt = csscrush_regex::createFunctionMatchPatt( self::$functionList, true ); } public static function getFunctions () { @@ -87,6 +82,7 @@ public static function parseCustomFunctions ( $str, $patt, $process_callback = n } if ( 0 === $paren_score ) { + // Get the function inards $content_start = $offset + strlen( $before_char ) + strlen( $raw_fn_name ) + 1; $content_finish = $index; @@ -103,6 +99,7 @@ public static function parseCustomFunctions ( $str, $patt, $process_callback = n $result = ''; if ( ! $process_callback ) { + // If no callback reference it's a built-in if ( in_array( $fn_name, self::$functionList ) ) { $fn_name_clean = str_replace( '-', '_', $fn_name ); diff --git a/lib/Mixin.php b/lib/Mixin.php index e80e230..7f5a433 100644 --- a/lib/Mixin.php +++ b/lib/Mixin.php @@ -54,7 +54,6 @@ public function __construct ( $block ) { return ''; } - public function call ( array $args ) { // Copy the template @@ -74,7 +73,6 @@ public function call ( array $args ) { return $declarations; } - public static function parseSingleValue ( $message ) { $message = ltrim( $message ); @@ -154,7 +152,6 @@ public static function parseSingleValue ( $message ) { return $mixin->call( $args ); } - public static function parseValue ( $message ) { // Call the mixin and return the list of declarations @@ -231,20 +228,11 @@ class csscrush_arglist implements Countable { // The string passed in with arg calls replaced by tokens public $string; - // The arg matching regex - protected static $argRegex; - - public static function init () { - - // Create regex pattern for matching 'arg' functions - self::$argRegex = csscrush_function::createFunctionMatchPatt( array( 'arg' ), false ); - } - function __construct ( $str ) { // Parse all arg function calls in the passed string, callback creates default values - $this->string = csscrush_function::parseCustomFunctions( - $str, self::$argRegex, array( $this, 'store' ) ); + $this->string = csscrush_function::parseCustomFunctions( $str, + csscrush_regex::$patt->argFunction, array( $this, 'store' ) ); } public function store ( $raw_argument ) { diff --git a/lib/Regex.php b/lib/Regex.php index 05cee9c..7f2b56e 100644 --- a/lib/Regex.php +++ b/lib/Regex.php @@ -1,13 +1,13 @@ function = '!(^|[^a-z0-9_-])([a-z_-]+)(___p\d+___)!i'; - + + // Specific functions + $patt->argFunction = csscrush_regex::createFunctionMatchPatt( array( 'arg' ) ); + $patt->thisFunction = csscrush_regex::createFunctionMatchPatt( array( 'this' ) ); + // Misc. $patt->vendorPrefix = '!^-([a-z]+)-([a-z-]+)!'; $patt->absoluteUrl = '!^https?://!'; @@ -73,16 +77,16 @@ public static function init () { public static function create ( $pattern_template, $flags = '' ) { // Sugar - $pattern = str_replace( - array( '', '' ), - array( self::$class->name, self::$class->notName ), + $pattern = str_replace( + array( '', '' ), + array( self::$class->name, self::$class->notName ), $pattern_template ); return '!' . $pattern . "!$flags"; } - - + + public static function matchAll ( $patt, $subject, $preprocess_patt = false, $offset = 0 ) { - + if ( $preprocess_patt ) { // Assume case-insensitive $patt = self::create( $patt, 'i' ); @@ -91,5 +95,12 @@ public static function matchAll ( $patt, $subject, $preprocess_patt = false, $of preg_match_all( $patt, $subject, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER, $offset ); return $matches; } + + + public static function createFunctionMatchPatt ( $list, $include_unnamed_function = false ) { + + $question = $include_unnamed_function ? '?' : ''; + return '!(^|[^a-z0-9_-])(' . implode( '|', $list ) . ')' . $question . '\(!i'; + } } diff --git a/lib/Rule.php b/lib/Rule.php index 4e8be06..25b2be3 100644 --- a/lib/Rule.php +++ b/lib/Rule.php @@ -11,8 +11,6 @@ class csscrush_rule implements IteratorAggregate { public $isNested; public $label; - // public $abstractName; - public $properties = array(); public $selectorList = array(); @@ -25,6 +23,37 @@ class csscrush_rule implements IteratorAggregate { public $_declarations = array(); + // A table for storing the declarations as data for the this() reference function + public $data = array(); + + public function declarationCheckin ( $prop, $value, &$pairs ) { + + if ( $prop !== '' && $value !== '' ) { + + if ( strpos( $prop, 'data-' ) === 0 ) { + + // If it's with data prefix, we don't want to print it + // Just remove the prefix + $prop = substr( $prop, strlen( 'data-' ) ); + } + else { + + // Add to the stack + $pairs[] = array( $prop, $value ); + } + + // Set on the data table + $this->data[ $prop ] = $value; + + // Unset on data table if the value has a 'this' function call: + // - Restriction to avoid circular references + if ( preg_match( csscrush_regex::$patt->thisFunction, $value ) ) { + + unset( $this->data[ $prop ] ); + } + } + } + public function __construct ( $selector_string = null, $declarations_string ) { $regex = csscrush_regex::$patt; @@ -67,6 +96,9 @@ public function __construct ( $selector_string = null, $declarations_string ) { // Need to split safely as there are semi-colons in data-uris $declarations_match = csscrush_util::splitDelimList( $declarations_string, ';', true ); + // First create a simple array of all properties and value pairs in raw state + $pairs = array(); + // Split declarations in to property/value pairs foreach ( $declarations_match->list as $declaration ) { @@ -76,8 +108,8 @@ public function __construct ( $selector_string = null, $declarations_string ) { // Extract the property part of the declaration $colonPos = strpos( $declaration, ':' ); if ( $colonPos === false ) { - // If there is no colon it's malformed - continue; + + continue; // If there's no colon it's malformed } $prop = trim( substr( $declaration, 0, $colonPos ) ); @@ -92,7 +124,9 @@ public function __construct ( $selector_string = null, $declarations_string ) { // Add mixin declarations to the stack while ( $mixin_declaration = array_shift( $mixin_declarations ) ) { - $this->addDeclaration( $mixin_declaration['property'], $mixin_declaration['value'] ); + + $this->declarationCheckin( + $mixin_declaration['property'], $mixin_declaration['value'], $pairs ); } } } @@ -103,10 +137,24 @@ public function __construct ( $selector_string = null, $declarations_string ) { } else { - // Add declaration to the stack - $this->addDeclaration( $prop, $value ); + $this->declarationCheckin( $prop, $value, $pairs ); } } + + // Bind declaration objects on the rule + foreach ( $pairs as $pair ) { + + list( $prop, $value ) = $pair; + + if ( preg_match( csscrush_regex::$patt->thisFunction, $value ) ) { + + // Resolve this() references + $value = csscrush_function::parseCustomFunctions( $value, + csscrush_regex::$patt->thisFunction, array( $this, 'thisCssFunction' ) ); + } + + $this->addDeclaration( $prop, $value ); + } } public function __set ( $name, $value ) { @@ -126,6 +174,11 @@ public function __get ( $name ) { } } + public function thisCssFunction ( $raw_argument ) { + + return isset( $this->data[ $raw_argument ] ) ? $this->data[ $raw_argument ] : ''; + } + public function updatePropertyTable () { // Create a new table of properties @@ -401,7 +454,7 @@ public function applyExtendables () { $selectorRelationships = csscrush::$process->selectorRelationships; // Filter the extendArgs list to usable references - foreach ( $this->extendArgs as $index => $extend_arg ) { + foreach ( $this->extendArgs as $key => $extend_arg ) { $name = $extend_arg->name; @@ -420,7 +473,7 @@ public function applyExtendables () { else { // Unusable, so unset it - unset( $this->extendArgs[ $index ] ); + unset( $this->extendArgs[ $key ] ); } } From ea5ed5cb1ed3d266024f3c4397680179b47de6ac Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 23 Jun 2012 15:04:35 +0100 Subject: [PATCH 024/421] Added default value argument for variables In most custom functions commas for seperating arguments are optional, a space can be used instead --- lib/Core.php | 59 ++++++++++++++++++++++++++++++++++-------------- lib/Function.php | 57 +++++++++++++++++++++++----------------------- lib/Mixin.php | 13 +++++++---- lib/Regex.php | 10 ++++++-- lib/Rule.php | 28 +++++++++++++++-------- lib/Util.php | 5 ++-- 6 files changed, 108 insertions(+), 64 deletions(-) diff --git a/lib/Core.php b/lib/Core.php index 1021409..48af20c 100644 --- a/lib/Core.php +++ b/lib/Core.php @@ -635,22 +635,31 @@ protected static function calculateVariables () { $value = ltrim( $value, "!\t\r " ); } else { - $value = csscrush_function::parseAndExecuteValue( $value ); + csscrush_function::executeCustomFunctions( $value ); } } } protected static function placeVariables ( $stream ) { + // Substitute simple case variables $stream = preg_replace_callback( csscrush_regex::$patt->varFunction, array( 'self', 'cb_placeVariables' ), $stream ); - // Place variables in any string tokens + // Substitute variables with default values + $var_fn_patt = csscrush_regex::createFunctionMatchPatt( array( '$' ) ); + $var_fn_callback = array( 'csscrush', 'cb_varFunctionWithDefault' ); + csscrush_function::executeCustomFunctions( $stream, $var_fn_patt, $var_fn_callback ); + + // Repeat above steps for variables embedded in string tokens foreach ( self::$storage->tokens->strings as $label => &$string ) { - if ( strpos( $string, '$' ) !== false ) { + + if ( strpos( $string, '$(' ) !== false ) { + $string = preg_replace_callback( - csscrush_regex::$patt->varFunction, - array( 'self', 'cb_placeVariables' ), $string ); + csscrush_regex::$patt->varFunction, + array( 'self', 'cb_placeVariables' ), $string ); + csscrush_function::executeCustomFunctions( $string, $var_fn_patt, $var_fn_callback ); } } return $stream; @@ -1082,6 +1091,18 @@ protected static function cb_restoreComments ( $match ) { return self::$storage->tokens->comments[ $match[0] ]; } + protected static function cb_extractMixins ( $match ) { + + $name = trim( $match[1] ); + $block = trim( $match[2] ); + + if ( ! empty( $name ) && ! empty( $block ) ) { + self::$process->mixins[ $name ] = new csscrush_mixin( $block ); + } + + return ''; + } + protected static function cb_extractVariables ( $match ) { $regex = csscrush_regex::$patt; @@ -1107,23 +1128,13 @@ protected static function cb_extractVariables ( $match ) { return ''; } - protected static function cb_extractMixins ( $match ) { - - $name = trim( $match[1] ); - $block = trim( $match[2] ); - - if ( ! empty( $name ) && ! empty( $block ) ) { - self::$process->mixins[ $name ] = new csscrush_mixin( $block ); - } - - return ''; - } - protected static function cb_placeVariables ( $match ) { + $before_char = $match[1]; // Check for dollar shorthand if ( empty( $match[2] ) && isset( $match[3] ) && strpos( $match[0], '$' ) !== false ) { + $variable_name = $match[3]; } else { @@ -1131,6 +1142,7 @@ protected static function cb_placeVariables ( $match ) { } if ( isset( self::$storage->variables[ $variable_name ] ) ) { + return $before_char . self::$storage->variables[ $variable_name ]; } else { @@ -1138,6 +1150,19 @@ protected static function cb_placeVariables ( $match ) { } } + public static function cb_varFunctionWithDefault ( $raw_argument ) { + + list( $name, $default_value ) = csscrush_function::parseArgsSimple( $raw_argument ); + + if ( isset( self::$storage->variables[ $name ] ) ) { + + return self::$storage->variables[ $name ]; + } + else { + return $default_value; + } + } + protected static function cb_extractRules ( $match ) { $rule = new stdclass(); diff --git a/lib/Function.php b/lib/Function.php index 0fb2c36..cb6dfbc 100644 --- a/lib/Function.php +++ b/lib/Function.php @@ -35,16 +35,21 @@ public static function getFunctions () { return $fn_methods; } - public static function parseCustomFunctions ( $str, $patt, $process_callback = null ) { + public static function executeCustomFunctions ( &$str, $patt = null, $process_callback = null ) { // No bracketed expressions, early return if ( false === strpos( $str, '(' ) ) { - return $str; + return; + } + + // Set default pattern if not set + if ( is_null( $patt ) ) { + $patt = csscrush_function::$functionPatt; } // No custom functions, early return if ( ! preg_match( $patt, $str ) ) { - return $str; + return; } // Need a space inside the front function paren for the following match_all to be reliable @@ -73,6 +78,7 @@ public static function parseCustomFunctions ( $str, $patt, $process_callback = n $paren_score = 0; for ( $index = $first_paren_offset; $index < strlen( $str ); $index++ ) { + $char = $str[ $index ]; if ( '(' === $char ) { $paren_score++; @@ -122,31 +128,13 @@ public static function parseCustomFunctions ( $str, $patt, $process_callback = n $str = str_replace( '( ', '(', $str ); } - return $str; - + // return $str; } - public static function parseAndExecuteValue ( $str ) { - return self::parseCustomFunctions( $str, self::$functionPatt ); - } - ############ # Helpers - protected static function parseMathArgs ( $input ) { - // Split on comma, trim, and remove empties - $args = array_filter( array_map( 'trim', explode( ',', $input ) ) ); - - // Pass anything non-numeric through math - foreach ( $args as &$arg ) { - if ( !preg_match( '!^-?[\.0-9]+$!', $arg ) ) { - $arg = self::css_fn__math( $arg ); - } - } - return $args; - } - protected static function parseArgs ( $input, $allowSpaceDelim = false ) { $args = csscrush_util::splitDelimList( @@ -155,11 +143,16 @@ protected static function parseArgs ( $input, $allowSpaceDelim = false ) { true, true ); - // return array_map( 'trim', $args->list ); - return $args->list; } + // Intended as a quick arg-list parse for function that take up-to 2 arguments + // with the proviso the first argument is a name + public static function parseArgsSimple ( $input ) { + + return preg_split( csscrush_regex::$patt->argListSplit, $input, 2 ); + } + protected static function colorAdjust ( $color, array $adjustments ) { $fn_matched = preg_match( '!^(#|rgba?|hsla?)!', $color, $m ); @@ -251,8 +244,9 @@ protected static function colorAdjust ( $color, array $adjustments ) { ############ public static function css_fn__math ( $input ) { - // Whitelist allowed characters - $input = preg_replace( '![^\.0-9\*\/\+\-\(\)]!', '', $input ); + + // Strip blacklisted characters + $input = preg_replace( csscrush_regex::$patt->mathBlacklist, '', $input ); $result = @eval( "return $input;" ); @@ -261,7 +255,12 @@ public static function css_fn__math ( $input ) { public static function css_fn__percent ( $input ) { - $args = self::parseMathArgs( $input ); + // Strip non-numeric and non delimiter characters + $input = preg_replace( '![^\d\.\s,]!S', '', $input ); + + $args = preg_split( csscrush_regex::$patt->argListSplit, $input, -1, PREG_SPLIT_NO_EMPTY ); + + // csscrush::log( $input ); // Use precision argument if it exists, use default otherwise $precision = isset( $args[2] ) ? $args[2] : 5; @@ -270,7 +269,7 @@ public static function css_fn__percent ( $input ) { $result = 0; // Need to check arguments or we may see divide by zero errors - if ( count( $args ) > 1 && !empty( $args[0] ) && !empty( $args[1] ) ) { + if ( count( $args ) > 1 && ! empty( $args[0] ) && ! empty( $args[1] ) ) { // Use bcmath if it's available for higher precision @@ -300,7 +299,7 @@ public static function css_fn__percent ( $input ) { // Percent function alias public static function css_fn__pc ( $input ) { - return self::css_fn_percent( $input ); + return self::css_fn__percent( $input ); } public static function css_fn__data_uri ( $input ) { diff --git a/lib/Mixin.php b/lib/Mixin.php index 7f5a433..b91edae 100644 --- a/lib/Mixin.php +++ b/lib/Mixin.php @@ -231,24 +231,27 @@ class csscrush_arglist implements Countable { function __construct ( $str ) { // Parse all arg function calls in the passed string, callback creates default values - $this->string = csscrush_function::parseCustomFunctions( $str, + csscrush_function::executeCustomFunctions( $str, csscrush_regex::$patt->argFunction, array( $this, 'store' ) ); + $this->string = $str; } public function store ( $raw_argument ) { + $args = csscrush_function::parseArgsSimple( $raw_argument ); + // Match the argument index integer - if ( ! preg_match( '!^[0-9]+!', $raw_argument, $position_match ) ) { + if ( ! ctype_digit( $args[0] ) ) { + // On failure to match an integer, return an empty string return ''; } // Get the match from the array - $position_match = $position_match[0]; + $position_match = $args[0]; // Store the default value - $default_value = substr( $raw_argument, strlen( $position_match ) ); - $default_value = $default_value ? ltrim( $default_value, " ,\t\n\r" ) : null; + $default_value = isset( $args[1] ) ? $args[1] : null; if ( ! is_null( $default_value ) ) { $this->defaults[ $position_match ] = trim( $default_value ); diff --git a/lib/Regex.php b/lib/Regex.php index 7f2b56e..bbb82f8 100644 --- a/lib/Regex.php +++ b/lib/Regex.php @@ -69,8 +69,10 @@ public static function init () { $patt->thisFunction = csscrush_regex::createFunctionMatchPatt( array( 'this' ) ); // Misc. - $patt->vendorPrefix = '!^-([a-z]+)-([a-z-]+)!'; - $patt->absoluteUrl = '!^https?://!'; + $patt->vendorPrefix = '!^-([a-z]+)-([a-z-]+)!'; + $patt->absoluteUrl = '!^https?://!'; + $patt->argListSplit = '!\s*[,\s]\s*!S'; + $patt->mathBlacklist = '![^\.0-9\*\/\+\-\(\)]!S'; } @@ -100,6 +102,10 @@ public static function matchAll ( $patt, $subject, $preprocess_patt = false, $of public static function createFunctionMatchPatt ( $list, $include_unnamed_function = false ) { $question = $include_unnamed_function ? '?' : ''; + + foreach ( $list as &$fn_name ) { + $fn_name = preg_quote( $fn_name ); + } return '!(^|[^a-z0-9_-])(' . implode( '|', $list ) . ')' . $question . '\(!i'; } } diff --git a/lib/Rule.php b/lib/Rule.php index 25b2be3..e346eff 100644 --- a/lib/Rule.php +++ b/lib/Rule.php @@ -125,7 +125,7 @@ public function __construct ( $selector_string = null, $declarations_string ) { // Add mixin declarations to the stack while ( $mixin_declaration = array_shift( $mixin_declarations ) ) { - $this->declarationCheckin( + $this->declarationCheckin( $mixin_declaration['property'], $mixin_declaration['value'], $pairs ); } } @@ -146,12 +146,9 @@ public function __construct ( $selector_string = null, $declarations_string ) { list( $prop, $value ) = $pair; - if ( preg_match( csscrush_regex::$patt->thisFunction, $value ) ) { - - // Resolve this() references - $value = csscrush_function::parseCustomFunctions( $value, - csscrush_regex::$patt->thisFunction, array( $this, 'thisCssFunction' ) ); - } + // Resolve this() references + csscrush_function::executeCustomFunctions( $value, + csscrush_regex::$patt->thisFunction, array( $this, 'thisCssFunction' ) ); $this->addDeclaration( $prop, $value ); } @@ -176,7 +173,20 @@ public function __get ( $name ) { public function thisCssFunction ( $raw_argument ) { - return isset( $this->data[ $raw_argument ] ) ? $this->data[ $raw_argument ] : ''; + $args = csscrush_function::parseArgsSimple( $raw_argument ); + + if ( isset( $this->data[ $args[0] ] ) ) { + + return $this->data[ $args[0] ]; + } + elseif ( isset( $args[1] ) ) { + + return $args[1]; + } + else { + + return ''; + } } public function updatePropertyTable () { @@ -637,7 +647,7 @@ public function __construct ( $prop, $value ) { // Apply custom functions if ( ! $skip ) { - $value = csscrush_function::parseAndExecuteValue( $value ); + csscrush_function::executeCustomFunctions( $value ); } // Tokenize all remaining paren pairs diff --git a/lib/Util.php b/lib/Util.php index f242ca1..0b459c2 100644 --- a/lib/Util.php +++ b/lib/Util.php @@ -149,9 +149,11 @@ public static function splitDelimList ( $str, $delim, $fold_in = false, $trim = // If the delimiter is one character do a simple split // Otherwise do a regex split if ( 1 === strlen( $delim ) ) { + $match_obj->list = explode( $delim, $match_obj->string ); } else { + $match_obj->list = preg_split( '!' . $delim . '!', $match_obj->string ); } @@ -172,8 +174,7 @@ public static function splitDelimList ( $str, $delim, $fold_in = false, $trim = } - public static function matchBrackets ( $str, $brackets = array( '(', ')' ), - $search_pos = 0, $capture_text = false ) { + public static function matchBrackets ( $str, $brackets = array( '(', ')' ), $search_pos = 0, $capture_text = false ) { list( $opener, $closer ) = $brackets; $openings = array(); From 403a5a59a733ed638e7f1e4d4281dce3bad58caf Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 23 Jun 2012 16:00:13 +0100 Subject: [PATCH 025/421] Reducing memory usage --- lib/Core.php | 63 ++++++++++++++++++++---------------------------- lib/Hook.php | 4 ++- lib/Importer.php | 10 +++++--- 3 files changed, 35 insertions(+), 42 deletions(-) diff --git a/lib/Core.php b/lib/Core.php index 48af20c..cb123aa 100644 --- a/lib/Core.php +++ b/lib/Core.php @@ -640,7 +640,7 @@ protected static function calculateVariables () { } } - protected static function placeVariables ( $stream ) { + protected static function placeVariables ( &$stream ) { // Substitute simple case variables $stream = preg_replace_callback( @@ -657,12 +657,11 @@ protected static function placeVariables ( $stream ) { if ( strpos( $string, '$(' ) !== false ) { $string = preg_replace_callback( - csscrush_regex::$patt->varFunction, + csscrush_regex::$patt->varFunction, array( 'self', 'cb_placeVariables' ), $string ); csscrush_function::executeCustomFunctions( $string, $var_fn_patt, $var_fn_callback ); } } - return $stream; } protected static function reset ( $options = null ) { @@ -709,22 +708,19 @@ protected static function compile ( $stream ) { self::pruneAliases(); // Parse variables - $stream = self::extractVariables( $stream ); + self::extractVariables( $stream ); // Calculate the variable stack self::calculateVariables(); // Place the variables - $stream = self::placeVariables( $stream ); + self::placeVariables( $stream ); // Pull out the mixin declarations - $stream = self::extractMixins( $stream ); + self::extractMixins( $stream ); // Pull out the fragments - $stream = self::extractFragments( $stream ); - - // Normalize whitespace - $stream = csscrush_util::normalizeWhiteSpace( $stream ); + self::extractFragments( $stream ); // Adjust the stream so we can extract the rules cleanly $map = array( @@ -736,10 +732,10 @@ protected static function compile ( $stream ) { $stream = "\n" . str_replace( array_keys( $map ), array_values( $map ), $stream ); // Rules - $stream = self::extractRules( $stream ); + self::extractRules( $stream ); // Process any @-in blocks - $stream = self::prefixSelectors( $stream ); + self::prefixSelectors( $stream ); // Main processing on the rule objects self::processRules(); @@ -747,10 +743,10 @@ protected static function compile ( $stream ) { // csscrush::log( array_keys( self::$process->selectorRelationships ) ); // Alias any @-rules - $stream = self::aliasAtRules( $stream ); + self::aliasAtRules( $stream ); // Print it all back - $stream = self::display( $stream ); + self::display( $stream ); // Add in boilerplate if ( $options[ 'boilerplate' ] ) { @@ -763,7 +759,7 @@ protected static function compile ( $stream ) { return $stream; } - protected static function display ( $stream ) { + protected static function display ( &$stream ) { $minify = ! self::$options[ 'debug' ]; $regex = csscrush_regex::$patt; @@ -823,8 +819,6 @@ protected static function display ( $stream ) { // Insert string literals $stream = csscrush_util::strReplaceHash( $stream, self::$storage->tokens->strings ); - // I think we're done - return $stream; } protected static function minify ( $str ) { @@ -843,10 +837,10 @@ protected static function minify ( $str ) { array_keys( $replacements ), array_values( $replacements ), $str ); } - protected static function aliasAtRules ( $stream ) { + protected static function aliasAtRules ( &$stream ) { if ( empty( self::$config->aliases[ 'at-rules' ] ) ) { - return $stream; + return; } $aliases = self::$config->aliases[ 'at-rules' ]; @@ -938,10 +932,9 @@ protected static function aliasAtRules ( $stream ) { } // while } // foreach - return $stream; } - protected static function prefixSelectors ( $stream ) { + protected static function prefixSelectors ( &$stream ) { $matches = csscrush_regex::matchAll( '@in\s+([^\{]+){', $stream, true ); @@ -1019,8 +1012,6 @@ protected static function prefixSelectors ( $stream ) { // Concatenate $stream = $before . $curly_match->inside . $curly_match->after; } - - return $stream; } public static function tokenLabelCreate ( $prefix ) { @@ -1041,6 +1032,7 @@ public static function processRules () { csscrush_hook::run( 'rule_prealias', $rule ); if ( ! empty( self::$config->aliases ) ) { + $rule->addPropertyAliases(); $rule->addFunctionAliases(); $rule->addValueAliases(); @@ -1243,27 +1235,27 @@ protected static function cb_printRule ( $match ) { ############ # Parsing methods - public static function extractRules ( $stream ) { - return preg_replace_callback( csscrush_regex::$patt->rule, array( 'self', 'cb_extractRules' ), $stream ); + public static function extractRules ( &$stream ) { + $stream = preg_replace_callback( csscrush_regex::$patt->rule, array( 'self', 'cb_extractRules' ), $stream ); } - public static function extractVariables ( $stream ) { - return preg_replace_callback( csscrush_regex::$patt->variables, array( 'self', 'cb_extractVariables' ), $stream ); + public static function extractVariables ( &$stream ) { + $stream = preg_replace_callback( csscrush_regex::$patt->variables, array( 'self', 'cb_extractVariables' ), $stream ); } - public static function extractComments ( $stream ) { - return preg_replace_callback( csscrush_regex::$patt->comment, array( 'self', 'cb_extractComments' ), $stream ); + public static function extractComments ( &$stream ) { + $stream = preg_replace_callback( csscrush_regex::$patt->comment, array( 'self', 'cb_extractComments' ), $stream ); } - public static function extractStrings ( $stream ) { - return preg_replace_callback( csscrush_regex::$patt->string, array( 'self', 'cb_extractStrings' ), $stream ); + public static function extractStrings ( &$stream ) { + $stream = preg_replace_callback( csscrush_regex::$patt->string, array( 'self', 'cb_extractStrings' ), $stream ); } - public static function extractMixins ( $stream ) { - return preg_replace_callback( csscrush_regex::$patt->mixin, array( 'self', 'cb_extractMixins' ), $stream ); + public static function extractMixins ( &$stream ) { + $stream = preg_replace_callback( csscrush_regex::$patt->mixin, array( 'self', 'cb_extractMixins' ), $stream ); } - public static function extractFragments ( $stream ) { + public static function extractFragments ( &$stream ) { $matches = csscrush_regex::matchAll( '@fragment\s+()\s*{', $stream, true ); @@ -1348,10 +1340,7 @@ public static function extractFragments ( $stream ) { $stream = $before . $fragment_return . $after; } } - - return $stream; } - } diff --git a/lib/Hook.php b/lib/Hook.php index 36a1956..f602b74 100644 --- a/lib/Hook.php +++ b/lib/Hook.php @@ -10,6 +10,7 @@ class csscrush_hook { static public $record = array(); static public function add ( $hook, $fn_name ) { + // Store in associative array so no duplicates if ( function_exists( $fn_name ) ) { self::$record[ $hook ][ $fn_name ] = true; @@ -17,8 +18,9 @@ static public function add ( $hook, $fn_name ) { } static public function run ( $hook, $arg_obj ) { + // Run all callbacks attached to the hook - if ( !isset( self::$record[ $hook ] ) ) { + if ( ! isset( self::$record[ $hook ] ) ) { return; } foreach ( array_keys( self::$record[ $hook ] ) as $fn_name ) { diff --git a/lib/Importer.php b/lib/Importer.php index 92556e9..303643e 100644 --- a/lib/Importer.php +++ b/lib/Importer.php @@ -52,8 +52,9 @@ public static function hostfile () { $stream = file_get_contents( $prependFile ) . $stream; } - $stream = csscrush::extractComments( $stream ); - $stream = csscrush::extractStrings( $stream ); + csscrush::extractComments( $stream ); + csscrush::extractStrings( $stream ); + $stream = csscrush_util::normalizeWhiteSpace( $stream ); // If rewriting URLs as absolute we need to do some extra work if ( $options[ 'rewrite_import_urls' ] === 'absolute' ) { @@ -142,8 +143,9 @@ public static function hostfile () { // they will be brought inline with the hostfile // Start with extracting strings and comments in the import - $import->content = csscrush::extractComments( $import->content ); - $import->content = csscrush::extractStrings( $import->content ); + csscrush::extractComments( $import->content ); + csscrush::extractStrings( $import->content ); + $import->content = csscrush_util::normalizeWhiteSpace( $import->content ); $import->dir = dirname( $import->url ); From f48b1c6b06973b94d80a0586eceae38cececca6f Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 23 Jun 2012 21:23:29 +0100 Subject: [PATCH 026/421] Cleaning up --- CHANGELOG.txt | 2 ++ CssCrush.php | 2 +- lib/Core.php | 65 +++++++++++++++++++----------------------------- lib/Importer.php | 12 ++------- lib/Regex.php | 13 ++++++---- lib/Rule.php | 7 ++++-- 6 files changed, 44 insertions(+), 57 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 02168f1..e470a6f 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -2,6 +2,8 @@ ----- Inheritance model improved to support adoption of pseudo classes and elements (see wiki) Added rule self-referencing function 'this()' and complimentary data-* properties +Added default value argument for variables +Commas are now optional when specifying arguments for most custom functions Double-colon plugin moved to core diff --git a/CssCrush.php b/CssCrush.php index 60037ed..5b497d9 100644 --- a/CssCrush.php +++ b/CssCrush.php @@ -4,7 +4,7 @@ * CSS Crush * Extensible CSS preprocessor * - * @version 1.5.4-beta + * @version 1.5.4 * @link https://github.com/peteboere/css-crush * @license http://www.opensource.org/licenses/mit-license.php (MIT) * @copyright Copyright 2010-2012 Pete Boere diff --git a/lib/Core.php b/lib/Core.php index cb123aa..72a3f77 100644 --- a/lib/Core.php +++ b/lib/Core.php @@ -475,6 +475,14 @@ public static function logError ( $msg ) { ##################### # Internal functions + public static function prepareStream ( &$stream ) { + + $stream = preg_replace_callback( csscrush_regex::$patt->commentAndString, + array( 'self', 'cb_extractCommentAndString' ), $stream ); + + $stream = csscrush_util::normalizeWhiteSpace( $stream ); + } + protected static function getBoilerplate () { $file = csscrush_util::find( 'CssCrush-local.boilerplate', 'CssCrush.boilerplate' ); @@ -1053,36 +1061,31 @@ public static function processRules () { ############################# # preg_replace callbacks - protected static function cb_extractStrings ( $match ) { - $label = csscrush::tokenLabelCreate( 's' ); - csscrush::$storage->tokens->strings[ $label ] = $match[0]; - return $label; - } + protected static function cb_extractCommentAndString ( $match ) { - protected static function cb_restoreStrings ( $match ) { - return csscrush::$storage->tokens->strings[ $match[0] ]; - } + $capture = $match[0]; - protected static function cb_extractComments ( $match ) { + if ( strpos( $capture, '/*' ) === 0 ) { - $comment = $match[0]; + // Strip private comments + $private_comment_marker = '$!'; - // Strip private comments - $private_comment_marker = '$!'; - if ( strpos( $comment, '/*' . $private_comment_marker ) === 0 ) { - return ''; + if ( strpos( $capture, '/*' . $private_comment_marker ) === 0 ) { + return ''; + } + + $label = self::tokenLabelCreate( 'c' ); + self::$storage->tokens->comments[ $label ] = $capture; } + else { - $label = self::tokenLabelCreate( 'c' ); - self::$storage->tokens->comments[ $label ] = $comment; + $label = csscrush::tokenLabelCreate( 's' ); + csscrush::$storage->tokens->strings[ $label ] = $capture; + } return $label; } - protected static function cb_restoreComments ( $match ) { - return self::$storage->tokens->comments[ $match[0] ]; - } - protected static function cb_extractMixins ( $match ) { $name = trim( $match[1] ); @@ -1173,20 +1176,12 @@ protected static function cb_extractRules ( $match ) { self::$storage->tokens->rules[ $label ] = $rule; if ( $rule->_declarations ) { - return $label . "\n"; - } - else { + // If only using extend no need to return a label - return ''; + return $label . "\n"; } } - else { - return ''; - } - } - - protected static function cb_restoreLiteral ( $match ) { - return self::$storage->tokens[ $match[0] ]; + return ''; } protected static function cb_printRule ( $match ) { @@ -1243,14 +1238,6 @@ public static function extractVariables ( &$stream ) { $stream = preg_replace_callback( csscrush_regex::$patt->variables, array( 'self', 'cb_extractVariables' ), $stream ); } - public static function extractComments ( &$stream ) { - $stream = preg_replace_callback( csscrush_regex::$patt->comment, array( 'self', 'cb_extractComments' ), $stream ); - } - - public static function extractStrings ( &$stream ) { - $stream = preg_replace_callback( csscrush_regex::$patt->string, array( 'self', 'cb_extractStrings' ), $stream ); - } - public static function extractMixins ( &$stream ) { $stream = preg_replace_callback( csscrush_regex::$patt->mixin, array( 'self', 'cb_extractMixins' ), $stream ); } diff --git a/lib/Importer.php b/lib/Importer.php index 303643e..7cbea65 100644 --- a/lib/Importer.php +++ b/lib/Importer.php @@ -52,9 +52,7 @@ public static function hostfile () { $stream = file_get_contents( $prependFile ) . $stream; } - csscrush::extractComments( $stream ); - csscrush::extractStrings( $stream ); - $stream = csscrush_util::normalizeWhiteSpace( $stream ); + csscrush::prepareStream( $stream ); // If rewriting URLs as absolute we need to do some extra work if ( $options[ 'rewrite_import_urls' ] === 'absolute' ) { @@ -119,8 +117,6 @@ public static function hostfile () { $import->mediaContext = $media_context; $import->hostDir = $process->inputDir; - // csscrush::log( $import ); - // Check to see if the url is root relative // Flatten import path for convenience if ( strpos( $import->url, '/' ) === 0 ) { @@ -141,11 +137,7 @@ public static function hostfile () { // Import file opened successfully so we process it: // - We need to resolve import statement urls in all imported files since // they will be brought inline with the hostfile - - // Start with extracting strings and comments in the import - csscrush::extractComments( $import->content ); - csscrush::extractStrings( $import->content ); - $import->content = csscrush_util::normalizeWhiteSpace( $import->content ); + csscrush::prepareStream( $import->content ); $import->dir = dirname( $import->url ); diff --git a/lib/Regex.php b/lib/Regex.php index bbb82f8..b63489e 100644 --- a/lib/Regex.php +++ b/lib/Regex.php @@ -34,19 +34,22 @@ public static function init () { ([_s\d]+) # string token ) \s*([^;]*); # media argument - !x'; + !xS'; $patt->variables = '!@(?:variables|define)\s*([^\{]*)\{\s*(.*?)\s*\};?!s'; $patt->mixin = '!@mixin\s*([^\{]*)\{\s*(.*?)\s*\};?!s'; $patt->abstract = csscrush_regex::create( '^@abstract\s+()', 'i' ); - $patt->comment = '!/\*(.*?)\*/!sS'; - $patt->string = '!(\'|")(?:\\1|[^\1])*?\1!S'; + $patt->commentAndString = '! + (\'|")(?:\\1|[^\1])*?\1 + | + /\*(?:.*?)\*/ + !xsS'; // As an exception we treat some @-rules like standard rule blocks $patt->rule = '! (\n(?:[^@{}]+|@(?:font-face|page|abstract)[^{]*)) # The selector \{([^{}]*)\} # The declaration block - !x'; + !xS'; // Tokens $patt->commentToken = '!___c\d+___!'; @@ -61,7 +64,7 @@ public static function init () { var\(\s*([a-z0-9_-]+)\s*\) | \$\(\s*([a-z0-9_-]+)\s*\) # Dollar syntax - )!ix'; + )!ixS'; $patt->function = '!(^|[^a-z0-9_-])([a-z_-]+)(___p\d+___)!i'; // Specific functions diff --git a/lib/Rule.php b/lib/Rule.php index e346eff..9b4a0fa 100644 --- a/lib/Rule.php +++ b/lib/Rule.php @@ -66,8 +66,11 @@ public function __construct ( $selector_string = null, $declarations_string ) { // Remove and store comments that sit above the first selector // remove all comments between the other selectors - preg_match_all( $regex->commentToken, $selectors_match->list[0], $m ); - $this->comments = $m[0]; + if ( strpos( $selectors_match->list[0], '___c' ) !== false ) { + + preg_match_all( $regex->commentToken, $selectors_match->list[0], $m ); + $this->comments = $m[0]; + } // Strip any other comments then create selector instances foreach ( $selectors_match->list as $selector ) { From 2bd15386794ce19f7b9d4741053569e1aaad52e5 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 23 Jun 2012 21:23:29 +0100 Subject: [PATCH 027/421] Option rewrite_import_urls now defaults to true Some cleaning up --- CHANGELOG.txt | 3 +++ CssCrush.php | 2 +- Plugins.ini | 3 --- lib/Core.php | 66 +++++++++++++++++++----------------------------- lib/Importer.php | 12 ++------- lib/Regex.php | 16 +++++++----- lib/Rule.php | 7 +++-- lib/Util.php | 2 +- 8 files changed, 47 insertions(+), 64 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 02168f1..9655785 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -2,6 +2,9 @@ ----- Inheritance model improved to support adoption of pseudo classes and elements (see wiki) Added rule self-referencing function 'this()' and complimentary data-* properties +Added default value argument for variables +Commas are now optional when specifying arguments for most custom functions +Option rewrite_import_urls now defaults to true Double-colon plugin moved to core diff --git a/CssCrush.php b/CssCrush.php index 60037ed..5b497d9 100644 --- a/CssCrush.php +++ b/CssCrush.php @@ -4,7 +4,7 @@ * CSS Crush * Extensible CSS preprocessor * - * @version 1.5.4-beta + * @version 1.5.4 * @link https://github.com/peteboere/css-crush * @license http://www.opensource.org/licenses/mit-license.php (MIT) * @copyright Copyright 2010-2012 Pete Boere diff --git a/Plugins.ini b/Plugins.ini index 4a208ce..8501440 100644 --- a/Plugins.ini +++ b/Plugins.ini @@ -27,9 +27,6 @@ plugins[] = ie-inline-block.php ; HSL shim - converts HSL values to hex codes ; plugins[] = hsl-to-hex.php -; Compiles pseudo element double colon syntax to single colon for backwards compatibility -plugins[] = double-colon.php - ; Non-standard composite pseudo classes plugins[] = hocus-pocus.php diff --git a/lib/Core.php b/lib/Core.php index cb123aa..c8e4314 100644 --- a/lib/Core.php +++ b/lib/Core.php @@ -475,6 +475,14 @@ public static function logError ( $msg ) { ##################### # Internal functions + public static function prepareStream ( &$stream ) { + + $stream = preg_replace_callback( csscrush_regex::$patt->commentAndString, + array( 'self', 'cb_extractCommentAndString' ), $stream ); + + $stream = csscrush_util::normalizeWhiteSpace( $stream ); + } + protected static function getBoilerplate () { $file = csscrush_util::find( 'CssCrush-local.boilerplate', 'CssCrush.boilerplate' ); @@ -544,7 +552,6 @@ protected static function getOptions ( $options ) { 'vendor_target' => 'all', // Whether to rewrite the url references inside imported files - // This will probably be 'true' by default eventually 'rewrite_import_urls' => true, // Keeping track of global vars internally @@ -1053,36 +1060,31 @@ public static function processRules () { ############################# # preg_replace callbacks - protected static function cb_extractStrings ( $match ) { - $label = csscrush::tokenLabelCreate( 's' ); - csscrush::$storage->tokens->strings[ $label ] = $match[0]; - return $label; - } + protected static function cb_extractCommentAndString ( $match ) { - protected static function cb_restoreStrings ( $match ) { - return csscrush::$storage->tokens->strings[ $match[0] ]; - } + $capture = $match[0]; - protected static function cb_extractComments ( $match ) { + if ( strpos( $capture, '/*' ) === 0 ) { - $comment = $match[0]; + // Strip private comments + $private_comment_marker = '$!'; - // Strip private comments - $private_comment_marker = '$!'; - if ( strpos( $comment, '/*' . $private_comment_marker ) === 0 ) { - return ''; + if ( strpos( $capture, '/*' . $private_comment_marker ) === 0 ) { + return ''; + } + + $label = self::tokenLabelCreate( 'c' ); + self::$storage->tokens->comments[ $label ] = $capture; } + else { - $label = self::tokenLabelCreate( 'c' ); - self::$storage->tokens->comments[ $label ] = $comment; + $label = csscrush::tokenLabelCreate( 's' ); + csscrush::$storage->tokens->strings[ $label ] = $capture; + } return $label; } - protected static function cb_restoreComments ( $match ) { - return self::$storage->tokens->comments[ $match[0] ]; - } - protected static function cb_extractMixins ( $match ) { $name = trim( $match[1] ); @@ -1173,20 +1175,12 @@ protected static function cb_extractRules ( $match ) { self::$storage->tokens->rules[ $label ] = $rule; if ( $rule->_declarations ) { - return $label . "\n"; - } - else { + // If only using extend no need to return a label - return ''; + return $label . "\n"; } } - else { - return ''; - } - } - - protected static function cb_restoreLiteral ( $match ) { - return self::$storage->tokens[ $match[0] ]; + return ''; } protected static function cb_printRule ( $match ) { @@ -1243,14 +1237,6 @@ public static function extractVariables ( &$stream ) { $stream = preg_replace_callback( csscrush_regex::$patt->variables, array( 'self', 'cb_extractVariables' ), $stream ); } - public static function extractComments ( &$stream ) { - $stream = preg_replace_callback( csscrush_regex::$patt->comment, array( 'self', 'cb_extractComments' ), $stream ); - } - - public static function extractStrings ( &$stream ) { - $stream = preg_replace_callback( csscrush_regex::$patt->string, array( 'self', 'cb_extractStrings' ), $stream ); - } - public static function extractMixins ( &$stream ) { $stream = preg_replace_callback( csscrush_regex::$patt->mixin, array( 'self', 'cb_extractMixins' ), $stream ); } diff --git a/lib/Importer.php b/lib/Importer.php index 303643e..7cbea65 100644 --- a/lib/Importer.php +++ b/lib/Importer.php @@ -52,9 +52,7 @@ public static function hostfile () { $stream = file_get_contents( $prependFile ) . $stream; } - csscrush::extractComments( $stream ); - csscrush::extractStrings( $stream ); - $stream = csscrush_util::normalizeWhiteSpace( $stream ); + csscrush::prepareStream( $stream ); // If rewriting URLs as absolute we need to do some extra work if ( $options[ 'rewrite_import_urls' ] === 'absolute' ) { @@ -119,8 +117,6 @@ public static function hostfile () { $import->mediaContext = $media_context; $import->hostDir = $process->inputDir; - // csscrush::log( $import ); - // Check to see if the url is root relative // Flatten import path for convenience if ( strpos( $import->url, '/' ) === 0 ) { @@ -141,11 +137,7 @@ public static function hostfile () { // Import file opened successfully so we process it: // - We need to resolve import statement urls in all imported files since // they will be brought inline with the hostfile - - // Start with extracting strings and comments in the import - csscrush::extractComments( $import->content ); - csscrush::extractStrings( $import->content ); - $import->content = csscrush_util::normalizeWhiteSpace( $import->content ); + csscrush::prepareStream( $import->content ); $import->dir = dirname( $import->url ); diff --git a/lib/Regex.php b/lib/Regex.php index bbb82f8..bf45f43 100644 --- a/lib/Regex.php +++ b/lib/Regex.php @@ -5,7 +5,6 @@ * */ - class csscrush_regex { public static $patt; @@ -34,19 +33,22 @@ public static function init () { ([_s\d]+) # string token ) \s*([^;]*); # media argument - !x'; + !xS'; $patt->variables = '!@(?:variables|define)\s*([^\{]*)\{\s*(.*?)\s*\};?!s'; $patt->mixin = '!@mixin\s*([^\{]*)\{\s*(.*?)\s*\};?!s'; $patt->abstract = csscrush_regex::create( '^@abstract\s+()', 'i' ); - $patt->comment = '!/\*(.*?)\*/!sS'; - $patt->string = '!(\'|")(?:\\1|[^\1])*?\1!S'; + $patt->commentAndString = '! + (\'|")(?:\\1|[^\1])*?\1 + | + /\*(?:.*?)\*/ + !xsS'; // As an exception we treat some @-rules like standard rule blocks $patt->rule = '! (\n(?:[^@{}]+|@(?:font-face|page|abstract)[^{]*)) # The selector \{([^{}]*)\} # The declaration block - !x'; + !xS'; // Tokens $patt->commentToken = '!___c\d+___!'; @@ -61,7 +63,7 @@ public static function init () { var\(\s*([a-z0-9_-]+)\s*\) | \$\(\s*([a-z0-9_-]+)\s*\) # Dollar syntax - )!ix'; + )!ixS'; $patt->function = '!(^|[^a-z0-9_-])([a-z_-]+)(___p\d+___)!i'; // Specific functions @@ -106,7 +108,7 @@ public static function createFunctionMatchPatt ( $list, $include_unnamed_functio foreach ( $list as &$fn_name ) { $fn_name = preg_quote( $fn_name ); } - return '!(^|[^a-z0-9_-])(' . implode( '|', $list ) . ')' . $question . '\(!i'; + return '!(^|[^a-z0-9_-])(' . implode( '|', $list ) . ')' . $question . '\(!iS'; } } diff --git a/lib/Rule.php b/lib/Rule.php index e346eff..9b4a0fa 100644 --- a/lib/Rule.php +++ b/lib/Rule.php @@ -66,8 +66,11 @@ public function __construct ( $selector_string = null, $declarations_string ) { // Remove and store comments that sit above the first selector // remove all comments between the other selectors - preg_match_all( $regex->commentToken, $selectors_match->list[0], $m ); - $this->comments = $m[0]; + if ( strpos( $selectors_match->list[0], '___c' ) !== false ) { + + preg_match_all( $regex->commentToken, $selectors_match->list[0], $m ); + $this->comments = $m[0]; + } // Strip any other comments then create selector instances foreach ( $selectors_match->list as $selector ) { diff --git a/lib/Util.php b/lib/Util.php index 0b459c2..34be935 100644 --- a/lib/Util.php +++ b/lib/Util.php @@ -28,7 +28,7 @@ public static function strEndsWith ( $haystack, $needle ) { public static function normalizePath ( $path, $strip_ms_dos = false ) { - $path = rtrim( preg_replace( '![\\/]+!', '/', $path ), '/' ); + $path = rtrim( preg_replace( '![\\\\/]+!', '/', $path ), '/' ); if ( $strip_ms_dos ) { $path = preg_replace( '!^[a-z]\:!i', '', $path ); From 1a6a79c0cf125dad56380bc1dfdd81de109e1b4d Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 28 Jun 2012 09:50:50 +0100 Subject: [PATCH 028/421] Added compare method to csscrush_version objects --- lib/Util.php | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/lib/Util.php b/lib/Util.php index 34be935..dd931f9 100644 --- a/lib/Util.php +++ b/lib/Util.php @@ -343,9 +343,9 @@ public function update ( $newValue ) { */ class csscrush_version { - public $major; - public $minor; - public $revision; + public $major = 0; + public $minor = 0; + public $revision = 0; public $extra; public function __construct ( $version_string ) { @@ -384,5 +384,26 @@ public function __toString () { return $out; } + + public function compare ( $version_string ) { + + $LESS = -1; + $MORE = 1; + $EQUAL = 0; + + $test = new csscrush_version( $version_string ); + + foreach ( array( 'major', 'minor', 'revision' ) as $level ) { + + if ( $test->{ $level } < $this->{ $level } ) { + return $LESS; + } + elseif ( $test->{ $level } > $this->{ $level } ) { + return $MORE; + } + } + + return $EQUAL; + } } From 1a8bde80547c0a0f3ee06c03e3e712b8c643be0b Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 7 Jul 2012 12:55:38 +0100 Subject: [PATCH 029/421] Query referencing branch --- lib/Core.php | 19 +++++---- lib/Function.php | 8 ++-- lib/Mixin.php | 2 +- lib/Regex.php | 1 + lib/Rule.php | 107 ++++++++++++++++++++++++++++++++++++++++------- 5 files changed, 110 insertions(+), 27 deletions(-) diff --git a/lib/Core.php b/lib/Core.php index c8e4314..2e5672b 100644 --- a/lib/Core.php +++ b/lib/Core.php @@ -655,7 +655,7 @@ protected static function placeVariables ( &$stream ) { // Substitute variables with default values $var_fn_patt = csscrush_regex::createFunctionMatchPatt( array( '$' ) ); - $var_fn_callback = array( 'csscrush', 'cb_varFunctionWithDefault' ); + $var_fn_callback = array( '$' => array( 'csscrush', 'cb_varFunctionWithDefault' ) ); csscrush_function::executeCustomFunctions( $stream, $var_fn_patt, $var_fn_callback ); // Repeat above steps for variables embedded in string tokens @@ -679,7 +679,6 @@ protected static function reset ( $options = null ) { self::$process = new stdclass(); self::$process->cacheData = array(); self::$process->mixins = array(); - self::$process->fragments = array(); self::$process->abstracts = array(); self::$process->errors = array(); self::$process->selectorRelationships = array(); @@ -726,8 +725,8 @@ protected static function compile ( $stream ) { // Pull out the mixin declarations self::extractMixins( $stream ); - // Pull out the fragments - self::extractFragments( $stream ); + // Process fragments + self::resolveFragments( $stream ); // Adjust the stream so we can extract the rules cleanly $map = array( @@ -762,6 +761,9 @@ protected static function compile ( $stream ) { // Release memory self::$storage = null; + self::$process->mixins = null; + self::$process->abstracts = null; + self::$process->selectorRelationships = null; return $stream; } @@ -1241,9 +1243,10 @@ public static function extractMixins ( &$stream ) { $stream = preg_replace_callback( csscrush_regex::$patt->mixin, array( 'self', 'cb_extractMixins' ), $stream ); } - public static function extractFragments ( &$stream ) { + public static function resolveFragments ( &$stream ) { $matches = csscrush_regex::matchAll( '@fragment\s+()\s*{', $stream, true ); + $fragments = array(); // Move through the matches last to first while ( $match = array_pop( $matches ) ) { @@ -1267,10 +1270,10 @@ public static function extractFragments ( &$stream ) { $stream = $before . $curly_match->after; // Create the fragment and store it - self::$process->fragments[ $fragment_name ] = + $fragments[ $fragment_name ] = new csscrush_fragment( $curly_match->inside ); - // csscrush::log( self::$process->fragments ); + // csscrush::log( $fragments ); } } @@ -1288,7 +1291,7 @@ public static function extractFragments ( &$stream ) { $fragment_name = $match[1][0]; // The fragment object, or null if name not present - $fragment = isset( self::$process->fragments[ $fragment_name ] ) ? self::$process->fragments[ $fragment_name ] : null; + $fragment = isset( $fragments[ $fragment_name ] ) ? $fragments[ $fragment_name ] : null; // Fragment may be called without any argument list $with_arguments = $match[2][0] === '('; diff --git a/lib/Function.php b/lib/Function.php index cb6dfbc..9334b9b 100644 --- a/lib/Function.php +++ b/lib/Function.php @@ -35,7 +35,7 @@ public static function getFunctions () { return $fn_methods; } - public static function executeCustomFunctions ( &$str, $patt = null, $process_callback = null ) { + public static function executeCustomFunctions ( &$str, $patt = null, $process_callback = null, $property = null ) { // No bracketed expressions, early return if ( false === strpos( $str, '(' ) ) { @@ -113,7 +113,9 @@ public static function executeCustomFunctions ( &$str, $patt = null, $process_ca } } else { - $result = call_user_func( $process_callback, $content ); + if ( isset( $process_callback[ $fn_name ] ) ) { + $result = call_user_func( $process_callback[ $fn_name ], $content, $fn_name, $property ); + } } // Join together the result @@ -135,7 +137,7 @@ public static function executeCustomFunctions ( &$str, $patt = null, $process_ca ############ # Helpers - protected static function parseArgs ( $input, $allowSpaceDelim = false ) { + public static function parseArgs ( $input, $allowSpaceDelim = false ) { $args = csscrush_util::splitDelimList( $input, diff --git a/lib/Mixin.php b/lib/Mixin.php index b91edae..cc3d093 100644 --- a/lib/Mixin.php +++ b/lib/Mixin.php @@ -232,7 +232,7 @@ function __construct ( $str ) { // Parse all arg function calls in the passed string, callback creates default values csscrush_function::executeCustomFunctions( $str, - csscrush_regex::$patt->argFunction, array( $this, 'store' ) ); + csscrush_regex::$patt->argFunction, array( 'arg' => array( $this, 'store' ) ) ); $this->string = $str; } diff --git a/lib/Regex.php b/lib/Regex.php index bf45f43..8f9cac6 100644 --- a/lib/Regex.php +++ b/lib/Regex.php @@ -69,6 +69,7 @@ public static function init () { // Specific functions $patt->argFunction = csscrush_regex::createFunctionMatchPatt( array( 'arg' ) ); $patt->thisFunction = csscrush_regex::createFunctionMatchPatt( array( 'this' ) ); + $patt->referenceFunction = csscrush_regex::createFunctionMatchPatt( array( 'this', 'query' ) ); // Misc. $patt->vendorPrefix = '!^-([a-z]+)-([a-z-]+)!'; diff --git a/lib/Rule.php b/lib/Rule.php index 9b4a0fa..737d56c 100644 --- a/lib/Rule.php +++ b/lib/Rule.php @@ -23,7 +23,10 @@ class csscrush_rule implements IteratorAggregate { public $_declarations = array(); - // A table for storing the declarations as data for the this() reference function + // A table for storing the declarations as data for this() referencing + public $localData = array(); + + // A table for storing the declarations as data for external query() referencing public $data = array(); public function declarationCheckin ( $prop, $value, &$pairs ) { @@ -35,6 +38,9 @@ public function declarationCheckin ( $prop, $value, &$pairs ) { // If it's with data prefix, we don't want to print it // Just remove the prefix $prop = substr( $prop, strlen( 'data-' ) ); + + // On first pass we only want to store data properties on $this->data + $this->data[ $prop ] = $value; } else { @@ -42,14 +48,18 @@ public function declarationCheckin ( $prop, $value, &$pairs ) { $pairs[] = array( $prop, $value ); } - // Set on the data table - $this->data[ $prop ] = $value; + // Set on $this->localData + $this->localData[ $prop ] = $value; - // Unset on data table if the value has a 'this' function call: + // Unset on data tables if the value has a this() call: // - Restriction to avoid circular references if ( preg_match( csscrush_regex::$patt->thisFunction, $value ) ) { - unset( $this->data[ $prop ] ); + unset( $this->localData[ $prop ] ); + + if ( isset( $this->data[ $prop ] ) ) { + unset( $this->data[ $prop ] ); + } } } } @@ -151,10 +161,24 @@ public function __construct ( $selector_string = null, $declarations_string ) { // Resolve this() references csscrush_function::executeCustomFunctions( $value, - csscrush_regex::$patt->thisFunction, array( $this, 'thisCssFunction' ) ); + csscrush_regex::$patt->referenceFunction, array( + 'this' => array( $this, 'cssThisFunction' ), + 'query' => array( $this, 'cssQueryFunction' ), + ), $prop ); + + if ( trim( $value ) !== '' ) { - $this->addDeclaration( $prop, $value ); + // Add declaration and update the data table + $this->data[ $prop ] = $value; + $this->addDeclaration( $prop, $value ); + } } + + // csscrush::log( $this->localData, 'LocalData' ); + // csscrush::log( $this->data, 'Data' ); + + // localData no longer required + $this->localData = null; } public function __set ( $name, $value ) { @@ -174,13 +198,13 @@ public function __get ( $name ) { } } - public function thisCssFunction ( $raw_argument ) { + public function cssThisFunction ( $input, $fn_name ) { - $args = csscrush_function::parseArgsSimple( $raw_argument ); + $args = csscrush_function::parseArgsSimple( $input ); - if ( isset( $this->data[ $args[0] ] ) ) { + if ( isset( $this->localData[ $args[0] ] ) ) { - return $this->data[ $args[0] ]; + return $this->localData[ $args[0] ]; } elseif ( isset( $args[1] ) ) { @@ -192,6 +216,55 @@ public function thisCssFunction ( $raw_argument ) { } } + public function cssQueryFunction ( $input, $fn_name, $property ) { + + $result = ''; + $args = csscrush_function::parseArgs( $input ); + + if ( count( $args ) < 1 ) { + return $result; + } + + $abstracts =& csscrush::$process->abstracts; + $selectorRelationships =& csscrush::$process->selectorRelationships; + + // Resolve arguments + $name = array_shift( $args ); + $property = isset( $args[0] ) ? array_shift( $args ) : $property; + $default = isset( $args[0] ) ? $args[0] : null; + + // csscrush::log( array( $name, $property, $default ) ); + + // Try to match a abstract rule first + if ( preg_match( csscrush_regex::$patt->name, $name ) ) { + + // Search order: abstracts, rules + if ( isset( $abstracts[ $name ]->data[ $property ] ) ) { + + $result = $abstracts[ $name ]->data[ $property ]; + } + elseif ( isset( $selectorRelationships[ $name ]->data[ $property ] ) ) { + + $result = $selectorRelationships[ $name ]->data[ $property ]; + } + } + else { + + // Look for a rule match + $name = csscrush_selector::makeReadableSelector( $name ); + if ( isset( $selectorRelationships[ $name ]->data[ $property ] ) ) { + + $result = $selectorRelationships[ $name ]->data[ $property ]; + } + } + + if ( $result === '' && ! is_null( $default ) ) { + $result = $default; + } + + return $result; + } + public function updatePropertyTable () { // Create a new table of properties @@ -446,8 +519,8 @@ public function indexSelectors () { public function setExtendSelectors ( $raw_value ) { - $abstracts = csscrush::$process->abstracts; - $selectorRelationships = csscrush::$process->selectorRelationships; + $abstracts =& csscrush::$process->abstracts; + $selectorRelationships =& csscrush::$process->selectorRelationships; // Pass extra argument to trim the returned list $args = csscrush_util::splitDelimList( $raw_value, ',', true, true ); @@ -463,8 +536,12 @@ public function setExtendSelectors ( $raw_value ) { public function applyExtendables () { - $abstracts = csscrush::$process->abstracts; - $selectorRelationships = csscrush::$process->selectorRelationships; + if ( ! $this->extendArgs ) { + return; + } + + $abstracts =& csscrush::$process->abstracts; + $selectorRelationships =& csscrush::$process->selectorRelationships; // Filter the extendArgs list to usable references foreach ( $this->extendArgs as $key => $extend_arg ) { From 2008448ea80734537605b61211440f0161741f80 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 14 Jul 2012 15:23:15 +0100 Subject: [PATCH 030/421] Added hsl-adjust and hsla-adjust functions Some speed optimisations Upgraded version.next to 1.6 --- CHANGELOG.txt | 4 ++-- CssCrush.php | 2 +- lib/Core.php | 23 ++++++++++------------- lib/Function.php | 20 +++++++++++++++----- lib/IO.php | 4 ++-- lib/Importer.php | 4 ++-- lib/Regex.php | 4 ++-- lib/Rule.php | 29 ++++++++++++++++++++++------- lib/Util.php | 4 ++-- 9 files changed, 58 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 2b194dd..87ff6a9 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,5 +1,5 @@ -1.5.4 ------ +1.6 +--- Inheritance model improved to support adoption of pseudo classes and elements (see wiki) Added rule self-referencing function 'this()' and complimentary data-* properties Added default value argument for variables diff --git a/CssCrush.php b/CssCrush.php index 5b497d9..8c0d906 100644 --- a/CssCrush.php +++ b/CssCrush.php @@ -4,7 +4,7 @@ * CSS Crush * Extensible CSS preprocessor * - * @version 1.5.4 + * @version 1.6 * @link https://github.com/peteboere/css-crush * @license http://www.opensource.org/licenses/mit-license.php (MIT) * @copyright Copyright 2010-2012 Pete Boere diff --git a/lib/Core.php b/lib/Core.php index 2e5672b..e934873 100644 --- a/lib/Core.php +++ b/lib/Core.php @@ -676,14 +676,14 @@ protected static function reset ( $options = null ) { // Reset properties for current process self::$tokenUID = 0; - self::$process = new stdclass(); + self::$process = (object) array(); self::$process->cacheData = array(); self::$process->mixins = array(); self::$process->abstracts = array(); self::$process->errors = array(); self::$process->selectorRelationships = array(); - self::$storage = new stdclass(); + self::$storage = (object) array(); self::$storage->tokens = (object) array( 'strings' => array(), 'comments' => array(), @@ -693,7 +693,7 @@ protected static function reset ( $options = null ) { 'urls' => array(), ); self::$storage->variables = array(); - self::$storage->misc = new stdclass(); + self::$storage->misc = (object) array(); // Load the merged options self::$options = self::getOptions( $options ); @@ -1161,7 +1161,7 @@ public static function cb_varFunctionWithDefault ( $raw_argument ) { protected static function cb_extractRules ( $match ) { - $rule = new stdclass(); + $rule = (object) array(); $rule->selector_raw = trim( $match[1] ); $rule->declaration_raw = trim( $match[2] ); @@ -1170,17 +1170,14 @@ protected static function cb_extractRules ( $match ) { $rule = new csscrush_rule( $rule->selector_raw, $rule->declaration_raw ); // Store rules if they have declarations or extend arguments - if ( $rule->_declarations || $rule->extendArgs ) { + if ( count( $rule ) || $rule->extendArgs ) { $label = $rule->label; self::$storage->tokens->rules[ $label ] = $rule; - if ( $rule->_declarations ) { - - // If only using extend no need to return a label - return $label . "\n"; - } + // If only using extend still return a label + return $label . "\n"; } return ''; } @@ -1197,10 +1194,10 @@ protected static function cb_printRule ( $match ) { return ''; } - $rule = self::$storage->tokens->rules[ $ruleLabel ]; + $rule =& self::$storage->tokens->rules[ $ruleLabel ]; - // If there are no selectors associated with the rule return empty string - if ( empty( $rule->selectorList ) ) { + // If there are no selectors or declarations associated with the rule return empty string + if ( empty( $rule->selectorList ) || ! count( $rule ) ) { return ''; } diff --git a/lib/Function.php b/lib/Function.php index 9334b9b..6c3eb5b 100644 --- a/lib/Function.php +++ b/lib/Function.php @@ -305,7 +305,7 @@ public static function css_fn__pc ( $input ) { } public static function css_fn__data_uri ( $input ) { - +return ''; // Normalize, since argument might be a string token if ( strpos( $input, '___s' ) === 0 ) { $string_labels = array_keys( csscrush::$storage->tokens->strings ); @@ -358,23 +358,33 @@ public static function css_fn__data_uri ( $input ) { return "url(http://github.com/%22$data_uri/")"; } + public static function css_fn__hsla_adjust ( $input ) { + list( $color, $h, $s, $l, $a ) = array_pad( self::parseArgs( $input, true ), 5, 0 ); + return self::colorAdjust( $color, array( $h, $s, $l, $a ) ); + } + + public static function css_fn__hsl_adjust ( $input ) { + list( $color, $h, $s, $l ) = array_pad( self::parseArgs( $input, true ), 4, 0 ); + return self::colorAdjust( $color, array( $h, $s, $l, 0 ) ); + } + public static function css_fn__h_adjust ( $input ) { - @list( $color, $h ) = self::parseArgs( $input, true ); + list( $color, $h ) = array_pad( self::parseArgs( $input, true ), 2, 0 ); return self::colorAdjust( $color, array( $h, 0, 0, 0 ) ); } public static function css_fn__s_adjust ( $input ) { - @list( $color, $s ) = self::parseArgs( $input, true ); + list( $color, $s ) = array_pad( self::parseArgs( $input, true ), 2, 0 ); return self::colorAdjust( $color, array( 0, $s, 0, 0 ) ); } public static function css_fn__l_adjust ( $input ) { - @list( $color, $l ) = self::parseArgs( $input, true ); + list( $color, $l ) = array_pad( self::parseArgs( $input, true ), 2, 0 ); return self::colorAdjust( $color, array( 0, 0, $l, 0 ) ); } public static function css_fn__a_adjust ( $input ) { - @list( $color, $a ) = self::parseArgs( $input, true ); + list( $color, $a ) = array_pad( self::parseArgs( $input, true ), 2, 0 ); return self::colorAdjust( $color, array( 0, 0, 0, $a ) ); } diff --git a/lib/IO.php b/lib/IO.php index 6813041..c86b84f 100644 --- a/lib/IO.php +++ b/lib/IO.php @@ -26,7 +26,7 @@ public static function getInput ( $file = false ) { $process = csscrush::$process; // Make basic information about the input object accessible - $input = new stdclass(); + $input = (object) array(); $input->dir = ! empty( $process->inputDir ) ? $process->inputDir : null; $input->name = $file ? basename( $file ) : null; $input->path = $file ? "$process->inputDir/$input->name" : null; @@ -120,7 +120,7 @@ public static function validateExistingOutput () { // Cached file exists csscrush::log( 'Cached file exists' ); - $existingfile = new stdclass(); + $existingfile = (object) array(); $existingfile->name = $filename; $existingfile->path = "$process->outputDir/$existingfile->name"; $existingfile->URL = "$process->outputDirUrl/$existingfile->name"; diff --git a/lib/Importer.php b/lib/Importer.php index 7cbea65..708b9ba 100644 --- a/lib/Importer.php +++ b/lib/Importer.php @@ -112,7 +112,7 @@ public static function hostfile () { } // Create import object - $import = new stdclass(); + $import = (object) array(); $import->url = $url; $import->mediaContext = $media_context; $import->hostDir = $process->inputDir; @@ -129,7 +129,7 @@ public static function hostfile () { // Get the import contents, if unsuccessful just continue with the import line removed if ( ! ( $import->content = @file_get_contents( $import->path ) ) ) { - csscrush::log( "Import file '$import->url' not found at '$import->path'" ); + csscrush::log( "Import file '$import->url' not found" ); $stream = $pre_statement . $post_statement; continue; } diff --git a/lib/Regex.php b/lib/Regex.php index 8f9cac6..21a1453 100644 --- a/lib/Regex.php +++ b/lib/Regex.php @@ -14,8 +14,8 @@ class csscrush_regex { public static function init () { - self::$patt = $patt = new stdclass(); - self::$class = $class = new stdclass(); + self::$patt = $patt = (object) array(); + self::$class = $class = (object) array(); // Character classes $class->name = '[a-zA-Z0-9_-]+'; diff --git a/lib/Rule.php b/lib/Rule.php index 737d56c..f98ad2c 100644 --- a/lib/Rule.php +++ b/lib/Rule.php @@ -5,7 +5,7 @@ * */ -class csscrush_rule implements IteratorAggregate { +class csscrush_rule implements IteratorAggregate, Countable { public $vendorContext; public $isNested; @@ -39,7 +39,8 @@ public function declarationCheckin ( $prop, $value, &$pairs ) { // Just remove the prefix $prop = substr( $prop, strlen( 'data-' ) ); - // On first pass we only want to store data properties on $this->data + // On first pass we want to store data properties on $this->data, + // as well as on local $this->data[ $prop ] = $value; } else { @@ -173,7 +174,6 @@ public function __construct ( $selector_string = null, $declarations_string ) { $this->addDeclaration( $prop, $value ); } } - // csscrush::log( $this->localData, 'LocalData' ); // csscrush::log( $this->data, 'Data' ); @@ -216,7 +216,7 @@ public function cssThisFunction ( $input, $fn_name ) { } } - public function cssQueryFunction ( $input, $fn_name, $property ) { + public function cssQueryFunction ( $input, $fn_name, $call_property ) { $result = ''; $args = csscrush_function::parseArgs( $input ); @@ -230,10 +230,18 @@ public function cssQueryFunction ( $input, $fn_name, $property ) { // Resolve arguments $name = array_shift( $args ); - $property = isset( $args[0] ) ? array_shift( $args ) : $property; + $property = $call_property; + if ( isset( $args[0] ) ) { + if ( $args[0] !== 'default' ) { + $property = array_shift( $args ); + } + else { + array_shift( $args ); + } + } $default = isset( $args[0] ) ? $args[0] : null; - // csscrush::log( array( $name, $property, $default ) ); + // csscrush::log( array( $name, $property, $default ), 'query args' ); // Try to match a abstract rule first if ( preg_match( csscrush_regex::$patt->name, $name ) ) { @@ -261,7 +269,6 @@ public function cssQueryFunction ( $input, $fn_name, $property ) { if ( $result === '' && ! is_null( $default ) ) { $result = $default; } - return $result; } @@ -619,6 +626,14 @@ public function getIterator () { } + ############ + # Countable + + public function count() { + + return count( $this->_declarations ); + } + ############ # Rule API diff --git a/lib/Util.php b/lib/Util.php index dd931f9..f3cc391 100644 --- a/lib/Util.php +++ b/lib/Util.php @@ -181,7 +181,7 @@ public static function matchBrackets ( $str, $brackets = array( '(', ')' ), $sea $closings = array(); $brake = 50; // Set a limit in the case of errors - $match = new stdclass(); + $match = (object) array(); $start_index = strpos( $str, $opener, $search_pos ); $close_index = strpos( $str, $closer, $search_pos ); @@ -245,7 +245,7 @@ public static function matchBrackets ( $str, $brackets = array( '(', ')' ), $sea public static function matchAllBrackets ( $str, $pair = '()', $offset = 0 ) { - $match_obj = new stdclass(); + $match_obj = (object) array(); $match_obj->string = $str; $match_obj->raw = $str; $match_obj->matches = array(); From b88638b967449b0bcf18150a7cc69f19ab01d025 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 31 Jul 2012 10:35:58 +0100 Subject: [PATCH 031/421] Resolved some edge cases, added composer.json --- composer.json | 11 +++++++++++ lib/Core.php | 5 ++++- lib/Function.php | 4 ++-- lib/IO.php | 7 ++++--- lib/Importer.php | 8 +++++--- lib/Util.php | 6 +++++- 6 files changed, 31 insertions(+), 10 deletions(-) create mode 100644 composer.json diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..3932cba --- /dev/null +++ b/composer.json @@ -0,0 +1,11 @@ +{ + "name": "CssCrush/CssCrush", + "type": "library", + "description": "CSS preprocessor", + "keywords": ["css", "preprocessor"], + "homepage": "http://github.com/peteboere/css-crush", + "license": "MIT", + "require": { + "php": ">=5.12" + } +} \ No newline at end of file diff --git a/lib/Core.php b/lib/Core.php index e934873..6700a37 100644 --- a/lib/Core.php +++ b/lib/Core.php @@ -817,7 +817,10 @@ protected static function display ( &$stream ) { foreach ( csscrush::$storage->tokens->urls as $token => $url ) { // Optionally set the URLs to absolute - if ( self::$options[ 'rewrite_import_urls' ] === 'absolute' ) { + if ( + self::$options[ 'rewrite_import_urls' ] === 'absolute' && + strpos( $url, 'data:' ) !== 0 + ) { $url = self::$process->inputDirUrl . '/' . $url; } csscrush::$storage->tokens->urls[ $token ] = csscrush_util::cleanUpUrl( $url ); diff --git a/lib/Function.php b/lib/Function.php index 6c3eb5b..395913a 100644 --- a/lib/Function.php +++ b/lib/Function.php @@ -305,7 +305,7 @@ public static function css_fn__pc ( $input ) { } public static function css_fn__data_uri ( $input ) { -return ''; + // Normalize, since argument might be a string token if ( strpos( $input, '___s' ) === 0 ) { $string_labels = array_keys( csscrush::$storage->tokens->strings ); @@ -347,7 +347,7 @@ public static function css_fn__data_uri ( $input ) { 'jpg' => 'image/jpg', 'png' => 'image/png', ); - if ( !array_key_exists( $file_ext, $allowed_file_extensions ) ) { + if ( ! array_key_exists( $file_ext, $allowed_file_extensions ) ) { return $result; } diff --git a/lib/IO.php b/lib/IO.php index c86b84f..46257ff 100644 --- a/lib/IO.php +++ b/lib/IO.php @@ -153,9 +153,9 @@ public static function validateExistingOutput () { $existing_options = $process->cacheData[ $existingfile->name ][ 'options' ]; $existing_datesum = $process->cacheData[ $existingfile->name ][ 'datem_sum' ]; - $options_unchanged = $existing_options == csscrush::$options; + $options_unchanged = ! array_diff( $existing_options, csscrush::$options ); $files_unchanged = $existing_datesum == array_sum( $all_files ); - + if ( $options_unchanged && $files_unchanged ) { // Files have not been modified and config is the same: return the old file @@ -164,11 +164,12 @@ public static function validateExistingOutput () { return $existingfile->URL . ( csscrush::$options[ 'versioning' ] !== false ? "?$existing_datesum" : '' ); } else { + // Remove old file and continue making a new one... ! $options_unchanged && csscrush::log( 'Options have been modified' ); ! $files_unchanged && csscrush::log( 'Files have been modified' ); csscrush::log( 'Removing existing file' ); - + unlink( $existingfile->path ); } } diff --git a/lib/Importer.php b/lib/Importer.php index 708b9ba..76a83ec 100644 --- a/lib/Importer.php +++ b/lib/Importer.php @@ -378,13 +378,15 @@ protected static function cb_rewriteUrls ( $match ) { $url = csscrush_util::normalizePath( $url ); // No rewrite if: - // - $url begins with a variable, e.g '$(' - // - $url path is absolute or begins with slash // - $url is an empty string + // - $url path is absolute or begins with slash + // - $url begins with a variable, e.g '$(' + // - $url is a data uri if ( - empty( $url ) || + $url === '' || strpos( $url, '/' ) === 0 || strpos( $url, '$(' ) === 0 || + strpos( $url, 'data:' ) === 0 || preg_match( $regex->absoluteUrl, $url ) ) { diff --git a/lib/Util.php b/lib/Util.php index f3cc391..09ce4c0 100644 --- a/lib/Util.php +++ b/lib/Util.php @@ -162,7 +162,11 @@ public static function splitDelimList ( $str, $delim, $fold_in = false, $trim = } // Filter out empties - $match_obj->list = array_filter( $match_obj->list ); + foreach( $match_obj->list as $key => &$item ) { + if ( $item === '' ) { + unset( $match_obj->list[$key] ); + } + } if ( $fold_in ) { From 83542a3079395f2ebe501f6811accd1f2a90ae3a Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 1 Aug 2012 10:58:05 +0100 Subject: [PATCH 032/421] Mixin and fragment arg() function can now be nested query() can now reference mixins Updated composer.json and Changelog --- CHANGELOG.txt | 5 ++++- CssCrush.php | 2 +- composer.json | 17 +++++++++++++++-- lib/Mixin.php | 36 ++++++++++++++++++++++++++---------- lib/Regex.php | 3 ++- lib/Rule.php | 30 +++++++++++++++++++----------- 6 files changed, 67 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 87ff6a9..8445336 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,8 +1,11 @@ 1.6 --- Inheritance model improved to support adoption of pseudo classes and elements (see wiki) -Added rule self-referencing function 'this()' and complimentary data-* properties +Added rule self-referencing function this() and complimentary data-* properties +Added rule referencing function query() Added default value argument for variables +Added hsl-adjust() and hsla-adjust() color functions +Mixin and fragment arg() function can now be nested Commas are now optional when specifying arguments for most custom functions Double-colon plugin moved to core Option rewrite_import_urls now defaults to true diff --git a/CssCrush.php b/CssCrush.php index 8c0d906..fdc7f69 100644 --- a/CssCrush.php +++ b/CssCrush.php @@ -7,7 +7,7 @@ * @version 1.6 * @link https://github.com/peteboere/css-crush * @license http://www.opensource.org/licenses/mit-license.php (MIT) - * @copyright Copyright 2010-2012 Pete Boere + * @copyright (c) 2010-2012 Pete Boere * */ diff --git a/composer.json b/composer.json index 3932cba..7ddf044 100644 --- a/composer.json +++ b/composer.json @@ -1,11 +1,24 @@ { - "name": "CssCrush/CssCrush", + "name": "css-crush/css-crush", "type": "library", "description": "CSS preprocessor", "keywords": ["css", "preprocessor"], "homepage": "http://github.com/peteboere/css-crush", "license": "MIT", + "authors": [ + { + "name": "Pete Boere", + "email": "pete@the-echoplex.net" + }, + { + "name": "GitHub contributors", + "homepage": "https://github.com/peteboere/css-crush/contributors" + } + ], "require": { - "php": ">=5.12" + "php": ">=5.2.4" + }, + "autoload": { + "files": ["CssCrush.php"] } } \ No newline at end of file diff --git a/lib/Mixin.php b/lib/Mixin.php index cc3d093..0508a4e 100644 --- a/lib/Mixin.php +++ b/lib/Mixin.php @@ -11,6 +11,8 @@ class csscrush_mixin { public $arguments; + public $data = array(); + public function __construct ( $block ) { // Strip comment markers @@ -51,6 +53,14 @@ public function __construct ( $block ) { $this->declarationsTemplate[] = $declaration; } } + + // Create data table for the mixin. + // Values that use arg() are excluded + foreach ( $this->declarationsTemplate as &$declaration ) { + if ( ! preg_match( csscrush_regex::$patt->argToken, $declaration['value'] ) ) { + $this->data[ $declaration['property'] ] = $declaration['value']; + } + } return ''; } @@ -145,7 +155,6 @@ public static function parseSingleValue ( $message ) { $args = array(); if ( $message !== '' ) { $args = csscrush_util::splitDelimList( $message, ',', true, true ); - // $args = array_map( 'trim', $args->list ); $args = $args->list; } @@ -265,9 +274,22 @@ public function store ( $raw_argument ) { return "___arg{$position_match}___"; } - public function getDefaultValue ( $index ) { + public function getArgValue ( $index, &$args ) { + + // First lookup a passed value + if ( isset( $args[ $index ] ) && $args[ $index ] !== 'default' ) { + return $args[ $index ]; + } + + // Get a default value + $default = isset( $this->defaults[ $index ] ) ? $this->defaults[ $index ] : ''; - return isset( $this->defaults[ $index ] ) ? $this->defaults[ $index ] : ''; + // Recurse for nested arg() calls + if ( preg_match( csscrush_regex::$patt->argToken, $default, $m ) ) { + + $default = $this->getArgValue( (int) $m[1], $args ); + } + return $default; } public function getSubstitutions ( $args ) { @@ -281,13 +303,7 @@ public function getSubstitutions ( $args ) { foreach ( $argIndexes as $index ) { $find[] = "___arg{$index}___"; - - if ( isset( $args[ $index ] ) && $args[ $index ] !== 'default' ) { - $replace[] = $args[ $index ]; - } - else { - $replace[] = $this->getDefaultValue( $index ); - } + $replace[] = $this->getArgValue( $index, $args ); } return array( $find, $replace ); diff --git a/lib/Regex.php b/lib/Regex.php index 21a1453..e91dd45 100644 --- a/lib/Regex.php +++ b/lib/Regex.php @@ -56,6 +56,7 @@ public static function init () { $patt->ruleToken = '!___r\d+___!'; $patt->parenToken = '!___p\d+___!'; $patt->urlToken = '!___u\d+___!'; + $patt->argToken = '!___arg(\d+)___!'; // Functions $patt->varFunction = '!(?: @@ -68,8 +69,8 @@ public static function init () { // Specific functions $patt->argFunction = csscrush_regex::createFunctionMatchPatt( array( 'arg' ) ); + $patt->queryFunction = csscrush_regex::createFunctionMatchPatt( array( 'query' ) ); $patt->thisFunction = csscrush_regex::createFunctionMatchPatt( array( 'this' ) ); - $patt->referenceFunction = csscrush_regex::createFunctionMatchPatt( array( 'this', 'query' ) ); // Misc. $patt->vendorPrefix = '!^-([a-z]+)-([a-z-]+)!'; diff --git a/lib/Rule.php b/lib/Rule.php index f98ad2c..ef47c53 100644 --- a/lib/Rule.php +++ b/lib/Rule.php @@ -33,6 +33,15 @@ public function declarationCheckin ( $prop, $value, &$pairs ) { if ( $prop !== '' && $value !== '' ) { + // First resolve query() calls that reference earlier rules + if ( preg_match( csscrush_regex::$patt->queryFunction, $value ) ) { + + csscrush_function::executeCustomFunctions( $value, + csscrush_regex::$patt->queryFunction, array( + 'query' => array( $this, 'cssQueryFunction' ), + ), $prop ); + } + if ( strpos( $prop, 'data-' ) === 0 ) { // If it's with data prefix, we don't want to print it @@ -57,10 +66,7 @@ public function declarationCheckin ( $prop, $value, &$pairs ) { if ( preg_match( csscrush_regex::$patt->thisFunction, $value ) ) { unset( $this->localData[ $prop ] ); - - if ( isset( $this->data[ $prop ] ) ) { - unset( $this->data[ $prop ] ); - } + unset( $this->data[ $prop ] ); } } } @@ -146,7 +152,7 @@ public function __construct ( $selector_string = null, $declarations_string ) { } elseif ( $prop === 'extends' ) { - // Extends is a special case + // Extends are also a special case $this->setExtendSelectors( $value ); } else { @@ -160,11 +166,10 @@ public function __construct ( $selector_string = null, $declarations_string ) { list( $prop, $value ) = $pair; - // Resolve this() references + // Resolve self references, aka this() csscrush_function::executeCustomFunctions( $value, - csscrush_regex::$patt->referenceFunction, array( + csscrush_regex::$patt->thisFunction, array( 'this' => array( $this, 'cssThisFunction' ), - 'query' => array( $this, 'cssQueryFunction' ), ), $prop ); if ( trim( $value ) !== '' ) { @@ -174,8 +179,6 @@ public function __construct ( $selector_string = null, $declarations_string ) { $this->addDeclaration( $prop, $value ); } } - // csscrush::log( $this->localData, 'LocalData' ); - // csscrush::log( $this->data, 'Data' ); // localData no longer required $this->localData = null; @@ -226,6 +229,7 @@ public function cssQueryFunction ( $input, $fn_name, $call_property ) { } $abstracts =& csscrush::$process->abstracts; + $mixins =& csscrush::$process->mixins; $selectorRelationships =& csscrush::$process->selectorRelationships; // Resolve arguments @@ -246,11 +250,15 @@ public function cssQueryFunction ( $input, $fn_name, $call_property ) { // Try to match a abstract rule first if ( preg_match( csscrush_regex::$patt->name, $name ) ) { - // Search order: abstracts, rules + // Search order: abstracts, mixins, rules if ( isset( $abstracts[ $name ]->data[ $property ] ) ) { $result = $abstracts[ $name ]->data[ $property ]; } + elseif ( isset( $mixins[ $name ]->data[ $property ] ) ) { + + $result = $mixins[ $name ]->data[ $property ]; + } elseif ( isset( $selectorRelationships[ $name ]->data[ $property ] ) ) { $result = $selectorRelationships[ $name ]->data[ $property ]; From 9cd0981d2e6c911795f46a15316fccc247b646cc Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 22 Aug 2012 14:42:48 +0100 Subject: [PATCH 033/421] Resolved issues #34 and #35 --- CHANGELOG.txt | 6 ++++++ CssCrush.php | 2 +- composer.json | 2 +- lib/Core.php | 37 ++++++++++++++++++++++++++++--------- lib/Function.php | 2 +- lib/Importer.php | 6 ++++++ lib/Regex.php | 1 + 7 files changed, 44 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 8445336..0e43ea5 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,9 @@ +1.6.1 +--- +Resolved issue #35 +Resolved issue #34 + + 1.6 --- Inheritance model improved to support adoption of pseudo classes and elements (see wiki) diff --git a/CssCrush.php b/CssCrush.php index fdc7f69..e08e2cb 100644 --- a/CssCrush.php +++ b/CssCrush.php @@ -4,7 +4,7 @@ * CSS Crush * Extensible CSS preprocessor * - * @version 1.6 + * @version 1.6.1 * @link https://github.com/peteboere/css-crush * @license http://www.opensource.org/licenses/mit-license.php (MIT) * @copyright (c) 2010-2012 Pete Boere diff --git a/composer.json b/composer.json index 7ddf044..d8ca281 100644 --- a/composer.json +++ b/composer.json @@ -3,7 +3,7 @@ "type": "library", "description": "CSS preprocessor", "keywords": ["css", "preprocessor"], - "homepage": "http://github.com/peteboere/css-crush", + "homepage": "http://the-echoplex.net/csscrush", "license": "MIT", "authors": [ { diff --git a/lib/Core.php b/lib/Core.php index 6700a37..04c83e5 100644 --- a/lib/Core.php +++ b/lib/Core.php @@ -118,7 +118,9 @@ protected static function loadAssets () { // Load aliases file if it exists if ( $aliases_file ) { + if ( $result = @parse_ini_file( $aliases_file, true ) ) { + self::$config->aliasesRaw = $result; // Value aliases require a little preprocessing @@ -130,6 +132,14 @@ protected static function loadAssets () { } self::$config->aliasesRaw[ 'values' ] = $store; } + + // Ensure all alias groups are at least set (issue #34) + self::$config->aliasesRaw += array( + 'properties' => array(), + 'functions' => array(), + 'values' => array(), + 'at-rules' => array(), + ); } else { trigger_error( __METHOD__ . ": Aliases file could not be parsed.\n", E_USER_NOTICE ); @@ -568,14 +578,14 @@ protected static function pruneAliases () { // If a vendor target is given, we prune the aliases array $vendor = self::$options[ 'vendor_target' ]; - // For expicit 'none' argument turn off aliases - if ( 'none' === $vendor ) { - self::$config->aliases = null; + // Default vendor argument, use all aliases as normal + if ( 'all' === $vendor ) { return; } - // Default vendor argument, use all aliases as normal - if ( 'all' === $vendor ) { + // For expicit 'none' argument turn off aliases + if ( 'none' === $vendor ) { + self::$config->aliases = null; return; } @@ -682,6 +692,7 @@ protected static function reset ( $options = null ) { self::$process->abstracts = array(); self::$process->errors = array(); self::$process->selectorRelationships = array(); + self::$process->charset = null; self::$storage = (object) array(); self::$storage->tokens = (object) array( @@ -702,6 +713,7 @@ protected static function reset ( $options = null ) { protected static function compile ( $stream ) { $options = self::$options; + $process = self::$process; // Load in aliases and macros if ( ! self::$assetsLoaded ) { @@ -709,8 +721,10 @@ protected static function compile ( $stream ) { self::$assetsLoaded = true; } - // Set aliases. May be pruned if a vendor target is set + // Set aliases self::$config->aliases = self::$config->aliasesRaw; + + // Prune if a vendor target is set self::pruneAliases(); // Parse variables @@ -759,11 +773,16 @@ protected static function compile ( $stream ) { $stream = self::getBoilerplate() . "\n$stream"; } + // Add @charset at top if set + if ( $process->charset ) { + $stream = "@charset \"{$process->charset}\";\n" . $stream; + } + // Release memory self::$storage = null; - self::$process->mixins = null; - self::$process->abstracts = null; - self::$process->selectorRelationships = null; + $process->mixins = null; + $process->abstracts = null; + $process->selectorRelationships = null; return $stream; } diff --git a/lib/Function.php b/lib/Function.php index 395913a..357f0b5 100644 --- a/lib/Function.php +++ b/lib/Function.php @@ -338,7 +338,7 @@ public static function css_fn__data_uri ( $input ) { // Only allow certain extensions $allowed_file_extensions = array( - 'woff' => 'font/woff;charset=utf-8', + 'woff' => 'application/x-font-woff;charset=utf-8', 'ttf' => 'font/truetype;charset=utf-8', 'svg' => 'image/svg+xml', 'svgz' => 'image/svg+xml', diff --git a/lib/Importer.php b/lib/Importer.php index 76a83ec..21c0870 100644 --- a/lib/Importer.php +++ b/lib/Importer.php @@ -52,6 +52,12 @@ public static function hostfile () { $stream = file_get_contents( $prependFile ) . $stream; } + // If @charset is set store it + if ( preg_match( $regex->charset, $stream, $m ) ) { + $stream = preg_replace( $regex->charset, '', $stream ); + $process->charset = $m[2]; + } + csscrush::prepareStream( $stream ); // If rewriting URLs as absolute we need to do some extra work diff --git a/lib/Regex.php b/lib/Regex.php index e91dd45..5ad3067 100644 --- a/lib/Regex.php +++ b/lib/Regex.php @@ -77,6 +77,7 @@ public static function init () { $patt->absoluteUrl = '!^https?://!'; $patt->argListSplit = '!\s*[,\s]\s*!S'; $patt->mathBlacklist = '![^\.0-9\*\/\+\-\(\)]!S'; + $patt->charset = '!@charset\s+([\'"])([\w-]+)\1\s*;!i'; } From 8bbc2e43799e8795aa3b5eeaf2ef367ee380903a Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 22 Aug 2012 16:33:06 +0100 Subject: [PATCH 034/421] Default options now editable ($config->options) --- lib/Core.php | 74 +++++++++++++++++++++++++++++----------------------- 1 file changed, 41 insertions(+), 33 deletions(-) diff --git a/lib/Core.php b/lib/Core.php index 04c83e5..d2d4e6c 100644 --- a/lib/Core.php +++ b/lib/Core.php @@ -45,6 +45,34 @@ public static function init ( $seed_file ) { self::$config->aliases = array(); self::$config->aliasesRaw = array(); + // Default options + self::$config->options = array( + + // Minify. Set true for formatting and comments + 'debug' => false, + + // Append 'checksum' to output file name + 'versioning' => true, + + // Use the template boilerplate + 'boilerplate' => true, + + // Variables passed in at runtime + 'vars' => array(), + + // Enable/disable the cache + 'cache' => true, + + // Output file. Defaults the host-filename + 'output_file' => null, + + // Vendor target. Only apply prefixes for a specific vendor, set to 'none' for no prefixes + 'vendor_target' => 'all', + + // Whether to rewrite the url references inside imported files + 'rewrite_import_urls' => true, + ); + // Initialise other classes csscrush_regex::init(); csscrush_function::init(); @@ -537,40 +565,17 @@ protected static function getBoilerplate () { protected static function getOptions ( $options ) { - // Create default options for those not set - $option_defaults = array( - - // Minify. Set true for formatting and comments - 'debug' => false, - - // Append 'checksum' to output file name - 'versioning' => true, - - // Use the template boilerplate - 'boilerplate' => true, - - // Variables passed in at runtime - 'vars' => array(), - - // Enable/disable the cache - 'cache' => true, - - // Output file. Defaults the host-filename - 'output_file' => null, - - // Vendor target. Only apply prefixes for a specific vendor, set to 'none' for no prefixes - 'vendor_target' => 'all', - - // Whether to rewrite the url references inside imported files - 'rewrite_import_urls' => true, + if ( ! is_array( $options ) ) { + $options = array(); + } - // Keeping track of global vars internally - '_globalVars' => self::$config->vars, - ); + // Keeping track of global vars internally to maintain cache integrity + $options[ '_globalVars' ] = self::$config->vars; - return is_array( $options ) ? - array_merge( $option_defaults, $options ) : $option_defaults; + // Populate unset options with defaults + $options += self::$config->options; + return $options; } protected static function pruneAliases () { @@ -747,7 +752,7 @@ protected static function compile ( $stream ) { '@' => "\n@", '}' => "}\n", '{' => "{\n", - ';' => ";\n", + // ';' => ";\n", ); $stream = "\n" . str_replace( array_keys( $map ), array_values( $map ), $stream ); @@ -1093,7 +1098,10 @@ protected static function cb_extractCommentAndString ( $match ) { // Strip private comments $private_comment_marker = '$!'; - if ( strpos( $capture, '/*' . $private_comment_marker ) === 0 ) { + if ( + strpos( $capture, '/*' . $private_comment_marker ) === 0 || + ! self::$options[ 'debug' ] + ) { return ''; } From 125bffa7d21d3fa55c49c51c7706f0a065161cf3 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 22 Aug 2012 19:01:52 +0100 Subject: [PATCH 035/421] Refactoring csscrush_version::compare() now returns the comparison to itself Made self::$options property part of self::$process --- lib/Core.php | 55 +++++++++++++++++++++++++----------------------- lib/IO.php | 18 +++++++++++----- lib/Importer.php | 11 +++++----- lib/Util.php | 6 +++--- 4 files changed, 50 insertions(+), 40 deletions(-) diff --git a/lib/Core.php b/lib/Core.php index d2d4e6c..a35605b 100644 --- a/lib/Core.php +++ b/lib/Core.php @@ -12,7 +12,6 @@ class csscrush { // Properties available to each process public static $process; - public static $options; public static $storage; // Internal @@ -46,7 +45,7 @@ public static function init ( $seed_file ) { self::$config->aliasesRaw = array(); // Default options - self::$config->options = array( + self::$config->options = (object) array( // Minify. Set true for formatting and comments 'debug' => false, @@ -249,7 +248,7 @@ public static function file ( $file, $options = null ) { $config = self::$config; $process = self::$process; - $options = self::$options; + $options = $process->options; $doc_root = $config->docRoot; // Since we're comparing strings, we need to iron out OS differences @@ -287,7 +286,7 @@ public static function file ( $file, $options = null ) { // Used in validateCache, and writing to filesystem $process->outputFileName = csscrush::io_call( 'getOutputFileName' ); - if ( $options[ 'cache' ] === true ) { + if ( $options->cache === true ) { // If cache is enabled check for a valid compiled file $valid_compliled_file = csscrush::io_call( 'validateExistingOutput' ); @@ -305,7 +304,7 @@ public static function file ( $file, $options = null ) { // Create file and return url. Return empty string on failure if ( file_put_contents( "$process->outputDir/$process->outputFileName", $stream ) ) { - $timestamp = $options[ 'versioning' ] ? '?' . time() : ''; + $timestamp = $options->versioning ? '?' . time() : ''; return "$process->outputDirUrl/$process->outputFileName$timestamp"; } else { @@ -409,11 +408,11 @@ public static function string ( $string, $options = null ) { $config = self::$config; $process = self::$process; - $options = self::$options; + $options = $process->options; // Set the path context if one is given - if ( isset( $options[ 'context' ] ) && ! empty( $options[ 'context' ] ) ) { - self::setPath( $options[ 'context' ] ); + if ( isset( $options->context ) && ! empty( $options->context ) ) { + self::setPath( $options->context ); } // It's not associated with a real file so we create an 'empty' input object @@ -423,7 +422,7 @@ public static function string ( $string, $options = null ) { $process->input->string = $string; // Import files may be ignored - if ( isset( $options[ 'no_import' ] ) ) { + if ( isset( $options->no_import ) ) { $process->input->importIgnore = true; } @@ -525,7 +524,7 @@ protected static function getBoilerplate () { $file = csscrush_util::find( 'CssCrush-local.boilerplate', 'CssCrush.boilerplate' ); - if ( ! $file || ! self::$options[ 'boilerplate' ] ) { + if ( ! $file || ! self::$process->options->boilerplate ) { return ''; } @@ -573,15 +572,17 @@ protected static function getOptions ( $options ) { $options[ '_globalVars' ] = self::$config->vars; // Populate unset options with defaults - $options += self::$config->options; + $options += (array) self::$config->options; - return $options; + return (object) $options; } protected static function pruneAliases () { + $options = self::$process->options; + // If a vendor target is given, we prune the aliases array - $vendor = self::$options[ 'vendor_target' ]; + $vendor = $options->vendor_target; // Default vendor argument, use all aliases as normal if ( 'all' === $vendor ) { @@ -595,11 +596,11 @@ protected static function pruneAliases () { } // Normalize vendor_target argument - $vendor = str_replace( '-', '', self::$options[ 'vendor_target' ] ); - $vendor = "-$vendor-"; + $vendor = '-' . str_replace( '-', '', $vendor ) . '-'; // Loop the aliases array, filter down to the target vendor foreach ( self::$config->aliases as $group_name => $group_array ) { + // Property/value aliases are a special case if ( 'values' === $group_name ) { foreach ( $group_array as $property => $values ) { @@ -616,6 +617,7 @@ protected static function pruneAliases () { continue; } foreach ( $group_array as $alias_keyword => $prefix_array ) { + $result = array(); foreach ( $prefix_array as $prefix ) { if ( strpos( $prefix, $vendor ) === 0 ) { @@ -635,13 +637,15 @@ protected static function pruneAliases () { protected static function calculateVariables () { + $options = self::$process->options; + // In-file variables override global variables // Runtime variables override in-file variables self::$storage->variables = array_merge( self::$config->vars, self::$storage->variables ); - if ( !empty( self::$options[ 'vars' ] ) ) { + if ( ! empty( $options->vars ) ) { self::$storage->variables = array_merge( - self::$storage->variables, self::$options[ 'vars' ] ); + self::$storage->variables, $options->vars ); } // Place variables referenced inside variables @@ -698,6 +702,7 @@ protected static function reset ( $options = null ) { self::$process->errors = array(); self::$process->selectorRelationships = array(); self::$process->charset = null; + self::$process->options = self::getOptions( $options ); self::$storage = (object) array(); self::$storage->tokens = (object) array( @@ -710,15 +715,12 @@ protected static function reset ( $options = null ) { ); self::$storage->variables = array(); self::$storage->misc = (object) array(); - - // Load the merged options - self::$options = self::getOptions( $options ); } protected static function compile ( $stream ) { - $options = self::$options; $process = self::$process; + $options = $process->options; // Load in aliases and macros if ( ! self::$assetsLoaded ) { @@ -774,7 +776,7 @@ protected static function compile ( $stream ) { self::display( $stream ); // Add in boilerplate - if ( $options[ 'boilerplate' ] ) { + if ( $options->boilerplate ) { $stream = self::getBoilerplate() . "\n$stream"; } @@ -794,7 +796,8 @@ protected static function compile ( $stream ) { protected static function display ( &$stream ) { - $minify = ! self::$options[ 'debug' ]; + $options = self::$process->options; + $minify = ! $options->debug; $regex = csscrush_regex::$patt; if ( $minify ) { @@ -842,7 +845,7 @@ protected static function display ( &$stream ) { // Optionally set the URLs to absolute if ( - self::$options[ 'rewrite_import_urls' ] === 'absolute' && + $options->rewrite_import_urls === 'absolute' && strpos( $url, 'data:' ) !== 0 ) { $url = self::$process->inputDirUrl . '/' . $url; @@ -1100,7 +1103,7 @@ protected static function cb_extractCommentAndString ( $match ) { if ( strpos( $capture, '/*' . $private_comment_marker ) === 0 || - ! self::$options[ 'debug' ] + ! self::$process->options->debug ) { return ''; } @@ -1214,7 +1217,7 @@ protected static function cb_extractRules ( $match ) { protected static function cb_printRule ( $match ) { - $minify = ! self::$options[ 'debug' ]; + $minify = ! self::$process->options->debug; $whitespace = $minify ? '' : ' '; $ruleLabel = $match[0]; diff --git a/lib/IO.php b/lib/IO.php index 46257ff..13229b3 100644 --- a/lib/IO.php +++ b/lib/IO.php @@ -92,13 +92,13 @@ public static function testOutputDir ( $write_test = true ) { public static function getOutputFileName () { $process = csscrush::$process; - $options = csscrush::$options; + $options = $process->options; $input = $process->input; $output_basename = basename( $input->name, '.css' ); - if ( ! empty( $options[ 'output_file' ] ) ) { - $output_basename = basename( $options[ 'output_file' ], '.css' ); + if ( ! empty( $options->output_file ) ) { + $output_basename = basename( $options->output_file, '.css' ); } return "$output_basename.crush.css"; @@ -108,6 +108,7 @@ public static function getOutputFileName () { public static function validateExistingOutput () { $process = csscrush::$process; + $options = $process->options; $config = csscrush::$config; $input = $process->input; @@ -153,7 +154,14 @@ public static function validateExistingOutput () { $existing_options = $process->cacheData[ $existingfile->name ][ 'options' ]; $existing_datesum = $process->cacheData[ $existingfile->name ][ 'datem_sum' ]; - $options_unchanged = ! array_diff( $existing_options, csscrush::$options ); + $options_unchanged = true; + foreach ( $existing_options as $key => &$value ) { + if ( $existing_options->{ $key } !== $options->{ $key } ) { + // csscrush::log( "$key is different" ); + $options_unchanged = false; + break; + } + } $files_unchanged = $existing_datesum == array_sum( $all_files ); if ( $options_unchanged && $files_unchanged ) { @@ -161,7 +169,7 @@ public static function validateExistingOutput () { // Files have not been modified and config is the same: return the old file csscrush::log( "Files and options have not been modified, returning existing file '$existingfile->URL'" ); - return $existingfile->URL . ( csscrush::$options[ 'versioning' ] !== false ? "?$existing_datesum" : '' ); + return $existingfile->URL . ( $options->versioning !== false ? "?$existing_datesum" : '' ); } else { diff --git a/lib/Importer.php b/lib/Importer.php index 21c0870..a2ca85c 100644 --- a/lib/Importer.php +++ b/lib/Importer.php @@ -11,10 +11,10 @@ class csscrush_importer { public static function save ( $data ) { $process = csscrush::$process; - $options = csscrush::$options; + $options = $process->options; // No saving if caching is disabled, return early - if ( ! $options[ 'cache' ] ) { + if ( ! $options->cache ) { return; } @@ -30,7 +30,7 @@ public static function hostfile () { $config = csscrush::$config; $process = csscrush::$process; - $options = csscrush::$options; + $options = $process->options; $regex = csscrush_regex::$patt; $hostfile = $process->input; @@ -61,7 +61,7 @@ public static function hostfile () { csscrush::prepareStream( $stream ); // If rewriting URLs as absolute we need to do some extra work - if ( $options[ 'rewrite_import_urls' ] === 'absolute' ) { + if ( $options->rewrite_import_urls === 'absolute' ) { // Normalize the @import statements in this case foreach ( csscrush_regex::matchAll( $regex->import, $stream ) as $match ) { @@ -217,7 +217,7 @@ public static function hostfile () { } // Optionally rewrite relative url and custom function data-uri references - if ( $options[ 'rewrite_import_urls' ] ) { + if ( $options->rewrite_import_urls ) { $import->content = self::rewriteImportUrls( $import ); } @@ -265,7 +265,6 @@ protected static function normalizeImportStatement ( $import_statement ) { $import_statement = str_replace( $full_match, $the_space . $string_label, $import_statement ); } } - // csscrush::log( 'Normalised: ' . $import_statement ); return $import_statement; } diff --git a/lib/Util.php b/lib/Util.php index 09ce4c0..2101246 100644 --- a/lib/Util.php +++ b/lib/Util.php @@ -395,14 +395,14 @@ public function compare ( $version_string ) { $MORE = 1; $EQUAL = 0; - $test = new csscrush_version( $version_string ); + $test = new csscrush_version( $version_string ); foreach ( array( 'major', 'minor', 'revision' ) as $level ) { - if ( $test->{ $level } < $this->{ $level } ) { + if ( $this->{ $level } < $test->{ $level } ) { return $LESS; } - elseif ( $test->{ $level } > $this->{ $level } ) { + elseif ( $this->{ $level } > $test->{ $level } ) { return $MORE; } } From b3ab68ddec41b1dd9bf81365c3beb59b0ce7a8f3 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 27 Aug 2012 13:20:01 +0100 Subject: [PATCH 036/421] Reverting a change in 1.6.1 that caused some edge-case parsing issues Some minor formatting changes for consistency --- CHANGELOG.txt | 7 ++++++- CssCrush.php | 9 +-------- lib/Core.php | 8 +++++--- lib/Regex.php | 16 ++++++++-------- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 0e43ea5..a19cdf4 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,5 +1,10 @@ +1.6.2 +----- +Fixed parsing issue introduced in 1.6.1 + + 1.6.1 ---- +----- Resolved issue #35 Resolved issue #34 diff --git a/CssCrush.php b/CssCrush.php index e08e2cb..31eb95e 100644 --- a/CssCrush.php +++ b/CssCrush.php @@ -4,11 +4,10 @@ * CSS Crush * Extensible CSS preprocessor * - * @version 1.6.1 + * @version 1.6.2 * @link https://github.com/peteboere/css-crush * @license http://www.opensource.org/licenses/mit-license.php (MIT) * @copyright (c) 2010-2012 Pete Boere - * */ require_once 'lib/Util.php'; @@ -23,9 +22,3 @@ require_once 'lib/Hook.php'; csscrush::init( __FILE__ ); - - - - - - diff --git a/lib/Core.php b/lib/Core.php index a35605b..7801506 100644 --- a/lib/Core.php +++ b/lib/Core.php @@ -754,7 +754,7 @@ protected static function compile ( $stream ) { '@' => "\n@", '}' => "}\n", '{' => "{\n", - // ';' => ";\n", + ';' => ";\n", ); $stream = "\n" . str_replace( array_keys( $map ), array_values( $map ), $stream ); @@ -804,10 +804,12 @@ protected static function display ( &$stream ) { $stream = csscrush_util::stripComments( $stream ); } else { - // Create newlines after tokens + // Formatting $stream = preg_replace( '!([{}])!', "$1\n", $stream ); $stream = preg_replace( '!([@])!', "\n$1", $stream ); - $stream = preg_replace( '!(___[a-z0-9]+___)!', "$1\n", $stream ); + + // Newlines after some tokens + $stream = preg_replace( '!(___[rc][0-9]+___)!', "$1\n", $stream ); // Kill double spaces $stream = ltrim( preg_replace( '!\n+!', "\n", $stream ) ); diff --git a/lib/Regex.php b/lib/Regex.php index 5ad3067..7eec415 100644 --- a/lib/Regex.php +++ b/lib/Regex.php @@ -26,27 +26,27 @@ public static function init () { $patt->notName = '!^' . $class->notName . '$!'; $patt->import = '! - @import\s+ # import at-rule + @import\s+ # import at-rule (?: - url\(\s*([^\)]+)\s*\) # url function - | # or - ([_s\d]+) # string token + url\(\s*([^\)]+)\s*\) # url function + | # or + ([_s\d]+) # string token ) - \s*([^;]*); # media argument + \s*([^;]*); # media argument !xS'; $patt->variables = '!@(?:variables|define)\s*([^\{]*)\{\s*(.*?)\s*\};?!s'; $patt->mixin = '!@mixin\s*([^\{]*)\{\s*(.*?)\s*\};?!s'; $patt->abstract = csscrush_regex::create( '^@abstract\s+()', 'i' ); $patt->commentAndString = '! - (\'|")(?:\\1|[^\1])*?\1 + (\'|")(?:\\1|[^\1])*?\1 # quoted string | - /\*(?:.*?)\*/ + /\*(?:.*?)\*/ # block comment !xsS'; // As an exception we treat some @-rules like standard rule blocks $patt->rule = '! - (\n(?:[^@{}]+|@(?:font-face|page|abstract)[^{]*)) # The selector + (\n(?:[^@{}]+|@(?:font-face|page|abstract)[^{]*)) # The selector \{([^{}]*)\} # The declaration block !xS'; From 8f31b1adf38034b158f8136e34955cba84a1ca73 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 28 Aug 2012 10:34:36 +0100 Subject: [PATCH 037/421] Minor update to the command line application --- cli.php | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/cli.php b/cli.php index 8efc260..1d2cfb4 100644 --- a/cli.php +++ b/cli.php @@ -65,22 +65,22 @@ function stdout ( $lines, $closing_newline = true ) { ## Options $short_opts = array( - "f::", // Input file. Defaults to sdtin - "o::", // Output file. Defaults to stdout - "p", // Pretty formatting - 'b', // Output boilerplate - 'h', // Display help + "f:", // Input file. Defaults to sdtin + "o:", // Output file. Defaults to stdout + "p", // Pretty formatting + 'b', // Output boilerplate + 'h', // Display help ); $long_opts = array( - 'file::', // Input file. Defaults to sdtin - 'output::', // Output file. Defaults to stdout - 'pretty', // Pretty formatting - 'boilerplate', // Output boilerplate - 'help', // Display help - 'version', // Display version - 'vendor-target:', // Vendor target - 'variables:', // Map of variable names in an http query string format + 'file:', // Input file. Defaults to sdtin + 'output:', // Output file. Defaults to stdout + 'pretty', // Pretty formatting + 'boilerplate', // Output boilerplate + 'help', // Display help + 'version', // Display version + 'vendor-target:', // Vendor target + 'variables:', // Map of variable names in an http query string format ); $opts = getopt( implode( $short_opts ), $long_opts ); @@ -134,7 +134,7 @@ function stdout ( $lines, $closing_newline = true ) { Version number Examples: - $command -f=styles.css --pretty --vendor-target=webkit + $command -f styles.css --pretty --vendor-target webkit # Piping on unix based terminals cat 'styles.css' | $command --boilerplate From 64ad5d9cce930a717c6ab70230fbf8a9bf4725b1 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 29 Aug 2012 14:51:22 +0100 Subject: [PATCH 038/421] Removed -moz-border-radius and -moz-box-shadow aliases Updated changelog --- Aliases.ini | 6 ------ CHANGELOG.txt | 2 ++ 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/Aliases.ini b/Aliases.ini index 9728edf..02a2a94 100644 --- a/Aliases.ini +++ b/Aliases.ini @@ -58,15 +58,10 @@ ; Border radius border-radius[] = -webkit-border-radius - border-radius[] = -moz-border-radius border-top-left-radius[] = -webkit-border-top-left-radius - border-top-left-radius[] = -moz-border-radius-topleft border-top-right-radius[] = -webkit-border-top-right-radius - border-top-right-radius[] = -moz-border-radius-topright border-bottom-left-radius[] = -webkit-border-bottom-left-radius - border-bottom-left-radius[] = -moz-border-radius-bottomleft border-bottom-right-radius[] = -webkit-border-bottom-right-radius - border-bottom-right-radius[] = -moz-border-radius-bottomright ; Border-image border-image[] = -webkit-border-image @@ -92,7 +87,6 @@ ; Box shadow box-shadow[] = -webkit-box-shadow - box-shadow[] = -moz-box-shadow ; Box sizing box-sizing[] = -webkit-box-sizing diff --git a/CHANGELOG.txt b/CHANGELOG.txt index a19cdf4..b78bc29 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,6 +1,8 @@ 1.6.2 ----- Fixed parsing issue introduced in 1.6.1 +Minor update to command line appication +Updated aliases file 1.6.1 From c32468501004bd5fa2dcfe5b8be7b1f114172d73 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 1 Sep 2012 13:17:46 +0100 Subject: [PATCH 039/421] Plugin API updates Added ability to diable and enable plugins as an option Added property sorter plugin Added overflow-* initital values --- CHANGELOG.txt | 2 +- CssCrush.php | 5 +- Plugins.ini | 20 ++--- cli.php | 2 + lib/Core.php | 54 ++++++++--- lib/Hook.php | 36 ++++++-- lib/Plugin.php | 64 ++++++++++++++ lib/Rule.php | 12 +-- misc/initial-values.ini | 2 + misc/property-sorting.ini | 172 ++++++++++++++++++++++++++++++++++++ plugins/hocus-pocus.php | 4 +- plugins/hsl-to-hex.php | 4 +- plugins/ie-clip.php | 4 +- plugins/ie-filter.php | 21 ++--- plugins/ie-inline-block.php | 7 +- plugins/ie-min-height.php | 7 +- plugins/ie-opacity.php | 7 +- plugins/initial.php | 4 +- plugins/property-sorter.php | 136 ++++++++++++++++++++++++++++ plugins/rgba-fallback.php | 4 +- plugins/spiffing.php | 4 +- 21 files changed, 501 insertions(+), 70 deletions(-) create mode 100644 lib/Plugin.php create mode 100644 misc/property-sorting.ini create mode 100644 plugins/property-sorter.php diff --git a/CHANGELOG.txt b/CHANGELOG.txt index b78bc29..5942c2d 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,4 +1,4 @@ -1.6.2 +1.7 ----- Fixed parsing issue introduced in 1.6.1 Minor update to command line appication diff --git a/CssCrush.php b/CssCrush.php index 31eb95e..7cea0fd 100644 --- a/CssCrush.php +++ b/CssCrush.php @@ -3,8 +3,8 @@ * * CSS Crush * Extensible CSS preprocessor - * - * @version 1.6.2 + * + * @version 1.7 * @link https://github.com/peteboere/css-crush * @license http://www.opensource.org/licenses/mit-license.php (MIT) * @copyright (c) 2010-2012 Pete Boere @@ -20,5 +20,6 @@ require_once 'lib/Color.php'; require_once 'lib/Regex.php'; require_once 'lib/Hook.php'; +require_once 'lib/Plugin.php'; csscrush::init( __FILE__ ); diff --git a/Plugins.ini b/Plugins.ini index 8501440..d2279aa 100644 --- a/Plugins.ini +++ b/Plugins.ini @@ -7,32 +7,32 @@ ;---------------------------------------------------------------- ; min-height shim for IE < 7 -; plugins[] = ie-min-height.php +; plugins[] = ie-min-height ; inline-block shim for IE < 8 -plugins[] = ie-inline-block.php +plugins[] = ie-inline-block ; clip property shim for IE < 8 -; plugins[] = ie-clip.php +; plugins[] = ie-clip ; IE filter shorthand -; plugins[] = ie-filter.php +; plugins[] = ie-filter ; Opacity for IE < 9 (uses filter) -; plugins[] = ie-opacity.php +; plugins[] = ie-opacity ; Opaque fallback colors for clients that don't support RGBA -; plugins[] = rgba-fallback.php +; plugins[] = rgba-fallback ; HSL shim - converts HSL values to hex codes -; plugins[] = hsl-to-hex.php +; plugins[] = hsl-to-hex ; Non-standard composite pseudo classes -plugins[] = hocus-pocus.php +plugins[] = hocus-pocus ; CSS3 'initial' keyword shim -plugins[] = initial.php +plugins[] = initial ; Transforms correctly-spelt Queen's English into valid CSS (http://spiffingcss.com) -; plugins[] = spiffing.php +; plugins[] = spiffing diff --git a/cli.php b/cli.php index 1d2cfb4..97a4771 100644 --- a/cli.php +++ b/cli.php @@ -81,6 +81,8 @@ function stdout ( $lines, $closing_newline = true ) { 'version', // Display version 'vendor-target:', // Vendor target 'variables:', // Map of variable names in an http query string format + 'enable:', // List of plugins to enable + 'disable:', // List of plugins to disable ); $opts = getopt( implode( $short_opts ), $long_opts ); diff --git a/lib/Core.php b/lib/Core.php index 7801506..cf0ef66 100644 --- a/lib/Core.php +++ b/lib/Core.php @@ -38,11 +38,11 @@ public static function init ( $seed_file ) { // Set the default IO handler self::$config->io = 'csscrush_io'; - // Global assets + // Global storage self::$config->vars = array(); - self::$config->plugins = array(); self::$config->aliases = array(); self::$config->aliasesRaw = array(); + self::$config->plugins = array(); // Default options self::$config->options = (object) array( @@ -70,6 +70,12 @@ public static function init ( $seed_file ) { // Whether to rewrite the url references inside imported files 'rewrite_import_urls' => true, + + // List of plugins to enable (as Array of names) + 'enable' => null, + + // List of plugins to disable (as Array of names) + 'disable' => null, ); // Initialise other classes @@ -166,7 +172,7 @@ protected static function loadAssets () { 'functions' => array(), 'values' => array(), 'at-rules' => array(), - ); + ); } else { trigger_error( __METHOD__ . ": Aliases file could not be parsed.\n", E_USER_NOTICE ); @@ -176,21 +182,18 @@ protected static function loadAssets () { trigger_error( __METHOD__ . ": Aliases file not found.\n", E_USER_NOTICE ); } - // Find a plugins file in the root directory + // Find a plugins file in the root directory, // a local file overrides the default $plugins_file = csscrush_util::find( 'Plugins-local.ini', 'Plugins.ini' ); // Load plugins if ( $plugins_file ) { if ( $result = @parse_ini_file( $plugins_file ) ) { - foreach ( $result[ 'plugins' ] as $plugin_file ) { - $path = self::$config->location . "/plugins/$plugin_file"; - if ( file_exists( $path ) ) { - self::$config->plugins[] = $plugin_file; - require_once $path; - } - else { - trigger_error( __METHOD__ . ": Plugin file $plugin_file not found.\n", E_USER_NOTICE ); + foreach ( $result[ 'plugins' ] as $plugin_name ) { + // Backwards compat. + $plugin_name = basename( $plugin_name, '.php' ); + if ( csscrush_plugin::enable( $plugin_name ) ) { + self::$config->plugins[ $plugin_name ] = true; } } } @@ -448,7 +451,7 @@ public static function globalVars ( $vars ) { } // Test for a file. If it is attempt to parse it elseif ( is_string( $vars ) && file_exists( $vars ) ) { - if ( $result = parse_ini_file( $vars ) ) { + if ( $result = @parse_ini_file( $vars ) ) { $config->vars = array_merge( $config->vars, $result ); } } @@ -719,6 +722,7 @@ protected static function reset ( $options = null ) { protected static function compile ( $stream ) { + $config = self::$config; $process = self::$process; $options = $process->options; @@ -728,6 +732,30 @@ protected static function compile ( $stream ) { self::$assetsLoaded = true; } + // Load and unload plugins. + // First copy the base $config->plugins list. + $process->plugins = $config->plugins; + + // Add option enabled plugins to the list. + if ( is_array( $options->enable ) ) { + foreach ( $options->enable as $plugin_name ) { + $process->plugins[ $plugin_name ] = true; + } + } + + // Remove option disabled plugins from the list, and disable them. + if ( is_array( $options->disable ) ) { + foreach ( $options->disable as $plugin_name ) { + csscrush_plugin::disable( $plugin_name ); + unset( $process->plugins[ $plugin_name ] ); + } + } + + // Enable all plugins in the remaining list. + foreach ( $process->plugins as $plugin_name => $bool ) { + csscrush_plugin::enable( $plugin_name ); + } + // Set aliases self::$config->aliases = self::$config->aliasesRaw; diff --git a/lib/Hook.php b/lib/Hook.php index f602b74..c302e9b 100644 --- a/lib/Hook.php +++ b/lib/Hook.php @@ -1,30 +1,50 @@ location . "/plugins/$plugin_name.php"; + + if ( ! file_exists( $path ) ) { + + trigger_error( __METHOD__ . + ": $plugin_name plugin not found.\n", E_USER_NOTICE ); + return false; + } + require_once $path; + } + + // If the plugin is associated with a hook, we make sure it is hooked + if ( isset( self::$associated_hooks[ $plugin_name ] ) ) { + + csscrush_hook::add( + self::$associated_hooks[ $plugin_name ], + $plugin_function ); + } + return true; + } + + + static public function disable ( $plugin_name ) { + + // If the plugin is associated with a hook, we 'un-hook' it + if ( isset( self::$associated_hooks[ $plugin_name ] ) ) { + + csscrush_hook::remove( + self::$associated_hooks[ $plugin_name ], + self::$prefix . str_replace( '-', '_', $plugin_name ) ); + } + } +} diff --git a/lib/Rule.php b/lib/Rule.php index ef47c53..5d3cc29 100644 --- a/lib/Rule.php +++ b/lib/Rule.php @@ -162,7 +162,7 @@ public function __construct ( $selector_string = null, $declarations_string ) { } // Bind declaration objects on the rule - foreach ( $pairs as $pair ) { + foreach ( $pairs as $index => &$pair ) { list( $prop, $value ) = $pair; @@ -176,7 +176,7 @@ public function __construct ( $selector_string = null, $declarations_string ) { // Add declaration and update the data table $this->data[ $prop ] = $value; - $this->addDeclaration( $prop, $value ); + $this->addDeclaration( $prop, $value, $index ); } } @@ -663,10 +663,10 @@ public function addProperty ( $prop ) { } } - public function addDeclaration ( $prop, $value ) { + public function addDeclaration ( $prop, $value, $contextIndex = 0 ) { // Create declaration, add to the stack if it's valid - $declaration = new csscrush_declaration( $prop, $value ); + $declaration = new csscrush_declaration( $prop, $value, $contextIndex ); if ( $declaration->isValid ) { @@ -701,12 +701,13 @@ class csscrush_declaration { public $vendor; public $functions; public $value; + public $index; public $skip; public $important; public $parenTokens; public $isValid = true; - public function __construct ( $prop, $value ) { + public function __construct ( $prop, $value, $contextIndex = 0 ) { $regex = csscrush_regex::$patt; @@ -774,6 +775,7 @@ public function __construct ( $prop, $value ) { $this->family = $family; $this->vendor = $vendor; $this->functions = $functions; + $this->index = $contextIndex; $this->value = $value; $this->skip = $skip; $this->important = $important; diff --git a/misc/initial-values.ini b/misc/initial-values.ini index e0b5781..ff26d35 100644 --- a/misc/initial-values.ini +++ b/misc/initial-values.ini @@ -105,6 +105,8 @@ outline-color = "invert" outline-style = "none" outline-width = "medium" overflow = "visible" +overflow-x = "visible" +overflow-y = "visible" padding = "0" padding-bottom = "0" padding-left = "0" diff --git a/misc/property-sorting.ini b/misc/property-sorting.ini new file mode 100644 index 0000000..7535065 --- /dev/null +++ b/misc/property-sorting.ini @@ -0,0 +1,172 @@ +; Table for property sorting. +; Vendor prefixes are added ar runtime. + +; Positioning +position +z-index +top +right +bottom +left + +; Display +visibility +opacity +display +overflow +overflow-x +overflow-y +vertical-align + +; Generated content +content +counter-increment +counter-reset +quotes + +; Floats +float +clear + +; Transforms +transform +transform-style +perspective +perspective-origin +backface-visibility + +; Box-model: dimensions +box-sizing +width +height +min-width +max-width +min-height +max-height + +; Box-model: padding +padding +padding-top +padding-right +padding-bottom +padding-left + +; Box-model: margins +margin +margin-top +margin-right +margin-bottom +margin-left + +; Box-model: borders +border +border-width +border-style +border-radius +border-image +border-color +border-top +border-top-width +border-top-style +border-top-left-radius +border-top-right-radius +border-top-color +border-right +border-right-width +border-right-style +border-right-color +border-bottom +border-bottom-width +border-bottom-style +border-bottom-left-radius +border-bottom-right-radius +border-bottom-color +border-left +border-left-width +border-left-style +border-left-color + +; Box-model: effects +box-shadow + +; Foreground color +color + +; Background +background +background-image +background-color +background-repeat +background-position +background-origin +background-size +background-clip +background-attachment + +; Text +direction +text-align +text-align-last +text-indent +text-overflow +text-transform +text-decoration +text-decoration-color +text-decoration-line +text-decoration-style +text-shadow + +; Fonts: general +font +font-family +font-size +line-height +font-weight +font-style +font-variant + +; Fonts: spacing and behaviour +letter-spacing +word-spacing +word-wrap +word-break +white-space +hyphens +orphans + +; Outlines +outline +outline-width +outline-style +outline-color +outline-offset + +; Animations +animation +animation-delay +animation-direction +animation-duration +animation-fill-mode +animation-iteration-count +animation-name +animation-play-state +animation-timing-function + +; Transitions +transition +transition-delay +transition-duration +transition-property +transition-timing-function + +; Tables specific +table-layout +border-collapse +caption-side +empty-cells + +; Lists specific +list-style +list-style-image +list-style-type +list-style-position diff --git a/plugins/hocus-pocus.php b/plugins/hocus-pocus.php index 8eb9603..e0fa054 100644 --- a/plugins/hocus-pocus.php +++ b/plugins/hocus-pocus.php @@ -13,9 +13,9 @@ * */ -csscrush_hook::add( 'rule_preprocess', 'csscrush_hocuspocus' ); +csscrush_hook::add( 'rule_preprocess', 'csscrush__hocus_pocus' ); -function csscrush_hocuspocus ( $rule ) { +function csscrush__hocus_pocus ( $rule ) { $adjustments = array( '!:hocus([^a-z0-9_-]|$)!' => ':any(:hover,:focus)$1', diff --git a/plugins/hsl-to-hex.php b/plugins/hsl-to-hex.php index dc4d761..a84e524 100644 --- a/plugins/hsl-to-hex.php +++ b/plugins/hsl-to-hex.php @@ -10,9 +10,9 @@ * color: #6abf40 */ -csscrush_hook::add( 'rule_postalias', 'csscrush_hsl' ); +csscrush_hook::add( 'rule_postalias', 'csscrush__hsl_to_hex' ); -function csscrush_hsl ( csscrush_rule $rule ) { +function csscrush__hsl_to_hex ( csscrush_rule $rule ) { foreach ( $rule as &$declaration ) { if ( diff --git a/plugins/ie-clip.php b/plugins/ie-clip.php index 1fca038..a9c5195 100644 --- a/plugins/ie-clip.php +++ b/plugins/ie-clip.php @@ -10,9 +10,9 @@ * *clip: rect(1px 1px 1px 1px); */ -csscrush_hook::add( 'rule_postalias', 'csscrush_clip' ); +csscrush_hook::add( 'rule_postalias', 'csscrush__ie_clip' ); -function csscrush_clip ( csscrush_rule $rule ) { +function csscrush__ie_clip ( csscrush_rule $rule ) { // Assume it's been dealt with if the property occurs more than once if ( $rule->propertyCount( 'clip' ) !== 1 ) { diff --git a/plugins/ie-filter.php b/plugins/ie-filter.php index 33c8927..66a3beb 100644 --- a/plugins/ie-filter.php +++ b/plugins/ie-filter.php @@ -1,24 +1,25 @@ 7 * Outputs '*' escaped filter property for IE < 8 * Adds hasLayout via zoom property (required by filter effects) - * - * + * + * * @before * -ms-filter: alpha(opacity=50), blur(strength=10); - * + * * @after * -ms-filter: "alpha(opacity=50), progid:DXImageTransform.Microsoft.Blur(strength=10)"; * *filter: alpha(opacity=50), progid:DXImageTransform.Microsoft.Blur(strength=10); * zoom: 1; */ -csscrush_hook::add( 'rule_postalias', 'csscrush_filter' ); +csscrush_hook::add( 'rule_postalias', 'csscrush__ie_filter' ); + +function csscrush__ie_filter ( csscrush_rule $rule ) { -function csscrush_filter ( csscrush_rule $rule ) { if ( $rule->propertyCount( '-ms-filter' ) < 1 ) { return; } @@ -27,15 +28,15 @@ function csscrush_filter ( csscrush_rule $rule ) { foreach ( $rule as $declaration ) { if ( $declaration->skip || - $declaration->property !== '-ms-filter' + $declaration->property !== '-ms-filter' ) { $new_set[] = $declaration; continue; } $list = array_map( 'trim', explode( ',', $declaration->value ) ); foreach ( $list as &$item ) { - if ( - strpos( $item, $filter_prefix ) !== 0 && + if ( + strpos( $item, $filter_prefix ) !== 0 && strpos( $item, 'alpha' ) !== 0 // Shortcut syntax permissable on alpha ) { $item = $filter_prefix . ucfirst( $item ); @@ -52,4 +53,4 @@ function csscrush_filter ( csscrush_rule $rule ) { $new_set[] = new csscrush_declaration( '*filter', $declaration->value ); } $rule->declarations = $new_set; -} \ No newline at end of file +} diff --git a/plugins/ie-inline-block.php b/plugins/ie-inline-block.php index 6cebedb..95472ab 100644 --- a/plugins/ie-inline-block.php +++ b/plugins/ie-inline-block.php @@ -11,9 +11,10 @@ * *zoom: 1; */ -csscrush_hook::add( 'rule_postalias', 'csscrush_display_inlineblock' ); +csscrush_hook::add( 'rule_postalias', 'csscrush__ie_inline_block' ); + +function csscrush__ie_inline_block ( csscrush_rule $rule ) { -function csscrush_display_inlineblock ( csscrush_rule $rule ) { if ( $rule->propertyCount( 'display' ) < 1 ) { return; } @@ -31,4 +32,4 @@ function csscrush_display_inlineblock ( csscrush_rule $rule ) { $new_set[] = new csscrush_declaration( '*zoom', 1 ); } $rule->declarations = $new_set; -} \ No newline at end of file +} diff --git a/plugins/ie-min-height.php b/plugins/ie-min-height.php index a38e7fd..dd496fe 100644 --- a/plugins/ie-min-height.php +++ b/plugins/ie-min-height.php @@ -10,9 +10,10 @@ * _height: 100px; */ -csscrush_hook::add( 'rule_postalias', 'csscrush_minheight' ); +csscrush_hook::add( 'rule_postalias', 'csscrush__ie_min_height' ); + +function csscrush__ie_min_height ( csscrush_rule $rule ) { -function csscrush_minheight ( csscrush_rule $rule ) { if ( $rule->propertyCount( 'min-height' ) < 1 ) { return; } @@ -27,4 +28,4 @@ function csscrush_minheight ( csscrush_rule $rule ) { $new_set[] = new csscrush_declaration( '_height', $declaration->value ); } $rule->declarations = $new_set; -} \ No newline at end of file +} diff --git a/plugins/ie-opacity.php b/plugins/ie-opacity.php index bd1d309..ee56c98 100755 --- a/plugins/ie-opacity.php +++ b/plugins/ie-opacity.php @@ -12,9 +12,10 @@ * zoom: 1; */ -csscrush_hook::add( 'rule_postalias', 'csscrush_opacity' ); +csscrush_hook::add( 'rule_postalias', 'csscrush__ie_opacity' ); + +function csscrush__ie_opacity ( csscrush_rule $rule ) { -function csscrush_opacity ( csscrush_rule $rule ) { if ( $rule->propertyCount( 'opacity' ) < 1 ) { return; } @@ -40,4 +41,4 @@ function csscrush_opacity ( csscrush_rule $rule ) { $new_set[] = new csscrush_declaration( '*filter', $value ); } $rule->declarations = $new_set; -} \ No newline at end of file +} diff --git a/plugins/initial.php b/plugins/initial.php index 4e09c66..03a99c7 100644 --- a/plugins/initial.php +++ b/plugins/initial.php @@ -15,9 +15,9 @@ * */ -csscrush_hook::add( 'rule_prealias', 'csscrush_initial' ); +csscrush_hook::add( 'rule_prealias', 'csscrush__initial' ); -function csscrush_initial ( csscrush_rule $rule ) { +function csscrush__initial ( csscrush_rule $rule ) { static $initialValues = null; if ( ! $initialValues ) { diff --git a/plugins/property-sorter.php b/plugins/property-sorter.php new file mode 100644 index 0000000..6d835ca --- /dev/null +++ b/plugins/property-sorter.php @@ -0,0 +1,136 @@ +declarations = $new_set; +} + + +/* + Callback for sorting +*/ +function _csscrush__property_sorter_callback ( $a, $b ) { + + $map =& _csscrush__property_sorter_get_table(); + $a_prop =& $a->property; + $b_prop =& $b->property; + $a_listed = isset( $map[ $a_prop ] ); + $b_listed = isset( $map[ $b_prop ] ); + + // If both properties are listed do a table comparison. + if ( $a_listed && $b_listed ) { + + if ( $a_prop === $b_prop ) { + return $a->index > $b->index ? 1 : -1; + } + return $map[ $a_prop ] > $map[ $b_prop ] ? 1 : -1; + } + + // Listed properties always come before un-listed. + if ( $a_listed && ! $b_listed ) { + return -1; + } + if ( $b_listed && ! $a_listed ) { + return 1; + } + + // If propertes are the same compare declaration indexes. + if ( $a_prop === $b_prop ) { + return $a->index > $b->index ? 1 : -1; + } + + // If neither property is listed do a regular sort. + return $a_prop > $b_prop ? 1 : -1; +} + + +/* + Cache for the table of values to compare against. + + If you need to re-define the sort table during runtime unset the cache first: + unset( $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER_CACHE' ] ); +*/ +function &_csscrush__property_sorter_get_table () { + + // Check for cached table. + if ( isset( $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER_CACHE' ] ) ) { + return $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER_CACHE' ]; + } + + $table = array(); + + // Nothing cached, check for a user-defined table. + if ( isset( $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER' ] ) ) { + $table = (array) $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER' ]; + } + + // No user-defined table, use pre-defined. + else { + + // Load from property-sorting.ini + if ( $sorting_file_contents + = file_get_contents( csscrush::$config->location . '/misc/property-sorting.ini' ) ) { + + $sorting_file_contents = preg_replace( '!;[^\r\n]*!', '', $sorting_file_contents ); + $table = preg_split( '!\s+!', trim( $sorting_file_contents ) ); + } + else { + trigger_error( __METHOD__ . ": Property sorting file not found.\n", E_USER_NOTICE ); + } + } + + // Add in prefixed properties based on the aliases file + $collated_table = array(); + $property_aliases =& csscrush::$config->aliases[ 'properties' ]; + $priority = 0; + + foreach ( $table as $property ) { + if ( isset( $property_aliases[ $property ] ) ) { + foreach ( $property_aliases[ $property ] as &$property_alias ) { + $collated_table[ $property_alias ] = ++$priority; + } + } + $collated_table[ $property ] = ++$priority; + } + + // Cache the collated table + $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER_CACHE' ] = $collated_table; + + return $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER_CACHE' ]; +} diff --git a/plugins/rgba-fallback.php b/plugins/rgba-fallback.php index 44fc136..487043d 100644 --- a/plugins/rgba-fallback.php +++ b/plugins/rgba-fallback.php @@ -12,10 +12,10 @@ * background: rgba(0,0,0,.5); */ -csscrush_hook::add( 'rule_postalias', 'csscrush_rgba' ); +csscrush_hook::add( 'rule_postalias', 'csscrush__rgba_fallback' ); -function csscrush_rgba ( csscrush_rule $rule ) { +function csscrush__rgba_fallback ( csscrush_rule $rule ) { $props = array_keys( $rule->properties ); diff --git a/plugins/spiffing.php b/plugins/spiffing.php index 2a0b5d6..cd86c28 100644 --- a/plugins/spiffing.php +++ b/plugins/spiffing.php @@ -13,9 +13,9 @@ * */ -csscrush_hook::add( 'rule_preprocess', 'csscrush_spiffing' ); +csscrush_hook::add( 'rule_preprocess', 'csscrush__spiffing' ); -function csscrush_spiffing ( $rule ) { +function csscrush__spiffing ( $rule ) { $find = array( 'colour', 'grey', '!please', 'transparency', 'centre', 'plump', 'photograph', 'capitalise' ); $replace = array( 'color', 'gray', '!important', 'opacity', 'center', 'bold', 'image', 'capitalize' ); From b13156e1e832b7b20ce95a882a894a756ceaf99d Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 1 Sep 2012 15:05:17 +0100 Subject: [PATCH 040/421] Added --enable and --disable command line options --- cli.php | 47 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/cli.php b/cli.php index 97a4771..dc7c52e 100644 --- a/cli.php +++ b/cli.php @@ -7,7 +7,6 @@ require_once 'CssCrush.php'; - // Exit status constants define( 'STATUS_OK', 0 ); define( 'STATUS_ERROR', 1 ); @@ -89,12 +88,14 @@ function stdout ( $lines, $closing_newline = true ) { $input_file = @( $opts['f'] ?: $opts['file'] ); $output_file = @( $opts['o'] ?: $opts['output'] ); -$variables = @$opts['variables']; -$vendor_target = @$opts['vendor-target']; -$boilerplate = @( isset( $opts['b'] ) ?: isset( $opts['boilerplate'] ) ); $pretty = @( isset( $opts['p'] ) ?: isset( $opts['pretty'] ) ); +$boilerplate = @( isset( $opts['b'] ) ?: isset( $opts['boilerplate'] ) ); $help_flag = @( isset( $opts['h'] ) ?: isset( $opts['help'] ) ); $version_flag = @isset( $opts['version'] ); +$vendor_target = @$opts['vendor-target']; +$variables = @$opts['variables']; +$enable_plugins = isset( $opts['enable'] ) ? (array) $opts['enable'] : null; +$disable_plugins = isset( $opts['disable'] ) ? (array) $opts['disable'] : null; ################################################################## @@ -124,6 +125,12 @@ function stdout ( $lines, $closing_newline = true ) { -h, --help: Display this help mesasge + --enable: + List of plugins to enable + + --disable: + List of plugins to disable + --variables: Map of variable names in an http query string format @@ -141,6 +148,9 @@ function stdout ( $lines, $closing_newline = true ) { # Piping on unix based terminals cat 'styles.css' | $command --boilerplate + # Linting + $command -f screen.css -p --enable property-sorter -o screen-linted.css + TPL; @@ -166,7 +176,6 @@ function stdout ( $lines, $closing_newline = true ) { if ( $input_file ) { if ( ! file_exists( $input_file ) ) { - stdout( 'Input file not found' . PHP_EOL ); exit( STATUS_ERROR ); } @@ -192,12 +201,31 @@ function stdout ( $lines, $closing_newline = true ) { $process_opts[ 'debug' ] = $pretty ? true : false; $process_opts[ 'rewrite_import_urls' ] = true; -if ( $vendor_target ) { +// Enable plugin args +if ( $enable_plugins ) { + foreach ( $enable_plugins as $arg ) { + foreach ( preg_split( '!\s*,\s*!', $arg ) as $plugin ) { + $process_opts[ 'enable' ][] = $plugin; + } + } +} +// Disable plugin args +if ( $disable_plugins ) { + foreach ( $disable_plugins as $arg ) { + foreach ( preg_split( '!\s*,\s*!', $arg ) as $plugin ) { + $process_opts[ 'disable' ][] = $plugin; + } + } +} + +// Vendor target args +if ( $vendor_target ) { $process_opts[ 'vendor_target' ] = $vendor_target; } -if ( $variables ) { +// Variables args +if ( $variables ) { parse_str( $variables, $in_vars ); $process_opts[ 'vars' ] = $in_vars; } @@ -217,7 +245,6 @@ function stdout ( $lines, $closing_newline = true ) { // Reset the document root after processing if ( $hostfile_context ) { - csscrush::$config->docRoot = $old_doc_root; } @@ -232,7 +259,6 @@ function stdout ( $lines, $closing_newline = true ) { $message[] = "Could not write to path '$output_file'"; if ( strpos( $output_file, '~' ) === 0 ) { - $message[] = 'Tilde expansion does not work here'; } @@ -249,6 +275,3 @@ function stdout ( $lines, $closing_newline = true ) { stdout( $output ); exit( STATUS_OK ); } - - - From 49c7de1b99c29870bbb2ff6047acae100f1e5879 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 10 Sep 2012 11:09:38 +0100 Subject: [PATCH 041/421] Added support for SASS-like @include/@extend syntax for invoking mixins and extends inside rules. csscrush::string() now uses document_root as a default context for finding linked resources. Boilerplate option now accepts a filename string. --- CHANGELOG.txt | 10 ++++++++-- Plugins.ini | 3 +++ lib/Core.php | 39 ++++++++++++++++++++++++++++++--------- lib/Rule.php | 24 ++++++++++++++++-------- misc/initial-values.ini | 2 ++ misc/property-sorting.ini | 2 ++ 6 files changed, 61 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 5942c2d..b915c08 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,8 +1,14 @@ 1.7 ----- +Updated plugin API +Added options for enabling and disabling plugins at runtime +Added support for SASS-like @include/@extend syntax for invoking mixins and extends +Added property sorter plugin +Boilerplate option now accepts a filename string as a boilerplate template +CssCrush::string() now uses document_root as a default context for finding linked resources +Updated command line appication +Updated aliases and initial value files Fixed parsing issue introduced in 1.6.1 -Minor update to command line appication -Updated aliases file 1.6.1 diff --git a/Plugins.ini b/Plugins.ini index d2279aa..ee9030f 100644 --- a/Plugins.ini +++ b/Plugins.ini @@ -6,6 +6,9 @@ ; ;---------------------------------------------------------------- +; Property sorting +; plugins[] = property-sorter + ; min-height shim for IE < 7 ; plugins[] = ie-min-height diff --git a/lib/Core.php b/lib/Core.php index cf0ef66..92c63b7 100644 --- a/lib/Core.php +++ b/lib/Core.php @@ -299,10 +299,10 @@ public static function file ( $file, $options = null ) { } } - // Collate hostfile and imports + // Collate hostfile and imports. $stream = csscrush_importer::hostfile( $process->input ); - // Compile + // Compile. $stream = self::compile( $stream ); // Create file and return url. Return empty string on failure @@ -413,12 +413,16 @@ public static function string ( $string, $options = null ) { $process = self::$process; $options = $process->options; - // Set the path context if one is given - if ( isset( $options->context ) && ! empty( $options->context ) ) { + // Set the path context if one is given. + // Fallback to document root. + if ( ! empty( $options->context ) ) { self::setPath( $options->context ); } + else { + self::setPath( $config->docRoot ); + } - // It's not associated with a real file so we create an 'empty' input object + // It's not associated with a real file so we create an 'empty' input object. $process->input = csscrush::io_call( 'getInput' ); // Set the string on the object @@ -525,9 +529,21 @@ public static function prepareStream ( &$stream ) { protected static function getBoilerplate () { - $file = csscrush_util::find( 'CssCrush-local.boilerplate', 'CssCrush.boilerplate' ); + $file = false; + $boilerplate_option = self::$process->options->boilerplate; + + if ( $boilerplate_option === true ) { + $file = csscrush_util::find( + 'CssCrush-local.boilerplate', 'CssCrush.boilerplate' ); + } + elseif ( is_string( $boilerplate_option ) ) { + if ( file_exists( $boilerplate_option ) ) { + $file = $boilerplate_option; + } + } - if ( ! $file || ! self::$process->options->boilerplate ) { + // Return an empty string if no file is found. + if ( ! $file ) { return ''; } @@ -1093,6 +1109,8 @@ public static function processRules () { // Reset the selector relationships self::$process->selectorRelationships = array(); + $aliases =& self::$config->aliases; + foreach ( self::$storage->tokens->rules as $rule ) { // Store selector relationships @@ -1100,10 +1118,13 @@ public static function processRules () { csscrush_hook::run( 'rule_prealias', $rule ); - if ( ! empty( self::$config->aliases ) ) { - + if ( ! empty( $aliases[ 'properties' ] ) ) { $rule->addPropertyAliases(); + } + if ( ! empty( $aliases[ 'functions' ] ) ) { $rule->addFunctionAliases(); + } + if ( ! empty( $aliases[ 'values' ] ) ) { $rule->addValueAliases(); } diff --git a/lib/Rule.php b/lib/Rule.php index 5d3cc29..2dd40e3 100644 --- a/lib/Rule.php +++ b/lib/Rule.php @@ -114,7 +114,7 @@ public function __construct ( $selector_string = null, $declarations_string ) { // Parse the declarations chunk // Need to split safely as there are semi-colons in data-uris - $declarations_match = csscrush_util::splitDelimList( $declarations_string, ';', true ); + $declarations_match = csscrush_util::splitDelimList( $declarations_string, ';', true, true ); // First create a simple array of all properties and value pairs in raw state $pairs = array(); @@ -125,16 +125,24 @@ public function __construct ( $selector_string = null, $declarations_string ) { // Strip comments around the property $declaration = csscrush_util::stripComments( $declaration ); - // Extract the property part of the declaration - $colonPos = strpos( $declaration, ':' ); - if ( $colonPos === false ) { + // Accept several different syntaxes for mixin and extends. + if ( preg_match( '!^(?:(@include|mixin)|(@?extends?))[\s\:]+!iS', $declaration, $m ) ) { - continue; // If there's no colon it's malformed + $prop = isset( $m[2] ) ? 'extends' : 'mixin'; + $value = substr( $declaration, strlen( $m[0] ) ); } - $prop = trim( substr( $declaration, 0, $colonPos ) ); + elseif ( ( $colonPos = strpos( $declaration, ':' ) ) !== false ) { - // Extract the value part of the declaration - $value = substr( $declaration, $colonPos + 1 ); + $prop = trim( substr( $declaration, 0, $colonPos ) ); + // Extract the value part of the declaration. + $value = substr( $declaration, $colonPos + 1 ); + } + else { + // Must be malformed. + continue; + } + + // Some cleanup. $value = $value !== false ? trim( $value ) : $value; if ( $prop === 'mixin' ) { diff --git a/misc/initial-values.ini b/misc/initial-values.ini index ff26d35..7755969 100644 --- a/misc/initial-values.ini +++ b/misc/initial-values.ini @@ -19,6 +19,8 @@ background-color = "transparent" background-image = "none" background-origin = "padding-box" background-position = "0 0" +background-position-x = "0" +background-position-y = "0" background-repeat = "repeat" background-size = "auto auto" border = "0" diff --git a/misc/property-sorting.ini b/misc/property-sorting.ini index 7535065..8f1eb1e 100644 --- a/misc/property-sorting.ini +++ b/misc/property-sorting.ini @@ -98,6 +98,8 @@ background-image background-color background-repeat background-position +background-position-x +background-position-y background-origin background-size background-clip From 7b1c3fc0f56098e1726d3096836047d65c4cbeeb Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 12 Sep 2012 15:58:15 +0100 Subject: [PATCH 042/421] Property sorter plugin now supports legacy vendor prefixes (for linting). Removed support for the deprecated var() variable notation. Some internal refactoring. --- README.md | 18 +++---- lib/Core.php | 17 +------ lib/Regex.php | 10 ++-- lib/Rule.php | 62 +++++++++++------------- plugins/property-sorter.php | 94 ++++++++++++++++++++++++------------- 5 files changed, 104 insertions(+), 97 deletions(-) diff --git a/README.md b/README.md index 857a709..55ed647 100644 --- a/README.md +++ b/README.md @@ -13,14 +13,16 @@ http://the-echoplex.net/csscrush Quick start =================================== - - - +```php + + + +``` Submitting bugs diff --git a/lib/Core.php b/lib/Core.php index 92c63b7..fa0ec43 100644 --- a/lib/Core.php +++ b/lib/Core.php @@ -1210,23 +1210,10 @@ protected static function cb_extractVariables ( $match ) { protected static function cb_placeVariables ( $match ) { - $before_char = $match[1]; - - // Check for dollar shorthand - if ( empty( $match[2] ) && isset( $match[3] ) && strpos( $match[0], '$' ) !== false ) { - - $variable_name = $match[3]; - } - else { - $variable_name = $match[2]; - } + $variable_name = $match[1]; if ( isset( self::$storage->variables[ $variable_name ] ) ) { - - return $before_char . self::$storage->variables[ $variable_name ]; - } - else { - return $before_char; + return self::$storage->variables[ $variable_name ]; } } diff --git a/lib/Regex.php b/lib/Regex.php index 7eec415..aab2ca5 100644 --- a/lib/Regex.php +++ b/lib/Regex.php @@ -59,12 +59,7 @@ public static function init () { $patt->argToken = '!___arg(\d+)___!'; // Functions - $patt->varFunction = '!(?: - ([^a-z0-9_-]) - var\(\s*([a-z0-9_-]+)\s*\) - | - \$\(\s*([a-z0-9_-]+)\s*\) # Dollar syntax - )!ixS'; + $patt->varFunction = '!\$\(\s*([a-z0-9_-]+)\s*\)!iS'; $patt->function = '!(^|[^a-z0-9_-])([a-z_-]+)(___p\d+___)!i'; // Specific functions @@ -73,7 +68,8 @@ public static function init () { $patt->thisFunction = csscrush_regex::createFunctionMatchPatt( array( 'this' ) ); // Misc. - $patt->vendorPrefix = '!^-([a-z]+)-([a-z-]+)!'; + $patt->vendorPrefix = '!^-([a-z]+)-([a-z-]+)!iS'; + $patt->mixinExtend = '!^(?:(@include|mixin)|(@?extends?))[\s\:]+!iS'; $patt->absoluteUrl = '!^https?://!'; $patt->argListSplit = '!\s*[,\s]\s*!S'; $patt->mathBlacklist = '![^\.0-9\*\/\+\-\(\)]!S'; diff --git a/lib/Rule.php b/lib/Rule.php index 2dd40e3..76c8522 100644 --- a/lib/Rule.php +++ b/lib/Rule.php @@ -126,7 +126,7 @@ public function __construct ( $selector_string = null, $declarations_string ) { $declaration = csscrush_util::stripComments( $declaration ); // Accept several different syntaxes for mixin and extends. - if ( preg_match( '!^(?:(@include|mixin)|(@?extends?))[\s\:]+!iS', $declaration, $m ) ) { + if ( preg_match( $regex->mixinExtend, $declaration, $m ) ) { $prop = isset( $m[2] ) ? 'extends' : 'mixin'; $value = substr( $declaration, strlen( $m[0] ) ); @@ -253,8 +253,6 @@ public function cssQueryFunction ( $input, $fn_name, $call_property ) { } $default = isset( $args[0] ) ? $args[0] : null; - // csscrush::log( array( $name, $property, $default ), 'query args' ); - // Try to match a abstract rule first if ( preg_match( csscrush_regex::$patt->name, $name ) ) { @@ -330,30 +328,27 @@ public function addPropertyAliases () { // There are aliases for the current property foreach ( $aliasedProperties[ $prop ] as $prop_alias ) { + // If an aliased version already exists to not create one. if ( $this->propertyCount( $prop_alias ) ) { continue; } - // If the aliased property hasn't been set manually, we create it + // Create the aliased declaration. $copy = clone $declaration; - $copy->family = $copy->property; $copy->property = $prop_alias; - // Remembering to set the vendor property + // Set the aliased declaration vendor property. $copy->vendor = null; - - // Increment the property count - $this->addProperty( $prop_alias ); if ( preg_match( $regex->vendorPrefix, $prop_alias, $vendor ) ) { $copy->vendor = $vendor[1]; } $new_set[] = $copy; } } - // Un-aliased property or a property alias that has been manually set + // Un-aliased property or a property alias that has been manually set. $new_set[] = $declaration; } - // Re-assign + // Re-assign. $this->declarations = $new_set; } @@ -368,8 +363,8 @@ public function addFunctionAliases () { $new_set = array(); - // Keep track of the function aliases we apply and to which property 'family' - // they belong, so we can avoid un-unecessary duplications + // Keep track of the function aliases we apply and to which property + // they belong, so we can avoid un-unecessary duplications. $used_fn_aliases = array(); // Shim in aliased functions @@ -383,14 +378,15 @@ public function addFunctionAliases () { $new_set[] = $declaration; continue; } - // Get list of functions used in declaration that are alias-able, if none skip + + // Get list of functions used in declaration that are alias-able, if none skip. $intersect = array_intersect( $declaration->functions, $aliased_functions ); if ( empty( $intersect ) ) { $new_set[] = $declaration; continue; } - // csscrush::log($intersect); - // Loop the aliasable functions + + // Loop the aliasable functions. foreach ( $intersect as $fn_name ) { if ( $declaration->vendor ) { @@ -404,7 +400,7 @@ public function addFunctionAliases () { '${1}' . $fn_search, $declaration->value ); - $used_fn_aliases[ $declaration->family ][] = $fn_search; + $used_fn_aliases[ $declaration->canonicalProperty ][] = $fn_search; } } else { @@ -413,8 +409,8 @@ public function addFunctionAliases () { foreach ( $function_aliases[ $fn_name ] as $fn_alias ) { if ( - isset( $used_fn_aliases[ $declaration->family ] ) && - in_array( $fn_alias, $used_fn_aliases[ $declaration->family ] ) + isset( $used_fn_aliases[ $declaration->canonicalProperty ] ) && + in_array( $fn_alias, $used_fn_aliases[ $declaration->canonicalProperty ] ) ) { // If the function alias has already been applied in a vendor property // for the same declaration property assume all is good @@ -427,8 +423,6 @@ public function addFunctionAliases () { $copy->value ); $new_set[] = $copy; - // Increment the property count - $this->addProperty( $copy->property ); } } } @@ -705,7 +699,7 @@ public static function get ( $token ) { class csscrush_declaration { public $property; - public $family; + public $canonicalProperty; public $vendor; public $functions; public $value; @@ -734,15 +728,15 @@ public function __construct ( $prop, $value, $contextIndex = 0 ) { $prop = substr( $prop, 1 ); } - // Store the property family - // Store the vendor id, if one is present + // Store the canonical property name. + // Store the vendor mark if one is present. if ( preg_match( $regex->vendorPrefix, $prop, $vendor ) ) { - $family = $vendor[2]; + $canonical_property = $vendor[2]; $vendor = $vendor[1]; } else { $vendor = null; - $family = $prop; + $canonical_property = $prop; } // Check for !important keywords @@ -779,14 +773,14 @@ public function __construct ( $prop, $value, $contextIndex = 0 ) { $functions = array(); } - $this->property = $prop; - $this->family = $family; - $this->vendor = $vendor; - $this->functions = $functions; - $this->index = $contextIndex; - $this->value = $value; - $this->skip = $skip; - $this->important = $important; + $this->property = $prop; + $this->canonicalProperty = $canonical_property; + $this->vendor = $vendor; + $this->functions = $functions; + $this->index = $contextIndex; + $this->value = $value; + $this->skip = $skip; + $this->important = $important; } public function getFullValue () { diff --git a/plugins/property-sorter.php b/plugins/property-sorter.php index 6d835ca..5c1cc91 100644 --- a/plugins/property-sorter.php +++ b/plugins/property-sorter.php @@ -2,21 +2,21 @@ /** * Property sorter. * Order CSS properties according to a sorting table. - * + * * Examples use the predefined sorting table. - * + * * To define a custom sorting table globally define $CSSCRUSH_PROPERTY_SORT_ORDER. * Assign an empty array to create an alphabetical sort: - * + * * $CSSCRUSH_PROPERTY_SORT_ORDER = array( 'color', ... ); - * + * * * @before * color: red; * background: #000; * opacity: .5; * display: block; - * + * * @after * display: block; * opacity: .5; @@ -48,35 +48,77 @@ function csscrush__property_sorter ( csscrush_rule $rule ) { function _csscrush__property_sorter_callback ( $a, $b ) { $map =& _csscrush__property_sorter_get_table(); - $a_prop =& $a->property; - $b_prop =& $b->property; + $a_prop =& $a->canonicalProperty; + $b_prop =& $b->canonicalProperty; $a_listed = isset( $map[ $a_prop ] ); $b_listed = isset( $map[ $b_prop ] ); - // If both properties are listed do a table comparison. + // If the properties are identical we need to flag for an index comparison. + $compare_indexes = false; + + // If the 'canonical' properties are identical we need to flag for a vendor comparison. + $compare_vendor = false; + + // If both properties are listed. if ( $a_listed && $b_listed ) { if ( $a_prop === $b_prop ) { - return $a->index > $b->index ? 1 : -1; + if ( $a->vendor || $b->vendor ) { + $compare_vendor = true; + } + else { + $compare_indexes = true; + } + } + else { + // Table comparison. + return $map[ $a_prop ] > $map[ $b_prop ] ? 1 : -1; } - return $map[ $a_prop ] > $map[ $b_prop ] ? 1 : -1; } - // Listed properties always come before un-listed. - if ( $a_listed && ! $b_listed ) { + // If one property is listed it always takes higher priority. + elseif ( $a_listed && ! $b_listed ) { return -1; } - if ( $b_listed && ! $a_listed ) { + elseif ( $b_listed && ! $a_listed ) { return 1; } - // If propertes are the same compare declaration indexes. - if ( $a_prop === $b_prop ) { + // If neither property is listed. + else { + + if ( $a_prop === $b_prop ) { + if ( $a->vendor || $b->vendor ) { + $compare_vendor = true; + } + else { + $compare_indexes = true; + } + } + else { + // Regular sort. + return $a_prop > $b_prop ? 1 : -1; + } + } + + // Comparing by index. + if ( $compare_indexes ) { return $a->index > $b->index ? 1 : -1; } - // If neither property is listed do a regular sort. - return $a_prop > $b_prop ? 1 : -1; + // Comparing by vendor mark. + if ( $compare_vendor ) { + if ( ! $a->vendor && $b->vendor ) { + return 1; + } + elseif ( $a->vendor && ! $b->vendor ) { + return -1; + } + else { + // If both have a vendor mark compare vendor name length. + return strlen( $b->vendor ) > strlen( $a->vendor ) ? 1 : -1; + } + } } @@ -115,22 +157,8 @@ function &_csscrush__property_sorter_get_table () { } } - // Add in prefixed properties based on the aliases file - $collated_table = array(); - $property_aliases =& csscrush::$config->aliases[ 'properties' ]; - $priority = 0; - - foreach ( $table as $property ) { - if ( isset( $property_aliases[ $property ] ) ) { - foreach ( $property_aliases[ $property ] as &$property_alias ) { - $collated_table[ $property_alias ] = ++$priority; - } - } - $collated_table[ $property ] = ++$priority; - } - - // Cache the collated table - $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER_CACHE' ] = $collated_table; + // Cache the table (and flip it). + $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER_CACHE' ] = array_flip( $table ); return $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER_CACHE' ]; } From 727af5a86391f2f085a4e3888a4e67fb7d440bdb Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 14 Sep 2012 15:31:30 +0100 Subject: [PATCH 043/421] Switching cache storage from serialized php to json. --- lib/IO.php | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/lib/IO.php b/lib/IO.php index 13229b3..7172d40 100644 --- a/lib/IO.php +++ b/lib/IO.php @@ -79,7 +79,7 @@ public static function testOutputDir ( $write_test = true ) { csscrush::log( 'Permissions updated' ); } } - + if ( $error ) { csscrush::logError( $error ); trigger_error( __METHOD__ . ": $error\n", E_USER_WARNING ); @@ -156,8 +156,7 @@ public static function validateExistingOutput () { $options_unchanged = true; foreach ( $existing_options as $key => &$value ) { - if ( $existing_options->{ $key } !== $options->{ $key } ) { - // csscrush::log( "$key is different" ); + if ( $existing_options[ $key ] !== $options->{ $key } ) { $options_unchanged = false; break; } @@ -241,9 +240,12 @@ public static function getCacheData () { $cache_data = array(); - if ( $cache_data_exists && $cache_data_file_is_writable ) { - // Load from file - $cache_data = unserialize( file_get_contents( $process->cacheFilePath ) ); + if ( + $cache_data_exists && + $cache_data_file_is_writable && + $cache_data = json_decode( file_get_contents( $process->cacheFilePath ), true ) + ) { + // Successfully loaded config file. } else { // Config file may exist but not be writable (may not be visible in some ftp situations?) @@ -255,9 +257,9 @@ public static function getCacheData () { trigger_error( __METHOD__ . ": $error\n", E_USER_NOTICE ); } } - // Create + // Create config file. csscrush::log( 'Creating cache data file' ); - file_put_contents( $process->cacheFilePath, serialize( array() ) ); + file_put_contents( $process->cacheFilePath, json_encode( array() ) ); } return $cache_data; @@ -267,12 +269,12 @@ public static function getCacheData () { public static function saveCacheData () { $process = csscrush::$process; - + // Need to store the current path so we can check we're using the right config path later $process->cacheData[ 'originPath' ] = $process->cacheFilePath; - + csscrush::log( 'Saving config' ); - file_put_contents( $process->cacheFilePath, serialize( $process->cacheData ) ); + file_put_contents( $process->cacheFilePath, json_encode( $process->cacheData ) ); } } From 157b18899783e9a70154cf938cee2dc408721ca6 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 18 Sep 2012 16:27:21 +0100 Subject: [PATCH 044/421] Added trace option to output SASS compatible debug-info stubs for use with tools like FireSass. Some internal refactoring. --- Prepend.css | 4 - cli.php | 30 ++++++-- lib/Core.php | 187 ++++++++++++++++++++++++++++++++++++++++------- lib/IO.php | 26 ++++--- lib/Importer.php | 45 ++++++++---- lib/Mixin.php | 2 +- lib/Regex.php | 11 +-- lib/Rule.php | 15 +++- lib/Util.php | 6 +- 9 files changed, 252 insertions(+), 74 deletions(-) mode change 100644 => 100755 cli.php diff --git a/Prepend.css b/Prepend.css index 465cf37..9928ea6 100644 --- a/Prepend.css +++ b/Prepend.css @@ -39,7 +39,3 @@ Prepend.css contains library variables by default, but it could also contain res monospace: $( courier ); } - - - - diff --git a/cli.php b/cli.php old mode 100644 new mode 100755 index dc7c52e..ee379ea --- a/cli.php +++ b/cli.php @@ -1,3 +1,4 @@ +#!/usr/bin/env php docRoot; - csscrush::$config->docRoot = $hostfile_context; - $process_opts[ 'context' ] = $hostfile_context; + csscrush::$config->docRoot = $context; + $process_opts[ 'context' ] = $context; } // Process the stream $output = csscrush::string( $input, $process_opts ); // Reset the document root after processing -if ( $hostfile_context ) { +if ( $context ) { csscrush::$config->docRoot = $old_doc_root; } diff --git a/lib/Core.php b/lib/Core.php index fa0ec43..3af5d74 100644 --- a/lib/Core.php +++ b/lib/Core.php @@ -76,6 +76,9 @@ public static function init ( $seed_file ) { // List of plugins to disable (as Array of names) 'disable' => null, + + // Output sass debug-info stubs that work with development tools like FireSass. + 'trace' => false, ); // Initialise other classes @@ -277,9 +280,6 @@ public static function file ( $file, $options = null ) { return ''; } - // Load the cache data - $process->cacheData = csscrush::io_call( 'getCacheData' ); - // Get the input file object if ( ! ( $process->input = csscrush::io_call( 'getInput', $file ) ) ) { return ''; @@ -289,7 +289,11 @@ public static function file ( $file, $options = null ) { // Used in validateCache, and writing to filesystem $process->outputFileName = csscrush::io_call( 'getOutputFileName' ); - if ( $options->cache === true ) { + // Caching. + if ( $options->cache ) { + + // Load the cache data + $process->cacheData = csscrush::io_call( 'getCacheData' ); // If cache is enabled check for a valid compiled file $valid_compliled_file = csscrush::io_call( 'validateExistingOutput' ); @@ -519,12 +523,117 @@ public static function logError ( $msg ) { ##################### # Internal functions + public static function addTracingStubs ( &$stream ) { + + $selector_patt = '! (^|;|\})+ ([^;{}]+) (\{) !xmS'; + $token_or_whitespace = '!(\s*___c\d+___\s*|\s+)!'; + + $matches = csscrush_regex::matchAll( $selector_patt, $stream ); + + // Start from last match and move backwards. + while ( $m = array_pop( $matches ) ) { + + // Shortcuts for readability. + list( $full_match, $before, $content, $after ) = $m; + $full_match_text = $full_match[0]; + $full_match_start = $full_match[1]; + + // The correct before string. + $before = substr( $full_match_text, 0, $content[1] - $full_match_start ); + + // Split the matched selector part. + $content_parts = preg_split( $token_or_whitespace, $content[0], null, + PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY ); + + foreach ( $content_parts as $part ) { + + if ( ! preg_match( $token_or_whitespace, $part ) ) { + + // Match to a valid selector. + if ( preg_match( '!^([^@]|@(?:page|abstract))!S', $part ) ) { + + // Count line breaks between the start of stream and + // the matched selector to get the line number. + $selector_index = $full_match_start + strlen( $before ); + $line_num = 1; + $stream_before = ""; + if ( $selector_index ) { + $stream_before = substr( $stream, 0, $selector_index ); + $line_num = substr_count( $stream_before, "\n" ) + 1; + } + + // Get the currently processed file path, and escape it. + // if ( $current_file = csscrush::$process->currentFile ) { + $current_file = str_replace( ' ', '%20', csscrush::$process->currentFile ); + $current_file = preg_replace( '![^\w-]!', '\\\\$0', $current_file ); + + // Splice in tracing stub. + $label = csscrush::tokenLabelCreate( 't' ); + $stream = $stream_before . "$label" . substr( $stream, $selector_index ); + self::$storage->tokens->traces[ $label ] + = "@media -sass-debug-info{filename{font-family:$current_file}line{font-family:\\00003$line_num}}"; + + } + else { + // Not matched as a valid selector, move on. + continue 2; + } + break; + } + + // Append split segment to $before. + $before .= $part; + } + } + } + public static function prepareStream ( &$stream ) { + $regex = csscrush_regex::$patt; + $process = csscrush::$process; + $trace = $process->options->trace; + $stream = preg_replace_callback( csscrush_regex::$patt->commentAndString, array( 'self', 'cb_extractCommentAndString' ), $stream ); + // If @charset is set store it. + if ( preg_match( $regex->charset, $stream, $m ) ) { + $replace = ''; + if ( ! $process->charset ) { + $replace = str_repeat( "\n", substr_count( $m[0], "\n" ) ); + $process->charset = new csscrush_string( $m[1] ); + } + $stream = preg_replace( $regex->charset, $replace, $stream ); + } + + // Catch obvious typing errors. + $parse_errors = array(); + $current_file = $process->currentFile; + $balanced_parens = substr_count( $stream, "(" ) === substr_count( $stream, ")" ); + $balanced_curlies = substr_count( $stream, "{" ) === substr_count( $stream, "}" ); + + if ( ! $balanced_parens ) { + $parse_errors[] = "Unmatched '(' in $current_file."; + } + if ( ! $balanced_curlies ) { + $parse_errors[] = "Unmatched '{' in $current_file."; + } + if ( $parse_errors ) { + foreach ( $parse_errors as $error_msg ) { + csscrush::logError( $error_msg ); + trigger_error( "$error_msg\n", E_USER_NOTICE ); + } + return false; + } + + // Optionally add tracing stubs. + if ( $trace ) { + self::addTracingStubs( $stream ); + } + $stream = csscrush_util::normalizeWhiteSpace( $stream ); + + return true; } protected static function getBoilerplate () { @@ -721,6 +830,7 @@ protected static function reset ( $options = null ) { self::$process->errors = array(); self::$process->selectorRelationships = array(); self::$process->charset = null; + self::$process->currentFile = null; self::$process->options = self::getOptions( $options ); self::$storage = (object) array(); @@ -731,6 +841,7 @@ protected static function reset ( $options = null ) { 'parens' => array(), 'mixinArgs' => array(), 'urls' => array(), + 'traces' => array(), ); self::$storage->variables = array(); self::$storage->misc = (object) array(); @@ -819,6 +930,12 @@ protected static function compile ( $stream ) { // Print it all back self::display( $stream ); + // Release memory + self::$storage = null; + $process->mixins = null; + $process->abstracts = null; + $process->selectorRelationships = null; + // Add in boilerplate if ( $options->boilerplate ) { $stream = self::getBoilerplate() . "\n$stream"; @@ -826,15 +943,9 @@ protected static function compile ( $stream ) { // Add @charset at top if set if ( $process->charset ) { - $stream = "@charset \"{$process->charset}\";\n" . $stream; + $stream = "@charset $process->charset;\n" . $stream; } - // Release memory - self::$storage = null; - $process->mixins = null; - $process->abstracts = null; - $process->selectorRelationships = null; - return $stream; } @@ -845,7 +956,7 @@ protected static function display ( &$stream ) { $regex = csscrush_regex::$patt; if ( $minify ) { - $stream = csscrush_util::stripComments( $stream ); + $stream = csscrush_util::stripCommentTokens( $stream ); } else { // Formatting @@ -1021,7 +1132,7 @@ protected static function aliasAtRules ( &$stream ) { protected static function prefixSelectors ( &$stream ) { - $matches = csscrush_regex::matchAll( '@in\s+([^\{]+){', $stream, true ); + $matches = csscrush_regex::matchAll( '@in\s+([^{]+){', $stream, true ); // Move through the matches in reverse order while ( $match = array_pop( $matches ) ) { @@ -1145,30 +1256,45 @@ public static function processRules () { protected static function cb_extractCommentAndString ( $match ) { - $capture = $match[0]; + $full_match = $match[0]; - if ( strpos( $capture, '/*' ) === 0 ) { + // We return the newlines to maintain line numbering when tracing. + $newlines = str_repeat( "\n", substr_count( $full_match, "\n" ) ); + + if ( strpos( $full_match, '/*' ) === 0 ) { // Strip private comments $private_comment_marker = '$!'; if ( - strpos( $capture, '/*' . $private_comment_marker ) === 0 || + strpos( $full_match, '/*' . $private_comment_marker ) === 0 || ! self::$process->options->debug ) { - return ''; + return $newlines; } $label = self::tokenLabelCreate( 'c' ); - self::$storage->tokens->comments[ $label ] = $capture; + + // Fix broken comments as they will break any subsquent + // imported files that are inlined. + if ( ! preg_match( '!\*/$!', $full_match ) ) { + $full_match .= '*/'; + } + self::$storage->tokens->comments[ $label ] = $full_match; } else { $label = csscrush::tokenLabelCreate( 's' ); - csscrush::$storage->tokens->strings[ $label ] = $capture; + + // Fix broken strings as they will break any subsquent + // imported files that are inlined. + if ( $full_match[0] !== $full_match[ strlen( $full_match )-1 ] ) { + $full_match .= $full_match[0]; + } + csscrush::$storage->tokens->strings[ $label ] = $full_match; } - return $label; + return $newlines . $label; } protected static function cb_extractMixins ( $match ) { @@ -1190,7 +1316,7 @@ protected static function cb_extractVariables ( $match ) { $block = $match[2]; // Strip comment markers - $block = csscrush_util::stripComments( $block ); + $block = csscrush_util::stripCommentTokens( $block ); // Need to split safely as there are semi-colons in data-uris $variables_match = csscrush_util::splitDelimList( $block, ';', true ); @@ -1267,6 +1393,12 @@ protected static function cb_printRule ( $match ) { $rule =& self::$storage->tokens->rules[ $ruleLabel ]; + // Tracing stubs. + $tracing_stub = ''; + if ( $rule->tracingStub ) { + $tracing_stub =& self::$storage->tokens->traces[ $rule->tracingStub ]; + } + // If there are no selectors or declarations associated with the rule return empty string if ( empty( $rule->selectorList ) || ! count( $rule ) ) { return ''; @@ -1285,13 +1417,16 @@ protected static function cb_printRule ( $match ) { // Return whole rule if ( $minify ) { $block = implode( ';', $block ); - return "$selectors{{$block}}"; + return "$tracing_stub$selectors{{$block}}"; } else { - $block = implode( ";\n\t", $block ); - // Include pre rule comments + // Include pre-rule comments. $comments = implode( "\n", $rule->comments ); - return "$comments\n$selectors {\n\t$block;\n\t}\n"; + if ( $tracing_stub ) { + $tracing_stub .= "\n"; + } + $block = implode( ";\n\t", $block ); + return "$comments\n$tracing_stub$selectors {\n\t$block;\n\t}\n"; } } @@ -1340,8 +1475,6 @@ public static function resolveFragments ( &$stream ) { // Create the fragment and store it $fragments[ $fragment_name ] = new csscrush_fragment( $curly_match->inside ); - - // csscrush::log( $fragments ); } } diff --git a/lib/IO.php b/lib/IO.php index 7172d40..6b42e26 100644 --- a/lib/IO.php +++ b/lib/IO.php @@ -119,7 +119,7 @@ public static function validateExistingOutput () { continue; } // Cached file exists - csscrush::log( 'Cached file exists' ); + csscrush::log( 'Cached file exists.' ); $existingfile = (object) array(); $existingfile->name = $filename; @@ -132,7 +132,7 @@ public static function validateExistingOutput () { if ( file_exists( $existingfile->path ) && isset( $process->cacheData[ $process->outputFileName ] ) ) { // File exists and has config - csscrush::log( 'has config' ); + csscrush::log( 'Cached file is registered.' ); foreach ( $process->cacheData[ $existingfile->name ][ 'imports' ] as $import_file ) { @@ -145,7 +145,7 @@ public static function validateExistingOutput () { } else { // File has been moved, remove old file and skip to compile - csscrush::log( 'Import file has been moved, removing existing file' ); + csscrush::log( 'Import file has been moved, removing existing file.' ); unlink( $existingfile->path ); return false; } @@ -167,22 +167,22 @@ public static function validateExistingOutput () { // Files have not been modified and config is the same: return the old file csscrush::log( "Files and options have not been modified, returning existing - file '$existingfile->URL'" ); + file '$existingfile->URL'." ); return $existingfile->URL . ( $options->versioning !== false ? "?$existing_datesum" : '' ); } else { // Remove old file and continue making a new one... - ! $options_unchanged && csscrush::log( 'Options have been modified' ); - ! $files_unchanged && csscrush::log( 'Files have been modified' ); - csscrush::log( 'Removing existing file' ); + ! $options_unchanged && csscrush::log( 'Options have been modified.' ); + ! $files_unchanged && csscrush::log( 'Files have been modified.' ); + csscrush::log( 'Removing existing file.' ); unlink( $existingfile->path ); } } else if ( file_exists( $existingfile->path ) ) { // File exists but has no config - csscrush::log( 'File exists but no config, removing existing file' ); + csscrush::log( 'File exists but no config, removing existing file.' ); unlink( $existingfile->path ); } return false; @@ -237,7 +237,6 @@ public static function getCacheData () { $cache_data_exists = file_exists( $process->cacheFilePath ); $cache_data_file_is_writable = $cache_data_exists ? is_writable( $process->cacheFilePath ) : false; - $cache_data = array(); if ( @@ -246,6 +245,7 @@ public static function getCacheData () { $cache_data = json_decode( file_get_contents( $process->cacheFilePath ), true ) ) { // Successfully loaded config file. + csscrush::log( 'Cache data loaded.' ); } else { // Config file may exist but not be writable (may not be visible in some ftp situations?) @@ -257,8 +257,10 @@ public static function getCacheData () { trigger_error( __METHOD__ . ": $error\n", E_USER_NOTICE ); } } - // Create config file. - csscrush::log( 'Creating cache data file' ); + else { + // Create config file. + csscrush::log( 'Creating cache data file.' ); + } file_put_contents( $process->cacheFilePath, json_encode( array() ) ); } @@ -273,7 +275,7 @@ public static function saveCacheData () { // Need to store the current path so we can check we're using the right config path later $process->cacheData[ 'originPath' ] = $process->cacheFilePath; - csscrush::log( 'Saving config' ); + csscrush::log( 'Saving config.' ); file_put_contents( $process->cacheFilePath, json_encode( $process->cacheData ) ); } diff --git a/lib/Importer.php b/lib/Importer.php index a2ca85c..801d97f 100644 --- a/lib/Importer.php +++ b/lib/Importer.php @@ -38,27 +38,36 @@ public static function hostfile () { $mtimes = array(); $filenames = array(); - // Determine input; string or file - // Extract the comments then strings + $stream = ''; + $prepend_file_contents = ''; + + // The prepend file. + if ( $prepend_file = csscrush_util::find( 'Prepend-local.css', 'Prepend.css' ) ) { + $prepend_file_contents = file_get_contents( $prepend_file ); + $process->currentFile = 'file://' . $prepend_file; + // If there's a parsing error inside the prepend file, wipe $prepend_file_contents. + if ( ! csscrush::prepareStream( $prepend_file_contents ) ) { + $prepend_file_contents = ''; + } + } + + // Resolve main input: string or file. if ( isset( $hostfile->string ) ) { - $stream = $hostfile->string; + $stream .= $hostfile->string; + $process->currentFile = 'inline-css'; } else { - $stream = file_get_contents( $hostfile->path ); - } - - // If there's a prepend file, prepend it - if ( $prependFile = csscrush_util::find( 'Prepend-local.css', 'Prepend.css' ) ) { - $stream = file_get_contents( $prependFile ) . $stream; + $stream .= file_get_contents( $hostfile->path ); + $process->currentFile = 'file://' . $hostfile->path; } - // If @charset is set store it - if ( preg_match( $regex->charset, $stream, $m ) ) { - $stream = preg_replace( $regex->charset, '', $stream ); - $process->charset = $m[2]; + // If there's a parsing error go no further. + if ( ! csscrush::prepareStream( $stream ) ) { + return $stream; } - csscrush::prepareStream( $stream ); + // Not forgetting to prepend the prepend file contents. + $stream = $prepend_file_contents . $stream; // If rewriting URLs as absolute we need to do some extra work if ( $options->rewrite_import_urls === 'absolute' ) { @@ -143,7 +152,13 @@ public static function hostfile () { // Import file opened successfully so we process it: // - We need to resolve import statement urls in all imported files since // they will be brought inline with the hostfile - csscrush::prepareStream( $import->content ); + $process->currentFile = 'file://' . $import->path; + + // If there are unmatched brackets inside the import, strip it. + if ( ! csscrush::prepareStream( $import->content ) ) { + $stream = $pre_statement . $post_statement; + continue; + } $import->dir = dirname( $import->url ); diff --git a/lib/Mixin.php b/lib/Mixin.php index 0508a4e..20e4d13 100644 --- a/lib/Mixin.php +++ b/lib/Mixin.php @@ -16,7 +16,7 @@ class csscrush_mixin { public function __construct ( $block ) { // Strip comment markers - $block = csscrush_util::stripComments( $block ); + $block = csscrush_util::stripCommentTokens( $block ); // Prepare the arguments object $this->arguments = new csscrush_arglist( $block ); diff --git a/lib/Regex.php b/lib/Regex.php index aab2ca5..8d750da 100644 --- a/lib/Regex.php +++ b/lib/Regex.php @@ -39,9 +39,9 @@ public static function init () { $patt->mixin = '!@mixin\s*([^\{]*)\{\s*(.*?)\s*\};?!s'; $patt->abstract = csscrush_regex::create( '^@abstract\s+()', 'i' ); $patt->commentAndString = '! - (\'|")(?:\\1|[^\1])*?\1 # quoted string + (\'|")(?:\\\\\1|[^\1])*?(?:\1|$) # quoted string (to EOF if unmatched) | - /\*(?:.*?)\*/ # block comment + /\*(?:.*?)(?:\*/|$) # block comment (to EOF if unmatched) !xsS'; // As an exception we treat some @-rules like standard rule blocks @@ -56,6 +56,7 @@ public static function init () { $patt->ruleToken = '!___r\d+___!'; $patt->parenToken = '!___p\d+___!'; $patt->urlToken = '!___u\d+___!'; + $patt->traceToken = '!___t\d+___!'; $patt->argToken = '!___arg(\d+)___!'; // Functions @@ -73,7 +74,7 @@ public static function init () { $patt->absoluteUrl = '!^https?://!'; $patt->argListSplit = '!\s*[,\s]\s*!S'; $patt->mathBlacklist = '![^\.0-9\*\/\+\-\(\)]!S'; - $patt->charset = '!@charset\s+([\'"])([\w-]+)\1\s*;!i'; + $patt->charset = '!@charset\s+(___s\d+___)\s*;!i'; } @@ -95,8 +96,8 @@ public static function matchAll ( $patt, $subject, $preprocess_patt = false, $of $patt = self::create( $patt, 'i' ); } - preg_match_all( $patt, $subject, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER, $offset ); - return $matches; + $count = preg_match_all( $patt, $subject, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER, $offset ); + return $count ? $matches : array(); } diff --git a/lib/Rule.php b/lib/Rule.php index 76c8522..0c005aa 100644 --- a/lib/Rule.php +++ b/lib/Rule.php @@ -11,6 +11,8 @@ class csscrush_rule implements IteratorAggregate, Countable { public $isNested; public $label; + public $tracingStub = null; + public $properties = array(); public $selectorList = array(); @@ -74,8 +76,16 @@ public function declarationCheckin ( $prop, $value, &$pairs ) { public function __construct ( $selector_string = null, $declarations_string ) { $regex = csscrush_regex::$patt; + $options = csscrush::$process->options; $this->label = csscrush::tokenLabelCreate( 'r' ); + // If tracing store the last tracing stub, then strip all. + if ( $options->trace && $trace_tokens = csscrush_regex::matchAll( $regex->traceToken, $selector_string ) ) { + $trace_token = array_pop( $trace_tokens ); + $this->tracingStub = $trace_token[0][0]; + $selector_string = preg_replace( $regex->traceToken, '', $selector_string ); + } + // Parse the selectors chunk if ( ! empty( $selector_string ) ) { @@ -84,7 +94,6 @@ public function __construct ( $selector_string = null, $declarations_string ) { // Remove and store comments that sit above the first selector // remove all comments between the other selectors if ( strpos( $selectors_match->list[0], '___c' ) !== false ) { - preg_match_all( $regex->commentToken, $selectors_match->list[0], $m ); $this->comments = $m[0]; } @@ -92,7 +101,7 @@ public function __construct ( $selector_string = null, $declarations_string ) { // Strip any other comments then create selector instances foreach ( $selectors_match->list as $selector ) { - $selector = trim( csscrush_util::stripComments( $selector ) ); + $selector = trim( csscrush_util::stripCommentTokens( $selector ) ); // If the selector matches an absract directive if ( preg_match( $regex->abstract, $selector, $m ) ) { @@ -123,7 +132,7 @@ public function __construct ( $selector_string = null, $declarations_string ) { foreach ( $declarations_match->list as $declaration ) { // Strip comments around the property - $declaration = csscrush_util::stripComments( $declaration ); + $declaration = csscrush_util::stripCommentTokens( $declaration ); // Accept several different syntaxes for mixin and extends. if ( preg_match( $regex->mixinExtend, $declaration, $m ) ) { diff --git a/lib/Util.php b/lib/Util.php index 2101246..478a09c 100644 --- a/lib/Util.php +++ b/lib/Util.php @@ -78,7 +78,7 @@ public static function find () { } - public static function stripComments ( $str ) { + public static function stripCommentTokens ( $str ) { return preg_replace( csscrush_regex::$patt->commentToken, '', $str ); } @@ -334,6 +334,10 @@ public function __construct ( $token ) { $this->quoteMark = $raw[0]; } + public function __toString () { + return csscrush::$storage->tokens->strings[ $this->token ]; + } + public function update ( $newValue ) { csscrush::$storage->tokens->strings[ $this->token ] = $newValue; } From 041b1e5b670caeca5c7a6e98bbad6877574ea2fc Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 19 Sep 2012 15:26:40 +0100 Subject: [PATCH 045/421] Some tweaks to improve un-cached performance. --- CHANGELOG.txt | 211 +++++++++++++++++++++++++------------------------- README.md | 2 +- lib/Core.php | 20 +++-- lib/Regex.php | 4 + lib/Util.php | 108 +++++++------------------- 5 files changed, 152 insertions(+), 193 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index b915c08..9c2f51e 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,183 +1,184 @@ 1.7 ------ -Updated plugin API -Added options for enabling and disabling plugins at runtime -Added support for SASS-like @include/@extend syntax for invoking mixins and extends -Added property sorter plugin -Boilerplate option now accepts a filename string as a boilerplate template -CssCrush::string() now uses document_root as a default context for finding linked resources -Updated command line appication -Updated aliases and initial value files -Fixed parsing issue introduced in 1.6.1 +--- +Added trace option to output SASS compatible debug-info stubs for use with tools like FireSass. +Updated plugin API. +Added options for enabling and disabling plugins at runtime. +Added property sorter plugin. +Added support for SASS-like @include/@extend syntax for invoking mixins and extends. +Boilerplate option now accepts a filename string as a boilerplate template. +CssCrush::string() now uses document_root as a default context for finding linked resources. +Updated command line appication. +Updated aliases and initial value files. +Fixed parsing issue introduced in 1.6.1. 1.6.1 ----- -Resolved issue #35 -Resolved issue #34 +Resolved issue #35. +Resolved issue #34. 1.6 --- -Inheritance model improved to support adoption of pseudo classes and elements (see wiki) -Added rule self-referencing function this() and complimentary data-* properties -Added rule referencing function query() -Added default value argument for variables -Added hsl-adjust() and hsla-adjust() color functions -Mixin and fragment arg() function can now be nested -Commas are now optional when specifying arguments for most custom functions -Double-colon plugin moved to core -Option rewrite_import_urls now defaults to true +Inheritance model improved to support adoption of pseudo classes and elements (see wiki). +Added rule self-referencing function this() and complimentary data-* properties. +Added rule referencing function query(). +Added default value argument for variables. +Added hsl-adjust() and hsla-adjust() color functions. +Mixin and fragment arg() function can now be nested. +Commas are now optional when specifying arguments for most custom functions. +Double-colon plugin moved to core. +Option rewrite_import_urls now defaults to true. 1.5.3 ----- -Refactoring -Fixed some test cases +Refactoring. +Fixed some test cases. 1.5.2 ----- -Resolved issue #32 -csscrush::inline method now defaults to not printing a boilerplate -Updated aliases file +Resolved issue #32. +csscrush::inline method now defaults to not printing a boilerplate. +Updated aliases file. 1.5.1 ----- -Extended mixins to work with abstract rules and regular rules -Fixed issue with selector grouping and inheritance in combination +Extended mixins to work with abstract rules and regular rules. +Fixed issue with selector grouping and inheritance in combination. 1.5 --- -New feature: Rule inheritance / abstract rules -New feature: Block nesting -New feature: Mixins -New feature: Fragments -Abstracted IO interface -Added some error reporting -Added spiffing.css plugin -csscrush_tag method now uses media type 'all' by default -Updated alias and initial-value tables -Internal refactoring -Resolved issue #23 -Resolved issue #24 -Resolved issue #27 -Resolved issue #28 -Resolved issue #29 +New feature: Rule inheritance / abstract rules. +New feature: Block nesting. +New feature: Mixins. +New feature: Fragments. +Abstracted IO interface. +Added some error reporting. +Added spiffing.css plugin. +csscrush_tag method now uses media type 'all' by default. +Updated alias and initial-value tables. +Internal refactoring. +Resolved issue #23. +Resolved issue #24. +Resolved issue #27. +Resolved issue #28. +Resolved issue #29. 1.4.2 ----- -Fixed bug with @import statement parsing -Some minor under the hood changes +Fixed bug with @import statement parsing. +Some minor under the hood changes. 1.4.1 ----- -Added command line application -Added 'rewrite_import_urls' option - Ability to rewrite relative url references inside imported css files -Added Prepend.css - Optionally prepend css to every input -Fix for issue #21 -Reorganized aliases file with some additions -Initial-values updated -Updated csscrush::string method to correctly handle import statements +Added command line application. +Added 'rewrite_import_urls' option - Ability to rewrite relative url references inside imported css files. +Added Prepend.css - Optionally prepend css to every input. +Fix for issue #21. +Reorganized aliases file with some additions. +Initial-values updated. +Updated csscrush::string method to correctly handle import statements. 1.4 --- -Added initial-keyword plugin (shim for the CSS3 keyword) -Added inline method (Issue #18) -Added ability to escape declarations from aliasing or plugins by prefixing with tilde -Added procedural style public API to mirror the static class API -Deprecated @variables syntax for @define. @variables still supported -Adjusted color functions to accept a space delimiter (as well as comma) in the arguments list -Surpressed some benign PHP warning messages -Some internal cleaning up -Disabled IE6 min-height plugin by default - - +Added initial-keyword plugin (shim for the CSS3 keyword). +Added inline method (Issue #18). +Added ability to escape declarations from aliasing or plugins by prefixing with tilde. +Added procedural style public API to mirror the static class API. +Deprecated @variables syntax for @define. @variables still supported. +Adjusted color functions to accept a space delimiter (as well as comma) in the arguments list. +Surpressed some benign PHP warning messages. +Some internal cleaning up. +Disabled IE6 min-height plugin by default. + +. 1.3.6 ----- -Improved color functions -Added a-adjust function for altering a color's opacity -Deprecated hsl-adjust function (you can use nested color functions instead) -Added the ability to use local versions of alias and plugin files so pull updates don't clobber local settings - +Improved color functions. +Added a-adjust function for altering a color's opacity. +Deprecated hsl-adjust function (you can use nested color functions instead). +Added the ability to use local versions of alias and plugin files so pull updates don't clobber local settings. +. 1.3.5 ----- -Added hook system for plugins -Plugins split into seperate files -Aliases and Plugins files renamed with '.ini' file extensions to be editor friendly -Added opacity plugin -Updated filter plugin -Fixed nested custom function parsing (issue #14) +Added hook system for plugins. +Plugins split into seperate files. +Aliases and Plugins files renamed with '.ini' file extensions to be editor friendly. +Added opacity plugin. +Updated filter plugin. +Fixed nested custom function parsing (issue #14). 1.3.4 ----- -Added output_filename option -Added vendor_target option -Renamed 'macros' to the more general 'plugins' and split them into their own files -Removed superfluous outer containing directory (update your include paths) +Added output_filename option. +Added vendor_target option. +Renamed 'macros' to the more general 'plugins' and split them into their own files. +Removed superfluous outer containing directory (update your include paths). 1.3.3 ----- -Fixed regression with absolute URL file imports (issue #12) -Fixed minification bug (issue #13) +Fixed regression with absolute URL file imports (issue #12). +Fixed minification bug (issue #13). 1.3.2 ----- -Updated variable syntax -Fixed minification bug +Updated variable syntax. +Fixed minification bug. 1.3.1 ----- -Added support for svg and svgz data uris -Added animation shorthand alias -Added user-select alias +Added support for svg and svgz data uris. +Added animation shorthand alias. +Added user-select alias. 1.3 --- -Added the public function CssCrush::string for processing raw strings of CSS -Added color functions -Added aliases for IE10 +Added the public function CssCrush::string for processing raw strings of CSS. +Added color functions. +Added aliases for IE10. 1.2 --- -Rewritten the file importer +Rewritten the file importer. 1.1 --- -Added global variables support -Added support for variable interpolation within string literals -Added 'tag' method for outputting an html link tag instead of returning a filename -Added values aliases, dynamic 'runtime' variables -Added RGBA macro -Added IE clip macro -Added data uri function -Minor correction to WAMP support -Minor fix to rule API +Added global variables support. +Added support for variable interpolation within string literals. +Added 'tag' method for outputting an html link tag instead of returning a filename. +Added values aliases, dynamic 'runtime' variables. +Added RGBA macro. +Added IE clip macro. +Added data uri function. +Minor correction to WAMP support. +Minor fix to rule API. 1.0 --- -Major refactoring -Custom functions -Optional boilerplate -Double colon syntax shim -Resolved document root issues -Minification improvements +Major refactoring. +Custom functions. +Optional boilerplate. +Double colon syntax shim. +Resolved document root issues. +Minification improvements. 0.9 --- -Initial release +Initial release. diff --git a/README.md b/README.md index 55ed647..3d0a607 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ CSS Crush -===== +=================================== CSS Crush is an extensible PHP based CSS preprocessor that aims to alleviate many of the hacks and workarounds necessary in modern CSS development. diff --git a/lib/Core.php b/lib/Core.php index 3af5d74..7a9e85a 100644 --- a/lib/Core.php +++ b/lib/Core.php @@ -618,10 +618,11 @@ public static function prepareStream ( &$stream ) { if ( ! $balanced_curlies ) { $parse_errors[] = "Unmatched '{' in $current_file."; } + if ( $parse_errors ) { foreach ( $parse_errors as $error_msg ) { csscrush::logError( $error_msg ); - trigger_error( "$error_msg\n", E_USER_NOTICE ); + trigger_error( "$error_msg\n", E_USER_WARNING ); } return false; } @@ -871,6 +872,9 @@ protected static function compile ( $stream ) { } // Remove option disabled plugins from the list, and disable them. + if ( $options->disable === 'all' ) { + $options->disable = array_keys( $config->plugins ); + } if ( is_array( $options->disable ) ) { foreach ( $options->disable as $plugin_name ) { csscrush_plugin::disable( $plugin_name ); @@ -930,12 +934,6 @@ protected static function compile ( $stream ) { // Print it all back self::display( $stream ); - // Release memory - self::$storage = null; - $process->mixins = null; - $process->abstracts = null; - $process->selectorRelationships = null; - // Add in boilerplate if ( $options->boilerplate ) { $stream = self::getBoilerplate() . "\n$stream"; @@ -946,6 +944,12 @@ protected static function compile ( $stream ) { $stream = "@charset $process->charset;\n" . $stream; } + // Release memory + self::$storage = null; + $process->mixins = null; + $process->abstracts = null; + $process->selectorRelationships = null; + return $stream; } @@ -1148,7 +1152,7 @@ protected static function prefixSelectors ( &$stream ) { $arguments = $arguments->list; $curly_match = csscrush_util::matchBrackets( - $stream, $brackets = array( '{', '}' ), $match_start_pos, true ); + $stream, array( '{', '}' ), $match_start_pos, true ); if ( ! $curly_match || empty( $raw_argument ) ) { // Couldn't match the block diff --git a/lib/Regex.php b/lib/Regex.php index 8d750da..77cb406 100644 --- a/lib/Regex.php +++ b/lib/Regex.php @@ -50,6 +50,10 @@ public static function init () { \{([^{}]*)\} # The declaration block !xS'; + // Balanced bracket matching. + $patt->balancedParens = '!\( (?: (?: (?>[^()]+) | (?R) )* ) \)!xS'; + $patt->balancedCurlies = '!\{ (?: (?: (?>[^{}]+) | (?R) )* ) \}!xS'; + // Tokens $patt->commentToken = '!___c\d+___!'; $patt->stringToken = '!___s\d+___!'; diff --git a/lib/Util.php b/lib/Util.php index 478a09c..a99691c 100644 --- a/lib/Util.php +++ b/lib/Util.php @@ -178,71 +178,41 @@ public static function splitDelimList ( $str, $delim, $fold_in = false, $trim = } - public static function matchBrackets ( $str, $brackets = array( '(', ')' ), $search_pos = 0, $capture_text = false ) { + public static function matchBrackets ( $str, $brackets = array( '(', ')' ), $offset = 0, $capture_text = false ) { list( $opener, $closer ) = $brackets; - $openings = array(); - $closings = array(); - $brake = 50; // Set a limit in the case of errors $match = (object) array(); - $start_index = strpos( $str, $opener, $search_pos ); - $close_index = strpos( $str, $closer, $search_pos ); - - if ( $start_index === false ) { - + if ( strpos( $str, $opener, $offset ) === false ) { return false; } - if ( substr_count( $str, $opener ) !== substr_count( $str, $closer ) ) { - $sample = substr( $str, 0, 25 ); + if ( substr_count( $str, $opener ) !== substr_count( $str, $closer ) ) { + $sample = substr( $str, 0, 25 ); trigger_error( __METHOD__ . ": Unmatched token near '$sample'.\n", E_USER_WARNING ); return false; } - while ( - ( $start_index !== false || $close_index !== false ) && $brake-- - ) { - if ( $start_index !== false && $close_index !== false ) { - $search_pos = min( $start_index, $close_index ); - if ( $start_index < $close_index ) { - $openings[] = $start_index; - } - else { - $closings[] = $close_index; - } - } - elseif ( $start_index !== false ) { - $search_pos = $start_index; - $openings[] = $start_index; - } - else { - $search_pos = $close_index; - $closings[] = $close_index; - } - $search_pos += 1; // Advance - - if ( count( $closings ) === count( $openings ) ) { + $patt = csscrush_regex::$patt->balancedParens; + if ( $opener === '{' ) { + $patt = csscrush_regex::$patt->balancedCurlies; + } - $match->openings = $openings; - $match->start = $start = $openings[0]; - $match->closings = $closings; - $match->end = $closings[ count( $closings ) - 1 ] + 1; + if ( preg_match( $patt, $str, $m, PREG_OFFSET_CAPTURE, $offset ) ) { - if ( $capture_text ) { - // Text capturing is optional to avoid using memory when not necessary - $match->inside = substr( $str, $start + 1, $match->end - $start - 2 ); - $match->after = substr( $str, $match->end ); - } + $match->start = $start = $m[0][1]; + $match->end = $start + strlen( $m[0][0] ); - return $match; + if ( $capture_text ) { + // Text capturing is optional to avoid using memory when not necessary. + $match->inside = substr( $str, $start + 1, $match->end - $start - 2 ); + $match->after = substr( $str, $match->end ); } - $start_index = strpos( $str, $opener, $search_pos ); - $close_index = strpos( $str, $closer, $search_pos ); + return $match; } - trigger_error( __METHOD__ . ": Reached brake limit of '$brake'. Exiting.\n", E_USER_WARNING ); + trigger_error( __METHOD__ . ": Could not match '$opener'. Exiting.\n", E_USER_WARNING ); return false; } @@ -250,45 +220,25 @@ public static function matchBrackets ( $str, $brackets = array( '(', ')' ), $sea public static function matchAllBrackets ( $str, $pair = '()', $offset = 0 ) { $match_obj = (object) array(); - $match_obj->string = $str; - $match_obj->raw = $str; $match_obj->matches = array(); list( $opener, $closer ) = str_split( $pair, 1 ); - // Return early if there's no match - if ( false === ( $first_offset = strpos( $str, $opener, $offset ) ) ) { + // Return early if there's no match. + if ( false === strpos( $str, $opener, $offset ) ) { + $match_obj->string = $str; return $match_obj; } - // Step through the string one character at a time storing offsets - $paren_score = -1; - $inside_paren = false; - $match_start = 0; - $offsets = array(); - - for ( $index = $first_offset; $index < strlen( $str ); $index++ ) { - $char = $str[ $index ]; - - if ( $opener === $char ) { - if ( ! $inside_paren ) { - $paren_score = 1; - $match_start = $index; - } - else { - $paren_score++; - } - $inside_paren = true; - } - elseif ( $closer === $char ) { - $paren_score--; - } + $patt = csscrush_regex::$patt->balancedParens; + if ( $opener === '{' ) { + $patt = csscrush_regex::$patt->balancedCurlies; + } - if ( 0 === $paren_score ) { - $inside_paren = false; - $paren_score = -1; - $offsets[] = array( $match_start, $index + 1 ); - } + // Match balanced bracket pairs. + $offsets = array(); + foreach ( csscrush_regex::matchAll( $patt, $str ) as $m ) { + $offsets[] = array( $m[0][1], $m[0][1] + strlen( $m[0][0] ) ); } // Step backwards through the matches @@ -304,7 +254,7 @@ public static function matchAllBrackets ( $str, $pair = '()', $offset = 0 ) { $str = $before . $label . $after; $match_obj->matches[] = $label; - // Parens will be folded in later + // Parens will be folded in later. csscrush::$storage->tokens->parens[ $label ] = $content; } From 0c96aa87f8a6cbc4bd60ca675ed90af132cf3955 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 21 Sep 2012 15:16:03 +0100 Subject: [PATCH 046/421] Added @ifdefine directive for dynamically including or excluding chunks of CSS based on existence of variables. --- CHANGELOG.txt | 1 + lib/Core.php | 82 ++++++++++++++++++++++++++++++++++++--------------- lib/Regex.php | 18 +++++------ lib/Rule.php | 4 +-- 4 files changed, 70 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 9c2f51e..b731ed5 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,6 +1,7 @@ 1.7 --- Added trace option to output SASS compatible debug-info stubs for use with tools like FireSass. +Added @ifdefine directive for dynamically including/excluding parts of a CSS file based on the existence of variables. Updated plugin API. Added options for enabling and disabling plugins at runtime. Added property sorter plugin. diff --git a/lib/Core.php b/lib/Core.php index 7a9e85a..aa491aa 100644 --- a/lib/Core.php +++ b/lib/Core.php @@ -563,7 +563,6 @@ public static function addTracingStubs ( &$stream ) { } // Get the currently processed file path, and escape it. - // if ( $current_file = csscrush::$process->currentFile ) { $current_file = str_replace( ' ', '%20', csscrush::$process->currentFile ); $current_file = preg_replace( '![^\w-]!', '\\\\$0', $current_file ); @@ -854,7 +853,7 @@ protected static function compile ( $stream ) { $process = self::$process; $options = $process->options; - // Load in aliases and macros + // Load in aliases and plugins. if ( ! self::$assetsLoaded ) { self::loadAssets(); self::$assetsLoaded = true; @@ -887,28 +886,31 @@ protected static function compile ( $stream ) { csscrush_plugin::enable( $plugin_name ); } - // Set aliases + // Set aliases. self::$config->aliases = self::$config->aliasesRaw; - // Prune if a vendor target is set + // Prune if a vendor target is set. self::pruneAliases(); - // Parse variables + // Parse variables. self::extractVariables( $stream ); - // Calculate the variable stack + // Calculate the variable stack. self::calculateVariables(); - // Place the variables + // Apply variables. self::placeVariables( $stream ); - // Pull out the mixin declarations + // Resolve @ifdefine blocks. + self::resolveIfDefines( $stream ); + + // Pull out @mixin definitions. self::extractMixins( $stream ); - // Process fragments + // Pull out @fragment blocks, and invoke. self::resolveFragments( $stream ); - // Adjust the stream so we can extract the rules cleanly + // Adjust the stream so we can extract the rules cleanly. $map = array( '@' => "\n@", '}' => "}\n", @@ -917,34 +919,32 @@ protected static function compile ( $stream ) { ); $stream = "\n" . str_replace( array_keys( $map ), array_values( $map ), $stream ); - // Rules + // Parse rules. self::extractRules( $stream ); - // Process any @-in blocks + // Process @in blocks. self::prefixSelectors( $stream ); - // Main processing on the rule objects + // Main processing on the rule objects. self::processRules(); - // csscrush::log( array_keys( self::$process->selectorRelationships ) ); - - // Alias any @-rules + // Alias any @-rules. self::aliasAtRules( $stream ); - // Print it all back + // Print it all back. self::display( $stream ); - // Add in boilerplate + // Add in boilerplate. if ( $options->boilerplate ) { $stream = self::getBoilerplate() . "\n$stream"; } - // Add @charset at top if set + // Add @charset at top if set. if ( $process->charset ) { $stream = "@charset $process->charset;\n" . $stream; } - // Release memory + // Release memory. self::$storage = null; $process->mixins = null; $process->abstracts = null; @@ -1452,7 +1452,7 @@ public static function extractMixins ( &$stream ) { public static function resolveFragments ( &$stream ) { - $matches = csscrush_regex::matchAll( '@fragment\s+()\s*{', $stream, true ); + $matches = csscrush_regex::matchAll( '@fragment\s+()\s*{', $stream, true ); $fragments = array(); // Move through the matches last to first @@ -1483,7 +1483,7 @@ public static function resolveFragments ( &$stream ) { } // Now find all the fragment calls - $matches = csscrush_regex::matchAll( '@fragment\s+()\s*(\(|;)', $stream, true ); + $matches = csscrush_regex::matchAll( '@fragment\s+()\s*(\(|;)', $stream, true ); // Move through the matches last to first while ( $match = array_pop( $matches ) ) { @@ -1501,7 +1501,6 @@ public static function resolveFragments ( &$stream ) { // Fragment may be called without any argument list $with_arguments = $match[2][0] === '('; - if ( $with_arguments ) { $paren_match = csscrush_util::matchBrackets( $stream, $brackets = array( '(', ')' ), $match_start_pos, true ); @@ -1523,7 +1522,6 @@ public static function resolveFragments ( &$stream ) { if ( $with_arguments ) { // Get the argument array to pass to the fragment $args = csscrush_util::splitDelimList( $paren_match->inside, ',', true, true ); - // $args = array_map( 'trim', $args->list ); $args = $args->list; } @@ -1535,6 +1533,42 @@ public static function resolveFragments ( &$stream ) { } } } + + public static function resolveIfDefines ( &$stream ) { + + $matches = csscrush_regex::matchAll( '@ifdefine\s+(not\s+)?()\s*\{', $stream, true ); + + // Move through the matches last to first. + while ( $match = array_pop( $matches ) ) { + + $full_match = $match[0][0]; + $full_match_start = $match[0][1]; + $before = substr( $stream, 0, $full_match_start ); + + $negate = $match[1][1] != -1; + $name = $match[2][0]; + $name_defined = isset( self::$storage->variables[ $name ] ); + + $curly_match = csscrush_util::matchBrackets( + $stream, $brackets = array( '{', '}' ), $full_match_start, true ); + + if ( ! $curly_match ) { + // Couldn't match the block. + $stream = $before . substr( $stream, $full_match_start + strlen( $full_match ) ); + continue; + } + + if ( ! $negate && $name_defined || $negate && ! $name_defined ) { + // Test resolved true so include the innards. + $stream = $before . $curly_match->inside . $curly_match->after; + } + else { + // Recontruct the stream without the innards. + $stream = $before . $curly_match->after; + } + } + } + } diff --git a/lib/Regex.php b/lib/Regex.php index 77cb406..6039456 100644 --- a/lib/Regex.php +++ b/lib/Regex.php @@ -17,13 +17,13 @@ public static function init () { self::$patt = $patt = (object) array(); self::$class = $class = (object) array(); - // Character classes - $class->name = '[a-zA-Z0-9_-]+'; - $class->notName = '[^a-zA-Z0-9_-]+'; + // Character classes. + $class->ident = '[a-zA-Z0-9_-]+'; + $class->notIdent = '[^a-zA-Z0-9_-]+'; - // Patterns - $patt->name = '!^' . $class->name . '$!'; - $patt->notName = '!^' . $class->notName . '$!'; + // Patterns. + $patt->ident = '!^' . $class->ident . '$!'; + $patt->notIdent = '!^' . $class->notIdent . '$!'; $patt->import = '! @import\s+ # import at-rule @@ -37,7 +37,7 @@ public static function init () { $patt->variables = '!@(?:variables|define)\s*([^\{]*)\{\s*(.*?)\s*\};?!s'; $patt->mixin = '!@mixin\s*([^\{]*)\{\s*(.*?)\s*\};?!s'; - $patt->abstract = csscrush_regex::create( '^@abstract\s+()', 'i' ); + $patt->abstract = csscrush_regex::create( '^@abstract\s+()', 'i' ); $patt->commentAndString = '! (\'|")(?:\\\\\1|[^\1])*?(?:\1|$) # quoted string (to EOF if unmatched) | @@ -86,8 +86,8 @@ public static function create ( $pattern_template, $flags = '' ) { // Sugar $pattern = str_replace( - array( '', '' ), - array( self::$class->name, self::$class->notName ), + array( '', '' ), + array( self::$class->ident, self::$class->notIdent ), $pattern_template ); return '!' . $pattern . "!$flags"; } diff --git a/lib/Rule.php b/lib/Rule.php index 0c005aa..429cb47 100644 --- a/lib/Rule.php +++ b/lib/Rule.php @@ -263,7 +263,7 @@ public function cssQueryFunction ( $input, $fn_name, $call_property ) { $default = isset( $args[0] ) ? $args[0] : null; // Try to match a abstract rule first - if ( preg_match( csscrush_regex::$patt->name, $name ) ) { + if ( preg_match( csscrush_regex::$patt->ident, $name ) ) { // Search order: abstracts, mixins, rules if ( isset( $abstracts[ $name ]->data[ $property ] ) ) { @@ -882,7 +882,7 @@ public function __construct ( $name ) { $this->name = $name; - if ( ! preg_match( csscrush_regex::$patt->name, $this->name ) ) { + if ( ! preg_match( csscrush_regex::$patt->ident, $this->name ) ) { // Not a regular name: Some kind of selector so normalize it for later comparison $this->name = csscrush_selector::makeReadableSelector( $this->name ); From 34aede4f02fbbc7b65d98c3ecc953ffef12691df Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 26 Sep 2012 19:52:21 +0100 Subject: [PATCH 047/421] Tweaking regular expressions. Removing some cruft. Added text-overflow alias for Opera mini support. --- Aliases.ini | 3 +++ lib/Regex.php | 57 +++++++++++++++++++++++++++++---------------------- lib/Util.php | 2 +- 3 files changed, 36 insertions(+), 26 deletions(-) diff --git a/Aliases.ini b/Aliases.ini index 02a2a94..9e89136 100644 --- a/Aliases.ini +++ b/Aliases.ini @@ -148,6 +148,9 @@ text-decoration-line[] = -moz-text-decoration-line text-decoration-style[] = -moz-text-decoration-style + ; Text overflow (Opera mini support) + text-overflow[] = -o-text-overflow + ; Transforms transform[] = -webkit-transform transform[] = -moz-transform diff --git a/lib/Regex.php b/lib/Regex.php index 6039456..9762df8 100644 --- a/lib/Regex.php +++ b/lib/Regex.php @@ -1,7 +1,7 @@ ident = '[a-zA-Z0-9_-]+'; - $class->notIdent = '[^a-zA-Z0-9_-]+'; + $classes->ident = '[a-zA-Z0-9_-]+'; // Patterns. - $patt->ident = '!^' . $class->ident . '$!'; - $patt->notIdent = '!^' . $class->notIdent . '$!'; + $patt->ident = '!^' . $classes->ident . '$!'; $patt->import = '! @import\s+ # import at-rule @@ -33,28 +31,37 @@ public static function init () { ([_s\d]+) # string token ) \s*([^;]*); # media argument - !xS'; + !xiS'; $patt->variables = '!@(?:variables|define)\s*([^\{]*)\{\s*(.*?)\s*\};?!s'; $patt->mixin = '!@mixin\s*([^\{]*)\{\s*(.*?)\s*\};?!s'; $patt->abstract = csscrush_regex::create( '^@abstract\s+()', 'i' ); + $patt->commentAndString = '! - (\'|")(?:\\\\\1|[^\1])*?(?:\1|$) # quoted string (to EOF if unmatched) + # Quoted string (to EOF if unmatched). + (\'|")(?:\\\\\1|[^\1])*?(?:\1|$) + | + # Block comment (to EOF if unmatched). + /\*(?:.*?)(?:\*/|$) + !xsS'; + + // As an exception we treat some @-rules like standard rule blocks. + $patt->rule = '~ + # The selector. + \n( + [^@{}]+ | - /\*(?:.*?)(?:\*/|$) # block comment (to EOF if unmatched) - !xsS'; - - // As an exception we treat some @-rules like standard rule blocks - $patt->rule = '! - (\n(?:[^@{}]+|@(?:font-face|page|abstract)[^{]*)) # The selector - \{([^{}]*)\} # The declaration block - !xS'; + (?: [^@{}]+ )? @(?: font-face|page|abstract ) (?!-)\b [^{]* + ) + # The declaration block. + \{ ( [^{}]* ) \} + ~xiS'; // Balanced bracket matching. $patt->balancedParens = '!\( (?: (?: (?>[^()]+) | (?R) )* ) \)!xS'; $patt->balancedCurlies = '!\{ (?: (?: (?>[^{}]+) | (?R) )* ) \}!xS'; - // Tokens + // Tokens. $patt->commentToken = '!___c\d+___!'; $patt->stringToken = '!___s\d+___!'; $patt->ruleToken = '!___r\d+___!'; @@ -63,11 +70,11 @@ public static function init () { $patt->traceToken = '!___t\d+___!'; $patt->argToken = '!___arg(\d+)___!'; - // Functions + // Functions. $patt->varFunction = '!\$\(\s*([a-z0-9_-]+)\s*\)!iS'; $patt->function = '!(^|[^a-z0-9_-])([a-z_-]+)(___p\d+___)!i'; - // Specific functions + // Specific functions. $patt->argFunction = csscrush_regex::createFunctionMatchPatt( array( 'arg' ) ); $patt->queryFunction = csscrush_regex::createFunctionMatchPatt( array( 'query' ) ); $patt->thisFunction = csscrush_regex::createFunctionMatchPatt( array( 'this' ) ); @@ -84,10 +91,10 @@ public static function init () { public static function create ( $pattern_template, $flags = '' ) { - // Sugar + // Sugar. $pattern = str_replace( - array( '', '' ), - array( self::$class->ident, self::$class->notIdent ), + array( '' ), + array( self::$classes->ident ), $pattern_template ); return '!' . $pattern . "!$flags"; } @@ -96,7 +103,7 @@ public static function create ( $pattern_template, $flags = '' ) { public static function matchAll ( $patt, $subject, $preprocess_patt = false, $offset = 0 ) { if ( $preprocess_patt ) { - // Assume case-insensitive + // Assume case-insensitive. $patt = self::create( $patt, 'i' ); } diff --git a/lib/Util.php b/lib/Util.php index a99691c..43a6433 100644 --- a/lib/Util.php +++ b/lib/Util.php @@ -189,7 +189,7 @@ public static function matchBrackets ( $str, $brackets = array( '(', ')' ), $off } if ( substr_count( $str, $opener ) !== substr_count( $str, $closer ) ) { - $sample = substr( $str, 0, 25 ); + $sample = substr( $str, $offset, 25 ); trigger_error( __METHOD__ . ": Unmatched token near '$sample'.\n", E_USER_WARNING ); return false; } From ee61355d8d107d20ca59e2de55fcf7726301bb82 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 28 Sep 2012 17:24:05 +0100 Subject: [PATCH 048/421] Minor amend for compatibility with IO adapters --- lib/IO.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/IO.php b/lib/IO.php index 6b42e26..4d2137d 100644 --- a/lib/IO.php +++ b/lib/IO.php @@ -151,7 +151,8 @@ public static function validateExistingOutput () { } } - $existing_options = $process->cacheData[ $existingfile->name ][ 'options' ]; + // Cast because the cached options may be a stdClass if an IO adapter has been used. + $existing_options = (array) $process->cacheData[ $existingfile->name ][ 'options' ]; $existing_datesum = $process->cacheData[ $existingfile->name ][ 'datem_sum' ]; $options_unchanged = true; From ef9254a259d32fc5e1b80856dae54294c58b51be Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 3 Oct 2012 17:15:20 +0100 Subject: [PATCH 049/421] Minification improvements. --- CHANGELOG.txt | 5 +++ CssCrush.php | 2 +- lib/Core.php | 88 ++++++++++++++++++++++++++++++------------------ lib/Function.php | 4 +-- lib/Importer.php | 4 +-- lib/Regex.php | 6 ++-- lib/Rule.php | 9 +++-- lib/Util.php | 17 +++++++--- 8 files changed, 88 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index b731ed5..ea31a80 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,8 @@ +1.7.1 +----- +Improved minification. + + 1.7 --- Added trace option to output SASS compatible debug-info stubs for use with tools like FireSass. diff --git a/CssCrush.php b/CssCrush.php index 7cea0fd..9e5fdbf 100644 --- a/CssCrush.php +++ b/CssCrush.php @@ -4,7 +4,7 @@ * CSS Crush * Extensible CSS preprocessor * - * @version 1.7 + * @version 1.7.1 * @link https://github.com/peteboere/css-crush * @license http://www.opensource.org/licenses/mit-license.php (MIT) * @copyright (c) 2010-2012 Pete Boere diff --git a/lib/Core.php b/lib/Core.php index aa491aa..e2be809 100644 --- a/lib/Core.php +++ b/lib/Core.php @@ -931,8 +931,8 @@ protected static function compile ( $stream ) { // Alias any @-rules. self::aliasAtRules( $stream ); - // Print it all back. - self::display( $stream ); + // Print rules, optionally minify. + self::collate( $stream ); // Add in boilerplate. if ( $options->boilerplate ) { @@ -953,58 +953,71 @@ protected static function compile ( $stream ) { return $stream; } - protected static function display ( &$stream ) { + protected static function collate ( &$stream ) { $options = self::$process->options; $minify = ! $options->debug; $regex = csscrush_regex::$patt; if ( $minify ) { - $stream = csscrush_util::stripCommentTokens( $stream ); + + // Strip whitespace around colons used in @-rule arguments. + $stream = preg_replace( '! ?\: ?!', ':', $stream ); + // Strip newlines added during parsing. + $stream = preg_replace( '!\n+!', '', $stream ); } else { - // Formatting + + // Pretty printing. $stream = preg_replace( '!([{}])!', "$1\n", $stream ); + $stream = preg_replace( '!([^\s])\{!', "$1 {", $stream ); $stream = preg_replace( '!([@])!', "\n$1", $stream ); - // Newlines after some tokens + // Newlines after some tokens. $stream = preg_replace( '!(___[rc][0-9]+___)!', "$1\n", $stream ); - // Kill double spaces + // Kill double spaces. $stream = ltrim( preg_replace( '!\n+!', "\n", $stream ) ); } - // Kill leading space + // Kill leading space. $stream = preg_replace( '!\n\s+!', "\n", $stream ); - // Print out rules + // Print out rules. $stream = preg_replace_callback( $regex->ruleToken, array( 'self', 'cb_printRule' ), $stream ); - // Insert parens + // Insert parens. $stream = csscrush_util::strReplaceHash( $stream, self::$storage->tokens->parens ); + // Compress hex-codes, collapse TRBL lists etc. + $stream = self::decruft( $stream ); + if ( $minify ) { - $stream = self::minify( $stream ); + // Trim whitespace around selector combinators. + $stream = preg_replace( '! ?([>~+]) ?!S', '$1', $stream ); } else { - // Insert comments + // Add space after commas. + $stream = str_replace( ',', ', ', $stream ); + + // Insert comments. $comment_labels = array_keys( self::$storage->tokens->comments ); $comment_values = array_values( self::$storage->tokens->comments ); foreach ( $comment_values as &$comment ) { $comment = "$comment\n"; } $stream = str_replace( $comment_labels, $comment_values, $stream ); - // Normalize line breaks + // Normalize line breaks. $stream = preg_replace( '!\n{3,}!', "\n\n", $stream ); } - // Insert URLs + // Insert URLs. if ( self::$storage->tokens->urls ) { - // Clean-up rewritten URLs + // Clean-up rewritten URLs. foreach ( csscrush::$storage->tokens->urls as $token => $url ) { - // Optionally set the URLs to absolute + // Optionally set the URLs to absolute. if ( $options->rewrite_import_urls === 'absolute' && strpos( $url, 'data:' ) !== 0 @@ -1016,23 +1029,34 @@ protected static function display ( &$stream ) { $stream = csscrush_util::strReplaceHash( $stream, self::$storage->tokens->urls ); } - // Insert string literals + // Insert string literals. $stream = csscrush_util::strReplaceHash( $stream, self::$storage->tokens->strings ); - } - protected static function minify ( $str ) { + protected static function decruft ( $str ) { + $replacements = array( - '!\n+| (\{)!' => '$1', // Trim whitespace - '!(^|[: \(,])0(\.\d+)!' => '$1$2', // Strip leading zeros on floats - '!(^|[: \(,])\.?0(?:e[mx]|c[hm]|rem|v[hwm]|in|p[tcx])!i' - => '${1}0', // Strip unnecessary units on zero values for length types - '!(^|\:) *(0 0 0|0 0 0 0) *(;|\})!' => '${1}0${3}', // Collapse zero lists - '!(padding|margin) ?\: *0 0 *(;|\})!' => '${1}:0${2}', // Collapse zero lists continued - '!\s*([>~+=])\s*!' => '$1', // Clean-up around combinators - '!\#([0-9a-f])\1([0-9a-f])\2([0-9a-f])\3!i' - => '#$1$2$3', // Compress hex codes + + // Strip leading zeros on floats. + '!([: \(,])(-?)0(\.\d+)!S' => '$1$2$3', + + // Strip unnecessary units on zero values for length types. + '!([: \(,])\.?0(?:e[mx]|c[hm]|rem|v[hwm]|in|p[tcx])!iS' => '${1}0', + + // Collapse zero lists. + '!(\: *)(?:0 0 0|0 0 0 0) *([;}])!S' => '${1}0$2', + + // Collapse zero lists 2nd pass. + '!(padding|margin|border-radius) ?(\: *)0 0 *([;}])!iS' => '${1}${2}0$3', + + // Dropping redundant trailing zeros on TRBL lists. + '!(\: *)(-?(?:\d+)?\.?\d+[a-z]{1,4}) 0 0 0 *([;}])!iS' => '$1$2 0 0$3', + '!(\: *)0 0 (-?(?:\d+)?\.?\d+[a-z]{1,4}) 0 *([;}])!iS' => '${1}0 0 $2$3', + + // Compress hex codes. + '!\#([0-9a-f])\1([0-9a-f])\2([0-9a-f])\3!iS' => '#$1$2$3', ); + return preg_replace( array_keys( $replacements ), array_values( $replacements ), $str ); } @@ -1270,6 +1294,7 @@ protected static function cb_extractCommentAndString ( $match ) { // Strip private comments $private_comment_marker = '$!'; + // Bail without storing comment if in debug mode or a private comment. if ( strpos( $full_match, '/*' . $private_comment_marker ) === 0 || ! self::$process->options->debug @@ -1288,14 +1313,13 @@ protected static function cb_extractCommentAndString ( $match ) { } else { - $label = csscrush::tokenLabelCreate( 's' ); - // Fix broken strings as they will break any subsquent // imported files that are inlined. if ( $full_match[0] !== $full_match[ strlen( $full_match )-1 ] ) { $full_match .= $full_match[0]; } - csscrush::$storage->tokens->strings[ $label ] = $full_match; + + $label = csscrush_string::add( $full_match ); } return $newlines . $label; @@ -1409,7 +1433,7 @@ protected static function cb_printRule ( $match ) { } // Build the selector; uses selector __toString method - $selectors = implode( ",$whitespace", $rule->selectorList ); + $selectors = implode( ',', $rule->selectorList ); // Build the block $block = array(); diff --git a/lib/Function.php b/lib/Function.php index 357f0b5..97250fb 100644 --- a/lib/Function.php +++ b/lib/Function.php @@ -353,9 +353,9 @@ public static function css_fn__data_uri ( $input ) { $mime_type = $allowed_file_extensions[ $file_ext ]; $base64 = base64_encode( file_get_contents( $file ) ); - $data_uri = "data:{$mime_type};base64,$base64"; + $string_label = csscrush_string::add( "\"data:$mime_type;base64,$base64\"" ); - return "url(http://github.com/%22$data_uri/")"; + return "url(http://github.com/$string_label)"; } public static function css_fn__hsla_adjust ( $input ) { diff --git a/lib/Importer.php b/lib/Importer.php index 801d97f..1a92e60 100644 --- a/lib/Importer.php +++ b/lib/Importer.php @@ -275,8 +275,8 @@ protected static function normalizeImportStatement ( $import_statement ) { else { // @import url(http://github.com/some/path/styles.css); - $string_label = csscrush::tokenLabelCreate( 's' ); - csscrush::$storage->tokens->strings[ $string_label ] = '"' . $the_url . '"'; + $the_url = '"' . $the_url . '"'; + $string_label = csscrush_string::add( $the_url ); $import_statement = str_replace( $full_match, $the_space . $string_label, $import_statement ); } } diff --git a/lib/Regex.php b/lib/Regex.php index 9762df8..ffdbadc 100644 --- a/lib/Regex.php +++ b/lib/Regex.php @@ -54,7 +54,7 @@ public static function init () { (?: [^@{}]+ )? @(?: font-face|page|abstract ) (?!-)\b [^{]* ) # The declaration block. - \{ ( [^{}]* ) \} + \{ ([^{}]*) \} ~xiS'; // Balanced bracket matching. @@ -72,7 +72,7 @@ public static function init () { // Functions. $patt->varFunction = '!\$\(\s*([a-z0-9_-]+)\s*\)!iS'; - $patt->function = '!(^|[^a-z0-9_-])([a-z_-]+)(___p\d+___)!i'; + $patt->function = '!(^|[^a-z0-9_-])([a-z_-]+)(___p\d+___)!iS'; // Specific functions. $patt->argFunction = csscrush_regex::createFunctionMatchPatt( array( 'arg' ) ); @@ -85,7 +85,7 @@ public static function init () { $patt->absoluteUrl = '!^https?://!'; $patt->argListSplit = '!\s*[,\s]\s*!S'; $patt->mathBlacklist = '![^\.0-9\*\/\+\-\(\)]!S'; - $patt->charset = '!@charset\s+(___s\d+___)\s*;!i'; + $patt->charset = '!@charset\s+(___s\d+___)\s*;!iS'; } diff --git a/lib/Rule.php b/lib/Rule.php index 429cb47..2acec15 100644 --- a/lib/Rule.php +++ b/lib/Rule.php @@ -80,7 +80,10 @@ public function __construct ( $selector_string = null, $declarations_string ) { $this->label = csscrush::tokenLabelCreate( 'r' ); // If tracing store the last tracing stub, then strip all. - if ( $options->trace && $trace_tokens = csscrush_regex::matchAll( $regex->traceToken, $selector_string ) ) { + if ( + $options->trace && + $trace_tokens = csscrush_regex::matchAll( $regex->traceToken, $selector_string ) + ) { $trace_token = array_pop( $trace_tokens ); $this->tracingStub = $trace_token[0][0]; $selector_string = preg_replace( $regex->traceToken, '', $selector_string ); @@ -750,7 +753,7 @@ public function __construct ( $prop, $value, $contextIndex = 0 ) { // Check for !important keywords if ( ( $important = strpos( $value, '!important' ) ) !== false ) { - $value = substr( $value, 0, $important ); + $value = rtrim( substr( $value, 0, $important ) ); $important = true; } @@ -820,7 +823,7 @@ public static function makeReadableSelector ( $selector_string ) { } // Create space around combinators, then normalize whitespace - $selector_string = preg_replace( '!([>+~])!', ' $1 ', $selector_string ); + $selector_string = preg_replace( '#([>+]|~(?!=))#', ' $1 ', $selector_string ); $selector_string = csscrush_util::normalizeWhiteSpace( $selector_string ); // Quick test for string tokens diff --git a/lib/Util.php b/lib/Util.php index 43a6433..3ebb656 100644 --- a/lib/Util.php +++ b/lib/Util.php @@ -86,9 +86,12 @@ public static function stripCommentTokens ( $str ) { public static function normalizeWhiteSpace ( $str ) { $replacements = array( - '!\s+!' => ' ', - '!(\[)\s*|\s*(\])|(\()\s*|\s*(\))!' => '${1}${2}${3}${4}', // Trim internal bracket WS - '!\s*(;|,|\/|\!)\s*!' => '$1', // Trim WS around delimiters and special characters + // Convert all whitespace sequences to a single space. + '!\s+!S' => ' ', + // Trim bracket whitespace where it's safe to do it. + '!([\[(]) | ([\])])| ?([{}]) ?!S' => '${1}${2}${3}', + // Trim whitespace around delimiters and special characters. + '! ?([;/,]) ?!S' => '$1', ); return preg_replace( array_keys( $replacements ), array_values( $replacements ), $str ); @@ -241,7 +244,7 @@ public static function matchAllBrackets ( $str, $pair = '()', $offset = 0 ) { $offsets[] = array( $m[0][1], $m[0][1] + strlen( $m[0][0] ) ); } - // Step backwards through the matches + // Step backwards through the matches. while ( $offset = array_pop( $offsets ) ) { list( $start, $finish ) = $offset; @@ -276,6 +279,12 @@ class csscrush_string { public $value; public $quoteMark; + public static function add ( $full_match ) { + $label = csscrush::tokenLabelCreate( 's' ); + csscrush::$storage->tokens->strings[ $label ] = $full_match; + return $label; + } + public function __construct ( $token ) { $this->token = trim( $token ); From 6067f3101cbc8c8b4941e35ce53d9122854d795d Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 18 Oct 2012 16:14:13 +0100 Subject: [PATCH 050/421] Lots of refactoring and cleaning up of internals. --- CHANGELOG.txt | 2 +- CssCrush.php | 2 +- Prepend.css | 2 +- lib/Core.php | 643 +++++++++++++++++++----------------- lib/Function.php | 175 +++------- lib/IO.php | 96 +++--- lib/Importer.php | 346 +++---------------- lib/Mixin.php | 21 +- lib/Regex.php | 34 +- lib/Rule.php | 109 +++--- lib/Util.php | 322 ++++++++++-------- plugins/hsl-to-hex.php | 4 +- plugins/property-sorter.php | 34 +- plugins/rgba-fallback.php | 2 +- 14 files changed, 790 insertions(+), 1002 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index ea31a80..7ab6ea1 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,4 +1,4 @@ -1.7.1 +1.8 ----- Improved minification. diff --git a/CssCrush.php b/CssCrush.php index 9e5fdbf..499275e 100644 --- a/CssCrush.php +++ b/CssCrush.php @@ -4,7 +4,7 @@ * CSS Crush * Extensible CSS preprocessor * - * @version 1.7.1 + * @version 1.8 * @link https://github.com/peteboere/css-crush * @license http://www.opensource.org/licenses/mit-license.php (MIT) * @copyright (c) 2010-2012 Pete Boere diff --git a/Prepend.css b/Prepend.css index 9928ea6..de77113 100644 --- a/Prepend.css +++ b/Prepend.css @@ -1,4 +1,4 @@ -/*$! +/*$ Prepend.css contains library variables by default, but it could also contain reset styles such as reset.css or normalize.css that you would want prepended to every output file. diff --git a/lib/Core.php b/lib/Core.php index e2be809..48afcac 100644 --- a/lib/Core.php +++ b/lib/Core.php @@ -1,50 +1,44 @@ location = dirname( $seed_file ); - // Get version ID from seed file + // Get version ID from seed file. $seed_file_contents = file_get_contents( $seed_file ); $match_count = preg_match( '!@version\s+([\d\.\w-]+)!', $seed_file_contents, $version_match ); self::$config->version = $match_count ? new csscrush_version( $version_match[1] ) : null; - // Set the docRoot reference + // Set the docRoot reference. self::setDocRoot(); - // Set the default IO handler + // Set the default IO handler. self::$config->io = 'csscrush_io'; - // Global storage + // Global storage. self::$config->vars = array(); self::$config->aliases = array(); - self::$config->aliasesRaw = array(); self::$config->plugins = array(); - // Default options + // Default options. self::$config->options = (object) array( // Minify. Set true for formatting and comments @@ -81,7 +75,7 @@ public static function init ( $seed_file ) { 'trace' => false, ); - // Initialise other classes + // Initialise other classes. csscrush_regex::init(); csscrush_function::init(); } @@ -145,9 +139,14 @@ public static function io_call ( $method ) { } - // Aliases and macros loader + // Aliases and macros loader. protected static function loadAssets () { + static $called = false; + if ( $called ) { + return; + } + // Find an aliases file in the root directory // a local file overrides the default $aliases_file = csscrush_util::find( 'Aliases-local.ini', 'Aliases.ini' ); @@ -157,20 +156,20 @@ protected static function loadAssets () { if ( $result = @parse_ini_file( $aliases_file, true ) ) { - self::$config->aliasesRaw = $result; + self::$config->aliases = $result; // Value aliases require a little preprocessing - if ( isset( self::$config->aliasesRaw[ 'values' ] ) ) { + if ( isset( self::$config->aliases[ 'values' ] ) ) { $store = array(); - foreach ( self::$config->aliasesRaw[ 'values' ] as $prop_val => $aliases ) { + foreach ( self::$config->aliases[ 'values' ] as $prop_val => $aliases ) { list( $prop, $value ) = array_map( 'trim', explode( ':', $prop_val ) ); $store[ $prop ][ $value ] = $aliases; } - self::$config->aliasesRaw[ 'values' ] = $store; + self::$config->aliases[ 'values' ] = $store; } // Ensure all alias groups are at least set (issue #34) - self::$config->aliasesRaw += array( + self::$config->aliases += array( 'properties' => array(), 'functions' => array(), 'values' => array(), @@ -204,41 +203,48 @@ protected static function loadAssets () { trigger_error( __METHOD__ . ": Plugin file could not be parsed.\n", E_USER_NOTICE ); } } + + $called = true; } - // Establish the input and output directories and optionally test if output dir writable - protected static function setPath ( $input_dir, $write_test = true ) { + // Establish the input and output directories and optionally test output dir. + protected static function setContext ( $input_dir, $test_output_dir = true ) { $config = self::$config; $process = self::$process; $doc_root = $config->docRoot; if ( strpos( $input_dir, $doc_root ) !== 0 ) { - // Not a system path + // Not a system path. $input_dir = realpath( "$doc_root/$input_dir" ); } - // Store input directory - $process->inputDir = $input_dir; - $process->inputDirUrl = substr( $process->inputDir, strlen( $doc_root ) ); + // Initialise input object and store input directory. + $process->input->path = null; + $process->input->filename = null; + $process->input->dir = $input_dir; + $process->input->dirUrl = substr( $process->input->dir, strlen( $doc_root ) ); - // Store reference to the output dir - $process->outputDir = csscrush::io_call( 'getOutputDir' ); - $process->outputDirUrl = substr( $process->outputDir, strlen( $doc_root ) ); + // Store reference to the output dir. + $process->output->dir = csscrush::io_call( 'getOutputDir' ); + $process->output->dirUrl = substr( $process->output->dir, strlen( $doc_root ) ); - // Test the output directory to see if it's writable - $pathtest = csscrush::io_call( 'testOutputDir', $write_test ); + // Test the output directory to see it exists and is writable. + $pathtest = false; + if ( $test_output_dir ) { + $pathtest = csscrush::io_call( 'testOutputDir' ); + } - // Setup the IO handler + // Setup the IO handler. csscrush::io_call( 'init' ); return $pathtest; } - ############# - # Public API + ############################# + # External API. /** * Process host CSS file and return a new compiled file @@ -257,45 +263,45 @@ public static function file ( $file, $options = null ) { $options = $process->options; $doc_root = $config->docRoot; - // Since we're comparing strings, we need to iron out OS differences + // Since we're comparing strings, we need to iron out OS differences. $file = str_replace( '\\', '/', $file ); - // Finding the system path of the input file and validating it + // Finding the system path of the input file and validating it. $pathtest = true; if ( strpos( $file, $doc_root ) === 0 ) { - // System path - $pathtest = self::setPath( dirname( $file ) ); + // System path. + $pathtest = self::setContext( dirname( $file ) ); } else if ( strpos( $file, '/' ) === 0 ) { - // WWW root path - $pathtest = self::setPath( dirname( $doc_root . $file ) ); + // WWW root path. + $pathtest = self::setContext( dirname( $doc_root . $file ) ); } else { - // Relative path - $pathtest = self::setPath( dirname( dirname( __FILE__ ) . '/' . $file ) ); + // Relative path. + $pathtest = self::setContext( dirname( dirname( __FILE__ ) . '/' . $file ) ); } if ( ! $pathtest ) { - // Main directory not found or is not writable return an empty string + // Main directory not found or is not writable return an empty string. return ''; } - // Get the input file object - if ( ! ( $process->input = csscrush::io_call( 'getInput', $file ) ) ) { + // Validate file input. + if ( ! csscrush_io::registerInputFile( $file ) ) { return ''; } // Create a filename that will be used later // Used in validateCache, and writing to filesystem - $process->outputFileName = csscrush::io_call( 'getOutputFileName' ); + $process->output->filename = csscrush::io_call( 'getOutputFileName' ); // Caching. if ( $options->cache ) { - // Load the cache data + // Load the cache data. $process->cacheData = csscrush::io_call( 'getCacheData' ); - // If cache is enabled check for a valid compiled file + // If cache is enabled check for a valid compiled file. $valid_compliled_file = csscrush::io_call( 'validateExistingOutput' ); if ( is_string( $valid_compliled_file ) ) { @@ -309,10 +315,10 @@ public static function file ( $file, $options = null ) { // Compile. $stream = self::compile( $stream ); - // Create file and return url. Return empty string on failure - if ( file_put_contents( "$process->outputDir/$process->outputFileName", $stream ) ) { + // Create file and return url. Return empty string on failure. + if ( file_put_contents( "{$process->output->dir}/{$process->output->filename}", $stream ) ) { $timestamp = $options->versioning ? '?' . time() : ''; - return "$process->outputDirUrl/$process->outputFileName$timestamp"; + return "{$process->output->dirUrl}/{$process->output->filename}$timestamp"; } else { return ''; @@ -376,7 +382,8 @@ public static function inline ( $file, $options = null, $attributes = array() ) if ( ! empty( $file ) ) { // On success fetch the CSS text - $content = file_get_contents( self::$process->outputDir . '/' . self::$process->outputFileName ); + $content = file_get_contents( self::$process->output->dir . '/' + . self::$process->output->filename ); $tag_open = ''; $tag_close = ''; @@ -420,16 +427,13 @@ public static function string ( $string, $options = null ) { // Set the path context if one is given. // Fallback to document root. if ( ! empty( $options->context ) ) { - self::setPath( $options->context ); + self::setContext( $options->context, false ); } else { - self::setPath( $config->docRoot ); + self::setContext( $config->docRoot, false ); } - // It's not associated with a real file so we create an 'empty' input object. - $process->input = csscrush::io_call( 'getInput' ); - - // Set the string on the object + // Set the string on the input object. $process->input->string = $string; // Import files may be ignored @@ -479,8 +483,8 @@ public static function clearCache ( $dir = '' ) { } - ##################### - # Developer related + ############################# + # Internal development. public static $logging = false; @@ -520,13 +524,96 @@ public static function logError ( $msg ) { } - ##################### - # Internal functions + ############################# + # Internal functions. + + public static function prepareStream ( &$stream ) { + + $regex = csscrush_regex::$patt; + $process = csscrush::$process; + $trace = $process->options->trace; + + $stream = preg_replace_callback( $regex->commentAndString, + array( 'self', 'cb_extractCommentAndString' ), $stream ); + + // If @charset is set store it. + if ( preg_match( $regex->charset, $stream, $m ) ) { + $replace = ''; + if ( ! $process->charset ) { + // Keep track of newlines for line tracing. + $replace = str_repeat( "\n", substr_count( $m[0], "\n" ) ); + $process->charset = trim( csscrush::tokenFetch( $m[1] ), '"\'' ); + } + $stream = preg_replace( $regex->charset, $replace, $stream ); + } + + // Catch obvious typing errors. + $parse_errors = array(); + $current_file = $process->currentFile; + $balanced_parens = substr_count( $stream, "(" ) === substr_count( $stream, ")" ); + $balanced_curlies = substr_count( $stream, "{" ) === substr_count( $stream, "}" ); + + if ( ! $balanced_parens ) { + $parse_errors[] = "Unmatched '(' in $current_file."; + } + if ( ! $balanced_curlies ) { + $parse_errors[] = "Unmatched '{' in $current_file."; + } + + if ( $parse_errors ) { + foreach ( $parse_errors as $error_msg ) { + csscrush::logError( $error_msg ); + trigger_error( "$error_msg\n", E_USER_WARNING ); + } + return false; + } + + // Optionally add tracing stubs. + if ( $trace ) { + self::addTracingStubs( $stream ); + } + + // Strip unneeded whitespace. + $stream = csscrush_util::normalizeWhiteSpace( $stream ); + + // Tokenize all the URLs. + $patt = '# + @import\x20(\?s\d+\?) + | + (?label, $stream ); + } + // Match parenthesis if not a string token. + elseif ( preg_match( $regex->balancedParens, $stream, $inner_m, PREG_OFFSET_CAPTURE, $outer_offset ) ) { + $url = new csscrush_url(http://github.com/$inner_m[1][0]); + $func_name = strtolower( $outer_m[2][0] ); + $url->convertToData = 'data-uri' === $func_name; + $stream = substr_replace( $stream, $url->label, $outer_offset, + strlen( $func_name ) + strlen( $inner_m[0][0] ) ); + } + // If brackets cannot be matched, skip over the original match. + else { + $offset += strlen( $outer_m[0][0] ); + } + } + + return true; + } public static function addTracingStubs ( &$stream ) { $selector_patt = '! (^|;|\})+ ([^;{}]+) (\{) !xmS'; - $token_or_whitespace = '!(\s*___c\d+___\s*|\s+)!'; + $token_or_whitespace = '!(\s*\?c\d+\?\s*|\s+)!'; $matches = csscrush_regex::matchAll( $selector_patt, $stream ); @@ -569,7 +656,7 @@ public static function addTracingStubs ( &$stream ) { // Splice in tracing stub. $label = csscrush::tokenLabelCreate( 't' ); $stream = $stream_before . "$label" . substr( $stream, $selector_index ); - self::$storage->tokens->traces[ $label ] + self::$process->tokens->t[ $label ] = "@media -sass-debug-info{filename{font-family:$current_file}line{font-family:\\00003$line_num}}"; } @@ -586,56 +673,6 @@ public static function addTracingStubs ( &$stream ) { } } - public static function prepareStream ( &$stream ) { - - $regex = csscrush_regex::$patt; - $process = csscrush::$process; - $trace = $process->options->trace; - - $stream = preg_replace_callback( csscrush_regex::$patt->commentAndString, - array( 'self', 'cb_extractCommentAndString' ), $stream ); - - // If @charset is set store it. - if ( preg_match( $regex->charset, $stream, $m ) ) { - $replace = ''; - if ( ! $process->charset ) { - $replace = str_repeat( "\n", substr_count( $m[0], "\n" ) ); - $process->charset = new csscrush_string( $m[1] ); - } - $stream = preg_replace( $regex->charset, $replace, $stream ); - } - - // Catch obvious typing errors. - $parse_errors = array(); - $current_file = $process->currentFile; - $balanced_parens = substr_count( $stream, "(" ) === substr_count( $stream, ")" ); - $balanced_curlies = substr_count( $stream, "{" ) === substr_count( $stream, "}" ); - - if ( ! $balanced_parens ) { - $parse_errors[] = "Unmatched '(' in $current_file."; - } - if ( ! $balanced_curlies ) { - $parse_errors[] = "Unmatched '{' in $current_file."; - } - - if ( $parse_errors ) { - foreach ( $parse_errors as $error_msg ) { - csscrush::logError( $error_msg ); - trigger_error( "$error_msg\n", E_USER_WARNING ); - } - return false; - } - - // Optionally add tracing stubs. - if ( $trace ) { - self::addTracingStubs( $stream ); - } - - $stream = csscrush_util::normalizeWhiteSpace( $stream ); - - return true; - } - protected static function getBoilerplate () { $file = false; @@ -719,7 +756,7 @@ protected static function pruneAliases () { // For expicit 'none' argument turn off aliases if ( 'none' === $vendor ) { - self::$config->aliases = null; + self::$process->aliases = array(); return; } @@ -727,7 +764,7 @@ protected static function pruneAliases () { $vendor = '-' . str_replace( '-', '', $vendor ) . '-'; // Loop the aliases array, filter down to the target vendor - foreach ( self::$config->aliases as $group_name => $group_array ) { + foreach ( self::$process->aliases as $group_name => $group_array ) { // Property/value aliases are a special case if ( 'values' === $group_name ) { @@ -740,7 +777,7 @@ protected static function pruneAliases () { } } } - self::$config->aliases[ 'values' ][ $property ][ $value ] = $result; + self::$process->aliases[ 'values' ][ $property ][ $value ] = $result; } continue; } @@ -754,10 +791,10 @@ protected static function pruneAliases () { } // Prune the whole alias keyword if there is no result if ( empty( $result ) ) { - unset( self::$config->aliases[ $group_name ][ $alias_keyword ] ); + unset( self::$process->aliases[ $group_name ][ $alias_keyword ] ); } else { - self::$config->aliases[ $group_name ][ $alias_keyword ] = $result; + self::$process->aliases[ $group_name ][ $alias_keyword ] = $result; } } } @@ -769,16 +806,16 @@ protected static function calculateVariables () { // In-file variables override global variables // Runtime variables override in-file variables - self::$storage->variables = array_merge( self::$config->vars, self::$storage->variables ); + self::$process->variables = array_merge( self::$config->vars, self::$process->variables ); if ( ! empty( $options->vars ) ) { - self::$storage->variables = array_merge( - self::$storage->variables, $options->vars ); + self::$process->variables = array_merge( + self::$process->variables, $options->vars ); } // Place variables referenced inside variables // Excecute any custom functions - foreach ( self::$storage->variables as $name => &$value ) { + foreach ( self::$process->variables as $name => &$value ) { // Referenced variables $value = preg_replace_callback( csscrush_regex::$patt->varFunction, array( 'self', 'cb_placeVariables' ), $value ); @@ -794,7 +831,7 @@ protected static function calculateVariables () { } } - protected static function placeVariables ( &$stream ) { + public static function placeVariables ( &$stream ) { // Substitute simple case variables $stream = preg_replace_callback( @@ -806,7 +843,7 @@ protected static function placeVariables ( &$stream ) { csscrush_function::executeCustomFunctions( $stream, $var_fn_patt, $var_fn_callback ); // Repeat above steps for variables embedded in string tokens - foreach ( self::$storage->tokens->strings as $label => &$string ) { + foreach ( self::$process->tokens->s as $label => &$string ) { if ( strpos( $string, '$(' ) !== false ) { @@ -820,31 +857,36 @@ protected static function placeVariables ( &$stream ) { protected static function reset ( $options = null ) { - // Reset properties for current process - self::$tokenUID = 0; - - self::$process = (object) array(); - self::$process->cacheData = array(); - self::$process->mixins = array(); - self::$process->abstracts = array(); - self::$process->errors = array(); - self::$process->selectorRelationships = array(); - self::$process->charset = null; - self::$process->currentFile = null; - self::$process->options = self::getOptions( $options ); - - self::$storage = (object) array(); - self::$storage->tokens = (object) array( - 'strings' => array(), - 'comments' => array(), - 'rules' => array(), - 'parens' => array(), - 'mixinArgs' => array(), - 'urls' => array(), - 'traces' => array(), + // Load in aliases and plugins. + self::loadAssets(); + + // Reset properties for current process. + self::$process = $process = (object) array(); + $process->uid = 0; + $process->cacheData = array(); + $process->mixins = array(); + $process->abstracts = array(); + $process->errors = array(); + $process->selectorRelationships = array(); + $process->charset = null; + $process->currentFile = null; + $process->options = self::getOptions( $options ); + $process->tokens = (object) array( + 's' => array(), // Strings + 'c' => array(), // Comments + 'r' => array(), // Rules + 'p' => array(), // Parens + 'u' => array(), // URLs + 't' => array(), // Traces ); - self::$storage->variables = array(); - self::$storage->misc = (object) array(); + $process->variables = array(); + $process->misc = (object) array(); + $process->input = (object) array(); + $process->output = (object) array(); + + // Copy config values. + $process->plugins = self::$config->plugins; + $process->aliases = self::$config->aliases; } protected static function compile ( $stream ) { @@ -853,16 +895,7 @@ protected static function compile ( $stream ) { $process = self::$process; $options = $process->options; - // Load in aliases and plugins. - if ( ! self::$assetsLoaded ) { - self::loadAssets(); - self::$assetsLoaded = true; - } - // Load and unload plugins. - // First copy the base $config->plugins list. - $process->plugins = $config->plugins; - // Add option enabled plugins to the list. if ( is_array( $options->enable ) ) { foreach ( $options->enable as $plugin_name ) { @@ -886,9 +919,6 @@ protected static function compile ( $stream ) { csscrush_plugin::enable( $plugin_name ); } - // Set aliases. - self::$config->aliases = self::$config->aliasesRaw; - // Prune if a vendor target is set. self::pruneAliases(); @@ -941,21 +971,28 @@ protected static function compile ( $stream ) { // Add @charset at top if set. if ( $process->charset ) { - $stream = "@charset $process->charset;\n" . $stream; + $stream = "@charset \"$process->charset\";\n" . $stream; } // Release memory. - self::$storage = null; - $process->mixins = null; - $process->abstracts = null; - $process->selectorRelationships = null; + unset( + $process->tokens, + $process->variables, + $process->mixins, + $process->abstracts, + $process->selectorRelationships, + $process->misc, + $process->plugins, + $process->aliases + ); return $stream; } protected static function collate ( &$stream ) { - $options = self::$process->options; + $process = self::$process; + $options = $process->options; $minify = ! $options->debug; $regex = csscrush_regex::$patt; @@ -974,7 +1011,7 @@ protected static function collate ( &$stream ) { $stream = preg_replace( '!([@])!', "\n$1", $stream ); // Newlines after some tokens. - $stream = preg_replace( '!(___[rc][0-9]+___)!', "$1\n", $stream ); + $stream = preg_replace( '!(\?[rc][0-9]+\?)!', "$1\n", $stream ); // Kill double spaces. $stream = ltrim( preg_replace( '!\n+!', "\n", $stream ) ); @@ -984,10 +1021,10 @@ protected static function collate ( &$stream ) { $stream = preg_replace( '!\n\s+!', "\n", $stream ); // Print out rules. - $stream = preg_replace_callback( $regex->ruleToken, array( 'self', 'cb_printRule' ), $stream ); + $stream = csscrush_util::strReplaceHash( $stream, $process->tokens->r ); // Insert parens. - $stream = csscrush_util::strReplaceHash( $stream, self::$storage->tokens->parens ); + $stream = csscrush_util::strReplaceHash( $stream, $process->tokens->p ); // Compress hex-codes, collapse TRBL lists etc. $stream = self::decruft( $stream ); @@ -1001,36 +1038,48 @@ protected static function collate ( &$stream ) { $stream = str_replace( ',', ', ', $stream ); // Insert comments. - $comment_labels = array_keys( self::$storage->tokens->comments ); - $comment_values = array_values( self::$storage->tokens->comments ); - foreach ( $comment_values as &$comment ) { - $comment = "$comment\n"; + foreach ( $process->tokens->c as $token => &$comment ) { + $comment .= "\n"; } - $stream = str_replace( $comment_labels, $comment_values, $stream ); + $stream = csscrush_util::strReplaceHash( $stream, $process->tokens->c ); + // Normalize line breaks. $stream = preg_replace( '!\n{3,}!', "\n\n", $stream ); } // Insert URLs. - if ( self::$storage->tokens->urls ) { + $link = csscrush_util::getLinkBetweenDirs( $process->output->dir, $process->input->dir ); - // Clean-up rewritten URLs. - foreach ( csscrush::$storage->tokens->urls as $token => $url ) { + if ( $process->tokens->u ) { + foreach ( $process->tokens->u as $token => $url ) { - // Optionally set the URLs to absolute. - if ( - $options->rewrite_import_urls === 'absolute' && - strpos( $url, 'data:' ) !== 0 - ) { - $url = self::$process->inputDirUrl . '/' . $url; + if ( strpos( $url->value, '$(' ) === 0 ) { + $url->applyVariables(); + } + + if ( $url->isRelative ) { + // Optionally set the URLs to absolute. + if ( $options->rewrite_import_urls === 'absolute' ) { + $url->prepend( $process->input->dirUrl . '/' ); + } + // If output dir is different to input dir prepend a link between the two. + elseif ( $link ) { + $url->prepend( $link ); + } + } + + if ( $url->convertToData ) { + $url->toData(); + } + else { + $url->simplify(); } - csscrush::$storage->tokens->urls[ $token ] = csscrush_util::cleanUpUrl( $url ); } - $stream = csscrush_util::strReplaceHash( $stream, self::$storage->tokens->urls ); + $stream = csscrush_util::strReplaceHash( $stream, $process->tokens->u ); } // Insert string literals. - $stream = csscrush_util::strReplaceHash( $stream, self::$storage->tokens->strings ); + $stream = csscrush_util::strReplaceHash( $stream, $process->tokens->s ); } protected static function decruft ( $str ) { @@ -1063,11 +1112,11 @@ protected static function decruft ( $str ) { protected static function aliasAtRules ( &$stream ) { - if ( empty( self::$config->aliases[ 'at-rules' ] ) ) { + if ( empty( self::$process->aliases[ 'at-rules' ] ) ) { return; } - $aliases = self::$config->aliases[ 'at-rules' ]; + $aliases = self::$process->aliases[ 'at-rules' ]; foreach ( $aliases as $at_rule => $at_rule_aliases ) { if ( @@ -1086,7 +1135,7 @@ protected static function aliasAtRules ( &$stream ) { $block_start_pos = $match[0][1]; // Capture the curly bracketed block - $curly_match = csscrush_util::matchBrackets( $stream, $brackets = array( '{', '}' ), $block_start_pos ); + $curly_match = csscrush_util::matchBrackets( $stream, '{}', $block_start_pos ); if ( ! $curly_match ) { // Couldn't match the block @@ -1109,14 +1158,14 @@ protected static function aliasAtRules ( &$stream ) { $vendor = $vendor ? $vendor[1] : null; // Duplicate rules - if ( preg_match_all( csscrush_regex::$patt->ruleToken, $copy_block, $copy_matches ) ) { + if ( preg_match_all( csscrush_regex::$patt->rToken, $copy_block, $copy_matches ) ) { $originals = array(); $replacements = array(); foreach ( $copy_matches[0] as $copy_match ) { // Clone the matched rule $originals[] = $rule_label = $copy_match; - $cloneRule = clone self::$storage->tokens->rules[ $rule_label ]; + $cloneRule = clone self::$process->tokens->r[ $rule_label ]; // Set the vendor context $cloneRule->vendorContext = $vendor; @@ -1132,7 +1181,7 @@ protected static function aliasAtRules ( &$stream ) { // Store the clone $replacements[] = $clone_rule_label = self::tokenLabelCreate( 'r' ); - self::$storage->tokens->rules[ $clone_rule_label ] = $cloneRule; + self::$process->tokens->r[ $clone_rule_label ] = $cloneRule; } // Finally replace the original labels with the cloned rule labels $copy_block = str_replace( $originals, $replacements, $copy_block ); @@ -1144,11 +1193,8 @@ protected static function aliasAtRules ( &$stream ) { $blocks[] = $original_block; $blocks = implode( "\n", $blocks ); - // Glue back together - $stream = - substr( $stream, 0, $block_start_pos ) . - $blocks . - substr( $stream, $block_end_pos ); + // Splice in the blocks. + $stream = substr_replace( $stream, $blocks, $block_start_pos, $block_end_pos - $block_start_pos ); // Move the regex pointer forward $scan_pos = $block_start_pos + strlen( $blocks ); @@ -1172,11 +1218,10 @@ protected static function prefixSelectors ( &$stream ) { $raw_argument = trim( $match[1][0] ); - $arguments = csscrush_util::splitDelimList( $match[1][0], ',', false, true ); - $arguments = $arguments->list; + csscrush::captureParens( $match[1][0] ); + $arguments = csscrush_util::splitDelimList( $match[1][0] ); - $curly_match = csscrush_util::matchBrackets( - $stream, array( '{', '}' ), $match_start_pos, true ); + $curly_match = csscrush_util::matchBrackets( $stream, '{}', $match_start_pos, true ); if ( ! $curly_match || empty( $raw_argument ) ) { // Couldn't match the block @@ -1186,7 +1231,7 @@ protected static function prefixSelectors ( &$stream ) { // Match all the rule tokens $rule_matches = csscrush_regex::matchAll( - csscrush_regex::$patt->ruleToken, + csscrush_regex::$patt->rToken, $curly_match->inside ); foreach ( $rule_matches as $rule_match ) { @@ -1226,7 +1271,8 @@ protected static function prefixSelectors ( &$stream ) { else { // Not storing the selector as named - $new_selector_list[] = new csscrush_selector( "$arg_selector {$rule_selector->value}" ); + $new_selector_list[] + = new csscrush_selector( "$arg_selector {$rule_selector->value}" ); } } } @@ -1238,19 +1284,14 @@ protected static function prefixSelectors ( &$stream ) { } } - public static function tokenLabelCreate ( $prefix ) { - $counter = ++self::$tokenUID; - return "___$prefix{$counter}___"; - } - public static function processRules () { // Reset the selector relationships self::$process->selectorRelationships = array(); - $aliases =& self::$config->aliases; + $aliases =& self::$process->aliases; - foreach ( self::$storage->tokens->rules as $rule ) { + foreach ( self::$process->tokens->r as $rule ) { // Store selector relationships $rule->indexSelectors(); @@ -1280,7 +1321,52 @@ public static function processRules () { ############################# - # preg_replace callbacks + # Tokens. + + public static function tokenLabelCreate ( $type ) { + $counter = ++self::$process->uid; + return "?$type$counter?"; + } + + public static function tokenFetch ( $token ) { + $type = substr( $token, 1, 1 ); + $path =& self::$process->tokens->{ $type }; + if ( isset( $path[ $token ] ) ) { + return $path[ $token ]; + } + return null; + } + + public static function tokenAdd ( $value, $type ) { + $label = self::tokenLabelCreate( $type ); + self::$process->tokens->{ $type }[ $label ] = $value; + return $label; + } + + public static function tokenRelease ( $token ) { + unset( self::$process->tokens->{ substr( $token, 1, 1 ) }[ $token ] ); + } + + public static function tokenRestoreAll ( $str, $type = 'p' ) { + + // Reference the token table. + $token_table =& csscrush::$process->tokens->{ $type }; + + // Find matching tokens. + $matches = csscrush_regex::matchAll( csscrush_regex::$patt->{ "{$type}Token" }, $str ); + + foreach ( $matches as $m ) { + $token = $m[0][0]; + if ( isset( $token_table[ $token ] ) ) { + $str = str_replace( $token, $token_table[ $token ], $str ); + } + } + return $str; + } + + + ############################# + # preg_replace callbacks. protected static function cb_extractCommentAndString ( $match ) { @@ -1292,7 +1378,7 @@ protected static function cb_extractCommentAndString ( $match ) { if ( strpos( $full_match, '/*' ) === 0 ) { // Strip private comments - $private_comment_marker = '$!'; + $private_comment_marker = '$'; // Bail without storing comment if in debug mode or a private comment. if ( @@ -1302,14 +1388,12 @@ protected static function cb_extractCommentAndString ( $match ) { return $newlines; } - $label = self::tokenLabelCreate( 'c' ); - // Fix broken comments as they will break any subsquent // imported files that are inlined. if ( ! preg_match( '!\*/$!', $full_match ) ) { $full_match .= '*/'; } - self::$storage->tokens->comments[ $label ] = $full_match; + $label = csscrush::tokenAdd( $full_match, 'c' ); } else { @@ -1318,8 +1402,7 @@ protected static function cb_extractCommentAndString ( $match ) { if ( $full_match[0] !== $full_match[ strlen( $full_match )-1 ] ) { $full_match .= $full_match[0]; } - - $label = csscrush_string::add( $full_match ); + $label = csscrush::tokenAdd( $full_match, 's' ); } return $newlines . $label; @@ -1341,23 +1424,20 @@ protected static function cb_extractVariables ( $match ) { $regex = csscrush_regex::$patt; - $block = $match[2]; - - // Strip comment markers - $block = csscrush_util::stripCommentTokens( $block ); + // Strip comment markers. + $block = trim( csscrush_util::stripCommentTokens( $match[2] ) ); - // Need to split safely as there are semi-colons in data-uris - $variables_match = csscrush_util::splitDelimList( $block, ';', true ); + $pairs = preg_split( '!\s*;\s*!', $block, null, PREG_SPLIT_NO_EMPTY ); - // Loop through the pairs - foreach ( $variables_match->list as $var ) { + // Loop through the pairs. + foreach ( $pairs as $var ) { $colon = strpos( $var, ':' ); if ( $colon === -1 ) { continue; } $name = trim( substr( $var, 0, $colon ) ); $value = trim( substr( $var, $colon + 1 ) ); - self::$storage->variables[ trim( $name ) ] = $value; + self::$process->variables[ trim( $name ) ] = $value; } return ''; } @@ -1366,8 +1446,8 @@ protected static function cb_placeVariables ( $match ) { $variable_name = $match[1]; - if ( isset( self::$storage->variables[ $variable_name ] ) ) { - return self::$storage->variables[ $variable_name ]; + if ( isset( self::$process->variables[ $variable_name ] ) ) { + return self::$process->variables[ $variable_name ]; } } @@ -1375,9 +1455,9 @@ public static function cb_varFunctionWithDefault ( $raw_argument ) { list( $name, $default_value ) = csscrush_function::parseArgsSimple( $raw_argument ); - if ( isset( self::$storage->variables[ $name ] ) ) { + if ( isset( self::$process->variables[ $name ] ) ) { - return self::$storage->variables[ $name ]; + return self::$process->variables[ $name ]; } else { return $default_value; @@ -1397,71 +1477,46 @@ protected static function cb_extractRules ( $match ) { // Store rules if they have declarations or extend arguments if ( count( $rule ) || $rule->extendArgs ) { - $label = $rule->label; - - self::$storage->tokens->rules[ $label ] = $rule; + self::$process->tokens->r[ $rule->label ] = $rule; // If only using extend still return a label - return $label . "\n"; + return $rule->label . "\n"; } return ''; } - protected static function cb_printRule ( $match ) { - - $minify = ! self::$process->options->debug; - $whitespace = $minify ? '' : ' '; - - $ruleLabel = $match[0]; - // If no rule matches the label return empty string - if ( ! isset( self::$storage->tokens->rules[ $ruleLabel ] ) ) { - return ''; - } + ############################# + # Parsing methods. - $rule =& self::$storage->tokens->rules[ $ruleLabel ]; + public static function captureParens ( &$str ) { - // Tracing stubs. - $tracing_stub = ''; - if ( $rule->tracingStub ) { - $tracing_stub =& self::$storage->tokens->traces[ $rule->tracingStub ]; - } + // PHP >= 5.3 + // $str = preg_replace_callback( csscrush_regex::$patt->balancedParens, function ( $m ) { + // return csscrush::tokenAdd( $m[0], 'p' ); + // }, $str ); - // If there are no selectors or declarations associated with the rule return empty string - if ( empty( $rule->selectorList ) || ! count( $rule ) ) { - return ''; + while ( preg_match( csscrush_regex::$patt->balancedParens, $str, $m, PREG_OFFSET_CAPTURE ) ) { + $label = csscrush::tokenAdd( $m[0][0], 'p' ); + $str = substr_replace( $str, $label, $m[0][1], strlen( $m[0][0] ) ); } + } - // Build the selector; uses selector __toString method - $selectors = implode( ',', $rule->selectorList ); + public static function restoreParens ( &$str, $release = true ) { - // Build the block - $block = array(); - foreach ( $rule as $declaration ) { - $important = $declaration->important ? "$whitespace!important" : ''; - $block[] = "$declaration->property:{$whitespace}$declaration->value{$important}"; - } + $token_table =& csscrush::$process->tokens->p; - // Return whole rule - if ( $minify ) { - $block = implode( ';', $block ); - return "$tracing_stub$selectors{{$block}}"; - } - else { - // Include pre-rule comments. - $comments = implode( "\n", $rule->comments ); - if ( $tracing_stub ) { - $tracing_stub .= "\n"; + foreach ( csscrush_regex::matchAll( csscrush_regex::$patt->pToken, $str ) as $m ) { + $token = $m[0][0]; + if ( isset( $token_table[ $token ] ) ) { + $str = str_replace( $token, $token_table[ $token ], $str ); + if ( $release ) { + unset( $token_table[ $token ] ); + } } - $block = implode( ";\n\t", $block ); - return "$comments\n$tracing_stub$selectors {\n\t$block;\n\t}\n"; } } - - ############ - # Parsing methods - public static function extractRules ( &$stream ) { $stream = preg_replace_callback( csscrush_regex::$patt->rule, array( 'self', 'cb_extractRules' ), $stream ); } @@ -1488,16 +1543,15 @@ public static function resolveFragments ( &$stream ) { $match_length = strlen( $match_string ); $before = substr( $stream, 0, $match_start_pos ); - $curly_match = csscrush_util::matchBrackets( - $stream, $brackets = array( '{', '}' ), $match_start_pos, true ); + $curly_match = csscrush_util::matchBrackets( $stream, '{}', $match_start_pos, true ); if ( ! $curly_match ) { - // Couldn't match the block - $stream = $before . substr( $stream, $match_start_pos + $match_length ); + // Couldn't match the block. + $stream = substr_replace( $stream, '', $match_start_pos, $match_length ); continue; } else { - // Recontruct the stream without the fragment + // Reconstruct the stream without the fragment. $stream = $before . $curly_match->after; // Create the fragment and store it @@ -1526,8 +1580,7 @@ public static function resolveFragments ( &$stream ) { $with_arguments = $match[2][0] === '('; if ( $with_arguments ) { - $paren_match = csscrush_util::matchBrackets( - $stream, $brackets = array( '(', ')' ), $match_start_pos, true ); + $paren_match = csscrush_util::matchBrackets( $stream, '()', $match_start_pos, true ); $after = ltrim( $paren_match->after, ';' ); } else { @@ -1544,9 +1597,8 @@ public static function resolveFragments ( &$stream ) { $args = array(); if ( $with_arguments ) { - // Get the argument array to pass to the fragment - $args = csscrush_util::splitDelimList( $paren_match->inside, ',', true, true ); - $args = $args->list; + // Get the argument array to pass to the fragment. + $args = csscrush_util::splitDelimList( $paren_match->inside ); } // Execute the fragment and get the return value @@ -1571,10 +1623,9 @@ public static function resolveIfDefines ( &$stream ) { $negate = $match[1][1] != -1; $name = $match[2][0]; - $name_defined = isset( self::$storage->variables[ $name ] ); + $name_defined = isset( self::$process->variables[ $name ] ); - $curly_match = csscrush_util::matchBrackets( - $stream, $brackets = array( '{', '}' ), $full_match_start, true ); + $curly_match = csscrush_util::matchBrackets( $stream, '{}', $full_match_start, true ); if ( ! $curly_match ) { // Couldn't match the block. @@ -1596,8 +1647,8 @@ public static function resolveIfDefines ( &$stream ) { } -####################### -# Procedural style API +############################# +# Procedural style external API. function csscrush_file ( $file, $options = null ) { return csscrush::file( $file, $options ); diff --git a/lib/Function.php b/lib/Function.php index 97250fb..24157dd 100644 --- a/lib/Function.php +++ b/lib/Function.php @@ -37,121 +37,84 @@ public static function getFunctions () { public static function executeCustomFunctions ( &$str, $patt = null, $process_callback = null, $property = null ) { - // No bracketed expressions, early return + // No bracketed expressions, early return. if ( false === strpos( $str, '(' ) ) { return; } - // Set default pattern if not set + // Set default pattern if not set. if ( is_null( $patt ) ) { $patt = csscrush_function::$functionPatt; } - // No custom functions, early return + // No custom functions, early return. if ( ! preg_match( $patt, $str ) ) { return; } - // Need a space inside the front function paren for the following match_all to be reliable - $str = preg_replace( '!\(([^\s])!', '( $1', $str, -1, $spacing_count ); - - // Find custom function matches + // Find custom function matches. $matches = csscrush_regex::matchAll( $patt, $str ); - // Step through the matches from last to first + // Step through the matches from last to first. while ( $match = array_pop( $matches ) ) { $offset = $match[0][1]; - $before_char = $match[1][0]; - $before_char_len = strlen( $before_char ); - // No function name default to math expression - // Store the raw function name match - $raw_fn_name = isset( $match[2] ) ? $match[2][0] : ''; + if ( ! preg_match( csscrush_regex::$patt->balancedParens, + $str, $parens, PREG_OFFSET_CAPTURE, $offset ) ) { + continue; + } + + // No function name default to math expression. + // Store the raw function name match. + $raw_fn_name = isset( $match[1] ) ? $match[1][0] : ''; $fn_name = $raw_fn_name ? $raw_fn_name : 'math'; if ( '-' === $fn_name ) { $fn_name = 'math'; } - // Loop throught the string - $first_paren_offset = strpos( $str, '(', $offset ); - $paren_score = 0; + $opening_paren = $parens[0][1]; + $closing_paren = $opening_paren + strlen( $parens[0][0] ); - for ( $index = $first_paren_offset; $index < strlen( $str ); $index++ ) { - - $char = $str[ $index ]; - if ( '(' === $char ) { - $paren_score++; - } - elseif ( ')' === $char ) { - $paren_score--; - } + // Get the function arguments. + $args = $parens[1][0]; - if ( 0 === $paren_score ) { + // Workaround the minus. + $minus_before = '-' === $raw_fn_name ? '-' : ''; - // Get the function inards - $content_start = $offset + strlen( $before_char ) + strlen( $raw_fn_name ) + 1; - $content_finish = $index; - $content = substr( $str, $content_start, $content_finish - $content_start ); - $content = trim( $content ); + $func_returns = ''; - // Calculate offsets - $func_start = $offset + strlen( $before_char ); - $func_end = $index + 1; - - // Workaround the minus - $minus_before = '-' === $raw_fn_name ? '-' : ''; - - $result = ''; - - if ( ! $process_callback ) { - - // If no callback reference it's a built-in - if ( in_array( $fn_name, self::$functionList ) ) { - $fn_name_clean = str_replace( '-', '_', $fn_name ); - $result = call_user_func( array( 'self', "css_fn__$fn_name_clean" ), $content ); - } - } - else { - if ( isset( $process_callback[ $fn_name ] ) ) { - $result = call_user_func( $process_callback[ $fn_name ], $content, $fn_name, $property ); - } - } + if ( ! $process_callback ) { - // Join together the result - $str = substr( $str, 0, $func_start ) . $minus_before . $result . substr( $str, $func_end ); - break; + // If no callback reference it's a built-in. + if ( in_array( $fn_name, self::$functionList ) ) { + $fn_name_clean = str_replace( '-', '_', $fn_name ); + $func_returns = call_user_func( array( 'self', "css_fn__$fn_name_clean" ), $args ); + } + } + else { + if ( isset( $process_callback[ $fn_name ] ) ) { + $func_returns = call_user_func( $process_callback[ $fn_name ], $args, $fn_name, $property ); } } - } // while - // Restore the whitespace - if ( $spacing_count ) { - $str = str_replace( '( ', '(', $str ); + // Join together the result. + $str = substr( $str, 0, $offset ) . $minus_before . $func_returns . substr( $str, $closing_paren ); } - - // return $str; - } + } ############ # Helpers public static function parseArgs ( $input, $allowSpaceDelim = false ) { - - $args = csscrush_util::splitDelimList( - $input, - ( $allowSpaceDelim ? '\s*[,\s]\s*' : ',' ), - true, - true ); - - return $args->list; + return csscrush_util::splitDelimList( + $input, ( $allowSpaceDelim ? '\s*[,\s]\s*' : ',' ) ); } // Intended as a quick arg-list parse for function that take up-to 2 arguments // with the proviso the first argument is a name public static function parseArgsSimple ( $input ) { - return preg_split( csscrush_regex::$patt->argListSplit, $input, 2 ); } @@ -162,7 +125,7 @@ protected static function colorAdjust ( $color, array $adjustments ) { // Support for Hex, RGB, RGBa and keywords // HSL and HSLa are passed over - if ( $fn_matched || array_key_exists( $color, $keywords ) ) { + if ( $fn_matched || isset( $keywords[ $color ] ) ) { $alpha = 1; $rgb = null; @@ -207,10 +170,10 @@ protected static function colorAdjust ( $color, array $adjustments ) { foreach ( $adjustments as $val ) { // Normalize argument $_val = $val ? trim( str_replace( '%', '', $val ) ) : 0; - + // Reduce value to float $_val /= 100; - + // Adjust alpha component if necessary if ( 3 === $index ) { if ( 0 != $val ) { @@ -225,7 +188,7 @@ protected static function colorAdjust ( $color, array $adjustments ) { } $index++; } - + // Finally convert new HSL value to RGB $rgb = csscrush_color::hslToRgb( $hsl ); @@ -251,7 +214,7 @@ public static function css_fn__math ( $input ) { $input = preg_replace( csscrush_regex::$patt->mathBlacklist, '', $input ); $result = @eval( "return $input;" ); - + return $result === false ? 0 : round( $result, 5 ); } @@ -262,8 +225,6 @@ public static function css_fn__percent ( $input ) { $args = preg_split( csscrush_regex::$patt->argListSplit, $input, -1, PREG_SPLIT_NO_EMPTY ); - // csscrush::log( $input ); - // Use precision argument if it exists, use default otherwise $precision = isset( $args[2] ) ? $args[2] : 5; @@ -304,60 +265,6 @@ public static function css_fn__pc ( $input ) { return self::css_fn__percent( $input ); } - public static function css_fn__data_uri ( $input ) { - - // Normalize, since argument might be a string token - if ( strpos( $input, '___s' ) === 0 ) { - $string_labels = array_keys( csscrush::$storage->tokens->strings ); - $string_values = array_values( csscrush::$storage->tokens->strings ); - $input = trim( str_replace( $string_labels, $string_values, $input ), '\'"`' ); - } - - // Default return value - $result = "url(http://github.com/$input)"; - - // No attempt to process absolute urls - if ( preg_match( csscrush_regex::$patt->absoluteUrl, $input ) ) { - return $result; - } - - // Get system file path - if ( strpos( $input, '/' ) === 0 ) { - $file = csscrush::$config->docRoot . $input; - } - else { - $file = csscrush::$process->inputDir . "/$input"; - } - - // File not found - if ( ! file_exists( $file ) ) { - return $result; - } - - $file_ext = pathinfo( $file, PATHINFO_EXTENSION ); - - // Only allow certain extensions - $allowed_file_extensions = array( - 'woff' => 'application/x-font-woff;charset=utf-8', - 'ttf' => 'font/truetype;charset=utf-8', - 'svg' => 'image/svg+xml', - 'svgz' => 'image/svg+xml', - 'gif' => 'image/gif', - 'jpeg' => 'image/jpg', - 'jpg' => 'image/jpg', - 'png' => 'image/png', - ); - if ( ! array_key_exists( $file_ext, $allowed_file_extensions ) ) { - return $result; - } - - $mime_type = $allowed_file_extensions[ $file_ext ]; - $base64 = base64_encode( file_get_contents( $file ) ); - $string_label = csscrush_string::add( "\"data:$mime_type;base64,$base64\"" ); - - return "url(http://github.com/$string_label)"; - } - public static function css_fn__hsla_adjust ( $input ) { list( $color, $h, $s, $l, $a ) = array_pad( self::parseArgs( $input, true ), 5, 0 ); return self::colorAdjust( $color, array( $h, $s, $l, $a ) ); @@ -382,7 +289,7 @@ public static function css_fn__l_adjust ( $input ) { list( $color, $l ) = array_pad( self::parseArgs( $input, true ), 2, 0 ); return self::colorAdjust( $color, array( 0, 0, $l, 0 ) ); } - + public static function css_fn__a_adjust ( $input ) { list( $color, $a ) = array_pad( self::parseArgs( $input, true ), 2, 0 ); return self::colorAdjust( $color, array( 0, 0, 0, $a ) ); diff --git a/lib/IO.php b/lib/IO.php index 4d2137d..43fe00d 100644 --- a/lib/IO.php +++ b/lib/IO.php @@ -14,50 +14,18 @@ public static function init () { $process = csscrush::$process; $process->cacheFileName = '.csscrush'; - $process->cacheFilePath = "$process->inputDir/$process->cacheFileName"; - } - - - public static function getInput ( $file = false ) { - - // May return a hostfile object associated with a real file - // Alternatively it may return a hostfile object with string input - - $process = csscrush::$process; - - // Make basic information about the input object accessible - $input = (object) array(); - $input->dir = ! empty( $process->inputDir ) ? $process->inputDir : null; - $input->name = $file ? basename( $file ) : null; - $input->path = $file ? "$process->inputDir/$input->name" : null; - - if ( $file ) { - - if ( ! file_exists( $input->path ) ) { - - // On failure return false - $error = "Input file '$input->name' not found."; - csscrush::logError( $error ); - trigger_error( __METHOD__ . ": $error\n", E_USER_WARNING ); - return false; - } - else { - // Capture the modified time - $input->mtime = filemtime( $input->path ); - } - } - return $input; + $process->cacheFilePath = "{$process->input->dir}/$process->cacheFileName"; } public static function getOutputDir () { - return csscrush::$process->inputDir; + return csscrush::$process->input->dir; } - public static function testOutputDir ( $write_test = true ) { + public static function testOutputDir () { - $output_dir = csscrush::$process->outputDir; + $output_dir = csscrush::$process->output->dir; $pathtest = true; $error = false; @@ -66,9 +34,9 @@ public static function testOutputDir ( $write_test = true ) { $error = "Output directory '$output_dir' doesn't exist."; $pathtest = false; } - else if ( $write_test && ! is_writable( $output_dir ) ) { + else if ( ! is_writable( $output_dir ) ) { - csscrush::log( 'Attempting to change permissions' ); + csscrush::log( 'Attempting to change permissions.' ); if ( ! @chmod( $output_dir, 0755 ) ) { @@ -76,7 +44,7 @@ public static function testOutputDir ( $write_test = true ) { $pathtest = false; } else { - csscrush::log( 'Permissions updated' ); + csscrush::log( 'Permissions updated.' ); } } @@ -93,9 +61,8 @@ public static function getOutputFileName () { $process = csscrush::$process; $options = $process->options; - $input = $process->input; - $output_basename = basename( $input->name, '.css' ); + $output_basename = basename( $process->input->filename, '.css' ); if ( ! empty( $options->output_file ) ) { $output_basename = basename( $options->output_file, '.css' ); @@ -112,32 +79,33 @@ public static function validateExistingOutput () { $config = csscrush::$config; $input = $process->input; - // Search base directory for an existing compiled file - foreach ( scandir( $process->outputDir ) as $filename ) { + // Search base directory for an existing compiled file. + foreach ( scandir( $process->output->dir ) as $filename ) { - if ( $process->outputFileName != $filename ) { + if ( $process->output->filename != $filename ) { continue; } - // Cached file exists + + // Cached file exists. csscrush::log( 'Cached file exists.' ); $existingfile = (object) array(); - $existingfile->name = $filename; - $existingfile->path = "$process->outputDir/$existingfile->name"; - $existingfile->URL = "$process->outputDirUrl/$existingfile->name"; + $existingfile->filename = $filename; + $existingfile->path = "{$process->output->dir}/$existingfile->filename"; + $existingfile->URL = "{$process->output->dirUrl}/$existingfile->filename"; // Start off with the input file then add imported files $all_files = array( $input->mtime ); - if ( file_exists( $existingfile->path ) && isset( $process->cacheData[ $process->outputFileName ] ) ) { + if ( file_exists( $existingfile->path ) && isset( $process->cacheData[ $process->output->filename ] ) ) { // File exists and has config csscrush::log( 'Cached file is registered.' ); - foreach ( $process->cacheData[ $existingfile->name ][ 'imports' ] as $import_file ) { + foreach ( $process->cacheData[ $existingfile->filename ][ 'imports' ] as $import_file ) { // Check if this is docroot relative or input dir relative - $root = strpos( $import_file, '/' ) === 0 ? $config->docRoot : $process->inputDir; + $root = strpos( $import_file, '/' ) === 0 ? $config->docRoot : $process->input->dir; $import_filepath = realpath( $root ) . "/$import_file"; if ( file_exists( $import_filepath ) ) { @@ -152,8 +120,8 @@ public static function validateExistingOutput () { } // Cast because the cached options may be a stdClass if an IO adapter has been used. - $existing_options = (array) $process->cacheData[ $existingfile->name ][ 'options' ]; - $existing_datesum = $process->cacheData[ $existingfile->name ][ 'datem_sum' ]; + $existing_options = (array) $process->cacheData[ $existingfile->filename ][ 'options' ]; + $existing_datesum = $process->cacheData[ $existingfile->filename ][ 'datem_sum' ]; $options_unchanged = true; foreach ( $existing_options as $key => &$value ) { @@ -280,6 +248,26 @@ public static function saveCacheData () { file_put_contents( $process->cacheFilePath, json_encode( $process->cacheData ) ); } -} + final static function registerInputFile ( $file ) { + + $input = csscrush::$process->input; + $input->filename = basename( $file ); + $input->path = "$input->dir/$input->filename"; + + if ( ! file_exists( $input->path ) ) { + + // On failure return false. + $error = "Input file '$input->filename' not found."; + csscrush::logError( $error ); + trigger_error( __METHOD__ . ": $error\n", E_USER_WARNING ); + return false; + } + else { + // Capture the modified time. + $input->mtime = filemtime( $input->path ); + return true; + } + } +} diff --git a/lib/Importer.php b/lib/Importer.php index 1a92e60..02611a9 100644 --- a/lib/Importer.php +++ b/lib/Importer.php @@ -19,7 +19,7 @@ public static function save ( $data ) { } // Write to config - $process->cacheData[ $process->outputFileName ] = $data; + $process->cacheData[ $process->output->filename ] = $data; // Save config changes csscrush::io_call( 'saveCacheData' ); @@ -34,7 +34,7 @@ public static function hostfile () { $regex = csscrush_regex::$patt; $hostfile = $process->input; - // Keep track of all import file info for later logging + // Keep track of all import file info for cache data. $mtimes = array(); $filenames = array(); @@ -51,7 +51,7 @@ public static function hostfile () { } } - // Resolve main input: string or file. + // Resolve main input; Is it a bare string or a file. if ( isset( $hostfile->string ) ) { $stream .= $hostfile->string; $process->currentFile = 'inline-css'; @@ -66,86 +66,54 @@ public static function hostfile () { return $stream; } - // Not forgetting to prepend the prepend file contents. + // Prepend any prepend file contents here. $stream = $prepend_file_contents . $stream; - // If rewriting URLs as absolute we need to do some extra work - if ( $options->rewrite_import_urls === 'absolute' ) { - - // Normalize the @import statements in this case - foreach ( csscrush_regex::matchAll( $regex->import, $stream ) as $match ) { - $full_match = $match[0][0]; - $normalized_import_statement = self::normalizeImportStatement( $full_match ); - $stream = str_replace( $full_match, $normalized_import_statement, $stream ); - } - - // Convert URLs to URL tokens by setting an empty prefix - csscrush::$storage->misc->rewriteUrlPrefix = ''; - $stream = self::rewriteUrls( $stream ); - } - - // This may be set non-zero during the script if an absolute @import URL is encountered + // This may be set non-zero during the script if an absolute @import URL is encountered. $search_offset = 0; - // Recurses until the nesting heirarchy is flattened and all files are combined + // Recurses until the nesting heirarchy is flattened and all import files are inlined. while ( preg_match( $regex->import, $stream, $match, PREG_OFFSET_CAPTURE, $search_offset ) ) { - $full_match = $match[0][0]; // Full match - $match_start = $match[0][1]; // Full match offset - $match_end = $match_start + strlen( $full_match ); - $pre_statement = substr( $stream, 0, $match_start ); - $post_statement = substr( $stream, $match_end ); + $match_len = strlen( $match[0][0] ); + $match_start = $match[0][1]; + $match_end = $match_start + $match_len; // If just stripping the import statements if ( isset( $hostfile->importIgnore ) ) { - $stream = $pre_statement . $post_statement; + $stream = substr_replace( $stream, '', $match_start, $match_len ); continue; } - // The media context (if specified) at position 3 in the match - $media_context = trim( $match[3][0] ); - - // The url may be at position 1 or 2 in the match depending on the syntax used - $url = trim( $match[1][0] ); - if ( ! $url ) { - $url = trim( $match[2][0] ); - } - - // Url may be a string token - if ( preg_match( $regex->stringToken, $url ) ) { - - $import_url_token = new csscrush_string( $url ); - $url = $import_url_token->value; - } - - // Pass over absolute urls - // Move the search pointer forward - if ( preg_match( $regex->absoluteUrl, $url ) ) { + // Fetch the URL object. + $url = csscrush_url::get( $match[1][0] ); + // Pass over protocoled import urls. + if ( $url->protocol ) { $search_offset = $match_end; continue; } - // Create import object + // The media context (if specified). + $media_context = trim( $match[2][0] ); + + // Create import object. $import = (object) array(); $import->url = $url; $import->mediaContext = $media_context; - $import->hostDir = $process->inputDir; - // Check to see if the url is root relative - // Flatten import path for convenience - if ( strpos( $import->url, '/' ) === 0 ) { - $import->path = realpath( $config->docRoot . $import->url ); + // Resolve import realpath. + if ( $url->isRooted ) { + $import->path = realpath( $config->docRoot . $import->url->value ); } else { - $import->path = realpath( "$hostfile->dir/$import->url" ); + $import->path = realpath( "$hostfile->dir/{$import->url->value}" ); } - // Get the import contents, if unsuccessful just continue with the import line removed + // Get the import contents, if unsuccessful just continue with the import line removed. if ( ! ( $import->content = @file_get_contents( $import->path ) ) ) { - - csscrush::log( "Import file '$import->url' not found" ); - $stream = $pre_statement . $post_statement; + csscrush::log( "Import file '{$import->url->value}' not found" ); + $stream = substr_replace( $stream, '', $match_start, $match_len ); continue; } @@ -156,84 +124,35 @@ public static function hostfile () { // If there are unmatched brackets inside the import, strip it. if ( ! csscrush::prepareStream( $import->content ) ) { - $stream = $pre_statement . $post_statement; + $stream = substr_replace( $stream, '', $match_start, $match_len ); continue; } - $import->dir = dirname( $import->url ); + $import->dir = dirname( $import->url->value ); - // Store import file info for cache validation + // Store import file info for cache validation. $mtimes[] = filemtime( $import->path ); - $filenames[] = $import->url; - - - // Alter all the url strings to be paths relative to the hostfile: - // - Match all @import statements in the import content - // - Store the replacements we might find - $replacements = array(); - - foreach ( csscrush_regex::matchAll( $regex->import, $import->content ) as $match ) { - - $full_match = $match[0][0]; - - // Url match may be at one of 2 positions - $url_match = $match[1][1] == -1 ? $match[2][0] : $match[1][0]; - - // Url may be a string token - if ( $url_match_token = preg_match( $regex->stringToken, $url_match ) ) { - - // Store the token - $url_match_token = new csscrush_string( $url_match ); - - // Set $url_match to the actual value - $url_match = $url_match_token->value; - } - - // Search and replace on the statement url - $search = $url_match; - $replace = "$import->dir/$url_match"; - - // Try to resolve absolute paths - // On failure strip the @import statement - if ( strpos( $url_match, '/' ) === 0 ) { - $replace = self::resolveAbsolutePath( $url_match ); - if ( ! $replace ) { - $search = $full_match; - $replace = ''; - } - } + $filenames[] = $import->url->value; - // The full revised statement for replacement - $statement = $full_match; + // Alter all the @import urls to be paths relative to the hostfile. + foreach ( csscrush_regex::matchAll( $regex->import, $import->content ) as $m ) { - if ( $url_match_token && ! empty( $replace ) ) { + // Fetch the matched URL. + $url2 = csscrush_url::get( $m[1][0] ); - // Alter the stored token on internal hash table - $url_match_token->update( $replace ); + // Try to resolve absolute paths. + // On failure strip the @import statement. + if ( $url2->isRooted ) { + $url2->resolveRootedPath(); } else { - - // Trim the statement and set the resolved path - $statement = trim( str_replace( $search, $replace, $full_match ) ); - } - - // Normalise import statement to be without url() syntax: - // - So relative urls can be targeted later - $statement = self::normalizeImportStatement( $statement ); - - if ( $full_match !== $statement ) { - $replacements[ $full_match ] = $statement; + $url2->prepend( "$import->dir/" ); } } - // If we've stored any altered @import strings then we need to apply them - if ( $replacements ) { - $import->content = csscrush_util::strReplaceHash( $import->content, $replacements ); - } - - // Optionally rewrite relative url and custom function data-uri references + // Optionally rewrite relative url and custom function data-uri references. if ( $options->rewrite_import_urls ) { - $import->content = self::rewriteImportUrls( $import ); + self::rewriteImportedUrls( $import ); } // Add media context if it exists @@ -241,9 +160,8 @@ public static function hostfile () { $import->content = "@media $import->mediaContext {{$import->content}}"; } - $stream = $pre_statement . $import->content . $post_statement; - - } // End while + $stream = substr_replace( $stream, $import->content, $match_start, $match_len ); + } // Save only if the hostfile object is associated with a real file if ( $hostfile->path ) { @@ -259,181 +177,27 @@ public static function hostfile () { } - protected static function normalizeImportStatement ( $import_statement ) { - - if ( preg_match( '!(\s)url\(\s*([^\)]+)\)!', $import_statement, $m ) ) { + protected static function rewriteImportedUrls ( $import ) { - list( $full_match, $the_space, $the_url ) = $m; - $the_url = rtrim( $the_url ); - - if ( preg_match( csscrush_regex::$patt->stringToken, $the_url ) ) { - - // @import url(http://github.com/___s34___) screen and ( max-width: 500px ); - // @import url(http://github.com/___s34___); - $import_statement = str_replace( $full_match, $the_space . $the_url, $import_statement ); - } - else { + $link = csscrush_util::getLinkBetweenDirs( + csscrush::$process->input->dir, dirname( $import->path ) ); - // @import url(http://github.com/some/path/styles.css); - $the_url = '"' . $the_url . '"'; - $string_label = csscrush_string::add( $the_url ); - $import_statement = str_replace( $full_match, $the_space . $string_label, $import_statement ); - } + if ( empty( $link ) ) { + return; } - return $import_statement; - } - - - protected static function resolveAbsolutePath ( $url ) { + // Match all urls that are not imports. + preg_match_all( '#(?content, $matches ); - $config = csscrush::$config; - $process = csscrush::$process; - - if ( ! file_exists ( $config->docRoot . $url ) ) { - return false; - } - // Move upwards '..' by the number of slashes in baseURL to get a relative path - $url = str_repeat( '../', substr_count( $process->inputDirUrl, '/' ) ) . substr( $url, 1 ); - - return $url; - } + foreach ( $matches[0] as $token ) { + // Fetch the matched URL. + $url = csscrush_url::get( $token ); - protected static function rewriteImportUrls ( $import ) { - - $stream = $import->content; - - // Normalise the paths - $host_dir = csscrush_util::normalizePath( $import->hostDir, true ); - $import_dir = csscrush_util::normalizePath( dirname( $import->path ), true ); - - csscrush::$storage->misc->rewriteUrlPrefix = ''; - $url_prefix = ''; - - if ( $import_dir === $host_dir ) { - - // Do nothing if files are in the same directory - return $stream; - - } - elseif ( strpos( $import_dir, $host_dir ) === false ) { - - // Import directory is higher than the host directory - // Split the directory paths into arrays so we can compare segment by segment - $host_segs = preg_split( '!/+!', $host_dir, null, PREG_SPLIT_NO_EMPTY ); - $import_segs = preg_split( '!/+!', $import_dir, null, PREG_SPLIT_NO_EMPTY ); - - // Shift the segments until they are on different branches - while ( @( $host_segs[0] == $import_segs[0] ) ) { - array_shift( $host_segs ); - array_shift( $import_segs ); - // csscrush::log( array( $host_segs, $import_segs ) ); + if ( $url->isRelative ) { + // Prepend the relative url prefix. + $url->prepend( $link ); } - - // Count the remaining $host_segs to get the offset - $level_diff = count( $host_segs ); - - $url_prefix = str_repeat( '../', $level_diff ) . implode( '/', $import_segs ); - - } - else { - - // Import directory is lower than host directory - // easy, url_prefix is the difference - $url_prefix = substr( $import_dir, strlen( $host_dir ) + 1 ); - } - - if ( empty( $url_prefix ) ) { - return $stream; - } - - // Add the directory seperator ending (if needed) - if ( substr( $url_prefix, -1 ) !== '/' ) { - $url_prefix .= '/'; } - - // Make $url_prefix accessible in callback scope - csscrush::$storage->misc->rewriteUrlPrefix = $url_prefix; - - // Search for all relative url and data-uri references in the content - // and prepend $relative_url_prefix - return self::rewriteUrls( $stream ); - } - - - protected static function rewriteUrls ( $stream ) { - - $url_function_patt = '! - ([^a-z-]) # the preceeding character - (data-uri|url) # the function name - \(\s*([^\)]+)\s*\) # the url - !xi'; - $stream = preg_replace_callback( $url_function_patt, - array( 'self', 'cb_rewriteUrls' ), $stream ); - - return $stream; - } - - - protected static function cb_rewriteUrls ( $match ) { - - $regex = csscrush_regex::$patt; - $storage = csscrush::$storage; - - // The relative url prefix - $relative_url_prefix = $storage->misc->rewriteUrlPrefix; - - list( $full_match, $before, $function, $url ) = $match; - $url = trim( $url ); - - // If the url is a string token we'll need to restore it as a string token later - if ( $url_is_string = preg_match( $regex->stringToken, $url ) ) { - - $url_string = new csscrush_string( $url ); - $url = $url_string->value; - } - - // Normalise the path - $url = csscrush_util::normalizePath( $url ); - - // No rewrite if: - // - $url is an empty string - // - $url path is absolute or begins with slash - // - $url begins with a variable, e.g '$(' - // - $url is a data uri - if ( - $url === '' || - strpos( $url, '/' ) === 0 || - strpos( $url, '$(' ) === 0 || - strpos( $url, 'data:' ) === 0 || - preg_match( $regex->absoluteUrl, $url ) - ) { - - // Token or not, it's ok to return the full match - // if $url is a root relative or absolute ref - return $full_match; - } - - // Prepend the relative url prefix - $url = $relative_url_prefix . $url; - - // If the path comes via the css url function convert it to a URL token - if ( $function == 'url' ) { - - $label = csscrush::tokenLabelCreate( 'u' ); - csscrush::$storage->tokens->urls[ $label ] = $url; - $url = $label; - } - elseif ( $url_is_string ) { - - // Restore quotes if $url was a string token, update the token and return it - $url_string->update( $url_string->quoteMark . $url . $url_string->quoteMark ); - $url = $url_string->token; - } - - // Reconstruct the match and return - return "$before$function($url)"; } } - diff --git a/lib/Mixin.php b/lib/Mixin.php index 20e4d13..0eec59c 100644 --- a/lib/Mixin.php +++ b/lib/Mixin.php @@ -24,10 +24,10 @@ public function __construct ( $block ) { // Re-assign with the parsed arguments string $block = $this->arguments->string; - // Need to split safely as there are semi-colons in data-uris - $declarations_match = csscrush_util::splitDelimList( $block, ';', true ); + // Split the block around semi-colons. + $declarations = preg_split( '!\s*;\s*!', trim( $block ), null, PREG_SPLIT_NO_EMPTY ); - foreach ( $declarations_match->list as $raw_declaration ) { + foreach ( $declarations as $raw_declaration ) { $colon = strpos( $raw_declaration, ':' ); if ( $colon === -1 ) { @@ -57,7 +57,7 @@ public function __construct ( $block ) { // Create data table for the mixin. // Values that use arg() are excluded foreach ( $this->declarationsTemplate as &$declaration ) { - if ( ! preg_match( csscrush_regex::$patt->argToken, $declaration['value'] ) ) { + if ( ! preg_match( csscrush_regex::$patt->aToken, $declaration['value'] ) ) { $this->data[ $declaration['property'] ] = $declaration['value']; } } @@ -154,8 +154,7 @@ public static function parseSingleValue ( $message ) { // Determine what raw arguments there are to pass to the mixin $args = array(); if ( $message !== '' ) { - $args = csscrush_util::splitDelimList( $message, ',', true, true ); - $args = $args->list; + $args = csscrush_util::splitDelimList( $message ); } return $mixin->call( $args ); @@ -164,11 +163,9 @@ public static function parseSingleValue ( $message ) { public static function parseValue ( $message ) { // Call the mixin and return the list of declarations - $values = csscrush_util::splitDelimList( $message, ',', true ); - $declarations = array(); - foreach ( $values->list as $item ) { + foreach ( csscrush_util::splitDelimList( $message ) as $item ) { if ( $result = self::parseSingleValue( $item ) ) { @@ -271,7 +268,7 @@ public function store ( $raw_argument ) { $this->argCount = max( $this->argCount, $argNumber ); // Return the argument token - return "___arg{$position_match}___"; + return "?arg$position_match?"; } public function getArgValue ( $index, &$args ) { @@ -285,7 +282,7 @@ public function getArgValue ( $index, &$args ) { $default = isset( $this->defaults[ $index ] ) ? $this->defaults[ $index ] : ''; // Recurse for nested arg() calls - if ( preg_match( csscrush_regex::$patt->argToken, $default, $m ) ) { + if ( preg_match( csscrush_regex::$patt->aToken, $default, $m ) ) { $default = $this->getArgValue( (int) $m[1], $args ); } @@ -302,7 +299,7 @@ public function getSubstitutions ( $args ) { foreach ( $argIndexes as $index ) { - $find[] = "___arg{$index}___"; + $find[] = "?arg$index?"; $replace[] = $this->getArgValue( $index, $args ); } diff --git a/lib/Regex.php b/lib/Regex.php index ffdbadc..745ae0e 100644 --- a/lib/Regex.php +++ b/lib/Regex.php @@ -23,15 +23,7 @@ public static function init () { // Patterns. $patt->ident = '!^' . $classes->ident . '$!'; - $patt->import = '! - @import\s+ # import at-rule - (?: - url\(\s*([^\)]+)\s*\) # url function - | # or - ([_s\d]+) # string token - ) - \s*([^;]*); # media argument - !xiS'; + $patt->import = '!@import (\?u\d+\?) ?([^;]*);!iS'; $patt->variables = '!@(?:variables|define)\s*([^\{]*)\{\s*(.*?)\s*\};?!s'; $patt->mixin = '!@mixin\s*([^\{]*)\{\s*(.*?)\s*\};?!s'; @@ -58,21 +50,21 @@ public static function init () { ~xiS'; // Balanced bracket matching. - $patt->balancedParens = '!\( (?: (?: (?>[^()]+) | (?R) )* ) \)!xS'; - $patt->balancedCurlies = '!\{ (?: (?: (?>[^{}]+) | (?R) )* ) \}!xS'; + $patt->balancedParens = '!\(\s* ( (?: (?>[^()]+) | (?R) )* ) \s*\)!xS'; + $patt->balancedCurlies = '!\{\s* ( (?: (?>[^{}]+) | (?R) )* ) \s*\}!xS'; // Tokens. - $patt->commentToken = '!___c\d+___!'; - $patt->stringToken = '!___s\d+___!'; - $patt->ruleToken = '!___r\d+___!'; - $patt->parenToken = '!___p\d+___!'; - $patt->urlToken = '!___u\d+___!'; - $patt->traceToken = '!___t\d+___!'; - $patt->argToken = '!___arg(\d+)___!'; + $patt->cToken = '!\?c\d+\?!'; // Comments + $patt->sToken = '!\?s\d+\?!'; // Strings + $patt->rToken = '!\?r\d+\?!'; // Rules + $patt->pToken = '!\?p\d+\?!'; // Parens + $patt->uToken = '!\?u\d+\?!'; // URLs + $patt->tToken = '!\?t\d+\?!'; // Traces + $patt->aToken = '!\?arg(\d+)\?!'; // Args // Functions. $patt->varFunction = '!\$\(\s*([a-z0-9_-]+)\s*\)!iS'; - $patt->function = '!(^|[^a-z0-9_-])([a-z_-]+)(___p\d+___)!iS'; + $patt->function = '!(^|[^a-z0-9_-])([a-z_-]+)(\?p\d+\?)!iS'; // Specific functions. $patt->argFunction = csscrush_regex::createFunctionMatchPatt( array( 'arg' ) ); @@ -85,7 +77,7 @@ public static function init () { $patt->absoluteUrl = '!^https?://!'; $patt->argListSplit = '!\s*[,\s]\s*!S'; $patt->mathBlacklist = '![^\.0-9\*\/\+\-\(\)]!S'; - $patt->charset = '!@charset\s+(___s\d+___)\s*;!iS'; + $patt->charset = '!@charset\s+(\?s\d+\?)\s*;!iS'; } @@ -119,7 +111,7 @@ public static function createFunctionMatchPatt ( $list, $include_unnamed_functio foreach ( $list as &$fn_name ) { $fn_name = preg_quote( $fn_name ); } - return '!(^|[^a-z0-9_-])(' . implode( '|', $list ) . ')' . $question . '\(!iS'; + return '#(?trace && - $trace_tokens = csscrush_regex::matchAll( $regex->traceToken, $selector_string ) + $trace_tokens = csscrush_regex::matchAll( $regex->tToken, $selector_string ) ) { $trace_token = array_pop( $trace_tokens ); $this->tracingStub = $trace_token[0][0]; - $selector_string = preg_replace( $regex->traceToken, '', $selector_string ); + $selector_string = preg_replace( $regex->tToken, '', $selector_string ); } // Parse the selectors chunk if ( ! empty( $selector_string ) ) { - $selectors_match = csscrush_util::splitDelimList( $selector_string, ',' ); + csscrush::captureParens( $selector_string ); + $selectors = csscrush_util::splitDelimList( $selector_string ); // Remove and store comments that sit above the first selector // remove all comments between the other selectors - if ( strpos( $selectors_match->list[0], '___c' ) !== false ) { - preg_match_all( $regex->commentToken, $selectors_match->list[0], $m ); + if ( strpos( $selectors[0], '?c' ) !== false ) { + preg_match_all( $regex->cToken, $selectors[0], $m ); $this->comments = $m[0]; } // Strip any other comments then create selector instances - foreach ( $selectors_match->list as $selector ) { + foreach ( $selectors as $selector ) { $selector = trim( csscrush_util::stripCommentTokens( $selector ) ); @@ -124,15 +125,14 @@ public function __construct ( $selector_string = null, $declarations_string ) { } } - // Parse the declarations chunk - // Need to split safely as there are semi-colons in data-uris - $declarations_match = csscrush_util::splitDelimList( $declarations_string, ';', true, true ); + // Parse the declarations chunk. + $declarations = preg_split( '!\s*;\s*!', trim( $declarations_string ), null, PREG_SPLIT_NO_EMPTY ); // First create a simple array of all properties and value pairs in raw state $pairs = array(); // Split declarations in to property/value pairs - foreach ( $declarations_match->list as $declaration ) { + foreach ( $declarations as $declaration ) { // Strip comments around the property $declaration = csscrush_util::stripCommentTokens( $declaration ); @@ -221,6 +221,48 @@ public function __get ( $name ) { } } + public function __toString () { + + $minify = ! csscrush::$process->options->debug; + $whitespace = $minify ? '' : ' '; + + // Tracing stubs. + $tracing_stub = ''; + if ( $this->tracingStub ) { + $tracing_stub =& csscrush::$process->tokens->t[ $this->tracingStub ]; + } + + // If there are no selectors or declarations associated with the rule return empty string + if ( empty( $this->selectorList ) || ! count( $this ) ) { + return ''; + } + + // Build the selector; uses selector __toString method + $selectors = implode( ',', $this->selectorList ); + + // Build the block + $block = array(); + foreach ( $this as $declaration ) { + $important = $declaration->important ? "$whitespace!important" : ''; + $block[] = "$declaration->property:{$whitespace}$declaration->value{$important}"; + } + + // Return whole rule + if ( $minify ) { + $block = implode( ';', $block ); + return "$tracing_stub$selectors{{$block}}"; + } + else { + // Include pre-rule comments. + $comments = implode( "\n", $this->comments ); + if ( $tracing_stub ) { + $tracing_stub .= "\n"; + } + $block = implode( ";\n\t", $block ); + return "$comments\n$tracing_stub$selectors {\n\t$block;\n\t}\n"; + } + } + public function cssThisFunction ( $input, $fn_name ) { $args = csscrush_function::parseArgsSimple( $input ); @@ -321,7 +363,7 @@ public function updatePropertyTable () { public function addPropertyAliases () { $regex = csscrush_regex::$patt; - $aliasedProperties =& csscrush::$config->aliases[ 'properties' ]; + $aliasedProperties =& csscrush::$process->aliases[ 'properties' ]; // First test for the existence of any aliased properties $intersect = array_intersect( array_keys( $aliasedProperties ), array_keys( $this->properties ) ); @@ -366,7 +408,7 @@ public function addPropertyAliases () { public function addFunctionAliases () { - $function_aliases =& csscrush::$config->aliases[ 'functions' ]; + $function_aliases =& csscrush::$process->aliases[ 'functions' ]; $aliased_functions = array_keys( $function_aliases ); if ( empty( $aliased_functions ) ) { @@ -447,7 +489,7 @@ public function addFunctionAliases () { public function addValueAliases () { - $aliasedValues =& csscrush::$config->aliases[ 'values' ]; + $aliasedValues =& csscrush::$process->aliases[ 'values' ]; // First test for the existence of any aliased properties $intersect = array_intersect( array_keys( $aliasedValues ), array_keys( $this->properties ) ); @@ -487,7 +529,7 @@ public function expandSelectors () { foreach ( $this->selectorList as $readableValue => $selector ) { - $pos = strpos( $selector->value, ':any___' ); + $pos = strpos( $selector->value, ':any?' ); if ( $pos !== false ) { @@ -495,10 +537,10 @@ public function expandSelectors () { $chain = array( '' ); do { if ( $pos === 0 ) { - preg_match( '!:any(___p\d+___)!', $selector->value, $m ); + preg_match( '!:any(\?p\d+\?)!', $selector->value, $m ); // Parse the arguments - $expression = trim( csscrush::$storage->tokens->parens[ $m[1] ], '()' ); + $expression = trim( csscrush::$process->tokens->p[ $m[1] ], '()' ); $parts = preg_split( $reg_comma, $expression, null, PREG_SPLIT_NO_EMPTY ); $tmp = array(); @@ -516,7 +558,7 @@ public function expandSelectors () { } $selector->value = substr( $selector->value, $pos ); } - } while ( ( $pos = strpos( $selector->value, ':any___' ) ) !== false ); + } while ( ( $pos = strpos( $selector->value, ':any?' ) ) !== false ); // Finish off foreach ( $chain as &$row ) { @@ -551,14 +593,10 @@ public function setExtendSelectors ( $raw_value ) { $abstracts =& csscrush::$process->abstracts; $selectorRelationships =& csscrush::$process->selectorRelationships; - // Pass extra argument to trim the returned list - $args = csscrush_util::splitDelimList( $raw_value, ',', true, true ); - - // Reset if called earlier, last call wins by intention + // Reset if called earlier, last call wins by intention. $this->extendArgs = array(); - foreach ( $args->list as $arg ) { - + foreach ( csscrush_util::splitDelimList( $raw_value ) as $arg ) { $this->extendArgs[] = new csscrush_extendArg( $arg ); } } @@ -661,7 +699,7 @@ public function count() { public function propertyCount ( $prop ) { - if ( array_key_exists( $prop, $this->properties ) ) { + if ( isset( $this->properties[ $prop ] ) ) { return $this->properties[ $prop ]; } return 0; @@ -695,8 +733,8 @@ public function addDeclaration ( $prop, $value, $contextIndex = 0 ) { public static function get ( $token ) { - if ( isset( csscrush::$storage->tokens->rules[ $token ] ) ) { - return csscrush::$storage->tokens->rules[ $token ]; + if ( isset( csscrush::$process->tokens->r[ $token ] ) ) { + return csscrush::$process->tokens->r[ $token ]; } return null; } @@ -718,7 +756,6 @@ class csscrush_declaration { public $index; public $skip; public $important; - public $parenTokens; public $isValid = true; public function __construct ( $prop, $value, $contextIndex = 0 ) { @@ -768,12 +805,10 @@ public function __construct ( $prop, $value, $contextIndex = 0 ) { csscrush_function::executeCustomFunctions( $value ); } - // Tokenize all remaining paren pairs - $match_obj = csscrush_util::matchAllBrackets( $value ); - $this->parenTokens = $match_obj->matches; - $value = $match_obj->string; + // Capture all remaining paren pairs. + csscrush::captureParens( $value ); - // Create an index of all regular functions in the value + // Create an index of all regular functions in the value. if ( preg_match_all( $regex->function, $value, $functions ) > 0 ) { $out = array(); foreach ( $functions[2] as $index => $fn_name ) { @@ -797,7 +832,7 @@ public function __construct ( $prop, $value, $contextIndex = 0 ) { public function getFullValue () { - return csscrush_util::tokenReplace( $this->value, $this->parenTokens ); + return csscrush::tokenRestoreAll( $this->value, 'p' ); } } @@ -818,8 +853,8 @@ class csscrush_selector { public static function makeReadableSelector ( $selector_string ) { // Quick test for paren tokens - if ( strpos( $selector_string, '___p' ) !== false ) { - $selector_string = csscrush_util::tokenReplaceAll( $selector_string, 'parens' ); + if ( strpos( $selector_string, '?p' ) !== false ) { + $selector_string = csscrush::tokenRestoreAll( $selector_string, 'p' ); } // Create space around combinators, then normalize whitespace @@ -827,8 +862,8 @@ public static function makeReadableSelector ( $selector_string ) { $selector_string = csscrush_util::normalizeWhiteSpace( $selector_string ); // Quick test for string tokens - if ( strpos( $selector_string, '___s' ) !== false ) { - $selector_string = csscrush_util::tokenReplaceAll( $selector_string, 'strings' ); + if ( strpos( $selector_string, '?s' ) !== false ) { + $selector_string = csscrush::tokenRestoreAll( $selector_string, 's' ); } // Quick test for double-colons for backwards compat diff --git a/lib/Util.php b/lib/Util.php index 3ebb656..fd12f80 100644 --- a/lib/Util.php +++ b/lib/Util.php @@ -4,7 +4,6 @@ * Utilities * */ - class csscrush_util { @@ -26,32 +25,18 @@ public static function strEndsWith ( $haystack, $needle ) { } - public static function normalizePath ( $path, $strip_ms_dos = false ) { - - $path = rtrim( preg_replace( '![\\\\/]+!', '/', $path ), '/' ); + public static function normalizePath ( $path, $strip_drive_letter = false ) { - if ( $strip_ms_dos ) { + if ( $strip_drive_letter ) { $path = preg_replace( '!^[a-z]\:!i', '', $path ); } - return $path; - } - - - public static function cleanUpUrl ( $url ) { - - // Reduce redundant path segments (issue #32): - // e.g 'foo/../bar' => 'bar' - $patt = '![^/.]+/\.\./!'; - - while ( preg_match( $patt, $url ) ) { - $url = preg_replace( $patt, '', $url ); - } + // Backslashes and repeat slashes to a single forward slash. + $path = rtrim( preg_replace( '![\\\\/]+!', '/', $path ), '/' ); - if ( strpos( $url, '(' ) !== false || strpos( $url, ')' ) !== false ) { - $url = "\"$url\""; - } + // Removing redundant './'. + $path = preg_replace( '!^\./|/\./!', '', $path ); - return $url; + return $path; } @@ -79,7 +64,8 @@ public static function find () { public static function stripCommentTokens ( $str ) { - return preg_replace( csscrush_regex::$patt->commentToken, '', $str ); + + return preg_replace( csscrush_regex::$patt->cToken, '', $str ); } @@ -98,92 +84,57 @@ public static function normalizeWhiteSpace ( $str ) { } - public static function tokenReplace ( $string, $token_replace, $type = 'parens' ) { - - // The tokens to replace - $token_replace = (array) $token_replace; + public static function splitDelimList ( $str, $delim = ',', $trim = true ) { - // Reference the token table - $token_table =& csscrush::$storage->tokens->{ $type }; + $do_preg_split = strlen( $delim ) > 1 ? true : false; - // Replace the tokens listed - foreach ( $token_replace as $token ) { - if ( isset( $token_table[ $token ] ) ) { - $string = str_replace( $token, $token_table[ $token ], $string ); + if ( ! $do_preg_split && strpos( $str, $delim ) === false ) { + if ( $trim ) { + $str = trim( $str ); } + return array( $str ); } - return $string; - } - - - public static function tokenReplaceAll ( $str, $type = 'parens' ) { - - // Only $type 'parens' or 'strings' make any sense - $token_patt = 'parenToken'; - - if ( $type === 'strings' ) { - $token_patt = 'stringToken'; + if ( strpos( $str, '(' ) !== false ) { + $match_count + = preg_match_all( csscrush_regex::$patt->balancedParens, $str, $matches ); + } + else { + $match_count = 0; } - // Reference the token table - $token_table =& csscrush::$storage->tokens->{ $type }; - - // Find tokens - $matches = csscrush_regex::matchAll( csscrush_regex::$patt->{ $token_patt }, $str ); - - foreach ( $matches as $m ) { - - $token = $m[0][0]; - - if ( isset( $token_table[ $token ] ) ) { - - $str = str_replace( $token, $token_table[ $token ], $str ); + if ( $match_count ) { + $keys = array(); + foreach ( $matches[0] as $index => &$value ) { + $keys[] = "?$index?"; } + $str = str_replace( $matches[0], $keys, $str ); } - return $str; - } - - public static function splitDelimList ( $str, $delim, $fold_in = false, $trim = false ) { - - $match_obj = self::matchAllBrackets( $str ); - - // If the delimiter is one character do a simple split - // Otherwise do a regex split - if ( 1 === strlen( $delim ) ) { - - $match_obj->list = explode( $delim, $match_obj->string ); + if ( $do_preg_split ) { + $list = preg_split( '!' . $delim . '!', $str ); } else { - - $match_obj->list = preg_split( '!' . $delim . '!', $match_obj->string ); - } - - if ( true === $trim ) { - $match_obj->list = array_map( 'trim', $match_obj->list ); + $list = explode( $delim, $str ); } - // Filter out empties - foreach( $match_obj->list as $key => &$item ) { - if ( $item === '' ) { - unset( $match_obj->list[$key] ); + if ( $match_count ) { + foreach ( $list as &$value ) { + $value = str_replace( $keys, $matches[0], $value ); } } - if ( $fold_in ) { - - foreach ( $match_obj->list as &$item ) { - $item = csscrush_util::tokenReplace( $item, $match_obj->matches ); - } + if ( $trim ) { + $list = array_map( 'trim', $list ); } - return $match_obj; + + return $list; } - public static function matchBrackets ( $str, $brackets = array( '(', ')' ), $offset = 0, $capture_text = false ) { + public static function matchBrackets ( $str, $brackets = '()', $offset = 0, $capture_text = false ) { - list( $opener, $closer ) = $brackets; + list( $opener, $closer ) = str_split( $brackets, 1 ); $match = (object) array(); @@ -204,12 +155,12 @@ public static function matchBrackets ( $str, $brackets = array( '(', ')' ), $off if ( preg_match( $patt, $str, $m, PREG_OFFSET_CAPTURE, $offset ) ) { - $match->start = $start = $m[0][1]; - $match->end = $start + strlen( $m[0][0] ); + $match->start = $m[0][1]; + $match->end = $match->start + strlen( $m[0][0] ); if ( $capture_text ) { // Text capturing is optional to avoid using memory when not necessary. - $match->inside = substr( $str, $start + 1, $match->end - $start - 2 ); + $match->inside = $m[1][0]; $match->after = substr( $str, $match->end ); } return $match; @@ -220,85 +171,170 @@ public static function matchBrackets ( $str, $brackets = array( '(', ')' ), $off } - public static function matchAllBrackets ( $str, $pair = '()', $offset = 0 ) { - - $match_obj = (object) array(); - $match_obj->matches = array(); - - list( $opener, $closer ) = str_split( $pair, 1 ); - - // Return early if there's no match. - if ( false === strpos( $str, $opener, $offset ) ) { - $match_obj->string = $str; - return $match_obj; - } - - $patt = csscrush_regex::$patt->balancedParens; - if ( $opener === '{' ) { - $patt = csscrush_regex::$patt->balancedCurlies; - } + public static function getLinkBetweenDirs ( $dir1, $dir2 ) { - // Match balanced bracket pairs. - $offsets = array(); - foreach ( csscrush_regex::matchAll( $patt, $str ) as $m ) { - $offsets[] = array( $m[0][1], $m[0][1] + strlen( $m[0][0] ) ); - } + // Normalise the paths. + $dir1 = trim( csscrush_util::normalizePath( $dir1, true ), '/' ); + $dir2 = trim( csscrush_util::normalizePath( $dir2, true ), '/' ); - // Step backwards through the matches. - while ( $offset = array_pop( $offsets ) ) { + // The link between. + $link = ''; - list( $start, $finish ) = $offset; + if ( $dir1 != $dir2 ) { - $before = substr( $str, 0, $start ); - $content = substr( $str, $start, $finish - $start ); - $after = substr( $str, $finish ); + // Split the directory paths into arrays so we can compare segment by segment. + $dir1_segs = explode( '/', $dir1 ); + $dir2_segs = explode( '/', $dir2 ); - $label = csscrush::tokenLabelCreate( 'p' ); - $str = $before . $label . $after; - $match_obj->matches[] = $label; + // Shift the segments until they are on different branches. + while ( isset( $dir1_segs[0] ) && isset( $dir2_segs[0] ) && ( $dir1_segs[0] === $dir2_segs[0] ) ) { + array_shift( $dir1_segs ); + array_shift( $dir2_segs ); + } - // Parens will be folded in later. - csscrush::$storage->tokens->parens[ $label ] = $content; + $link = str_repeat( '../', count( $dir1_segs ) ) . implode( '/', $dir2_segs ); } - $match_obj->string = $str; - - return $match_obj; + // Add closing slash. + return $link !== '' ? rtrim( $link, '/' ) . '/' : ''; } } /** * - * String sugar + * URL tokens. * */ -class csscrush_string { +class csscrush_url { - public $token; + public $protocol; + public $isRelative; + public $isRooted; + public $convertToData; public $value; - public $quoteMark; + public $label; - public static function add ( $full_match ) { - $label = csscrush::tokenLabelCreate( 's' ); - csscrush::$storage->tokens->strings[ $label ] = $full_match; - return $label; - } + public function __construct ( $raw_value, $convert_to_data = false ) { - public function __construct ( $token ) { + $regex = csscrush_regex::$patt; - $this->token = trim( $token ); - $raw = csscrush::$storage->tokens->strings[ $this->token ]; - $this->value = trim( $raw, '\'"' ); - $this->quoteMark = $raw[0]; + if ( preg_match( $regex->sToken, $raw_value ) ) { + $this->value = trim( csscrush::tokenFetch( $raw_value ), '\'"' ); + csscrush::tokenRelease( $raw_value ); + } + else { + $this->value = $raw_value; + } + + $this->evaluate(); + $this->label = csscrush::tokenLabelCreate( 'u' ); + csscrush::$process->tokens->u[ $this->label ] = $this; } public function __toString () { - return csscrush::$storage->tokens->strings[ $this->token ]; + $quote = ''; + if ( preg_match( '![()*]!', $this->value ) || 'data' === $this->protocol ) { + $quote = '"'; + } + return "url(http://github.com/$quote$this-%3Evalue$quote)"; + } + + public static function get ( $token ) { + return csscrush::$process->tokens->u[ $token ]; + } + + public function evaluate () { + + $leading_variable = strpos( $this->value, '$(' ) === 0; + + if ( preg_match( '!^([a-z]+)\:!i', $this->value, $m ) ) { + $this->protocol = strtolower( $m[1] ); + } + else { + // Normalize './' led paths. + $this->value = preg_replace( '!^\.\/+!i', '', $this->value ); + if ( $this->value[0] === '/' ) { + $this->isRooted = true; + } + elseif ( ! $leading_variable ) { + $this->isRelative = true; + } + // Normalize slashes. + $this->value = rtrim( preg_replace( '![\\\\/]+!', '/', $this->value ), '/' ); + } + } + + public function applyVariables () { + csscrush::placeVariables( $this->value ); + $this->evaluate(); + } + + public function toData () { + + if ( $this->isRooted ) { + $file = csscrush::$config->docRoot . $this->value; + } + else { + $file = csscrush::$process->input->dir . "/$this->value"; + } + + // File not found. + if ( ! file_exists( $file ) ) { + return; + } + + $file_ext = pathinfo( $file, PATHINFO_EXTENSION ); + + // Only allow certain extensions + static $allowed_file_extensions = array( + 'woff' => 'application/x-font-woff;charset=utf-8', + 'ttf' => 'font/truetype;charset=utf-8', + 'svg' => 'image/svg+xml', + 'svgz' => 'image/svg+xml', + 'gif' => 'image/gif', + 'jpeg' => 'image/jpg', + 'jpg' => 'image/jpg', + 'png' => 'image/png', + ); + + if ( ! isset( $allowed_file_extensions[ $file_ext ] ) ) { + return; + } + + $mime_type = $allowed_file_extensions[ $file_ext ]; + $base64 = base64_encode( file_get_contents( $file ) ); + $this->value = "data:$mime_type;base64,$base64"; + $this->protocol = 'data'; } - public function update ( $newValue ) { - csscrush::$storage->tokens->strings[ $this->token ] = $newValue; + public function prepend ( $path_fragment ) { + $this->value = $path_fragment . $this->value; + } + + public function resolveRootedPath () { + + $config = csscrush::$config; + $process = csscrush::$process; + + if ( ! file_exists ( $config->docRoot . $this->value ) ) { + return false; + } + + // Move upwards '..' by the number of slashes in baseURL to get a relative path. + $this->value = str_repeat( '../', substr_count( $process->input->dirUrl, '/' ) ) . + substr( $this->value, 1 ); + } + + public function simplify () { + + // Reduce redundant path segments (issue #32): + // e.g 'foo/../bar' => 'bar' + $patt = '![^/.]+/\.\./!'; + + while ( preg_match( $patt, $this->value ) ) { + $this->value = preg_replace( $patt, '', $this->value ); + } } } diff --git a/plugins/hsl-to-hex.php b/plugins/hsl-to-hex.php index a84e524..179dd7f 100644 --- a/plugins/hsl-to-hex.php +++ b/plugins/hsl-to-hex.php @@ -19,10 +19,10 @@ function csscrush__hsl_to_hex ( csscrush_rule $rule ) { ! $declaration->skip && ( ! empty( $declaration->functions ) && in_array( 'hsl', $declaration->functions ) ) ) { - while ( preg_match( '!hsl(___p\d+___)!', $declaration->value, $m ) ) { + while ( preg_match( '!hsl(\?p\d+\?)!', $declaration->value, $m ) ) { $full_match = $m[0]; $token = $m[1]; - $hsl = trim( csscrush::$storage->tokens->parens[ $token ], '()' ); + $hsl = trim( csscrush::$process->tokens->p[ $token ], '()' ); $hsl = array_map( 'trim', explode( ',', $hsl ) ); $rgb = csscrush_color::cssHslToRgb( $hsl ); $hex = csscrush_color::rgbToHex( $rgb ); diff --git a/plugins/property-sorter.php b/plugins/property-sorter.php index 5c1cc91..3d02191 100644 --- a/plugins/property-sorter.php +++ b/plugins/property-sorter.php @@ -5,10 +5,9 @@ * * Examples use the predefined sorting table. * - * To define a custom sorting table globally define $CSSCRUSH_PROPERTY_SORT_ORDER. - * Assign an empty array to create an alphabetical sort: + * To define a custom sorting order pass an array to csscrush_set_property_sort_order(): * - * $CSSCRUSH_PROPERTY_SORT_ORDER = array( 'color', ... ); + * csscrush_set_property_sort_order( array( 'color', ... ) ); * * * @before @@ -43,7 +42,7 @@ function csscrush__property_sorter ( csscrush_rule $rule ) { /* - Callback for sorting + Callback for sorting. */ function _csscrush__property_sorter_callback ( $a, $b ) { @@ -124,9 +123,6 @@ function _csscrush__property_sorter_callback ( $a, $b ) { /* Cache for the table of values to compare against. - - If you need to re-define the sort table during runtime unset the cache first: - unset( $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER_CACHE' ] ); */ function &_csscrush__property_sorter_get_table () { @@ -145,7 +141,7 @@ function &_csscrush__property_sorter_get_table () { // No user-defined table, use pre-defined. else { - // Load from property-sorting.ini + // Load from property-sorting.ini. if ( $sorting_file_contents = file_get_contents( csscrush::$config->location . '/misc/property-sorting.ini' ) ) { @@ -155,6 +151,9 @@ function &_csscrush__property_sorter_get_table () { else { trigger_error( __METHOD__ . ": Property sorting file not found.\n", E_USER_NOTICE ); } + + // Store to the global variable. + $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER' ] = $table; } // Cache the table (and flip it). @@ -162,3 +161,22 @@ function &_csscrush__property_sorter_get_table () { return $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER_CACHE' ]; } + + +/* + Get the current sorting table. +*/ +function csscrush_get_property_sort_order () { + _csscrush__property_sorter_get_table(); + return $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER' ]; +} + + +/* + Set a custom sorting table. +*/ +function csscrush_set_property_sort_order ( array $new_order ) { + unset( $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER_CACHE' ] ); + $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER' ] = $new_order; +} + diff --git a/plugins/rgba-fallback.php b/plugins/rgba-fallback.php index 487043d..609bd49 100644 --- a/plugins/rgba-fallback.php +++ b/plugins/rgba-fallback.php @@ -36,7 +36,7 @@ function csscrush__rgba_fallback ( csscrush_rule $rule ) { if ( $declaration->skip || ! $is_viable || - $is_viable && !preg_match( '!^rgba___p\d+___$!', $declaration->value ) + $is_viable && !preg_match( '!^rgba\?p\d+\?$!', $declaration->value ) ) { $new_set[] = $declaration; continue; From 5fe6157ac8282039b60ba6bed7f79ff0ad33b43f Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 19 Oct 2012 11:47:46 +0100 Subject: [PATCH 051/421] Added selector aliases. Added viewport @-rule aliases. Extensive refactoring. --- Aliases.ini | 38 +- CHANGELOG.txt | 3 + CssCrush.php | 1 + Plugins.ini | 6 +- lib/Color.php | 17 +- lib/Core.php | 1243 +------------------------------------ lib/Function.php | 49 +- lib/Hook.php | 3 +- lib/IO.php | 28 +- lib/Importer.php | 265 ++++++-- lib/Mixin.php | 16 +- lib/Plugin.php | 5 - lib/Process.php | 1066 +++++++++++++++++++++++++++++++ lib/Regex.php | 52 +- lib/Rule.php | 121 ++-- lib/Util.php | 279 ++++++--- misc/property-sorting.ini | 2 +- plugins/hocus-pocus.php | 12 +- 18 files changed, 1670 insertions(+), 1536 deletions(-) create mode 100644 lib/Process.php diff --git a/Aliases.ini b/Aliases.ini index 9e89136..7c139bd 100644 --- a/Aliases.ini +++ b/Aliases.ini @@ -1,12 +1,15 @@ - -;---------------------------------------------------------------------------- +;---------------------------------------------------------------- +; +; Add or delete aliases to suit your needs. ; -; Enable/disable aliases to suit your needs +; Sources: +; http://developer.mozilla.org/en-US/docs/CSS/CSS_Reference +; http://css3please.com +; http://caniuse.com/#cats=CSS ; -;---------------------------------------------------------------------------- +;---------------------------------------------------------------- -;---------------------------------------------------------------------------- -;-- Property aliases +; Property aliases [properties] @@ -189,19 +192,16 @@ user-select[] = user-select -;---------------------------------------------------------------------------- -;-- Property:value aliases +;---------------------------------------------------------------- +; Property:value aliases [values] - ; Flexbox - display:box[] = -webkit-box - display:box[] = -moz-box - display:box[] = -ms-box + ; Flexbox TBC. -;---------------------------------------------------------------------------- -;-- Function aliases +;---------------------------------------------------------------- +; Function aliases [functions] @@ -229,13 +229,19 @@ repeating-radial-gradient[] = -o-repeating-radial-gradient -;---------------------------------------------------------------------------- -;-- @rule aliases +;---------------------------------------------------------------- +; @rule aliases [at-rules] + ; Keyframes keyframes[] = -webkit-keyframes keyframes[] = -moz-keyframes keyframes[] = -o-keyframes + ; Viewport + viewport[] = -webkit-viewport + viewport[] = -moz-viewport + viewport[] = -ms-viewport + viewport[] = -o-viewport diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 7ab6ea1..d5d4cd0 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,6 +1,9 @@ 1.8 ----- +Added selector aliasing with @selector-alias directive. +Added viewport @-rule aliases. Improved minification. +Major refactoring. 1.7 diff --git a/CssCrush.php b/CssCrush.php index 499275e..9a1d201 100644 --- a/CssCrush.php +++ b/CssCrush.php @@ -13,6 +13,7 @@ require_once 'lib/Util.php'; require_once 'lib/IO.php'; require_once 'lib/Core.php'; +require_once 'lib/Process.php'; require_once 'lib/Rule.php'; require_once 'lib/Mixin.php'; require_once 'lib/Function.php'; diff --git a/Plugins.ini b/Plugins.ini index ee9030f..717dd00 100644 --- a/Plugins.ini +++ b/Plugins.ini @@ -1,7 +1,7 @@ - ;---------------------------------------------------------------- ; -; Enable/disable plugins to suit your needs. +; Enable or disable plugins to suit your needs. +; ; See plugin docs for details: https://github.com/peteboere/css-crush/wiki/Plugins ; ;---------------------------------------------------------------- @@ -13,7 +13,7 @@ ; plugins[] = ie-min-height ; inline-block shim for IE < 8 -plugins[] = ie-inline-block +; plugins[] = ie-inline-block ; clip property shim for IE < 8 ; plugins[] = ie-clip diff --git a/lib/Color.php b/lib/Color.php index 4df58dc..fdf502d 100644 --- a/lib/Color.php +++ b/lib/Color.php @@ -4,12 +4,11 @@ * Colour parsing and conversion * */ - class csscrush_color { protected static $keywords = array(); - public static function getKeywords () { + static public function getKeywords () { // Load the keywords if necessary if ( empty( self::$keywords ) ) { $path = csscrush::$config->location . '/misc/color-keywords.ini'; @@ -32,7 +31,7 @@ public static function getKeywords () { * Assumes r, g, and b are contained in the set [0, 255] and * returns h, s, and l in the set [0, 1]. */ - public static function rgbToHsl ( array $rgb ) { + static public function rgbToHsl ( array $rgb ) { list( $r, $g, $b ) = $rgb; $r /= 255; @@ -76,7 +75,7 @@ public static function rgbToHsl ( array $rgb ) { * Assumes h, s, and l are contained in the set [0, 1] and * returns r, g, and b in the set [0, 255]. */ - public static function hslToRgb ( array $hsl ) { + static public function hslToRgb ( array $hsl ) { list( $h, $s, $l ) = $hsl; $r; $g; @@ -95,7 +94,7 @@ public static function hslToRgb ( array $hsl ) { } // Convert percentages to points (0-255) - public static function normalizeCssRgb ( array $rgb ) { + static public function normalizeCssRgb ( array $rgb ) { foreach ( $rgb as &$val ) { if ( strpos( $val, '%' ) !== false ) { $val = str_replace( '%', '', $val ); @@ -105,7 +104,7 @@ public static function normalizeCssRgb ( array $rgb ) { return $rgb; } - public static function cssHslToRgb ( array $hsl ) { + static public function cssHslToRgb ( array $hsl ) { // Normalize the hue degree value then convert to float $h = array_shift( $hsl ); @@ -128,7 +127,7 @@ public static function cssHslToRgb ( array $hsl ) { return $rgb; } - public static function hueToRgb ( $p, $q, $t ) { + static public function hueToRgb ( $p, $q, $t ) { if ( $t < 0 ) $t += 1; if ( $t > 1 ) $t -= 1; if ( $t < 1/6 ) return $p + ( $q - $p ) * 6 * $t; @@ -137,7 +136,7 @@ public static function hueToRgb ( $p, $q, $t ) { return $p; } - public static function rgbToHex ( array $rgb ) { + static public function rgbToHex ( array $rgb ) { $hex_out = '#'; foreach ( $rgb as $val ) { $hex_out .= str_pad( dechex( $val ), 2, '0', STR_PAD_LEFT ); @@ -145,7 +144,7 @@ public static function rgbToHex ( array $rgb ) { return $hex_out; } - public static function hexToRgb ( $hex ) { + static public function hexToRgb ( $hex ) { $hex = substr( $hex, 1 ); // Handle shortened format diff --git a/lib/Core.php b/lib/Core.php index 48afcac..02b28d1 100644 --- a/lib/Core.php +++ b/lib/Core.php @@ -8,14 +8,13 @@ class csscrush { // Global settings. - public static $config; + static public $config; // The current active process. - public static $process; - + static public $process; // Init called once manually post class definition. - public static function init ( $seed_file ) { + static public function init ( $seed_file ) { self::$config = new stdclass(); @@ -36,6 +35,7 @@ public static function init ( $seed_file ) { // Global storage. self::$config->vars = array(); self::$config->aliases = array(); + self::$config->selectorAliases = array(); self::$config->plugins = array(); // Default options. @@ -80,8 +80,7 @@ public static function init ( $seed_file ) { csscrush_function::init(); } - - public static function setDocRoot ( $doc_root = null ) { + static protected function setDocRoot ( $doc_root = null ) { // Get document_root reference // $_SERVER['DOCUMENT_ROOT'] is unreliable in certain CGI/Apache/IIS setups @@ -124,25 +123,10 @@ public static function setDocRoot ( $doc_root = null ) { self::$config->docRoot = csscrush_util::normalizePath( $doc_root ); } - - public static function io_call ( $method ) { - - // Fetch the argument list, shift off the first item - $args = func_get_args(); - array_shift( $args ); - - // The method address - $the_method = array( self::$config->io, $method ); - - // Return the call result - return call_user_func_array( $the_method, $args ); - } - - // Aliases and macros loader. - protected static function loadAssets () { + static public function loadAssets () { - static $called = false; + static $called; if ( $called ) { return; } @@ -208,41 +192,6 @@ protected static function loadAssets () { } - // Establish the input and output directories and optionally test output dir. - protected static function setContext ( $input_dir, $test_output_dir = true ) { - - $config = self::$config; - $process = self::$process; - $doc_root = $config->docRoot; - - if ( strpos( $input_dir, $doc_root ) !== 0 ) { - // Not a system path. - $input_dir = realpath( "$doc_root/$input_dir" ); - } - - // Initialise input object and store input directory. - $process->input->path = null; - $process->input->filename = null; - $process->input->dir = $input_dir; - $process->input->dirUrl = substr( $process->input->dir, strlen( $doc_root ) ); - - // Store reference to the output dir. - $process->output->dir = csscrush::io_call( 'getOutputDir' ); - $process->output->dirUrl = substr( $process->output->dir, strlen( $doc_root ) ); - - // Test the output directory to see it exists and is writable. - $pathtest = false; - if ( $test_output_dir ) { - $pathtest = csscrush::io_call( 'testOutputDir' ); - } - - // Setup the IO handler. - csscrush::io_call( 'init' ); - - return $pathtest; - } - - ############################# # External API. @@ -253,10 +202,9 @@ protected static function setContext ( $input_dir, $test_output_dir = true ) { * @param mixed $options An array of options or null * @return string The public path to the compiled file or an empty string */ - public static function file ( $file, $options = null ) { + static public function file ( $file, $options = null ) { - // Reset for current process - self::reset( $options ); + self::$process = new csscrush_process( $options ); $config = self::$config; $process = self::$process; @@ -270,15 +218,15 @@ public static function file ( $file, $options = null ) { $pathtest = true; if ( strpos( $file, $doc_root ) === 0 ) { // System path. - $pathtest = self::setContext( dirname( $file ) ); + $pathtest = $process->setContext( dirname( $file ) ); } else if ( strpos( $file, '/' ) === 0 ) { // WWW root path. - $pathtest = self::setContext( dirname( $doc_root . $file ) ); + $pathtest = $process->setContext( dirname( $doc_root . $file ) ); } else { // Relative path. - $pathtest = self::setContext( dirname( dirname( __FILE__ ) . '/' . $file ) ); + $pathtest = $process->setContext( dirname( dirname( __FILE__ ) . '/' . $file ) ); } if ( ! $pathtest ) { @@ -293,27 +241,24 @@ public static function file ( $file, $options = null ) { // Create a filename that will be used later // Used in validateCache, and writing to filesystem - $process->output->filename = csscrush::io_call( 'getOutputFileName' ); + $process->output->filename = $process->ioCall( 'getOutputFileName' ); // Caching. if ( $options->cache ) { // Load the cache data. - $process->cacheData = csscrush::io_call( 'getCacheData' ); + $process->cacheData = $process->ioCall( 'getCacheData' ); // If cache is enabled check for a valid compiled file. - $valid_compliled_file = csscrush::io_call( 'validateExistingOutput' ); + $valid_compliled_file = $process->ioCall( 'validateExistingOutput' ); if ( is_string( $valid_compliled_file ) ) { return $valid_compliled_file; } } - // Collate hostfile and imports. - $stream = csscrush_importer::hostfile( $process->input ); - // Compile. - $stream = self::compile( $stream ); + $stream = $process->compile(); // Create file and return url. Return empty string on failure. if ( file_put_contents( "{$process->output->dir}/{$process->output->filename}", $stream ) ) { @@ -333,7 +278,7 @@ public static function file ( $file, $options = null ) { * @param array $attributes An array of HTML attributes * @return string HTML link tag or error message inside HTML comment */ - public static function tag ( $file, $options = null, $attributes = array() ) { + static public function tag ( $file, $options = null, $attributes = array() ) { $file = self::file( $file, $options ); @@ -367,7 +312,7 @@ public static function tag ( $file, $options = null, $attributes = array() ) { * @param array $attributes An array of HTML attributes, set false to return CSS text without tag * @return string HTML link tag or error message inside HTML comment */ - public static function inline ( $file, $options = null, $attributes = array() ) { + static public function inline ( $file, $options = null, $attributes = array() ) { // For inline output set boilerplate to not display by default if ( ! is_array( $options ) ) { @@ -410,15 +355,14 @@ public static function inline ( $file, $options = null, $attributes = array() ) * @param mixed $options An array of options or null * @return string CSS text */ - public static function string ( $string, $options = null ) { + static public function string ( $string, $options = null ) { // For strings set boilerplate to not display by default if ( ! isset( $options[ 'boilerplate' ] ) ) { $options[ 'boilerplate' ] = false; } - // Reset for current process - self::reset( $options ); + self::$process = new csscrush_process( $options ); $config = self::$config; $process = self::$process; @@ -427,10 +371,10 @@ public static function string ( $string, $options = null ) { // Set the path context if one is given. // Fallback to document root. if ( ! empty( $options->context ) ) { - self::setContext( $options->context, false ); + $process->setContext( $options->context, false ); } else { - self::setContext( $config->docRoot, false ); + $process->setContext( $config->docRoot, false ); } // Set the string on the input object. @@ -441,11 +385,8 @@ public static function string ( $string, $options = null ) { $process->input->importIgnore = true; } - // Collate imports - $stream = csscrush_importer::hostfile( $process->input ); - - // Return compiled string - return self::compile( $stream ); + // Compile and return. + return $process->compile(); } /** @@ -453,7 +394,7 @@ public static function string ( $string, $options = null ) { * * @param mixed $var Assoc array of variable names and values, a php ini filename or null */ - public static function globalVars ( $vars ) { + static public function globalVars ( $vars ) { $config = self::$config; @@ -478,17 +419,17 @@ public static function globalVars ( $vars ) { * * @param string $dir System path to the directory */ - public static function clearCache ( $dir = '' ) { - return csscrush::io_call( 'clearCache', $dir ); + static public function clearCache ( $dir = '' ) { + return $process->ioCall( 'clearCache', $dir ); } ############################# # Internal development. - public static $logging = false; + static public $logging = false; - public static function log ( $arg = null, $label = '' ) { + static public function log ( $arg = null, $label = '' ) { if ( ! self::$logging ) { return; @@ -518,1132 +459,11 @@ public static function log ( $arg = null, $label = '' ) { } } - public static function logError ( $msg ) { + static public function logError ( $msg ) { self::$process->errors[] = $msg; self::log( $msg ); } - - ############################# - # Internal functions. - - public static function prepareStream ( &$stream ) { - - $regex = csscrush_regex::$patt; - $process = csscrush::$process; - $trace = $process->options->trace; - - $stream = preg_replace_callback( $regex->commentAndString, - array( 'self', 'cb_extractCommentAndString' ), $stream ); - - // If @charset is set store it. - if ( preg_match( $regex->charset, $stream, $m ) ) { - $replace = ''; - if ( ! $process->charset ) { - // Keep track of newlines for line tracing. - $replace = str_repeat( "\n", substr_count( $m[0], "\n" ) ); - $process->charset = trim( csscrush::tokenFetch( $m[1] ), '"\'' ); - } - $stream = preg_replace( $regex->charset, $replace, $stream ); - } - - // Catch obvious typing errors. - $parse_errors = array(); - $current_file = $process->currentFile; - $balanced_parens = substr_count( $stream, "(" ) === substr_count( $stream, ")" ); - $balanced_curlies = substr_count( $stream, "{" ) === substr_count( $stream, "}" ); - - if ( ! $balanced_parens ) { - $parse_errors[] = "Unmatched '(' in $current_file."; - } - if ( ! $balanced_curlies ) { - $parse_errors[] = "Unmatched '{' in $current_file."; - } - - if ( $parse_errors ) { - foreach ( $parse_errors as $error_msg ) { - csscrush::logError( $error_msg ); - trigger_error( "$error_msg\n", E_USER_WARNING ); - } - return false; - } - - // Optionally add tracing stubs. - if ( $trace ) { - self::addTracingStubs( $stream ); - } - - // Strip unneeded whitespace. - $stream = csscrush_util::normalizeWhiteSpace( $stream ); - - // Tokenize all the URLs. - $patt = '# - @import\x20(\?s\d+\?) - | - (?label, $stream ); - } - // Match parenthesis if not a string token. - elseif ( preg_match( $regex->balancedParens, $stream, $inner_m, PREG_OFFSET_CAPTURE, $outer_offset ) ) { - $url = new csscrush_url(http://github.com/$inner_m[1][0]); - $func_name = strtolower( $outer_m[2][0] ); - $url->convertToData = 'data-uri' === $func_name; - $stream = substr_replace( $stream, $url->label, $outer_offset, - strlen( $func_name ) + strlen( $inner_m[0][0] ) ); - } - // If brackets cannot be matched, skip over the original match. - else { - $offset += strlen( $outer_m[0][0] ); - } - } - - return true; - } - - public static function addTracingStubs ( &$stream ) { - - $selector_patt = '! (^|;|\})+ ([^;{}]+) (\{) !xmS'; - $token_or_whitespace = '!(\s*\?c\d+\?\s*|\s+)!'; - - $matches = csscrush_regex::matchAll( $selector_patt, $stream ); - - // Start from last match and move backwards. - while ( $m = array_pop( $matches ) ) { - - // Shortcuts for readability. - list( $full_match, $before, $content, $after ) = $m; - $full_match_text = $full_match[0]; - $full_match_start = $full_match[1]; - - // The correct before string. - $before = substr( $full_match_text, 0, $content[1] - $full_match_start ); - - // Split the matched selector part. - $content_parts = preg_split( $token_or_whitespace, $content[0], null, - PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY ); - - foreach ( $content_parts as $part ) { - - if ( ! preg_match( $token_or_whitespace, $part ) ) { - - // Match to a valid selector. - if ( preg_match( '!^([^@]|@(?:page|abstract))!S', $part ) ) { - - // Count line breaks between the start of stream and - // the matched selector to get the line number. - $selector_index = $full_match_start + strlen( $before ); - $line_num = 1; - $stream_before = ""; - if ( $selector_index ) { - $stream_before = substr( $stream, 0, $selector_index ); - $line_num = substr_count( $stream_before, "\n" ) + 1; - } - - // Get the currently processed file path, and escape it. - $current_file = str_replace( ' ', '%20', csscrush::$process->currentFile ); - $current_file = preg_replace( '![^\w-]!', '\\\\$0', $current_file ); - - // Splice in tracing stub. - $label = csscrush::tokenLabelCreate( 't' ); - $stream = $stream_before . "$label" . substr( $stream, $selector_index ); - self::$process->tokens->t[ $label ] - = "@media -sass-debug-info{filename{font-family:$current_file}line{font-family:\\00003$line_num}}"; - - } - else { - // Not matched as a valid selector, move on. - continue 2; - } - break; - } - - // Append split segment to $before. - $before .= $part; - } - } - } - - protected static function getBoilerplate () { - - $file = false; - $boilerplate_option = self::$process->options->boilerplate; - - if ( $boilerplate_option === true ) { - $file = csscrush_util::find( - 'CssCrush-local.boilerplate', 'CssCrush.boilerplate' ); - } - elseif ( is_string( $boilerplate_option ) ) { - if ( file_exists( $boilerplate_option ) ) { - $file = $boilerplate_option; - } - } - - // Return an empty string if no file is found. - if ( ! $file ) { - return ''; - } - - // Load the file - $boilerplate = file_get_contents( $file ); - - // Substitute any tags - if ( preg_match_all( '!\{\{([^}]+)\}\}!', $boilerplate, $boilerplate_matches ) ) { - - $replacements = array(); - foreach ( $boilerplate_matches[0] as $index => $tag ) { - $tag_name = $boilerplate_matches[1][$index]; - if ( $tag_name === 'datetime' ) { - $replacements[] = @date( 'Y-m-d H:i:s O' ); - } - elseif ( $tag_name === 'version' ) { - $replacements[] = 'v' . csscrush::$config->version; - } - else { - $replacements[] = '?'; - } - } - $boilerplate = str_replace( $boilerplate_matches[0], $replacements, $boilerplate ); - } - - // Pretty print - $boilerplate = explode( PHP_EOL, $boilerplate ); - $boilerplate = array_map( 'trim', $boilerplate ); - $boilerplate = array_map( create_function( '$it', 'return !empty($it) ? " $it" : $it;' ), $boilerplate ); - $boilerplate = implode( PHP_EOL . ' *', $boilerplate ); - return <<vars; - - // Populate unset options with defaults - $options += (array) self::$config->options; - - return (object) $options; - } - - protected static function pruneAliases () { - - $options = self::$process->options; - - // If a vendor target is given, we prune the aliases array - $vendor = $options->vendor_target; - - // Default vendor argument, use all aliases as normal - if ( 'all' === $vendor ) { - return; - } - - // For expicit 'none' argument turn off aliases - if ( 'none' === $vendor ) { - self::$process->aliases = array(); - return; - } - - // Normalize vendor_target argument - $vendor = '-' . str_replace( '-', '', $vendor ) . '-'; - - // Loop the aliases array, filter down to the target vendor - foreach ( self::$process->aliases as $group_name => $group_array ) { - - // Property/value aliases are a special case - if ( 'values' === $group_name ) { - foreach ( $group_array as $property => $values ) { - $result = array(); - foreach ( $values as $value => $prefix_values ) { - foreach ( $prefix_values as $prefix ) { - if ( strpos( $prefix, $vendor ) === 0 ) { - $result[] = $prefix; - } - } - } - self::$process->aliases[ 'values' ][ $property ][ $value ] = $result; - } - continue; - } - foreach ( $group_array as $alias_keyword => $prefix_array ) { - - $result = array(); - foreach ( $prefix_array as $prefix ) { - if ( strpos( $prefix, $vendor ) === 0 ) { - $result[] = $prefix; - } - } - // Prune the whole alias keyword if there is no result - if ( empty( $result ) ) { - unset( self::$process->aliases[ $group_name ][ $alias_keyword ] ); - } - else { - self::$process->aliases[ $group_name ][ $alias_keyword ] = $result; - } - } - } - } - - protected static function calculateVariables () { - - $options = self::$process->options; - - // In-file variables override global variables - // Runtime variables override in-file variables - self::$process->variables = array_merge( self::$config->vars, self::$process->variables ); - - if ( ! empty( $options->vars ) ) { - self::$process->variables = array_merge( - self::$process->variables, $options->vars ); - } - - // Place variables referenced inside variables - // Excecute any custom functions - foreach ( self::$process->variables as $name => &$value ) { - // Referenced variables - $value = preg_replace_callback( - csscrush_regex::$patt->varFunction, array( 'self', 'cb_placeVariables' ), $value ); - - // Custom functions: - // Variable values can be escaped from function parsing with a tilde prefix - if ( strpos( $value, '~' ) === 0 ) { - $value = ltrim( $value, "!\t\r " ); - } - else { - csscrush_function::executeCustomFunctions( $value ); - } - } - } - - public static function placeVariables ( &$stream ) { - - // Substitute simple case variables - $stream = preg_replace_callback( - csscrush_regex::$patt->varFunction, array( 'self', 'cb_placeVariables' ), $stream ); - - // Substitute variables with default values - $var_fn_patt = csscrush_regex::createFunctionMatchPatt( array( '$' ) ); - $var_fn_callback = array( '$' => array( 'csscrush', 'cb_varFunctionWithDefault' ) ); - csscrush_function::executeCustomFunctions( $stream, $var_fn_patt, $var_fn_callback ); - - // Repeat above steps for variables embedded in string tokens - foreach ( self::$process->tokens->s as $label => &$string ) { - - if ( strpos( $string, '$(' ) !== false ) { - - $string = preg_replace_callback( - csscrush_regex::$patt->varFunction, - array( 'self', 'cb_placeVariables' ), $string ); - csscrush_function::executeCustomFunctions( $string, $var_fn_patt, $var_fn_callback ); - } - } - } - - protected static function reset ( $options = null ) { - - // Load in aliases and plugins. - self::loadAssets(); - - // Reset properties for current process. - self::$process = $process = (object) array(); - $process->uid = 0; - $process->cacheData = array(); - $process->mixins = array(); - $process->abstracts = array(); - $process->errors = array(); - $process->selectorRelationships = array(); - $process->charset = null; - $process->currentFile = null; - $process->options = self::getOptions( $options ); - $process->tokens = (object) array( - 's' => array(), // Strings - 'c' => array(), // Comments - 'r' => array(), // Rules - 'p' => array(), // Parens - 'u' => array(), // URLs - 't' => array(), // Traces - ); - $process->variables = array(); - $process->misc = (object) array(); - $process->input = (object) array(); - $process->output = (object) array(); - - // Copy config values. - $process->plugins = self::$config->plugins; - $process->aliases = self::$config->aliases; - } - - protected static function compile ( $stream ) { - - $config = self::$config; - $process = self::$process; - $options = $process->options; - - // Load and unload plugins. - // Add option enabled plugins to the list. - if ( is_array( $options->enable ) ) { - foreach ( $options->enable as $plugin_name ) { - $process->plugins[ $plugin_name ] = true; - } - } - - // Remove option disabled plugins from the list, and disable them. - if ( $options->disable === 'all' ) { - $options->disable = array_keys( $config->plugins ); - } - if ( is_array( $options->disable ) ) { - foreach ( $options->disable as $plugin_name ) { - csscrush_plugin::disable( $plugin_name ); - unset( $process->plugins[ $plugin_name ] ); - } - } - - // Enable all plugins in the remaining list. - foreach ( $process->plugins as $plugin_name => $bool ) { - csscrush_plugin::enable( $plugin_name ); - } - - // Prune if a vendor target is set. - self::pruneAliases(); - - // Parse variables. - self::extractVariables( $stream ); - - // Calculate the variable stack. - self::calculateVariables(); - - // Apply variables. - self::placeVariables( $stream ); - - // Resolve @ifdefine blocks. - self::resolveIfDefines( $stream ); - - // Pull out @mixin definitions. - self::extractMixins( $stream ); - - // Pull out @fragment blocks, and invoke. - self::resolveFragments( $stream ); - - // Adjust the stream so we can extract the rules cleanly. - $map = array( - '@' => "\n@", - '}' => "}\n", - '{' => "{\n", - ';' => ";\n", - ); - $stream = "\n" . str_replace( array_keys( $map ), array_values( $map ), $stream ); - - // Parse rules. - self::extractRules( $stream ); - - // Process @in blocks. - self::prefixSelectors( $stream ); - - // Main processing on the rule objects. - self::processRules(); - - // Alias any @-rules. - self::aliasAtRules( $stream ); - - // Print rules, optionally minify. - self::collate( $stream ); - - // Add in boilerplate. - if ( $options->boilerplate ) { - $stream = self::getBoilerplate() . "\n$stream"; - } - - // Add @charset at top if set. - if ( $process->charset ) { - $stream = "@charset \"$process->charset\";\n" . $stream; - } - - // Release memory. - unset( - $process->tokens, - $process->variables, - $process->mixins, - $process->abstracts, - $process->selectorRelationships, - $process->misc, - $process->plugins, - $process->aliases - ); - - return $stream; - } - - protected static function collate ( &$stream ) { - - $process = self::$process; - $options = $process->options; - $minify = ! $options->debug; - $regex = csscrush_regex::$patt; - - if ( $minify ) { - - // Strip whitespace around colons used in @-rule arguments. - $stream = preg_replace( '! ?\: ?!', ':', $stream ); - // Strip newlines added during parsing. - $stream = preg_replace( '!\n+!', '', $stream ); - } - else { - - // Pretty printing. - $stream = preg_replace( '!([{}])!', "$1\n", $stream ); - $stream = preg_replace( '!([^\s])\{!', "$1 {", $stream ); - $stream = preg_replace( '!([@])!', "\n$1", $stream ); - - // Newlines after some tokens. - $stream = preg_replace( '!(\?[rc][0-9]+\?)!', "$1\n", $stream ); - - // Kill double spaces. - $stream = ltrim( preg_replace( '!\n+!', "\n", $stream ) ); - } - - // Kill leading space. - $stream = preg_replace( '!\n\s+!', "\n", $stream ); - - // Print out rules. - $stream = csscrush_util::strReplaceHash( $stream, $process->tokens->r ); - - // Insert parens. - $stream = csscrush_util::strReplaceHash( $stream, $process->tokens->p ); - - // Compress hex-codes, collapse TRBL lists etc. - $stream = self::decruft( $stream ); - - if ( $minify ) { - // Trim whitespace around selector combinators. - $stream = preg_replace( '! ?([>~+]) ?!S', '$1', $stream ); - } - else { - // Add space after commas. - $stream = str_replace( ',', ', ', $stream ); - - // Insert comments. - foreach ( $process->tokens->c as $token => &$comment ) { - $comment .= "\n"; - } - $stream = csscrush_util::strReplaceHash( $stream, $process->tokens->c ); - - // Normalize line breaks. - $stream = preg_replace( '!\n{3,}!', "\n\n", $stream ); - } - - // Insert URLs. - $link = csscrush_util::getLinkBetweenDirs( $process->output->dir, $process->input->dir ); - - if ( $process->tokens->u ) { - foreach ( $process->tokens->u as $token => $url ) { - - if ( strpos( $url->value, '$(' ) === 0 ) { - $url->applyVariables(); - } - - if ( $url->isRelative ) { - // Optionally set the URLs to absolute. - if ( $options->rewrite_import_urls === 'absolute' ) { - $url->prepend( $process->input->dirUrl . '/' ); - } - // If output dir is different to input dir prepend a link between the two. - elseif ( $link ) { - $url->prepend( $link ); - } - } - - if ( $url->convertToData ) { - $url->toData(); - } - else { - $url->simplify(); - } - } - $stream = csscrush_util::strReplaceHash( $stream, $process->tokens->u ); - } - - // Insert string literals. - $stream = csscrush_util::strReplaceHash( $stream, $process->tokens->s ); - } - - protected static function decruft ( $str ) { - - $replacements = array( - - // Strip leading zeros on floats. - '!([: \(,])(-?)0(\.\d+)!S' => '$1$2$3', - - // Strip unnecessary units on zero values for length types. - '!([: \(,])\.?0(?:e[mx]|c[hm]|rem|v[hwm]|in|p[tcx])!iS' => '${1}0', - - // Collapse zero lists. - '!(\: *)(?:0 0 0|0 0 0 0) *([;}])!S' => '${1}0$2', - - // Collapse zero lists 2nd pass. - '!(padding|margin|border-radius) ?(\: *)0 0 *([;}])!iS' => '${1}${2}0$3', - - // Dropping redundant trailing zeros on TRBL lists. - '!(\: *)(-?(?:\d+)?\.?\d+[a-z]{1,4}) 0 0 0 *([;}])!iS' => '$1$2 0 0$3', - '!(\: *)0 0 (-?(?:\d+)?\.?\d+[a-z]{1,4}) 0 *([;}])!iS' => '${1}0 0 $2$3', - - // Compress hex codes. - '!\#([0-9a-f])\1([0-9a-f])\2([0-9a-f])\3!iS' => '#$1$2$3', - ); - - return preg_replace( - array_keys( $replacements ), array_values( $replacements ), $str ); - } - - protected static function aliasAtRules ( &$stream ) { - - if ( empty( self::$process->aliases[ 'at-rules' ] ) ) { - return; - } - - $aliases = self::$process->aliases[ 'at-rules' ]; - - foreach ( $aliases as $at_rule => $at_rule_aliases ) { - if ( - strpos( $stream, "@$at_rule " ) === -1 || - strpos( $stream, "@$at_rule{" ) === -1 - ) { - // Nothing to see here - continue; - } - $scan_pos = 0; - - // Find at-rules that we want to alias - while ( preg_match( "!@$at_rule" . '[\s{]!', $stream, $match, PREG_OFFSET_CAPTURE, $scan_pos ) ) { - - // Store the match position - $block_start_pos = $match[0][1]; - - // Capture the curly bracketed block - $curly_match = csscrush_util::matchBrackets( $stream, '{}', $block_start_pos ); - - if ( ! $curly_match ) { - // Couldn't match the block - break; - } - - // The end of the block - $block_end_pos = $curly_match->end; - - // Build up string with aliased blocks for splicing - $original_block = substr( $stream, $block_start_pos, $block_end_pos - $block_start_pos ); - $blocks = array(); - foreach ( $at_rule_aliases as $alias ) { - // Copy original block, replacing at-rule with alias name - $copy_block = str_replace( "@$at_rule", "@$alias", $original_block ); - - // Aliases are nearly always prefixed, capture the current vendor name - preg_match( csscrush_regex::$patt->vendorPrefix, $alias, $vendor ); - - $vendor = $vendor ? $vendor[1] : null; - - // Duplicate rules - if ( preg_match_all( csscrush_regex::$patt->rToken, $copy_block, $copy_matches ) ) { - $originals = array(); - $replacements = array(); - - foreach ( $copy_matches[0] as $copy_match ) { - // Clone the matched rule - $originals[] = $rule_label = $copy_match; - $cloneRule = clone self::$process->tokens->r[ $rule_label ]; - - // Set the vendor context - $cloneRule->vendorContext = $vendor; - - // Filter out declarations that have different vendor context - $new_set = array(); - foreach ( $cloneRule as $declaration ) { - if ( ! $declaration->vendor || $declaration->vendor === $vendor ) { - $new_set[] = $declaration; - } - } - $cloneRule->declarations = $new_set; - - // Store the clone - $replacements[] = $clone_rule_label = self::tokenLabelCreate( 'r' ); - self::$process->tokens->r[ $clone_rule_label ] = $cloneRule; - } - // Finally replace the original labels with the cloned rule labels - $copy_block = str_replace( $originals, $replacements, $copy_block ); - } - $blocks[] = $copy_block; - } - - // The original version is always last in the list - $blocks[] = $original_block; - $blocks = implode( "\n", $blocks ); - - // Splice in the blocks. - $stream = substr_replace( $stream, $blocks, $block_start_pos, $block_end_pos - $block_start_pos ); - - // Move the regex pointer forward - $scan_pos = $block_start_pos + strlen( $blocks ); - - } // while - - } // foreach - } - - protected static function prefixSelectors ( &$stream ) { - - $matches = csscrush_regex::matchAll( '@in\s+([^{]+){', $stream, true ); - - // Move through the matches in reverse order - while ( $match = array_pop( $matches ) ) { - - list( $match_string, $match_start_pos ) = $match[0]; - $match_length = strlen( $match_string ); - - $before = substr( $stream, 0, $match_start_pos ); - - $raw_argument = trim( $match[1][0] ); - - csscrush::captureParens( $match[1][0] ); - $arguments = csscrush_util::splitDelimList( $match[1][0] ); - - $curly_match = csscrush_util::matchBrackets( $stream, '{}', $match_start_pos, true ); - - if ( ! $curly_match || empty( $raw_argument ) ) { - // Couldn't match the block - $stream = $before . substr( $stream, $match_start_pos + $match_length ); - continue; - } - - // Match all the rule tokens - $rule_matches = csscrush_regex::matchAll( - csscrush_regex::$patt->rToken, - $curly_match->inside ); - - foreach ( $rule_matches as $rule_match ) { - - // Get the rule instance - $rule = csscrush_rule::get( $rule_match[0][0] ); - - // Set the isNested flag - $rule->isNested = true; - - // Using arguments create new selector list for the rule - $new_selector_list = array(); - - foreach ( $arguments as $arg_selector ) { - - foreach ( $rule->selectorList as $rule_selector ) { - - if ( ! $rule_selector->allowPrefix ) { - - $new_selector_list[ $rule_selector->readableValue ] = $rule_selector; - } - elseif ( strpos( $rule_selector->value, '&' ) !== false ) { - - // Ampersand is the positional symbol for where the - // prefix will be placed - - // Find and replace (only once) the ampersand - $new_value = preg_replace( - '!&!', - $arg_selector, - $rule_selector->value, - 1 ); - - // Not storing the selector as named - $new_selector_list[] = new csscrush_selector( $new_value ); - } - else { - - // Not storing the selector as named - $new_selector_list[] - = new csscrush_selector( "$arg_selector {$rule_selector->value}" ); - } - } - } - $rule->selectorList = $new_selector_list; - } - - // Concatenate - $stream = $before . $curly_match->inside . $curly_match->after; - } - } - - public static function processRules () { - - // Reset the selector relationships - self::$process->selectorRelationships = array(); - - $aliases =& self::$process->aliases; - - foreach ( self::$process->tokens->r as $rule ) { - - // Store selector relationships - $rule->indexSelectors(); - - csscrush_hook::run( 'rule_prealias', $rule ); - - if ( ! empty( $aliases[ 'properties' ] ) ) { - $rule->addPropertyAliases(); - } - if ( ! empty( $aliases[ 'functions' ] ) ) { - $rule->addFunctionAliases(); - } - if ( ! empty( $aliases[ 'values' ] ) ) { - $rule->addValueAliases(); - } - - csscrush_hook::run( 'rule_postalias', $rule ); - - $rule->expandSelectors(); - - // Find previous selectors and apply them - $rule->applyExtendables(); - - csscrush_hook::run( 'rule_postprocess', $rule ); - } - } - - - ############################# - # Tokens. - - public static function tokenLabelCreate ( $type ) { - $counter = ++self::$process->uid; - return "?$type$counter?"; - } - - public static function tokenFetch ( $token ) { - $type = substr( $token, 1, 1 ); - $path =& self::$process->tokens->{ $type }; - if ( isset( $path[ $token ] ) ) { - return $path[ $token ]; - } - return null; - } - - public static function tokenAdd ( $value, $type ) { - $label = self::tokenLabelCreate( $type ); - self::$process->tokens->{ $type }[ $label ] = $value; - return $label; - } - - public static function tokenRelease ( $token ) { - unset( self::$process->tokens->{ substr( $token, 1, 1 ) }[ $token ] ); - } - - public static function tokenRestoreAll ( $str, $type = 'p' ) { - - // Reference the token table. - $token_table =& csscrush::$process->tokens->{ $type }; - - // Find matching tokens. - $matches = csscrush_regex::matchAll( csscrush_regex::$patt->{ "{$type}Token" }, $str ); - - foreach ( $matches as $m ) { - $token = $m[0][0]; - if ( isset( $token_table[ $token ] ) ) { - $str = str_replace( $token, $token_table[ $token ], $str ); - } - } - return $str; - } - - - ############################# - # preg_replace callbacks. - - protected static function cb_extractCommentAndString ( $match ) { - - $full_match = $match[0]; - - // We return the newlines to maintain line numbering when tracing. - $newlines = str_repeat( "\n", substr_count( $full_match, "\n" ) ); - - if ( strpos( $full_match, '/*' ) === 0 ) { - - // Strip private comments - $private_comment_marker = '$'; - - // Bail without storing comment if in debug mode or a private comment. - if ( - strpos( $full_match, '/*' . $private_comment_marker ) === 0 || - ! self::$process->options->debug - ) { - return $newlines; - } - - // Fix broken comments as they will break any subsquent - // imported files that are inlined. - if ( ! preg_match( '!\*/$!', $full_match ) ) { - $full_match .= '*/'; - } - $label = csscrush::tokenAdd( $full_match, 'c' ); - } - else { - - // Fix broken strings as they will break any subsquent - // imported files that are inlined. - if ( $full_match[0] !== $full_match[ strlen( $full_match )-1 ] ) { - $full_match .= $full_match[0]; - } - $label = csscrush::tokenAdd( $full_match, 's' ); - } - - return $newlines . $label; - } - - protected static function cb_extractMixins ( $match ) { - - $name = trim( $match[1] ); - $block = trim( $match[2] ); - - if ( ! empty( $name ) && ! empty( $block ) ) { - self::$process->mixins[ $name ] = new csscrush_mixin( $block ); - } - - return ''; - } - - protected static function cb_extractVariables ( $match ) { - - $regex = csscrush_regex::$patt; - - // Strip comment markers. - $block = trim( csscrush_util::stripCommentTokens( $match[2] ) ); - - $pairs = preg_split( '!\s*;\s*!', $block, null, PREG_SPLIT_NO_EMPTY ); - - // Loop through the pairs. - foreach ( $pairs as $var ) { - $colon = strpos( $var, ':' ); - if ( $colon === -1 ) { - continue; - } - $name = trim( substr( $var, 0, $colon ) ); - $value = trim( substr( $var, $colon + 1 ) ); - self::$process->variables[ trim( $name ) ] = $value; - } - return ''; - } - - protected static function cb_placeVariables ( $match ) { - - $variable_name = $match[1]; - - if ( isset( self::$process->variables[ $variable_name ] ) ) { - return self::$process->variables[ $variable_name ]; - } - } - - public static function cb_varFunctionWithDefault ( $raw_argument ) { - - list( $name, $default_value ) = csscrush_function::parseArgsSimple( $raw_argument ); - - if ( isset( self::$process->variables[ $name ] ) ) { - - return self::$process->variables[ $name ]; - } - else { - return $default_value; - } - } - - protected static function cb_extractRules ( $match ) { - - $rule = (object) array(); - $rule->selector_raw = trim( $match[1] ); - $rule->declaration_raw = trim( $match[2] ); - - csscrush_hook::run( 'rule_preprocess', $rule ); - - $rule = new csscrush_rule( $rule->selector_raw, $rule->declaration_raw ); - - // Store rules if they have declarations or extend arguments - if ( count( $rule ) || $rule->extendArgs ) { - - self::$process->tokens->r[ $rule->label ] = $rule; - - // If only using extend still return a label - return $rule->label . "\n"; - } - return ''; - } - - - ############################# - # Parsing methods. - - public static function captureParens ( &$str ) { - - // PHP >= 5.3 - // $str = preg_replace_callback( csscrush_regex::$patt->balancedParens, function ( $m ) { - // return csscrush::tokenAdd( $m[0], 'p' ); - // }, $str ); - - while ( preg_match( csscrush_regex::$patt->balancedParens, $str, $m, PREG_OFFSET_CAPTURE ) ) { - $label = csscrush::tokenAdd( $m[0][0], 'p' ); - $str = substr_replace( $str, $label, $m[0][1], strlen( $m[0][0] ) ); - } - } - - public static function restoreParens ( &$str, $release = true ) { - - $token_table =& csscrush::$process->tokens->p; - - foreach ( csscrush_regex::matchAll( csscrush_regex::$patt->pToken, $str ) as $m ) { - $token = $m[0][0]; - if ( isset( $token_table[ $token ] ) ) { - $str = str_replace( $token, $token_table[ $token ], $str ); - if ( $release ) { - unset( $token_table[ $token ] ); - } - } - } - } - - public static function extractRules ( &$stream ) { - $stream = preg_replace_callback( csscrush_regex::$patt->rule, array( 'self', 'cb_extractRules' ), $stream ); - } - - public static function extractVariables ( &$stream ) { - $stream = preg_replace_callback( csscrush_regex::$patt->variables, array( 'self', 'cb_extractVariables' ), $stream ); - } - - public static function extractMixins ( &$stream ) { - $stream = preg_replace_callback( csscrush_regex::$patt->mixin, array( 'self', 'cb_extractMixins' ), $stream ); - } - - public static function resolveFragments ( &$stream ) { - - $matches = csscrush_regex::matchAll( '@fragment\s+()\s*{', $stream, true ); - $fragments = array(); - - // Move through the matches last to first - while ( $match = array_pop( $matches ) ) { - - list( $match_string, $match_start_pos ) = $match[0]; - $fragment_name = $match[1][0]; - - $match_length = strlen( $match_string ); - $before = substr( $stream, 0, $match_start_pos ); - - $curly_match = csscrush_util::matchBrackets( $stream, '{}', $match_start_pos, true ); - - if ( ! $curly_match ) { - // Couldn't match the block. - $stream = substr_replace( $stream, '', $match_start_pos, $match_length ); - continue; - } - else { - // Reconstruct the stream without the fragment. - $stream = $before . $curly_match->after; - - // Create the fragment and store it - $fragments[ $fragment_name ] = - new csscrush_fragment( $curly_match->inside ); - } - } - - // Now find all the fragment calls - $matches = csscrush_regex::matchAll( '@fragment\s+()\s*(\(|;)', $stream, true ); - - // Move through the matches last to first - while ( $match = array_pop( $matches ) ) { - - list( $match_string, $match_start_pos ) = $match[0]; - $match_length = strlen( $match_string ); - $before = substr( $stream, 0, $match_start_pos ); - - // The matched fragment name - $fragment_name = $match[1][0]; - - // The fragment object, or null if name not present - $fragment = isset( $fragments[ $fragment_name ] ) ? $fragments[ $fragment_name ] : null; - - // Fragment may be called without any argument list - $with_arguments = $match[2][0] === '('; - - if ( $with_arguments ) { - $paren_match = csscrush_util::matchBrackets( $stream, '()', $match_start_pos, true ); - $after = ltrim( $paren_match->after, ';' ); - } - else { - $after = substr( $stream, $match_start_pos + $match_length ); - } - - if ( ! $fragment || ( $with_arguments && ! $paren_match ) ) { - - // Invalid fragment, or malformed argument list - $stream = $before . substr( $stream, $match_start_pos + $match_length ); - continue; - } - else { - - $args = array(); - if ( $with_arguments ) { - // Get the argument array to pass to the fragment. - $args = csscrush_util::splitDelimList( $paren_match->inside ); - } - - // Execute the fragment and get the return value - $fragment_return = $fragment->call( $args ); - - // Recontruct the stream with the fragment return value - $stream = $before . $fragment_return . $after; - } - } - } - - public static function resolveIfDefines ( &$stream ) { - - $matches = csscrush_regex::matchAll( '@ifdefine\s+(not\s+)?()\s*\{', $stream, true ); - - // Move through the matches last to first. - while ( $match = array_pop( $matches ) ) { - - $full_match = $match[0][0]; - $full_match_start = $match[0][1]; - $before = substr( $stream, 0, $full_match_start ); - - $negate = $match[1][1] != -1; - $name = $match[2][0]; - $name_defined = isset( self::$process->variables[ $name ] ); - - $curly_match = csscrush_util::matchBrackets( $stream, '{}', $full_match_start, true ); - - if ( ! $curly_match ) { - // Couldn't match the block. - $stream = $before . substr( $stream, $full_match_start + strlen( $full_match ) ); - continue; - } - - if ( ! $negate && $name_defined || $negate && ! $name_defined ) { - // Test resolved true so include the innards. - $stream = $before . $curly_match->inside . $curly_match->after; - } - else { - // Recontruct the stream without the innards. - $stream = $before . $curly_match->after; - } - } - } - } @@ -1668,6 +488,3 @@ function csscrush_globalvars ( $vars ) { function csscrush_clearcache ( $dir = '' ) { return csscrush::clearcache( $dir ); } - - - diff --git a/lib/Function.php b/lib/Function.php index 24157dd..060405b 100644 --- a/lib/Function.php +++ b/lib/Function.php @@ -4,23 +4,22 @@ * Custom CSS functions * */ - class csscrush_function { // Regex pattern for finding custom functions - public static $functionPatt; + static public $functionPatt; // Cache for function names - public static $functionList; + static public $functionList; - public static function init () { + static public function init () { // Set the custom function regex pattern self::$functionList = self::getFunctions(); self::$functionPatt = csscrush_regex::createFunctionMatchPatt( self::$functionList, true ); } - public static function getFunctions () { + static public function getFunctions () { // Fetch custom function names // Include subtraction operator @@ -35,7 +34,7 @@ public static function getFunctions () { return $fn_methods; } - public static function executeCustomFunctions ( &$str, $patt = null, $process_callback = null, $property = null ) { + static public function executeCustomFunctions ( &$str, $patt = null, $process_callback = null, $property = null ) { // No bracketed expressions, early return. if ( false === strpos( $str, '(' ) ) { @@ -77,7 +76,7 @@ public static function executeCustomFunctions ( &$str, $patt = null, $process_ca $closing_paren = $opening_paren + strlen( $parens[0][0] ); // Get the function arguments. - $args = $parens[1][0]; + $args = trim( $parens[1][0] ); // Workaround the minus. $minus_before = '-' === $raw_fn_name ? '-' : ''; @@ -104,21 +103,21 @@ public static function executeCustomFunctions ( &$str, $patt = null, $process_ca } - ############ - # Helpers + ############################# + # Helpers. - public static function parseArgs ( $input, $allowSpaceDelim = false ) { + static public function parseArgs ( $input, $allowSpaceDelim = false ) { return csscrush_util::splitDelimList( $input, ( $allowSpaceDelim ? '\s*[,\s]\s*' : ',' ) ); } // Intended as a quick arg-list parse for function that take up-to 2 arguments // with the proviso the first argument is a name - public static function parseArgsSimple ( $input ) { + static public function parseArgsSimple ( $input ) { return preg_split( csscrush_regex::$patt->argListSplit, $input, 2 ); } - protected static function colorAdjust ( $color, array $adjustments ) { + static protected function colorAdjust ( $color, array $adjustments ) { $fn_matched = preg_match( '!^(#|rgba?|hsla?)!', $color, $m ); $keywords = csscrush_color::getKeywords(); @@ -206,9 +205,10 @@ protected static function colorAdjust ( $color, array $adjustments ) { } - ############ + ############################# + # CSS functions. - public static function css_fn__math ( $input ) { + static protected function css_fn__math ( $input ) { // Strip blacklisted characters $input = preg_replace( csscrush_regex::$patt->mathBlacklist, '', $input ); @@ -218,7 +218,7 @@ public static function css_fn__math ( $input ) { return $result === false ? 0 : round( $result, 5 ); } - public static function css_fn__percent ( $input ) { + static protected function css_fn__percent ( $input ) { // Strip non-numeric and non delimiter characters $input = preg_replace( '![^\d\.\s,]!S', '', $input ); @@ -260,42 +260,39 @@ public static function css_fn__percent ( $input ) { return $result . '%'; } - // Percent function alias - public static function css_fn__pc ( $input ) { + // Percent function alias. + static protected function css_fn__pc ( $input ) { return self::css_fn__percent( $input ); } - public static function css_fn__hsla_adjust ( $input ) { + static protected function css_fn__hsla_adjust ( $input ) { list( $color, $h, $s, $l, $a ) = array_pad( self::parseArgs( $input, true ), 5, 0 ); return self::colorAdjust( $color, array( $h, $s, $l, $a ) ); } - public static function css_fn__hsl_adjust ( $input ) { + static protected function css_fn__hsl_adjust ( $input ) { list( $color, $h, $s, $l ) = array_pad( self::parseArgs( $input, true ), 4, 0 ); return self::colorAdjust( $color, array( $h, $s, $l, 0 ) ); } - public static function css_fn__h_adjust ( $input ) { + static protected function css_fn__h_adjust ( $input ) { list( $color, $h ) = array_pad( self::parseArgs( $input, true ), 2, 0 ); return self::colorAdjust( $color, array( $h, 0, 0, 0 ) ); } - public static function css_fn__s_adjust ( $input ) { + static protected function css_fn__s_adjust ( $input ) { list( $color, $s ) = array_pad( self::parseArgs( $input, true ), 2, 0 ); return self::colorAdjust( $color, array( 0, $s, 0, 0 ) ); } - public static function css_fn__l_adjust ( $input ) { + static protected function css_fn__l_adjust ( $input ) { list( $color, $l ) = array_pad( self::parseArgs( $input, true ), 2, 0 ); return self::colorAdjust( $color, array( 0, 0, $l, 0 ) ); } - public static function css_fn__a_adjust ( $input ) { + static protected function css_fn__a_adjust ( $input ) { list( $color, $a ) = array_pad( self::parseArgs( $input, true ), 2, 0 ); return self::colorAdjust( $color, array( 0, 0, 0, $a ) ); } - } - - diff --git a/lib/Hook.php b/lib/Hook.php index c302e9b..943b5f3 100644 --- a/lib/Hook.php +++ b/lib/Hook.php @@ -4,7 +4,6 @@ * Access to the execution flow * */ - class csscrush_hook { // Table of hooks and the functions attached to them @@ -37,7 +36,7 @@ static public function remove ( $hook, $fn_name ) { unset( self::$register[ $hook ][ $fn_name ] ); } - static public function run ( $hook, $arg_obj ) { + static public function run ( $hook, $arg_obj = null ) { // Run all callbacks attached to the hook if ( ! isset( self::$register[ $hook ] ) ) { diff --git a/lib/IO.php b/lib/IO.php index 43fe00d..da61f4e 100644 --- a/lib/IO.php +++ b/lib/IO.php @@ -4,12 +4,10 @@ * Interface for writing files, retrieving files and checking caches * */ - class csscrush_io { - // Any setup that needs to be done - public static function init () { + static public function init () { $process = csscrush::$process; @@ -17,13 +15,11 @@ public static function init () { $process->cacheFilePath = "{$process->input->dir}/$process->cacheFileName"; } - - public static function getOutputDir () { + static public function getOutputDir () { return csscrush::$process->input->dir; } - - public static function testOutputDir () { + static public function testOutputDir () { $output_dir = csscrush::$process->output->dir; $pathtest = true; @@ -56,8 +52,7 @@ public static function testOutputDir () { return $pathtest; } - - public static function getOutputFileName () { + static public function getOutputFileName () { $process = csscrush::$process; $options = $process->options; @@ -71,8 +66,7 @@ public static function getOutputFileName () { return "$output_basename.crush.css"; } - - public static function validateExistingOutput () { + static public function validateExistingOutput () { $process = csscrush::$process; $options = $process->options; @@ -161,8 +155,7 @@ public static function validateExistingOutput () { return false; } - - public static function clearCache ( $dir ) { + static public function clearCache ( $dir ) { if ( empty( $dir ) ) { $dir = dirname( __FILE__ ); @@ -189,8 +182,7 @@ public static function clearCache ( $dir ) { } } - - public static function getCacheData () { + static public function getCacheData () { $config = csscrush::$config; $process = csscrush::$process; @@ -236,8 +228,7 @@ public static function getCacheData () { return $cache_data; } - - public static function saveCacheData () { + static public function saveCacheData () { $process = csscrush::$process; @@ -248,8 +239,7 @@ public static function saveCacheData () { file_put_contents( $process->cacheFilePath, json_encode( $process->cacheData ) ); } - - final static function registerInputFile ( $file ) { + static final function registerInputFile ( $file ) { $input = csscrush::$process->input; diff --git a/lib/Importer.php b/lib/Importer.php index 02611a9..cc5a3ca 100644 --- a/lib/Importer.php +++ b/lib/Importer.php @@ -4,41 +4,21 @@ * Recursive file importing * */ - class csscrush_importer { - - public static function save ( $data ) { - - $process = csscrush::$process; - $options = $process->options; - - // No saving if caching is disabled, return early - if ( ! $options->cache ) { - return; - } - - // Write to config - $process->cacheData[ $process->output->filename ] = $data; - - // Save config changes - csscrush::io_call( 'saveCacheData' ); - } - - - public static function hostfile () { + static public function hostfile () { $config = csscrush::$config; $process = csscrush::$process; $options = $process->options; $regex = csscrush_regex::$patt; - $hostfile = $process->input; + $input = $process->input; // Keep track of all import file info for cache data. $mtimes = array(); $filenames = array(); - $stream = ''; + $str = ''; $prepend_file_contents = ''; // The prepend file. @@ -46,42 +26,42 @@ public static function hostfile () { $prepend_file_contents = file_get_contents( $prepend_file ); $process->currentFile = 'file://' . $prepend_file; // If there's a parsing error inside the prepend file, wipe $prepend_file_contents. - if ( ! csscrush::prepareStream( $prepend_file_contents ) ) { + if ( ! self::prepareForStream( $prepend_file_contents ) ) { $prepend_file_contents = ''; } } // Resolve main input; Is it a bare string or a file. - if ( isset( $hostfile->string ) ) { - $stream .= $hostfile->string; - $process->currentFile = 'inline-css'; + if ( isset( $input->string ) ) { + $str .= $input->string; + $process->currentFile = 'inline css'; } else { - $stream .= file_get_contents( $hostfile->path ); - $process->currentFile = 'file://' . $hostfile->path; + $str .= file_get_contents( $input->path ); + $process->currentFile = 'file://' . $input->path; } // If there's a parsing error go no further. - if ( ! csscrush::prepareStream( $stream ) ) { - return $stream; + if ( ! self::prepareForStream( $str ) ) { + return $str; } // Prepend any prepend file contents here. - $stream = $prepend_file_contents . $stream; + $str = $prepend_file_contents . $str; // This may be set non-zero during the script if an absolute @import URL is encountered. $search_offset = 0; // Recurses until the nesting heirarchy is flattened and all import files are inlined. - while ( preg_match( $regex->import, $stream, $match, PREG_OFFSET_CAPTURE, $search_offset ) ) { + while ( preg_match( $regex->import, $str, $match, PREG_OFFSET_CAPTURE, $search_offset ) ) { $match_len = strlen( $match[0][0] ); $match_start = $match[0][1]; $match_end = $match_start + $match_len; // If just stripping the import statements - if ( isset( $hostfile->importIgnore ) ) { - $stream = substr_replace( $stream, '', $match_start, $match_len ); + if ( isset( $input->importIgnore ) ) { + $str = substr_replace( $str, '', $match_start, $match_len ); continue; } @@ -107,13 +87,13 @@ public static function hostfile () { $import->path = realpath( $config->docRoot . $import->url->value ); } else { - $import->path = realpath( "$hostfile->dir/{$import->url->value}" ); + $import->path = realpath( "$input->dir/{$import->url->value}" ); } // Get the import contents, if unsuccessful just continue with the import line removed. if ( ! ( $import->content = @file_get_contents( $import->path ) ) ) { csscrush::log( "Import file '{$import->url->value}' not found" ); - $stream = substr_replace( $stream, '', $match_start, $match_len ); + $str = substr_replace( $str, '', $match_start, $match_len ); continue; } @@ -123,8 +103,8 @@ public static function hostfile () { $process->currentFile = 'file://' . $import->path; // If there are unmatched brackets inside the import, strip it. - if ( ! csscrush::prepareStream( $import->content ) ) { - $stream = substr_replace( $stream, '', $match_start, $match_len ); + if ( ! self::prepareForStream( $import->content ) ) { + $str = substr_replace( $str, '', $match_start, $match_len ); continue; } @@ -160,24 +140,27 @@ public static function hostfile () { $import->content = "@media $import->mediaContext {{$import->content}}"; } - $stream = substr_replace( $stream, $import->content, $match_start, $match_len ); + $str = substr_replace( $str, $import->content, $match_start, $match_len ); } - // Save only if the hostfile object is associated with a real file - if ( $hostfile->path ) { + // Save only if caching is on and the hostfile object is associated with a real file. + if ( $input->path && $process->options->cache ) { - self::save( array( + // Write to config. + $process->cacheData[ $process->output->filename ] = array( 'imports' => $filenames, - 'datem_sum' => array_sum( $mtimes ) + $hostfile->mtime, + 'datem_sum' => array_sum( $mtimes ) + $input->mtime, 'options' => $options, - )); + ); + + // Save config changes. + $process->ioCall( 'saveCacheData' ); } - return $stream; + return $str; } - - protected static function rewriteImportedUrls ( $import ) { + static protected function rewriteImportedUrls ( $import ) { $link = csscrush_util::getLinkBetweenDirs( csscrush::$process->input->dir, dirname( $import->path ) ); @@ -200,4 +183,190 @@ protected static function rewriteImportedUrls ( $import ) { } } } + + static protected function prepareForStream ( &$str ) { + + $regex = csscrush_regex::$patt; + $process = csscrush::$process; + $trace = $process->options->trace; + + $str = preg_replace_callback( $regex->commentAndString, + array( 'self', 'cb_extractCommentAndString' ), $str ); + + // If @charset is set store it. + if ( preg_match( $regex->charset, $str, $m ) ) { + $replace = ''; + if ( ! $process->charset ) { + // Keep track of newlines for line tracing. + $replace = str_repeat( "\n", substr_count( $m[0], "\n" ) ); + $process->charset = trim( $process->fetchToken( $m[1] ), '"\'' ); + } + $str = preg_replace( $regex->charset, $replace, $str ); + } + + // Catch obvious typing errors. + $parse_errors = array(); + $current_file = $process->currentFile; + $balanced_parens = substr_count( $str, "(" ) === substr_count( $str, ")" ); + $balanced_curlies = substr_count( $str, "{" ) === substr_count( $str, "}" ); + + if ( ! $balanced_parens ) { + $parse_errors[] = "Unmatched '(' in $current_file."; + } + if ( ! $balanced_curlies ) { + $parse_errors[] = "Unmatched '{' in $current_file."; + } + + if ( $parse_errors ) { + foreach ( $parse_errors as $error_msg ) { + csscrush::logError( $error_msg ); + trigger_error( "$error_msg\n", E_USER_WARNING ); + } + return false; + } + + // Optionally add tracing stubs. + if ( $trace ) { + self::addTracingStubs( $str ); + } + + // Strip unneeded whitespace. + $str = csscrush_util::normalizeWhiteSpace( $str ); + + // Tokenize all the URLs. + $patt = '# + @import\x{20}(\?s\d+\?) + | + (?label, $str ); + } + // Match parenthesis if not a string token. + elseif ( preg_match( $regex->balancedParens, $str, $inner_m, PREG_OFFSET_CAPTURE, $outer_offset ) ) { + $url = new csscrush_url(http://github.com/$inner_m[1][0]); + $func_name = strtolower( $outer_m[2][0] ); + $url->convertToData = 'data-uri' === $func_name; + $str = substr_replace( $str, $url->label, $outer_offset, + strlen( $func_name ) + strlen( $inner_m[0][0] ) ); + } + // If brackets cannot be matched, skip over the original match. + else { + $offset += strlen( $outer_m[0][0] ); + } + } + + return true; + } + + static protected function cb_extractCommentAndString ( $match ) { + + $full_match = $match[0]; + $process = csscrush::$process; + + // We return the newlines to maintain line numbering when tracing. + $newlines = str_repeat( "\n", substr_count( $full_match, "\n" ) ); + + if ( strpos( $full_match, '/*' ) === 0 ) { + + // Strip private comments + $private_comment_marker = '$'; + + // Bail without storing comment if in debug mode or a private comment. + if ( + strpos( $full_match, '/*' . $private_comment_marker ) === 0 || + ! $process->options->debug + ) { + return $newlines; + } + + // Fix broken comments as they will break any subsquent + // imported files that are inlined. + if ( ! preg_match( '!\*/$!', $full_match ) ) { + $full_match .= '*/'; + } + $label = $process->addToken( $full_match, 'c' ); + } + else { + + // Fix broken strings as they will break any subsquent + // imported files that are inlined. + if ( $full_match[0] !== $full_match[ strlen( $full_match )-1 ] ) { + $full_match .= $full_match[0]; + } + $label = $process->addToken( $full_match, 's' ); + } + + return $newlines . $label; + } + + static protected function addTracingStubs ( &$str ) { + + $selector_patt = '! (^|;|\})+ ([^;{}]+) (\{) !xmS'; + $token_or_whitespace = '!(\s*\?c\d+\?\s*|\s+)!S'; + + $matches = csscrush_regex::matchAll( $selector_patt, $str ); + + // Start from last match and move backwards. + while ( $m = array_pop( $matches ) ) { + + // Shortcuts for readability. + list( $full_match, $before, $content, $after ) = $m; + $full_match_text = $full_match[0]; + $full_match_start = $full_match[1]; + + // The correct before string. + $before = substr( $full_match_text, 0, $content[1] - $full_match_start ); + + // Split the matched selector part. + $content_parts = preg_split( $token_or_whitespace, $content[0], null, + PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY ); + + foreach ( $content_parts as $part ) { + + if ( ! preg_match( $token_or_whitespace, $part ) ) { + + // Match to a valid selector. + if ( preg_match( '!^([^@]|@(?:page|abstract))!iS', $part ) ) { + + // Count line breaks between the start of stream and + // the matched selector to get the line number. + $selector_index = $full_match_start + strlen( $before ); + $line_num = 1; + $str_before = ""; + if ( $selector_index ) { + $str_before = substr( $str, 0, $selector_index ); + $line_num = substr_count( $str_before, "\n" ) + 1; + } + + // Get the currently processed file path, and escape it. + $current_file = str_replace( ' ', '%20', csscrush::$process->currentFile ); + $current_file = preg_replace( '![^\w-]!', '\\\\$0', $current_file ); + + // Splice in tracing stub. + $label = csscrush::$process->addToken( "@media -sass-debug-info{filename{font-family:$current_file}line{font-family:\\00003$line_num}}", 't' ); + + $str = $str_before . $label . substr( $str, $selector_index ); + } + else { + // Not matched as a valid selector, move on. + continue 2; + } + break; + } + + // Append split segment to $before. + $before .= $part; + } + } + } + } diff --git a/lib/Mixin.php b/lib/Mixin.php index 0eec59c..dbb1fea 100644 --- a/lib/Mixin.php +++ b/lib/Mixin.php @@ -4,7 +4,6 @@ * Mixin objects * */ - class csscrush_mixin { public $declarationsTemplate = array(); @@ -83,7 +82,7 @@ public function call ( array $args ) { return $declarations; } - public static function parseSingleValue ( $message ) { + static public function parseSingleValue ( $message ) { $message = ltrim( $message ); $mixin = null; @@ -160,7 +159,7 @@ public static function parseSingleValue ( $message ) { return $mixin->call( $args ); } - public static function parseValue ( $message ) { + static public function parseValue ( $message ) { // Call the mixin and return the list of declarations $declarations = array(); @@ -182,7 +181,6 @@ public static function parseValue ( $message ) { * Fragment objects * */ - class csscrush_fragment { public $template = array(); @@ -215,14 +213,11 @@ public function call ( array $args ) { } - - /** * * Argument list management for mixins and fragments * */ - class csscrush_arglist implements Countable { // Positional argument default values @@ -234,11 +229,13 @@ class csscrush_arglist implements Countable { // The string passed in with arg calls replaced by tokens public $string; - function __construct ( $str ) { + public function __construct ( $str ) { // Parse all arg function calls in the passed string, callback creates default values csscrush_function::executeCustomFunctions( $str, - csscrush_regex::$patt->argFunction, array( 'arg' => array( $this, 'store' ) ) ); + csscrush_regex::$patt->argFunction, array( + 'arg' => array( $this, 'store' ) + )); $this->string = $str; } @@ -311,4 +308,3 @@ public function count () { } } - diff --git a/lib/Plugin.php b/lib/Plugin.php index cdc201f..a40de04 100644 --- a/lib/Plugin.php +++ b/lib/Plugin.php @@ -4,24 +4,20 @@ * Plugin API * */ - class csscrush_plugin { - // The required prefix to all plugin function names static public $prefix = 'csscrush__'; // The current loaded plugins static protected $associated_hooks = array(); - // Externally associate a hook with the plugin static public function registerHook ( $plugin_name, $hook ) { self::$associated_hooks[ $plugin_name ] = $hook; } - static public function enable ( $plugin_name ) { $plugin_function = self::$prefix . $plugin_name; @@ -50,7 +46,6 @@ static public function enable ( $plugin_name ) { return true; } - static public function disable ( $plugin_name ) { // If the plugin is associated with a hook, we 'un-hook' it diff --git a/lib/Process.php b/lib/Process.php new file mode 100644 index 0000000..cba6c56 --- /dev/null +++ b/lib/Process.php @@ -0,0 +1,1066 @@ +setOptions( $options ); + + // Initialize properties. + $this->uid = 0; + $this->cacheData = array(); + $this->mixins = array(); + $this->abstracts = array(); + $this->errors = array(); + $this->selectorRelationships = array(); + $this->charset = null; + $this->currentFile = null; + $this->tokens = (object) array( + 's' => array(), // Strings + 'c' => array(), // Comments + 'r' => array(), // Rules + 'p' => array(), // Parens + 'u' => array(), // URLs + 't' => array(), // Traces + ); + $this->variables = array(); + $this->misc = new stdclass(); + $this->input = new stdclass(); + $this->output = new stdclass(); + + // Copy config values. + $this->plugins = $config->plugins; + $this->aliases = $config->aliases; + $this->selectorAliases = $config->selectorAliases; + $this->selectorAliasesPatt = null; + + // Run process_init hook. + csscrush_hook::run( 'process_init' ); + } + + public function release () { + unset( + $this->tokens, + $this->variables, + $this->mixins, + $this->abstracts, + $this->selectorRelationships, + $this->misc, + $this->plugins, + $this->aliases, + $this->selectorAliases + ); + } + + // Establish the input and output directories and optionally test output dir. + public function setContext ( $input_dir, $test_output_dir = true ) { + + $doc_root = csscrush::$config->docRoot; + + if ( strpos( $input_dir, $doc_root ) !== 0 ) { + // Not a system path. + $input_dir = realpath( "$doc_root/$input_dir" ); + } + + // Initialise input object and store input directory. + $this->input->path = null; + $this->input->filename = null; + $this->input->dir = $input_dir; + $this->input->dirUrl = substr( $this->input->dir, strlen( $doc_root ) ); + + // Store reference to the output dir. + $this->output->dir = $this->ioCall( 'getOutputDir' ); + $this->output->dirUrl = substr( $this->output->dir, strlen( $doc_root ) ); + + // Test the output directory to see it exists and is writable. + $output_dir_ok = false; + if ( $test_output_dir ) { + $output_dir_ok = $this->ioCall( 'testOutputDir' ); + } + + // Setup the IO handler. + $this->ioCall( 'init' ); + + return $output_dir_ok; + } + + public function ioCall ( $method ) { + + // Fetch the argument list, shift off the first item + $args = func_get_args(); + array_shift( $args ); + + // The method address + $the_method = array( csscrush::$config->io, $method ); + + // Return the call result + return call_user_func_array( $the_method, $args ); + } + + public function setOptions ( $options ) { + + if ( ! is_array( $options ) ) { + $options = array(); + } + + // Keeping track of global vars internally to maintain cache integrity. + $options[ '_globalVars' ] = csscrush::$config->vars; + + // Populate unset options with defaults. + $options += (array) csscrush::$config->options; + + $this->options = (object) $options; + } + + + ############################# + # Tokens. + + public function createTokenLabel ( $type ) { + $counter = ++$this->uid; + return "?$type$counter?"; + } + + public function addToken ( $value, $type ) { + $label = $this->createTokenLabel( $type ); + $this->tokens->{ $type }[ $label ] = $value; + return $label; + } + + public function fetchToken ( $token ) { + $type = substr( $token, 1, 1 ); + $path =& $this->tokens->{ $type }; + if ( isset( $path[ $token ] ) ) { + return $path[ $token ]; + } + return null; + } + + public function releaseToken ( $token ) { + unset( $this->tokens->{ substr( $token, 1, 1 ) }[ $token ] ); + } + + public function restoreTokens ( $str, $type = 'p' ) { + + // Reference the token table. + $token_table =& $this->tokens->{ $type }; + + // Find matching tokens. + $matches = csscrush_regex::matchAll( csscrush_regex::$patt->{ "{$type}Token" }, $str ); + + foreach ( $matches as $m ) { + $token = $m[0][0]; + if ( isset( $token_table[ $token ] ) ) { + $str = str_replace( $token, $token_table[ $token ], $str ); + } + } + return $str; + } + + + ############################# + # Parens. + + public function captureParens ( &$str ) { + + static $callback; + if ( ! $callback ) { + $callback = create_function( '$m', 'return csscrush::$process->addToken( $m[0], \'p\' );' ); + } + $str = preg_replace_callback( csscrush_regex::$patt->balancedParens, $callback, $str ); + } + + public function restoreParens ( &$str, $release = true ) { + + $token_table =& $this->tokens->p; + + foreach ( csscrush_regex::matchAll( csscrush_regex::$patt->pToken, $str ) as $m ) { + $token = $m[0][0]; + if ( isset( $token_table[ $token ] ) ) { + $str = str_replace( $token, $token_table[ $token ], $str ); + if ( $release ) { + unset( $token_table[ $token ] ); + } + } + } + } + + + ############################# + # Boilerplate. + + protected function getBoilerplate () { + + $file = false; + $boilerplate_option = $this->options->boilerplate; + + if ( $boilerplate_option === true ) { + $file = csscrush_util::find( + 'CssCrush-local.boilerplate', 'CssCrush.boilerplate' ); + } + elseif ( is_string( $boilerplate_option ) ) { + if ( file_exists( $boilerplate_option ) ) { + $file = $boilerplate_option; + } + } + + // Return an empty string if no file is found. + if ( ! $file ) { + return ''; + } + + // Load the file + $boilerplate = file_get_contents( $file ); + + // Substitute any tags + if ( preg_match_all( '!\{\{([^}]+)\}\}!', $boilerplate, $boilerplate_matches ) ) { + + $replacements = array(); + foreach ( $boilerplate_matches[0] as $index => $tag ) { + $tag_name = $boilerplate_matches[1][$index]; + if ( $tag_name === 'datetime' ) { + $replacements[] = @date( 'Y-m-d H:i:s O' ); + } + elseif ( $tag_name === 'version' ) { + $replacements[] = 'v' . csscrush::$config->version; + } + else { + $replacements[] = '?'; + } + } + $boilerplate = str_replace( $boilerplate_matches[0], $replacements, $boilerplate ); + } + + // Pretty print. + static $format_callback; + if ( ! $format_callback ) { + $format_callback = create_function( '$it', 'return ! empty($it) ? " $it" : $it;' ); + } + $boilerplate = explode( PHP_EOL, $boilerplate ); + $boilerplate = array_map( 'trim', $boilerplate ); + $boilerplate = array_map( $format_callback, $boilerplate ); + $boilerplate = implode( PHP_EOL . ' *', $boilerplate ); + $boilerplate = <<selectorAliases ) { + + static $callback; + if ( ! $callback ) { + $callback = create_function( '$m', + '$table =& csscrush::$process->selectorAliases; + return isset( $table[ $m[1] ] ) ? $table[ $m[1] ] : ""; + '); + } + $str = preg_replace_callback( + csscrush::$process->selectorAliasesPatt, $callback, $str ); + } + } + + protected function extractSelectorAliases () { + + static $callback; + if ( ! $callback ) { + $callback = create_function( '$m', + 'csscrush::$process->selectorAliases[ $m[1] ] = $m[2];' ); + } + + $this->stream->pregReplaceCallback( csscrush_regex::$patt->selectorAlias, $callback ); + + // Create the selector aliases pattern and store it. + if ( $this->selectorAliases ) { + $names = implode( '|', array_keys( $this->selectorAliases ) ); + $this->selectorAliasesPatt = '#\:(' . $names . ')\b(?!-)#iS'; + } + } + + protected function filterAliases () { + + $options = $this->options; + + // If a vendor target is given, we prune the aliases array + $vendor = $options->vendor_target; + + // Default vendor argument, use all aliases as normal + if ( 'all' === $vendor ) { + return; + } + + // For expicit 'none' argument turn off aliases + if ( 'none' === $vendor ) { + $this->aliases = array(); + return; + } + + // Normalize vendor_target argument + $vendor = '-' . str_replace( '-', '', $vendor ) . '-'; + + // Loop the aliases array, filter down to the target vendor + foreach ( $this->aliases as $group_name => $group_array ) { + + // Property/value aliases are special. + if ( 'values' === $group_name ) { + foreach ( $group_array as $property => $values ) { + $result = array(); + foreach ( $values as $value => $prefix_values ) { + foreach ( $prefix_values as $prefix ) { + if ( strpos( $prefix, $vendor ) === 0 ) { + $result[] = $prefix; + } + } + } + $this->aliases[ 'values' ][ $property ][ $value ] = $result; + } + continue; + } + + foreach ( $group_array as $alias_keyword => $prefix_array ) { + + $result = array(); + foreach ( $prefix_array as $prefix ) { + if ( strpos( $prefix, $vendor ) === 0 ) { + $result[] = $prefix; + } + } + // Prune the whole alias keyword if there is no result. + if ( empty( $result ) ) { + unset( $this->aliases[ $group_name ][ $alias_keyword ] ); + } + else { + $this->aliases[ $group_name ][ $alias_keyword ] = $result; + } + } + } + } + + + ############################# + # Plugins. + + protected function filterPlugins () { + + $options =& $this->options; + + // Load and unload plugins. + // Add option enabled plugins to the list. + if ( is_array( $options->enable ) ) { + foreach ( $options->enable as $plugin_name ) { + $this->plugins[ $plugin_name ] = true; + } + } + + // Remove option disabled plugins from the list, and disable them. + if ( $options->disable === 'all' ) { + $options->disable = array_keys( $config->plugins ); + } + if ( is_array( $options->disable ) ) { + foreach ( $options->disable as $plugin_name ) { + csscrush_plugin::disable( $plugin_name ); + unset( $this->plugins[ $plugin_name ] ); + } + } + + // Enable all plugins in the remaining list. + foreach ( $this->plugins as $plugin_name => $bool ) { + csscrush_plugin::enable( $plugin_name ); + } + } + + + ############################# + # Variables. + + protected function calculateVariables () { + + $regex = csscrush_regex::$patt; + $options = $this->options; + $config = csscrush::$config; + + $this->stream->pregReplaceCallback( $regex->variables, + array( 'csscrush_process', 'cb_extractVariables' ) ); + + // In-file variables override global variables. + $this->variables = array_merge( $config->vars, $this->variables ); + + // Runtime variables override in-file variables. + if ( ! empty( $options->vars ) ) { + $this->variables = array_merge( $this->variables, $options->vars ); + } + + // Place variables referenced inside variables. Excecute custom functions. + foreach ( $this->variables as $name => &$value ) { + + // Referenced variables. + $value = preg_replace_callback( $regex->varFunction, array( 'self', 'cb_placeVariables' ), $value ); + + // Variable values can be escaped from function parsing with a tilde prefix. + if ( strpos( $value, '~' ) !== 0 ) { + csscrush_function::executeCustomFunctions( $value ); + } + } + } + + protected function placeAllVariables () { + + // Place variables in main stream. + self::placeVariables( $this->stream->raw ); + + // Repeat above steps for variables embedded in string tokens. + foreach ( $this->tokens->s as $label => &$value ) { + self::placeVariables( $value ); + } + + // Repeat above steps for variables embedded in URL tokens. + foreach ( $this->tokens->u as $label => $url ) { + if ( self::placeVariables( $url->value ) ) { + // Re-evaluate $url->value if anything has been interpolated. + $url->evaluate(); + } + } + } + + static protected function placeVariables ( &$value ) { + + $regex = csscrush_regex::$patt; + + // Variables with no default value. + $value = preg_replace_callback( $regex->varFunction, + array( 'csscrush_process', 'cb_placeVariables' ), $value, -1, $count ); + + if ( strpos( $value, '$(' ) !== false ) { + + // Variables with default value. + csscrush_function::executeCustomFunctions( $value, $regex->varFunctionStart, + array( '$' => array( 'csscrush_process', 'cb_placeVariablesWithDefault' ) ) ); + + // Assume at least 1 replace. + $count = 1; + } + + // If we know replacements have been made we may want to update $value. e.g URL tokens. + return $count; + } + + static public function cb_extractVariables ( $m ) { + + $regex = csscrush_regex::$patt; + + // Strip comment markers. + $block = trim( csscrush_util::stripCommentTokens( $m[2] ) ); + + $pairs = preg_split( '!\s*;\s*!', $block, null, PREG_SPLIT_NO_EMPTY ); + + // Loop through the pairs. + foreach ( $pairs as $var ) { + $colon = strpos( $var, ':' ); + if ( $colon === -1 ) { + continue; + } + $name = trim( substr( $var, 0, $colon ) ); + $value = trim( substr( $var, $colon + 1 ) ); + csscrush::$process->variables[ trim( $name ) ] = $value; + } + } + + static protected function cb_placeVariables ( $m ) { + $variable_name = $m[1]; + if ( isset( csscrush::$process->variables[ $variable_name ] ) ) { + return csscrush::$process->variables[ $variable_name ]; + } + } + + static public function cb_placeVariablesWithDefault ( $raw_arg ) { + + list( $name, $default_value ) = csscrush_function::parseArgsSimple( $raw_arg ); + + if ( isset( csscrush::$process->variables[ $name ] ) ) { + return csscrush::$process->variables[ $name ]; + } + else { + return $default_value; + } + } + + + ############################# + # @ifdefine blocks. + + protected function resolveIfDefines () { + + $matches = $this->stream->matchAll( csscrush_regex::$patt->ifDefine ); + + // Move through the matches last to first. + while ( $match = array_pop( $matches ) ) { + + $curly_match = new csscrush_balancedMatch( $this->stream, $match[0][1] ); + + if ( ! $curly_match->match ) { + // Couldn't match the block. + continue; + } + + $negate = $match[1][1] != -1; + $name = $match[2][0]; + $name_defined = isset( $this->variables[ $name ] ); + + if ( ! $negate && $name_defined || $negate && ! $name_defined ) { + // Test resolved true so include the innards. + $curly_match->unWrap(); + } + else { + // Recontruct the stream without the innards. + $curly_match->replace( '' ); + } + } + } + + + ############################# + # Mixins. + + protected function extractMixins () { + + static $callback; + if ( ! $callback ) { + $callback = create_function( '$m', ' + $name = trim( $m[1] ); + $block = trim( $m[2] ); + if ( ! empty( $name ) && ! empty( $block ) ) { + csscrush::$process->mixins[ $name ] = new csscrush_mixin( $block ); + } + ' ); + } + + $this->stream->pregReplaceCallback( csscrush_regex::$patt->mixin, $callback ); + } + + + ############################# + # Fragments. + + protected function resolveFragments () { + + $regex = csscrush_regex::$patt; + $matches = $this->stream->matchAll( $regex->fragmentDef ); + $fragments = array(); + + // Move through the matches last to first. + while ( $match = array_pop( $matches ) ) { + + $match_start_pos = $match[0][1]; + $fragment_name = $match[1][0]; + + $curly_match = new csscrush_balancedMatch( $this->stream, $match_start_pos ); + + if ( ! $curly_match->match ) { + // Couldn't match the block. + continue; + } + else { + // Reconstruct the stream without the fragment. + $curly_match->replace( '' ); + + // Create the fragment and store it. + $fragments[ $fragment_name ] = new csscrush_fragment( $curly_match->inside() ); + } + } + + // Now find all the fragment calls. + $matches = $this->stream->matchAll( $regex->fragmentCall ); + + // Move through the matches last to first. + while ( $match = array_pop( $matches ) ) { + + list( $match_string, $match_start_pos ) = $match[0]; + + // The matched fragment name. + $fragment_name = $match[1][0]; + + // The fragment object, or null if name not present. + $fragment = isset( $fragments[ $fragment_name ] ) ? $fragments[ $fragment_name ] : null; + + // Fragment may be called without any argument list. + $with_arguments = $match[2][0] === '('; + + if ( $with_arguments ) { + $paren_match = new csscrush_balancedMatch( $this->stream, $match_start_pos, '()' ); + // Get offset of statement terminating semi-colon. + $match_end = $paren_match->nextIndexOf( ';' ) + 1; + $match_length = $match_end - $match_start_pos; + } + else { + $match_length = strlen( $match_string ); + } + + if ( ! $fragment || ( $with_arguments && ! $paren_match->match ) ) { + + // Invalid fragment or malformed argument list. + $this->stream->splice( '', $match_start_pos, $match_length ); + continue; + } + else { + + $args = array(); + if ( $with_arguments ) { + // Get the argument array to pass to the fragment. + $args = csscrush_util::splitDelimList( $paren_match->inside() ); + } + + // Execute the fragment and get the return value. + $fragment_return = $fragment->call( $args ); + + // Recontruct the stream with the fragment return value. + $this->stream->splice( $fragment_return, $match_start_pos, $match_length ); + } + } + } + + + ############################# + # Rules. + + public function extractRules () { + $this->stream->pregReplaceCallback( csscrush_regex::$patt->rule, array( 'csscrush_process', 'cb_extractRules' ) ); + } + + protected function processRules () { + + // Reset the selector relationships + $this->selectorRelationships = array(); + + $aliases =& $this->aliases; + + foreach ( $this->tokens->r as $rule ) { + + // Store selector relationships + $rule->indexSelectors(); + + csscrush_hook::run( 'rule_prealias', $rule ); + + if ( ! empty( $aliases[ 'properties' ] ) ) { + $rule->addPropertyAliases(); + } + if ( ! empty( $aliases[ 'functions' ] ) ) { + $rule->addFunctionAliases(); + } + if ( ! empty( $aliases[ 'values' ] ) ) { + $rule->addValueAliases(); + } + + csscrush_hook::run( 'rule_postalias', $rule ); + + $rule->expandSelectors(); + + // Find previous selectors and apply them + $rule->applyExtendables(); + + csscrush_hook::run( 'rule_postprocess', $rule ); + } + } + + static public function cb_extractRules ( $m ) { + + $rule = (object) array(); + $rule->selector_raw = trim( $m[1] ); + $rule->declaration_raw = trim( $m[2] ); + + // Apply any selector aliases. + csscrush_process::applySelectorAliases( $rule->selector_raw ); + + // Run rule_preprocess hook. + csscrush_hook::run( 'rule_preprocess', $rule ); + + $rule = new csscrush_rule( $rule->selector_raw, $rule->declaration_raw ); + + // Store rules if they have declarations or extend arguments. + if ( count( $rule ) || $rule->extendArgs ) { + + csscrush::$process->tokens->r[ $rule->label ] = $rule; + + // If only using extend still return a label. + return $rule->label . "\n"; + } + } + + + ############################# + # @in blocks. + + protected function prefixSelectors () { + + $matches = $this->stream->matchAll( '~@in\s+([^{]+)\{~iS' ); + + // Move through the matches in reverse order. + while ( $match = array_pop( $matches ) ) { + + $match_start_pos = $match[0][1]; + $raw_argument = trim( $match[1][0] ); + + csscrush_process::applySelectorAliases( $raw_argument ); + + $this->captureParens( $raw_argument ); + $arguments = csscrush_util::splitDelimList( $raw_argument ); + + $curly_match = new csscrush_balancedMatch( $this->stream, $match_start_pos ); + + if ( ! $curly_match->match || empty( $raw_argument ) ) { + // Couldn't match the block. + continue; + } + + // Match all the rule tokens. + $rule_matches = csscrush_regex::matchAll( + csscrush_regex::$patt->rToken, $curly_match->inside() ); + + foreach ( $rule_matches as $rule_match ) { + + // Get the rule instance. + $rule = csscrush_rule::get( $rule_match[0][0] ); + + // Set the isNested flag. + $rule->isNested = true; + + // Using arguments create new selector list for the rule. + $new_selector_list = array(); + + foreach ( $arguments as $arg_selector ) { + + foreach ( $rule->selectorList as $rule_selector ) { + + if ( ! $rule_selector->allowPrefix ) { + + $new_selector_list[ $rule_selector->readableValue ] = $rule_selector; + } + elseif ( strpos( $rule_selector->value, '&' ) !== false ) { + + // Ampersand is the positional symbol for where the + // prefix will be placed. + + // Find and replace (once) the ampersand. + $new_value = preg_replace( + '!&!', + $arg_selector, + $rule_selector->value, + 1 ); + + // Not storing the selector as named. + $new_selector_list[] = new csscrush_selector( $new_value ); + } + else { + + // Not storing the selector as named. + $new_selector_list[] + = new csscrush_selector( "$arg_selector {$rule_selector->value}" ); + } + } + } + $rule->selectorList = $new_selector_list; + } + + $curly_match->unWrap(); + } + } + + + ############################# + # @-rule aliasing. + + protected function aliasAtRules () { + + if ( empty( $this->aliases[ 'at-rules' ] ) ) { + return; + } + + $aliases = $this->aliases[ 'at-rules' ]; + $regex = csscrush_regex::$patt; + + foreach ( $aliases as $at_rule => $at_rule_aliases ) { + + $matches = $this->stream->matchAll( "~@$at_rule" . '[\s{]~i' ); + + // Find at-rules that we want to alias. + while ( $match = array_pop( $matches ) ) { + + $curly_match = new csscrush_balancedMatch( $this->stream, $match[0][1] ); + + if ( ! $curly_match->match ) { + // Couldn't match the block. + continue; + } + + // Build up string with aliased blocks for splicing. + $original_block = $curly_match->whole(); + $new_blocks = array(); + + foreach ( $at_rule_aliases as $alias ) { + + // Copy original block, replacing at-rule with alias name. + $copy_block = str_replace( "@$at_rule", "@$alias", $original_block ); + + // Aliases are nearly always prefixed, capture the current vendor name. + preg_match( $regex->vendorPrefix, $alias, $vendor ); + + $vendor = $vendor ? $vendor[1] : null; + + // Duplicate rules. + if ( preg_match_all( $regex->rToken, $copy_block, $copy_matches ) ) { + + $originals = array(); + $replacements = array(); + + foreach ( $copy_matches[0] as $copy_match ) { + + // Clone the matched rule. + $originals[] = $rule_label = $copy_match; + $cloneRule = clone $this->tokens->r[ $rule_label ]; + + // Set the vendor context. + $cloneRule->vendorContext = $vendor; + + // Filter out declarations that have different vendor context. + $new_set = array(); + foreach ( $cloneRule as $declaration ) { + if ( ! $declaration->vendor || $declaration->vendor === $vendor ) { + $new_set[] = $declaration; + } + } + $cloneRule->declarations = $new_set; + + // Store the clone. + $replacements[] = $this->addToken( $cloneRule, 'r' ); + + } + // Finally replace the original labels with the cloned rule labels. + $copy_block = str_replace( $originals, $replacements, $copy_block ); + } + + // Add the copied block to the stack. + $new_blocks[] = $copy_block; + } + + // The original version is always pushed last in the list. + $new_blocks[] = $original_block; + + // Splice in the blocks. + $curly_match->replace( implode( "\n", $new_blocks ) ); + } + } + } + + + ############################# + # Compile / collate. + + protected function collate () { + + $options = $this->options; + $minify = ! $options->debug; + $regex = csscrush_regex::$patt; + $regex_replacements = array(); + + if ( $minify ) { + + // Strip whitespace around colons used in @-rule arguments. + $regex_replacements[ '! ?\: ?!' ] = ':'; + // Strip newlines added during parsing. + $regex_replacements[ '!\n+!' ] = ''; + } + else { + + // Pretty printing. + $regex_replacements[ '!([{}])!' ] = "$1\n"; + $regex_replacements[ '!([^\s])\{!' ] = "$1 {"; + $regex_replacements[ '!([@])!' ] = "\n$1"; + + // Newlines after some tokens. + $regex_replacements[ '!(\?[rc][0-9]+\?)!' ] = "$1\n"; + + // Kill double spaces. + $regex_replacements[ '!\n+!' ] = "\n"; + } + + // Kill leading space. + $regex_replacements[ '!\n\s+!' ] = "\n"; + + // Apply all replacements. + $this->stream->pregReplaceHash( $regex_replacements )->lTrim(); + + // Print out rules. + $this->stream->replaceHash( $this->tokens->r ); + + // Insert parens. + $this->stream->replaceHash( $this->tokens->p ); + + // Compress hex-codes, collapse TRBL lists etc. + $this->decruft(); + + if ( $minify ) { + // Trim whitespace around selector combinators. + $this->stream->pregReplace( '! ?([>~+]) ?!S', '$1' ); + } + else { + // Add space after commas. + $this->stream->replace( ',', ', ' ); + + // Add newlines after comments. + foreach ( $this->tokens->c as $token => &$comment ) { + $comment .= "\n"; + } + + // Insert comments and do final whitespace cleanup. + $this->stream + ->replaceHash( $this->tokens->c ) + ->pregReplace( '!\n{3,}!', "\n\n" ) + ->trim() + ->append( "\n" ); + } + + // Insert URLs. + if ( $this->tokens->u ) { + + $link = csscrush_util::getLinkBetweenDirs( $this->output->dir, $this->input->dir ); + + foreach ( $this->tokens->u as $token => $url ) { + + if ( $url->isRelative ) { + // Optionally set the URLs to absolute. + if ( $options->rewrite_import_urls === 'absolute' ) { + $url->prepend( $this->input->dirUrl . '/' ); + } + // If output dir is different to input dir prepend a link between the two. + elseif ( $link ) { + $url->prepend( $link ); + } + } + + if ( $url->convertToData ) { + $url->toData(); + } + else { + $url->simplify(); + } + } + $this->stream->replaceHash( $this->tokens->u ); + } + + // Insert string literals. + $this->stream->replaceHash( $this->tokens->s ); + + // Add in boilerplate. + if ( $options->boilerplate ) { + $this->stream->prepend( $this->getBoilerplate() ); + } + + // Add @charset at top if set. + if ( $this->charset ) { + $this->stream->prepend( "@charset \"$this->charset\";\n" ); + } + } + + public function compile () { + + // Resolve active aliases and plugins. + $this->filterPlugins(); + $this->filterAliases(); + + // Collate hostfile and imports. + $this->stream = new csscrush_stream( csscrush_importer::hostfile( $this->input ) ); + + // Extract and calculate variables. + $this->calculateVariables(); + + // Place variables. + $this->placeAllVariables(); + + // Resolve @ifdefine blocks. + $this->resolveIfDefines(); + + // Get selector aliases. + $this->extractSelectorAliases(); + + // Pull out @mixin definitions. + $this->extractMixins(); + + // Pull out @fragment blocks, and invoke. + $this->resolveFragments(); + + // Adjust the stream so we can extract the rules cleanly. + $this->stream->replaceHash( array( + '@' => "\n@", + '}' => "}\n", + '{' => "{\n", + ';' => ";\n", + ))->prepend( "\n" ); + + // Parse rules. + $this->extractRules(); + + // Process @in blocks. + $this->prefixSelectors(); + + // Main processing on the rule objects. + $this->processRules(); + + // Alias any @-rules. + $this->aliasAtRules(); + + // Print rules, optionally minify. + $this->collate(); + + // Release memory. + $this->release(); + + return $this->stream; + } + + + ############################# + # Decruft. + + protected function decruft () { + + return $this->stream->pregReplaceHash( array( + + // Strip leading zeros on floats. + '!([: \(,])(-?)0(\.\d+)!S' => '$1$2$3', + + // Strip unnecessary units on zero values for length types. + '!([: \(,])\.?0(?:e[mx]|c[hm]|rem|v[hwm]|in|p[tcx])!iS' => '${1}0', + + // Collapse zero lists. + '!(\: *)(?:0 0 0|0 0 0 0) *([;}])!S' => '${1}0$2', + + // Collapse zero lists 2nd pass. + '!(padding|margin|border-radius) ?(\: *)0 0 *([;}])!iS' => '${1}${2}0$3', + + // Dropping redundant trailing zeros on TRBL lists. + '!(\: *)(-?(?:\d+)?\.?\d+[a-z]{1,4}) 0 0 0 *([;}])!iS' => '$1$2 0 0$3', + '!(\: *)0 0 (-?(?:\d+)?\.?\d+[a-z]{1,4}) 0 *([;}])!iS' => '${1}0 0 $2$3', + + // Compress hex codes. + '!\#([0-9a-f])\1([0-9a-f])\2([0-9a-f])\3!iS' => '#$1$2$3', + )); + } + +} diff --git a/lib/Regex.php b/lib/Regex.php index 745ae0e..f9b4869 100644 --- a/lib/Regex.php +++ b/lib/Regex.php @@ -4,18 +4,18 @@ * Regex management. * */ - class csscrush_regex { - public static $patt; + // Patterns. + static public $patt; // Character classes. - public static $classes; + static public $classes; - public static function init () { + static public function init () { - self::$patt = $patt = (object) array(); - self::$classes = $classes = (object) array(); + self::$patt = $patt = new stdclass(); + self::$classes = $classes = new stdclass(); // Character classes. $classes->ident = '[a-zA-Z0-9_-]+'; @@ -23,19 +23,23 @@ public static function init () { // Patterns. $patt->ident = '!^' . $classes->ident . '$!'; - $patt->import = '!@import (\?u\d+\?) ?([^;]*);!iS'; - - $patt->variables = '!@(?:variables|define)\s*([^\{]*)\{\s*(.*?)\s*\};?!s'; - $patt->mixin = '!@mixin\s*([^\{]*)\{\s*(.*?)\s*\};?!s'; - $patt->abstract = csscrush_regex::create( '^@abstract\s+()', 'i' ); - - $patt->commentAndString = '! + // @-rule blocks. + $patt->import = '~@import\s+(\?u\d+\?)\s?([^;]*);~iS'; + $patt->variables = '~@(?:define|variables) *([^\{]*)\{ *(.*?) *\};?~iS'; + $patt->mixin = '~@mixin *([^\{]*)\{ *(.*?) *\};?~iS'; + $patt->abstract = csscrush_regex::create( '^@abstract\s+()', 'i' ); + $patt->selectorAlias = csscrush_regex::create( '@selector-alias +\:() +([^;]+) *;', 'iS' ); + $patt->ifDefine = csscrush_regex::create( '@ifdefine +(not +)?() *\{', 'iS' ); + $patt->fragmentDef = csscrush_regex::create( '@fragment +() *\{', 'iS' ); + $patt->fragmentCall = csscrush_regex::create( '@fragment +() *(\(|;)', 'iS' ); + + $patt->commentAndString = '~ # Quoted string (to EOF if unmatched). (\'|")(?:\\\\\1|[^\1])*?(?:\1|$) | # Block comment (to EOF if unmatched). /\*(?:.*?)(?:\*/|$) - !xsS'; + ~xsS'; // As an exception we treat some @-rules like standard rule blocks. $patt->rule = '~ @@ -63,10 +67,9 @@ public static function init () { $patt->aToken = '!\?arg(\d+)\?!'; // Args // Functions. - $patt->varFunction = '!\$\(\s*([a-z0-9_-]+)\s*\)!iS'; $patt->function = '!(^|[^a-z0-9_-])([a-z_-]+)(\?p\d+\?)!iS'; - - // Specific functions. + $patt->varFunction = csscrush_regex::create( '\$\(\s*()\s*\)', 'iS' ); + $patt->varFunctionStart = '!(\$)\(!'; $patt->argFunction = csscrush_regex::createFunctionMatchPatt( array( 'arg' ) ); $patt->queryFunction = csscrush_regex::createFunctionMatchPatt( array( 'query' ) ); $patt->thisFunction = csscrush_regex::createFunctionMatchPatt( array( 'this' ) ); @@ -74,25 +77,22 @@ public static function init () { // Misc. $patt->vendorPrefix = '!^-([a-z]+)-([a-z-]+)!iS'; $patt->mixinExtend = '!^(?:(@include|mixin)|(@?extends?))[\s\:]+!iS'; - $patt->absoluteUrl = '!^https?://!'; $patt->argListSplit = '!\s*[,\s]\s*!S'; $patt->mathBlacklist = '![^\.0-9\*\/\+\-\(\)]!S'; $patt->charset = '!@charset\s+(\?s\d+\?)\s*;!iS'; } - - public static function create ( $pattern_template, $flags = '' ) { + static public function create ( $pattern_template, $flags = '', $delim = '!' ) { // Sugar. $pattern = str_replace( array( '' ), array( self::$classes->ident ), $pattern_template ); - return '!' . $pattern . "!$flags"; + return "$delim{$pattern}$delim{$flags}"; } - - public static function matchAll ( $patt, $subject, $preprocess_patt = false, $offset = 0 ) { + static public function matchAll ( $patt, $subject, $preprocess_patt = false, $offset = 0 ) { if ( $preprocess_patt ) { // Assume case-insensitive. @@ -103,15 +103,13 @@ public static function matchAll ( $patt, $subject, $preprocess_patt = false, $of return $count ? $matches : array(); } - - public static function createFunctionMatchPatt ( $list, $include_unnamed_function = false ) { + static public function createFunctionMatchPatt ( $list, $include_unnamed_function = false ) { $question = $include_unnamed_function ? '?' : ''; foreach ( $list as &$fn_name ) { $fn_name = preg_quote( $fn_name ); } - return '#(?queryFunction, $value ) ) { - - csscrush_function::executeCustomFunctions( $value, - csscrush_regex::$patt->queryFunction, array( - 'query' => array( $this, 'cssQueryFunction' ), - ), $prop ); - } - - if ( strpos( $prop, 'data-' ) === 0 ) { - - // If it's with data prefix, we don't want to print it - // Just remove the prefix - $prop = substr( $prop, strlen( 'data-' ) ); - - // On first pass we want to store data properties on $this->data, - // as well as on local - $this->data[ $prop ] = $value; - } - else { - - // Add to the stack - $pairs[] = array( $prop, $value ); - } - - // Set on $this->localData - $this->localData[ $prop ] = $value; - - // Unset on data tables if the value has a this() call: - // - Restriction to avoid circular references - if ( preg_match( csscrush_regex::$patt->thisFunction, $value ) ) { - - unset( $this->localData[ $prop ] ); - unset( $this->data[ $prop ] ); - } - } - } - public function __construct ( $selector_string = null, $declarations_string ) { $regex = csscrush_regex::$patt; - $options = csscrush::$process->options; - $this->label = csscrush::tokenLabelCreate( 'r' ); + $process = csscrush::$process; + $options = $process->options; + $this->label = $process->createTokenLabel( 'r' ); // If tracing store the last tracing stub, then strip all. if ( @@ -92,7 +50,7 @@ public function __construct ( $selector_string = null, $declarations_string ) { // Parse the selectors chunk if ( ! empty( $selector_string ) ) { - csscrush::captureParens( $selector_string ); + $process->captureParens( $selector_string ); $selectors = csscrush_util::splitDelimList( $selector_string ); // Remove and store comments that sit above the first selector @@ -113,7 +71,7 @@ public function __construct ( $selector_string = null, $declarations_string ) { $abstract_name = $m[1]; // Link the rule to the abstract name and skip forward to declaration parsing - csscrush::$process->abstracts[ $abstract_name ] = $this; + $process->abstracts[ $abstract_name ] = $this; break; } @@ -263,6 +221,47 @@ public function __toString () { } } + public function declarationCheckin ( $prop, $value, &$pairs ) { + + if ( $prop !== '' && $value !== '' ) { + + // First resolve query() calls that reference earlier rules + if ( preg_match( csscrush_regex::$patt->queryFunction, $value ) ) { + + csscrush_function::executeCustomFunctions( $value, + csscrush_regex::$patt->queryFunction, array( + 'query' => array( $this, 'cssQueryFunction' ), + ), $prop ); + } + + if ( strpos( $prop, 'data-' ) === 0 ) { + + // If it's with data prefix, we don't want to print it + // Just remove the prefix + $prop = substr( $prop, strlen( 'data-' ) ); + + // On first pass we want to store data properties on $this->data, + // as well as on local + $this->data[ $prop ] = $value; + } + else { + + // Add to the stack + $pairs[] = array( $prop, $value ); + } + + // Set on $this->localData + $this->localData[ $prop ] = $value; + + // Unset on data tables if the value has a this() call: + // - Restriction to avoid circular references + if ( preg_match( csscrush_regex::$patt->thisFunction, $value ) ) { + + unset( $this->localData[ $prop ], $this->data[ $prop ] ); + } + } + } + public function cssThisFunction ( $input, $fn_name ) { $args = csscrush_function::parseArgsSimple( $input ); @@ -694,6 +693,7 @@ public function count() { return count( $this->_declarations ); } + ############ # Rule API @@ -731,7 +731,7 @@ public function addDeclaration ( $prop, $value, $contextIndex = 0 ) { return false; } - public static function get ( $token ) { + static public function get ( $token ) { if ( isset( csscrush::$process->tokens->r[ $token ] ) ) { return csscrush::$process->tokens->r[ $token ]; @@ -740,12 +740,12 @@ public static function get ( $token ) { } } + /** * - * Declaration objects + * Declaration objects. * */ - class csscrush_declaration { public $property; @@ -806,7 +806,7 @@ public function __construct ( $prop, $value, $contextIndex = 0 ) { } // Capture all remaining paren pairs. - csscrush::captureParens( $value ); + csscrush::$process->captureParens( $value ); // Create an index of all regular functions in the value. if ( preg_match_all( $regex->function, $value, $functions ) > 0 ) { @@ -832,16 +832,15 @@ public function __construct ( $prop, $value, $contextIndex = 0 ) { public function getFullValue () { - return csscrush::tokenRestoreAll( $this->value, 'p' ); + return csscrush::$process->restoreTokens( $this->value, 'p' ); } } - /** * - * Selector objects + * Selector objects. * */ class csscrush_selector { @@ -850,11 +849,11 @@ class csscrush_selector { public $readableValue; public $allowPrefix = true; - public static function makeReadableSelector ( $selector_string ) { + static function makeReadableSelector ( $selector_string ) { // Quick test for paren tokens if ( strpos( $selector_string, '?p' ) !== false ) { - $selector_string = csscrush::tokenRestoreAll( $selector_string, 'p' ); + $selector_string = csscrush::$process->restoreTokens( $selector_string, 'p' ); } // Create space around combinators, then normalize whitespace @@ -863,7 +862,7 @@ public static function makeReadableSelector ( $selector_string ) { // Quick test for string tokens if ( strpos( $selector_string, '?s' ) !== false ) { - $selector_string = csscrush::tokenRestoreAll( $selector_string, 's' ); + $selector_string = csscrush::$process->restoreTokens( $selector_string, 's' ); } // Quick test for double-colons for backwards compat @@ -894,7 +893,7 @@ public function __toString () { public function appendPseudo ( $pseudo ) { // Check to avoid doubling-up - if ( ! csscrush_util::strEndsWith( $this->readableValue, $pseudo ) ) { + if ( ! csscrush_stream::endsWith( $this->readableValue, $pseudo ) ) { $this->readableValue .= $pseudo; $this->value .= $pseudo; @@ -904,10 +903,9 @@ public function appendPseudo ( $pseudo ) { } - /** * - * Extend argument objects + * Extend argument objects. * */ class csscrush_extendArg { @@ -937,4 +935,3 @@ public function __construct ( $name ) { } } - diff --git a/lib/Util.php b/lib/Util.php index fd12f80..318fa87 100644 --- a/lib/Util.php +++ b/lib/Util.php @@ -1,14 +1,13 @@ $value ) { @@ -18,14 +17,7 @@ public static function htmlAttributes ( array $attributes ) { return $attr_string; } - - public static function strEndsWith ( $haystack, $needle ) { - - return substr( $haystack, -strlen( $needle ) ) === $needle; - } - - - public static function normalizePath ( $path, $strip_drive_letter = false ) { + static public function normalizePath ( $path, $strip_drive_letter = false ) { if ( $strip_drive_letter ) { $path = preg_replace( '!^[a-z]\:!i', '', $path ); @@ -34,24 +26,15 @@ public static function normalizePath ( $path, $strip_drive_letter = false ) { $path = rtrim( preg_replace( '![\\\\/]+!', '/', $path ), '/' ); // Removing redundant './'. - $path = preg_replace( '!^\./|/\./!', '', $path ); + $path = str_replace( '/./', '/', $path ); + if ( strpos( $path, './' ) === 0 ) { + $path = substr( $path, 2 ); + } return $path; } - - public static function strReplaceHash ( $str, $map = array() ) { - - if ( ! $map ) { - return $str; - } - $labels = array_keys( $map ); - $values = array_values( $map ); - return str_replace( $labels, $values, $str ); - } - - - public static function find () { + static public function find () { foreach ( func_get_args() as $file ) { $file_path = csscrush::$config->location . '/' . $file; @@ -62,14 +45,12 @@ public static function find () { return false; } - - public static function stripCommentTokens ( $str ) { + static public function stripCommentTokens ( $str ) { return preg_replace( csscrush_regex::$patt->cToken, '', $str ); } - - public static function normalizeWhiteSpace ( $str ) { + static public function normalizeWhiteSpace ( $str ) { $replacements = array( // Convert all whitespace sequences to a single space. @@ -77,14 +58,13 @@ public static function normalizeWhiteSpace ( $str ) { // Trim bracket whitespace where it's safe to do it. '!([\[(]) | ([\])])| ?([{}]) ?!S' => '${1}${2}${3}', // Trim whitespace around delimiters and special characters. - '! ?([;/,]) ?!S' => '$1', + '! ?([;,]) ?!S' => '$1', ); return preg_replace( array_keys( $replacements ), array_values( $replacements ), $str ); } - - public static function splitDelimList ( $str, $delim = ',', $trim = true ) { + static public function splitDelimList ( $str, $delim = ',', $trim = true ) { $do_preg_split = strlen( $delim ) > 1 ? true : false; @@ -131,47 +111,7 @@ public static function splitDelimList ( $str, $delim = ',', $trim = true ) { return $list; } - - public static function matchBrackets ( $str, $brackets = '()', $offset = 0, $capture_text = false ) { - - list( $opener, $closer ) = str_split( $brackets, 1 ); - - $match = (object) array(); - - if ( strpos( $str, $opener, $offset ) === false ) { - return false; - } - - if ( substr_count( $str, $opener ) !== substr_count( $str, $closer ) ) { - $sample = substr( $str, $offset, 25 ); - trigger_error( __METHOD__ . ": Unmatched token near '$sample'.\n", E_USER_WARNING ); - return false; - } - - $patt = csscrush_regex::$patt->balancedParens; - if ( $opener === '{' ) { - $patt = csscrush_regex::$patt->balancedCurlies; - } - - if ( preg_match( $patt, $str, $m, PREG_OFFSET_CAPTURE, $offset ) ) { - - $match->start = $m[0][1]; - $match->end = $match->start + strlen( $m[0][0] ); - - if ( $capture_text ) { - // Text capturing is optional to avoid using memory when not necessary. - $match->inside = $m[1][0]; - $match->after = substr( $str, $match->end ); - } - return $match; - } - - trigger_error( __METHOD__ . ": Could not match '$opener'. Exiting.\n", E_USER_WARNING ); - return false; - } - - - public static function getLinkBetweenDirs ( $dir1, $dir2 ) { + static public function getLinkBetweenDirs ( $dir1, $dir2 ) { // Normalise the paths. $dir1 = trim( csscrush_util::normalizePath( $dir1, true ), '/' ); @@ -201,6 +141,75 @@ public static function getLinkBetweenDirs ( $dir1, $dir2 ) { } +/** + * + * Balanced bracket matching on the main stream. + * + */ +class csscrush_balancedMatch { + + public function __construct ( csscrush_stream $stream, $offset, $brackets = '{}' ) { + + $this->stream = $stream; + $this->offset = $offset; + $this->match = null; + $this->length = 0; + + list( $opener, $closer ) = str_split( $brackets, 1 ); + + if ( strpos( $stream->raw, $opener, $this->offset ) === false ) { + return; + } + + if ( substr_count( $stream->raw, $opener ) !== substr_count( $stream->raw, $closer ) ) { + $sample = substr( $stream->raw, $this->offset, 25 ); + trigger_error( __METHOD__ . ": Unmatched token near '$sample'.\n", E_USER_WARNING ); + return; + } + + $patt = $opener === '{' ? + csscrush_regex::$patt->balancedCurlies : csscrush_regex::$patt->balancedParens; + + if ( preg_match( $patt, $stream->raw, $m, PREG_OFFSET_CAPTURE, $this->offset ) ) { + + $this->match = $m; + $this->matchLength = strlen( $m[0][0] ); + $this->matchStart = $m[0][1]; + $this->matchEnd = $this->matchStart + $this->matchLength; + $this->length = $this->matchEnd - $this->offset; + } + else { + trigger_error( __METHOD__ . ": Could not match '$opener'. Exiting.\n", E_USER_WARNING ); + } + } + + public function inside () { + + return $this->match[1][0]; + } + + public function whole () { + + return substr( $this->stream->raw, $this->offset, $this->length ); + } + + public function replace ( $replacement ) { + + $this->stream->splice( $replacement, $this->offset, $this->length ); + } + + public function unWrap () { + + $this->stream->splice( $this->inside(), $this->offset, $this->length ); + } + + public function nextIndexOf ( $needle ) { + + return strpos( $this->stream->raw, $needle, $this->offset ); + } +} + + /** * * URL tokens. @@ -218,18 +227,18 @@ class csscrush_url { public function __construct ( $raw_value, $convert_to_data = false ) { $regex = csscrush_regex::$patt; + $process = csscrush::$process; if ( preg_match( $regex->sToken, $raw_value ) ) { - $this->value = trim( csscrush::tokenFetch( $raw_value ), '\'"' ); - csscrush::tokenRelease( $raw_value ); + $this->value = trim( $process->fetchToken( $raw_value ), '\'"' ); + $process->releaseToken( $raw_value ); } else { $this->value = $raw_value; } $this->evaluate(); - $this->label = csscrush::tokenLabelCreate( 'u' ); - csscrush::$process->tokens->u[ $this->label ] = $this; + $this->label = $process->addToken( $this, 'u' ); } public function __toString () { @@ -240,7 +249,7 @@ public function __toString () { return "url(http://github.com/$quote$this-%3Evalue$quote)"; } - public static function get ( $token ) { + static public function get ( $token ) { return csscrush::$process->tokens->u[ $token ]; } @@ -265,11 +274,6 @@ public function evaluate () { } } - public function applyVariables () { - csscrush::placeVariables( $this->value ); - $this->evaluate(); - } - public function toData () { if ( $this->isRooted ) { @@ -341,7 +345,112 @@ public function simplify () { /** * - * Version string sugar + * Stream sugar. + * + */ +class csscrush_stream { + + public function __construct ( $str ) { + $this->raw = $str; + } + + public function __toString () { + return $this->raw; + } + + static public function endsWith ( $haystack, $needle ) { + + return substr( $haystack, -strlen( $needle ) ) === $needle; + } + + public function update ( $str ) { + $this->raw = $str; + return $this; + } + + public function substr ( $start, $length = null ) { + if ( is_null( $length ) ) { + return substr( $this->raw, $start ); + } + else { + return substr( $this->raw, $start, $length ); + } + } + + public function matchAll ( $patt, $preprocess_patt = false ) { + return csscrush_regex::matchAll( $patt, $this->raw, $preprocess_patt ); + } + + public function replace ( $find, $replacement ) { + $this->raw = str_replace( $find, $replacement, $this->raw ); + return $this; + } + + public function replaceHash ( $replacements ) { + if ( $replacements ) { + $this->raw = str_replace( + array_keys( $replacements ), + array_values( $replacements ), + $this->raw ); + } + return $this; + } + + public function pregReplace ( $patt, $replacement ) { + $this->raw = preg_replace( $patt, $replacement, $this->raw ); + return $this; + } + + public function pregReplaceCallback ( $patt, $callback ) { + $this->raw = preg_replace_callback( $patt, $callback, $this->raw ); + return $this; + } + + public function pregReplaceHash ( $replacements ) { + if ( $replacements ) { + $this->raw = preg_replace( + array_keys( $replacements ), + array_values( $replacements ), + $this->raw ); + } + return $this; + } + + public function append ( $append ) { + $this->raw .= $append; + return $this; + } + + public function prepend ( $prepend ) { + $this->raw = $prepend . $this->raw; + return $this; + } + + public function splice ( $replacement, $offset, $length = null ) { + $this->raw = substr_replace( $this->raw, $replacement, $offset, $length ); + return $this; + } + + public function trim () { + $this->raw = trim( $this->raw ); + return $this; + } + + public function rTrim () { + $this->raw = rtrim( $this->raw ); + return $this; + } + + public function lTrim () { + $this->raw = ltrim( $this->raw ); + return $this; + } +} + + +/** + * + * Version string. * */ class csscrush_version { diff --git a/misc/property-sorting.ini b/misc/property-sorting.ini index 8f1eb1e..799d262 100644 --- a/misc/property-sorting.ini +++ b/misc/property-sorting.ini @@ -1,5 +1,5 @@ ; Table for property sorting. -; Vendor prefixes are added ar runtime. +; Vendor prefixes are added at runtime. ; Positioning position diff --git a/plugins/hocus-pocus.php b/plugins/hocus-pocus.php index e0fa054..f638832 100644 --- a/plugins/hocus-pocus.php +++ b/plugins/hocus-pocus.php @@ -13,13 +13,5 @@ * */ -csscrush_hook::add( 'rule_preprocess', 'csscrush__hocus_pocus' ); - -function csscrush__hocus_pocus ( $rule ) { - - $adjustments = array( - '!:hocus([^a-z0-9_-]|$)!' => ':any(:hover,:focus)$1', - '!:pocus([^a-z0-9_-]|$)!' => ':any(:hover,:focus,:active)$1', - ); - $rule->selector_raw = preg_replace( array_keys( $adjustments ), array_values( $adjustments ), $rule->selector_raw ); -} +csscrush::$config->selectorAliases[ 'hocus' ] = ':any(:hover,:focus)'; +csscrush::$config->selectorAliases[ 'pocus' ] = ':any(:hover,:focus,:active)'; From 70af995ff613391f972f873835922203afd38b7d Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 5 Nov 2012 12:23:55 +0000 Subject: [PATCH 052/421] Added csscrush::stat method to retrieve logged parameters. Expanded trace option to take an optional array of log parameters; log params available are stubs, selector_count and compile_time. Debug option renamed to 'minify'; debug option will still work as before but is deprecated. Added support for outputting end-of-lines according to OS. --- CHANGELOG.txt | 7 +++- cli.php | 2 +- lib/Core.php | 100 +++++++++++++++++++++++++++++++++++------------ lib/Importer.php | 30 ++++++++------ lib/Process.php | 80 ++++++++++++++++++------------------- lib/Rule.php | 21 +++++----- 6 files changed, 150 insertions(+), 90 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index d5d4cd0..984578e 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -3,6 +3,11 @@ Added selector aliasing with @selector-alias directive. Added viewport @-rule aliases. Improved minification. +Improved cross OS support. +Debug option renamed to 'minify'; debug option will still work as before but is deprecated. +Expanded trace option to take an optional array of log parameters; +log params available are stubs, selector_count and compile_time. +Added csscrush::stat method to retrieve logged parameters. Major refactoring. @@ -15,7 +20,7 @@ Added options for enabling and disabling plugins at runtime. Added property sorter plugin. Added support for SASS-like @include/@extend syntax for invoking mixins and extends. Boilerplate option now accepts a filename string as a boilerplate template. -CssCrush::string() now uses document_root as a default context for finding linked resources. +csscrush::string method now uses document_root as a default context for finding linked resources. Updated command line appication. Updated aliases and initial value files. Fixed parsing issue introduced in 1.6.1. diff --git a/cli.php b/cli.php index ee379ea..cc008a3 100755 --- a/cli.php +++ b/cli.php @@ -209,7 +209,7 @@ function stdout ( $lines, $closing_newline = true ) { $process_opts = array(); $process_opts[ 'boilerplate' ] = $boilerplate ? true : false; -$process_opts[ 'debug' ] = $pretty ? true : false; +$process_opts[ 'minify' ] = $pretty ? false : true; $process_opts[ 'rewrite_import_urls' ] = true; // Enable plugin args diff --git a/lib/Core.php b/lib/Core.php index 02b28d1..a3bcafd 100644 --- a/lib/Core.php +++ b/lib/Core.php @@ -41,8 +41,8 @@ static public function init ( $seed_file ) { // Default options. self::$config->options = (object) array( - // Minify. Set true for formatting and comments - 'debug' => false, + // Minify. Set false for formatting and comments + 'minify' => true, // Append 'checksum' to output file name 'versioning' => true, @@ -71,8 +71,9 @@ static public function init ( $seed_file ) { // List of plugins to disable (as Array of names) 'disable' => null, - // Output sass debug-info stubs that work with development tools like FireSass. - 'trace' => false, + // Debugging options. + // Set true to output sass debug-info stubs that work with development tools like FireSass. + 'trace' => array(), ); // Initialise other classes. @@ -196,11 +197,11 @@ static public function loadAssets () { # External API. /** - * Process host CSS file and return a new compiled file + * Process host CSS file and return a new compiled file. * - * @param string $file URL or System path to the host CSS file - * @param mixed $options An array of options or null - * @return string The public path to the compiled file or an empty string + * @param string $file URL or System path to the host CSS file. + * @param mixed $options An array of options or null. + * @return string The public path to the compiled file or an empty string. */ static public function file ( $file, $options = null ) { @@ -271,12 +272,12 @@ static public function file ( $file, $options = null ) { } /** - * Process host CSS file and return an HTML link tag with populated href + * Process host CSS file and return an HTML link tag with populated href. * - * @param string $file Absolute or relative path to the host CSS file - * @param mixed $options An array of options or null - * @param array $attributes An array of HTML attributes - * @return string HTML link tag or error message inside HTML comment + * @param string $file Absolute or relative path to the host CSS file. + * @param mixed $options An array of options or null. + * @param array $attributes An array of HTML attributes. + * @return string HTML link tag or error message inside HTML comment. */ static public function tag ( $file, $options = null, $attributes = array() ) { @@ -305,12 +306,12 @@ static public function tag ( $file, $options = null, $attributes = array() ) { } /** - * Process host CSS file and return CSS as text wrapped in html style tags + * Process host CSS file and return CSS as text wrapped in html style tags. * - * @param string $file Absolute or relative path to the host CSS file - * @param mixed $options An array of options or null - * @param array $attributes An array of HTML attributes, set false to return CSS text without tag - * @return string HTML link tag or error message inside HTML comment + * @param string $file Absolute or relative path to the host CSS file. + * @param mixed $options An array of options or null. + * @param array $attributes An array of HTML attributes, set false to return CSS text without tag. + * @return string HTML link tag or error message inside HTML comment. */ static public function inline ( $file, $options = null, $attributes = array() ) { @@ -349,11 +350,11 @@ static public function inline ( $file, $options = null, $attributes = array() ) } /** - * Compile a raw string of CSS string and return it + * Compile a raw string of CSS string and return it. * - * @param string $string CSS text - * @param mixed $options An array of options or null - * @return string CSS text + * @param string $string CSS text. + * @param mixed $options An array of options or null. + * @return string CSS text. */ static public function string ( $string, $options = null ) { @@ -390,9 +391,9 @@ static public function string ( $string, $options = null ) { } /** - * Add variables globally + * Add variables globally. * - * @param mixed $var Assoc array of variable names and values, a php ini filename or null + * @param mixed $var Assoc array of variable names and values, a php ini filename or null. */ static public function globalVars ( $vars ) { @@ -415,14 +416,34 @@ static public function globalVars ( $vars ) { } /** - * Clear config file and compiled files for the specified directory + * Clear config file and compiled files for the specified directory. * - * @param string $dir System path to the directory + * @param string $dir System path to the directory. */ static public function clearCache ( $dir = '' ) { return $process->ioCall( 'clearCache', $dir ); } + /** + * Get debug info. + * Depends on arguments passed to the trace option. + * + * @param string $name Name of stat to retrieve. Leave blank to retrieve all. + */ + static public function stat ( $name = null ) { + + $stat = csscrush::$process->stat; + + if ( $name && array_key_exists( $name, $stat ) ) { + return array( $name => $stat[ $name ] ); + } + + // Lose stats that are only useful internally. + unset( $stat[ 'compile_start_time' ] ); + + return $stat; + } + ############################# # Internal development. @@ -464,6 +485,33 @@ static public function logError ( $msg ) { self::log( $msg ); } + static public function runStat ( $name ) { + + $process = csscrush::$process; + + if ( ! $process->options->trace || ! in_array( $name, $process->options->trace ) ) { + return; + } + + switch ( $name ) { + + case 'selector_count': + $process->stat[ 'selector_count' ] = 0; + foreach ( $process->tokens->r as $rule ) { + $process->stat[ 'selector_count' ] += count( $rule->selectorList ); + } + break; + + case 'rule_count': + $process->stat[ 'rule_count' ] = count( $process->tokens->r ); + break; + + case 'compile_time': + $time = microtime( true ); + $process->stat[ 'compile_time' ] = $time - $process->stat[ 'compile_start_time' ]; + break; + } + } } diff --git a/lib/Importer.php b/lib/Importer.php index cc5a3ca..a99ae49 100644 --- a/lib/Importer.php +++ b/lib/Importer.php @@ -25,13 +25,14 @@ static public function hostfile () { if ( $prepend_file = csscrush_util::find( 'Prepend-local.css', 'Prepend.css' ) ) { $prepend_file_contents = file_get_contents( $prepend_file ); $process->currentFile = 'file://' . $prepend_file; + // If there's a parsing error inside the prepend file, wipe $prepend_file_contents. if ( ! self::prepareForStream( $prepend_file_contents ) ) { $prepend_file_contents = ''; } } - // Resolve main input; Is it a bare string or a file. + // Resolve main input; a string of css or a file. if ( isset( $input->string ) ) { $str .= $input->string; $process->currentFile = 'inline css'; @@ -188,7 +189,9 @@ static protected function prepareForStream ( &$str ) { $regex = csscrush_regex::$patt; $process = csscrush::$process; - $trace = $process->options->trace; + + // Convert all end-of-lines to unix style. + $str = preg_replace( '~\r\n?~', "\n", $str ); $str = preg_replace_callback( $regex->commentAndString, array( 'self', 'cb_extractCommentAndString' ), $str ); @@ -226,14 +229,20 @@ static protected function prepareForStream ( &$str ) { } // Optionally add tracing stubs. - if ( $trace ) { + if ( in_array( 'stubs', $process->options->trace ) ) { self::addTracingStubs( $str ); } // Strip unneeded whitespace. $str = csscrush_util::normalizeWhiteSpace( $str ); - // Tokenize all the URLs. + self::captureUrls( $str ); + + return true; + } + + static protected function captureUrls ( &$str ) { + $patt = '# @import\x{20}(\?s\d+\?) | @@ -251,7 +260,9 @@ static protected function prepareForStream ( &$str ) { $str = str_replace( $outer_m[1][0], $url->label, $str ); } // Match parenthesis if not a string token. - elseif ( preg_match( $regex->balancedParens, $str, $inner_m, PREG_OFFSET_CAPTURE, $outer_offset ) ) { + elseif ( + preg_match( csscrush_regex::$patt->balancedParens, $str, $inner_m, PREG_OFFSET_CAPTURE, $outer_offset ) + ) { $url = new csscrush_url(http://github.com/$inner_m[1][0]); $func_name = strtolower( $outer_m[2][0] ); $url->convertToData = 'data-uri' === $func_name; @@ -263,8 +274,6 @@ static protected function prepareForStream ( &$str ) { $offset += strlen( $outer_m[0][0] ); } } - - return true; } static protected function cb_extractCommentAndString ( $match ) { @@ -277,13 +286,10 @@ static protected function cb_extractCommentAndString ( $match ) { if ( strpos( $full_match, '/*' ) === 0 ) { - // Strip private comments - $private_comment_marker = '$'; - // Bail without storing comment if in debug mode or a private comment. if ( - strpos( $full_match, '/*' . $private_comment_marker ) === 0 || - ! $process->options->debug + $process->options->minify || + strpos( $full_match, '/*$' ) === 0 ) { return $newlines; } diff --git a/lib/Process.php b/lib/Process.php index cba6c56..a877cf4 100644 --- a/lib/Process.php +++ b/lib/Process.php @@ -22,6 +22,7 @@ public function __construct ( $options ) { $this->mixins = array(); $this->abstracts = array(); $this->errors = array(); + $this->stat = array(); $this->selectorRelationships = array(); $this->charset = null; $this->currentFile = null; @@ -109,17 +110,27 @@ public function ioCall ( $method ) { public function setOptions ( $options ) { + $config = csscrush::$config; + if ( ! is_array( $options ) ) { $options = array(); } + // Backwards compat for change option name from debug to minify. + if ( array_key_exists( 'debug', $options ) && ! array_key_exists( 'minify', $options ) ) { + $options[ 'minify' ] = ! $options[ 'debug' ]; + } + + // Resolve trace options. + if ( array_key_exists( 'trace', $options ) && ! is_array( $options[ 'trace' ] ) ) { + $options[ 'trace' ] = $options[ 'trace' ] ? array( 'stubs' ) : array(); + } + // Keeping track of global vars internally to maintain cache integrity. - $options[ '_globalVars' ] = csscrush::$config->vars; + $options[ '_globalVars' ] = $config->vars; // Populate unset options with defaults. - $options += (array) csscrush::$config->options; - - $this->options = (object) $options; + $this->options = (object) ( $options + (array) $config->options ); } @@ -242,20 +253,11 @@ protected function getBoilerplate () { } // Pretty print. - static $format_callback; - if ( ! $format_callback ) { - $format_callback = create_function( '$it', 'return ! empty($it) ? " $it" : $it;' ); - } - $boilerplate = explode( PHP_EOL, $boilerplate ); + $EOL = PHP_EOL; + $boilerplate = preg_split( '![\t ]*(\r\n?|\n)[\t ]*!S', $boilerplate ); $boilerplate = array_map( 'trim', $boilerplate ); - $boilerplate = array_map( $format_callback, $boilerplate ); - $boilerplate = implode( PHP_EOL . ' *', $boilerplate ); - $boilerplate = <<tokens->r[ $rule->label ] = $rule; // If only using extend still return a label. - return $rule->label . "\n"; + return $rule->label; } } @@ -875,39 +877,33 @@ protected function aliasAtRules () { protected function collate () { $options = $this->options; - $minify = ! $options->debug; + $minify = $options->minify; $regex = csscrush_regex::$patt; $regex_replacements = array(); + $EOL = PHP_EOL; - if ( $minify ) { + // Strip newlines added during parsing. + $regex_replacements[ '!\n+!' ] = ''; + if ( $minify ) { // Strip whitespace around colons used in @-rule arguments. $regex_replacements[ '! ?\: ?!' ] = ':'; - // Strip newlines added during parsing. - $regex_replacements[ '!\n+!' ] = ''; } else { - // Pretty printing. - $regex_replacements[ '!([{}])!' ] = "$1\n"; + $regex_replacements[ '!}!' ] = "$0$EOL$EOL"; $regex_replacements[ '!([^\s])\{!' ] = "$1 {"; - $regex_replacements[ '!([@])!' ] = "\n$1"; - - // Newlines after some tokens. - $regex_replacements[ '!(\?[rc][0-9]+\?)!' ] = "$1\n"; - - // Kill double spaces. - $regex_replacements[ '!\n+!' ] = "\n"; + $regex_replacements[ '! ?(@[^{]+\{)!' ] = "$1$EOL"; + $regex_replacements[ '! ?(@[^;]+\;)!' ] = "$1$EOL"; } - // Kill leading space. - $regex_replacements[ '!\n\s+!' ] = "\n"; - // Apply all replacements. $this->stream->pregReplaceHash( $regex_replacements )->lTrim(); // Print out rules. $this->stream->replaceHash( $this->tokens->r ); + csscrush::runStat( 'selector_count' ); + csscrush::runStat( 'rule_count' ); // Insert parens. $this->stream->replaceHash( $this->tokens->p ); @@ -920,20 +916,17 @@ protected function collate () { $this->stream->pregReplace( '! ?([>~+]) ?!S', '$1' ); } else { - // Add space after commas. - $this->stream->replace( ',', ', ' ); // Add newlines after comments. foreach ( $this->tokens->c as $token => &$comment ) { - $comment .= "\n"; + $comment .= "$EOL$EOL"; } // Insert comments and do final whitespace cleanup. $this->stream ->replaceHash( $this->tokens->c ) - ->pregReplace( '!\n{3,}!', "\n\n" ) ->trim() - ->append( "\n" ); + ->append( $EOL ); } // Insert URLs. @@ -974,12 +967,15 @@ protected function collate () { // Add @charset at top if set. if ( $this->charset ) { - $this->stream->prepend( "@charset \"$this->charset\";\n" ); + $this->stream->prepend( "@charset \"$this->charset\";$EOL" ); } } public function compile () { + // Always store start time. + $this->stat[ 'compile_start_time' ] = microtime( true ); + // Resolve active aliases and plugins. $this->filterPlugins(); $this->filterAliases(); @@ -1005,7 +1001,7 @@ public function compile () { // Pull out @fragment blocks, and invoke. $this->resolveFragments(); - // Adjust the stream so we can extract the rules cleanly. + // Adjust meta characters so we can extract the rules cleanly. $this->stream->replaceHash( array( '@' => "\n@", '}' => "}\n", @@ -1031,6 +1027,8 @@ public function compile () { // Release memory. $this->release(); + csscrush::runStat( 'compile_time' ); + return $this->stream; } diff --git a/lib/Rule.php b/lib/Rule.php index d64dd05..1e251f5 100644 --- a/lib/Rule.php +++ b/lib/Rule.php @@ -10,7 +10,7 @@ class csscrush_rule implements IteratorAggregate, Countable { public $isNested; public $label; - public $tracingStub = null; + public $tracingStub; public $properties = array(); @@ -181,8 +181,9 @@ public function __get ( $name ) { public function __toString () { - $minify = ! csscrush::$process->options->debug; + $minify = csscrush::$process->options->minify; $whitespace = $minify ? '' : ' '; + $EOL = PHP_EOL; // Tracing stubs. $tracing_stub = ''; @@ -190,13 +191,15 @@ public function __toString () { $tracing_stub =& csscrush::$process->tokens->t[ $this->tracingStub ]; } - // If there are no selectors or declarations associated with the rule return empty string + // If there are no selectors or declarations associated with the rule return empty string. if ( empty( $this->selectorList ) || ! count( $this ) ) { + // De-referencing self. + unset( csscrush::$process->tokens->r[ $this->label ] ); return ''; } - // Build the selector; uses selector __toString method - $selectors = implode( ',', $this->selectorList ); + // Build the selector; uses selector __toString method. + $selectors = implode( $minify ? ',' : ",$EOL", $this->selectorList ); // Build the block $block = array(); @@ -212,12 +215,12 @@ public function __toString () { } else { // Include pre-rule comments. - $comments = implode( "\n", $this->comments ); + $comments = implode( '', $this->comments ); if ( $tracing_stub ) { - $tracing_stub .= "\n"; + $tracing_stub .= $EOL; } - $block = implode( ";\n\t", $block ); - return "$comments\n$tracing_stub$selectors {\n\t$block;\n\t}\n"; + $block = implode( ";$EOL\t", $block ); + return "$comments$tracing_stub$selectors {{$EOL}\t$block;$EOL\t}$EOL$EOL"; } } From cd804d56e405871d9447498d8927e8629b034bc8 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 7 Nov 2012 12:54:46 +0000 Subject: [PATCH 053/421] Minify option expanded to take an array of advanced minification parameters. Added advanced minification paramater 'colors', which converts any rgb/hsl color to a hex representation, and also any color keywords that are smaller as a hex representation. --- CHANGELOG.txt | 1 + lib/Color.php | 63 +++++++++++++++++++++++++++++++++++------------ lib/Function.php | 2 +- lib/Process.php | 49 ++++++++++++++++++++++++++++++++---- lib/Regex.php | 1 + lib/Rule.php | 64 ++++++++++++++++++++++++------------------------ 6 files changed, 127 insertions(+), 53 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 984578e..bccd4b2 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -5,6 +5,7 @@ Added viewport @-rule aliases. Improved minification. Improved cross OS support. Debug option renamed to 'minify'; debug option will still work as before but is deprecated. +New minify option optionally takes an array of advanced minification parameters. Expanded trace option to take an optional array of log parameters; log params available are stubs, selector_count and compile_time. Added csscrush::stat method to retrieve logged parameters. diff --git a/lib/Color.php b/lib/Color.php index fdf502d..15fa142 100644 --- a/lib/Color.php +++ b/lib/Color.php @@ -1,16 +1,20 @@ location . '/misc/color-keywords.ini'; if ( $keywords = parse_ini_file( $path ) ) { foreach ( $keywords as $word => $rgb ) { @@ -21,11 +25,41 @@ static public function getKeywords () { } return self::$keywords; } - + + static public function &loadMinifyableKeywords () { + + if ( is_null( self::$minifyableKeywords ) ) { + + // If color name is longer than 4 and less than 8 test to see if its hex + // representation could be shortened. + $table = array(); + $keywords =& csscrush_color::loadKeywords(); + + foreach ( $keywords as $name => &$rgb ) { + $name_len = strlen( $name ); + if ( $name_len < 5 ) { + continue; + } + + $hex = self::rgbToHex( $rgb ); + + if ( $name_len > 7 ) { + self::$minifyableKeywords[ $name ] = $hex; + } + else { + if ( preg_match( csscrush_regex::$patt->cruftyHex, $hex ) ) { + self::$minifyableKeywords[ $name ] = $hex; + } + } + } + } + return self::$minifyableKeywords; + } + /** * http://mjijackson.com/2008/02/ * rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript - * + * * Converts an RGB color value to HSL. Conversion formula * adapted from http://en.wikipedia.org/wiki/HSL_color_space. * Assumes r, g, and b are contained in the set [0, 255] and @@ -120,10 +154,10 @@ static public function cssHslToRgb ( array $hsl ) { $val /= 100; } list( $s, $l ) = $hsl; - + $hsl = array( $h, $s, $l ); $rgb = self::hslToRgb( $hsl ); - + return $rgb; } @@ -137,7 +171,7 @@ static public function hueToRgb ( $p, $q, $t ) { } static public function rgbToHex ( array $rgb ) { - $hex_out = '#'; + $hex_out = '#'; foreach ( $rgb as $val ) { $hex_out .= str_pad( dechex( $val ), 2, '0', STR_PAD_LEFT ); } @@ -146,7 +180,7 @@ static public function rgbToHex ( array $rgb ) { static public function hexToRgb ( $hex ) { $hex = substr( $hex, 1 ); - + // Handle shortened format if ( strlen( $hex ) === 3 ) { $long_hex = array(); @@ -162,4 +196,3 @@ static public function hexToRgb ( $hex ) { } } - diff --git a/lib/Function.php b/lib/Function.php index 060405b..fbc31e6 100644 --- a/lib/Function.php +++ b/lib/Function.php @@ -120,7 +120,7 @@ static public function parseArgsSimple ( $input ) { static protected function colorAdjust ( $color, array $adjustments ) { $fn_matched = preg_match( '!^(#|rgba?|hsla?)!', $color, $m ); - $keywords = csscrush_color::getKeywords(); + $keywords =& csscrush_color::loadKeywords(); // Support for Hex, RGB, RGBa and keywords // HSL and HSLa are passed over diff --git a/lib/Process.php b/lib/Process.php index a877cf4..44f790d 100644 --- a/lib/Process.php +++ b/lib/Process.php @@ -696,7 +696,7 @@ static public function cb_extractRules ( $m ) { $rule = new csscrush_rule( $rule->selector_raw, $rule->declaration_raw ); // Store rules if they have declarations or extend arguments. - if ( count( $rule ) || $rule->extendArgs ) { + if ( ! empty( $rule->_declarations ) || $rule->extendArgs ) { csscrush::$process->tokens->r[ $rule->label ] = $rule; @@ -740,9 +740,6 @@ protected function prefixSelectors () { // Get the rule instance. $rule = csscrush_rule::get( $rule_match[0][0] ); - // Set the isNested flag. - $rule->isNested = true; - // Using arguments create new selector list for the rule. $new_selector_list = array(); @@ -908,6 +905,13 @@ protected function collate () { // Insert parens. $this->stream->replaceHash( $this->tokens->p ); + // Advanced minification parameters. + if ( is_array( $minify ) ) { + if ( in_array( 'colors', $minify ) ) { + $this->minifyColors(); + } + } + // Compress hex-codes, collapse TRBL lists etc. $this->decruft(); @@ -1057,8 +1061,43 @@ protected function decruft () { '!(\: *)0 0 (-?(?:\d+)?\.?\d+[a-z]{1,4}) 0 *([;}])!iS' => '${1}0 0 $2$3', // Compress hex codes. - '!\#([0-9a-f])\1([0-9a-f])\2([0-9a-f])\3!iS' => '#$1$2$3', + csscrush_regex::$patt->cruftyHex => '#$1$2$3', )); } + + ############################# + # Advanced minification. + + protected function minifyColors () { + + static $keywords_patt; + if ( ! $keywords_patt ) { + $keywords =& csscrush_color::loadMinifyableKeywords(); + $keywords_patt = '~(?stream->pregReplaceCallback( $keywords_patt, $keywords_callback ); + + static $functions_callback; + if ( ! $functions_callback ) { + $functions_callback = create_function( '$m', ' + $args = csscrush_function::parseArgs( trim( $m[2] ) ); + if ( stripos( $m[1], \'hsl\' ) === 0 ) { + $args = csscrush_color::cssHslToRgb( $args ); + } + return csscrush_color::rgbToHex( $args ); + '); + } + + $this->stream->pregReplaceCallback( + '~(?argListSplit = '!\s*[,\s]\s*!S'; $patt->mathBlacklist = '![^\.0-9\*\/\+\-\(\)]!S'; $patt->charset = '!@charset\s+(\?s\d+\?)\s*;!iS'; + $patt->cruftyHex = '!\#([0-9a-f])\1([0-9a-f])\2([0-9a-f])\3!iS'; } static public function create ( $pattern_template, $flags = '', $delim = '!' ) { diff --git a/lib/Rule.php b/lib/Rule.php index 1e251f5..f992ca4 100644 --- a/lib/Rule.php +++ b/lib/Rule.php @@ -7,9 +7,7 @@ class csscrush_rule implements IteratorAggregate, Countable { public $vendorContext; - public $isNested; public $label; - public $tracingStub; public $properties = array(); @@ -181,45 +179,34 @@ public function __get ( $name ) { public function __toString () { - $minify = csscrush::$process->options->minify; - $whitespace = $minify ? '' : ' '; - $EOL = PHP_EOL; - - // Tracing stubs. - $tracing_stub = ''; - if ( $this->tracingStub ) { - $tracing_stub =& csscrush::$process->tokens->t[ $this->tracingStub ]; - } - // If there are no selectors or declarations associated with the rule return empty string. - if ( empty( $this->selectorList ) || ! count( $this ) ) { + if ( empty( $this->selectorList ) || empty( $this->_declarations ) ) { // De-referencing self. unset( csscrush::$process->tokens->r[ $this->label ] ); return ''; } - // Build the selector; uses selector __toString method. - $selectors = implode( $minify ? ',' : ",$EOL", $this->selectorList ); - - // Build the block - $block = array(); - foreach ( $this as $declaration ) { - $important = $declaration->important ? "$whitespace!important" : ''; - $block[] = "$declaration->property:{$whitespace}$declaration->value{$important}"; + // Tracing stubs. + $tracing_stub = ''; + if ( $this->tracingStub ) { + $tracing_stub =& csscrush::$process->tokens->t[ $this->tracingStub ]; } - // Return whole rule - if ( $minify ) { - $block = implode( ';', $block ); + // Concat and return. + if ( csscrush::$process->options->minify ) { + $selectors = implode( ',', $this->selectorList ); + $block = implode( ';', $this->_declarations ); return "$tracing_stub$selectors{{$block}}"; } else { - // Include pre-rule comments. - $comments = implode( '', $this->comments ); + $EOL = PHP_EOL; if ( $tracing_stub ) { $tracing_stub .= $EOL; } - $block = implode( ";$EOL\t", $block ); + // Include pre-rule comments. + $comments = implode( '', $this->comments ); + $selectors = implode( ",$EOL", $this->selectorList ); + $block = implode( ";$EOL\t", $this->_declarations ); return "$comments$tracing_stub$selectors {{$EOL}\t$block;$EOL\t}$EOL$EOL"; } } @@ -833,6 +820,19 @@ public function __construct ( $prop, $value, $contextIndex = 0 ) { $this->important = $important; } + public function __toString () { + + if ( csscrush::$process->options->minify ) { + $whitespace = ''; + } + else { + $whitespace = ' '; + } + $important = $this->important ? "$whitespace!important" : ''; + + return "$this->property:$whitespace$this->value$important"; + } + public function getFullValue () { return csscrush::$process->restoreTokens( $this->value, 'p' ); @@ -854,23 +854,23 @@ class csscrush_selector { static function makeReadableSelector ( $selector_string ) { - // Quick test for paren tokens + // Quick test for paren tokens. if ( strpos( $selector_string, '?p' ) !== false ) { $selector_string = csscrush::$process->restoreTokens( $selector_string, 'p' ); } - // Create space around combinators, then normalize whitespace + // Create space around combinators, then normalize whitespace. $selector_string = preg_replace( '#([>+]|~(?!=))#', ' $1 ', $selector_string ); $selector_string = csscrush_util::normalizeWhiteSpace( $selector_string ); - // Quick test for string tokens + // Quick test for string tokens. if ( strpos( $selector_string, '?s' ) !== false ) { $selector_string = csscrush::$process->restoreTokens( $selector_string, 's' ); } - // Quick test for double-colons for backwards compat + // Quick test for double-colons for backwards compat. if ( strpos( $selector_string, '::' ) !== false ) { - $selector_string = preg_replace( '!::(after|before|first-(?:letter|line))!', ':$1', $selector_string ); + $selector_string = preg_replace( '!::(after|before|first-(?:letter|line))!iS', ':$1', $selector_string ); } return $selector_string; From 48319937169a1d74af5e05faffe57462022d1b2c Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 8 Nov 2012 14:32:26 +0000 Subject: [PATCH 054/421] Untabbifying project. Github uses 8-tab spaces in code view, I use 4. Most PHP open source projects use 4-space indentation so making the jump. --- Aliases.ini | 412 ++++---- Prepend.css | 63 +- cli.php | 154 +-- composer.json | 44 +- lib/Color.php | 376 +++---- lib/Core.php | 954 +++++++++--------- lib/Function.php | 576 +++++------ lib/Hook.php | 80 +- lib/IO.php | 464 ++++----- lib/Importer.php | 736 +++++++------- lib/Mixin.php | 402 ++++---- lib/Plugin.php | 72 +- lib/Process.php | 1864 +++++++++++++++++------------------ lib/Regex.php | 214 ++-- lib/Rule.php | 1588 ++++++++++++++--------------- lib/Util.php | 880 ++++++++--------- plugins/hsl-to-hex.php | 34 +- plugins/ie-clip.php | 33 +- plugins/ie-filter.php | 66 +- plugins/ie-inline-block.php | 34 +- plugins/ie-min-height.php | 28 +- plugins/ie-opacity.php | 46 +- plugins/initial.php | 38 +- plugins/property-sorter.php | 227 +++-- plugins/rgba-fallback.php | 64 +- plugins/spiffing.php | 8 +- 26 files changed, 4727 insertions(+), 4730 deletions(-) diff --git a/Aliases.ini b/Aliases.ini index 7c139bd..c58e98c 100644 --- a/Aliases.ini +++ b/Aliases.ini @@ -13,183 +13,183 @@ [properties] - ; Animations - animation[] = -webkit-animation - animation[] = -moz-animation - animation[] = -o-animation - animation-delay[] = -webkit-animation-delay - animation-delay[] = -moz-animation-delay - animation-delay[] = -o-animation-delay - animation-direction[] = -webkit-animation-direction - animation-direction[] = -moz-animation-direction - animation-direction[] = -o-animation-direction - animation-duration[] = -webkit-animation-duration - animation-duration[] = -moz-animation-duration - animation-duration[] = -o-animation-duration - animation-fill-mode[] = -webkit-animation-fill-mode - animation-fill-mode[] = -moz-animation-fill-mode - animation-fill-mode[] = -o-animation-fill-mode - animation-iteration-count[] = -webkit-animation-iteration-count - animation-iteration-count[] = -moz-animation-iteration-count - animation-iteration-count[] = -o-animation-iteration-count - animation-name[] = -webkit-animation-name - animation-name[] = -moz-animation-name - animation-name[] = -o-animation-name - animation-play-state[] = -webkit-animation-play-state - animation-play-state[] = -moz-animation-play-state - animation-play-state[] = -o-animation-play-state - animation-timing-function[] = -webkit-animation-timing-function - animation-timing-function[] = -moz-animation-timing-function - animation-timing-function[] = -o-animation-timing-function - - ; Backface visibility - backface-visibility[] = -webkit-backface-visibility - backface-visibility[] = -moz-backface-visibility - backface-visibility[] = -ms-backface-visibility - - ; Background clip - background-clip[] = -webkit-background-clip - background-clip[] = -moz-background-clip - - ; Background origin - background-origin[] = -webkit-background-origin - background-origin[] = -moz-background-origin - - ; Background size - background-size[] = -webkit-background-size - background-size[] = -moz-background-size - - ; Border radius - border-radius[] = -webkit-border-radius - border-top-left-radius[] = -webkit-border-top-left-radius - border-top-right-radius[] = -webkit-border-top-right-radius - border-bottom-left-radius[] = -webkit-border-bottom-left-radius - border-bottom-right-radius[] = -webkit-border-bottom-right-radius - - ; Border-image - border-image[] = -webkit-border-image - border-image[] = -moz-border-image - border-image[] = -o-border-image - - ; Flexbox (old, but supported implementation) - box-align[] = -webkit-box-align - box-align[] = -moz-box-align - box-align[] = -ms-box-align - box-direction[] = -webkit-box-direction - box-direction[] = -moz-box-direction - box-direction[] = -ms-box-direction - box-flex[] = -webkit-box-flex - box-flex[] = -moz-box-flex - box-flex[] = -ms-box-flex - box-orient[] = -webkit-box-orient - box-orient[] = -moz-box-orient - box-orient[] = -ms-box-orient - box-pack[] = -webkit-box-pack - box-pack[] = -moz-box-pack - box-pack[] = -ms-box-pack - - ; Box shadow - box-shadow[] = -webkit-box-shadow - - ; Box sizing - box-sizing[] = -webkit-box-sizing - box-sizing[] = -moz-box-sizing - - ; Columns - columns[] = -webkit-columns - columns[] = -moz-columns - column-count[] = -webkit-column-count - column-count[] = -moz-column-count - column-fill[] = -webkit-column-fill - column-fill[] = -moz-column-fill - column-gap[] = -webkit-column-gap - column-gap[] = -moz-column-gap - column-rule[] = -webkit-column-rule - column-rule[] = -moz-column-rule - column-rule-style[] = -webkit-column-rule-style - column-rule-style[] = -moz-column-rule-style - column-rule-width[] = -webkit-column-rule-width - column-rule-width[] = -moz-column-rule-width - column-rule-style[] = -webkit-column-rule-style - column-rule-style[] = -moz-column-rule-style - column-rule-color[] = -webkit-column-rule-color - column-rule-color[] = -moz-column-rule-color - column-span[] = -webkit-column-span - column-span[] = -moz-column-span - column-width[] = -webkit-column-width - column-width[] = -moz-column-width - - ; Hyphens - hyphens[] = -webkit-hyphens - hyphens[] = -moz-hyphens - hyphens[] = -ms-hyphens - - ; Outline radius - outline-radius[] = -moz-outline-radius - outline-top-left-radius[] = -moz-outline-radius-topleft - outline-top-right-radius[] = -moz-outline-radius-topright - outline-bottom-left-radius[] = -moz-outline-radius-bottomleft - outline-bottom-right-radius[] = -moz-outline-radius-bottomright - - ; Perspective - perspective[] = -webkit-perspective - perspective[] = -moz-perspective - perspective[] = -ms-perspective - perspective-origin[] = -webkit-perspective-origin - perspective-origin[] = -moz-perspective-origin - perspective-origin[] = -ms-perspective-origin - - ; Tab size - tab-size[] = -moz-tab-size - tab-size[] = -o-tab-size - - ; Text align last - text-align-last[] = -moz-text-align-last - - ; Text decoration - text-decoration-color[] = -moz-text-decoration-color - text-decoration-line[] = -moz-text-decoration-line - text-decoration-style[] = -moz-text-decoration-style - - ; Text overflow (Opera mini support) - text-overflow[] = -o-text-overflow - - ; Transforms - transform[] = -webkit-transform - transform[] = -moz-transform - transform[] = -ms-transform - transform[] = -o-transform - transform-origin[] = -webkit-transform-origin - transform-origin[] = -moz-transform-origin - transform-origin[] = -ms-transform-origin - transform-origin[] = -o-transform-origin - transform-style[] = -webkit-transform-style - transform-style[] = -moz-transform-style - transform-style[] = -ms-transform-style - - ; Transitions - transition[] = -webkit-transition - transition[] = -moz-transition - transition[] = -o-transition - transition-delay[] = -webkit-transition-delay - transition-delay[] = -moz-transition-delay - transition-delay[] = -o-transition-delay - transition-duration[] = -webkit-transition-duration - transition-duration[] = -moz-transition-duration - transition-duration[] = -o-transition-duration - transition-property[] = -webkit-transition-property - transition-property[] = -moz-transition-property - transition-property[] = -o-transition-property - transition-timing-function[] = -webkit-transition-timing-function - transition-timing-function[] = -moz-transition-timing-function - transition-timing-function[] = -o-transition-timing-function - - ; User select (non standard) - user-select[] = -webkit-user-select - user-select[] = -moz-user-select - user-select[] = -ms-user-select - user-select[] = -o-user-select - user-select[] = user-select + ; Animations + animation[] = -webkit-animation + animation[] = -moz-animation + animation[] = -o-animation + animation-delay[] = -webkit-animation-delay + animation-delay[] = -moz-animation-delay + animation-delay[] = -o-animation-delay + animation-direction[] = -webkit-animation-direction + animation-direction[] = -moz-animation-direction + animation-direction[] = -o-animation-direction + animation-duration[] = -webkit-animation-duration + animation-duration[] = -moz-animation-duration + animation-duration[] = -o-animation-duration + animation-fill-mode[] = -webkit-animation-fill-mode + animation-fill-mode[] = -moz-animation-fill-mode + animation-fill-mode[] = -o-animation-fill-mode + animation-iteration-count[] = -webkit-animation-iteration-count + animation-iteration-count[] = -moz-animation-iteration-count + animation-iteration-count[] = -o-animation-iteration-count + animation-name[] = -webkit-animation-name + animation-name[] = -moz-animation-name + animation-name[] = -o-animation-name + animation-play-state[] = -webkit-animation-play-state + animation-play-state[] = -moz-animation-play-state + animation-play-state[] = -o-animation-play-state + animation-timing-function[] = -webkit-animation-timing-function + animation-timing-function[] = -moz-animation-timing-function + animation-timing-function[] = -o-animation-timing-function + + ; Backface visibility + backface-visibility[] = -webkit-backface-visibility + backface-visibility[] = -moz-backface-visibility + backface-visibility[] = -ms-backface-visibility + + ; Background clip + background-clip[] = -webkit-background-clip + background-clip[] = -moz-background-clip + + ; Background origin + background-origin[] = -webkit-background-origin + background-origin[] = -moz-background-origin + + ; Background size + background-size[] = -webkit-background-size + background-size[] = -moz-background-size + + ; Border radius + border-radius[] = -webkit-border-radius + border-top-left-radius[] = -webkit-border-top-left-radius + border-top-right-radius[] = -webkit-border-top-right-radius + border-bottom-left-radius[] = -webkit-border-bottom-left-radius + border-bottom-right-radius[] = -webkit-border-bottom-right-radius + + ; Border-image + border-image[] = -webkit-border-image + border-image[] = -moz-border-image + border-image[] = -o-border-image + + ; Flexbox (old, but supported implementation) + box-align[] = -webkit-box-align + box-align[] = -moz-box-align + box-align[] = -ms-box-align + box-direction[] = -webkit-box-direction + box-direction[] = -moz-box-direction + box-direction[] = -ms-box-direction + box-flex[] = -webkit-box-flex + box-flex[] = -moz-box-flex + box-flex[] = -ms-box-flex + box-orient[] = -webkit-box-orient + box-orient[] = -moz-box-orient + box-orient[] = -ms-box-orient + box-pack[] = -webkit-box-pack + box-pack[] = -moz-box-pack + box-pack[] = -ms-box-pack + + ; Box shadow + box-shadow[] = -webkit-box-shadow + + ; Box sizing + box-sizing[] = -webkit-box-sizing + box-sizing[] = -moz-box-sizing + + ; Columns + columns[] = -webkit-columns + columns[] = -moz-columns + column-count[] = -webkit-column-count + column-count[] = -moz-column-count + column-fill[] = -webkit-column-fill + column-fill[] = -moz-column-fill + column-gap[] = -webkit-column-gap + column-gap[] = -moz-column-gap + column-rule[] = -webkit-column-rule + column-rule[] = -moz-column-rule + column-rule-style[] = -webkit-column-rule-style + column-rule-style[] = -moz-column-rule-style + column-rule-width[] = -webkit-column-rule-width + column-rule-width[] = -moz-column-rule-width + column-rule-style[] = -webkit-column-rule-style + column-rule-style[] = -moz-column-rule-style + column-rule-color[] = -webkit-column-rule-color + column-rule-color[] = -moz-column-rule-color + column-span[] = -webkit-column-span + column-span[] = -moz-column-span + column-width[] = -webkit-column-width + column-width[] = -moz-column-width + + ; Hyphens + hyphens[] = -webkit-hyphens + hyphens[] = -moz-hyphens + hyphens[] = -ms-hyphens + + ; Outline radius + outline-radius[] = -moz-outline-radius + outline-top-left-radius[] = -moz-outline-radius-topleft + outline-top-right-radius[] = -moz-outline-radius-topright + outline-bottom-left-radius[] = -moz-outline-radius-bottomleft + outline-bottom-right-radius[] = -moz-outline-radius-bottomright + + ; Perspective + perspective[] = -webkit-perspective + perspective[] = -moz-perspective + perspective[] = -ms-perspective + perspective-origin[] = -webkit-perspective-origin + perspective-origin[] = -moz-perspective-origin + perspective-origin[] = -ms-perspective-origin + + ; Tab size + tab-size[] = -moz-tab-size + tab-size[] = -o-tab-size + + ; Text align last + text-align-last[] = -moz-text-align-last + + ; Text decoration + text-decoration-color[] = -moz-text-decoration-color + text-decoration-line[] = -moz-text-decoration-line + text-decoration-style[] = -moz-text-decoration-style + + ; Text overflow (Opera mini support) + text-overflow[] = -o-text-overflow + + ; Transforms + transform[] = -webkit-transform + transform[] = -moz-transform + transform[] = -ms-transform + transform[] = -o-transform + transform-origin[] = -webkit-transform-origin + transform-origin[] = -moz-transform-origin + transform-origin[] = -ms-transform-origin + transform-origin[] = -o-transform-origin + transform-style[] = -webkit-transform-style + transform-style[] = -moz-transform-style + transform-style[] = -ms-transform-style + + ; Transitions + transition[] = -webkit-transition + transition[] = -moz-transition + transition[] = -o-transition + transition-delay[] = -webkit-transition-delay + transition-delay[] = -moz-transition-delay + transition-delay[] = -o-transition-delay + transition-duration[] = -webkit-transition-duration + transition-duration[] = -moz-transition-duration + transition-duration[] = -o-transition-duration + transition-property[] = -webkit-transition-property + transition-property[] = -moz-transition-property + transition-property[] = -o-transition-property + transition-timing-function[] = -webkit-transition-timing-function + transition-timing-function[] = -moz-transition-timing-function + transition-timing-function[] = -o-transition-timing-function + + ; User select (non standard) + user-select[] = -webkit-user-select + user-select[] = -moz-user-select + user-select[] = -ms-user-select + user-select[] = -o-user-select + user-select[] = user-select ;---------------------------------------------------------------- @@ -197,7 +197,7 @@ [values] - ; Flexbox TBC. + ; Flexbox TBC. ;---------------------------------------------------------------- @@ -205,28 +205,28 @@ [functions] - ; Calc - calc[] = -webkit-calc - calc[] = -moz-calc + ; Calc + calc[] = -webkit-calc + calc[] = -moz-calc - ; Element - element[] = -moz-element + ; Element + element[] = -moz-element - ; Gradients - linear-gradient[] = -webkit-linear-gradient - linear-gradient[] = -moz-linear-gradient - linear-gradient[] = -o-linear-gradient - radial-gradient[] = -webkit-radial-gradient - radial-gradient[] = -moz-radial-gradient - radial-gradient[] = -o-radial-gradient + ; Gradients + linear-gradient[] = -webkit-linear-gradient + linear-gradient[] = -moz-linear-gradient + linear-gradient[] = -o-linear-gradient + radial-gradient[] = -webkit-radial-gradient + radial-gradient[] = -moz-radial-gradient + radial-gradient[] = -o-radial-gradient - ; Repeating gradients - repeating-linear-gradient[] = -webkit-repeating-linear-gradient - repeating-linear-gradient[] = -moz-repeating-linear-gradient - repeating-linear-gradient[] = -o-repeating-linear-gradient - repeating-radial-gradient[] = -webkit-repeating-radial-gradient - repeating-radial-gradient[] = -moz-repeating-radial-gradient - repeating-radial-gradient[] = -o-repeating-radial-gradient + ; Repeating gradients + repeating-linear-gradient[] = -webkit-repeating-linear-gradient + repeating-linear-gradient[] = -moz-repeating-linear-gradient + repeating-linear-gradient[] = -o-repeating-linear-gradient + repeating-radial-gradient[] = -webkit-repeating-radial-gradient + repeating-radial-gradient[] = -moz-repeating-radial-gradient + repeating-radial-gradient[] = -o-repeating-radial-gradient ;---------------------------------------------------------------- @@ -234,14 +234,14 @@ [at-rules] - ; Keyframes - keyframes[] = -webkit-keyframes - keyframes[] = -moz-keyframes - keyframes[] = -o-keyframes + ; Keyframes + keyframes[] = -webkit-keyframes + keyframes[] = -moz-keyframes + keyframes[] = -o-keyframes - ; Viewport - viewport[] = -webkit-viewport - viewport[] = -moz-viewport - viewport[] = -ms-viewport - viewport[] = -o-viewport + ; Viewport + viewport[] = -webkit-viewport + viewport[] = -moz-viewport + viewport[] = -ms-viewport + viewport[] = -o-viewport diff --git a/Prepend.css b/Prepend.css index de77113..a5c7514 100644 --- a/Prepend.css +++ b/Prepend.css @@ -3,39 +3,38 @@ Prepend.css contains library variables by default, but it could also contain reset styles such as reset.css or normalize.css that you would want prepended to every output file. */ - @define { - /* Font stacks - - Sources: - http://cssfontstack.com - http://www.codestyle.org - ---------------------------------------- */ - - /* Serif */ - baskerville: Baskerville, "Baskerville Old Face", "Hoefler Text", Garamond, "Times New Roman", serif; - georgia: Georgia, Times, "Times New Roman", serif; - palatino: Palatino, "Palatino Linotype", "Palatino LT STD", "Book Antiqua", Georgia, serif; - times: TimesNewRoman, "Times New Roman", Times, serif; - - /* Sans-serif */ - arial: Arial, Helvetica, sans-serif; - arial-narrow: "Arial Narrow", Arial, sans-serif; - gill-sans: "Gill Sans", "Gill Sans MT", Calibri, sans-serif; - helvetica: "Helvetica Neue", Helvetica, Arial, sans-serif; - lucida: "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Verdana, sans-serif; - trebuchet-ms: "Trebuchet MS", "Lucida Sans Unicode", "Lucida Grande", "Lucida Sans", Arial, sans-serif; - verdana: Verdana, Geneva, sans-serif; - - /* Monospace */ - consolas: Consolas, Monaco, monospace; - courier: "Courier New", Courier, monospace; - monaco: Monaco, Consolas, "Lucida Console", monospace; - - /* Generic defaults */ - serif: $( times ); - sans-serif: $( arial ); - monospace: $( courier ); + /* Font stacks + + Sources: + http://cssfontstack.com + http://www.codestyle.org + ---------------------------------------- */ + + /* Serif */ + baskerville: Baskerville, "Baskerville Old Face", "Hoefler Text", Garamond, "Times New Roman", serif; + georgia: Georgia, Times, "Times New Roman", serif; + palatino: Palatino, "Palatino Linotype", "Palatino LT STD", "Book Antiqua", Georgia, serif; + times: TimesNewRoman, "Times New Roman", Times, serif; + + /* Sans-serif */ + arial: Arial, Helvetica, sans-serif; + arial-narrow: "Arial Narrow", Arial, sans-serif; + gill-sans: "Gill Sans", "Gill Sans MT", Calibri, sans-serif; + helvetica: "Helvetica Neue", Helvetica, Arial, sans-serif; + lucida: "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Verdana, sans-serif; + trebuchet-ms: "Trebuchet MS", "Lucida Sans Unicode", "Lucida Grande", "Lucida Sans", Arial, sans-serif; + verdana: Verdana, Geneva, sans-serif; + + /* Monospace */ + consolas: Consolas, Monaco, monospace; + courier: "Courier New", Courier, monospace; + monaco: Monaco, Consolas, "Lucida Console", monospace; + + /* Generic defaults */ + serif: $( times ); + sans-serif: $( arial ); + monospace: $( courier ); } diff --git a/cli.php b/cli.php index cc008a3..62321e0 100755 --- a/cli.php +++ b/cli.php @@ -20,8 +20,8 @@ // Get stdin contents if ( ! stream_set_blocking( $stdin, false ) ) { - stderr( 'Failed to disable stdin blocking' ); - exit( STATUS_ERROR ); + stderr( 'Failed to disable stdin blocking' ); + exit( STATUS_ERROR ); } $stdin_contents = stream_get_contents( $stdin ); fclose( $stdin ); @@ -31,17 +31,17 @@ ## Helpers function stderr ( $lines, $closing_newline = true ) { - global $stderr; - fwrite( $stderr, - implode( PHP_EOL, (array) $lines ) . ( $closing_newline ? PHP_EOL : '' ) - ); + global $stderr; + fwrite( $stderr, + implode( PHP_EOL, (array) $lines ) . ( $closing_newline ? PHP_EOL : '' ) + ); } function stdout ( $lines, $closing_newline = true ) { - global $stdout; - fwrite( $stdout, - implode( PHP_EOL, (array) $lines ) . ( $closing_newline ? PHP_EOL : '' ) - ); + global $stdout; + fwrite( $stdout, + implode( PHP_EOL, (array) $lines ) . ( $closing_newline ? PHP_EOL : '' ) + ); } @@ -53,11 +53,11 @@ function stdout ( $lines, $closing_newline = true ) { if ( $version < $required_version ) { - stderr( array( - "PHP version $required_version or higher is required to use this tool.", - "You are currently running PHP version $version" ) - ); - exit( STATUS_ERROR ); + stderr( array( + "PHP version $required_version or higher is required to use this tool.", + "You are currently running PHP version $version" ) + ); + exit( STATUS_ERROR ); } @@ -65,26 +65,26 @@ function stdout ( $lines, $closing_newline = true ) { ## Options $short_opts = array( - "f:", // Input file. Defaults to sdtin - "o:", // Output file. Defaults to stdout - "p", // Pretty formatting - 'b', // Output boilerplate - 'h', // Display help + "f:", // Input file. Defaults to sdtin + "o:", // Output file. Defaults to stdout + "p", // Pretty formatting + 'b', // Output boilerplate + 'h', // Display help ); $long_opts = array( - 'file:', // Input file. Defaults to sdtin - 'output:', // Output file. Defaults to stdout - 'pretty', // Pretty formatting - 'boilerplate', // Output boilerplate - 'help', // Display help - 'version', // Display version - 'trace', // Output sass tracing stubs - 'vendor-target:', // Vendor target - 'variables:', // Map of variable names in an http query string format - 'enable:', // List of plugins to enable - 'disable:', // List of plugins to disable - 'context:', // Context for resolving URLs + 'file:', // Input file. Defaults to sdtin + 'output:', // Output file. Defaults to stdout + 'pretty', // Pretty formatting + 'boilerplate', // Output boilerplate + 'help', // Display help + 'version', // Display version + 'trace', // Output sass tracing stubs + 'vendor-target:', // Vendor target + 'variables:', // Map of variable names in an http query string format + 'enable:', // List of plugins to enable + 'disable:', // List of plugins to disable + 'context:', // Context for resolving URLs ); $opts = getopt( implode( $short_opts ), $long_opts ); @@ -168,14 +168,14 @@ function stdout ( $lines, $closing_newline = true ) { if ( $version_flag ) { - stdout( 'CSS Crush ' . csscrush::$config->version ); - exit( STATUS_OK ); + stdout( 'CSS Crush ' . csscrush::$config->version ); + exit( STATUS_OK ); } if ( $help_flag ) { - stdout( $help ); - exit( STATUS_OK ); + stdout( $help ); + exit( STATUS_OK ); } @@ -186,21 +186,21 @@ function stdout ( $lines, $closing_newline = true ) { if ( $input_file ) { - if ( ! file_exists( $input_file ) ) { - stdout( 'Input file not found' . PHP_EOL ); - exit( STATUS_ERROR ); - } - $input = file_get_contents( $input_file ); + if ( ! file_exists( $input_file ) ) { + stdout( 'Input file not found' . PHP_EOL ); + exit( STATUS_ERROR ); + } + $input = file_get_contents( $input_file ); } elseif ( $stdin_contents ) { - $input = $stdin_contents; + $input = $stdin_contents; } else { - // No input, just output help screen - stdout( $help ); - exit( STATUS_OK ); + // No input, just output help screen + stdout( $help ); + exit( STATUS_OK ); } @@ -214,48 +214,48 @@ function stdout ( $lines, $closing_newline = true ) { // Enable plugin args if ( $enable_plugins ) { - foreach ( $enable_plugins as $arg ) { - foreach ( preg_split( '!\s*,\s*!', $arg ) as $plugin ) { - $process_opts[ 'enable' ][] = $plugin; - } - } + foreach ( $enable_plugins as $arg ) { + foreach ( preg_split( '!\s*,\s*!', $arg ) as $plugin ) { + $process_opts[ 'enable' ][] = $plugin; + } + } } // Disable plugin args if ( $disable_plugins ) { - foreach ( $disable_plugins as $arg ) { - foreach ( preg_split( '!\s*,\s*!', $arg ) as $plugin ) { - $process_opts[ 'disable' ][] = $plugin; - } - } + foreach ( $disable_plugins as $arg ) { + foreach ( preg_split( '!\s*,\s*!', $arg ) as $plugin ) { + $process_opts[ 'disable' ][] = $plugin; + } + } } // Tracing if ( $trace_flag ) { - $process_opts[ 'trace' ] = true; + $process_opts[ 'trace' ] = true; } // Vendor target args if ( $vendor_target ) { - $process_opts[ 'vendor_target' ] = $vendor_target; + $process_opts[ 'vendor_target' ] = $vendor_target; } // Variables args if ( $variables ) { - parse_str( $variables, $in_vars ); - $process_opts[ 'vars' ] = $in_vars; + parse_str( $variables, $in_vars ); + $process_opts[ 'vars' ] = $in_vars; } // Resolve a context for URLs if ( ! $context ) { - $context = $input_file ? dirname( realpath( $input_file ) ) : null; + $context = $input_file ? dirname( realpath( $input_file ) ) : null; } // If there is an import context set it to the document root if ( $context ) { - $old_doc_root = csscrush::$config->docRoot; - csscrush::$config->docRoot = $context; - $process_opts[ 'context' ] = $context; + $old_doc_root = csscrush::$config->docRoot; + csscrush::$config->docRoot = $context; + $process_opts[ 'context' ] = $context; } // Process the stream @@ -263,7 +263,7 @@ function stdout ( $lines, $closing_newline = true ) { // Reset the document root after processing if ( $context ) { - csscrush::$config->docRoot = $old_doc_root; + csscrush::$config->docRoot = $old_doc_root; } @@ -272,24 +272,24 @@ function stdout ( $lines, $closing_newline = true ) { if ( $output_file ) { - if ( ! @file_put_contents( $output_file, $output ) ) { + if ( ! @file_put_contents( $output_file, $output ) ) { - $message[] = "Could not write to path '$output_file'"; + $message[] = "Could not write to path '$output_file'"; - if ( strpos( $output_file, '~' ) === 0 ) { - $message[] = 'Tilde expansion does not work here'; - } + if ( strpos( $output_file, '~' ) === 0 ) { + $message[] = 'Tilde expansion does not work here'; + } - stderr( $message ); - exit( STATUS_ERROR ); - } + stderr( $message ); + exit( STATUS_ERROR ); + } } else { - if ( csscrush::$process->errors ) { - stderr( csscrush::$process->errors ); - } + if ( csscrush::$process->errors ) { + stderr( csscrush::$process->errors ); + } - stdout( $output ); - exit( STATUS_OK ); + stdout( $output ); + exit( STATUS_OK ); } diff --git a/composer.json b/composer.json index d8ca281..df29807 100644 --- a/composer.json +++ b/composer.json @@ -1,24 +1,24 @@ { - "name": "css-crush/css-crush", - "type": "library", - "description": "CSS preprocessor", - "keywords": ["css", "preprocessor"], - "homepage": "http://the-echoplex.net/csscrush", - "license": "MIT", - "authors": [ - { - "name": "Pete Boere", - "email": "pete@the-echoplex.net" - }, - { - "name": "GitHub contributors", - "homepage": "https://github.com/peteboere/css-crush/contributors" - } - ], - "require": { - "php": ">=5.2.4" - }, - "autoload": { - "files": ["CssCrush.php"] - } + "name": "css-crush/css-crush", + "type": "library", + "description": "CSS preprocessor", + "keywords": ["css", "preprocessor"], + "homepage": "http://the-echoplex.net/csscrush", + "license": "MIT", + "authors": [ + { + "name": "Pete Boere", + "email": "pete@the-echoplex.net" + }, + { + "name": "GitHub contributors", + "homepage": "https://github.com/peteboere/css-crush/contributors" + } + ], + "require": { + "php": ">=5.2.4" + }, + "autoload": { + "files": ["CssCrush.php"] + } } \ No newline at end of file diff --git a/lib/Color.php b/lib/Color.php index 15fa142..8b59c12 100644 --- a/lib/Color.php +++ b/lib/Color.php @@ -6,193 +6,193 @@ */ class csscrush_color { - // Cached color keyword tables. - static public $keywords; - static public $minifyableKeywords; - - static public function &loadKeywords () { - - if ( is_null( self::$keywords ) ) { - - $table = array(); - $path = csscrush::$config->location . '/misc/color-keywords.ini'; - if ( $keywords = parse_ini_file( $path ) ) { - foreach ( $keywords as $word => $rgb ) { - $rgb = array_map( 'intval', explode( ',', $rgb ) ); - self::$keywords[ $word ] = $rgb; - } - } - } - return self::$keywords; - } - - static public function &loadMinifyableKeywords () { - - if ( is_null( self::$minifyableKeywords ) ) { - - // If color name is longer than 4 and less than 8 test to see if its hex - // representation could be shortened. - $table = array(); - $keywords =& csscrush_color::loadKeywords(); - - foreach ( $keywords as $name => &$rgb ) { - $name_len = strlen( $name ); - if ( $name_len < 5 ) { - continue; - } - - $hex = self::rgbToHex( $rgb ); - - if ( $name_len > 7 ) { - self::$minifyableKeywords[ $name ] = $hex; - } - else { - if ( preg_match( csscrush_regex::$patt->cruftyHex, $hex ) ) { - self::$minifyableKeywords[ $name ] = $hex; - } - } - } - } - return self::$minifyableKeywords; - } - - /** - * http://mjijackson.com/2008/02/ - * rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript - * - * Converts an RGB color value to HSL. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes r, g, and b are contained in the set [0, 255] and - * returns h, s, and l in the set [0, 1]. - */ - static public function rgbToHsl ( array $rgb ) { - - list( $r, $g, $b ) = $rgb; - $r /= 255; - $g /= 255; - $b /= 255; - $max = max( $r, $g, $b ); - $min = min( $r, $g, $b ); - $h; - $s; - $l = ( $max + $min ) / 2; - - if ( $max == $min ) { - $h = $s = 0; - } - else { - $d = $max - $min; - $s = $l > 0.5 ? $d / ( 2 - $max - $min ) : $d / ( $max + $min ); - switch( $max ) { - case $r: - $h = ( $g - $b ) / $d + ( $g < $b ? 6 : 0 ); - break; - case $g: - $h = ( $b - $r ) / $d + 2; - break; - case $b: - $h = ( $r - $g ) / $d + 4; - break; - } - $h /= 6; - } - - return array( $h, $s, $l ); - } - - /** - * http://mjijackson.com/2008/02/ - * rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript - * - * Converts an HSL color value to RGB. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes h, s, and l are contained in the set [0, 1] and - * returns r, g, and b in the set [0, 255]. - */ - static public function hslToRgb ( array $hsl ) { - list( $h, $s, $l ) = $hsl; - $r; - $g; - $b; - if ( $s == 0 ) { - $r = $g = $b = $l; - } - else { - $q = $l < 0.5 ? $l * ( 1 + $s ) : $l + $s - $l * $s; - $p = 2 * $l - $q; - $r = self::hueToRgb( $p, $q, $h + 1 / 3 ); - $g = self::hueToRgb( $p, $q, $h ); - $b = self::hueToRgb( $p, $q, $h - 1 / 3 ); - } - return array( round( $r * 255 ), round( $g * 255 ), round( $b * 255 ) ); - } - - // Convert percentages to points (0-255) - static public function normalizeCssRgb ( array $rgb ) { - foreach ( $rgb as &$val ) { - if ( strpos( $val, '%' ) !== false ) { - $val = str_replace( '%', '', $val ); - $val = round( $val * 2.55 ); - } - } - return $rgb; - } - - static public function cssHslToRgb ( array $hsl ) { - - // Normalize the hue degree value then convert to float - $h = array_shift( $hsl ); - $h = $h % 360; - if ( $h < 0 ) { - $h = 360 + $h; - } - $h = $h / 360; - - // Convert s and l to floats - foreach ( $hsl as &$val ) { - $val = str_replace( '%', '', $val ); - $val /= 100; - } - list( $s, $l ) = $hsl; - - $hsl = array( $h, $s, $l ); - $rgb = self::hslToRgb( $hsl ); - - return $rgb; - } - - static public function hueToRgb ( $p, $q, $t ) { - if ( $t < 0 ) $t += 1; - if ( $t > 1 ) $t -= 1; - if ( $t < 1/6 ) return $p + ( $q - $p ) * 6 * $t; - if ( $t < 1/2 ) return $q; - if ( $t < 2/3 ) return $p + ( $q - $p ) * ( 2 / 3 - $t ) * 6; - return $p; - } - - static public function rgbToHex ( array $rgb ) { - $hex_out = '#'; - foreach ( $rgb as $val ) { - $hex_out .= str_pad( dechex( $val ), 2, '0', STR_PAD_LEFT ); - } - return $hex_out; - } - - static public function hexToRgb ( $hex ) { - $hex = substr( $hex, 1 ); - - // Handle shortened format - if ( strlen( $hex ) === 3 ) { - $long_hex = array(); - foreach ( str_split( $hex ) as $val ) { - $long_hex[] = $val . $val; - } - $hex = $long_hex; - } - else { - $hex = str_split( $hex, 2 ); - } - return array_map( 'hexdec', $hex ); - } + // Cached color keyword tables. + static public $keywords; + static public $minifyableKeywords; + + static public function &loadKeywords () { + + if ( is_null( self::$keywords ) ) { + + $table = array(); + $path = csscrush::$config->location . '/misc/color-keywords.ini'; + if ( $keywords = parse_ini_file( $path ) ) { + foreach ( $keywords as $word => $rgb ) { + $rgb = array_map( 'intval', explode( ',', $rgb ) ); + self::$keywords[ $word ] = $rgb; + } + } + } + return self::$keywords; + } + + static public function &loadMinifyableKeywords () { + + if ( is_null( self::$minifyableKeywords ) ) { + + // If color name is longer than 4 and less than 8 test to see if its hex + // representation could be shortened. + $table = array(); + $keywords =& csscrush_color::loadKeywords(); + + foreach ( $keywords as $name => &$rgb ) { + $name_len = strlen( $name ); + if ( $name_len < 5 ) { + continue; + } + + $hex = self::rgbToHex( $rgb ); + + if ( $name_len > 7 ) { + self::$minifyableKeywords[ $name ] = $hex; + } + else { + if ( preg_match( csscrush_regex::$patt->cruftyHex, $hex ) ) { + self::$minifyableKeywords[ $name ] = $hex; + } + } + } + } + return self::$minifyableKeywords; + } + + /** + * http://mjijackson.com/2008/02/ + * rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript + * + * Converts an RGB color value to HSL. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSL_color_space. + * Assumes r, g, and b are contained in the set [0, 255] and + * returns h, s, and l in the set [0, 1]. + */ + static public function rgbToHsl ( array $rgb ) { + + list( $r, $g, $b ) = $rgb; + $r /= 255; + $g /= 255; + $b /= 255; + $max = max( $r, $g, $b ); + $min = min( $r, $g, $b ); + $h; + $s; + $l = ( $max + $min ) / 2; + + if ( $max == $min ) { + $h = $s = 0; + } + else { + $d = $max - $min; + $s = $l > 0.5 ? $d / ( 2 - $max - $min ) : $d / ( $max + $min ); + switch( $max ) { + case $r: + $h = ( $g - $b ) / $d + ( $g < $b ? 6 : 0 ); + break; + case $g: + $h = ( $b - $r ) / $d + 2; + break; + case $b: + $h = ( $r - $g ) / $d + 4; + break; + } + $h /= 6; + } + + return array( $h, $s, $l ); + } + + /** + * http://mjijackson.com/2008/02/ + * rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript + * + * Converts an HSL color value to RGB. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSL_color_space. + * Assumes h, s, and l are contained in the set [0, 1] and + * returns r, g, and b in the set [0, 255]. + */ + static public function hslToRgb ( array $hsl ) { + list( $h, $s, $l ) = $hsl; + $r; + $g; + $b; + if ( $s == 0 ) { + $r = $g = $b = $l; + } + else { + $q = $l < 0.5 ? $l * ( 1 + $s ) : $l + $s - $l * $s; + $p = 2 * $l - $q; + $r = self::hueToRgb( $p, $q, $h + 1 / 3 ); + $g = self::hueToRgb( $p, $q, $h ); + $b = self::hueToRgb( $p, $q, $h - 1 / 3 ); + } + return array( round( $r * 255 ), round( $g * 255 ), round( $b * 255 ) ); + } + + // Convert percentages to points (0-255) + static public function normalizeCssRgb ( array $rgb ) { + foreach ( $rgb as &$val ) { + if ( strpos( $val, '%' ) !== false ) { + $val = str_replace( '%', '', $val ); + $val = round( $val * 2.55 ); + } + } + return $rgb; + } + + static public function cssHslToRgb ( array $hsl ) { + + // Normalize the hue degree value then convert to float + $h = array_shift( $hsl ); + $h = $h % 360; + if ( $h < 0 ) { + $h = 360 + $h; + } + $h = $h / 360; + + // Convert s and l to floats + foreach ( $hsl as &$val ) { + $val = str_replace( '%', '', $val ); + $val /= 100; + } + list( $s, $l ) = $hsl; + + $hsl = array( $h, $s, $l ); + $rgb = self::hslToRgb( $hsl ); + + return $rgb; + } + + static public function hueToRgb ( $p, $q, $t ) { + if ( $t < 0 ) $t += 1; + if ( $t > 1 ) $t -= 1; + if ( $t < 1/6 ) return $p + ( $q - $p ) * 6 * $t; + if ( $t < 1/2 ) return $q; + if ( $t < 2/3 ) return $p + ( $q - $p ) * ( 2 / 3 - $t ) * 6; + return $p; + } + + static public function rgbToHex ( array $rgb ) { + $hex_out = '#'; + foreach ( $rgb as $val ) { + $hex_out .= str_pad( dechex( $val ), 2, '0', STR_PAD_LEFT ); + } + return $hex_out; + } + + static public function hexToRgb ( $hex ) { + $hex = substr( $hex, 1 ); + + // Handle shortened format + if ( strlen( $hex ) === 3 ) { + $long_hex = array(); + foreach ( str_split( $hex ) as $val ) { + $long_hex[] = $val . $val; + } + $hex = $long_hex; + } + else { + $hex = str_split( $hex, 2 ); + } + return array_map( 'hexdec', $hex ); + } } diff --git a/lib/Core.php b/lib/Core.php index a3bcafd..3d29cb7 100644 --- a/lib/Core.php +++ b/lib/Core.php @@ -7,511 +7,511 @@ class csscrush { - // Global settings. - static public $config; + // Global settings. + static public $config; - // The current active process. - static public $process; + // The current active process. + static public $process; - // Init called once manually post class definition. - static public function init ( $seed_file ) { + // Init called once manually post class definition. + static public function init ( $seed_file ) { - self::$config = new stdclass(); + self::$config = new stdclass(); - // Path to this installation. - self::$config->location = dirname( $seed_file ); + // Path to this installation. + self::$config->location = dirname( $seed_file ); - // Get version ID from seed file. - $seed_file_contents = file_get_contents( $seed_file ); - $match_count = preg_match( '!@version\s+([\d\.\w-]+)!', $seed_file_contents, $version_match ); - self::$config->version = $match_count ? new csscrush_version( $version_match[1] ) : null; + // Get version ID from seed file. + $seed_file_contents = file_get_contents( $seed_file ); + $match_count = preg_match( '!@version\s+([\d\.\w-]+)!', $seed_file_contents, $version_match ); + self::$config->version = $match_count ? new csscrush_version( $version_match[1] ) : null; - // Set the docRoot reference. - self::setDocRoot(); + // Set the docRoot reference. + self::setDocRoot(); - // Set the default IO handler. - self::$config->io = 'csscrush_io'; + // Set the default IO handler. + self::$config->io = 'csscrush_io'; - // Global storage. - self::$config->vars = array(); - self::$config->aliases = array(); - self::$config->selectorAliases = array(); - self::$config->plugins = array(); + // Global storage. + self::$config->vars = array(); + self::$config->aliases = array(); + self::$config->selectorAliases = array(); + self::$config->plugins = array(); - // Default options. - self::$config->options = (object) array( + // Default options. + self::$config->options = (object) array( - // Minify. Set false for formatting and comments - 'minify' => true, + // Minify. Set false for formatting and comments + 'minify' => true, - // Append 'checksum' to output file name - 'versioning' => true, + // Append 'checksum' to output file name + 'versioning' => true, - // Use the template boilerplate - 'boilerplate' => true, + // Use the template boilerplate + 'boilerplate' => true, - // Variables passed in at runtime - 'vars' => array(), + // Variables passed in at runtime + 'vars' => array(), - // Enable/disable the cache - 'cache' => true, + // Enable/disable the cache + 'cache' => true, - // Output file. Defaults the host-filename - 'output_file' => null, + // Output file. Defaults the host-filename + 'output_file' => null, - // Vendor target. Only apply prefixes for a specific vendor, set to 'none' for no prefixes - 'vendor_target' => 'all', + // Vendor target. Only apply prefixes for a specific vendor, set to 'none' for no prefixes + 'vendor_target' => 'all', - // Whether to rewrite the url references inside imported files - 'rewrite_import_urls' => true, + // Whether to rewrite the url references inside imported files + 'rewrite_import_urls' => true, - // List of plugins to enable (as Array of names) - 'enable' => null, + // List of plugins to enable (as Array of names) + 'enable' => null, - // List of plugins to disable (as Array of names) - 'disable' => null, + // List of plugins to disable (as Array of names) + 'disable' => null, - // Debugging options. - // Set true to output sass debug-info stubs that work with development tools like FireSass. - 'trace' => array(), - ); + // Debugging options. + // Set true to output sass debug-info stubs that work with development tools like FireSass. + 'trace' => array(), + ); - // Initialise other classes. - csscrush_regex::init(); - csscrush_function::init(); - } + // Initialise other classes. + csscrush_regex::init(); + csscrush_function::init(); + } - static protected function setDocRoot ( $doc_root = null ) { + static protected function setDocRoot ( $doc_root = null ) { - // Get document_root reference - // $_SERVER['DOCUMENT_ROOT'] is unreliable in certain CGI/Apache/IIS setups + // Get document_root reference + // $_SERVER['DOCUMENT_ROOT'] is unreliable in certain CGI/Apache/IIS setups - if ( ! $doc_root ) { + if ( ! $doc_root ) { - $script_filename = $_SERVER[ 'SCRIPT_FILENAME' ]; - $script_name = $_SERVER[ 'SCRIPT_NAME' ]; + $script_filename = $_SERVER[ 'SCRIPT_FILENAME' ]; + $script_name = $_SERVER[ 'SCRIPT_NAME' ]; - if ( $script_filename && $script_name ) { + if ( $script_filename && $script_name ) { - $len_diff = strlen( $script_filename ) - strlen( $script_name ); + $len_diff = strlen( $script_filename ) - strlen( $script_name ); - // We're comparing the two strings so normalize OS directory separators - $script_filename = str_replace( '\\', '/', $script_filename ); - $script_name = str_replace( '\\', '/', $script_name ); + // We're comparing the two strings so normalize OS directory separators + $script_filename = str_replace( '\\', '/', $script_filename ); + $script_name = str_replace( '\\', '/', $script_name ); - // Check $script_filename ends with $script_name - if ( substr( $script_filename, $len_diff ) === $script_name ) { + // Check $script_filename ends with $script_name + if ( substr( $script_filename, $len_diff ) === $script_name ) { - $doc_root = realpath( substr( $script_filename, 0, $len_diff ) ); - } - } + $doc_root = realpath( substr( $script_filename, 0, $len_diff ) ); + } + } - if ( ! $doc_root ) { + if ( ! $doc_root ) { - // If doc_root is still falsy, fallback to DOCUMENT_ROOT - $doc_root = realpath( $_SERVER[ 'DOCUMENT_ROOT' ] ); - } + // If doc_root is still falsy, fallback to DOCUMENT_ROOT + $doc_root = realpath( $_SERVER[ 'DOCUMENT_ROOT' ] ); + } - if ( ! $doc_root ) { + if ( ! $doc_root ) { - // If doc_root is still falsy, log an error - $error = "Could not get a document_root reference."; - csscrush::logError( $error ); - trigger_error( __METHOD__ . ": $error\n", E_USER_NOTICE ); - } - } - - self::$config->docRoot = csscrush_util::normalizePath( $doc_root ); - } - - // Aliases and macros loader. - static public function loadAssets () { - - static $called; - if ( $called ) { - return; - } - - // Find an aliases file in the root directory - // a local file overrides the default - $aliases_file = csscrush_util::find( 'Aliases-local.ini', 'Aliases.ini' ); - - // Load aliases file if it exists - if ( $aliases_file ) { - - if ( $result = @parse_ini_file( $aliases_file, true ) ) { - - self::$config->aliases = $result; - - // Value aliases require a little preprocessing - if ( isset( self::$config->aliases[ 'values' ] ) ) { - $store = array(); - foreach ( self::$config->aliases[ 'values' ] as $prop_val => $aliases ) { - list( $prop, $value ) = array_map( 'trim', explode( ':', $prop_val ) ); - $store[ $prop ][ $value ] = $aliases; - } - self::$config->aliases[ 'values' ] = $store; - } - - // Ensure all alias groups are at least set (issue #34) - self::$config->aliases += array( - 'properties' => array(), - 'functions' => array(), - 'values' => array(), - 'at-rules' => array(), - ); - } - else { - trigger_error( __METHOD__ . ": Aliases file could not be parsed.\n", E_USER_NOTICE ); - } - } - else { - trigger_error( __METHOD__ . ": Aliases file not found.\n", E_USER_NOTICE ); - } - - // Find a plugins file in the root directory, - // a local file overrides the default - $plugins_file = csscrush_util::find( 'Plugins-local.ini', 'Plugins.ini' ); - - // Load plugins - if ( $plugins_file ) { - if ( $result = @parse_ini_file( $plugins_file ) ) { - foreach ( $result[ 'plugins' ] as $plugin_name ) { - // Backwards compat. - $plugin_name = basename( $plugin_name, '.php' ); - if ( csscrush_plugin::enable( $plugin_name ) ) { - self::$config->plugins[ $plugin_name ] = true; - } - } - } - else { - trigger_error( __METHOD__ . ": Plugin file could not be parsed.\n", E_USER_NOTICE ); - } - } - - $called = true; - } - - - ############################# - # External API. - - /** - * Process host CSS file and return a new compiled file. - * - * @param string $file URL or System path to the host CSS file. - * @param mixed $options An array of options or null. - * @return string The public path to the compiled file or an empty string. - */ - static public function file ( $file, $options = null ) { - - self::$process = new csscrush_process( $options ); - - $config = self::$config; - $process = self::$process; - $options = $process->options; - $doc_root = $config->docRoot; - - // Since we're comparing strings, we need to iron out OS differences. - $file = str_replace( '\\', '/', $file ); - - // Finding the system path of the input file and validating it. - $pathtest = true; - if ( strpos( $file, $doc_root ) === 0 ) { - // System path. - $pathtest = $process->setContext( dirname( $file ) ); - } - else if ( strpos( $file, '/' ) === 0 ) { - // WWW root path. - $pathtest = $process->setContext( dirname( $doc_root . $file ) ); - } - else { - // Relative path. - $pathtest = $process->setContext( dirname( dirname( __FILE__ ) . '/' . $file ) ); - } - - if ( ! $pathtest ) { - // Main directory not found or is not writable return an empty string. - return ''; - } - - // Validate file input. - if ( ! csscrush_io::registerInputFile( $file ) ) { - return ''; - } - - // Create a filename that will be used later - // Used in validateCache, and writing to filesystem - $process->output->filename = $process->ioCall( 'getOutputFileName' ); - - // Caching. - if ( $options->cache ) { - - // Load the cache data. - $process->cacheData = $process->ioCall( 'getCacheData' ); - - // If cache is enabled check for a valid compiled file. - $valid_compliled_file = $process->ioCall( 'validateExistingOutput' ); - - if ( is_string( $valid_compliled_file ) ) { - return $valid_compliled_file; - } - } - - // Compile. - $stream = $process->compile(); - - // Create file and return url. Return empty string on failure. - if ( file_put_contents( "{$process->output->dir}/{$process->output->filename}", $stream ) ) { - $timestamp = $options->versioning ? '?' . time() : ''; - return "{$process->output->dirUrl}/{$process->output->filename}$timestamp"; - } - else { - return ''; - } - } - - /** - * Process host CSS file and return an HTML link tag with populated href. - * - * @param string $file Absolute or relative path to the host CSS file. - * @param mixed $options An array of options or null. - * @param array $attributes An array of HTML attributes. - * @return string HTML link tag or error message inside HTML comment. - */ - static public function tag ( $file, $options = null, $attributes = array() ) { - - $file = self::file( $file, $options ); - - if ( ! empty( $file ) ) { - - // On success return the tag with any custom attributes - $attributes[ 'rel' ] = 'stylesheet'; - $attributes[ 'href' ] = $file; - - // Should media type be forced to 'all'? - if ( ! isset( $attributes[ 'media' ] ) ) { - $attributes[ 'media' ] = 'all'; - } - $attr_string = csscrush_util::htmlAttributes( $attributes ); - return "\n"; - } - else { - - // Return an HTML comment with message on failure - $class = __CLASS__; - $errors = implode( "\n", self::$process->errors ); - return "\n"; - } - } - - /** - * Process host CSS file and return CSS as text wrapped in html style tags. - * - * @param string $file Absolute or relative path to the host CSS file. - * @param mixed $options An array of options or null. - * @param array $attributes An array of HTML attributes, set false to return CSS text without tag. - * @return string HTML link tag or error message inside HTML comment. - */ - static public function inline ( $file, $options = null, $attributes = array() ) { - - // For inline output set boilerplate to not display by default - if ( ! is_array( $options ) ) { - $options = array(); - } - if ( ! isset( $options[ 'boilerplate' ] ) ) { - $options[ 'boilerplate' ] = false; - } - - $file = self::file( $file, $options ); - - if ( ! empty( $file ) ) { - - // On success fetch the CSS text - $content = file_get_contents( self::$process->output->dir . '/' - . self::$process->output->filename ); - $tag_open = ''; - $tag_close = ''; - - if ( is_array( $attributes ) ) { - $attr_string = csscrush_util::htmlAttributes( $attributes ); - $tag_open = ""; - $tag_close = ''; - } - return "$tag_open{$content}$tag_close\n"; - } - else { - - // Return an HTML comment with message on failure - $class = __CLASS__; - $errors = implode( "\n", self::$process->errors ); - return "\n"; - } - } - - /** - * Compile a raw string of CSS string and return it. - * - * @param string $string CSS text. - * @param mixed $options An array of options or null. - * @return string CSS text. - */ - static public function string ( $string, $options = null ) { - - // For strings set boilerplate to not display by default - if ( ! isset( $options[ 'boilerplate' ] ) ) { - $options[ 'boilerplate' ] = false; - } - - self::$process = new csscrush_process( $options ); - - $config = self::$config; - $process = self::$process; - $options = $process->options; - - // Set the path context if one is given. - // Fallback to document root. - if ( ! empty( $options->context ) ) { - $process->setContext( $options->context, false ); - } - else { - $process->setContext( $config->docRoot, false ); - } - - // Set the string on the input object. - $process->input->string = $string; - - // Import files may be ignored - if ( isset( $options->no_import ) ) { - $process->input->importIgnore = true; - } - - // Compile and return. - return $process->compile(); - } - - /** - * Add variables globally. - * - * @param mixed $var Assoc array of variable names and values, a php ini filename or null. - */ - static public function globalVars ( $vars ) { - - $config = self::$config; - - // Merge into the stack, overrides existing variables of the same name - if ( is_array( $vars ) ) { - $config->vars = array_merge( $config->vars, $vars ); - } - // Test for a file. If it is attempt to parse it - elseif ( is_string( $vars ) && file_exists( $vars ) ) { - if ( $result = @parse_ini_file( $vars ) ) { - $config->vars = array_merge( $config->vars, $result ); - } - } - // Clear the stack if the argument is explicitly null - elseif ( is_null( $vars ) ) { - $config->vars = array(); - } - } - - /** - * Clear config file and compiled files for the specified directory. - * - * @param string $dir System path to the directory. - */ - static public function clearCache ( $dir = '' ) { - return $process->ioCall( 'clearCache', $dir ); - } - - /** - * Get debug info. - * Depends on arguments passed to the trace option. - * - * @param string $name Name of stat to retrieve. Leave blank to retrieve all. - */ - static public function stat ( $name = null ) { - - $stat = csscrush::$process->stat; - - if ( $name && array_key_exists( $name, $stat ) ) { - return array( $name => $stat[ $name ] ); - } - - // Lose stats that are only useful internally. - unset( $stat[ 'compile_start_time' ] ); - - return $stat; - } - - - ############################# - # Internal development. - - static public $logging = false; - - static public function log ( $arg = null, $label = '' ) { - - if ( ! self::$logging ) { - return; - } - static $log = ''; - - $args = func_get_args(); - if ( ! count( $args ) ) { - // No arguments, return the log - return $log; - } - - if ( $label ) { - $log .= "

$label

"; - } - - if ( is_string( $arg ) ) { - $log .= $arg . '
'; - } - else { - $out = '
';
-			ob_start();
-			print_r( $arg );
-			$out .= ob_get_clean();
-			$out .= '
'; - $log .= $out . '
'; - } - } - - static public function logError ( $msg ) { - self::$process->errors[] = $msg; - self::log( $msg ); - } - - static public function runStat ( $name ) { - - $process = csscrush::$process; - - if ( ! $process->options->trace || ! in_array( $name, $process->options->trace ) ) { - return; - } - - switch ( $name ) { - - case 'selector_count': - $process->stat[ 'selector_count' ] = 0; - foreach ( $process->tokens->r as $rule ) { - $process->stat[ 'selector_count' ] += count( $rule->selectorList ); - } - break; - - case 'rule_count': - $process->stat[ 'rule_count' ] = count( $process->tokens->r ); - break; - - case 'compile_time': - $time = microtime( true ); - $process->stat[ 'compile_time' ] = $time - $process->stat[ 'compile_start_time' ]; - break; - } - } + // If doc_root is still falsy, log an error + $error = "Could not get a document_root reference."; + csscrush::logError( $error ); + trigger_error( __METHOD__ . ": $error\n", E_USER_NOTICE ); + } + } + + self::$config->docRoot = csscrush_util::normalizePath( $doc_root ); + } + + // Aliases and macros loader. + static public function loadAssets () { + + static $called; + if ( $called ) { + return; + } + + // Find an aliases file in the root directory + // a local file overrides the default + $aliases_file = csscrush_util::find( 'Aliases-local.ini', 'Aliases.ini' ); + + // Load aliases file if it exists + if ( $aliases_file ) { + + if ( $result = @parse_ini_file( $aliases_file, true ) ) { + + self::$config->aliases = $result; + + // Value aliases require a little preprocessing + if ( isset( self::$config->aliases[ 'values' ] ) ) { + $store = array(); + foreach ( self::$config->aliases[ 'values' ] as $prop_val => $aliases ) { + list( $prop, $value ) = array_map( 'trim', explode( ':', $prop_val ) ); + $store[ $prop ][ $value ] = $aliases; + } + self::$config->aliases[ 'values' ] = $store; + } + + // Ensure all alias groups are at least set (issue #34) + self::$config->aliases += array( + 'properties' => array(), + 'functions' => array(), + 'values' => array(), + 'at-rules' => array(), + ); + } + else { + trigger_error( __METHOD__ . ": Aliases file could not be parsed.\n", E_USER_NOTICE ); + } + } + else { + trigger_error( __METHOD__ . ": Aliases file not found.\n", E_USER_NOTICE ); + } + + // Find a plugins file in the root directory, + // a local file overrides the default + $plugins_file = csscrush_util::find( 'Plugins-local.ini', 'Plugins.ini' ); + + // Load plugins + if ( $plugins_file ) { + if ( $result = @parse_ini_file( $plugins_file ) ) { + foreach ( $result[ 'plugins' ] as $plugin_name ) { + // Backwards compat. + $plugin_name = basename( $plugin_name, '.php' ); + if ( csscrush_plugin::enable( $plugin_name ) ) { + self::$config->plugins[ $plugin_name ] = true; + } + } + } + else { + trigger_error( __METHOD__ . ": Plugin file could not be parsed.\n", E_USER_NOTICE ); + } + } + + $called = true; + } + + + ############################# + # External API. + + /** + * Process host CSS file and return a new compiled file. + * + * @param string $file URL or System path to the host CSS file. + * @param mixed $options An array of options or null. + * @return string The public path to the compiled file or an empty string. + */ + static public function file ( $file, $options = null ) { + + self::$process = new csscrush_process( $options ); + + $config = self::$config; + $process = self::$process; + $options = $process->options; + $doc_root = $config->docRoot; + + // Since we're comparing strings, we need to iron out OS differences. + $file = str_replace( '\\', '/', $file ); + + // Finding the system path of the input file and validating it. + $pathtest = true; + if ( strpos( $file, $doc_root ) === 0 ) { + // System path. + $pathtest = $process->setContext( dirname( $file ) ); + } + else if ( strpos( $file, '/' ) === 0 ) { + // WWW root path. + $pathtest = $process->setContext( dirname( $doc_root . $file ) ); + } + else { + // Relative path. + $pathtest = $process->setContext( dirname( dirname( __FILE__ ) . '/' . $file ) ); + } + + if ( ! $pathtest ) { + // Main directory not found or is not writable return an empty string. + return ''; + } + + // Validate file input. + if ( ! csscrush_io::registerInputFile( $file ) ) { + return ''; + } + + // Create a filename that will be used later + // Used in validateCache, and writing to filesystem + $process->output->filename = $process->ioCall( 'getOutputFileName' ); + + // Caching. + if ( $options->cache ) { + + // Load the cache data. + $process->cacheData = $process->ioCall( 'getCacheData' ); + + // If cache is enabled check for a valid compiled file. + $valid_compliled_file = $process->ioCall( 'validateExistingOutput' ); + + if ( is_string( $valid_compliled_file ) ) { + return $valid_compliled_file; + } + } + + // Compile. + $stream = $process->compile(); + + // Create file and return url. Return empty string on failure. + if ( file_put_contents( "{$process->output->dir}/{$process->output->filename}", $stream ) ) { + $timestamp = $options->versioning ? '?' . time() : ''; + return "{$process->output->dirUrl}/{$process->output->filename}$timestamp"; + } + else { + return ''; + } + } + + /** + * Process host CSS file and return an HTML link tag with populated href. + * + * @param string $file Absolute or relative path to the host CSS file. + * @param mixed $options An array of options or null. + * @param array $attributes An array of HTML attributes. + * @return string HTML link tag or error message inside HTML comment. + */ + static public function tag ( $file, $options = null, $attributes = array() ) { + + $file = self::file( $file, $options ); + + if ( ! empty( $file ) ) { + + // On success return the tag with any custom attributes + $attributes[ 'rel' ] = 'stylesheet'; + $attributes[ 'href' ] = $file; + + // Should media type be forced to 'all'? + if ( ! isset( $attributes[ 'media' ] ) ) { + $attributes[ 'media' ] = 'all'; + } + $attr_string = csscrush_util::htmlAttributes( $attributes ); + return "\n"; + } + else { + + // Return an HTML comment with message on failure + $class = __CLASS__; + $errors = implode( "\n", self::$process->errors ); + return "\n"; + } + } + + /** + * Process host CSS file and return CSS as text wrapped in html style tags. + * + * @param string $file Absolute or relative path to the host CSS file. + * @param mixed $options An array of options or null. + * @param array $attributes An array of HTML attributes, set false to return CSS text without tag. + * @return string HTML link tag or error message inside HTML comment. + */ + static public function inline ( $file, $options = null, $attributes = array() ) { + + // For inline output set boilerplate to not display by default + if ( ! is_array( $options ) ) { + $options = array(); + } + if ( ! isset( $options[ 'boilerplate' ] ) ) { + $options[ 'boilerplate' ] = false; + } + + $file = self::file( $file, $options ); + + if ( ! empty( $file ) ) { + + // On success fetch the CSS text + $content = file_get_contents( self::$process->output->dir . '/' + . self::$process->output->filename ); + $tag_open = ''; + $tag_close = ''; + + if ( is_array( $attributes ) ) { + $attr_string = csscrush_util::htmlAttributes( $attributes ); + $tag_open = ""; + $tag_close = ''; + } + return "$tag_open{$content}$tag_close\n"; + } + else { + + // Return an HTML comment with message on failure + $class = __CLASS__; + $errors = implode( "\n", self::$process->errors ); + return "\n"; + } + } + + /** + * Compile a raw string of CSS string and return it. + * + * @param string $string CSS text. + * @param mixed $options An array of options or null. + * @return string CSS text. + */ + static public function string ( $string, $options = null ) { + + // For strings set boilerplate to not display by default + if ( ! isset( $options[ 'boilerplate' ] ) ) { + $options[ 'boilerplate' ] = false; + } + + self::$process = new csscrush_process( $options ); + + $config = self::$config; + $process = self::$process; + $options = $process->options; + + // Set the path context if one is given. + // Fallback to document root. + if ( ! empty( $options->context ) ) { + $process->setContext( $options->context, false ); + } + else { + $process->setContext( $config->docRoot, false ); + } + + // Set the string on the input object. + $process->input->string = $string; + + // Import files may be ignored + if ( isset( $options->no_import ) ) { + $process->input->importIgnore = true; + } + + // Compile and return. + return $process->compile(); + } + + /** + * Add variables globally. + * + * @param mixed $var Assoc array of variable names and values, a php ini filename or null. + */ + static public function globalVars ( $vars ) { + + $config = self::$config; + + // Merge into the stack, overrides existing variables of the same name + if ( is_array( $vars ) ) { + $config->vars = array_merge( $config->vars, $vars ); + } + // Test for a file. If it is attempt to parse it + elseif ( is_string( $vars ) && file_exists( $vars ) ) { + if ( $result = @parse_ini_file( $vars ) ) { + $config->vars = array_merge( $config->vars, $result ); + } + } + // Clear the stack if the argument is explicitly null + elseif ( is_null( $vars ) ) { + $config->vars = array(); + } + } + + /** + * Clear config file and compiled files for the specified directory. + * + * @param string $dir System path to the directory. + */ + static public function clearCache ( $dir = '' ) { + return $process->ioCall( 'clearCache', $dir ); + } + + /** + * Get debug info. + * Depends on arguments passed to the trace option. + * + * @param string $name Name of stat to retrieve. Leave blank to retrieve all. + */ + static public function stat ( $name = null ) { + + $stat = csscrush::$process->stat; + + if ( $name && array_key_exists( $name, $stat ) ) { + return array( $name => $stat[ $name ] ); + } + + // Lose stats that are only useful internally. + unset( $stat[ 'compile_start_time' ] ); + + return $stat; + } + + + ############################# + # Internal development. + + static public $logging = false; + + static public function log ( $arg = null, $label = '' ) { + + if ( ! self::$logging ) { + return; + } + static $log = ''; + + $args = func_get_args(); + if ( ! count( $args ) ) { + // No arguments, return the log + return $log; + } + + if ( $label ) { + $log .= "

$label

"; + } + + if ( is_string( $arg ) ) { + $log .= $arg . '
'; + } + else { + $out = '
';
+            ob_start();
+            print_r( $arg );
+            $out .= ob_get_clean();
+            $out .= '
'; + $log .= $out . '
'; + } + } + + static public function logError ( $msg ) { + self::$process->errors[] = $msg; + self::log( $msg ); + } + + static public function runStat ( $name ) { + + $process = csscrush::$process; + + if ( ! $process->options->trace || ! in_array( $name, $process->options->trace ) ) { + return; + } + + switch ( $name ) { + + case 'selector_count': + $process->stat[ 'selector_count' ] = 0; + foreach ( $process->tokens->r as $rule ) { + $process->stat[ 'selector_count' ] += count( $rule->selectorList ); + } + break; + + case 'rule_count': + $process->stat[ 'rule_count' ] = count( $process->tokens->r ); + break; + + case 'compile_time': + $time = microtime( true ); + $process->stat[ 'compile_time' ] = $time - $process->stat[ 'compile_start_time' ]; + break; + } + } } @@ -519,20 +519,20 @@ static public function runStat ( $name ) { # Procedural style external API. function csscrush_file ( $file, $options = null ) { - return csscrush::file( $file, $options ); + return csscrush::file( $file, $options ); } function csscrush_tag ( $file, $options = null, $attributes = array() ) { - return csscrush::tag( $file, $options, $attributes ); + return csscrush::tag( $file, $options, $attributes ); } function csscrush_inline ( $file, $options = null, $attributes = array() ) { - return csscrush::inline( $file, $options, $attributes ); + return csscrush::inline( $file, $options, $attributes ); } function csscrush_string ( $string, $options = null ) { - return csscrush::string( $string, $options ); + return csscrush::string( $string, $options ); } function csscrush_globalvars ( $vars ) { - return csscrush::globalVars( $vars ); + return csscrush::globalVars( $vars ); } function csscrush_clearcache ( $dir = '' ) { - return csscrush::clearcache( $dir ); + return csscrush::clearcache( $dir ); } diff --git a/lib/Function.php b/lib/Function.php index fbc31e6..fe38111 100644 --- a/lib/Function.php +++ b/lib/Function.php @@ -6,293 +6,293 @@ */ class csscrush_function { - // Regex pattern for finding custom functions - static public $functionPatt; - - // Cache for function names - static public $functionList; - - static public function init () { - - // Set the custom function regex pattern - self::$functionList = self::getFunctions(); - self::$functionPatt = csscrush_regex::createFunctionMatchPatt( self::$functionList, true ); - } - - static public function getFunctions () { - - // Fetch custom function names - // Include subtraction operator - $fn_methods = array( '-' ); - $all_methods = get_class_methods( __CLASS__ ); - foreach ( $all_methods as &$_method ) { - $prefix = 'css_fn__'; - if ( ( $pos = strpos( $_method, $prefix ) ) === 0 ) { - $fn_methods[] = str_replace( '_', '-', substr( $_method, strlen( $prefix ) ) ); - } - } - return $fn_methods; - } - - static public function executeCustomFunctions ( &$str, $patt = null, $process_callback = null, $property = null ) { - - // No bracketed expressions, early return. - if ( false === strpos( $str, '(' ) ) { - return; - } - - // Set default pattern if not set. - if ( is_null( $patt ) ) { - $patt = csscrush_function::$functionPatt; - } - - // No custom functions, early return. - if ( ! preg_match( $patt, $str ) ) { - return; - } - - // Find custom function matches. - $matches = csscrush_regex::matchAll( $patt, $str ); - - // Step through the matches from last to first. - while ( $match = array_pop( $matches ) ) { - - $offset = $match[0][1]; - - if ( ! preg_match( csscrush_regex::$patt->balancedParens, - $str, $parens, PREG_OFFSET_CAPTURE, $offset ) ) { - continue; - } - - // No function name default to math expression. - // Store the raw function name match. - $raw_fn_name = isset( $match[1] ) ? $match[1][0] : ''; - $fn_name = $raw_fn_name ? $raw_fn_name : 'math'; - if ( '-' === $fn_name ) { - $fn_name = 'math'; - } - - $opening_paren = $parens[0][1]; - $closing_paren = $opening_paren + strlen( $parens[0][0] ); - - // Get the function arguments. - $args = trim( $parens[1][0] ); - - // Workaround the minus. - $minus_before = '-' === $raw_fn_name ? '-' : ''; - - $func_returns = ''; - - if ( ! $process_callback ) { - - // If no callback reference it's a built-in. - if ( in_array( $fn_name, self::$functionList ) ) { - $fn_name_clean = str_replace( '-', '_', $fn_name ); - $func_returns = call_user_func( array( 'self', "css_fn__$fn_name_clean" ), $args ); - } - } - else { - if ( isset( $process_callback[ $fn_name ] ) ) { - $func_returns = call_user_func( $process_callback[ $fn_name ], $args, $fn_name, $property ); - } - } - - // Join together the result. - $str = substr( $str, 0, $offset ) . $minus_before . $func_returns . substr( $str, $closing_paren ); - } - } - - - ############################# - # Helpers. - - static public function parseArgs ( $input, $allowSpaceDelim = false ) { - return csscrush_util::splitDelimList( - $input, ( $allowSpaceDelim ? '\s*[,\s]\s*' : ',' ) ); - } - - // Intended as a quick arg-list parse for function that take up-to 2 arguments - // with the proviso the first argument is a name - static public function parseArgsSimple ( $input ) { - return preg_split( csscrush_regex::$patt->argListSplit, $input, 2 ); - } - - static protected function colorAdjust ( $color, array $adjustments ) { - - $fn_matched = preg_match( '!^(#|rgba?|hsla?)!', $color, $m ); - $keywords =& csscrush_color::loadKeywords(); - - // Support for Hex, RGB, RGBa and keywords - // HSL and HSLa are passed over - if ( $fn_matched || isset( $keywords[ $color ] ) ) { - - $alpha = 1; - $rgb = null; - - // Get an RGB array from the color argument - if ( $fn_matched ) { - switch ( $m[1] ) { - case '#': - $rgb = csscrush_color::hexToRgb( $color ); - break; - - case 'rgb': - case 'rgba': - case 'hsl': - case 'hsla': - $function = $m[1]; - $alpha_channel = 4 === strlen( $function ) ? true : false; - $vals = substr( $color, strlen( $function ) + 1 ); // Trim function name and start paren - $vals = substr( $vals, 0, strlen( $vals ) - 1 ); // Trim end paren - $vals = array_map( 'trim', explode( ',', $vals ) ); // Explode to array of arguments - if ( $alpha_channel ) { - $alpha = array_pop( $vals ); - } - if ( 0 === strpos( $function, 'rgb' ) ) { - $rgb = csscrush_color::normalizeCssRgb( $vals ); - } - else { - $rgb = csscrush_color::cssHslToRgb( $vals ); - } - break; - } - } - else { - $rgb = $keywords[ $color ]; - } - - $hsl = csscrush_color::rgbToHsl( $rgb ); - - // Normalize adjustment parameters to floating point numbers - // then calculate the new HSL value - $index = 0; - foreach ( $adjustments as $val ) { - // Normalize argument - $_val = $val ? trim( str_replace( '%', '', $val ) ) : 0; - - // Reduce value to float - $_val /= 100; - - // Adjust alpha component if necessary - if ( 3 === $index ) { - if ( 0 != $val ) { - $alpha = max( 0, min( 1, $alpha + $_val ) ); - } - } - // Adjust HSL component value if necessary - else { - if ( 0 != $val ) { - $hsl[ $index ] = max( 0, min( 1, $hsl[ $index ] + $_val ) ); - } - } - $index++; - } - - // Finally convert new HSL value to RGB - $rgb = csscrush_color::hslToRgb( $hsl ); - - // Return as hex if there is no modified alpha channel - // Otherwise return RGBA string - if ( 1 === $alpha ) { - return csscrush_color::rgbToHex( $rgb ); - } - $rgb[] = $alpha; - return 'rgba(' . implode( ',', $rgb ) . ')'; - } - else { - return $color; - } - } - - - ############################# - # CSS functions. - - static protected function css_fn__math ( $input ) { - - // Strip blacklisted characters - $input = preg_replace( csscrush_regex::$patt->mathBlacklist, '', $input ); - - $result = @eval( "return $input;" ); - - return $result === false ? 0 : round( $result, 5 ); - } - - static protected function css_fn__percent ( $input ) { - - // Strip non-numeric and non delimiter characters - $input = preg_replace( '![^\d\.\s,]!S', '', $input ); - - $args = preg_split( csscrush_regex::$patt->argListSplit, $input, -1, PREG_SPLIT_NO_EMPTY ); - - // Use precision argument if it exists, use default otherwise - $precision = isset( $args[2] ) ? $args[2] : 5; - - // Output zero on failure - $result = 0; - - // Need to check arguments or we may see divide by zero errors - if ( count( $args ) > 1 && ! empty( $args[0] ) && ! empty( $args[1] ) ) { - - // Use bcmath if it's available for higher precision - - // Arbitary high precision division - if ( function_exists( 'bcdiv' ) ) { - $div = bcdiv( $args[0], $args[1], 25 ); - } - else { - $div = $args[0] / $args[1]; - } - - // Set precision percentage value - if ( function_exists( 'bcmul' ) ) { - $result = bcmul( (string) $div, '100', $precision ); - } - else { - $result = round( $div * 100, $precision ); - } - - // Trim unnecessary zeros and decimals - $result = trim( (string) $result, '0' ); - $result = rtrim( $result, '.' ); - } - - return $result . '%'; - } - - // Percent function alias. - static protected function css_fn__pc ( $input ) { - return self::css_fn__percent( $input ); - } - - static protected function css_fn__hsla_adjust ( $input ) { - list( $color, $h, $s, $l, $a ) = array_pad( self::parseArgs( $input, true ), 5, 0 ); - return self::colorAdjust( $color, array( $h, $s, $l, $a ) ); - } - - static protected function css_fn__hsl_adjust ( $input ) { - list( $color, $h, $s, $l ) = array_pad( self::parseArgs( $input, true ), 4, 0 ); - return self::colorAdjust( $color, array( $h, $s, $l, 0 ) ); - } - - static protected function css_fn__h_adjust ( $input ) { - list( $color, $h ) = array_pad( self::parseArgs( $input, true ), 2, 0 ); - return self::colorAdjust( $color, array( $h, 0, 0, 0 ) ); - } - - static protected function css_fn__s_adjust ( $input ) { - list( $color, $s ) = array_pad( self::parseArgs( $input, true ), 2, 0 ); - return self::colorAdjust( $color, array( 0, $s, 0, 0 ) ); - } - - static protected function css_fn__l_adjust ( $input ) { - list( $color, $l ) = array_pad( self::parseArgs( $input, true ), 2, 0 ); - return self::colorAdjust( $color, array( 0, 0, $l, 0 ) ); - } - - static protected function css_fn__a_adjust ( $input ) { - list( $color, $a ) = array_pad( self::parseArgs( $input, true ), 2, 0 ); - return self::colorAdjust( $color, array( 0, 0, 0, $a ) ); - } + // Regex pattern for finding custom functions + static public $functionPatt; + + // Cache for function names + static public $functionList; + + static public function init () { + + // Set the custom function regex pattern + self::$functionList = self::getFunctions(); + self::$functionPatt = csscrush_regex::createFunctionMatchPatt( self::$functionList, true ); + } + + static public function getFunctions () { + + // Fetch custom function names + // Include subtraction operator + $fn_methods = array( '-' ); + $all_methods = get_class_methods( __CLASS__ ); + foreach ( $all_methods as &$_method ) { + $prefix = 'css_fn__'; + if ( ( $pos = strpos( $_method, $prefix ) ) === 0 ) { + $fn_methods[] = str_replace( '_', '-', substr( $_method, strlen( $prefix ) ) ); + } + } + return $fn_methods; + } + + static public function executeCustomFunctions ( &$str, $patt = null, $process_callback = null, $property = null ) { + + // No bracketed expressions, early return. + if ( false === strpos( $str, '(' ) ) { + return; + } + + // Set default pattern if not set. + if ( is_null( $patt ) ) { + $patt = csscrush_function::$functionPatt; + } + + // No custom functions, early return. + if ( ! preg_match( $patt, $str ) ) { + return; + } + + // Find custom function matches. + $matches = csscrush_regex::matchAll( $patt, $str ); + + // Step through the matches from last to first. + while ( $match = array_pop( $matches ) ) { + + $offset = $match[0][1]; + + if ( ! preg_match( csscrush_regex::$patt->balancedParens, + $str, $parens, PREG_OFFSET_CAPTURE, $offset ) ) { + continue; + } + + // No function name default to math expression. + // Store the raw function name match. + $raw_fn_name = isset( $match[1] ) ? $match[1][0] : ''; + $fn_name = $raw_fn_name ? $raw_fn_name : 'math'; + if ( '-' === $fn_name ) { + $fn_name = 'math'; + } + + $opening_paren = $parens[0][1]; + $closing_paren = $opening_paren + strlen( $parens[0][0] ); + + // Get the function arguments. + $args = trim( $parens[1][0] ); + + // Workaround the minus. + $minus_before = '-' === $raw_fn_name ? '-' : ''; + + $func_returns = ''; + + if ( ! $process_callback ) { + + // If no callback reference it's a built-in. + if ( in_array( $fn_name, self::$functionList ) ) { + $fn_name_clean = str_replace( '-', '_', $fn_name ); + $func_returns = call_user_func( array( 'self', "css_fn__$fn_name_clean" ), $args ); + } + } + else { + if ( isset( $process_callback[ $fn_name ] ) ) { + $func_returns = call_user_func( $process_callback[ $fn_name ], $args, $fn_name, $property ); + } + } + + // Join together the result. + $str = substr( $str, 0, $offset ) . $minus_before . $func_returns . substr( $str, $closing_paren ); + } + } + + + ############################# + # Helpers. + + static public function parseArgs ( $input, $allowSpaceDelim = false ) { + return csscrush_util::splitDelimList( + $input, ( $allowSpaceDelim ? '\s*[,\s]\s*' : ',' ) ); + } + + // Intended as a quick arg-list parse for function that take up-to 2 arguments + // with the proviso the first argument is a name + static public function parseArgsSimple ( $input ) { + return preg_split( csscrush_regex::$patt->argListSplit, $input, 2 ); + } + + static protected function colorAdjust ( $color, array $adjustments ) { + + $fn_matched = preg_match( '!^(#|rgba?|hsla?)!', $color, $m ); + $keywords =& csscrush_color::loadKeywords(); + + // Support for Hex, RGB, RGBa and keywords + // HSL and HSLa are passed over + if ( $fn_matched || isset( $keywords[ $color ] ) ) { + + $alpha = 1; + $rgb = null; + + // Get an RGB array from the color argument + if ( $fn_matched ) { + switch ( $m[1] ) { + case '#': + $rgb = csscrush_color::hexToRgb( $color ); + break; + + case 'rgb': + case 'rgba': + case 'hsl': + case 'hsla': + $function = $m[1]; + $alpha_channel = 4 === strlen( $function ) ? true : false; + $vals = substr( $color, strlen( $function ) + 1 ); // Trim function name and start paren + $vals = substr( $vals, 0, strlen( $vals ) - 1 ); // Trim end paren + $vals = array_map( 'trim', explode( ',', $vals ) ); // Explode to array of arguments + if ( $alpha_channel ) { + $alpha = array_pop( $vals ); + } + if ( 0 === strpos( $function, 'rgb' ) ) { + $rgb = csscrush_color::normalizeCssRgb( $vals ); + } + else { + $rgb = csscrush_color::cssHslToRgb( $vals ); + } + break; + } + } + else { + $rgb = $keywords[ $color ]; + } + + $hsl = csscrush_color::rgbToHsl( $rgb ); + + // Normalize adjustment parameters to floating point numbers + // then calculate the new HSL value + $index = 0; + foreach ( $adjustments as $val ) { + // Normalize argument + $_val = $val ? trim( str_replace( '%', '', $val ) ) : 0; + + // Reduce value to float + $_val /= 100; + + // Adjust alpha component if necessary + if ( 3 === $index ) { + if ( 0 != $val ) { + $alpha = max( 0, min( 1, $alpha + $_val ) ); + } + } + // Adjust HSL component value if necessary + else { + if ( 0 != $val ) { + $hsl[ $index ] = max( 0, min( 1, $hsl[ $index ] + $_val ) ); + } + } + $index++; + } + + // Finally convert new HSL value to RGB + $rgb = csscrush_color::hslToRgb( $hsl ); + + // Return as hex if there is no modified alpha channel + // Otherwise return RGBA string + if ( 1 === $alpha ) { + return csscrush_color::rgbToHex( $rgb ); + } + $rgb[] = $alpha; + return 'rgba(' . implode( ',', $rgb ) . ')'; + } + else { + return $color; + } + } + + + ############################# + # CSS functions. + + static protected function css_fn__math ( $input ) { + + // Strip blacklisted characters + $input = preg_replace( csscrush_regex::$patt->mathBlacklist, '', $input ); + + $result = @eval( "return $input;" ); + + return $result === false ? 0 : round( $result, 5 ); + } + + static protected function css_fn__percent ( $input ) { + + // Strip non-numeric and non delimiter characters + $input = preg_replace( '![^\d\.\s,]!S', '', $input ); + + $args = preg_split( csscrush_regex::$patt->argListSplit, $input, -1, PREG_SPLIT_NO_EMPTY ); + + // Use precision argument if it exists, use default otherwise + $precision = isset( $args[2] ) ? $args[2] : 5; + + // Output zero on failure + $result = 0; + + // Need to check arguments or we may see divide by zero errors + if ( count( $args ) > 1 && ! empty( $args[0] ) && ! empty( $args[1] ) ) { + + // Use bcmath if it's available for higher precision + + // Arbitary high precision division + if ( function_exists( 'bcdiv' ) ) { + $div = bcdiv( $args[0], $args[1], 25 ); + } + else { + $div = $args[0] / $args[1]; + } + + // Set precision percentage value + if ( function_exists( 'bcmul' ) ) { + $result = bcmul( (string) $div, '100', $precision ); + } + else { + $result = round( $div * 100, $precision ); + } + + // Trim unnecessary zeros and decimals + $result = trim( (string) $result, '0' ); + $result = rtrim( $result, '.' ); + } + + return $result . '%'; + } + + // Percent function alias. + static protected function css_fn__pc ( $input ) { + return self::css_fn__percent( $input ); + } + + static protected function css_fn__hsla_adjust ( $input ) { + list( $color, $h, $s, $l, $a ) = array_pad( self::parseArgs( $input, true ), 5, 0 ); + return self::colorAdjust( $color, array( $h, $s, $l, $a ) ); + } + + static protected function css_fn__hsl_adjust ( $input ) { + list( $color, $h, $s, $l ) = array_pad( self::parseArgs( $input, true ), 4, 0 ); + return self::colorAdjust( $color, array( $h, $s, $l, 0 ) ); + } + + static protected function css_fn__h_adjust ( $input ) { + list( $color, $h ) = array_pad( self::parseArgs( $input, true ), 2, 0 ); + return self::colorAdjust( $color, array( $h, 0, 0, 0 ) ); + } + + static protected function css_fn__s_adjust ( $input ) { + list( $color, $s ) = array_pad( self::parseArgs( $input, true ), 2, 0 ); + return self::colorAdjust( $color, array( 0, $s, 0, 0 ) ); + } + + static protected function css_fn__l_adjust ( $input ) { + list( $color, $l ) = array_pad( self::parseArgs( $input, true ), 2, 0 ); + return self::colorAdjust( $color, array( 0, 0, $l, 0 ) ); + } + + static protected function css_fn__a_adjust ( $input ) { + list( $color, $a ) = array_pad( self::parseArgs( $input, true ), 2, 0 ); + return self::colorAdjust( $color, array( 0, 0, 0, $a ) ); + } } diff --git a/lib/Hook.php b/lib/Hook.php index 943b5f3..3eb72d9 100644 --- a/lib/Hook.php +++ b/lib/Hook.php @@ -6,44 +6,44 @@ */ class csscrush_hook { - // Table of hooks and the functions attached to them - static public $register = array(); - - static public function add ( $hook, $fn_name ) { - - // Bail early is the named hook and callback combination is already loaded - if ( isset( self::$register[ $hook ][ $fn_name ] ) ) { - return; - } - - // Register the hook and callback. - // Store in associative array so no duplicates - if ( function_exists( $fn_name ) ) { - - self::$register[ $hook ][ $fn_name ] = true; - - // If the callback is a plugin register the hook that it uses - if ( strpos( $fn_name, csscrush_plugin::$prefix ) === 0 ) { - - $plugin_name = str_replace( '_', '-', - substr( $fn_name, strlen( csscrush_plugin::$prefix ) ) ); - csscrush_plugin::registerHook( $plugin_name, $hook ); - } - } - } - - static public function remove ( $hook, $fn_name ) { - unset( self::$register[ $hook ][ $fn_name ] ); - } - - static public function run ( $hook, $arg_obj = null ) { - - // Run all callbacks attached to the hook - if ( ! isset( self::$register[ $hook ] ) ) { - return; - } - foreach ( array_keys( self::$register[ $hook ] ) as $fn_name ) { - call_user_func( $fn_name, $arg_obj ); - } - } + // Table of hooks and the functions attached to them + static public $register = array(); + + static public function add ( $hook, $fn_name ) { + + // Bail early is the named hook and callback combination is already loaded + if ( isset( self::$register[ $hook ][ $fn_name ] ) ) { + return; + } + + // Register the hook and callback. + // Store in associative array so no duplicates + if ( function_exists( $fn_name ) ) { + + self::$register[ $hook ][ $fn_name ] = true; + + // If the callback is a plugin register the hook that it uses + if ( strpos( $fn_name, csscrush_plugin::$prefix ) === 0 ) { + + $plugin_name = str_replace( '_', '-', + substr( $fn_name, strlen( csscrush_plugin::$prefix ) ) ); + csscrush_plugin::registerHook( $plugin_name, $hook ); + } + } + } + + static public function remove ( $hook, $fn_name ) { + unset( self::$register[ $hook ][ $fn_name ] ); + } + + static public function run ( $hook, $arg_obj = null ) { + + // Run all callbacks attached to the hook + if ( ! isset( self::$register[ $hook ] ) ) { + return; + } + foreach ( array_keys( self::$register[ $hook ] ) as $fn_name ) { + call_user_func( $fn_name, $arg_obj ); + } + } } diff --git a/lib/IO.php b/lib/IO.php index da61f4e..6507e41 100644 --- a/lib/IO.php +++ b/lib/IO.php @@ -6,258 +6,258 @@ */ class csscrush_io { - // Any setup that needs to be done - static public function init () { + // Any setup that needs to be done + static public function init () { - $process = csscrush::$process; + $process = csscrush::$process; - $process->cacheFileName = '.csscrush'; - $process->cacheFilePath = "{$process->input->dir}/$process->cacheFileName"; - } + $process->cacheFileName = '.csscrush'; + $process->cacheFilePath = "{$process->input->dir}/$process->cacheFileName"; + } - static public function getOutputDir () { - return csscrush::$process->input->dir; - } + static public function getOutputDir () { + return csscrush::$process->input->dir; + } - static public function testOutputDir () { + static public function testOutputDir () { - $output_dir = csscrush::$process->output->dir; - $pathtest = true; - $error = false; + $output_dir = csscrush::$process->output->dir; + $pathtest = true; + $error = false; - if ( ! file_exists( $output_dir ) ) { + if ( ! file_exists( $output_dir ) ) { - $error = "Output directory '$output_dir' doesn't exist."; - $pathtest = false; - } - else if ( ! is_writable( $output_dir ) ) { + $error = "Output directory '$output_dir' doesn't exist."; + $pathtest = false; + } + else if ( ! is_writable( $output_dir ) ) { - csscrush::log( 'Attempting to change permissions.' ); + csscrush::log( 'Attempting to change permissions.' ); - if ( ! @chmod( $output_dir, 0755 ) ) { + if ( ! @chmod( $output_dir, 0755 ) ) { - $error = "Output directory '$output_dir' is unwritable."; - $pathtest = false; - } - else { - csscrush::log( 'Permissions updated.' ); - } - } + $error = "Output directory '$output_dir' is unwritable."; + $pathtest = false; + } + else { + csscrush::log( 'Permissions updated.' ); + } + } - if ( $error ) { - csscrush::logError( $error ); - trigger_error( __METHOD__ . ": $error\n", E_USER_WARNING ); - } + if ( $error ) { + csscrush::logError( $error ); + trigger_error( __METHOD__ . ": $error\n", E_USER_WARNING ); + } - return $pathtest; - } + return $pathtest; + } - static public function getOutputFileName () { + static public function getOutputFileName () { - $process = csscrush::$process; - $options = $process->options; + $process = csscrush::$process; + $options = $process->options; - $output_basename = basename( $process->input->filename, '.css' ); + $output_basename = basename( $process->input->filename, '.css' ); - if ( ! empty( $options->output_file ) ) { - $output_basename = basename( $options->output_file, '.css' ); - } + if ( ! empty( $options->output_file ) ) { + $output_basename = basename( $options->output_file, '.css' ); + } - return "$output_basename.crush.css"; - } + return "$output_basename.crush.css"; + } - static public function validateExistingOutput () { + static public function validateExistingOutput () { - $process = csscrush::$process; - $options = $process->options; - $config = csscrush::$config; - $input = $process->input; + $process = csscrush::$process; + $options = $process->options; + $config = csscrush::$config; + $input = $process->input; - // Search base directory for an existing compiled file. - foreach ( scandir( $process->output->dir ) as $filename ) { + // Search base directory for an existing compiled file. + foreach ( scandir( $process->output->dir ) as $filename ) { - if ( $process->output->filename != $filename ) { - continue; - } - - // Cached file exists. - csscrush::log( 'Cached file exists.' ); - - $existingfile = (object) array(); - $existingfile->filename = $filename; - $existingfile->path = "{$process->output->dir}/$existingfile->filename"; - $existingfile->URL = "{$process->output->dirUrl}/$existingfile->filename"; + if ( $process->output->filename != $filename ) { + continue; + } + + // Cached file exists. + csscrush::log( 'Cached file exists.' ); + + $existingfile = (object) array(); + $existingfile->filename = $filename; + $existingfile->path = "{$process->output->dir}/$existingfile->filename"; + $existingfile->URL = "{$process->output->dirUrl}/$existingfile->filename"; - // Start off with the input file then add imported files - $all_files = array( $input->mtime ); - - if ( file_exists( $existingfile->path ) && isset( $process->cacheData[ $process->output->filename ] ) ) { - - // File exists and has config - csscrush::log( 'Cached file is registered.' ); - - foreach ( $process->cacheData[ $existingfile->filename ][ 'imports' ] as $import_file ) { - - // Check if this is docroot relative or input dir relative - $root = strpos( $import_file, '/' ) === 0 ? $config->docRoot : $process->input->dir; - $import_filepath = realpath( $root ) . "/$import_file"; - - if ( file_exists( $import_filepath ) ) { - $all_files[] = filemtime( $import_filepath ); - } - else { - // File has been moved, remove old file and skip to compile - csscrush::log( 'Import file has been moved, removing existing file.' ); - unlink( $existingfile->path ); - return false; - } - } - - // Cast because the cached options may be a stdClass if an IO adapter has been used. - $existing_options = (array) $process->cacheData[ $existingfile->filename ][ 'options' ]; - $existing_datesum = $process->cacheData[ $existingfile->filename ][ 'datem_sum' ]; - - $options_unchanged = true; - foreach ( $existing_options as $key => &$value ) { - if ( $existing_options[ $key ] !== $options->{ $key } ) { - $options_unchanged = false; - break; - } - } - $files_unchanged = $existing_datesum == array_sum( $all_files ); - - if ( $options_unchanged && $files_unchanged ) { - - // Files have not been modified and config is the same: return the old file - csscrush::log( "Files and options have not been modified, returning existing - file '$existingfile->URL'." ); - return $existingfile->URL . ( $options->versioning !== false ? "?$existing_datesum" : '' ); - } - else { - - // Remove old file and continue making a new one... - ! $options_unchanged && csscrush::log( 'Options have been modified.' ); - ! $files_unchanged && csscrush::log( 'Files have been modified.' ); - csscrush::log( 'Removing existing file.' ); - - unlink( $existingfile->path ); - } - } - else if ( file_exists( $existingfile->path ) ) { - // File exists but has no config - csscrush::log( 'File exists but no config, removing existing file.' ); - unlink( $existingfile->path ); - } - return false; - - } // foreach - - return false; - } - - static public function clearCache ( $dir ) { - - if ( empty( $dir ) ) { - $dir = dirname( __FILE__ ); - } - else if ( ! file_exists( $dir ) ) { - return; - } - - $configPath = $dir . '/' . csscrush::$process->cacheFilePath; - if ( file_exists( $configPath ) ) { - unlink( $configPath ); - } - - // Remove any compiled files - $suffix = '.crush.css'; - $suffixLength = strlen( $suffix ); - - foreach ( scandir( $dir ) as $file ) { - if ( - strpos( $file, $suffix ) === strlen( $file ) - $suffixLength - ) { - unlink( $dir . "/{$file}" ); - } - } - } - - static public function getCacheData () { - - $config = csscrush::$config; - $process = csscrush::$process; - - if ( - file_exists( $process->cacheFilePath ) && - $process->cacheData && - $process->cacheData[ 'originPath' ] == $process->cacheFilePath - ) { - // Already loaded and config file exists in the current directory - return; - } - - $cache_data_exists = file_exists( $process->cacheFilePath ); - $cache_data_file_is_writable = $cache_data_exists ? is_writable( $process->cacheFilePath ) : false; - $cache_data = array(); - - if ( - $cache_data_exists && - $cache_data_file_is_writable && - $cache_data = json_decode( file_get_contents( $process->cacheFilePath ), true ) - ) { - // Successfully loaded config file. - csscrush::log( 'Cache data loaded.' ); - } - else { - // Config file may exist but not be writable (may not be visible in some ftp situations?) - if ( $cache_data_exists ) { - if ( ! @unlink( $process->cacheFilePath ) ) { - - $error = "Could not delete config data file."; - csscrush::logError( $error ); - trigger_error( __METHOD__ . ": $error\n", E_USER_NOTICE ); - } - } - else { - // Create config file. - csscrush::log( 'Creating cache data file.' ); - } - file_put_contents( $process->cacheFilePath, json_encode( array() ) ); - } - - return $cache_data; - } - - static public function saveCacheData () { - - $process = csscrush::$process; - - // Need to store the current path so we can check we're using the right config path later - $process->cacheData[ 'originPath' ] = $process->cacheFilePath; - - csscrush::log( 'Saving config.' ); - file_put_contents( $process->cacheFilePath, json_encode( $process->cacheData ) ); - } - - static final function registerInputFile ( $file ) { - - $input = csscrush::$process->input; - - $input->filename = basename( $file ); - $input->path = "$input->dir/$input->filename"; - - if ( ! file_exists( $input->path ) ) { - - // On failure return false. - $error = "Input file '$input->filename' not found."; - csscrush::logError( $error ); - trigger_error( __METHOD__ . ": $error\n", E_USER_WARNING ); - return false; - } - else { - // Capture the modified time. - $input->mtime = filemtime( $input->path ); - return true; - } - } + // Start off with the input file then add imported files + $all_files = array( $input->mtime ); + + if ( file_exists( $existingfile->path ) && isset( $process->cacheData[ $process->output->filename ] ) ) { + + // File exists and has config + csscrush::log( 'Cached file is registered.' ); + + foreach ( $process->cacheData[ $existingfile->filename ][ 'imports' ] as $import_file ) { + + // Check if this is docroot relative or input dir relative + $root = strpos( $import_file, '/' ) === 0 ? $config->docRoot : $process->input->dir; + $import_filepath = realpath( $root ) . "/$import_file"; + + if ( file_exists( $import_filepath ) ) { + $all_files[] = filemtime( $import_filepath ); + } + else { + // File has been moved, remove old file and skip to compile + csscrush::log( 'Import file has been moved, removing existing file.' ); + unlink( $existingfile->path ); + return false; + } + } + + // Cast because the cached options may be a stdClass if an IO adapter has been used. + $existing_options = (array) $process->cacheData[ $existingfile->filename ][ 'options' ]; + $existing_datesum = $process->cacheData[ $existingfile->filename ][ 'datem_sum' ]; + + $options_unchanged = true; + foreach ( $existing_options as $key => &$value ) { + if ( $existing_options[ $key ] !== $options->{ $key } ) { + $options_unchanged = false; + break; + } + } + $files_unchanged = $existing_datesum == array_sum( $all_files ); + + if ( $options_unchanged && $files_unchanged ) { + + // Files have not been modified and config is the same: return the old file + csscrush::log( "Files and options have not been modified, returning existing + file '$existingfile->URL'." ); + return $existingfile->URL . ( $options->versioning !== false ? "?$existing_datesum" : '' ); + } + else { + + // Remove old file and continue making a new one... + ! $options_unchanged && csscrush::log( 'Options have been modified.' ); + ! $files_unchanged && csscrush::log( 'Files have been modified.' ); + csscrush::log( 'Removing existing file.' ); + + unlink( $existingfile->path ); + } + } + else if ( file_exists( $existingfile->path ) ) { + // File exists but has no config + csscrush::log( 'File exists but no config, removing existing file.' ); + unlink( $existingfile->path ); + } + return false; + + } // foreach + + return false; + } + + static public function clearCache ( $dir ) { + + if ( empty( $dir ) ) { + $dir = dirname( __FILE__ ); + } + else if ( ! file_exists( $dir ) ) { + return; + } + + $configPath = $dir . '/' . csscrush::$process->cacheFilePath; + if ( file_exists( $configPath ) ) { + unlink( $configPath ); + } + + // Remove any compiled files + $suffix = '.crush.css'; + $suffixLength = strlen( $suffix ); + + foreach ( scandir( $dir ) as $file ) { + if ( + strpos( $file, $suffix ) === strlen( $file ) - $suffixLength + ) { + unlink( $dir . "/{$file}" ); + } + } + } + + static public function getCacheData () { + + $config = csscrush::$config; + $process = csscrush::$process; + + if ( + file_exists( $process->cacheFilePath ) && + $process->cacheData && + $process->cacheData[ 'originPath' ] == $process->cacheFilePath + ) { + // Already loaded and config file exists in the current directory + return; + } + + $cache_data_exists = file_exists( $process->cacheFilePath ); + $cache_data_file_is_writable = $cache_data_exists ? is_writable( $process->cacheFilePath ) : false; + $cache_data = array(); + + if ( + $cache_data_exists && + $cache_data_file_is_writable && + $cache_data = json_decode( file_get_contents( $process->cacheFilePath ), true ) + ) { + // Successfully loaded config file. + csscrush::log( 'Cache data loaded.' ); + } + else { + // Config file may exist but not be writable (may not be visible in some ftp situations?) + if ( $cache_data_exists ) { + if ( ! @unlink( $process->cacheFilePath ) ) { + + $error = "Could not delete config data file."; + csscrush::logError( $error ); + trigger_error( __METHOD__ . ": $error\n", E_USER_NOTICE ); + } + } + else { + // Create config file. + csscrush::log( 'Creating cache data file.' ); + } + file_put_contents( $process->cacheFilePath, json_encode( array() ) ); + } + + return $cache_data; + } + + static public function saveCacheData () { + + $process = csscrush::$process; + + // Need to store the current path so we can check we're using the right config path later + $process->cacheData[ 'originPath' ] = $process->cacheFilePath; + + csscrush::log( 'Saving config.' ); + file_put_contents( $process->cacheFilePath, json_encode( $process->cacheData ) ); + } + + static final function registerInputFile ( $file ) { + + $input = csscrush::$process->input; + + $input->filename = basename( $file ); + $input->path = "$input->dir/$input->filename"; + + if ( ! file_exists( $input->path ) ) { + + // On failure return false. + $error = "Input file '$input->filename' not found."; + csscrush::logError( $error ); + trigger_error( __METHOD__ . ": $error\n", E_USER_WARNING ); + return false; + } + else { + // Capture the modified time. + $input->mtime = filemtime( $input->path ); + return true; + } + } } diff --git a/lib/Importer.php b/lib/Importer.php index a99ae49..37d63f6 100644 --- a/lib/Importer.php +++ b/lib/Importer.php @@ -6,373 +6,373 @@ */ class csscrush_importer { - static public function hostfile () { - - $config = csscrush::$config; - $process = csscrush::$process; - $options = $process->options; - $regex = csscrush_regex::$patt; - $input = $process->input; - - // Keep track of all import file info for cache data. - $mtimes = array(); - $filenames = array(); - - $str = ''; - $prepend_file_contents = ''; - - // The prepend file. - if ( $prepend_file = csscrush_util::find( 'Prepend-local.css', 'Prepend.css' ) ) { - $prepend_file_contents = file_get_contents( $prepend_file ); - $process->currentFile = 'file://' . $prepend_file; - - // If there's a parsing error inside the prepend file, wipe $prepend_file_contents. - if ( ! self::prepareForStream( $prepend_file_contents ) ) { - $prepend_file_contents = ''; - } - } - - // Resolve main input; a string of css or a file. - if ( isset( $input->string ) ) { - $str .= $input->string; - $process->currentFile = 'inline css'; - } - else { - $str .= file_get_contents( $input->path ); - $process->currentFile = 'file://' . $input->path; - } - - // If there's a parsing error go no further. - if ( ! self::prepareForStream( $str ) ) { - return $str; - } - - // Prepend any prepend file contents here. - $str = $prepend_file_contents . $str; - - // This may be set non-zero during the script if an absolute @import URL is encountered. - $search_offset = 0; - - // Recurses until the nesting heirarchy is flattened and all import files are inlined. - while ( preg_match( $regex->import, $str, $match, PREG_OFFSET_CAPTURE, $search_offset ) ) { - - $match_len = strlen( $match[0][0] ); - $match_start = $match[0][1]; - $match_end = $match_start + $match_len; - - // If just stripping the import statements - if ( isset( $input->importIgnore ) ) { - $str = substr_replace( $str, '', $match_start, $match_len ); - continue; - } - - // Fetch the URL object. - $url = csscrush_url::get( $match[1][0] ); - - // Pass over protocoled import urls. - if ( $url->protocol ) { - $search_offset = $match_end; - continue; - } - - // The media context (if specified). - $media_context = trim( $match[2][0] ); - - // Create import object. - $import = (object) array(); - $import->url = $url; - $import->mediaContext = $media_context; - - // Resolve import realpath. - if ( $url->isRooted ) { - $import->path = realpath( $config->docRoot . $import->url->value ); - } - else { - $import->path = realpath( "$input->dir/{$import->url->value}" ); - } - - // Get the import contents, if unsuccessful just continue with the import line removed. - if ( ! ( $import->content = @file_get_contents( $import->path ) ) ) { - csscrush::log( "Import file '{$import->url->value}' not found" ); - $str = substr_replace( $str, '', $match_start, $match_len ); - continue; - } - - // Import file opened successfully so we process it: - // - We need to resolve import statement urls in all imported files since - // they will be brought inline with the hostfile - $process->currentFile = 'file://' . $import->path; - - // If there are unmatched brackets inside the import, strip it. - if ( ! self::prepareForStream( $import->content ) ) { - $str = substr_replace( $str, '', $match_start, $match_len ); - continue; - } - - $import->dir = dirname( $import->url->value ); - - // Store import file info for cache validation. - $mtimes[] = filemtime( $import->path ); - $filenames[] = $import->url->value; - - // Alter all the @import urls to be paths relative to the hostfile. - foreach ( csscrush_regex::matchAll( $regex->import, $import->content ) as $m ) { - - // Fetch the matched URL. - $url2 = csscrush_url::get( $m[1][0] ); - - // Try to resolve absolute paths. - // On failure strip the @import statement. - if ( $url2->isRooted ) { - $url2->resolveRootedPath(); - } - else { - $url2->prepend( "$import->dir/" ); - } - } - - // Optionally rewrite relative url and custom function data-uri references. - if ( $options->rewrite_import_urls ) { - self::rewriteImportedUrls( $import ); - } - - // Add media context if it exists - if ( $import->mediaContext ) { - $import->content = "@media $import->mediaContext {{$import->content}}"; - } - - $str = substr_replace( $str, $import->content, $match_start, $match_len ); - } - - // Save only if caching is on and the hostfile object is associated with a real file. - if ( $input->path && $process->options->cache ) { - - // Write to config. - $process->cacheData[ $process->output->filename ] = array( - 'imports' => $filenames, - 'datem_sum' => array_sum( $mtimes ) + $input->mtime, - 'options' => $options, - ); - - // Save config changes. - $process->ioCall( 'saveCacheData' ); - } - - return $str; - } - - static protected function rewriteImportedUrls ( $import ) { - - $link = csscrush_util::getLinkBetweenDirs( - csscrush::$process->input->dir, dirname( $import->path ) ); - - if ( empty( $link ) ) { - return; - } - - // Match all urls that are not imports. - preg_match_all( '#(?content, $matches ); - - foreach ( $matches[0] as $token ) { - - // Fetch the matched URL. - $url = csscrush_url::get( $token ); - - if ( $url->isRelative ) { - // Prepend the relative url prefix. - $url->prepend( $link ); - } - } - } - - static protected function prepareForStream ( &$str ) { - - $regex = csscrush_regex::$patt; - $process = csscrush::$process; - - // Convert all end-of-lines to unix style. - $str = preg_replace( '~\r\n?~', "\n", $str ); - - $str = preg_replace_callback( $regex->commentAndString, - array( 'self', 'cb_extractCommentAndString' ), $str ); - - // If @charset is set store it. - if ( preg_match( $regex->charset, $str, $m ) ) { - $replace = ''; - if ( ! $process->charset ) { - // Keep track of newlines for line tracing. - $replace = str_repeat( "\n", substr_count( $m[0], "\n" ) ); - $process->charset = trim( $process->fetchToken( $m[1] ), '"\'' ); - } - $str = preg_replace( $regex->charset, $replace, $str ); - } - - // Catch obvious typing errors. - $parse_errors = array(); - $current_file = $process->currentFile; - $balanced_parens = substr_count( $str, "(" ) === substr_count( $str, ")" ); - $balanced_curlies = substr_count( $str, "{" ) === substr_count( $str, "}" ); - - if ( ! $balanced_parens ) { - $parse_errors[] = "Unmatched '(' in $current_file."; - } - if ( ! $balanced_curlies ) { - $parse_errors[] = "Unmatched '{' in $current_file."; - } - - if ( $parse_errors ) { - foreach ( $parse_errors as $error_msg ) { - csscrush::logError( $error_msg ); - trigger_error( "$error_msg\n", E_USER_WARNING ); - } - return false; - } - - // Optionally add tracing stubs. - if ( in_array( 'stubs', $process->options->trace ) ) { - self::addTracingStubs( $str ); - } - - // Strip unneeded whitespace. - $str = csscrush_util::normalizeWhiteSpace( $str ); - - self::captureUrls( $str ); - - return true; - } - - static protected function captureUrls ( &$str ) { - - $patt = '# - @import\x{20}(\?s\d+\?) - | - (?label, $str ); - } - // Match parenthesis if not a string token. - elseif ( - preg_match( csscrush_regex::$patt->balancedParens, $str, $inner_m, PREG_OFFSET_CAPTURE, $outer_offset ) - ) { - $url = new csscrush_url(http://github.com/$inner_m[1][0]); - $func_name = strtolower( $outer_m[2][0] ); - $url->convertToData = 'data-uri' === $func_name; - $str = substr_replace( $str, $url->label, $outer_offset, - strlen( $func_name ) + strlen( $inner_m[0][0] ) ); - } - // If brackets cannot be matched, skip over the original match. - else { - $offset += strlen( $outer_m[0][0] ); - } - } - } - - static protected function cb_extractCommentAndString ( $match ) { - - $full_match = $match[0]; - $process = csscrush::$process; - - // We return the newlines to maintain line numbering when tracing. - $newlines = str_repeat( "\n", substr_count( $full_match, "\n" ) ); - - if ( strpos( $full_match, '/*' ) === 0 ) { - - // Bail without storing comment if in debug mode or a private comment. - if ( - $process->options->minify || - strpos( $full_match, '/*$' ) === 0 - ) { - return $newlines; - } - - // Fix broken comments as they will break any subsquent - // imported files that are inlined. - if ( ! preg_match( '!\*/$!', $full_match ) ) { - $full_match .= '*/'; - } - $label = $process->addToken( $full_match, 'c' ); - } - else { - - // Fix broken strings as they will break any subsquent - // imported files that are inlined. - if ( $full_match[0] !== $full_match[ strlen( $full_match )-1 ] ) { - $full_match .= $full_match[0]; - } - $label = $process->addToken( $full_match, 's' ); - } - - return $newlines . $label; - } - - static protected function addTracingStubs ( &$str ) { - - $selector_patt = '! (^|;|\})+ ([^;{}]+) (\{) !xmS'; - $token_or_whitespace = '!(\s*\?c\d+\?\s*|\s+)!S'; - - $matches = csscrush_regex::matchAll( $selector_patt, $str ); - - // Start from last match and move backwards. - while ( $m = array_pop( $matches ) ) { - - // Shortcuts for readability. - list( $full_match, $before, $content, $after ) = $m; - $full_match_text = $full_match[0]; - $full_match_start = $full_match[1]; - - // The correct before string. - $before = substr( $full_match_text, 0, $content[1] - $full_match_start ); - - // Split the matched selector part. - $content_parts = preg_split( $token_or_whitespace, $content[0], null, - PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY ); - - foreach ( $content_parts as $part ) { - - if ( ! preg_match( $token_or_whitespace, $part ) ) { - - // Match to a valid selector. - if ( preg_match( '!^([^@]|@(?:page|abstract))!iS', $part ) ) { - - // Count line breaks between the start of stream and - // the matched selector to get the line number. - $selector_index = $full_match_start + strlen( $before ); - $line_num = 1; - $str_before = ""; - if ( $selector_index ) { - $str_before = substr( $str, 0, $selector_index ); - $line_num = substr_count( $str_before, "\n" ) + 1; - } - - // Get the currently processed file path, and escape it. - $current_file = str_replace( ' ', '%20', csscrush::$process->currentFile ); - $current_file = preg_replace( '![^\w-]!', '\\\\$0', $current_file ); - - // Splice in tracing stub. - $label = csscrush::$process->addToken( "@media -sass-debug-info{filename{font-family:$current_file}line{font-family:\\00003$line_num}}", 't' ); - - $str = $str_before . $label . substr( $str, $selector_index ); - } - else { - // Not matched as a valid selector, move on. - continue 2; - } - break; - } - - // Append split segment to $before. - $before .= $part; - } - } - } + static public function hostfile () { + + $config = csscrush::$config; + $process = csscrush::$process; + $options = $process->options; + $regex = csscrush_regex::$patt; + $input = $process->input; + + // Keep track of all import file info for cache data. + $mtimes = array(); + $filenames = array(); + + $str = ''; + $prepend_file_contents = ''; + + // The prepend file. + if ( $prepend_file = csscrush_util::find( 'Prepend-local.css', 'Prepend.css' ) ) { + $prepend_file_contents = file_get_contents( $prepend_file ); + $process->currentFile = 'file://' . $prepend_file; + + // If there's a parsing error inside the prepend file, wipe $prepend_file_contents. + if ( ! self::prepareForStream( $prepend_file_contents ) ) { + $prepend_file_contents = ''; + } + } + + // Resolve main input; a string of css or a file. + if ( isset( $input->string ) ) { + $str .= $input->string; + $process->currentFile = 'inline css'; + } + else { + $str .= file_get_contents( $input->path ); + $process->currentFile = 'file://' . $input->path; + } + + // If there's a parsing error go no further. + if ( ! self::prepareForStream( $str ) ) { + return $str; + } + + // Prepend any prepend file contents here. + $str = $prepend_file_contents . $str; + + // This may be set non-zero during the script if an absolute @import URL is encountered. + $search_offset = 0; + + // Recurses until the nesting heirarchy is flattened and all import files are inlined. + while ( preg_match( $regex->import, $str, $match, PREG_OFFSET_CAPTURE, $search_offset ) ) { + + $match_len = strlen( $match[0][0] ); + $match_start = $match[0][1]; + $match_end = $match_start + $match_len; + + // If just stripping the import statements + if ( isset( $input->importIgnore ) ) { + $str = substr_replace( $str, '', $match_start, $match_len ); + continue; + } + + // Fetch the URL object. + $url = csscrush_url::get( $match[1][0] ); + + // Pass over protocoled import urls. + if ( $url->protocol ) { + $search_offset = $match_end; + continue; + } + + // The media context (if specified). + $media_context = trim( $match[2][0] ); + + // Create import object. + $import = (object) array(); + $import->url = $url; + $import->mediaContext = $media_context; + + // Resolve import realpath. + if ( $url->isRooted ) { + $import->path = realpath( $config->docRoot . $import->url->value ); + } + else { + $import->path = realpath( "$input->dir/{$import->url->value}" ); + } + + // Get the import contents, if unsuccessful just continue with the import line removed. + if ( ! ( $import->content = @file_get_contents( $import->path ) ) ) { + csscrush::log( "Import file '{$import->url->value}' not found" ); + $str = substr_replace( $str, '', $match_start, $match_len ); + continue; + } + + // Import file opened successfully so we process it: + // - We need to resolve import statement urls in all imported files since + // they will be brought inline with the hostfile + $process->currentFile = 'file://' . $import->path; + + // If there are unmatched brackets inside the import, strip it. + if ( ! self::prepareForStream( $import->content ) ) { + $str = substr_replace( $str, '', $match_start, $match_len ); + continue; + } + + $import->dir = dirname( $import->url->value ); + + // Store import file info for cache validation. + $mtimes[] = filemtime( $import->path ); + $filenames[] = $import->url->value; + + // Alter all the @import urls to be paths relative to the hostfile. + foreach ( csscrush_regex::matchAll( $regex->import, $import->content ) as $m ) { + + // Fetch the matched URL. + $url2 = csscrush_url::get( $m[1][0] ); + + // Try to resolve absolute paths. + // On failure strip the @import statement. + if ( $url2->isRooted ) { + $url2->resolveRootedPath(); + } + else { + $url2->prepend( "$import->dir/" ); + } + } + + // Optionally rewrite relative url and custom function data-uri references. + if ( $options->rewrite_import_urls ) { + self::rewriteImportedUrls( $import ); + } + + // Add media context if it exists + if ( $import->mediaContext ) { + $import->content = "@media $import->mediaContext {{$import->content}}"; + } + + $str = substr_replace( $str, $import->content, $match_start, $match_len ); + } + + // Save only if caching is on and the hostfile object is associated with a real file. + if ( $input->path && $process->options->cache ) { + + // Write to config. + $process->cacheData[ $process->output->filename ] = array( + 'imports' => $filenames, + 'datem_sum' => array_sum( $mtimes ) + $input->mtime, + 'options' => $options, + ); + + // Save config changes. + $process->ioCall( 'saveCacheData' ); + } + + return $str; + } + + static protected function rewriteImportedUrls ( $import ) { + + $link = csscrush_util::getLinkBetweenDirs( + csscrush::$process->input->dir, dirname( $import->path ) ); + + if ( empty( $link ) ) { + return; + } + + // Match all urls that are not imports. + preg_match_all( '#(?content, $matches ); + + foreach ( $matches[0] as $token ) { + + // Fetch the matched URL. + $url = csscrush_url::get( $token ); + + if ( $url->isRelative ) { + // Prepend the relative url prefix. + $url->prepend( $link ); + } + } + } + + static protected function prepareForStream ( &$str ) { + + $regex = csscrush_regex::$patt; + $process = csscrush::$process; + + // Convert all end-of-lines to unix style. + $str = preg_replace( '~\r\n?~', "\n", $str ); + + $str = preg_replace_callback( $regex->commentAndString, + array( 'self', 'cb_extractCommentAndString' ), $str ); + + // If @charset is set store it. + if ( preg_match( $regex->charset, $str, $m ) ) { + $replace = ''; + if ( ! $process->charset ) { + // Keep track of newlines for line tracing. + $replace = str_repeat( "\n", substr_count( $m[0], "\n" ) ); + $process->charset = trim( $process->fetchToken( $m[1] ), '"\'' ); + } + $str = preg_replace( $regex->charset, $replace, $str ); + } + + // Catch obvious typing errors. + $parse_errors = array(); + $current_file = $process->currentFile; + $balanced_parens = substr_count( $str, "(" ) === substr_count( $str, ")" ); + $balanced_curlies = substr_count( $str, "{" ) === substr_count( $str, "}" ); + + if ( ! $balanced_parens ) { + $parse_errors[] = "Unmatched '(' in $current_file."; + } + if ( ! $balanced_curlies ) { + $parse_errors[] = "Unmatched '{' in $current_file."; + } + + if ( $parse_errors ) { + foreach ( $parse_errors as $error_msg ) { + csscrush::logError( $error_msg ); + trigger_error( "$error_msg\n", E_USER_WARNING ); + } + return false; + } + + // Optionally add tracing stubs. + if ( in_array( 'stubs', $process->options->trace ) ) { + self::addTracingStubs( $str ); + } + + // Strip unneeded whitespace. + $str = csscrush_util::normalizeWhiteSpace( $str ); + + self::captureUrls( $str ); + + return true; + } + + static protected function captureUrls ( &$str ) { + + $patt = '# + @import\x{20}(\?s\d+\?) + | + (?label, $str ); + } + // Match parenthesis if not a string token. + elseif ( + preg_match( csscrush_regex::$patt->balancedParens, $str, $inner_m, PREG_OFFSET_CAPTURE, $outer_offset ) + ) { + $url = new csscrush_url(http://github.com/$inner_m[1][0]); + $func_name = strtolower( $outer_m[2][0] ); + $url->convertToData = 'data-uri' === $func_name; + $str = substr_replace( $str, $url->label, $outer_offset, + strlen( $func_name ) + strlen( $inner_m[0][0] ) ); + } + // If brackets cannot be matched, skip over the original match. + else { + $offset += strlen( $outer_m[0][0] ); + } + } + } + + static protected function cb_extractCommentAndString ( $match ) { + + $full_match = $match[0]; + $process = csscrush::$process; + + // We return the newlines to maintain line numbering when tracing. + $newlines = str_repeat( "\n", substr_count( $full_match, "\n" ) ); + + if ( strpos( $full_match, '/*' ) === 0 ) { + + // Bail without storing comment if in debug mode or a private comment. + if ( + $process->options->minify || + strpos( $full_match, '/*$' ) === 0 + ) { + return $newlines; + } + + // Fix broken comments as they will break any subsquent + // imported files that are inlined. + if ( ! preg_match( '!\*/$!', $full_match ) ) { + $full_match .= '*/'; + } + $label = $process->addToken( $full_match, 'c' ); + } + else { + + // Fix broken strings as they will break any subsquent + // imported files that are inlined. + if ( $full_match[0] !== $full_match[ strlen( $full_match )-1 ] ) { + $full_match .= $full_match[0]; + } + $label = $process->addToken( $full_match, 's' ); + } + + return $newlines . $label; + } + + static protected function addTracingStubs ( &$str ) { + + $selector_patt = '! (^|;|\})+ ([^;{}]+) (\{) !xmS'; + $token_or_whitespace = '!(\s*\?c\d+\?\s*|\s+)!S'; + + $matches = csscrush_regex::matchAll( $selector_patt, $str ); + + // Start from last match and move backwards. + while ( $m = array_pop( $matches ) ) { + + // Shortcuts for readability. + list( $full_match, $before, $content, $after ) = $m; + $full_match_text = $full_match[0]; + $full_match_start = $full_match[1]; + + // The correct before string. + $before = substr( $full_match_text, 0, $content[1] - $full_match_start ); + + // Split the matched selector part. + $content_parts = preg_split( $token_or_whitespace, $content[0], null, + PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY ); + + foreach ( $content_parts as $part ) { + + if ( ! preg_match( $token_or_whitespace, $part ) ) { + + // Match to a valid selector. + if ( preg_match( '!^([^@]|@(?:page|abstract))!iS', $part ) ) { + + // Count line breaks between the start of stream and + // the matched selector to get the line number. + $selector_index = $full_match_start + strlen( $before ); + $line_num = 1; + $str_before = ""; + if ( $selector_index ) { + $str_before = substr( $str, 0, $selector_index ); + $line_num = substr_count( $str_before, "\n" ) + 1; + } + + // Get the currently processed file path, and escape it. + $current_file = str_replace( ' ', '%20', csscrush::$process->currentFile ); + $current_file = preg_replace( '![^\w-]!', '\\\\$0', $current_file ); + + // Splice in tracing stub. + $label = csscrush::$process->addToken( "@media -sass-debug-info{filename{font-family:$current_file}line{font-family:\\00003$line_num}}", 't' ); + + $str = $str_before . $label . substr( $str, $selector_index ); + } + else { + // Not matched as a valid selector, move on. + continue 2; + } + break; + } + + // Append split segment to $before. + $before .= $part; + } + } + } } diff --git a/lib/Mixin.php b/lib/Mixin.php index dbb1fea..54fe5f2 100644 --- a/lib/Mixin.php +++ b/lib/Mixin.php @@ -6,173 +6,173 @@ */ class csscrush_mixin { - public $declarationsTemplate = array(); + public $declarationsTemplate = array(); - public $arguments; + public $arguments; - public $data = array(); + public $data = array(); - public function __construct ( $block ) { + public function __construct ( $block ) { - // Strip comment markers - $block = csscrush_util::stripCommentTokens( $block ); + // Strip comment markers + $block = csscrush_util::stripCommentTokens( $block ); - // Prepare the arguments object - $this->arguments = new csscrush_arglist( $block ); + // Prepare the arguments object + $this->arguments = new csscrush_arglist( $block ); - // Re-assign with the parsed arguments string - $block = $this->arguments->string; + // Re-assign with the parsed arguments string + $block = $this->arguments->string; - // Split the block around semi-colons. - $declarations = preg_split( '!\s*;\s*!', trim( $block ), null, PREG_SPLIT_NO_EMPTY ); + // Split the block around semi-colons. + $declarations = preg_split( '!\s*;\s*!', trim( $block ), null, PREG_SPLIT_NO_EMPTY ); - foreach ( $declarations as $raw_declaration ) { + foreach ( $declarations as $raw_declaration ) { - $colon = strpos( $raw_declaration, ':' ); - if ( $colon === -1 ) { - continue; - } + $colon = strpos( $raw_declaration, ':' ); + if ( $colon === -1 ) { + continue; + } - // Store template declarations as arrays as they are copied by value not reference - $declaration = array(); + // Store template declarations as arrays as they are copied by value not reference + $declaration = array(); - $declaration['property'] = trim( substr( $raw_declaration, 0, $colon ) ); - $declaration['value'] = trim( substr( $raw_declaration, $colon + 1 ) ); + $declaration['property'] = trim( substr( $raw_declaration, 0, $colon ) ); + $declaration['value'] = trim( substr( $raw_declaration, $colon + 1 ) ); - if ( $declaration['property'] === 'mixin' ) { + if ( $declaration['property'] === 'mixin' ) { - // Mixin can contain other mixins if they are available - if ( $mixin_declarations = csscrush_mixin::parseValue( $declaration['value'] ) ) { + // Mixin can contain other mixins if they are available + if ( $mixin_declarations = csscrush_mixin::parseValue( $declaration['value'] ) ) { - // Add mixin result to the stack - $this->declarationsTemplate = array_merge( $this->declarationsTemplate, $mixin_declarations ); - } - } - elseif ( ! empty( $declaration['value'] ) ) { - $this->declarationsTemplate[] = $declaration; - } - } + // Add mixin result to the stack + $this->declarationsTemplate = array_merge( $this->declarationsTemplate, $mixin_declarations ); + } + } + elseif ( ! empty( $declaration['value'] ) ) { + $this->declarationsTemplate[] = $declaration; + } + } - // Create data table for the mixin. - // Values that use arg() are excluded - foreach ( $this->declarationsTemplate as &$declaration ) { - if ( ! preg_match( csscrush_regex::$patt->aToken, $declaration['value'] ) ) { - $this->data[ $declaration['property'] ] = $declaration['value']; - } - } - return ''; - } + // Create data table for the mixin. + // Values that use arg() are excluded + foreach ( $this->declarationsTemplate as &$declaration ) { + if ( ! preg_match( csscrush_regex::$patt->aToken, $declaration['value'] ) ) { + $this->data[ $declaration['property'] ] = $declaration['value']; + } + } + return ''; + } - public function call ( array $args ) { + public function call ( array $args ) { - // Copy the template - $declarations = $this->declarationsTemplate; + // Copy the template + $declarations = $this->declarationsTemplate; - if ( count( $this->arguments ) ) { + if ( count( $this->arguments ) ) { - list( $find, $replace ) = $this->arguments->getSubstitutions( $args ); + list( $find, $replace ) = $this->arguments->getSubstitutions( $args ); - // Place the arguments - foreach ( $declarations as &$declaration ) { - $declaration['value'] = str_replace( $find, $replace, $declaration['value'] ); - } - } + // Place the arguments + foreach ( $declarations as &$declaration ) { + $declaration['value'] = str_replace( $find, $replace, $declaration['value'] ); + } + } - // Return mixin declarations - return $declarations; - } + // Return mixin declarations + return $declarations; + } - static public function parseSingleValue ( $message ) { + static public function parseSingleValue ( $message ) { - $message = ltrim( $message ); - $mixin = null; - $non_mixin = null; + $message = ltrim( $message ); + $mixin = null; + $non_mixin = null; - // e.g. - // - mymixin( 50px, rgba(0,0,0,0), left 100% ) - // - abstract-rule - // - #selector + // e.g. + // - mymixin( 50px, rgba(0,0,0,0), left 100% ) + // - abstract-rule + // - #selector - // Test for leading name - if ( preg_match( '!^[\w-]+!', $message, $name_match ) ) { + // Test for leading name + if ( preg_match( '!^[\w-]+!', $message, $name_match ) ) { - $name = $name_match[0]; + $name = $name_match[0]; - if ( isset( csscrush::$process->mixins[ $name ] ) ) { + if ( isset( csscrush::$process->mixins[ $name ] ) ) { - // Mixin match - $mixin = csscrush::$process->mixins[ $name ]; - } - elseif ( isset( csscrush::$process->abstracts[ $name ] ) ) { + // Mixin match + $mixin = csscrush::$process->mixins[ $name ]; + } + elseif ( isset( csscrush::$process->abstracts[ $name ] ) ) { - // Abstract rule match - $non_mixin = csscrush::$process->abstracts[ $name ]; - } - } + // Abstract rule match + $non_mixin = csscrush::$process->abstracts[ $name ]; + } + } - // If no mixin or abstract rule matched, look for matching selector - if ( ! $mixin && ! $non_mixin ) { + // If no mixin or abstract rule matched, look for matching selector + if ( ! $mixin && ! $non_mixin ) { - $selector_test = csscrush_selector::makeReadableSelector( $message ); - // csscrush::log( array_keys( csscrush::$process->selectorRelationships ) ); + $selector_test = csscrush_selector::makeReadableSelector( $message ); + // csscrush::log( array_keys( csscrush::$process->selectorRelationships ) ); - if ( isset( csscrush::$process->selectorRelationships[ $selector_test ] ) ) { - $non_mixin = csscrush::$process->selectorRelationships[ $selector_test ]; - } - } + if ( isset( csscrush::$process->selectorRelationships[ $selector_test ] ) ) { + $non_mixin = csscrush::$process->selectorRelationships[ $selector_test ]; + } + } - // If no mixin matched, but matched alternative, use alternative - if ( ! $mixin ) { + // If no mixin matched, but matched alternative, use alternative + if ( ! $mixin ) { - if ( $non_mixin ) { + if ( $non_mixin ) { - // Return expected format - $result = array(); - foreach ( $non_mixin as $declaration ) { - $result[] = array( - 'property' => $declaration->property, - 'value' => $declaration->value, - ); - } - return $result; - } - else { + // Return expected format + $result = array(); + foreach ( $non_mixin as $declaration ) { + $result[] = array( + 'property' => $declaration->property, + 'value' => $declaration->value, + ); + } + return $result; + } + else { - // Nothing matches - return false; - } - } + // Nothing matches + return false; + } + } - // We have a valid mixin. - // Discard the name part and any wrapping parens and whitespace - $message = substr( $message, strlen( $name ) ); - $message = preg_replace( '!^\s*\(?\s*|\s*\)?\s*$!', '', $message ); + // We have a valid mixin. + // Discard the name part and any wrapping parens and whitespace + $message = substr( $message, strlen( $name ) ); + $message = preg_replace( '!^\s*\(?\s*|\s*\)?\s*$!', '', $message ); - // e.g. "value, rgba(0,0,0,0), left 100%" + // e.g. "value, rgba(0,0,0,0), left 100%" - // Determine what raw arguments there are to pass to the mixin - $args = array(); - if ( $message !== '' ) { - $args = csscrush_util::splitDelimList( $message ); - } + // Determine what raw arguments there are to pass to the mixin + $args = array(); + if ( $message !== '' ) { + $args = csscrush_util::splitDelimList( $message ); + } - return $mixin->call( $args ); - } + return $mixin->call( $args ); + } - static public function parseValue ( $message ) { + static public function parseValue ( $message ) { - // Call the mixin and return the list of declarations - $declarations = array(); + // Call the mixin and return the list of declarations + $declarations = array(); - foreach ( csscrush_util::splitDelimList( $message ) as $item ) { + foreach ( csscrush_util::splitDelimList( $message ) as $item ) { - if ( $result = self::parseSingleValue( $item ) ) { + if ( $result = self::parseSingleValue( $item ) ) { - $declarations = array_merge( $declarations, $result ); - } - } - return $declarations; - } + $declarations = array_merge( $declarations, $result ); + } + } + return $declarations; + } } @@ -183,33 +183,33 @@ static public function parseValue ( $message ) { */ class csscrush_fragment { - public $template = array(); + public $template = array(); - public $arguments; + public $arguments; - public function __construct ( $block ) { + public function __construct ( $block ) { - // Prepare the arguments object - $this->arguments = new csscrush_arglist( $block ); + // Prepare the arguments object + $this->arguments = new csscrush_arglist( $block ); - // Re-assign with the parsed arguments string - $this->template = $this->arguments->string; - } + // Re-assign with the parsed arguments string + $this->template = $this->arguments->string; + } - public function call ( array $args ) { + public function call ( array $args ) { - // Copy the template - $template = $this->template; + // Copy the template + $template = $this->template; - if ( count( $this->arguments ) ) { + if ( count( $this->arguments ) ) { - list( $find, $replace ) = $this->arguments->getSubstitutions( $args ); - $template = str_replace( $find, $replace, $template ); - } + list( $find, $replace ) = $this->arguments->getSubstitutions( $args ); + $template = str_replace( $find, $replace, $template ); + } - // Return fragment css - return $template; - } + // Return fragment css + return $template; + } } @@ -220,91 +220,91 @@ public function call ( array $args ) { */ class csscrush_arglist implements Countable { - // Positional argument default values - public $defaults = array(); + // Positional argument default values + public $defaults = array(); - // The number of expected arguments - public $argCount = 0; + // The number of expected arguments + public $argCount = 0; - // The string passed in with arg calls replaced by tokens - public $string; + // The string passed in with arg calls replaced by tokens + public $string; - public function __construct ( $str ) { + public function __construct ( $str ) { - // Parse all arg function calls in the passed string, callback creates default values - csscrush_function::executeCustomFunctions( $str, - csscrush_regex::$patt->argFunction, array( - 'arg' => array( $this, 'store' ) - )); - $this->string = $str; - } + // Parse all arg function calls in the passed string, callback creates default values + csscrush_function::executeCustomFunctions( $str, + csscrush_regex::$patt->argFunction, array( + 'arg' => array( $this, 'store' ) + )); + $this->string = $str; + } - public function store ( $raw_argument ) { + public function store ( $raw_argument ) { - $args = csscrush_function::parseArgsSimple( $raw_argument ); + $args = csscrush_function::parseArgsSimple( $raw_argument ); - // Match the argument index integer - if ( ! ctype_digit( $args[0] ) ) { + // Match the argument index integer + if ( ! ctype_digit( $args[0] ) ) { - // On failure to match an integer, return an empty string - return ''; - } + // On failure to match an integer, return an empty string + return ''; + } - // Get the match from the array - $position_match = $args[0]; + // Get the match from the array + $position_match = $args[0]; - // Store the default value - $default_value = isset( $args[1] ) ? $args[1] : null; + // Store the default value + $default_value = isset( $args[1] ) ? $args[1] : null; - if ( ! is_null( $default_value ) ) { - $this->defaults[ $position_match ] = trim( $default_value ); - } + if ( ! is_null( $default_value ) ) { + $this->defaults[ $position_match ] = trim( $default_value ); + } - // Update the mixin argument count - $argNumber = ( (int) $position_match ) + 1; - $this->argCount = max( $this->argCount, $argNumber ); + // Update the mixin argument count + $argNumber = ( (int) $position_match ) + 1; + $this->argCount = max( $this->argCount, $argNumber ); - // Return the argument token - return "?arg$position_match?"; - } + // Return the argument token + return "?arg$position_match?"; + } - public function getArgValue ( $index, &$args ) { + public function getArgValue ( $index, &$args ) { - // First lookup a passed value - if ( isset( $args[ $index ] ) && $args[ $index ] !== 'default' ) { - return $args[ $index ]; - } + // First lookup a passed value + if ( isset( $args[ $index ] ) && $args[ $index ] !== 'default' ) { + return $args[ $index ]; + } - // Get a default value - $default = isset( $this->defaults[ $index ] ) ? $this->defaults[ $index ] : ''; + // Get a default value + $default = isset( $this->defaults[ $index ] ) ? $this->defaults[ $index ] : ''; - // Recurse for nested arg() calls - if ( preg_match( csscrush_regex::$patt->aToken, $default, $m ) ) { + // Recurse for nested arg() calls + if ( preg_match( csscrush_regex::$patt->aToken, $default, $m ) ) { - $default = $this->getArgValue( (int) $m[1], $args ); - } - return $default; - } + $default = $this->getArgValue( (int) $m[1], $args ); + } + return $default; + } - public function getSubstitutions ( $args ) { + public function getSubstitutions ( $args ) { - $argIndexes = range( 0, $this->argCount-1 ); + $argIndexes = range( 0, $this->argCount-1 ); - // Create table of substitutions - $find = array(); - $replace = array(); + // Create table of substitutions + $find = array(); + $replace = array(); - foreach ( $argIndexes as $index ) { + foreach ( $argIndexes as $index ) { - $find[] = "?arg$index?"; - $replace[] = $this->getArgValue( $index, $args ); - } + $find[] = "?arg$index?"; + $replace[] = $this->getArgValue( $index, $args ); + } - return array( $find, $replace ); - } + return array( $find, $replace ); + } - public function count () { - return $this->argCount; - } + public function count () { + return $this->argCount; + } } diff --git a/lib/Plugin.php b/lib/Plugin.php index a40de04..a370623 100644 --- a/lib/Plugin.php +++ b/lib/Plugin.php @@ -6,54 +6,54 @@ */ class csscrush_plugin { - // The required prefix to all plugin function names - static public $prefix = 'csscrush__'; + // The required prefix to all plugin function names + static public $prefix = 'csscrush__'; - // The current loaded plugins - static protected $associated_hooks = array(); + // The current loaded plugins + static protected $associated_hooks = array(); - // Externally associate a hook with the plugin - static public function registerHook ( $plugin_name, $hook ) { + // Externally associate a hook with the plugin + static public function registerHook ( $plugin_name, $hook ) { - self::$associated_hooks[ $plugin_name ] = $hook; - } + self::$associated_hooks[ $plugin_name ] = $hook; + } - static public function enable ( $plugin_name ) { + static public function enable ( $plugin_name ) { - $plugin_function = self::$prefix . $plugin_name; + $plugin_function = self::$prefix . $plugin_name; - // Require the plugin file if it hasn't been already - if ( ! function_exists( $plugin_function ) ) { + // Require the plugin file if it hasn't been already + if ( ! function_exists( $plugin_function ) ) { - $path = csscrush::$config->location . "/plugins/$plugin_name.php"; + $path = csscrush::$config->location . "/plugins/$plugin_name.php"; - if ( ! file_exists( $path ) ) { + if ( ! file_exists( $path ) ) { - trigger_error( __METHOD__ . - ": $plugin_name plugin not found.\n", E_USER_NOTICE ); - return false; - } - require_once $path; - } + trigger_error( __METHOD__ . + ": $plugin_name plugin not found.\n", E_USER_NOTICE ); + return false; + } + require_once $path; + } - // If the plugin is associated with a hook, we make sure it is hooked - if ( isset( self::$associated_hooks[ $plugin_name ] ) ) { + // If the plugin is associated with a hook, we make sure it is hooked + if ( isset( self::$associated_hooks[ $plugin_name ] ) ) { - csscrush_hook::add( - self::$associated_hooks[ $plugin_name ], - $plugin_function ); - } - return true; - } + csscrush_hook::add( + self::$associated_hooks[ $plugin_name ], + $plugin_function ); + } + return true; + } - static public function disable ( $plugin_name ) { + static public function disable ( $plugin_name ) { - // If the plugin is associated with a hook, we 'un-hook' it - if ( isset( self::$associated_hooks[ $plugin_name ] ) ) { + // If the plugin is associated with a hook, we 'un-hook' it + if ( isset( self::$associated_hooks[ $plugin_name ] ) ) { - csscrush_hook::remove( - self::$associated_hooks[ $plugin_name ], - self::$prefix . str_replace( '-', '_', $plugin_name ) ); - } - } + csscrush_hook::remove( + self::$associated_hooks[ $plugin_name ], + self::$prefix . str_replace( '-', '_', $plugin_name ) ); + } + } } diff --git a/lib/Process.php b/lib/Process.php index 44f790d..ae1ec8d 100644 --- a/lib/Process.php +++ b/lib/Process.php @@ -6,1098 +6,1098 @@ */ class csscrush_process { - public function __construct ( $options ) { - - $config = csscrush::$config; - - // Load in aliases and plugins. - csscrush::loadAssets(); - - // Resolve process options. - $this->setOptions( $options ); - - // Initialize properties. - $this->uid = 0; - $this->cacheData = array(); - $this->mixins = array(); - $this->abstracts = array(); - $this->errors = array(); - $this->stat = array(); - $this->selectorRelationships = array(); - $this->charset = null; - $this->currentFile = null; - $this->tokens = (object) array( - 's' => array(), // Strings - 'c' => array(), // Comments - 'r' => array(), // Rules - 'p' => array(), // Parens - 'u' => array(), // URLs - 't' => array(), // Traces - ); - $this->variables = array(); - $this->misc = new stdclass(); - $this->input = new stdclass(); - $this->output = new stdclass(); - - // Copy config values. - $this->plugins = $config->plugins; - $this->aliases = $config->aliases; - $this->selectorAliases = $config->selectorAliases; - $this->selectorAliasesPatt = null; - - // Run process_init hook. - csscrush_hook::run( 'process_init' ); - } - - public function release () { - unset( - $this->tokens, - $this->variables, - $this->mixins, - $this->abstracts, - $this->selectorRelationships, - $this->misc, - $this->plugins, - $this->aliases, - $this->selectorAliases - ); - } - - // Establish the input and output directories and optionally test output dir. - public function setContext ( $input_dir, $test_output_dir = true ) { - - $doc_root = csscrush::$config->docRoot; - - if ( strpos( $input_dir, $doc_root ) !== 0 ) { - // Not a system path. - $input_dir = realpath( "$doc_root/$input_dir" ); - } - - // Initialise input object and store input directory. - $this->input->path = null; - $this->input->filename = null; - $this->input->dir = $input_dir; - $this->input->dirUrl = substr( $this->input->dir, strlen( $doc_root ) ); - - // Store reference to the output dir. - $this->output->dir = $this->ioCall( 'getOutputDir' ); - $this->output->dirUrl = substr( $this->output->dir, strlen( $doc_root ) ); - - // Test the output directory to see it exists and is writable. - $output_dir_ok = false; - if ( $test_output_dir ) { - $output_dir_ok = $this->ioCall( 'testOutputDir' ); - } - - // Setup the IO handler. - $this->ioCall( 'init' ); - - return $output_dir_ok; - } - - public function ioCall ( $method ) { - - // Fetch the argument list, shift off the first item - $args = func_get_args(); - array_shift( $args ); - - // The method address - $the_method = array( csscrush::$config->io, $method ); - - // Return the call result - return call_user_func_array( $the_method, $args ); - } - - public function setOptions ( $options ) { - - $config = csscrush::$config; - - if ( ! is_array( $options ) ) { - $options = array(); - } - - // Backwards compat for change option name from debug to minify. - if ( array_key_exists( 'debug', $options ) && ! array_key_exists( 'minify', $options ) ) { - $options[ 'minify' ] = ! $options[ 'debug' ]; - } - - // Resolve trace options. - if ( array_key_exists( 'trace', $options ) && ! is_array( $options[ 'trace' ] ) ) { - $options[ 'trace' ] = $options[ 'trace' ] ? array( 'stubs' ) : array(); - } - - // Keeping track of global vars internally to maintain cache integrity. - $options[ '_globalVars' ] = $config->vars; - - // Populate unset options with defaults. - $this->options = (object) ( $options + (array) $config->options ); - } - - - ############################# - # Tokens. - - public function createTokenLabel ( $type ) { - $counter = ++$this->uid; - return "?$type$counter?"; - } - - public function addToken ( $value, $type ) { - $label = $this->createTokenLabel( $type ); - $this->tokens->{ $type }[ $label ] = $value; - return $label; - } - - public function fetchToken ( $token ) { - $type = substr( $token, 1, 1 ); - $path =& $this->tokens->{ $type }; - if ( isset( $path[ $token ] ) ) { - return $path[ $token ]; - } - return null; - } - - public function releaseToken ( $token ) { - unset( $this->tokens->{ substr( $token, 1, 1 ) }[ $token ] ); - } - - public function restoreTokens ( $str, $type = 'p' ) { - - // Reference the token table. - $token_table =& $this->tokens->{ $type }; - - // Find matching tokens. - $matches = csscrush_regex::matchAll( csscrush_regex::$patt->{ "{$type}Token" }, $str ); - - foreach ( $matches as $m ) { - $token = $m[0][0]; - if ( isset( $token_table[ $token ] ) ) { - $str = str_replace( $token, $token_table[ $token ], $str ); - } - } - return $str; - } - - - ############################# - # Parens. - - public function captureParens ( &$str ) { - - static $callback; - if ( ! $callback ) { - $callback = create_function( '$m', 'return csscrush::$process->addToken( $m[0], \'p\' );' ); - } - $str = preg_replace_callback( csscrush_regex::$patt->balancedParens, $callback, $str ); - } - - public function restoreParens ( &$str, $release = true ) { - - $token_table =& $this->tokens->p; - - foreach ( csscrush_regex::matchAll( csscrush_regex::$patt->pToken, $str ) as $m ) { - $token = $m[0][0]; - if ( isset( $token_table[ $token ] ) ) { - $str = str_replace( $token, $token_table[ $token ], $str ); - if ( $release ) { - unset( $token_table[ $token ] ); - } - } - } - } - - - ############################# - # Boilerplate. - - protected function getBoilerplate () { - - $file = false; - $boilerplate_option = $this->options->boilerplate; - - if ( $boilerplate_option === true ) { - $file = csscrush_util::find( - 'CssCrush-local.boilerplate', 'CssCrush.boilerplate' ); - } - elseif ( is_string( $boilerplate_option ) ) { - if ( file_exists( $boilerplate_option ) ) { - $file = $boilerplate_option; - } - } - - // Return an empty string if no file is found. - if ( ! $file ) { - return ''; - } - - // Load the file - $boilerplate = file_get_contents( $file ); - - // Substitute any tags - if ( preg_match_all( '!\{\{([^}]+)\}\}!', $boilerplate, $boilerplate_matches ) ) { - - $replacements = array(); - foreach ( $boilerplate_matches[0] as $index => $tag ) { - $tag_name = $boilerplate_matches[1][$index]; - if ( $tag_name === 'datetime' ) { - $replacements[] = @date( 'Y-m-d H:i:s O' ); - } - elseif ( $tag_name === 'version' ) { - $replacements[] = 'v' . csscrush::$config->version; - } - else { - $replacements[] = '?'; - } - } - $boilerplate = str_replace( $boilerplate_matches[0], $replacements, $boilerplate ); - } - - // Pretty print. - $EOL = PHP_EOL; - $boilerplate = preg_split( '![\t ]*(\r\n?|\n)[\t ]*!S', $boilerplate ); - $boilerplate = array_map( 'trim', $boilerplate ); - $boilerplate = "$EOL * " . implode( "$EOL * ", $boilerplate ); - return "/*{$boilerplate}$EOL */$EOL"; - } - - - ############################# - # Aliases. - - static protected function applySelectorAliases ( &$str ) { - - if ( csscrush::$process->selectorAliases ) { - - static $callback; - if ( ! $callback ) { - $callback = create_function( '$m', - '$table =& csscrush::$process->selectorAliases; - return isset( $table[ $m[1] ] ) ? $table[ $m[1] ] : ""; - '); - } - $str = preg_replace_callback( - csscrush::$process->selectorAliasesPatt, $callback, $str ); - } - } - - protected function extractSelectorAliases () { - - static $callback; - if ( ! $callback ) { - $callback = create_function( '$m', - 'csscrush::$process->selectorAliases[ $m[1] ] = $m[2];' ); - } - - $this->stream->pregReplaceCallback( csscrush_regex::$patt->selectorAlias, $callback ); - - // Create the selector aliases pattern and store it. - if ( $this->selectorAliases ) { - $names = implode( '|', array_keys( $this->selectorAliases ) ); - $this->selectorAliasesPatt = '#\:(' . $names . ')\b(?!-)#iS'; - } - } - - protected function filterAliases () { - - $options = $this->options; - - // If a vendor target is given, we prune the aliases array - $vendor = $options->vendor_target; - - // Default vendor argument, use all aliases as normal - if ( 'all' === $vendor ) { - return; - } - - // For expicit 'none' argument turn off aliases - if ( 'none' === $vendor ) { - $this->aliases = array(); - return; - } - - // Normalize vendor_target argument - $vendor = '-' . str_replace( '-', '', $vendor ) . '-'; - - // Loop the aliases array, filter down to the target vendor - foreach ( $this->aliases as $group_name => $group_array ) { - - // Property/value aliases are special. - if ( 'values' === $group_name ) { - foreach ( $group_array as $property => $values ) { - $result = array(); - foreach ( $values as $value => $prefix_values ) { - foreach ( $prefix_values as $prefix ) { - if ( strpos( $prefix, $vendor ) === 0 ) { - $result[] = $prefix; - } - } - } - $this->aliases[ 'values' ][ $property ][ $value ] = $result; - } - continue; - } - - foreach ( $group_array as $alias_keyword => $prefix_array ) { - - $result = array(); - foreach ( $prefix_array as $prefix ) { - if ( strpos( $prefix, $vendor ) === 0 ) { - $result[] = $prefix; - } - } - // Prune the whole alias keyword if there is no result. - if ( empty( $result ) ) { - unset( $this->aliases[ $group_name ][ $alias_keyword ] ); - } - else { - $this->aliases[ $group_name ][ $alias_keyword ] = $result; - } - } - } - } - - - ############################# - # Plugins. - - protected function filterPlugins () { - - $options =& $this->options; - - // Load and unload plugins. - // Add option enabled plugins to the list. - if ( is_array( $options->enable ) ) { - foreach ( $options->enable as $plugin_name ) { - $this->plugins[ $plugin_name ] = true; - } - } - - // Remove option disabled plugins from the list, and disable them. - if ( $options->disable === 'all' ) { - $options->disable = array_keys( $config->plugins ); - } - if ( is_array( $options->disable ) ) { - foreach ( $options->disable as $plugin_name ) { - csscrush_plugin::disable( $plugin_name ); - unset( $this->plugins[ $plugin_name ] ); - } - } - - // Enable all plugins in the remaining list. - foreach ( $this->plugins as $plugin_name => $bool ) { - csscrush_plugin::enable( $plugin_name ); - } - } - - - ############################# - # Variables. - - protected function calculateVariables () { - - $regex = csscrush_regex::$patt; - $options = $this->options; - $config = csscrush::$config; - - $this->stream->pregReplaceCallback( $regex->variables, - array( 'csscrush_process', 'cb_extractVariables' ) ); - - // In-file variables override global variables. - $this->variables = array_merge( $config->vars, $this->variables ); - - // Runtime variables override in-file variables. - if ( ! empty( $options->vars ) ) { - $this->variables = array_merge( $this->variables, $options->vars ); - } - - // Place variables referenced inside variables. Excecute custom functions. - foreach ( $this->variables as $name => &$value ) { - - // Referenced variables. - $value = preg_replace_callback( $regex->varFunction, array( 'self', 'cb_placeVariables' ), $value ); - - // Variable values can be escaped from function parsing with a tilde prefix. - if ( strpos( $value, '~' ) !== 0 ) { - csscrush_function::executeCustomFunctions( $value ); - } - } - } - - protected function placeAllVariables () { - - // Place variables in main stream. - self::placeVariables( $this->stream->raw ); - - // Repeat above steps for variables embedded in string tokens. - foreach ( $this->tokens->s as $label => &$value ) { - self::placeVariables( $value ); - } - - // Repeat above steps for variables embedded in URL tokens. - foreach ( $this->tokens->u as $label => $url ) { - if ( self::placeVariables( $url->value ) ) { - // Re-evaluate $url->value if anything has been interpolated. - $url->evaluate(); - } - } - } - - static protected function placeVariables ( &$value ) { + public function __construct ( $options ) { + + $config = csscrush::$config; + + // Load in aliases and plugins. + csscrush::loadAssets(); + + // Resolve process options. + $this->setOptions( $options ); + + // Initialize properties. + $this->uid = 0; + $this->cacheData = array(); + $this->mixins = array(); + $this->abstracts = array(); + $this->errors = array(); + $this->stat = array(); + $this->selectorRelationships = array(); + $this->charset = null; + $this->currentFile = null; + $this->tokens = (object) array( + 's' => array(), // Strings + 'c' => array(), // Comments + 'r' => array(), // Rules + 'p' => array(), // Parens + 'u' => array(), // URLs + 't' => array(), // Traces + ); + $this->variables = array(); + $this->misc = new stdclass(); + $this->input = new stdclass(); + $this->output = new stdclass(); + + // Copy config values. + $this->plugins = $config->plugins; + $this->aliases = $config->aliases; + $this->selectorAliases = $config->selectorAliases; + $this->selectorAliasesPatt = null; + + // Run process_init hook. + csscrush_hook::run( 'process_init' ); + } + + public function release () { + unset( + $this->tokens, + $this->variables, + $this->mixins, + $this->abstracts, + $this->selectorRelationships, + $this->misc, + $this->plugins, + $this->aliases, + $this->selectorAliases + ); + } + + // Establish the input and output directories and optionally test output dir. + public function setContext ( $input_dir, $test_output_dir = true ) { + + $doc_root = csscrush::$config->docRoot; + + if ( strpos( $input_dir, $doc_root ) !== 0 ) { + // Not a system path. + $input_dir = realpath( "$doc_root/$input_dir" ); + } + + // Initialise input object and store input directory. + $this->input->path = null; + $this->input->filename = null; + $this->input->dir = $input_dir; + $this->input->dirUrl = substr( $this->input->dir, strlen( $doc_root ) ); + + // Store reference to the output dir. + $this->output->dir = $this->ioCall( 'getOutputDir' ); + $this->output->dirUrl = substr( $this->output->dir, strlen( $doc_root ) ); + + // Test the output directory to see it exists and is writable. + $output_dir_ok = false; + if ( $test_output_dir ) { + $output_dir_ok = $this->ioCall( 'testOutputDir' ); + } + + // Setup the IO handler. + $this->ioCall( 'init' ); + + return $output_dir_ok; + } + + public function ioCall ( $method ) { + + // Fetch the argument list, shift off the first item + $args = func_get_args(); + array_shift( $args ); + + // The method address + $the_method = array( csscrush::$config->io, $method ); + + // Return the call result + return call_user_func_array( $the_method, $args ); + } + + public function setOptions ( $options ) { + + $config = csscrush::$config; + + if ( ! is_array( $options ) ) { + $options = array(); + } + + // Backwards compat for change option name from debug to minify. + if ( array_key_exists( 'debug', $options ) && ! array_key_exists( 'minify', $options ) ) { + $options[ 'minify' ] = ! $options[ 'debug' ]; + } + + // Resolve trace options. + if ( array_key_exists( 'trace', $options ) && ! is_array( $options[ 'trace' ] ) ) { + $options[ 'trace' ] = $options[ 'trace' ] ? array( 'stubs' ) : array(); + } + + // Keeping track of global vars internally to maintain cache integrity. + $options[ '_globalVars' ] = $config->vars; + + // Populate unset options with defaults. + $this->options = (object) ( $options + (array) $config->options ); + } + + + ############################# + # Tokens. + + public function createTokenLabel ( $type ) { + $counter = ++$this->uid; + return "?$type$counter?"; + } + + public function addToken ( $value, $type ) { + $label = $this->createTokenLabel( $type ); + $this->tokens->{ $type }[ $label ] = $value; + return $label; + } + + public function fetchToken ( $token ) { + $type = substr( $token, 1, 1 ); + $path =& $this->tokens->{ $type }; + if ( isset( $path[ $token ] ) ) { + return $path[ $token ]; + } + return null; + } + + public function releaseToken ( $token ) { + unset( $this->tokens->{ substr( $token, 1, 1 ) }[ $token ] ); + } + + public function restoreTokens ( $str, $type = 'p' ) { + + // Reference the token table. + $token_table =& $this->tokens->{ $type }; + + // Find matching tokens. + $matches = csscrush_regex::matchAll( csscrush_regex::$patt->{ "{$type}Token" }, $str ); + + foreach ( $matches as $m ) { + $token = $m[0][0]; + if ( isset( $token_table[ $token ] ) ) { + $str = str_replace( $token, $token_table[ $token ], $str ); + } + } + return $str; + } + + + ############################# + # Parens. + + public function captureParens ( &$str ) { + + static $callback; + if ( ! $callback ) { + $callback = create_function( '$m', 'return csscrush::$process->addToken( $m[0], \'p\' );' ); + } + $str = preg_replace_callback( csscrush_regex::$patt->balancedParens, $callback, $str ); + } + + public function restoreParens ( &$str, $release = true ) { + + $token_table =& $this->tokens->p; + + foreach ( csscrush_regex::matchAll( csscrush_regex::$patt->pToken, $str ) as $m ) { + $token = $m[0][0]; + if ( isset( $token_table[ $token ] ) ) { + $str = str_replace( $token, $token_table[ $token ], $str ); + if ( $release ) { + unset( $token_table[ $token ] ); + } + } + } + } + + + ############################# + # Boilerplate. + + protected function getBoilerplate () { + + $file = false; + $boilerplate_option = $this->options->boilerplate; + + if ( $boilerplate_option === true ) { + $file = csscrush_util::find( + 'CssCrush-local.boilerplate', 'CssCrush.boilerplate' ); + } + elseif ( is_string( $boilerplate_option ) ) { + if ( file_exists( $boilerplate_option ) ) { + $file = $boilerplate_option; + } + } + + // Return an empty string if no file is found. + if ( ! $file ) { + return ''; + } + + // Load the file + $boilerplate = file_get_contents( $file ); + + // Substitute any tags + if ( preg_match_all( '!\{\{([^}]+)\}\}!', $boilerplate, $boilerplate_matches ) ) { + + $replacements = array(); + foreach ( $boilerplate_matches[0] as $index => $tag ) { + $tag_name = $boilerplate_matches[1][$index]; + if ( $tag_name === 'datetime' ) { + $replacements[] = @date( 'Y-m-d H:i:s O' ); + } + elseif ( $tag_name === 'version' ) { + $replacements[] = 'v' . csscrush::$config->version; + } + else { + $replacements[] = '?'; + } + } + $boilerplate = str_replace( $boilerplate_matches[0], $replacements, $boilerplate ); + } + + // Pretty print. + $EOL = PHP_EOL; + $boilerplate = preg_split( '![\t ]*(\r\n?|\n)[\t ]*!S', $boilerplate ); + $boilerplate = array_map( 'trim', $boilerplate ); + $boilerplate = "$EOL * " . implode( "$EOL * ", $boilerplate ); + return "/*{$boilerplate}$EOL */$EOL"; + } + + + ############################# + # Aliases. + + static protected function applySelectorAliases ( &$str ) { + + if ( csscrush::$process->selectorAliases ) { + + static $callback; + if ( ! $callback ) { + $callback = create_function( '$m', + '$table =& csscrush::$process->selectorAliases; + return isset( $table[ $m[1] ] ) ? $table[ $m[1] ] : ""; + '); + } + $str = preg_replace_callback( + csscrush::$process->selectorAliasesPatt, $callback, $str ); + } + } + + protected function extractSelectorAliases () { + + static $callback; + if ( ! $callback ) { + $callback = create_function( '$m', + 'csscrush::$process->selectorAliases[ $m[1] ] = $m[2];' ); + } + + $this->stream->pregReplaceCallback( csscrush_regex::$patt->selectorAlias, $callback ); + + // Create the selector aliases pattern and store it. + if ( $this->selectorAliases ) { + $names = implode( '|', array_keys( $this->selectorAliases ) ); + $this->selectorAliasesPatt = '#\:(' . $names . ')\b(?!-)#iS'; + } + } + + protected function filterAliases () { + + $options = $this->options; + + // If a vendor target is given, we prune the aliases array + $vendor = $options->vendor_target; + + // Default vendor argument, use all aliases as normal + if ( 'all' === $vendor ) { + return; + } + + // For expicit 'none' argument turn off aliases + if ( 'none' === $vendor ) { + $this->aliases = array(); + return; + } + + // Normalize vendor_target argument + $vendor = '-' . str_replace( '-', '', $vendor ) . '-'; + + // Loop the aliases array, filter down to the target vendor + foreach ( $this->aliases as $group_name => $group_array ) { + + // Property/value aliases are special. + if ( 'values' === $group_name ) { + foreach ( $group_array as $property => $values ) { + $result = array(); + foreach ( $values as $value => $prefix_values ) { + foreach ( $prefix_values as $prefix ) { + if ( strpos( $prefix, $vendor ) === 0 ) { + $result[] = $prefix; + } + } + } + $this->aliases[ 'values' ][ $property ][ $value ] = $result; + } + continue; + } + + foreach ( $group_array as $alias_keyword => $prefix_array ) { + + $result = array(); + foreach ( $prefix_array as $prefix ) { + if ( strpos( $prefix, $vendor ) === 0 ) { + $result[] = $prefix; + } + } + // Prune the whole alias keyword if there is no result. + if ( empty( $result ) ) { + unset( $this->aliases[ $group_name ][ $alias_keyword ] ); + } + else { + $this->aliases[ $group_name ][ $alias_keyword ] = $result; + } + } + } + } + + + ############################# + # Plugins. + + protected function filterPlugins () { + + $options =& $this->options; + + // Load and unload plugins. + // Add option enabled plugins to the list. + if ( is_array( $options->enable ) ) { + foreach ( $options->enable as $plugin_name ) { + $this->plugins[ $plugin_name ] = true; + } + } + + // Remove option disabled plugins from the list, and disable them. + if ( $options->disable === 'all' ) { + $options->disable = array_keys( $config->plugins ); + } + if ( is_array( $options->disable ) ) { + foreach ( $options->disable as $plugin_name ) { + csscrush_plugin::disable( $plugin_name ); + unset( $this->plugins[ $plugin_name ] ); + } + } + + // Enable all plugins in the remaining list. + foreach ( $this->plugins as $plugin_name => $bool ) { + csscrush_plugin::enable( $plugin_name ); + } + } + + + ############################# + # Variables. + + protected function calculateVariables () { + + $regex = csscrush_regex::$patt; + $options = $this->options; + $config = csscrush::$config; + + $this->stream->pregReplaceCallback( $regex->variables, + array( 'csscrush_process', 'cb_extractVariables' ) ); + + // In-file variables override global variables. + $this->variables = array_merge( $config->vars, $this->variables ); + + // Runtime variables override in-file variables. + if ( ! empty( $options->vars ) ) { + $this->variables = array_merge( $this->variables, $options->vars ); + } + + // Place variables referenced inside variables. Excecute custom functions. + foreach ( $this->variables as $name => &$value ) { + + // Referenced variables. + $value = preg_replace_callback( $regex->varFunction, array( 'self', 'cb_placeVariables' ), $value ); + + // Variable values can be escaped from function parsing with a tilde prefix. + if ( strpos( $value, '~' ) !== 0 ) { + csscrush_function::executeCustomFunctions( $value ); + } + } + } + + protected function placeAllVariables () { + + // Place variables in main stream. + self::placeVariables( $this->stream->raw ); + + // Repeat above steps for variables embedded in string tokens. + foreach ( $this->tokens->s as $label => &$value ) { + self::placeVariables( $value ); + } + + // Repeat above steps for variables embedded in URL tokens. + foreach ( $this->tokens->u as $label => $url ) { + if ( self::placeVariables( $url->value ) ) { + // Re-evaluate $url->value if anything has been interpolated. + $url->evaluate(); + } + } + } + + static protected function placeVariables ( &$value ) { - $regex = csscrush_regex::$patt; + $regex = csscrush_regex::$patt; - // Variables with no default value. - $value = preg_replace_callback( $regex->varFunction, - array( 'csscrush_process', 'cb_placeVariables' ), $value, -1, $count ); + // Variables with no default value. + $value = preg_replace_callback( $regex->varFunction, + array( 'csscrush_process', 'cb_placeVariables' ), $value, -1, $count ); - if ( strpos( $value, '$(' ) !== false ) { + if ( strpos( $value, '$(' ) !== false ) { - // Variables with default value. - csscrush_function::executeCustomFunctions( $value, $regex->varFunctionStart, - array( '$' => array( 'csscrush_process', 'cb_placeVariablesWithDefault' ) ) ); + // Variables with default value. + csscrush_function::executeCustomFunctions( $value, $regex->varFunctionStart, + array( '$' => array( 'csscrush_process', 'cb_placeVariablesWithDefault' ) ) ); - // Assume at least 1 replace. - $count = 1; - } + // Assume at least 1 replace. + $count = 1; + } - // If we know replacements have been made we may want to update $value. e.g URL tokens. - return $count; - } + // If we know replacements have been made we may want to update $value. e.g URL tokens. + return $count; + } - static public function cb_extractVariables ( $m ) { + static public function cb_extractVariables ( $m ) { - $regex = csscrush_regex::$patt; + $regex = csscrush_regex::$patt; - // Strip comment markers. - $block = trim( csscrush_util::stripCommentTokens( $m[2] ) ); + // Strip comment markers. + $block = trim( csscrush_util::stripCommentTokens( $m[2] ) ); - $pairs = preg_split( '!\s*;\s*!', $block, null, PREG_SPLIT_NO_EMPTY ); + $pairs = preg_split( '!\s*;\s*!', $block, null, PREG_SPLIT_NO_EMPTY ); - // Loop through the pairs. - foreach ( $pairs as $var ) { - $colon = strpos( $var, ':' ); - if ( $colon === -1 ) { - continue; - } - $name = trim( substr( $var, 0, $colon ) ); - $value = trim( substr( $var, $colon + 1 ) ); - csscrush::$process->variables[ trim( $name ) ] = $value; - } - } + // Loop through the pairs. + foreach ( $pairs as $var ) { + $colon = strpos( $var, ':' ); + if ( $colon === -1 ) { + continue; + } + $name = trim( substr( $var, 0, $colon ) ); + $value = trim( substr( $var, $colon + 1 ) ); + csscrush::$process->variables[ trim( $name ) ] = $value; + } + } - static protected function cb_placeVariables ( $m ) { - $variable_name = $m[1]; - if ( isset( csscrush::$process->variables[ $variable_name ] ) ) { - return csscrush::$process->variables[ $variable_name ]; - } - } + static protected function cb_placeVariables ( $m ) { + $variable_name = $m[1]; + if ( isset( csscrush::$process->variables[ $variable_name ] ) ) { + return csscrush::$process->variables[ $variable_name ]; + } + } - static public function cb_placeVariablesWithDefault ( $raw_arg ) { + static public function cb_placeVariablesWithDefault ( $raw_arg ) { - list( $name, $default_value ) = csscrush_function::parseArgsSimple( $raw_arg ); + list( $name, $default_value ) = csscrush_function::parseArgsSimple( $raw_arg ); - if ( isset( csscrush::$process->variables[ $name ] ) ) { - return csscrush::$process->variables[ $name ]; - } - else { - return $default_value; - } - } + if ( isset( csscrush::$process->variables[ $name ] ) ) { + return csscrush::$process->variables[ $name ]; + } + else { + return $default_value; + } + } - ############################# - # @ifdefine blocks. + ############################# + # @ifdefine blocks. - protected function resolveIfDefines () { + protected function resolveIfDefines () { - $matches = $this->stream->matchAll( csscrush_regex::$patt->ifDefine ); + $matches = $this->stream->matchAll( csscrush_regex::$patt->ifDefine ); - // Move through the matches last to first. - while ( $match = array_pop( $matches ) ) { + // Move through the matches last to first. + while ( $match = array_pop( $matches ) ) { - $curly_match = new csscrush_balancedMatch( $this->stream, $match[0][1] ); + $curly_match = new csscrush_balancedMatch( $this->stream, $match[0][1] ); - if ( ! $curly_match->match ) { - // Couldn't match the block. - continue; - } + if ( ! $curly_match->match ) { + // Couldn't match the block. + continue; + } - $negate = $match[1][1] != -1; - $name = $match[2][0]; - $name_defined = isset( $this->variables[ $name ] ); + $negate = $match[1][1] != -1; + $name = $match[2][0]; + $name_defined = isset( $this->variables[ $name ] ); - if ( ! $negate && $name_defined || $negate && ! $name_defined ) { - // Test resolved true so include the innards. - $curly_match->unWrap(); - } - else { - // Recontruct the stream without the innards. - $curly_match->replace( '' ); - } - } - } + if ( ! $negate && $name_defined || $negate && ! $name_defined ) { + // Test resolved true so include the innards. + $curly_match->unWrap(); + } + else { + // Recontruct the stream without the innards. + $curly_match->replace( '' ); + } + } + } - ############################# - # Mixins. + ############################# + # Mixins. - protected function extractMixins () { + protected function extractMixins () { - static $callback; - if ( ! $callback ) { - $callback = create_function( '$m', ' - $name = trim( $m[1] ); - $block = trim( $m[2] ); - if ( ! empty( $name ) && ! empty( $block ) ) { - csscrush::$process->mixins[ $name ] = new csscrush_mixin( $block ); - } - ' ); - } + static $callback; + if ( ! $callback ) { + $callback = create_function( '$m', ' + $name = trim( $m[1] ); + $block = trim( $m[2] ); + if ( ! empty( $name ) && ! empty( $block ) ) { + csscrush::$process->mixins[ $name ] = new csscrush_mixin( $block ); + } + ' ); + } - $this->stream->pregReplaceCallback( csscrush_regex::$patt->mixin, $callback ); - } + $this->stream->pregReplaceCallback( csscrush_regex::$patt->mixin, $callback ); + } - ############################# - # Fragments. + ############################# + # Fragments. - protected function resolveFragments () { + protected function resolveFragments () { - $regex = csscrush_regex::$patt; - $matches = $this->stream->matchAll( $regex->fragmentDef ); - $fragments = array(); + $regex = csscrush_regex::$patt; + $matches = $this->stream->matchAll( $regex->fragmentDef ); + $fragments = array(); - // Move through the matches last to first. - while ( $match = array_pop( $matches ) ) { + // Move through the matches last to first. + while ( $match = array_pop( $matches ) ) { - $match_start_pos = $match[0][1]; - $fragment_name = $match[1][0]; + $match_start_pos = $match[0][1]; + $fragment_name = $match[1][0]; - $curly_match = new csscrush_balancedMatch( $this->stream, $match_start_pos ); + $curly_match = new csscrush_balancedMatch( $this->stream, $match_start_pos ); - if ( ! $curly_match->match ) { - // Couldn't match the block. - continue; - } - else { - // Reconstruct the stream without the fragment. - $curly_match->replace( '' ); + if ( ! $curly_match->match ) { + // Couldn't match the block. + continue; + } + else { + // Reconstruct the stream without the fragment. + $curly_match->replace( '' ); - // Create the fragment and store it. - $fragments[ $fragment_name ] = new csscrush_fragment( $curly_match->inside() ); - } - } + // Create the fragment and store it. + $fragments[ $fragment_name ] = new csscrush_fragment( $curly_match->inside() ); + } + } - // Now find all the fragment calls. - $matches = $this->stream->matchAll( $regex->fragmentCall ); + // Now find all the fragment calls. + $matches = $this->stream->matchAll( $regex->fragmentCall ); - // Move through the matches last to first. - while ( $match = array_pop( $matches ) ) { + // Move through the matches last to first. + while ( $match = array_pop( $matches ) ) { - list( $match_string, $match_start_pos ) = $match[0]; + list( $match_string, $match_start_pos ) = $match[0]; - // The matched fragment name. - $fragment_name = $match[1][0]; + // The matched fragment name. + $fragment_name = $match[1][0]; - // The fragment object, or null if name not present. - $fragment = isset( $fragments[ $fragment_name ] ) ? $fragments[ $fragment_name ] : null; + // The fragment object, or null if name not present. + $fragment = isset( $fragments[ $fragment_name ] ) ? $fragments[ $fragment_name ] : null; - // Fragment may be called without any argument list. - $with_arguments = $match[2][0] === '('; + // Fragment may be called without any argument list. + $with_arguments = $match[2][0] === '('; - if ( $with_arguments ) { - $paren_match = new csscrush_balancedMatch( $this->stream, $match_start_pos, '()' ); - // Get offset of statement terminating semi-colon. - $match_end = $paren_match->nextIndexOf( ';' ) + 1; - $match_length = $match_end - $match_start_pos; - } - else { - $match_length = strlen( $match_string ); - } + if ( $with_arguments ) { + $paren_match = new csscrush_balancedMatch( $this->stream, $match_start_pos, '()' ); + // Get offset of statement terminating semi-colon. + $match_end = $paren_match->nextIndexOf( ';' ) + 1; + $match_length = $match_end - $match_start_pos; + } + else { + $match_length = strlen( $match_string ); + } - if ( ! $fragment || ( $with_arguments && ! $paren_match->match ) ) { + if ( ! $fragment || ( $with_arguments && ! $paren_match->match ) ) { - // Invalid fragment or malformed argument list. - $this->stream->splice( '', $match_start_pos, $match_length ); - continue; - } - else { + // Invalid fragment or malformed argument list. + $this->stream->splice( '', $match_start_pos, $match_length ); + continue; + } + else { - $args = array(); - if ( $with_arguments ) { - // Get the argument array to pass to the fragment. - $args = csscrush_util::splitDelimList( $paren_match->inside() ); - } + $args = array(); + if ( $with_arguments ) { + // Get the argument array to pass to the fragment. + $args = csscrush_util::splitDelimList( $paren_match->inside() ); + } - // Execute the fragment and get the return value. - $fragment_return = $fragment->call( $args ); + // Execute the fragment and get the return value. + $fragment_return = $fragment->call( $args ); - // Recontruct the stream with the fragment return value. - $this->stream->splice( $fragment_return, $match_start_pos, $match_length ); - } - } - } + // Recontruct the stream with the fragment return value. + $this->stream->splice( $fragment_return, $match_start_pos, $match_length ); + } + } + } - ############################# - # Rules. + ############################# + # Rules. - public function extractRules () { - $this->stream->pregReplaceCallback( csscrush_regex::$patt->rule, array( 'csscrush_process', 'cb_extractRules' ) ); - } + public function extractRules () { + $this->stream->pregReplaceCallback( csscrush_regex::$patt->rule, array( 'csscrush_process', 'cb_extractRules' ) ); + } - protected function processRules () { + protected function processRules () { - // Reset the selector relationships - $this->selectorRelationships = array(); + // Reset the selector relationships + $this->selectorRelationships = array(); - $aliases =& $this->aliases; + $aliases =& $this->aliases; - foreach ( $this->tokens->r as $rule ) { + foreach ( $this->tokens->r as $rule ) { - // Store selector relationships - $rule->indexSelectors(); + // Store selector relationships + $rule->indexSelectors(); - csscrush_hook::run( 'rule_prealias', $rule ); + csscrush_hook::run( 'rule_prealias', $rule ); - if ( ! empty( $aliases[ 'properties' ] ) ) { - $rule->addPropertyAliases(); - } - if ( ! empty( $aliases[ 'functions' ] ) ) { - $rule->addFunctionAliases(); - } - if ( ! empty( $aliases[ 'values' ] ) ) { - $rule->addValueAliases(); - } + if ( ! empty( $aliases[ 'properties' ] ) ) { + $rule->addPropertyAliases(); + } + if ( ! empty( $aliases[ 'functions' ] ) ) { + $rule->addFunctionAliases(); + } + if ( ! empty( $aliases[ 'values' ] ) ) { + $rule->addValueAliases(); + } - csscrush_hook::run( 'rule_postalias', $rule ); + csscrush_hook::run( 'rule_postalias', $rule ); - $rule->expandSelectors(); + $rule->expandSelectors(); - // Find previous selectors and apply them - $rule->applyExtendables(); + // Find previous selectors and apply them + $rule->applyExtendables(); - csscrush_hook::run( 'rule_postprocess', $rule ); - } - } + csscrush_hook::run( 'rule_postprocess', $rule ); + } + } - static public function cb_extractRules ( $m ) { + static public function cb_extractRules ( $m ) { - $rule = (object) array(); - $rule->selector_raw = trim( $m[1] ); - $rule->declaration_raw = trim( $m[2] ); + $rule = (object) array(); + $rule->selector_raw = trim( $m[1] ); + $rule->declaration_raw = trim( $m[2] ); - // Apply any selector aliases. - csscrush_process::applySelectorAliases( $rule->selector_raw ); + // Apply any selector aliases. + csscrush_process::applySelectorAliases( $rule->selector_raw ); - // Run rule_preprocess hook. - csscrush_hook::run( 'rule_preprocess', $rule ); + // Run rule_preprocess hook. + csscrush_hook::run( 'rule_preprocess', $rule ); - $rule = new csscrush_rule( $rule->selector_raw, $rule->declaration_raw ); + $rule = new csscrush_rule( $rule->selector_raw, $rule->declaration_raw ); - // Store rules if they have declarations or extend arguments. - if ( ! empty( $rule->_declarations ) || $rule->extendArgs ) { + // Store rules if they have declarations or extend arguments. + if ( ! empty( $rule->_declarations ) || $rule->extendArgs ) { - csscrush::$process->tokens->r[ $rule->label ] = $rule; + csscrush::$process->tokens->r[ $rule->label ] = $rule; - // If only using extend still return a label. - return $rule->label; - } - } + // If only using extend still return a label. + return $rule->label; + } + } - ############################# - # @in blocks. + ############################# + # @in blocks. - protected function prefixSelectors () { + protected function prefixSelectors () { - $matches = $this->stream->matchAll( '~@in\s+([^{]+)\{~iS' ); + $matches = $this->stream->matchAll( '~@in\s+([^{]+)\{~iS' ); - // Move through the matches in reverse order. - while ( $match = array_pop( $matches ) ) { + // Move through the matches in reverse order. + while ( $match = array_pop( $matches ) ) { - $match_start_pos = $match[0][1]; - $raw_argument = trim( $match[1][0] ); + $match_start_pos = $match[0][1]; + $raw_argument = trim( $match[1][0] ); - csscrush_process::applySelectorAliases( $raw_argument ); + csscrush_process::applySelectorAliases( $raw_argument ); - $this->captureParens( $raw_argument ); - $arguments = csscrush_util::splitDelimList( $raw_argument ); + $this->captureParens( $raw_argument ); + $arguments = csscrush_util::splitDelimList( $raw_argument ); - $curly_match = new csscrush_balancedMatch( $this->stream, $match_start_pos ); + $curly_match = new csscrush_balancedMatch( $this->stream, $match_start_pos ); - if ( ! $curly_match->match || empty( $raw_argument ) ) { - // Couldn't match the block. - continue; - } + if ( ! $curly_match->match || empty( $raw_argument ) ) { + // Couldn't match the block. + continue; + } - // Match all the rule tokens. - $rule_matches = csscrush_regex::matchAll( - csscrush_regex::$patt->rToken, $curly_match->inside() ); + // Match all the rule tokens. + $rule_matches = csscrush_regex::matchAll( + csscrush_regex::$patt->rToken, $curly_match->inside() ); - foreach ( $rule_matches as $rule_match ) { + foreach ( $rule_matches as $rule_match ) { - // Get the rule instance. - $rule = csscrush_rule::get( $rule_match[0][0] ); + // Get the rule instance. + $rule = csscrush_rule::get( $rule_match[0][0] ); - // Using arguments create new selector list for the rule. - $new_selector_list = array(); + // Using arguments create new selector list for the rule. + $new_selector_list = array(); - foreach ( $arguments as $arg_selector ) { + foreach ( $arguments as $arg_selector ) { - foreach ( $rule->selectorList as $rule_selector ) { + foreach ( $rule->selectorList as $rule_selector ) { - if ( ! $rule_selector->allowPrefix ) { + if ( ! $rule_selector->allowPrefix ) { - $new_selector_list[ $rule_selector->readableValue ] = $rule_selector; - } - elseif ( strpos( $rule_selector->value, '&' ) !== false ) { + $new_selector_list[ $rule_selector->readableValue ] = $rule_selector; + } + elseif ( strpos( $rule_selector->value, '&' ) !== false ) { - // Ampersand is the positional symbol for where the - // prefix will be placed. + // Ampersand is the positional symbol for where the + // prefix will be placed. - // Find and replace (once) the ampersand. - $new_value = preg_replace( - '!&!', - $arg_selector, - $rule_selector->value, - 1 ); + // Find and replace (once) the ampersand. + $new_value = preg_replace( + '!&!', + $arg_selector, + $rule_selector->value, + 1 ); - // Not storing the selector as named. - $new_selector_list[] = new csscrush_selector( $new_value ); - } - else { + // Not storing the selector as named. + $new_selector_list[] = new csscrush_selector( $new_value ); + } + else { - // Not storing the selector as named. - $new_selector_list[] - = new csscrush_selector( "$arg_selector {$rule_selector->value}" ); - } - } - } - $rule->selectorList = $new_selector_list; - } + // Not storing the selector as named. + $new_selector_list[] + = new csscrush_selector( "$arg_selector {$rule_selector->value}" ); + } + } + } + $rule->selectorList = $new_selector_list; + } - $curly_match->unWrap(); - } - } + $curly_match->unWrap(); + } + } - ############################# - # @-rule aliasing. + ############################# + # @-rule aliasing. - protected function aliasAtRules () { + protected function aliasAtRules () { - if ( empty( $this->aliases[ 'at-rules' ] ) ) { - return; - } + if ( empty( $this->aliases[ 'at-rules' ] ) ) { + return; + } - $aliases = $this->aliases[ 'at-rules' ]; - $regex = csscrush_regex::$patt; + $aliases = $this->aliases[ 'at-rules' ]; + $regex = csscrush_regex::$patt; - foreach ( $aliases as $at_rule => $at_rule_aliases ) { + foreach ( $aliases as $at_rule => $at_rule_aliases ) { - $matches = $this->stream->matchAll( "~@$at_rule" . '[\s{]~i' ); + $matches = $this->stream->matchAll( "~@$at_rule" . '[\s{]~i' ); - // Find at-rules that we want to alias. - while ( $match = array_pop( $matches ) ) { + // Find at-rules that we want to alias. + while ( $match = array_pop( $matches ) ) { - $curly_match = new csscrush_balancedMatch( $this->stream, $match[0][1] ); + $curly_match = new csscrush_balancedMatch( $this->stream, $match[0][1] ); - if ( ! $curly_match->match ) { - // Couldn't match the block. - continue; - } + if ( ! $curly_match->match ) { + // Couldn't match the block. + continue; + } - // Build up string with aliased blocks for splicing. - $original_block = $curly_match->whole(); - $new_blocks = array(); + // Build up string with aliased blocks for splicing. + $original_block = $curly_match->whole(); + $new_blocks = array(); - foreach ( $at_rule_aliases as $alias ) { + foreach ( $at_rule_aliases as $alias ) { - // Copy original block, replacing at-rule with alias name. - $copy_block = str_replace( "@$at_rule", "@$alias", $original_block ); + // Copy original block, replacing at-rule with alias name. + $copy_block = str_replace( "@$at_rule", "@$alias", $original_block ); - // Aliases are nearly always prefixed, capture the current vendor name. - preg_match( $regex->vendorPrefix, $alias, $vendor ); + // Aliases are nearly always prefixed, capture the current vendor name. + preg_match( $regex->vendorPrefix, $alias, $vendor ); - $vendor = $vendor ? $vendor[1] : null; + $vendor = $vendor ? $vendor[1] : null; - // Duplicate rules. - if ( preg_match_all( $regex->rToken, $copy_block, $copy_matches ) ) { + // Duplicate rules. + if ( preg_match_all( $regex->rToken, $copy_block, $copy_matches ) ) { - $originals = array(); - $replacements = array(); + $originals = array(); + $replacements = array(); - foreach ( $copy_matches[0] as $copy_match ) { + foreach ( $copy_matches[0] as $copy_match ) { - // Clone the matched rule. - $originals[] = $rule_label = $copy_match; - $cloneRule = clone $this->tokens->r[ $rule_label ]; + // Clone the matched rule. + $originals[] = $rule_label = $copy_match; + $cloneRule = clone $this->tokens->r[ $rule_label ]; - // Set the vendor context. - $cloneRule->vendorContext = $vendor; + // Set the vendor context. + $cloneRule->vendorContext = $vendor; - // Filter out declarations that have different vendor context. - $new_set = array(); - foreach ( $cloneRule as $declaration ) { - if ( ! $declaration->vendor || $declaration->vendor === $vendor ) { - $new_set[] = $declaration; - } - } - $cloneRule->declarations = $new_set; + // Filter out declarations that have different vendor context. + $new_set = array(); + foreach ( $cloneRule as $declaration ) { + if ( ! $declaration->vendor || $declaration->vendor === $vendor ) { + $new_set[] = $declaration; + } + } + $cloneRule->declarations = $new_set; - // Store the clone. - $replacements[] = $this->addToken( $cloneRule, 'r' ); + // Store the clone. + $replacements[] = $this->addToken( $cloneRule, 'r' ); - } - // Finally replace the original labels with the cloned rule labels. - $copy_block = str_replace( $originals, $replacements, $copy_block ); - } - - // Add the copied block to the stack. - $new_blocks[] = $copy_block; - } - - // The original version is always pushed last in the list. - $new_blocks[] = $original_block; - - // Splice in the blocks. - $curly_match->replace( implode( "\n", $new_blocks ) ); - } - } - } - - - ############################# - # Compile / collate. - - protected function collate () { - - $options = $this->options; - $minify = $options->minify; - $regex = csscrush_regex::$patt; - $regex_replacements = array(); - $EOL = PHP_EOL; + } + // Finally replace the original labels with the cloned rule labels. + $copy_block = str_replace( $originals, $replacements, $copy_block ); + } + + // Add the copied block to the stack. + $new_blocks[] = $copy_block; + } + + // The original version is always pushed last in the list. + $new_blocks[] = $original_block; + + // Splice in the blocks. + $curly_match->replace( implode( "\n", $new_blocks ) ); + } + } + } + + + ############################# + # Compile / collate. + + protected function collate () { + + $options = $this->options; + $minify = $options->minify; + $regex = csscrush_regex::$patt; + $regex_replacements = array(); + $EOL = PHP_EOL; - // Strip newlines added during parsing. - $regex_replacements[ '!\n+!' ] = ''; + // Strip newlines added during parsing. + $regex_replacements[ '!\n+!' ] = ''; - if ( $minify ) { - // Strip whitespace around colons used in @-rule arguments. - $regex_replacements[ '! ?\: ?!' ] = ':'; - } - else { - // Pretty printing. - $regex_replacements[ '!}!' ] = "$0$EOL$EOL"; - $regex_replacements[ '!([^\s])\{!' ] = "$1 {"; - $regex_replacements[ '! ?(@[^{]+\{)!' ] = "$1$EOL"; - $regex_replacements[ '! ?(@[^;]+\;)!' ] = "$1$EOL"; - } + if ( $minify ) { + // Strip whitespace around colons used in @-rule arguments. + $regex_replacements[ '! ?\: ?!' ] = ':'; + } + else { + // Pretty printing. + $regex_replacements[ '!}!' ] = "$0$EOL$EOL"; + $regex_replacements[ '!([^\s])\{!' ] = "$1 {"; + $regex_replacements[ '! ?(@[^{]+\{)!' ] = "$1$EOL"; + $regex_replacements[ '! ?(@[^;]+\;)!' ] = "$1$EOL"; + } - // Apply all replacements. - $this->stream->pregReplaceHash( $regex_replacements )->lTrim(); - - // Print out rules. - $this->stream->replaceHash( $this->tokens->r ); - csscrush::runStat( 'selector_count' ); - csscrush::runStat( 'rule_count' ); - - // Insert parens. - $this->stream->replaceHash( $this->tokens->p ); - - // Advanced minification parameters. - if ( is_array( $minify ) ) { - if ( in_array( 'colors', $minify ) ) { - $this->minifyColors(); - } - } - - // Compress hex-codes, collapse TRBL lists etc. - $this->decruft(); - - if ( $minify ) { - // Trim whitespace around selector combinators. - $this->stream->pregReplace( '! ?([>~+]) ?!S', '$1' ); - } - else { - - // Add newlines after comments. - foreach ( $this->tokens->c as $token => &$comment ) { - $comment .= "$EOL$EOL"; - } - - // Insert comments and do final whitespace cleanup. - $this->stream - ->replaceHash( $this->tokens->c ) - ->trim() - ->append( $EOL ); - } - - // Insert URLs. - if ( $this->tokens->u ) { - - $link = csscrush_util::getLinkBetweenDirs( $this->output->dir, $this->input->dir ); - - foreach ( $this->tokens->u as $token => $url ) { - - if ( $url->isRelative ) { - // Optionally set the URLs to absolute. - if ( $options->rewrite_import_urls === 'absolute' ) { - $url->prepend( $this->input->dirUrl . '/' ); - } - // If output dir is different to input dir prepend a link between the two. - elseif ( $link ) { - $url->prepend( $link ); - } - } + // Apply all replacements. + $this->stream->pregReplaceHash( $regex_replacements )->lTrim(); + + // Print out rules. + $this->stream->replaceHash( $this->tokens->r ); + csscrush::runStat( 'selector_count' ); + csscrush::runStat( 'rule_count' ); + + // Insert parens. + $this->stream->replaceHash( $this->tokens->p ); + + // Advanced minification parameters. + if ( is_array( $minify ) ) { + if ( in_array( 'colors', $minify ) ) { + $this->minifyColors(); + } + } + + // Compress hex-codes, collapse TRBL lists etc. + $this->decruft(); + + if ( $minify ) { + // Trim whitespace around selector combinators. + $this->stream->pregReplace( '! ?([>~+]) ?!S', '$1' ); + } + else { + + // Add newlines after comments. + foreach ( $this->tokens->c as $token => &$comment ) { + $comment .= "$EOL$EOL"; + } + + // Insert comments and do final whitespace cleanup. + $this->stream + ->replaceHash( $this->tokens->c ) + ->trim() + ->append( $EOL ); + } + + // Insert URLs. + if ( $this->tokens->u ) { + + $link = csscrush_util::getLinkBetweenDirs( $this->output->dir, $this->input->dir ); + + foreach ( $this->tokens->u as $token => $url ) { + + if ( $url->isRelative ) { + // Optionally set the URLs to absolute. + if ( $options->rewrite_import_urls === 'absolute' ) { + $url->prepend( $this->input->dirUrl . '/' ); + } + // If output dir is different to input dir prepend a link between the two. + elseif ( $link ) { + $url->prepend( $link ); + } + } - if ( $url->convertToData ) { - $url->toData(); - } - else { - $url->simplify(); - } - } - $this->stream->replaceHash( $this->tokens->u ); - } + if ( $url->convertToData ) { + $url->toData(); + } + else { + $url->simplify(); + } + } + $this->stream->replaceHash( $this->tokens->u ); + } - // Insert string literals. - $this->stream->replaceHash( $this->tokens->s ); + // Insert string literals. + $this->stream->replaceHash( $this->tokens->s ); - // Add in boilerplate. - if ( $options->boilerplate ) { - $this->stream->prepend( $this->getBoilerplate() ); - } + // Add in boilerplate. + if ( $options->boilerplate ) { + $this->stream->prepend( $this->getBoilerplate() ); + } - // Add @charset at top if set. - if ( $this->charset ) { - $this->stream->prepend( "@charset \"$this->charset\";$EOL" ); - } - } + // Add @charset at top if set. + if ( $this->charset ) { + $this->stream->prepend( "@charset \"$this->charset\";$EOL" ); + } + } - public function compile () { + public function compile () { - // Always store start time. - $this->stat[ 'compile_start_time' ] = microtime( true ); + // Always store start time. + $this->stat[ 'compile_start_time' ] = microtime( true ); - // Resolve active aliases and plugins. - $this->filterPlugins(); - $this->filterAliases(); + // Resolve active aliases and plugins. + $this->filterPlugins(); + $this->filterAliases(); - // Collate hostfile and imports. - $this->stream = new csscrush_stream( csscrush_importer::hostfile( $this->input ) ); + // Collate hostfile and imports. + $this->stream = new csscrush_stream( csscrush_importer::hostfile( $this->input ) ); - // Extract and calculate variables. - $this->calculateVariables(); + // Extract and calculate variables. + $this->calculateVariables(); - // Place variables. - $this->placeAllVariables(); + // Place variables. + $this->placeAllVariables(); - // Resolve @ifdefine blocks. - $this->resolveIfDefines(); + // Resolve @ifdefine blocks. + $this->resolveIfDefines(); - // Get selector aliases. - $this->extractSelectorAliases(); + // Get selector aliases. + $this->extractSelectorAliases(); - // Pull out @mixin definitions. - $this->extractMixins(); + // Pull out @mixin definitions. + $this->extractMixins(); - // Pull out @fragment blocks, and invoke. - $this->resolveFragments(); + // Pull out @fragment blocks, and invoke. + $this->resolveFragments(); - // Adjust meta characters so we can extract the rules cleanly. - $this->stream->replaceHash( array( - '@' => "\n@", - '}' => "}\n", - '{' => "{\n", - ';' => ";\n", - ))->prepend( "\n" ); + // Adjust meta characters so we can extract the rules cleanly. + $this->stream->replaceHash( array( + '@' => "\n@", + '}' => "}\n", + '{' => "{\n", + ';' => ";\n", + ))->prepend( "\n" ); - // Parse rules. - $this->extractRules(); + // Parse rules. + $this->extractRules(); - // Process @in blocks. - $this->prefixSelectors(); + // Process @in blocks. + $this->prefixSelectors(); - // Main processing on the rule objects. - $this->processRules(); + // Main processing on the rule objects. + $this->processRules(); - // Alias any @-rules. - $this->aliasAtRules(); + // Alias any @-rules. + $this->aliasAtRules(); - // Print rules, optionally minify. - $this->collate(); + // Print rules, optionally minify. + $this->collate(); - // Release memory. - $this->release(); + // Release memory. + $this->release(); - csscrush::runStat( 'compile_time' ); + csscrush::runStat( 'compile_time' ); - return $this->stream; - } + return $this->stream; + } - ############################# - # Decruft. + ############################# + # Decruft. - protected function decruft () { + protected function decruft () { - return $this->stream->pregReplaceHash( array( + return $this->stream->pregReplaceHash( array( - // Strip leading zeros on floats. - '!([: \(,])(-?)0(\.\d+)!S' => '$1$2$3', + // Strip leading zeros on floats. + '!([: \(,])(-?)0(\.\d+)!S' => '$1$2$3', - // Strip unnecessary units on zero values for length types. - '!([: \(,])\.?0(?:e[mx]|c[hm]|rem|v[hwm]|in|p[tcx])!iS' => '${1}0', + // Strip unnecessary units on zero values for length types. + '!([: \(,])\.?0(?:e[mx]|c[hm]|rem|v[hwm]|in|p[tcx])!iS' => '${1}0', - // Collapse zero lists. - '!(\: *)(?:0 0 0|0 0 0 0) *([;}])!S' => '${1}0$2', + // Collapse zero lists. + '!(\: *)(?:0 0 0|0 0 0 0) *([;}])!S' => '${1}0$2', - // Collapse zero lists 2nd pass. - '!(padding|margin|border-radius) ?(\: *)0 0 *([;}])!iS' => '${1}${2}0$3', + // Collapse zero lists 2nd pass. + '!(padding|margin|border-radius) ?(\: *)0 0 *([;}])!iS' => '${1}${2}0$3', - // Dropping redundant trailing zeros on TRBL lists. - '!(\: *)(-?(?:\d+)?\.?\d+[a-z]{1,4}) 0 0 0 *([;}])!iS' => '$1$2 0 0$3', - '!(\: *)0 0 (-?(?:\d+)?\.?\d+[a-z]{1,4}) 0 *([;}])!iS' => '${1}0 0 $2$3', + // Dropping redundant trailing zeros on TRBL lists. + '!(\: *)(-?(?:\d+)?\.?\d+[a-z]{1,4}) 0 0 0 *([;}])!iS' => '$1$2 0 0$3', + '!(\: *)0 0 (-?(?:\d+)?\.?\d+[a-z]{1,4}) 0 *([;}])!iS' => '${1}0 0 $2$3', - // Compress hex codes. - csscrush_regex::$patt->cruftyHex => '#$1$2$3', - )); - } + // Compress hex codes. + csscrush_regex::$patt->cruftyHex => '#$1$2$3', + )); + } - ############################# - # Advanced minification. + ############################# + # Advanced minification. - protected function minifyColors () { + protected function minifyColors () { - static $keywords_patt; - if ( ! $keywords_patt ) { - $keywords =& csscrush_color::loadMinifyableKeywords(); - $keywords_patt = '~(?stream->pregReplaceCallback( $keywords_patt, $keywords_callback ); + $this->stream->pregReplaceCallback( $keywords_patt, $keywords_callback ); - static $functions_callback; - if ( ! $functions_callback ) { - $functions_callback = create_function( '$m', ' - $args = csscrush_function::parseArgs( trim( $m[2] ) ); - if ( stripos( $m[1], \'hsl\' ) === 0 ) { - $args = csscrush_color::cssHslToRgb( $args ); - } - return csscrush_color::rgbToHex( $args ); - '); - } + static $functions_callback; + if ( ! $functions_callback ) { + $functions_callback = create_function( '$m', ' + $args = csscrush_function::parseArgs( trim( $m[2] ) ); + if ( stripos( $m[1], \'hsl\' ) === 0 ) { + $args = csscrush_color::cssHslToRgb( $args ); + } + return csscrush_color::rgbToHex( $args ); + '); + } - $this->stream->pregReplaceCallback( - '~(?stream->pregReplaceCallback( + '~(?ident = '[a-zA-Z0-9_-]+'; - - // Patterns. - $patt->ident = '!^' . $classes->ident . '$!'; - - // @-rule blocks. - $patt->import = '~@import\s+(\?u\d+\?)\s?([^;]*);~iS'; - $patt->variables = '~@(?:define|variables) *([^\{]*)\{ *(.*?) *\};?~iS'; - $patt->mixin = '~@mixin *([^\{]*)\{ *(.*?) *\};?~iS'; - $patt->abstract = csscrush_regex::create( '^@abstract\s+()', 'i' ); - $patt->selectorAlias = csscrush_regex::create( '@selector-alias +\:() +([^;]+) *;', 'iS' ); - $patt->ifDefine = csscrush_regex::create( '@ifdefine +(not +)?() *\{', 'iS' ); - $patt->fragmentDef = csscrush_regex::create( '@fragment +() *\{', 'iS' ); - $patt->fragmentCall = csscrush_regex::create( '@fragment +() *(\(|;)', 'iS' ); - - $patt->commentAndString = '~ - # Quoted string (to EOF if unmatched). - (\'|")(?:\\\\\1|[^\1])*?(?:\1|$) - | - # Block comment (to EOF if unmatched). - /\*(?:.*?)(?:\*/|$) - ~xsS'; - - // As an exception we treat some @-rules like standard rule blocks. - $patt->rule = '~ - # The selector. - \n( - [^@{}]+ - | - (?: [^@{}]+ )? @(?: font-face|page|abstract ) (?!-)\b [^{]* - ) - # The declaration block. - \{ ([^{}]*) \} - ~xiS'; - - // Balanced bracket matching. - $patt->balancedParens = '!\(\s* ( (?: (?>[^()]+) | (?R) )* ) \s*\)!xS'; - $patt->balancedCurlies = '!\{\s* ( (?: (?>[^{}]+) | (?R) )* ) \s*\}!xS'; - - // Tokens. - $patt->cToken = '!\?c\d+\?!'; // Comments - $patt->sToken = '!\?s\d+\?!'; // Strings - $patt->rToken = '!\?r\d+\?!'; // Rules - $patt->pToken = '!\?p\d+\?!'; // Parens - $patt->uToken = '!\?u\d+\?!'; // URLs - $patt->tToken = '!\?t\d+\?!'; // Traces - $patt->aToken = '!\?arg(\d+)\?!'; // Args - - // Functions. - $patt->function = '!(^|[^a-z0-9_-])([a-z_-]+)(\?p\d+\?)!iS'; - $patt->varFunction = csscrush_regex::create( '\$\(\s*()\s*\)', 'iS' ); - $patt->varFunctionStart = '!(\$)\(!'; - $patt->argFunction = csscrush_regex::createFunctionMatchPatt( array( 'arg' ) ); - $patt->queryFunction = csscrush_regex::createFunctionMatchPatt( array( 'query' ) ); - $patt->thisFunction = csscrush_regex::createFunctionMatchPatt( array( 'this' ) ); - - // Misc. - $patt->vendorPrefix = '!^-([a-z]+)-([a-z-]+)!iS'; - $patt->mixinExtend = '!^(?:(@include|mixin)|(@?extends?))[\s\:]+!iS'; - $patt->argListSplit = '!\s*[,\s]\s*!S'; - $patt->mathBlacklist = '![^\.0-9\*\/\+\-\(\)]!S'; - $patt->charset = '!@charset\s+(\?s\d+\?)\s*;!iS'; - $patt->cruftyHex = '!\#([0-9a-f])\1([0-9a-f])\2([0-9a-f])\3!iS'; - } - - static public function create ( $pattern_template, $flags = '', $delim = '!' ) { - - // Sugar. - $pattern = str_replace( - array( '' ), - array( self::$classes->ident ), - $pattern_template ); - return "$delim{$pattern}$delim{$flags}"; - } - - static public function matchAll ( $patt, $subject, $preprocess_patt = false, $offset = 0 ) { - - if ( $preprocess_patt ) { - // Assume case-insensitive. - $patt = self::create( $patt, 'i' ); - } - - $count = preg_match_all( $patt, $subject, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER, $offset ); - return $count ? $matches : array(); - } - - static public function createFunctionMatchPatt ( $list, $include_unnamed_function = false ) { - - $question = $include_unnamed_function ? '?' : ''; - - foreach ( $list as &$fn_name ) { - $fn_name = preg_quote( $fn_name ); - } - return '~(?ident = '[a-zA-Z0-9_-]+'; + + // Patterns. + $patt->ident = '!^' . $classes->ident . '$!'; + + // @-rule blocks. + $patt->import = '~@import\s+(\?u\d+\?)\s?([^;]*);~iS'; + $patt->variables = '~@(?:define|variables) *([^\{]*)\{ *(.*?) *\};?~iS'; + $patt->mixin = '~@mixin *([^\{]*)\{ *(.*?) *\};?~iS'; + $patt->abstract = csscrush_regex::create( '^@abstract\s+()', 'i' ); + $patt->selectorAlias = csscrush_regex::create( '@selector-alias +\:() +([^;]+) *;', 'iS' ); + $patt->ifDefine = csscrush_regex::create( '@ifdefine +(not +)?() *\{', 'iS' ); + $patt->fragmentDef = csscrush_regex::create( '@fragment +() *\{', 'iS' ); + $patt->fragmentCall = csscrush_regex::create( '@fragment +() *(\(|;)', 'iS' ); + + $patt->commentAndString = '~ + # Quoted string (to EOF if unmatched). + (\'|")(?:\\\\\1|[^\1])*?(?:\1|$) + | + # Block comment (to EOF if unmatched). + /\*(?:.*?)(?:\*/|$) + ~xsS'; + + // As an exception we treat some @-rules like standard rule blocks. + $patt->rule = '~ + # The selector. + \n( + [^@{}]+ + | + (?: [^@{}]+ )? @(?: font-face|page|abstract ) (?!-)\b [^{]* + ) + # The declaration block. + \{ ([^{}]*) \} + ~xiS'; + + // Balanced bracket matching. + $patt->balancedParens = '!\(\s* ( (?: (?>[^()]+) | (?R) )* ) \s*\)!xS'; + $patt->balancedCurlies = '!\{\s* ( (?: (?>[^{}]+) | (?R) )* ) \s*\}!xS'; + + // Tokens. + $patt->cToken = '!\?c\d+\?!'; // Comments + $patt->sToken = '!\?s\d+\?!'; // Strings + $patt->rToken = '!\?r\d+\?!'; // Rules + $patt->pToken = '!\?p\d+\?!'; // Parens + $patt->uToken = '!\?u\d+\?!'; // URLs + $patt->tToken = '!\?t\d+\?!'; // Traces + $patt->aToken = '!\?arg(\d+)\?!'; // Args + + // Functions. + $patt->function = '!(^|[^a-z0-9_-])([a-z_-]+)(\?p\d+\?)!iS'; + $patt->varFunction = csscrush_regex::create( '\$\(\s*()\s*\)', 'iS' ); + $patt->varFunctionStart = '!(\$)\(!'; + $patt->argFunction = csscrush_regex::createFunctionMatchPatt( array( 'arg' ) ); + $patt->queryFunction = csscrush_regex::createFunctionMatchPatt( array( 'query' ) ); + $patt->thisFunction = csscrush_regex::createFunctionMatchPatt( array( 'this' ) ); + + // Misc. + $patt->vendorPrefix = '!^-([a-z]+)-([a-z-]+)!iS'; + $patt->mixinExtend = '!^(?:(@include|mixin)|(@?extends?))[\s\:]+!iS'; + $patt->argListSplit = '!\s*[,\s]\s*!S'; + $patt->mathBlacklist = '![^\.0-9\*\/\+\-\(\)]!S'; + $patt->charset = '!@charset\s+(\?s\d+\?)\s*;!iS'; + $patt->cruftyHex = '!\#([0-9a-f])\1([0-9a-f])\2([0-9a-f])\3!iS'; + } + + static public function create ( $pattern_template, $flags = '', $delim = '!' ) { + + // Sugar. + $pattern = str_replace( + array( '' ), + array( self::$classes->ident ), + $pattern_template ); + return "$delim{$pattern}$delim{$flags}"; + } + + static public function matchAll ( $patt, $subject, $preprocess_patt = false, $offset = 0 ) { + + if ( $preprocess_patt ) { + // Assume case-insensitive. + $patt = self::create( $patt, 'i' ); + } + + $count = preg_match_all( $patt, $subject, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER, $offset ); + return $count ? $matches : array(); + } + + static public function createFunctionMatchPatt ( $list, $include_unnamed_function = false ) { + + $question = $include_unnamed_function ? '?' : ''; + + foreach ( $list as &$fn_name ) { + $fn_name = preg_quote( $fn_name ); + } + return '~(?options; - $this->label = $process->createTokenLabel( 'r' ); + $regex = csscrush_regex::$patt; + $process = csscrush::$process; + $options = $process->options; + $this->label = $process->createTokenLabel( 'r' ); - // If tracing store the last tracing stub, then strip all. - if ( - $options->trace && - $trace_tokens = csscrush_regex::matchAll( $regex->tToken, $selector_string ) - ) { - $trace_token = array_pop( $trace_tokens ); - $this->tracingStub = $trace_token[0][0]; - $selector_string = preg_replace( $regex->tToken, '', $selector_string ); - } + // If tracing store the last tracing stub, then strip all. + if ( + $options->trace && + $trace_tokens = csscrush_regex::matchAll( $regex->tToken, $selector_string ) + ) { + $trace_token = array_pop( $trace_tokens ); + $this->tracingStub = $trace_token[0][0]; + $selector_string = preg_replace( $regex->tToken, '', $selector_string ); + } - // Parse the selectors chunk - if ( ! empty( $selector_string ) ) { + // Parse the selectors chunk + if ( ! empty( $selector_string ) ) { - $process->captureParens( $selector_string ); - $selectors = csscrush_util::splitDelimList( $selector_string ); + $process->captureParens( $selector_string ); + $selectors = csscrush_util::splitDelimList( $selector_string ); - // Remove and store comments that sit above the first selector - // remove all comments between the other selectors - if ( strpos( $selectors[0], '?c' ) !== false ) { - preg_match_all( $regex->cToken, $selectors[0], $m ); - $this->comments = $m[0]; - } + // Remove and store comments that sit above the first selector + // remove all comments between the other selectors + if ( strpos( $selectors[0], '?c' ) !== false ) { + preg_match_all( $regex->cToken, $selectors[0], $m ); + $this->comments = $m[0]; + } - // Strip any other comments then create selector instances - foreach ( $selectors as $selector ) { + // Strip any other comments then create selector instances + foreach ( $selectors as $selector ) { - $selector = trim( csscrush_util::stripCommentTokens( $selector ) ); + $selector = trim( csscrush_util::stripCommentTokens( $selector ) ); - // If the selector matches an absract directive - if ( preg_match( $regex->abstract, $selector, $m ) ) { + // If the selector matches an absract directive + if ( preg_match( $regex->abstract, $selector, $m ) ) { - $abstract_name = $m[1]; + $abstract_name = $m[1]; - // Link the rule to the abstract name and skip forward to declaration parsing - $process->abstracts[ $abstract_name ] = $this; - break; - } + // Link the rule to the abstract name and skip forward to declaration parsing + $process->abstracts[ $abstract_name ] = $this; + break; + } - $this->addSelector( new csscrush_selector( $selector ) ); + $this->addSelector( new csscrush_selector( $selector ) ); - // Store selector relationships - // - This happens twice; on first pass for mixins, second pass is for inheritance - $this->indexSelectors(); - } - } + // Store selector relationships + // - This happens twice; on first pass for mixins, second pass is for inheritance + $this->indexSelectors(); + } + } - // Parse the declarations chunk. - $declarations = preg_split( '!\s*;\s*!', trim( $declarations_string ), null, PREG_SPLIT_NO_EMPTY ); + // Parse the declarations chunk. + $declarations = preg_split( '!\s*;\s*!', trim( $declarations_string ), null, PREG_SPLIT_NO_EMPTY ); - // First create a simple array of all properties and value pairs in raw state - $pairs = array(); + // First create a simple array of all properties and value pairs in raw state + $pairs = array(); - // Split declarations in to property/value pairs - foreach ( $declarations as $declaration ) { + // Split declarations in to property/value pairs + foreach ( $declarations as $declaration ) { - // Strip comments around the property - $declaration = csscrush_util::stripCommentTokens( $declaration ); + // Strip comments around the property + $declaration = csscrush_util::stripCommentTokens( $declaration ); - // Accept several different syntaxes for mixin and extends. - if ( preg_match( $regex->mixinExtend, $declaration, $m ) ) { + // Accept several different syntaxes for mixin and extends. + if ( preg_match( $regex->mixinExtend, $declaration, $m ) ) { - $prop = isset( $m[2] ) ? 'extends' : 'mixin'; - $value = substr( $declaration, strlen( $m[0] ) ); - } - elseif ( ( $colonPos = strpos( $declaration, ':' ) ) !== false ) { + $prop = isset( $m[2] ) ? 'extends' : 'mixin'; + $value = substr( $declaration, strlen( $m[0] ) ); + } + elseif ( ( $colonPos = strpos( $declaration, ':' ) ) !== false ) { - $prop = trim( substr( $declaration, 0, $colonPos ) ); - // Extract the value part of the declaration. - $value = substr( $declaration, $colonPos + 1 ); - } - else { - // Must be malformed. - continue; - } - - // Some cleanup. - $value = $value !== false ? trim( $value ) : $value; - - if ( $prop === 'mixin' ) { - - // Mixins are a special case - if ( $mixin_declarations = csscrush_mixin::parseValue( $value ) ) { - - // Add mixin declarations to the stack - while ( $mixin_declaration = array_shift( $mixin_declarations ) ) { - - $this->declarationCheckin( - $mixin_declaration['property'], $mixin_declaration['value'], $pairs ); - } - } - } - elseif ( $prop === 'extends' ) { - - // Extends are also a special case - $this->setExtendSelectors( $value ); - } - else { - - $this->declarationCheckin( $prop, $value, $pairs ); - } - } - - // Bind declaration objects on the rule - foreach ( $pairs as $index => &$pair ) { - - list( $prop, $value ) = $pair; - - // Resolve self references, aka this() - csscrush_function::executeCustomFunctions( $value, - csscrush_regex::$patt->thisFunction, array( - 'this' => array( $this, 'cssThisFunction' ), - ), $prop ); - - if ( trim( $value ) !== '' ) { - - // Add declaration and update the data table - $this->data[ $prop ] = $value; - $this->addDeclaration( $prop, $value, $index ); - } - } + $prop = trim( substr( $declaration, 0, $colonPos ) ); + // Extract the value part of the declaration. + $value = substr( $declaration, $colonPos + 1 ); + } + else { + // Must be malformed. + continue; + } + + // Some cleanup. + $value = $value !== false ? trim( $value ) : $value; + + if ( $prop === 'mixin' ) { + + // Mixins are a special case + if ( $mixin_declarations = csscrush_mixin::parseValue( $value ) ) { + + // Add mixin declarations to the stack + while ( $mixin_declaration = array_shift( $mixin_declarations ) ) { + + $this->declarationCheckin( + $mixin_declaration['property'], $mixin_declaration['value'], $pairs ); + } + } + } + elseif ( $prop === 'extends' ) { + + // Extends are also a special case + $this->setExtendSelectors( $value ); + } + else { + + $this->declarationCheckin( $prop, $value, $pairs ); + } + } + + // Bind declaration objects on the rule + foreach ( $pairs as $index => &$pair ) { + + list( $prop, $value ) = $pair; + + // Resolve self references, aka this() + csscrush_function::executeCustomFunctions( $value, + csscrush_regex::$patt->thisFunction, array( + 'this' => array( $this, 'cssThisFunction' ), + ), $prop ); + + if ( trim( $value ) !== '' ) { + + // Add declaration and update the data table + $this->data[ $prop ] = $value; + $this->addDeclaration( $prop, $value, $index ); + } + } - // localData no longer required - $this->localData = null; - } + // localData no longer required + $this->localData = null; + } - public function __set ( $name, $value ) { + public function __set ( $name, $value ) { - if ( $name === 'declarations' ) { - $this->_declarations = $value; - - // Update the table of properties - $this->updatePropertyTable(); - } - } - - public function __get ( $name ) { + if ( $name === 'declarations' ) { + $this->_declarations = $value; + + // Update the table of properties + $this->updatePropertyTable(); + } + } + + public function __get ( $name ) { - if ( $name === 'declarations' ) { - return $this->_declarations; - } - } - - public function __toString () { + if ( $name === 'declarations' ) { + return $this->_declarations; + } + } + + public function __toString () { - // If there are no selectors or declarations associated with the rule return empty string. - if ( empty( $this->selectorList ) || empty( $this->_declarations ) ) { - // De-referencing self. - unset( csscrush::$process->tokens->r[ $this->label ] ); - return ''; - } - - // Tracing stubs. - $tracing_stub = ''; - if ( $this->tracingStub ) { - $tracing_stub =& csscrush::$process->tokens->t[ $this->tracingStub ]; - } - - // Concat and return. - if ( csscrush::$process->options->minify ) { - $selectors = implode( ',', $this->selectorList ); - $block = implode( ';', $this->_declarations ); - return "$tracing_stub$selectors{{$block}}"; - } - else { - $EOL = PHP_EOL; - if ( $tracing_stub ) { - $tracing_stub .= $EOL; - } - // Include pre-rule comments. - $comments = implode( '', $this->comments ); - $selectors = implode( ",$EOL", $this->selectorList ); - $block = implode( ";$EOL\t", $this->_declarations ); - return "$comments$tracing_stub$selectors {{$EOL}\t$block;$EOL\t}$EOL$EOL"; - } - } - - public function declarationCheckin ( $prop, $value, &$pairs ) { - - if ( $prop !== '' && $value !== '' ) { - - // First resolve query() calls that reference earlier rules - if ( preg_match( csscrush_regex::$patt->queryFunction, $value ) ) { - - csscrush_function::executeCustomFunctions( $value, - csscrush_regex::$patt->queryFunction, array( - 'query' => array( $this, 'cssQueryFunction' ), - ), $prop ); - } - - if ( strpos( $prop, 'data-' ) === 0 ) { - - // If it's with data prefix, we don't want to print it - // Just remove the prefix - $prop = substr( $prop, strlen( 'data-' ) ); - - // On first pass we want to store data properties on $this->data, - // as well as on local - $this->data[ $prop ] = $value; - } - else { - - // Add to the stack - $pairs[] = array( $prop, $value ); - } - - // Set on $this->localData - $this->localData[ $prop ] = $value; - - // Unset on data tables if the value has a this() call: - // - Restriction to avoid circular references - if ( preg_match( csscrush_regex::$patt->thisFunction, $value ) ) { - - unset( $this->localData[ $prop ], $this->data[ $prop ] ); - } - } - } - - public function cssThisFunction ( $input, $fn_name ) { - - $args = csscrush_function::parseArgsSimple( $input ); - - if ( isset( $this->localData[ $args[0] ] ) ) { - - return $this->localData[ $args[0] ]; - } - elseif ( isset( $args[1] ) ) { - - return $args[1]; - } - else { - - return ''; - } - } - - public function cssQueryFunction ( $input, $fn_name, $call_property ) { - - $result = ''; - $args = csscrush_function::parseArgs( $input ); - - if ( count( $args ) < 1 ) { - return $result; - } - - $abstracts =& csscrush::$process->abstracts; - $mixins =& csscrush::$process->mixins; - $selectorRelationships =& csscrush::$process->selectorRelationships; - - // Resolve arguments - $name = array_shift( $args ); - $property = $call_property; - if ( isset( $args[0] ) ) { - if ( $args[0] !== 'default' ) { - $property = array_shift( $args ); - } - else { - array_shift( $args ); - } - } - $default = isset( $args[0] ) ? $args[0] : null; - - // Try to match a abstract rule first - if ( preg_match( csscrush_regex::$patt->ident, $name ) ) { - - // Search order: abstracts, mixins, rules - if ( isset( $abstracts[ $name ]->data[ $property ] ) ) { - - $result = $abstracts[ $name ]->data[ $property ]; - } - elseif ( isset( $mixins[ $name ]->data[ $property ] ) ) { - - $result = $mixins[ $name ]->data[ $property ]; - } - elseif ( isset( $selectorRelationships[ $name ]->data[ $property ] ) ) { - - $result = $selectorRelationships[ $name ]->data[ $property ]; - } - } - else { - - // Look for a rule match - $name = csscrush_selector::makeReadableSelector( $name ); - if ( isset( $selectorRelationships[ $name ]->data[ $property ] ) ) { - - $result = $selectorRelationships[ $name ]->data[ $property ]; - } - } - - if ( $result === '' && ! is_null( $default ) ) { - $result = $default; - } - return $result; - } - - public function updatePropertyTable () { - - // Create a new table of properties - $new_properties_table = array(); - - foreach ( $this as $declaration ) { - - $name = $declaration->property; - - if ( isset( $new_properties_table[ $name ] ) ) { - $new_properties_table[ $name ]++; - } - else { - $new_properties_table[ $name ] = 1; - } - } - - $this->properties = $new_properties_table; - } - - public function addPropertyAliases () { - - $regex = csscrush_regex::$patt; - $aliasedProperties =& csscrush::$process->aliases[ 'properties' ]; - - // First test for the existence of any aliased properties - $intersect = array_intersect( array_keys( $aliasedProperties ), array_keys( $this->properties ) ); - if ( empty( $intersect ) ) { - return; - } - - // Shim in aliased properties - $new_set = array(); - foreach ( $this->declarations as $declaration ) { - $prop = $declaration->property; - if ( - ! $declaration->skip && - isset( $aliasedProperties[ $prop ] ) - ) { - // There are aliases for the current property - foreach ( $aliasedProperties[ $prop ] as $prop_alias ) { - - // If an aliased version already exists to not create one. - if ( $this->propertyCount( $prop_alias ) ) { - continue; - } - - // Create the aliased declaration. - $copy = clone $declaration; - $copy->property = $prop_alias; - - // Set the aliased declaration vendor property. - $copy->vendor = null; - if ( preg_match( $regex->vendorPrefix, $prop_alias, $vendor ) ) { - $copy->vendor = $vendor[1]; - } - $new_set[] = $copy; - } - } - // Un-aliased property or a property alias that has been manually set. - $new_set[] = $declaration; - } - // Re-assign. - $this->declarations = $new_set; - } - - public function addFunctionAliases () { - - $function_aliases =& csscrush::$process->aliases[ 'functions' ]; - $aliased_functions = array_keys( $function_aliases ); - - if ( empty( $aliased_functions ) ) { - return; - } - - $new_set = array(); - - // Keep track of the function aliases we apply and to which property - // they belong, so we can avoid un-unecessary duplications. - $used_fn_aliases = array(); - - // Shim in aliased functions - foreach ( $this->declarations as $declaration ) { - - // No functions, skip - if ( - $declaration->skip || - empty( $declaration->functions ) - ) { - $new_set[] = $declaration; - continue; - } - - // Get list of functions used in declaration that are alias-able, if none skip. - $intersect = array_intersect( $declaration->functions, $aliased_functions ); - if ( empty( $intersect ) ) { - $new_set[] = $declaration; - continue; - } - - // Loop the aliasable functions. - foreach ( $intersect as $fn_name ) { - - if ( $declaration->vendor ) { - // If the property is vendor prefixed we use the vendor prefixed version - // of the function if it exists. - // Else we just skip and use the unprefixed version - $fn_search = "-{$declaration->vendor}-$fn_name"; - if ( in_array( $fn_search, $function_aliases[ $fn_name ] ) ) { - $declaration->value = preg_replace( - '!(^| |,)' . $fn_name . '!', - '${1}' . $fn_search, - $declaration->value - ); - $used_fn_aliases[ $declaration->canonicalProperty ][] = $fn_search; - } - } - else { - - // Duplicate the rule for each alias - foreach ( $function_aliases[ $fn_name ] as $fn_alias ) { - - if ( - isset( $used_fn_aliases[ $declaration->canonicalProperty ] ) && - in_array( $fn_alias, $used_fn_aliases[ $declaration->canonicalProperty ] ) - ) { - // If the function alias has already been applied in a vendor property - // for the same declaration property assume all is good - continue; - } - $copy = clone $declaration; - $copy->value = preg_replace( - '!(^| |,)' . $fn_name . '!', - '${1}' . $fn_alias, - $copy->value - ); - $new_set[] = $copy; - } - } - } - $new_set[] = $declaration; - } - - // Re-assign - $this->declarations = $new_set; - } - - public function addValueAliases () { - - $aliasedValues =& csscrush::$process->aliases[ 'values' ]; - - // First test for the existence of any aliased properties - $intersect = array_intersect( array_keys( $aliasedValues ), array_keys( $this->properties ) ); - - if ( empty( $intersect ) ) { - return; - } - - $new_set = array(); - foreach ( $this->declarations as $declaration ) { - if ( !$declaration->skip ) { - foreach ( $aliasedValues as $value_prop => $value_aliases ) { - if ( $this->propertyCount( $value_prop ) < 1 ) { - continue; - } - foreach ( $value_aliases as $value => $aliases ) { - if ( $declaration->value === $value ) { - foreach ( $aliases as $alias ) { - $copy = clone $declaration; - $copy->value = $alias; - $new_set[] = $copy; - } - } - } - } - } - $new_set[] = $declaration; - } - // Re-assign - $this->declarations = $new_set; - } - - public function expandSelectors () { - - $new_set = array(); - $reg_comma = '!\s*,\s*!'; - - foreach ( $this->selectorList as $readableValue => $selector ) { - - $pos = strpos( $selector->value, ':any?' ); - - if ( $pos !== false ) { - - // Contains an :any statement so we expand - $chain = array( '' ); - do { - if ( $pos === 0 ) { - preg_match( '!:any(\?p\d+\?)!', $selector->value, $m ); - - // Parse the arguments - $expression = trim( csscrush::$process->tokens->p[ $m[1] ], '()' ); - $parts = preg_split( $reg_comma, $expression, null, PREG_SPLIT_NO_EMPTY ); - - $tmp = array(); - foreach ( $chain as $rowCopy ) { - foreach ( $parts as $part ) { - $tmp[] = $rowCopy . $part; - } - } - $chain = $tmp; - $selector->value = substr( $selector->value, strlen( $m[0] ) ); - } - else { - foreach ( $chain as &$row ) { - $row .= substr( $selector->value, 0, $pos ); - } - $selector->value = substr( $selector->value, $pos ); - } - } while ( ( $pos = strpos( $selector->value, ':any?' ) ) !== false ); - - // Finish off - foreach ( $chain as &$row ) { - - // Not creating a named rule association with this expanded selector - $new_set[] = new csscrush_selector( $row . $selector->value ); - } - - // Store the unexpanded selector to selectorRelationships - csscrush::$process->selectorRelationships[ $readableValue ] = $this; - } - else { - - // Nothing to expand - $new_set[ $readableValue ] = $selector; - } + // If there are no selectors or declarations associated with the rule return empty string. + if ( empty( $this->selectorList ) || empty( $this->_declarations ) ) { + // De-referencing self. + unset( csscrush::$process->tokens->r[ $this->label ] ); + return ''; + } + + // Tracing stubs. + $tracing_stub = ''; + if ( $this->tracingStub ) { + $tracing_stub =& csscrush::$process->tokens->t[ $this->tracingStub ]; + } + + // Concat and return. + if ( csscrush::$process->options->minify ) { + $selectors = implode( ',', $this->selectorList ); + $block = implode( ';', $this->_declarations ); + return "$tracing_stub$selectors{{$block}}"; + } + else { + $EOL = PHP_EOL; + if ( $tracing_stub ) { + $tracing_stub .= $EOL; + } + // Include pre-rule comments. + $comments = implode( '', $this->comments ); + $selectors = implode( ",$EOL", $this->selectorList ); + $block = implode( ";$EOL\t", $this->_declarations ); + return "$comments$tracing_stub$selectors {{$EOL}\t$block;$EOL\t}$EOL$EOL"; + } + } + + public function declarationCheckin ( $prop, $value, &$pairs ) { + + if ( $prop !== '' && $value !== '' ) { + + // First resolve query() calls that reference earlier rules + if ( preg_match( csscrush_regex::$patt->queryFunction, $value ) ) { + + csscrush_function::executeCustomFunctions( $value, + csscrush_regex::$patt->queryFunction, array( + 'query' => array( $this, 'cssQueryFunction' ), + ), $prop ); + } + + if ( strpos( $prop, 'data-' ) === 0 ) { + + // If it's with data prefix, we don't want to print it + // Just remove the prefix + $prop = substr( $prop, strlen( 'data-' ) ); + + // On first pass we want to store data properties on $this->data, + // as well as on local + $this->data[ $prop ] = $value; + } + else { + + // Add to the stack + $pairs[] = array( $prop, $value ); + } + + // Set on $this->localData + $this->localData[ $prop ] = $value; + + // Unset on data tables if the value has a this() call: + // - Restriction to avoid circular references + if ( preg_match( csscrush_regex::$patt->thisFunction, $value ) ) { + + unset( $this->localData[ $prop ], $this->data[ $prop ] ); + } + } + } + + public function cssThisFunction ( $input, $fn_name ) { + + $args = csscrush_function::parseArgsSimple( $input ); + + if ( isset( $this->localData[ $args[0] ] ) ) { + + return $this->localData[ $args[0] ]; + } + elseif ( isset( $args[1] ) ) { + + return $args[1]; + } + else { + + return ''; + } + } + + public function cssQueryFunction ( $input, $fn_name, $call_property ) { + + $result = ''; + $args = csscrush_function::parseArgs( $input ); + + if ( count( $args ) < 1 ) { + return $result; + } + + $abstracts =& csscrush::$process->abstracts; + $mixins =& csscrush::$process->mixins; + $selectorRelationships =& csscrush::$process->selectorRelationships; + + // Resolve arguments + $name = array_shift( $args ); + $property = $call_property; + if ( isset( $args[0] ) ) { + if ( $args[0] !== 'default' ) { + $property = array_shift( $args ); + } + else { + array_shift( $args ); + } + } + $default = isset( $args[0] ) ? $args[0] : null; + + // Try to match a abstract rule first + if ( preg_match( csscrush_regex::$patt->ident, $name ) ) { + + // Search order: abstracts, mixins, rules + if ( isset( $abstracts[ $name ]->data[ $property ] ) ) { + + $result = $abstracts[ $name ]->data[ $property ]; + } + elseif ( isset( $mixins[ $name ]->data[ $property ] ) ) { + + $result = $mixins[ $name ]->data[ $property ]; + } + elseif ( isset( $selectorRelationships[ $name ]->data[ $property ] ) ) { + + $result = $selectorRelationships[ $name ]->data[ $property ]; + } + } + else { + + // Look for a rule match + $name = csscrush_selector::makeReadableSelector( $name ); + if ( isset( $selectorRelationships[ $name ]->data[ $property ] ) ) { + + $result = $selectorRelationships[ $name ]->data[ $property ]; + } + } + + if ( $result === '' && ! is_null( $default ) ) { + $result = $default; + } + return $result; + } + + public function updatePropertyTable () { + + // Create a new table of properties + $new_properties_table = array(); + + foreach ( $this as $declaration ) { + + $name = $declaration->property; + + if ( isset( $new_properties_table[ $name ] ) ) { + $new_properties_table[ $name ]++; + } + else { + $new_properties_table[ $name ] = 1; + } + } + + $this->properties = $new_properties_table; + } + + public function addPropertyAliases () { + + $regex = csscrush_regex::$patt; + $aliasedProperties =& csscrush::$process->aliases[ 'properties' ]; + + // First test for the existence of any aliased properties + $intersect = array_intersect( array_keys( $aliasedProperties ), array_keys( $this->properties ) ); + if ( empty( $intersect ) ) { + return; + } + + // Shim in aliased properties + $new_set = array(); + foreach ( $this->declarations as $declaration ) { + $prop = $declaration->property; + if ( + ! $declaration->skip && + isset( $aliasedProperties[ $prop ] ) + ) { + // There are aliases for the current property + foreach ( $aliasedProperties[ $prop ] as $prop_alias ) { + + // If an aliased version already exists to not create one. + if ( $this->propertyCount( $prop_alias ) ) { + continue; + } + + // Create the aliased declaration. + $copy = clone $declaration; + $copy->property = $prop_alias; + + // Set the aliased declaration vendor property. + $copy->vendor = null; + if ( preg_match( $regex->vendorPrefix, $prop_alias, $vendor ) ) { + $copy->vendor = $vendor[1]; + } + $new_set[] = $copy; + } + } + // Un-aliased property or a property alias that has been manually set. + $new_set[] = $declaration; + } + // Re-assign. + $this->declarations = $new_set; + } + + public function addFunctionAliases () { + + $function_aliases =& csscrush::$process->aliases[ 'functions' ]; + $aliased_functions = array_keys( $function_aliases ); + + if ( empty( $aliased_functions ) ) { + return; + } + + $new_set = array(); + + // Keep track of the function aliases we apply and to which property + // they belong, so we can avoid un-unecessary duplications. + $used_fn_aliases = array(); + + // Shim in aliased functions + foreach ( $this->declarations as $declaration ) { + + // No functions, skip + if ( + $declaration->skip || + empty( $declaration->functions ) + ) { + $new_set[] = $declaration; + continue; + } + + // Get list of functions used in declaration that are alias-able, if none skip. + $intersect = array_intersect( $declaration->functions, $aliased_functions ); + if ( empty( $intersect ) ) { + $new_set[] = $declaration; + continue; + } + + // Loop the aliasable functions. + foreach ( $intersect as $fn_name ) { + + if ( $declaration->vendor ) { + // If the property is vendor prefixed we use the vendor prefixed version + // of the function if it exists. + // Else we just skip and use the unprefixed version + $fn_search = "-{$declaration->vendor}-$fn_name"; + if ( in_array( $fn_search, $function_aliases[ $fn_name ] ) ) { + $declaration->value = preg_replace( + '!(^| |,)' . $fn_name . '!', + '${1}' . $fn_search, + $declaration->value + ); + $used_fn_aliases[ $declaration->canonicalProperty ][] = $fn_search; + } + } + else { + + // Duplicate the rule for each alias + foreach ( $function_aliases[ $fn_name ] as $fn_alias ) { + + if ( + isset( $used_fn_aliases[ $declaration->canonicalProperty ] ) && + in_array( $fn_alias, $used_fn_aliases[ $declaration->canonicalProperty ] ) + ) { + // If the function alias has already been applied in a vendor property + // for the same declaration property assume all is good + continue; + } + $copy = clone $declaration; + $copy->value = preg_replace( + '!(^| |,)' . $fn_name . '!', + '${1}' . $fn_alias, + $copy->value + ); + $new_set[] = $copy; + } + } + } + $new_set[] = $declaration; + } + + // Re-assign + $this->declarations = $new_set; + } + + public function addValueAliases () { + + $aliasedValues =& csscrush::$process->aliases[ 'values' ]; + + // First test for the existence of any aliased properties + $intersect = array_intersect( array_keys( $aliasedValues ), array_keys( $this->properties ) ); + + if ( empty( $intersect ) ) { + return; + } + + $new_set = array(); + foreach ( $this->declarations as $declaration ) { + if ( !$declaration->skip ) { + foreach ( $aliasedValues as $value_prop => $value_aliases ) { + if ( $this->propertyCount( $value_prop ) < 1 ) { + continue; + } + foreach ( $value_aliases as $value => $aliases ) { + if ( $declaration->value === $value ) { + foreach ( $aliases as $alias ) { + $copy = clone $declaration; + $copy->value = $alias; + $new_set[] = $copy; + } + } + } + } + } + $new_set[] = $declaration; + } + // Re-assign + $this->declarations = $new_set; + } + + public function expandSelectors () { + + $new_set = array(); + $reg_comma = '!\s*,\s*!'; + + foreach ( $this->selectorList as $readableValue => $selector ) { + + $pos = strpos( $selector->value, ':any?' ); + + if ( $pos !== false ) { + + // Contains an :any statement so we expand + $chain = array( '' ); + do { + if ( $pos === 0 ) { + preg_match( '!:any(\?p\d+\?)!', $selector->value, $m ); + + // Parse the arguments + $expression = trim( csscrush::$process->tokens->p[ $m[1] ], '()' ); + $parts = preg_split( $reg_comma, $expression, null, PREG_SPLIT_NO_EMPTY ); + + $tmp = array(); + foreach ( $chain as $rowCopy ) { + foreach ( $parts as $part ) { + $tmp[] = $rowCopy . $part; + } + } + $chain = $tmp; + $selector->value = substr( $selector->value, strlen( $m[0] ) ); + } + else { + foreach ( $chain as &$row ) { + $row .= substr( $selector->value, 0, $pos ); + } + $selector->value = substr( $selector->value, $pos ); + } + } while ( ( $pos = strpos( $selector->value, ':any?' ) ) !== false ); + + // Finish off + foreach ( $chain as &$row ) { + + // Not creating a named rule association with this expanded selector + $new_set[] = new csscrush_selector( $row . $selector->value ); + } + + // Store the unexpanded selector to selectorRelationships + csscrush::$process->selectorRelationships[ $readableValue ] = $this; + } + else { + + // Nothing to expand + $new_set[ $readableValue ] = $selector; + } - } // foreach + } // foreach - $this->selectorList = $new_set; - } + $this->selectorList = $new_set; + } - public function indexSelectors () { + public function indexSelectors () { - foreach ( $this->selectorList as $selector ) { - csscrush::$process->selectorRelationships[ $selector->readableValue ] = $this; - } - } + foreach ( $this->selectorList as $selector ) { + csscrush::$process->selectorRelationships[ $selector->readableValue ] = $this; + } + } - public function setExtendSelectors ( $raw_value ) { + public function setExtendSelectors ( $raw_value ) { - $abstracts =& csscrush::$process->abstracts; - $selectorRelationships =& csscrush::$process->selectorRelationships; + $abstracts =& csscrush::$process->abstracts; + $selectorRelationships =& csscrush::$process->selectorRelationships; - // Reset if called earlier, last call wins by intention. - $this->extendArgs = array(); + // Reset if called earlier, last call wins by intention. + $this->extendArgs = array(); - foreach ( csscrush_util::splitDelimList( $raw_value ) as $arg ) { - $this->extendArgs[] = new csscrush_extendArg( $arg ); - } - } + foreach ( csscrush_util::splitDelimList( $raw_value ) as $arg ) { + $this->extendArgs[] = new csscrush_extendArg( $arg ); + } + } - public function applyExtendables () { + public function applyExtendables () { - if ( ! $this->extendArgs ) { - return; - } + if ( ! $this->extendArgs ) { + return; + } - $abstracts =& csscrush::$process->abstracts; - $selectorRelationships =& csscrush::$process->selectorRelationships; + $abstracts =& csscrush::$process->abstracts; + $selectorRelationships =& csscrush::$process->selectorRelationships; - // Filter the extendArgs list to usable references - foreach ( $this->extendArgs as $key => $extend_arg ) { + // Filter the extendArgs list to usable references + foreach ( $this->extendArgs as $key => $extend_arg ) { - $name = $extend_arg->name; + $name = $extend_arg->name; - if ( isset( $abstracts[ $name ] ) ) { + if ( isset( $abstracts[ $name ] ) ) { - $parent_rule = $abstracts[ $name ]; - $extend_arg->pointer = $parent_rule; + $parent_rule = $abstracts[ $name ]; + $extend_arg->pointer = $parent_rule; - } - elseif ( isset( $selectorRelationships[ $name ] ) ) { + } + elseif ( isset( $selectorRelationships[ $name ] ) ) { - $parent_rule = $selectorRelationships[ $name ]; - $extend_arg->pointer = $parent_rule; + $parent_rule = $selectorRelationships[ $name ]; + $extend_arg->pointer = $parent_rule; - } - else { + } + else { - // Unusable, so unset it - unset( $this->extendArgs[ $key ] ); - } - } + // Unusable, so unset it + unset( $this->extendArgs[ $key ] ); + } + } - // Create a stack of all parent rule args - $parent_extend_args = array(); - foreach ( $this->extendArgs as $extend_arg ) { - $parent_extend_args = array_merge( $parent_extend_args, $extend_arg->pointer->extendArgs ); - } + // Create a stack of all parent rule args + $parent_extend_args = array(); + foreach ( $this->extendArgs as $extend_arg ) { + $parent_extend_args = array_merge( $parent_extend_args, $extend_arg->pointer->extendArgs ); + } - // Merge this rule's extendArgs with parent extendArgs - $this->extendArgs = array_merge( $this->extendArgs, $parent_extend_args ); + // Merge this rule's extendArgs with parent extendArgs + $this->extendArgs = array_merge( $this->extendArgs, $parent_extend_args ); - // Filter now? + // Filter now? - // Add this rule's selectors to all extendArgs - foreach ( $this->extendArgs as $extend_arg ) { + // Add this rule's selectors to all extendArgs + foreach ( $this->extendArgs as $extend_arg ) { - $ancestor = $extend_arg->pointer; + $ancestor = $extend_arg->pointer; - $extend_selectors = $this->selectorList; + $extend_selectors = $this->selectorList; - // If there is a pseudo class extension create a new set accordingly - if ( $extend_arg->pseudo ) { + // If there is a pseudo class extension create a new set accordingly + if ( $extend_arg->pseudo ) { - $extend_selectors = array(); - foreach ( $this->selectorList as $readable => $selector ) { - $new_selector = clone $selector; - $new_readable = $new_selector->appendPseudo( $extend_arg->pseudo ); - $extend_selectors[ $new_readable ] = $new_selector; - } - } + $extend_selectors = array(); + foreach ( $this->selectorList as $readable => $selector ) { + $new_selector = clone $selector; + $new_readable = $new_selector->appendPseudo( $extend_arg->pseudo ); + $extend_selectors[ $new_readable ] = $new_selector; + } + } - $ancestor->addSelectors( $extend_selectors ); - } - } + $ancestor->addSelectors( $extend_selectors ); + } + } - public function addSelector ( $selector ) { + public function addSelector ( $selector ) { - $this->selectorList[ $selector->readableValue ] = $selector; - } + $this->selectorList[ $selector->readableValue ] = $selector; + } - public function addSelectors ( $list ) { + public function addSelectors ( $list ) { - $this->selectorList = array_merge( $this->selectorList, $list ); - } + $this->selectorList = array_merge( $this->selectorList, $list ); + } - ############ - # IteratorAggregate + ############ + # IteratorAggregate - public function getIterator () { - return new ArrayIterator( $this->declarations ); - } + public function getIterator () { + return new ArrayIterator( $this->declarations ); + } - ############ - # Countable + ############ + # Countable - public function count() { + public function count() { - return count( $this->_declarations ); - } + return count( $this->_declarations ); + } - ############ - # Rule API + ############ + # Rule API - public function propertyCount ( $prop ) { + public function propertyCount ( $prop ) { - if ( isset( $this->properties[ $prop ] ) ) { - return $this->properties[ $prop ]; - } - return 0; - } + if ( isset( $this->properties[ $prop ] ) ) { + return $this->properties[ $prop ]; + } + return 0; + } - public function addProperty ( $prop ) { + public function addProperty ( $prop ) { - if ( isset( $this->properties[ $prop ] ) ) { - $this->properties[ $prop ]++; - } - else { - $this->properties[ $prop ] = 1; - } - } + if ( isset( $this->properties[ $prop ] ) ) { + $this->properties[ $prop ]++; + } + else { + $this->properties[ $prop ] = 1; + } + } - public function addDeclaration ( $prop, $value, $contextIndex = 0 ) { + public function addDeclaration ( $prop, $value, $contextIndex = 0 ) { - // Create declaration, add to the stack if it's valid - $declaration = new csscrush_declaration( $prop, $value, $contextIndex ); + // Create declaration, add to the stack if it's valid + $declaration = new csscrush_declaration( $prop, $value, $contextIndex ); - if ( $declaration->isValid ) { + if ( $declaration->isValid ) { - // Manually increment the property name since we're directly updating the _declarations list - $this->addProperty( $prop ); - $this->_declarations[] = $declaration; - return $declaration; - } + // Manually increment the property name since we're directly updating the _declarations list + $this->addProperty( $prop ); + $this->_declarations[] = $declaration; + return $declaration; + } - return false; - } + return false; + } - static public function get ( $token ) { + static public function get ( $token ) { - if ( isset( csscrush::$process->tokens->r[ $token ] ) ) { - return csscrush::$process->tokens->r[ $token ]; - } - return null; - } + if ( isset( csscrush::$process->tokens->r[ $token ] ) ) { + return csscrush::$process->tokens->r[ $token ]; + } + return null; + } } @@ -738,105 +738,105 @@ static public function get ( $token ) { */ class csscrush_declaration { - public $property; - public $canonicalProperty; - public $vendor; - public $functions; - public $value; - public $index; - public $skip; - public $important; - public $isValid = true; - - public function __construct ( $prop, $value, $contextIndex = 0 ) { - - $regex = csscrush_regex::$patt; - - // Normalize input. Lowercase the property name - $prop = strtolower( trim( $prop ) ); - $value = trim( $value ); - - // Check the input - if ( $prop === '' || $value === '' || $value === null ) { - $this->isValid = false; - return; - } - - // Test for escape tilde - if ( $skip = strpos( $prop, '~' ) === 0 ) { - $prop = substr( $prop, 1 ); - } - - // Store the canonical property name. - // Store the vendor mark if one is present. - if ( preg_match( $regex->vendorPrefix, $prop, $vendor ) ) { - $canonical_property = $vendor[2]; - $vendor = $vendor[1]; - } - else { - $vendor = null; - $canonical_property = $prop; - } - - // Check for !important keywords - if ( ( $important = strpos( $value, '!important' ) ) !== false ) { - $value = rtrim( substr( $value, 0, $important ) ); - $important = true; - } - - // Ignore declarations with null css values - if ( $value === false || $value === '' ) { - $this->isValid = false; - return; - } - - // Apply custom functions - if ( ! $skip ) { - csscrush_function::executeCustomFunctions( $value ); - } - - // Capture all remaining paren pairs. - csscrush::$process->captureParens( $value ); - - // Create an index of all regular functions in the value. - if ( preg_match_all( $regex->function, $value, $functions ) > 0 ) { - $out = array(); - foreach ( $functions[2] as $index => $fn_name ) { - $out[] = $fn_name; - } - $functions = array_unique( $out ); - } - else { - $functions = array(); - } - - $this->property = $prop; - $this->canonicalProperty = $canonical_property; - $this->vendor = $vendor; - $this->functions = $functions; - $this->index = $contextIndex; - $this->value = $value; - $this->skip = $skip; - $this->important = $important; - } - - public function __toString () { - - if ( csscrush::$process->options->minify ) { - $whitespace = ''; - } - else { - $whitespace = ' '; - } - $important = $this->important ? "$whitespace!important" : ''; - - return "$this->property:$whitespace$this->value$important"; - } - - public function getFullValue () { - - return csscrush::$process->restoreTokens( $this->value, 'p' ); - } + public $property; + public $canonicalProperty; + public $vendor; + public $functions; + public $value; + public $index; + public $skip; + public $important; + public $isValid = true; + + public function __construct ( $prop, $value, $contextIndex = 0 ) { + + $regex = csscrush_regex::$patt; + + // Normalize input. Lowercase the property name + $prop = strtolower( trim( $prop ) ); + $value = trim( $value ); + + // Check the input + if ( $prop === '' || $value === '' || $value === null ) { + $this->isValid = false; + return; + } + + // Test for escape tilde + if ( $skip = strpos( $prop, '~' ) === 0 ) { + $prop = substr( $prop, 1 ); + } + + // Store the canonical property name. + // Store the vendor mark if one is present. + if ( preg_match( $regex->vendorPrefix, $prop, $vendor ) ) { + $canonical_property = $vendor[2]; + $vendor = $vendor[1]; + } + else { + $vendor = null; + $canonical_property = $prop; + } + + // Check for !important keywords + if ( ( $important = strpos( $value, '!important' ) ) !== false ) { + $value = rtrim( substr( $value, 0, $important ) ); + $important = true; + } + + // Ignore declarations with null css values + if ( $value === false || $value === '' ) { + $this->isValid = false; + return; + } + + // Apply custom functions + if ( ! $skip ) { + csscrush_function::executeCustomFunctions( $value ); + } + + // Capture all remaining paren pairs. + csscrush::$process->captureParens( $value ); + + // Create an index of all regular functions in the value. + if ( preg_match_all( $regex->function, $value, $functions ) > 0 ) { + $out = array(); + foreach ( $functions[2] as $index => $fn_name ) { + $out[] = $fn_name; + } + $functions = array_unique( $out ); + } + else { + $functions = array(); + } + + $this->property = $prop; + $this->canonicalProperty = $canonical_property; + $this->vendor = $vendor; + $this->functions = $functions; + $this->index = $contextIndex; + $this->value = $value; + $this->skip = $skip; + $this->important = $important; + } + + public function __toString () { + + if ( csscrush::$process->options->minify ) { + $whitespace = ''; + } + else { + $whitespace = ' '; + } + $important = $this->important ? "$whitespace!important" : ''; + + return "$this->property:$whitespace$this->value$important"; + } + + public function getFullValue () { + + return csscrush::$process->restoreTokens( $this->value, 'p' ); + } } @@ -848,61 +848,61 @@ public function getFullValue () { */ class csscrush_selector { - public $value; - public $readableValue; - public $allowPrefix = true; + public $value; + public $readableValue; + public $allowPrefix = true; - static function makeReadableSelector ( $selector_string ) { + static function makeReadableSelector ( $selector_string ) { - // Quick test for paren tokens. - if ( strpos( $selector_string, '?p' ) !== false ) { - $selector_string = csscrush::$process->restoreTokens( $selector_string, 'p' ); - } + // Quick test for paren tokens. + if ( strpos( $selector_string, '?p' ) !== false ) { + $selector_string = csscrush::$process->restoreTokens( $selector_string, 'p' ); + } - // Create space around combinators, then normalize whitespace. - $selector_string = preg_replace( '#([>+]|~(?!=))#', ' $1 ', $selector_string ); - $selector_string = csscrush_util::normalizeWhiteSpace( $selector_string ); + // Create space around combinators, then normalize whitespace. + $selector_string = preg_replace( '#([>+]|~(?!=))#', ' $1 ', $selector_string ); + $selector_string = csscrush_util::normalizeWhiteSpace( $selector_string ); - // Quick test for string tokens. - if ( strpos( $selector_string, '?s' ) !== false ) { - $selector_string = csscrush::$process->restoreTokens( $selector_string, 's' ); - } + // Quick test for string tokens. + if ( strpos( $selector_string, '?s' ) !== false ) { + $selector_string = csscrush::$process->restoreTokens( $selector_string, 's' ); + } - // Quick test for double-colons for backwards compat. - if ( strpos( $selector_string, '::' ) !== false ) { - $selector_string = preg_replace( '!::(after|before|first-(?:letter|line))!iS', ':$1', $selector_string ); - } + // Quick test for double-colons for backwards compat. + if ( strpos( $selector_string, '::' ) !== false ) { + $selector_string = preg_replace( '!::(after|before|first-(?:letter|line))!iS', ':$1', $selector_string ); + } - return $selector_string; - } + return $selector_string; + } - public function __construct ( $raw_selector, $associated_rule = null ) { + public function __construct ( $raw_selector, $associated_rule = null ) { - if ( strpos( $raw_selector, '^' ) === 0 ) { + if ( strpos( $raw_selector, '^' ) === 0 ) { - $raw_selector = ltrim( $raw_selector, "^ \n\r\t" ); - $this->allowPrefix = false; - } + $raw_selector = ltrim( $raw_selector, "^ \n\r\t" ); + $this->allowPrefix = false; + } - $this->readableValue = self::makeReadableSelector( $raw_selector ); - $this->value = $raw_selector; - } + $this->readableValue = self::makeReadableSelector( $raw_selector ); + $this->value = $raw_selector; + } - public function __toString () { + public function __toString () { - return $this->readableValue; - } + return $this->readableValue; + } - public function appendPseudo ( $pseudo ) { + public function appendPseudo ( $pseudo ) { - // Check to avoid doubling-up - if ( ! csscrush_stream::endsWith( $this->readableValue, $pseudo ) ) { + // Check to avoid doubling-up + if ( ! csscrush_stream::endsWith( $this->readableValue, $pseudo ) ) { - $this->readableValue .= $pseudo; - $this->value .= $pseudo; - } - return $this->readableValue; - } + $this->readableValue .= $pseudo; + $this->value .= $pseudo; + } + return $this->readableValue; + } } @@ -913,28 +913,28 @@ public function appendPseudo ( $pseudo ) { */ class csscrush_extendArg { - public $pointer; - public $name; - public $pseudo; + public $pointer; + public $name; + public $pseudo; - public function __construct ( $name ) { + public function __construct ( $name ) { - $this->name = $name; + $this->name = $name; - if ( ! preg_match( csscrush_regex::$patt->ident, $this->name ) ) { + if ( ! preg_match( csscrush_regex::$patt->ident, $this->name ) ) { - // Not a regular name: Some kind of selector so normalize it for later comparison - $this->name = csscrush_selector::makeReadableSelector( $this->name ); + // Not a regular name: Some kind of selector so normalize it for later comparison + $this->name = csscrush_selector::makeReadableSelector( $this->name ); - // If applying the pseudo on output store - if ( substr( $this->name, -1 ) === '!' ) { + // If applying the pseudo on output store + if ( substr( $this->name, -1 ) === '!' ) { - $this->name = rtrim( $this->name, ' !' ); - if ( preg_match( '!\:\:?[\w-]+$!', $this->name, $m ) ) { - $this->pseudo = $m[0]; - } - } - } - } + $this->name = rtrim( $this->name, ' !' ); + if ( preg_match( '!\:\:?[\w-]+$!', $this->name, $m ) ) { + $this->pseudo = $m[0]; + } + } + } + } } diff --git a/lib/Util.php b/lib/Util.php index 318fa87..27b0d68 100644 --- a/lib/Util.php +++ b/lib/Util.php @@ -6,138 +6,138 @@ */ class csscrush_util { - // Create html attribute string from array. - static public function htmlAttributes ( array $attributes ) { - - $attr_string = ''; - foreach ( $attributes as $name => $value ) { - $value = htmlspecialchars( $value, ENT_COMPAT, 'UTF-8', false ); - $attr_string .= " $name=\"$value\""; - } - return $attr_string; - } - - static public function normalizePath ( $path, $strip_drive_letter = false ) { - - if ( $strip_drive_letter ) { - $path = preg_replace( '!^[a-z]\:!i', '', $path ); - } - // Backslashes and repeat slashes to a single forward slash. - $path = rtrim( preg_replace( '![\\\\/]+!', '/', $path ), '/' ); - - // Removing redundant './'. - $path = str_replace( '/./', '/', $path ); - if ( strpos( $path, './' ) === 0 ) { - $path = substr( $path, 2 ); - } - - return $path; - } - - static public function find () { - - foreach ( func_get_args() as $file ) { - $file_path = csscrush::$config->location . '/' . $file; - if ( file_exists( $file_path ) ) { - return $file_path; - } - } - return false; - } - - static public function stripCommentTokens ( $str ) { - - return preg_replace( csscrush_regex::$patt->cToken, '', $str ); - } - - static public function normalizeWhiteSpace ( $str ) { - - $replacements = array( - // Convert all whitespace sequences to a single space. - '!\s+!S' => ' ', - // Trim bracket whitespace where it's safe to do it. - '!([\[(]) | ([\])])| ?([{}]) ?!S' => '${1}${2}${3}', - // Trim whitespace around delimiters and special characters. - '! ?([;,]) ?!S' => '$1', - ); - return preg_replace( - array_keys( $replacements ), array_values( $replacements ), $str ); - } - - static public function splitDelimList ( $str, $delim = ',', $trim = true ) { - - $do_preg_split = strlen( $delim ) > 1 ? true : false; - - if ( ! $do_preg_split && strpos( $str, $delim ) === false ) { - if ( $trim ) { - $str = trim( $str ); - } - return array( $str ); - } - - if ( strpos( $str, '(' ) !== false ) { - $match_count - = preg_match_all( csscrush_regex::$patt->balancedParens, $str, $matches ); - } - else { - $match_count = 0; - } - - if ( $match_count ) { - $keys = array(); - foreach ( $matches[0] as $index => &$value ) { - $keys[] = "?$index?"; - } - $str = str_replace( $matches[0], $keys, $str ); - } - - if ( $do_preg_split ) { - $list = preg_split( '!' . $delim . '!', $str ); - } - else { - $list = explode( $delim, $str ); - } - - if ( $match_count ) { - foreach ( $list as &$value ) { - $value = str_replace( $keys, $matches[0], $value ); - } - } - - if ( $trim ) { - $list = array_map( 'trim', $list ); - } - - return $list; - } - - static public function getLinkBetweenDirs ( $dir1, $dir2 ) { - - // Normalise the paths. - $dir1 = trim( csscrush_util::normalizePath( $dir1, true ), '/' ); - $dir2 = trim( csscrush_util::normalizePath( $dir2, true ), '/' ); - - // The link between. - $link = ''; - - if ( $dir1 != $dir2 ) { - - // Split the directory paths into arrays so we can compare segment by segment. - $dir1_segs = explode( '/', $dir1 ); - $dir2_segs = explode( '/', $dir2 ); - - // Shift the segments until they are on different branches. - while ( isset( $dir1_segs[0] ) && isset( $dir2_segs[0] ) && ( $dir1_segs[0] === $dir2_segs[0] ) ) { - array_shift( $dir1_segs ); - array_shift( $dir2_segs ); - } - - $link = str_repeat( '../', count( $dir1_segs ) ) . implode( '/', $dir2_segs ); - } - - // Add closing slash. - return $link !== '' ? rtrim( $link, '/' ) . '/' : ''; - } + // Create html attribute string from array. + static public function htmlAttributes ( array $attributes ) { + + $attr_string = ''; + foreach ( $attributes as $name => $value ) { + $value = htmlspecialchars( $value, ENT_COMPAT, 'UTF-8', false ); + $attr_string .= " $name=\"$value\""; + } + return $attr_string; + } + + static public function normalizePath ( $path, $strip_drive_letter = false ) { + + if ( $strip_drive_letter ) { + $path = preg_replace( '!^[a-z]\:!i', '', $path ); + } + // Backslashes and repeat slashes to a single forward slash. + $path = rtrim( preg_replace( '![\\\\/]+!', '/', $path ), '/' ); + + // Removing redundant './'. + $path = str_replace( '/./', '/', $path ); + if ( strpos( $path, './' ) === 0 ) { + $path = substr( $path, 2 ); + } + + return $path; + } + + static public function find () { + + foreach ( func_get_args() as $file ) { + $file_path = csscrush::$config->location . '/' . $file; + if ( file_exists( $file_path ) ) { + return $file_path; + } + } + return false; + } + + static public function stripCommentTokens ( $str ) { + + return preg_replace( csscrush_regex::$patt->cToken, '', $str ); + } + + static public function normalizeWhiteSpace ( $str ) { + + $replacements = array( + // Convert all whitespace sequences to a single space. + '!\s+!S' => ' ', + // Trim bracket whitespace where it's safe to do it. + '!([\[(]) | ([\])])| ?([{}]) ?!S' => '${1}${2}${3}', + // Trim whitespace around delimiters and special characters. + '! ?([;,]) ?!S' => '$1', + ); + return preg_replace( + array_keys( $replacements ), array_values( $replacements ), $str ); + } + + static public function splitDelimList ( $str, $delim = ',', $trim = true ) { + + $do_preg_split = strlen( $delim ) > 1 ? true : false; + + if ( ! $do_preg_split && strpos( $str, $delim ) === false ) { + if ( $trim ) { + $str = trim( $str ); + } + return array( $str ); + } + + if ( strpos( $str, '(' ) !== false ) { + $match_count + = preg_match_all( csscrush_regex::$patt->balancedParens, $str, $matches ); + } + else { + $match_count = 0; + } + + if ( $match_count ) { + $keys = array(); + foreach ( $matches[0] as $index => &$value ) { + $keys[] = "?$index?"; + } + $str = str_replace( $matches[0], $keys, $str ); + } + + if ( $do_preg_split ) { + $list = preg_split( '!' . $delim . '!', $str ); + } + else { + $list = explode( $delim, $str ); + } + + if ( $match_count ) { + foreach ( $list as &$value ) { + $value = str_replace( $keys, $matches[0], $value ); + } + } + + if ( $trim ) { + $list = array_map( 'trim', $list ); + } + + return $list; + } + + static public function getLinkBetweenDirs ( $dir1, $dir2 ) { + + // Normalise the paths. + $dir1 = trim( csscrush_util::normalizePath( $dir1, true ), '/' ); + $dir2 = trim( csscrush_util::normalizePath( $dir2, true ), '/' ); + + // The link between. + $link = ''; + + if ( $dir1 != $dir2 ) { + + // Split the directory paths into arrays so we can compare segment by segment. + $dir1_segs = explode( '/', $dir1 ); + $dir2_segs = explode( '/', $dir2 ); + + // Shift the segments until they are on different branches. + while ( isset( $dir1_segs[0] ) && isset( $dir2_segs[0] ) && ( $dir1_segs[0] === $dir2_segs[0] ) ) { + array_shift( $dir1_segs ); + array_shift( $dir2_segs ); + } + + $link = str_repeat( '../', count( $dir1_segs ) ) . implode( '/', $dir2_segs ); + } + + // Add closing slash. + return $link !== '' ? rtrim( $link, '/' ) . '/' : ''; + } } @@ -148,65 +148,65 @@ static public function getLinkBetweenDirs ( $dir1, $dir2 ) { */ class csscrush_balancedMatch { - public function __construct ( csscrush_stream $stream, $offset, $brackets = '{}' ) { + public function __construct ( csscrush_stream $stream, $offset, $brackets = '{}' ) { - $this->stream = $stream; - $this->offset = $offset; - $this->match = null; - $this->length = 0; + $this->stream = $stream; + $this->offset = $offset; + $this->match = null; + $this->length = 0; - list( $opener, $closer ) = str_split( $brackets, 1 ); + list( $opener, $closer ) = str_split( $brackets, 1 ); - if ( strpos( $stream->raw, $opener, $this->offset ) === false ) { - return; - } + if ( strpos( $stream->raw, $opener, $this->offset ) === false ) { + return; + } - if ( substr_count( $stream->raw, $opener ) !== substr_count( $stream->raw, $closer ) ) { - $sample = substr( $stream->raw, $this->offset, 25 ); - trigger_error( __METHOD__ . ": Unmatched token near '$sample'.\n", E_USER_WARNING ); - return; - } + if ( substr_count( $stream->raw, $opener ) !== substr_count( $stream->raw, $closer ) ) { + $sample = substr( $stream->raw, $this->offset, 25 ); + trigger_error( __METHOD__ . ": Unmatched token near '$sample'.\n", E_USER_WARNING ); + return; + } - $patt = $opener === '{' ? - csscrush_regex::$patt->balancedCurlies : csscrush_regex::$patt->balancedParens; + $patt = $opener === '{' ? + csscrush_regex::$patt->balancedCurlies : csscrush_regex::$patt->balancedParens; - if ( preg_match( $patt, $stream->raw, $m, PREG_OFFSET_CAPTURE, $this->offset ) ) { + if ( preg_match( $patt, $stream->raw, $m, PREG_OFFSET_CAPTURE, $this->offset ) ) { - $this->match = $m; - $this->matchLength = strlen( $m[0][0] ); - $this->matchStart = $m[0][1]; - $this->matchEnd = $this->matchStart + $this->matchLength; - $this->length = $this->matchEnd - $this->offset; - } - else { - trigger_error( __METHOD__ . ": Could not match '$opener'. Exiting.\n", E_USER_WARNING ); - } - } + $this->match = $m; + $this->matchLength = strlen( $m[0][0] ); + $this->matchStart = $m[0][1]; + $this->matchEnd = $this->matchStart + $this->matchLength; + $this->length = $this->matchEnd - $this->offset; + } + else { + trigger_error( __METHOD__ . ": Could not match '$opener'. Exiting.\n", E_USER_WARNING ); + } + } - public function inside () { + public function inside () { - return $this->match[1][0]; - } + return $this->match[1][0]; + } - public function whole () { + public function whole () { - return substr( $this->stream->raw, $this->offset, $this->length ); - } + return substr( $this->stream->raw, $this->offset, $this->length ); + } - public function replace ( $replacement ) { + public function replace ( $replacement ) { - $this->stream->splice( $replacement, $this->offset, $this->length ); - } + $this->stream->splice( $replacement, $this->offset, $this->length ); + } - public function unWrap () { + public function unWrap () { - $this->stream->splice( $this->inside(), $this->offset, $this->length ); - } + $this->stream->splice( $this->inside(), $this->offset, $this->length ); + } - public function nextIndexOf ( $needle ) { + public function nextIndexOf ( $needle ) { - return strpos( $this->stream->raw, $needle, $this->offset ); - } + return strpos( $this->stream->raw, $needle, $this->offset ); + } } @@ -217,129 +217,129 @@ public function nextIndexOf ( $needle ) { */ class csscrush_url { - public $protocol; - public $isRelative; - public $isRooted; - public $convertToData; - public $value; - public $label; - - public function __construct ( $raw_value, $convert_to_data = false ) { - - $regex = csscrush_regex::$patt; - $process = csscrush::$process; - - if ( preg_match( $regex->sToken, $raw_value ) ) { - $this->value = trim( $process->fetchToken( $raw_value ), '\'"' ); - $process->releaseToken( $raw_value ); - } - else { - $this->value = $raw_value; - } - - $this->evaluate(); - $this->label = $process->addToken( $this, 'u' ); - } - - public function __toString () { - $quote = ''; - if ( preg_match( '![()*]!', $this->value ) || 'data' === $this->protocol ) { - $quote = '"'; - } - return "url(http://github.com/$quote$this-%3Evalue$quote)"; - } - - static public function get ( $token ) { - return csscrush::$process->tokens->u[ $token ]; - } - - public function evaluate () { - - $leading_variable = strpos( $this->value, '$(' ) === 0; - - if ( preg_match( '!^([a-z]+)\:!i', $this->value, $m ) ) { - $this->protocol = strtolower( $m[1] ); - } - else { - // Normalize './' led paths. - $this->value = preg_replace( '!^\.\/+!i', '', $this->value ); - if ( $this->value[0] === '/' ) { - $this->isRooted = true; - } - elseif ( ! $leading_variable ) { - $this->isRelative = true; - } - // Normalize slashes. - $this->value = rtrim( preg_replace( '![\\\\/]+!', '/', $this->value ), '/' ); - } - } - - public function toData () { - - if ( $this->isRooted ) { - $file = csscrush::$config->docRoot . $this->value; - } - else { - $file = csscrush::$process->input->dir . "/$this->value"; - } - - // File not found. - if ( ! file_exists( $file ) ) { - return; - } - - $file_ext = pathinfo( $file, PATHINFO_EXTENSION ); - - // Only allow certain extensions - static $allowed_file_extensions = array( - 'woff' => 'application/x-font-woff;charset=utf-8', - 'ttf' => 'font/truetype;charset=utf-8', - 'svg' => 'image/svg+xml', - 'svgz' => 'image/svg+xml', - 'gif' => 'image/gif', - 'jpeg' => 'image/jpg', - 'jpg' => 'image/jpg', - 'png' => 'image/png', - ); - - if ( ! isset( $allowed_file_extensions[ $file_ext ] ) ) { - return; - } - - $mime_type = $allowed_file_extensions[ $file_ext ]; - $base64 = base64_encode( file_get_contents( $file ) ); - $this->value = "data:$mime_type;base64,$base64"; - $this->protocol = 'data'; - } - - public function prepend ( $path_fragment ) { - $this->value = $path_fragment . $this->value; - } - - public function resolveRootedPath () { - - $config = csscrush::$config; - $process = csscrush::$process; - - if ( ! file_exists ( $config->docRoot . $this->value ) ) { - return false; - } - - // Move upwards '..' by the number of slashes in baseURL to get a relative path. - $this->value = str_repeat( '../', substr_count( $process->input->dirUrl, '/' ) ) . - substr( $this->value, 1 ); - } - - public function simplify () { - - // Reduce redundant path segments (issue #32): - // e.g 'foo/../bar' => 'bar' - $patt = '![^/.]+/\.\./!'; - - while ( preg_match( $patt, $this->value ) ) { - $this->value = preg_replace( $patt, '', $this->value ); - } - } + public $protocol; + public $isRelative; + public $isRooted; + public $convertToData; + public $value; + public $label; + + public function __construct ( $raw_value, $convert_to_data = false ) { + + $regex = csscrush_regex::$patt; + $process = csscrush::$process; + + if ( preg_match( $regex->sToken, $raw_value ) ) { + $this->value = trim( $process->fetchToken( $raw_value ), '\'"' ); + $process->releaseToken( $raw_value ); + } + else { + $this->value = $raw_value; + } + + $this->evaluate(); + $this->label = $process->addToken( $this, 'u' ); + } + + public function __toString () { + $quote = ''; + if ( preg_match( '![()*]!', $this->value ) || 'data' === $this->protocol ) { + $quote = '"'; + } + return "url(http://github.com/$quote$this-%3Evalue$quote)"; + } + + static public function get ( $token ) { + return csscrush::$process->tokens->u[ $token ]; + } + + public function evaluate () { + + $leading_variable = strpos( $this->value, '$(' ) === 0; + + if ( preg_match( '!^([a-z]+)\:!i', $this->value, $m ) ) { + $this->protocol = strtolower( $m[1] ); + } + else { + // Normalize './' led paths. + $this->value = preg_replace( '!^\.\/+!i', '', $this->value ); + if ( $this->value[0] === '/' ) { + $this->isRooted = true; + } + elseif ( ! $leading_variable ) { + $this->isRelative = true; + } + // Normalize slashes. + $this->value = rtrim( preg_replace( '![\\\\/]+!', '/', $this->value ), '/' ); + } + } + + public function toData () { + + if ( $this->isRooted ) { + $file = csscrush::$config->docRoot . $this->value; + } + else { + $file = csscrush::$process->input->dir . "/$this->value"; + } + + // File not found. + if ( ! file_exists( $file ) ) { + return; + } + + $file_ext = pathinfo( $file, PATHINFO_EXTENSION ); + + // Only allow certain extensions + static $allowed_file_extensions = array( + 'woff' => 'application/x-font-woff;charset=utf-8', + 'ttf' => 'font/truetype;charset=utf-8', + 'svg' => 'image/svg+xml', + 'svgz' => 'image/svg+xml', + 'gif' => 'image/gif', + 'jpeg' => 'image/jpg', + 'jpg' => 'image/jpg', + 'png' => 'image/png', + ); + + if ( ! isset( $allowed_file_extensions[ $file_ext ] ) ) { + return; + } + + $mime_type = $allowed_file_extensions[ $file_ext ]; + $base64 = base64_encode( file_get_contents( $file ) ); + $this->value = "data:$mime_type;base64,$base64"; + $this->protocol = 'data'; + } + + public function prepend ( $path_fragment ) { + $this->value = $path_fragment . $this->value; + } + + public function resolveRootedPath () { + + $config = csscrush::$config; + $process = csscrush::$process; + + if ( ! file_exists ( $config->docRoot . $this->value ) ) { + return false; + } + + // Move upwards '..' by the number of slashes in baseURL to get a relative path. + $this->value = str_repeat( '../', substr_count( $process->input->dirUrl, '/' ) ) . + substr( $this->value, 1 ); + } + + public function simplify () { + + // Reduce redundant path segments (issue #32): + // e.g 'foo/../bar' => 'bar' + $patt = '![^/.]+/\.\./!'; + + while ( preg_match( $patt, $this->value ) ) { + $this->value = preg_replace( $patt, '', $this->value ); + } + } } @@ -350,101 +350,101 @@ public function simplify () { */ class csscrush_stream { - public function __construct ( $str ) { - $this->raw = $str; - } - - public function __toString () { - return $this->raw; - } - - static public function endsWith ( $haystack, $needle ) { - - return substr( $haystack, -strlen( $needle ) ) === $needle; - } - - public function update ( $str ) { - $this->raw = $str; - return $this; - } - - public function substr ( $start, $length = null ) { - if ( is_null( $length ) ) { - return substr( $this->raw, $start ); - } - else { - return substr( $this->raw, $start, $length ); - } - } - - public function matchAll ( $patt, $preprocess_patt = false ) { - return csscrush_regex::matchAll( $patt, $this->raw, $preprocess_patt ); - } - - public function replace ( $find, $replacement ) { - $this->raw = str_replace( $find, $replacement, $this->raw ); - return $this; - } - - public function replaceHash ( $replacements ) { - if ( $replacements ) { - $this->raw = str_replace( - array_keys( $replacements ), - array_values( $replacements ), - $this->raw ); - } - return $this; - } - - public function pregReplace ( $patt, $replacement ) { - $this->raw = preg_replace( $patt, $replacement, $this->raw ); - return $this; - } - - public function pregReplaceCallback ( $patt, $callback ) { - $this->raw = preg_replace_callback( $patt, $callback, $this->raw ); - return $this; - } - - public function pregReplaceHash ( $replacements ) { - if ( $replacements ) { - $this->raw = preg_replace( - array_keys( $replacements ), - array_values( $replacements ), - $this->raw ); - } - return $this; - } - - public function append ( $append ) { - $this->raw .= $append; - return $this; - } - - public function prepend ( $prepend ) { - $this->raw = $prepend . $this->raw; - return $this; - } - - public function splice ( $replacement, $offset, $length = null ) { - $this->raw = substr_replace( $this->raw, $replacement, $offset, $length ); - return $this; - } - - public function trim () { - $this->raw = trim( $this->raw ); - return $this; - } - - public function rTrim () { - $this->raw = rtrim( $this->raw ); - return $this; - } - - public function lTrim () { - $this->raw = ltrim( $this->raw ); - return $this; - } + public function __construct ( $str ) { + $this->raw = $str; + } + + public function __toString () { + return $this->raw; + } + + static public function endsWith ( $haystack, $needle ) { + + return substr( $haystack, -strlen( $needle ) ) === $needle; + } + + public function update ( $str ) { + $this->raw = $str; + return $this; + } + + public function substr ( $start, $length = null ) { + if ( is_null( $length ) ) { + return substr( $this->raw, $start ); + } + else { + return substr( $this->raw, $start, $length ); + } + } + + public function matchAll ( $patt, $preprocess_patt = false ) { + return csscrush_regex::matchAll( $patt, $this->raw, $preprocess_patt ); + } + + public function replace ( $find, $replacement ) { + $this->raw = str_replace( $find, $replacement, $this->raw ); + return $this; + } + + public function replaceHash ( $replacements ) { + if ( $replacements ) { + $this->raw = str_replace( + array_keys( $replacements ), + array_values( $replacements ), + $this->raw ); + } + return $this; + } + + public function pregReplace ( $patt, $replacement ) { + $this->raw = preg_replace( $patt, $replacement, $this->raw ); + return $this; + } + + public function pregReplaceCallback ( $patt, $callback ) { + $this->raw = preg_replace_callback( $patt, $callback, $this->raw ); + return $this; + } + + public function pregReplaceHash ( $replacements ) { + if ( $replacements ) { + $this->raw = preg_replace( + array_keys( $replacements ), + array_values( $replacements ), + $this->raw ); + } + return $this; + } + + public function append ( $append ) { + $this->raw .= $append; + return $this; + } + + public function prepend ( $prepend ) { + $this->raw = $prepend . $this->raw; + return $this; + } + + public function splice ( $replacement, $offset, $length = null ) { + $this->raw = substr_replace( $this->raw, $replacement, $offset, $length ); + return $this; + } + + public function trim () { + $this->raw = trim( $this->raw ); + return $this; + } + + public function rTrim () { + $this->raw = rtrim( $this->raw ); + return $this; + } + + public function lTrim () { + $this->raw = ltrim( $this->raw ); + return $this; + } } @@ -455,67 +455,67 @@ public function lTrim () { */ class csscrush_version { - public $major = 0; - public $minor = 0; - public $revision = 0; - public $extra; + public $major = 0; + public $minor = 0; + public $revision = 0; + public $extra; - public function __construct ( $version_string ) { + public function __construct ( $version_string ) { - if ( ( $hyphen_pos = strpos( $version_string, '-' ) ) !== false ) { - $this->extra = substr( $version_string, $hyphen_pos + 1 ); - $version_string = substr( $version_string, 0, $hyphen_pos ); - } + if ( ( $hyphen_pos = strpos( $version_string, '-' ) ) !== false ) { + $this->extra = substr( $version_string, $hyphen_pos + 1 ); + $version_string = substr( $version_string, 0, $hyphen_pos ); + } - $parts = explode( '.', $version_string ); + $parts = explode( '.', $version_string ); - if ( ( $major = array_shift( $parts ) ) !== null ) { - $this->major = (int) $major; - } - if ( ( $minor = array_shift( $parts ) ) !== null ) { - $this->minor = (int) $minor; - } - if ( ( $revision = array_shift( $parts ) ) !== null ) { - $this->revision = (int) $revision; - } - } + if ( ( $major = array_shift( $parts ) ) !== null ) { + $this->major = (int) $major; + } + if ( ( $minor = array_shift( $parts ) ) !== null ) { + $this->minor = (int) $minor; + } + if ( ( $revision = array_shift( $parts ) ) !== null ) { + $this->revision = (int) $revision; + } + } - public function __toString () { + public function __toString () { - $out = (string) $this->major; + $out = (string) $this->major; - if ( ! is_null( $this->minor ) ) { - $out .= ".$this->minor"; - } - if ( ! is_null( $this->revision ) ) { - $out .= ".$this->revision"; - } - if ( ! is_null( $this->extra ) ) { - $out .= "-$this->extra"; - } + if ( ! is_null( $this->minor ) ) { + $out .= ".$this->minor"; + } + if ( ! is_null( $this->revision ) ) { + $out .= ".$this->revision"; + } + if ( ! is_null( $this->extra ) ) { + $out .= "-$this->extra"; + } - return $out; - } + return $out; + } - public function compare ( $version_string ) { + public function compare ( $version_string ) { - $LESS = -1; - $MORE = 1; - $EQUAL = 0; + $LESS = -1; + $MORE = 1; + $EQUAL = 0; - $test = new csscrush_version( $version_string ); + $test = new csscrush_version( $version_string ); - foreach ( array( 'major', 'minor', 'revision' ) as $level ) { + foreach ( array( 'major', 'minor', 'revision' ) as $level ) { - if ( $this->{ $level } < $test->{ $level } ) { - return $LESS; - } - elseif ( $this->{ $level } > $test->{ $level } ) { - return $MORE; - } - } + if ( $this->{ $level } < $test->{ $level } ) { + return $LESS; + } + elseif ( $this->{ $level } > $test->{ $level } ) { + return $MORE; + } + } - return $EQUAL; - } + return $EQUAL; + } } diff --git a/plugins/hsl-to-hex.php b/plugins/hsl-to-hex.php index 179dd7f..9180cf2 100644 --- a/plugins/hsl-to-hex.php +++ b/plugins/hsl-to-hex.php @@ -14,20 +14,20 @@ function csscrush__hsl_to_hex ( csscrush_rule $rule ) { - foreach ( $rule as &$declaration ) { - if ( - ! $declaration->skip && - ( ! empty( $declaration->functions ) && in_array( 'hsl', $declaration->functions ) ) - ) { - while ( preg_match( '!hsl(\?p\d+\?)!', $declaration->value, $m ) ) { - $full_match = $m[0]; - $token = $m[1]; - $hsl = trim( csscrush::$process->tokens->p[ $token ], '()' ); - $hsl = array_map( 'trim', explode( ',', $hsl ) ); - $rgb = csscrush_color::cssHslToRgb( $hsl ); - $hex = csscrush_color::rgbToHex( $rgb ); - $declaration->value = str_replace( $full_match, $hex, $declaration->value ); - } - } - } -} \ No newline at end of file + foreach ( $rule as &$declaration ) { + if ( + ! $declaration->skip && + ( ! empty( $declaration->functions ) && in_array( 'hsl', $declaration->functions ) ) + ) { + while ( preg_match( '!hsl(\?p\d+\?)!', $declaration->value, $m ) ) { + $full_match = $m[0]; + $token = $m[1]; + $hsl = trim( csscrush::$process->tokens->p[ $token ], '()' ); + $hsl = array_map( 'trim', explode( ',', $hsl ) ); + $rgb = csscrush_color::cssHslToRgb( $hsl ); + $hex = csscrush_color::rgbToHex( $rgb ); + $declaration->value = str_replace( $full_match, $hex, $declaration->value ); + } + } + } +} diff --git a/plugins/ie-clip.php b/plugins/ie-clip.php index a9c5195..1a70ea5 100644 --- a/plugins/ie-clip.php +++ b/plugins/ie-clip.php @@ -14,21 +14,20 @@ function csscrush__ie_clip ( csscrush_rule $rule ) { - // Assume it's been dealt with if the property occurs more than once - if ( $rule->propertyCount( 'clip' ) !== 1 ) { - return; - } - $new_set = array(); - foreach ( $rule as $declaration ) { - $new_set[] = $declaration; - if ( - $declaration->skip || - $declaration->property !== 'clip' - ) { - continue; - } - $new_set[] = new csscrush_declaration( '*clip', str_replace( ',', ' ', $declaration->getFullValue() ) ); - } - $rule->declarations = $new_set; + // Assume it's been dealt with if the property occurs more than once + if ( $rule->propertyCount( 'clip' ) !== 1 ) { + return; + } + $new_set = array(); + foreach ( $rule as $declaration ) { + $new_set[] = $declaration; + if ( + $declaration->skip || + $declaration->property !== 'clip' + ) { + continue; + } + $new_set[] = new csscrush_declaration( '*clip', str_replace( ',', ' ', $declaration->getFullValue() ) ); + } + $rule->declarations = $new_set; } - diff --git a/plugins/ie-filter.php b/plugins/ie-filter.php index 66a3beb..a0ebb2e 100644 --- a/plugins/ie-filter.php +++ b/plugins/ie-filter.php @@ -20,37 +20,37 @@ function csscrush__ie_filter ( csscrush_rule $rule ) { - if ( $rule->propertyCount( '-ms-filter' ) < 1 ) { - return; - } - $filter_prefix = 'progid:DXImageTransform.Microsoft.'; - $new_set = array(); - foreach ( $rule as $declaration ) { - if ( - $declaration->skip || - $declaration->property !== '-ms-filter' - ) { - $new_set[] = $declaration; - continue; - } - $list = array_map( 'trim', explode( ',', $declaration->value ) ); - foreach ( $list as &$item ) { - if ( - strpos( $item, $filter_prefix ) !== 0 && - strpos( $item, 'alpha' ) !== 0 // Shortcut syntax permissable on alpha - ) { - $item = $filter_prefix . ucfirst( $item ); - } - } - $declaration->value = implode( ',', $list ); - if ( ! $rule->propertyCount( 'zoom' ) ) { - // Filters need hasLayout - $new_set[] = new csscrush_declaration( 'zoom', 1 ); - } - // Quoted version for -ms-filter IE >= 8 - $new_set[] = new csscrush_declaration( '-ms-filter', "\"$declaration->value\"" ); - // Star escaped property for IE < 8 - $new_set[] = new csscrush_declaration( '*filter', $declaration->value ); - } - $rule->declarations = $new_set; + if ( $rule->propertyCount( '-ms-filter' ) < 1 ) { + return; + } + $filter_prefix = 'progid:DXImageTransform.Microsoft.'; + $new_set = array(); + foreach ( $rule as $declaration ) { + if ( + $declaration->skip || + $declaration->property !== '-ms-filter' + ) { + $new_set[] = $declaration; + continue; + } + $list = array_map( 'trim', explode( ',', $declaration->value ) ); + foreach ( $list as &$item ) { + if ( + strpos( $item, $filter_prefix ) !== 0 && + strpos( $item, 'alpha' ) !== 0 // Shortcut syntax permissable on alpha + ) { + $item = $filter_prefix . ucfirst( $item ); + } + } + $declaration->value = implode( ',', $list ); + if ( ! $rule->propertyCount( 'zoom' ) ) { + // Filters need hasLayout + $new_set[] = new csscrush_declaration( 'zoom', 1 ); + } + // Quoted version for -ms-filter IE >= 8 + $new_set[] = new csscrush_declaration( '-ms-filter', "\"$declaration->value\"" ); + // Star escaped property for IE < 8 + $new_set[] = new csscrush_declaration( '*filter', $declaration->value ); + } + $rule->declarations = $new_set; } diff --git a/plugins/ie-inline-block.php b/plugins/ie-inline-block.php index 95472ab..f61770c 100644 --- a/plugins/ie-inline-block.php +++ b/plugins/ie-inline-block.php @@ -15,21 +15,21 @@ function csscrush__ie_inline_block ( csscrush_rule $rule ) { - if ( $rule->propertyCount( 'display' ) < 1 ) { - return; - } - $new_set = array(); - foreach ( $rule as $declaration ) { - $new_set[] = $declaration; - $is_display = $declaration->property === 'display'; - if ( - $declaration->skip || - ! $is_display || - $is_display && $declaration->value !== 'inline-block' ) { - continue; - } - $new_set[] = new csscrush_declaration( '*display', 'inline' ); - $new_set[] = new csscrush_declaration( '*zoom', 1 ); - } - $rule->declarations = $new_set; + if ( $rule->propertyCount( 'display' ) < 1 ) { + return; + } + $new_set = array(); + foreach ( $rule as $declaration ) { + $new_set[] = $declaration; + $is_display = $declaration->property === 'display'; + if ( + $declaration->skip || + ! $is_display || + $is_display && $declaration->value !== 'inline-block' ) { + continue; + } + $new_set[] = new csscrush_declaration( '*display', 'inline' ); + $new_set[] = new csscrush_declaration( '*zoom', 1 ); + } + $rule->declarations = $new_set; } diff --git a/plugins/ie-min-height.php b/plugins/ie-min-height.php index dd496fe..29e130d 100644 --- a/plugins/ie-min-height.php +++ b/plugins/ie-min-height.php @@ -14,18 +14,18 @@ function csscrush__ie_min_height ( csscrush_rule $rule ) { - if ( $rule->propertyCount( 'min-height' ) < 1 ) { - return; - } - $new_set = array(); - foreach ( $rule as $declaration ) { - $new_set[] = $declaration; - if ( - $declaration->skip || - $declaration->property !== 'min-height' ) { - continue; - } - $new_set[] = new csscrush_declaration( '_height', $declaration->value ); - } - $rule->declarations = $new_set; + if ( $rule->propertyCount( 'min-height' ) < 1 ) { + return; + } + $new_set = array(); + foreach ( $rule as $declaration ) { + $new_set[] = $declaration; + if ( + $declaration->skip || + $declaration->property !== 'min-height' ) { + continue; + } + $new_set[] = new csscrush_declaration( '_height', $declaration->value ); + } + $rule->declarations = $new_set; } diff --git a/plugins/ie-opacity.php b/plugins/ie-opacity.php index ee56c98..352647e 100755 --- a/plugins/ie-opacity.php +++ b/plugins/ie-opacity.php @@ -16,29 +16,29 @@ function csscrush__ie_opacity ( csscrush_rule $rule ) { - if ( $rule->propertyCount( 'opacity' ) < 1 ) { - return; - } - $new_set = array(); - foreach ( $rule as $declaration ) { - $new_set[] = $declaration; - if ( - $declaration->skip || - $declaration->property != 'opacity' - ) { - continue; - } + if ( $rule->propertyCount( 'opacity' ) < 1 ) { + return; + } + $new_set = array(); + foreach ( $rule as $declaration ) { + $new_set[] = $declaration; + if ( + $declaration->skip || + $declaration->property != 'opacity' + ) { + continue; + } - $opacity = (float) $declaration->value; - $opacity = round( $opacity * 100 ); + $opacity = (float) $declaration->value; + $opacity = round( $opacity * 100 ); - if ( ! $rule->propertyCount( 'zoom' ) ) { - // Filters need hasLayout - $new_set[] = new csscrush_declaration( 'zoom', 1 ); - } - $value = "alpha(opacity=$opacity)"; - $new_set[] = new csscrush_declaration( '-ms-filter', "\"$value\"" ); - $new_set[] = new csscrush_declaration( '*filter', $value ); - } - $rule->declarations = $new_set; + if ( ! $rule->propertyCount( 'zoom' ) ) { + // Filters need hasLayout + $new_set[] = new csscrush_declaration( 'zoom', 1 ); + } + $value = "alpha(opacity=$opacity)"; + $new_set[] = new csscrush_declaration( '-ms-filter', "\"$value\"" ); + $new_set[] = new csscrush_declaration( '*filter', $value ); + } + $rule->declarations = $new_set; } diff --git a/plugins/initial.php b/plugins/initial.php index 03a99c7..095e89d 100644 --- a/plugins/initial.php +++ b/plugins/initial.php @@ -19,23 +19,23 @@ function csscrush__initial ( csscrush_rule $rule ) { - static $initialValues = null; - if ( ! $initialValues ) { - if ( ! ( $initialValues = @parse_ini_file( CssCrush::$config->location . '/misc/initial-values.ini' ) ) ) { - trigger_error( __METHOD__ . ": Initial keywords file could not be parsed.\n", E_USER_NOTICE ); - return; - } - } + static $initialValues = null; + if ( ! $initialValues ) { + if ( ! ( $initialValues = @parse_ini_file( CssCrush::$config->location . '/misc/initial-values.ini' ) ) ) { + trigger_error( __METHOD__ . ": Initial keywords file could not be parsed.\n", E_USER_NOTICE ); + return; + } + } - foreach ( $rule as &$declaration ) { - if ( !$declaration->skip && 'initial' === $declaration->value ) { - if ( isset( $initialValues[ $declaration->property ] ) ) { - $declaration->value = $initialValues[ $declaration->property ]; - } - else { - // Fallback to 'inherit' - $declaration->value = 'inherit'; - } - } - } -} \ No newline at end of file + foreach ( $rule as &$declaration ) { + if ( !$declaration->skip && 'initial' === $declaration->value ) { + if ( isset( $initialValues[ $declaration->property ] ) ) { + $declaration->value = $initialValues[ $declaration->property ]; + } + else { + // Fallback to 'inherit' + $declaration->value = 'inherit'; + } + } + } +} diff --git a/plugins/property-sorter.php b/plugins/property-sorter.php index 3d02191..1f994dd 100644 --- a/plugins/property-sorter.php +++ b/plugins/property-sorter.php @@ -28,155 +28,154 @@ function csscrush__property_sorter ( csscrush_rule $rule ) { - $new_set = array(); + $new_set = array(); - // Create plain array of rule declarations. - foreach ( $rule as $declaration ) { - $new_set[] = $declaration; - } + // Create plain array of rule declarations. + foreach ( $rule as $declaration ) { + $new_set[] = $declaration; + } - usort( $new_set, '_csscrush__property_sorter_callback' ); + usort( $new_set, '_csscrush__property_sorter_callback' ); - $rule->declarations = $new_set; + $rule->declarations = $new_set; } /* - Callback for sorting. + Callback for sorting. */ function _csscrush__property_sorter_callback ( $a, $b ) { - $map =& _csscrush__property_sorter_get_table(); - $a_prop =& $a->canonicalProperty; - $b_prop =& $b->canonicalProperty; - $a_listed = isset( $map[ $a_prop ] ); - $b_listed = isset( $map[ $b_prop ] ); - - // If the properties are identical we need to flag for an index comparison. - $compare_indexes = false; - - // If the 'canonical' properties are identical we need to flag for a vendor comparison. - $compare_vendor = false; - - // If both properties are listed. - if ( $a_listed && $b_listed ) { - - if ( $a_prop === $b_prop ) { - if ( $a->vendor || $b->vendor ) { - $compare_vendor = true; - } - else { - $compare_indexes = true; - } - } - else { - // Table comparison. - return $map[ $a_prop ] > $map[ $b_prop ] ? 1 : -1; - } - } - - // If one property is listed it always takes higher priority. - elseif ( $a_listed && ! $b_listed ) { - return -1; - } - elseif ( $b_listed && ! $a_listed ) { - return 1; - } - - // If neither property is listed. - else { - - if ( $a_prop === $b_prop ) { - if ( $a->vendor || $b->vendor ) { - $compare_vendor = true; - } - else { - $compare_indexes = true; - } - } - else { - // Regular sort. - return $a_prop > $b_prop ? 1 : -1; - } - } - - // Comparing by index. - if ( $compare_indexes ) { - return $a->index > $b->index ? 1 : -1; - } - - // Comparing by vendor mark. - if ( $compare_vendor ) { - if ( ! $a->vendor && $b->vendor ) { - return 1; - } - elseif ( $a->vendor && ! $b->vendor ) { - return -1; - } - else { - // If both have a vendor mark compare vendor name length. - return strlen( $b->vendor ) > strlen( $a->vendor ) ? 1 : -1; - } - } + $map =& _csscrush__property_sorter_get_table(); + $a_prop =& $a->canonicalProperty; + $b_prop =& $b->canonicalProperty; + $a_listed = isset( $map[ $a_prop ] ); + $b_listed = isset( $map[ $b_prop ] ); + + // If the properties are identical we need to flag for an index comparison. + $compare_indexes = false; + + // If the 'canonical' properties are identical we need to flag for a vendor comparison. + $compare_vendor = false; + + // If both properties are listed. + if ( $a_listed && $b_listed ) { + + if ( $a_prop === $b_prop ) { + if ( $a->vendor || $b->vendor ) { + $compare_vendor = true; + } + else { + $compare_indexes = true; + } + } + else { + // Table comparison. + return $map[ $a_prop ] > $map[ $b_prop ] ? 1 : -1; + } + } + + // If one property is listed it always takes higher priority. + elseif ( $a_listed && ! $b_listed ) { + return -1; + } + elseif ( $b_listed && ! $a_listed ) { + return 1; + } + + // If neither property is listed. + else { + + if ( $a_prop === $b_prop ) { + if ( $a->vendor || $b->vendor ) { + $compare_vendor = true; + } + else { + $compare_indexes = true; + } + } + else { + // Regular sort. + return $a_prop > $b_prop ? 1 : -1; + } + } + + // Comparing by index. + if ( $compare_indexes ) { + return $a->index > $b->index ? 1 : -1; + } + + // Comparing by vendor mark. + if ( $compare_vendor ) { + if ( ! $a->vendor && $b->vendor ) { + return 1; + } + elseif ( $a->vendor && ! $b->vendor ) { + return -1; + } + else { + // If both have a vendor mark compare vendor name length. + return strlen( $b->vendor ) > strlen( $a->vendor ) ? 1 : -1; + } + } } /* - Cache for the table of values to compare against. + Cache for the table of values to compare against. */ function &_csscrush__property_sorter_get_table () { - // Check for cached table. - if ( isset( $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER_CACHE' ] ) ) { - return $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER_CACHE' ]; - } + // Check for cached table. + if ( isset( $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER_CACHE' ] ) ) { + return $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER_CACHE' ]; + } - $table = array(); + $table = array(); - // Nothing cached, check for a user-defined table. - if ( isset( $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER' ] ) ) { - $table = (array) $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER' ]; - } + // Nothing cached, check for a user-defined table. + if ( isset( $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER' ] ) ) { + $table = (array) $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER' ]; + } - // No user-defined table, use pre-defined. - else { + // No user-defined table, use pre-defined. + else { - // Load from property-sorting.ini. - if ( $sorting_file_contents - = file_get_contents( csscrush::$config->location . '/misc/property-sorting.ini' ) ) { + // Load from property-sorting.ini. + if ( $sorting_file_contents + = file_get_contents( csscrush::$config->location . '/misc/property-sorting.ini' ) ) { - $sorting_file_contents = preg_replace( '!;[^\r\n]*!', '', $sorting_file_contents ); - $table = preg_split( '!\s+!', trim( $sorting_file_contents ) ); - } - else { - trigger_error( __METHOD__ . ": Property sorting file not found.\n", E_USER_NOTICE ); - } + $sorting_file_contents = preg_replace( '!;[^\r\n]*!', '', $sorting_file_contents ); + $table = preg_split( '!\s+!', trim( $sorting_file_contents ) ); + } + else { + trigger_error( __METHOD__ . ": Property sorting file not found.\n", E_USER_NOTICE ); + } - // Store to the global variable. - $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER' ] = $table; - } + // Store to the global variable. + $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER' ] = $table; + } - // Cache the table (and flip it). - $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER_CACHE' ] = array_flip( $table ); + // Cache the table (and flip it). + $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER_CACHE' ] = array_flip( $table ); - return $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER_CACHE' ]; + return $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER_CACHE' ]; } /* - Get the current sorting table. + Get the current sorting table. */ function csscrush_get_property_sort_order () { - _csscrush__property_sorter_get_table(); - return $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER' ]; + _csscrush__property_sorter_get_table(); + return $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER' ]; } /* - Set a custom sorting table. + Set a custom sorting table. */ function csscrush_set_property_sort_order ( array $new_order ) { - unset( $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER_CACHE' ] ); - $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER' ] = $new_order; + unset( $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER_CACHE' ] ); + $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER' ] = $new_order; } - diff --git a/plugins/rgba-fallback.php b/plugins/rgba-fallback.php index 609bd49..c2d93e3 100644 --- a/plugins/rgba-fallback.php +++ b/plugins/rgba-fallback.php @@ -17,38 +17,38 @@ function csscrush__rgba_fallback ( csscrush_rule $rule ) { - $props = array_keys( $rule->properties ); + $props = array_keys( $rule->properties ); - // Determine which properties apply - $rgba_props = array(); - foreach ( $props as $prop ) { - if ( $prop === 'background' || strpos( $prop, 'color' ) !== false ) { - $rgba_props[] = $prop; - } - } - if ( empty( $rgba_props ) ) { - return; - } + // Determine which properties apply + $rgba_props = array(); + foreach ( $props as $prop ) { + if ( $prop === 'background' || strpos( $prop, 'color' ) !== false ) { + $rgba_props[] = $prop; + } + } + if ( empty( $rgba_props ) ) { + return; + } - $new_set = array(); - foreach ( $rule as $declaration ) { - $is_viable = in_array( $declaration->property, $rgba_props ); - if ( - $declaration->skip || - ! $is_viable || - $is_viable && !preg_match( '!^rgba\?p\d+\?$!', $declaration->value ) - ) { - $new_set[] = $declaration; - continue; - } - // Create rgb value from rgba - $raw_value = $declaration->getFullValue(); - $raw_value = substr( $raw_value, 5, strlen( $raw_value ) - 1 ); - list( $r, $g, $b, $a ) = explode( ',', $raw_value ); - - // Add rgb value to the stack, followed by rgba - $new_set[] = new csscrush_declaration( $declaration->property, "rgb($r,$g,$b)" ); - $new_set[] = $declaration; - } - $rule->declarations = $new_set; + $new_set = array(); + foreach ( $rule as $declaration ) { + $is_viable = in_array( $declaration->property, $rgba_props ); + if ( + $declaration->skip || + ! $is_viable || + $is_viable && !preg_match( '!^rgba\?p\d+\?$!', $declaration->value ) + ) { + $new_set[] = $declaration; + continue; + } + // Create rgb value from rgba + $raw_value = $declaration->getFullValue(); + $raw_value = substr( $raw_value, 5, strlen( $raw_value ) - 1 ); + list( $r, $g, $b, $a ) = explode( ',', $raw_value ); + + // Add rgb value to the stack, followed by rgba + $new_set[] = new csscrush_declaration( $declaration->property, "rgb($r,$g,$b)" ); + $new_set[] = $declaration; + } + $rule->declarations = $new_set; } diff --git a/plugins/spiffing.php b/plugins/spiffing.php index cd86c28..436a0bb 100644 --- a/plugins/spiffing.php +++ b/plugins/spiffing.php @@ -17,8 +17,8 @@ function csscrush__spiffing ( $rule ) { - $find = array( 'colour', 'grey', '!please', 'transparency', 'centre', 'plump', 'photograph', 'capitalise' ); - $replace = array( 'color', 'gray', '!important', 'opacity', 'center', 'bold', 'image', 'capitalize' ); - - $rule->declaration_raw = str_ireplace( $find, $replace, $rule->declaration_raw ); + $find = array( 'colour', 'grey', '!please', 'transparency', 'centre', 'plump', 'photograph', 'capitalise' ); + $replace = array( 'color', 'gray', '!important', 'opacity', 'center', 'bold', 'image', 'capitalize' ); + + $rule->declaration_raw = str_ireplace( $find, $replace, $rule->declaration_raw ); } From 83f2fcf54f9d22c3f4f1bd086db4ff5465c6000d Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 13 Nov 2012 10:11:36 +0000 Subject: [PATCH 055/421] Added output_dir option. Added doc_root option in cases where a server alias or rewrite is used in serving css files. --- Aliases.ini | 1 + CHANGELOG.txt | 8 +++-- cli.php | 21 +++++-------- lib/Core.php | 45 +++++++++++++++++----------- lib/IO.php | 78 +++++++++++++++++++++++++++++------------------- lib/Importer.php | 11 ++++--- lib/Mixin.php | 1 - lib/Process.php | 12 ++++++-- lib/Util.php | 27 ++++++++++------- 9 files changed, 120 insertions(+), 84 deletions(-) diff --git a/Aliases.ini b/Aliases.ini index c58e98c..bec5f21 100644 --- a/Aliases.ini +++ b/Aliases.ini @@ -144,6 +144,7 @@ tab-size[] = -o-tab-size ; Text align last + text-align-last[] = -webkit-text-align-last text-align-last[] = -moz-text-align-last ; Text decoration diff --git a/CHANGELOG.txt b/CHANGELOG.txt index bccd4b2..e70094b 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,14 +1,16 @@ 1.8 ----- Added selector aliasing with @selector-alias directive. +Added output_dir option for specifying the destination of compiled files. +Added doc_root option for working around problems with server aliases or path rewrites. Added viewport @-rule aliases. -Improved minification. -Improved cross OS support. Debug option renamed to 'minify'; debug option will still work as before but is deprecated. New minify option optionally takes an array of advanced minification parameters. Expanded trace option to take an optional array of log parameters; -log params available are stubs, selector_count and compile_time. +log params available are stubs, selector_count, errors and compile_time. Added csscrush::stat method to retrieve logged parameters. +Improved cross OS support. +Improved minification. Major refactoring. diff --git a/cli.php b/cli.php index 62321e0..6fe65e5 100755 --- a/cli.php +++ b/cli.php @@ -221,7 +221,7 @@ function stdout ( $lines, $closing_newline = true ) { } } -// Disable plugin args +// Disable plugin args. if ( $disable_plugins ) { foreach ( $disable_plugins as $arg ) { foreach ( preg_split( '!\s*,\s*!', $arg ) as $plugin ) { @@ -230,42 +230,35 @@ function stdout ( $lines, $closing_newline = true ) { } } -// Tracing +// Tracing. if ( $trace_flag ) { $process_opts[ 'trace' ] = true; } -// Vendor target args +// Vendor target args. if ( $vendor_target ) { $process_opts[ 'vendor_target' ] = $vendor_target; } -// Variables args +// Variables args. if ( $variables ) { parse_str( $variables, $in_vars ); $process_opts[ 'vars' ] = $in_vars; } -// Resolve a context for URLs +// Resolve a context for URLs. if ( ! $context ) { $context = $input_file ? dirname( realpath( $input_file ) ) : null; } -// If there is an import context set it to the document root +// If there is an import context set document root also. if ( $context ) { - $old_doc_root = csscrush::$config->docRoot; - csscrush::$config->docRoot = $context; + $process_opts[ 'doc_root' ] = $context; $process_opts[ 'context' ] = $context; } -// Process the stream $output = csscrush::string( $input, $process_opts ); -// Reset the document root after processing -if ( $context ) { - csscrush::$config->docRoot = $old_doc_root; -} - ################################################################## ## Output diff --git a/lib/Core.php b/lib/Core.php index 3d29cb7..b0f03f7 100644 --- a/lib/Core.php +++ b/lib/Core.php @@ -4,7 +4,6 @@ * Main script. Includes core public API. * */ - class csscrush { // Global settings. @@ -41,34 +40,40 @@ static public function init ( $seed_file ) { // Default options. self::$config->options = (object) array( - // Minify. Set false for formatting and comments + // Minify. Set false for formatting and comments. 'minify' => true, - // Append 'checksum' to output file name + // Append 'checksum' to output file name. 'versioning' => true, - // Use the template boilerplate + // Use the template boilerplate. 'boilerplate' => true, - // Variables passed in at runtime + // Variables passed in at runtime. 'vars' => array(), - // Enable/disable the cache + // Enable/disable the cache. 'cache' => true, - // Output file. Defaults the host-filename + // Output filename. Defaults the host-filename. 'output_file' => null, - // Vendor target. Only apply prefixes for a specific vendor, set to 'none' for no prefixes + // Output directory. Defaults to the same directory as the host file. + 'output_dir' => null, + + // Alternative document_root may be used to workaround server aliases and rewrites. + 'doc_root' => null, + + // Vendor target. Only apply prefixes for a specific vendor, set to 'none' for no prefixes. 'vendor_target' => 'all', - // Whether to rewrite the url references inside imported files + // Whether to rewrite the url references inside imported files. 'rewrite_import_urls' => true, - // List of plugins to enable (as Array of names) + // List of plugins to enable (as Array of names). 'enable' => null, - // List of plugins to disable (as Array of names) + // List of plugins to disable (as Array of names). 'disable' => null, // Debugging options. @@ -210,7 +215,7 @@ static public function file ( $file, $options = null ) { $config = self::$config; $process = self::$process; $options = $process->options; - $doc_root = $config->docRoot; + $doc_root = $process->docRoot; // Since we're comparing strings, we need to iron out OS differences. $file = str_replace( '\\', '/', $file ); @@ -262,9 +267,9 @@ static public function file ( $file, $options = null ) { $stream = $process->compile(); // Create file and return url. Return empty string on failure. - if ( file_put_contents( "{$process->output->dir}/{$process->output->filename}", $stream ) ) { + if ( $url = $process->ioCall( 'write', $stream ) ) { $timestamp = $options->versioning ? '?' . time() : ''; - return "{$process->output->dirUrl}/{$process->output->filename}$timestamp"; + return "$url$timestamp"; } else { return ''; @@ -375,7 +380,7 @@ static public function string ( $string, $options = null ) { $process->setContext( $options->context, false ); } else { - $process->setContext( $config->docRoot, false ); + $process->setContext( $process->docRoot, false ); } // Set the string on the input object. @@ -432,7 +437,13 @@ static public function clearCache ( $dir = '' ) { */ static public function stat ( $name = null ) { - $stat = csscrush::$process->stat; + $process = csscrush::$process; + $stat = $process->stat; + + // Get logged errors as late as possible. + if ( in_array( 'errors', $process->options->trace ) && ( ! $name || 'errors' === $name ) ) { + $stat[ 'errors' ] = $process->errors; + } if ( $name && array_key_exists( $name, $stat ) ) { return array( $name => $stat[ $name ] ); @@ -508,7 +519,7 @@ static public function runStat ( $name ) { case 'compile_time': $time = microtime( true ); - $process->stat[ 'compile_time' ] = $time - $process->stat[ 'compile_start_time' ]; + $process->stat[ 'compile_time' ] = $time - $process->stat[ 'compile_start_time' ]; break; } } diff --git a/lib/IO.php b/lib/IO.php index 6507e41..b1a02af 100644 --- a/lib/IO.php +++ b/lib/IO.php @@ -10,13 +10,13 @@ class csscrush_io { static public function init () { $process = csscrush::$process; - - $process->cacheFileName = '.csscrush'; - $process->cacheFilePath = "{$process->input->dir}/$process->cacheFileName"; + $process->cacheFile = "{$process->output->dir}/.csscrush"; } static public function getOutputDir () { - return csscrush::$process->input->dir; + $process = csscrush::$process; + return $process->options->output_dir ? + $process->options->output_dir : $process->input->dir; } static public function testOutputDir () { @@ -99,7 +99,7 @@ static public function validateExistingOutput () { foreach ( $process->cacheData[ $existingfile->filename ][ 'imports' ] as $import_file ) { // Check if this is docroot relative or input dir relative - $root = strpos( $import_file, '/' ) === 0 ? $config->docRoot : $process->input->dir; + $root = strpos( $import_file, '/' ) === 0 ? $process->docRoot : $process->input->dir; $import_filepath = realpath( $root ) . "/$import_file"; if ( file_exists( $import_filepath ) ) { @@ -114,37 +114,44 @@ static public function validateExistingOutput () { } // Cast because the cached options may be a stdClass if an IO adapter has been used. - $existing_options = (array) $process->cacheData[ $existingfile->filename ][ 'options' ]; + $cached_options = (array) $process->cacheData[ $existingfile->filename ][ 'options' ]; $existing_datesum = $process->cacheData[ $existingfile->filename ][ 'datem_sum' ]; - $options_unchanged = true; - foreach ( $existing_options as $key => &$value ) { - if ( $existing_options[ $key ] !== $options->{ $key } ) { - $options_unchanged = false; + // Check for runtime options that are different to cached options. + $options_changed = false; + foreach ( $cached_options as $key => &$value ) { + if ( property_exists( $options, $key ) && $options->{ $key } !== $value ) { + $options_changed = true; break; } } - $files_unchanged = $existing_datesum == array_sum( $all_files ); - if ( $options_unchanged && $files_unchanged ) { + // Check if any of the files have changed. + $files_changed = $existing_datesum != array_sum( $all_files ); + + if ( ! $options_changed && ! $files_changed ) { - // Files have not been modified and config is the same: return the old file + // Files have not been modified and config is the same: return the old file. csscrush::log( "Files and options have not been modified, returning existing file '$existingfile->URL'." ); return $existingfile->URL . ( $options->versioning !== false ? "?$existing_datesum" : '' ); } else { - // Remove old file and continue making a new one... - ! $options_unchanged && csscrush::log( 'Options have been modified.' ); - ! $files_unchanged && csscrush::log( 'Files have been modified.' ); + if ( $options_changed ) { + csscrush::log( 'Options have been modified.' ); + } + if ( $files_changed ) { + csscrush::log( 'Files have been modified.' ); + } csscrush::log( 'Removing existing file.' ); + // Remove old file and continue making a new one... unlink( $existingfile->path ); } } else if ( file_exists( $existingfile->path ) ) { - // File exists but has no config + // File exists but has no config. csscrush::log( 'File exists but no config, removing existing file.' ); unlink( $existingfile->path ); } @@ -164,7 +171,7 @@ static public function clearCache ( $dir ) { return; } - $configPath = $dir . '/' . csscrush::$process->cacheFilePath; + $configPath = $dir . '/' . csscrush::$process->cacheFile; if ( file_exists( $configPath ) ) { unlink( $configPath ); } @@ -188,22 +195,21 @@ static public function getCacheData () { $process = csscrush::$process; if ( - file_exists( $process->cacheFilePath ) && - $process->cacheData && - $process->cacheData[ 'originPath' ] == $process->cacheFilePath + file_exists( $process->cacheFile ) && + $process->cacheData ) { // Already loaded and config file exists in the current directory return; } - $cache_data_exists = file_exists( $process->cacheFilePath ); - $cache_data_file_is_writable = $cache_data_exists ? is_writable( $process->cacheFilePath ) : false; + $cache_data_exists = file_exists( $process->cacheFile ); + $cache_data_file_is_writable = $cache_data_exists ? is_writable( $process->cacheFile ) : false; $cache_data = array(); if ( $cache_data_exists && $cache_data_file_is_writable && - $cache_data = json_decode( file_get_contents( $process->cacheFilePath ), true ) + $cache_data = json_decode( file_get_contents( $process->cacheFile ), true ) ) { // Successfully loaded config file. csscrush::log( 'Cache data loaded.' ); @@ -211,7 +217,7 @@ static public function getCacheData () { else { // Config file may exist but not be writable (may not be visible in some ftp situations?) if ( $cache_data_exists ) { - if ( ! @unlink( $process->cacheFilePath ) ) { + if ( ! @unlink( $process->cacheFile ) ) { $error = "Could not delete config data file."; csscrush::logError( $error ); @@ -222,7 +228,7 @@ static public function getCacheData () { // Create config file. csscrush::log( 'Creating cache data file.' ); } - file_put_contents( $process->cacheFilePath, json_encode( array() ) ); + file_put_contents( $process->cacheFile, json_encode( array() ) ); } return $cache_data; @@ -232,11 +238,23 @@ static public function saveCacheData () { $process = csscrush::$process; - // Need to store the current path so we can check we're using the right config path later - $process->cacheData[ 'originPath' ] = $process->cacheFilePath; - csscrush::log( 'Saving config.' ); - file_put_contents( $process->cacheFilePath, json_encode( $process->cacheData ) ); + file_put_contents( $process->cacheFile, json_encode( $process->cacheData ) ); + } + + static public function write ( csscrush_stream $stream ) { + + $process = csscrush::$process; + $target = "{$process->output->dir}/{$process->output->filename}"; + if ( @file_put_contents( $target, $stream ) ) { + return "{$process->output->dirUrl}/{$process->output->filename}"; + } + else { + $error = "Could not write file '$target'."; + csscrush::logError( $error ); + trigger_error( __METHOD__ . ": $error\n", E_USER_WARNING ); + } + return false; } static final function registerInputFile ( $file ) { diff --git a/lib/Importer.php b/lib/Importer.php index 37d63f6..4772fe1 100644 --- a/lib/Importer.php +++ b/lib/Importer.php @@ -85,7 +85,7 @@ static public function hostfile () { // Resolve import realpath. if ( $url->isRooted ) { - $import->path = realpath( $config->docRoot . $import->url->value ); + $import->path = realpath( $process->docRoot . $import->url->value ); } else { $import->path = realpath( "$input->dir/{$import->url->value}" ); @@ -145,13 +145,12 @@ static public function hostfile () { } // Save only if caching is on and the hostfile object is associated with a real file. - if ( $input->path && $process->options->cache ) { + if ( $input->path && $options->cache ) { - // Write to config. $process->cacheData[ $process->output->filename ] = array( - 'imports' => $filenames, - 'datem_sum' => array_sum( $mtimes ) + $input->mtime, - 'options' => $options, + 'imports' => $filenames, + 'datem_sum' => array_sum( $mtimes ) + $input->mtime, + 'options' => clone $options, ); // Save config changes. diff --git a/lib/Mixin.php b/lib/Mixin.php index 54fe5f2..f421d35 100644 --- a/lib/Mixin.php +++ b/lib/Mixin.php @@ -114,7 +114,6 @@ static public function parseSingleValue ( $message ) { if ( ! $mixin && ! $non_mixin ) { $selector_test = csscrush_selector::makeReadableSelector( $message ); - // csscrush::log( array_keys( csscrush::$process->selectorRelationships ) ); if ( isset( csscrush::$process->selectorRelationships[ $selector_test ] ) ) { $non_mixin = csscrush::$process->selectorRelationships[ $selector_test ]; diff --git a/lib/Process.php b/lib/Process.php index ae1ec8d..8758ab3 100644 --- a/lib/Process.php +++ b/lib/Process.php @@ -45,6 +45,10 @@ public function __construct ( $options ) { $this->selectorAliases = $config->selectorAliases; $this->selectorAliasesPatt = null; + // Pick a doc root. + $this->docRoot = isset( $this->options->doc_root ) ? + csscrush_util::normalizePath( $this->options->doc_root ) : $config->docRoot; + // Run process_init hook. csscrush_hook::run( 'process_init' ); } @@ -66,7 +70,7 @@ public function release () { // Establish the input and output directories and optionally test output dir. public function setContext ( $input_dir, $test_output_dir = true ) { - $doc_root = csscrush::$config->docRoot; + $doc_root = $this->docRoot; if ( strpos( $input_dir, $doc_root ) !== 0 ) { // Not a system path. @@ -125,6 +129,10 @@ public function setOptions ( $options ) { if ( array_key_exists( 'trace', $options ) && ! is_array( $options[ 'trace' ] ) ) { $options[ 'trace' ] = $options[ 'trace' ] ? array( 'stubs' ) : array(); } + // Legacy trace config option. + if ( ! is_array( $config->options->trace ) ) { + $config->options->trace = $config->options->trace ? array( 'stubs' ) : array(); + } // Keeping track of global vars internally to maintain cache integrity. $options[ '_globalVars' ] = $config->vars; @@ -952,7 +960,7 @@ protected function collate () { } if ( $url->convertToData ) { - $url->toData(); + $url->evaluate()->toData(); } else { $url->simplify(); diff --git a/lib/Util.php b/lib/Util.php index 27b0d68..2def9d4 100644 --- a/lib/Util.php +++ b/lib/Util.php @@ -31,6 +31,17 @@ static public function normalizePath ( $path, $strip_drive_letter = false ) { $path = substr( $path, 2 ); } + return csscrush_util::simplifyPath( $path ); + } + + static public function simplifyPath ( $path ) { + + // Reduce redundant path segments (issue #32): + // e.g 'foo/../bar' => 'bar' + $patt = '~[^/.]+/\.\./~S'; + while ( preg_match( $patt, $path ) ) { + $path = preg_replace( $patt, '', $path ); + } return $path; } @@ -263,7 +274,7 @@ public function evaluate () { else { // Normalize './' led paths. $this->value = preg_replace( '!^\.\/+!i', '', $this->value ); - if ( $this->value[0] === '/' ) { + if ( $this->value !== '' && $this->value[0] === '/' ) { $this->isRooted = true; } elseif ( ! $leading_variable ) { @@ -272,12 +283,13 @@ public function evaluate () { // Normalize slashes. $this->value = rtrim( preg_replace( '![\\\\/]+!', '/', $this->value ), '/' ); } + return $this; } public function toData () { if ( $this->isRooted ) { - $file = csscrush::$config->docRoot . $this->value; + $file = csscrush::$process->docRoot . $this->value; } else { $file = csscrush::$process->input->dir . "/$this->value"; @@ -318,10 +330,9 @@ public function prepend ( $path_fragment ) { public function resolveRootedPath () { - $config = csscrush::$config; $process = csscrush::$process; - if ( ! file_exists ( $config->docRoot . $this->value ) ) { + if ( ! file_exists ( $process->docRoot . $this->value ) ) { return false; } @@ -332,13 +343,7 @@ public function resolveRootedPath () { public function simplify () { - // Reduce redundant path segments (issue #32): - // e.g 'foo/../bar' => 'bar' - $patt = '![^/.]+/\.\./!'; - - while ( preg_match( $patt, $this->value ) ) { - $this->value = preg_replace( $patt, '', $this->value ); - } + $this->value = csscrush_util::simplifyPath( $this->value ); } } From a8cd954f9460ef327abd96298226323d8c8bcc78 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 5 Dec 2012 14:01:22 +0000 Subject: [PATCH 056/421] Added functions API for defining custom functions inside plugins. Updated plugin API to use distinct 'enable' and 'disable' handlers. Improved gradient function aliasing to handle new angle keywords ('to left', 'at center' etc.). Added svg-gradients plugin for simulating CSS3 gradients with data-uris. --- Aliases.ini | 4 + CHANGELOG.txt | 10 +- CssCrush.php | 2 +- Plugins.ini | 3 - cli.php | 2 - lib/Core.php | 5 +- lib/Function.php | 191 ++++++++++---------- lib/Hook.php | 18 +- lib/Importer.php | 2 +- lib/Plugin.php | 48 ++--- lib/Process.php | 32 +++- lib/Regex.php | 9 +- lib/Rule.php | 171 +++++++++++++----- plugins/hocus-pocus.php | 16 +- plugins/hsl-to-hex.php | 13 +- plugins/ie-clip.php | 13 +- plugins/ie-filter.php | 13 +- plugins/ie-inline-block.php | 13 +- plugins/ie-min-height.php | 13 +- plugins/ie-opacity.php | 13 +- plugins/initial.php | 13 +- plugins/property-sorter.php | 13 +- plugins/rgba-fallback.php | 12 +- plugins/spiffing.php | 23 ++- plugins/svg-gradients.php | 337 ++++++++++++++++++++++++++++++++++++ 25 files changed, 774 insertions(+), 215 deletions(-) create mode 100644 plugins/svg-gradients.php diff --git a/Aliases.ini b/Aliases.ini index bec5f21..33d304e 100644 --- a/Aliases.ini +++ b/Aliases.ini @@ -216,17 +216,21 @@ ; Gradients linear-gradient[] = -webkit-linear-gradient linear-gradient[] = -moz-linear-gradient + linear-gradient[] = -ms-linear-gradient linear-gradient[] = -o-linear-gradient radial-gradient[] = -webkit-radial-gradient radial-gradient[] = -moz-radial-gradient + radial-gradient[] = -ms-radial-gradient radial-gradient[] = -o-radial-gradient ; Repeating gradients repeating-linear-gradient[] = -webkit-repeating-linear-gradient repeating-linear-gradient[] = -moz-repeating-linear-gradient + repeating-linear-gradient[] = -ms-repeating-linear-gradient repeating-linear-gradient[] = -o-repeating-linear-gradient repeating-radial-gradient[] = -webkit-repeating-radial-gradient repeating-radial-gradient[] = -moz-repeating-radial-gradient + repeating-radial-gradient[] = -ms-repeating-radial-gradient repeating-radial-gradient[] = -o-repeating-radial-gradient diff --git a/CHANGELOG.txt b/CHANGELOG.txt index e70094b..e33730e 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,5 +1,13 @@ +1.9 +--- +Added functions API for defining custom functions inside plugins. +Updated plugin API to use distinct 'enable' and 'disable' handlers. +Improved gradient function aliasing to handle new angle keywords ('to left', 'at center' etc.). +Added svg-gradients plugin for simulating CSS3 gradients with data-uris. + + 1.8 ------ +--- Added selector aliasing with @selector-alias directive. Added output_dir option for specifying the destination of compiled files. Added doc_root option for working around problems with server aliases or path rewrites. diff --git a/CssCrush.php b/CssCrush.php index 9a1d201..2af10fc 100644 --- a/CssCrush.php +++ b/CssCrush.php @@ -4,7 +4,7 @@ * CSS Crush * Extensible CSS preprocessor * - * @version 1.8 + * @version 1.9 * @link https://github.com/peteboere/css-crush * @license http://www.opensource.org/licenses/mit-license.php (MIT) * @copyright (c) 2010-2012 Pete Boere diff --git a/Plugins.ini b/Plugins.ini index 717dd00..17e97ee 100644 --- a/Plugins.ini +++ b/Plugins.ini @@ -24,9 +24,6 @@ ; Opacity for IE < 9 (uses filter) ; plugins[] = ie-opacity -; Opaque fallback colors for clients that don't support RGBA -; plugins[] = rgba-fallback - ; HSL shim - converts HSL values to hex codes ; plugins[] = hsl-to-hex diff --git a/cli.php b/cli.php index 6fe65e5..8fefae5 100755 --- a/cli.php +++ b/cli.php @@ -5,7 +5,6 @@ * Command line application * */ - require_once 'CssCrush.php'; // Exit status constants @@ -256,7 +255,6 @@ function stdout ( $lines, $closing_newline = true ) { $process_opts[ 'doc_root' ] = $context; $process_opts[ 'context' ] = $context; } - $output = csscrush::string( $input, $process_opts ); diff --git a/lib/Core.php b/lib/Core.php index b0f03f7..604da68 100644 --- a/lib/Core.php +++ b/lib/Core.php @@ -83,7 +83,6 @@ static public function init ( $seed_file ) { // Initialise other classes. csscrush_regex::init(); - csscrush_function::init(); } static protected function setDocRoot ( $doc_root = null ) { @@ -184,7 +183,7 @@ static public function loadAssets () { foreach ( $result[ 'plugins' ] as $plugin_name ) { // Backwards compat. $plugin_name = basename( $plugin_name, '.php' ); - if ( csscrush_plugin::enable( $plugin_name ) ) { + if ( csscrush_plugin::load( $plugin_name ) ) { self::$config->plugins[ $plugin_name ] = true; } } @@ -479,7 +478,7 @@ static public function log ( $arg = null, $label = '' ) { } if ( is_string( $arg ) ) { - $log .= $arg . '
'; + $log .= htmlspecialchars( $arg ) . '
'; } else { $out = '
';
diff --git a/lib/Function.php b/lib/Function.php
index fe38111..6bd2ee2 100644
--- a/lib/Function.php
+++ b/lib/Function.php
@@ -6,32 +6,15 @@
  */
 class csscrush_function {
 
-    // Regex pattern for finding custom functions
+    // Regex pattern for finding custom functions.
     static public $functionPatt;
 
-    // Cache for function names
-    static public $functionList;
+    // Stack for function names.
+    static protected $customFunctions;
 
-    static public function init () {
+    static public function setMatchPatt () {
 
-        // Set the custom function regex pattern
-        self::$functionList = self::getFunctions();
-        self::$functionPatt = csscrush_regex::createFunctionMatchPatt( self::$functionList, true );
-    }
-
-    static public function getFunctions () {
-
-        // Fetch custom function names
-        // Include subtraction operator
-        $fn_methods = array( '-' );
-        $all_methods = get_class_methods( __CLASS__ );
-        foreach ( $all_methods as &$_method ) {
-            $prefix = 'css_fn__';
-            if ( ( $pos = strpos( $_method, $prefix ) ) === 0 ) {
-                $fn_methods[] = str_replace( '_', '-', substr( $_method, strlen( $prefix ) ) );
-            }
-        }
-        return $fn_methods;
+        self::$functionPatt = csscrush_regex::createFunctionMatchPatt( array_keys( self::$customFunctions ), true );
     }
 
     static public function executeCustomFunctions ( &$str, $patt = null, $process_callback = null, $property = null ) {
@@ -78,17 +61,15 @@ static public function executeCustomFunctions ( &$str, $patt = null, $process_ca
             // Get the function arguments.
             $args = trim( $parens[1][0] );
 
-            // Workaround the minus.
-            $minus_before = '-' === $raw_fn_name ? '-' : '';
+            // Workaround the signs.
+            $before_operator = '-' === $raw_fn_name ? '-' : '';
 
             $func_returns = '';
 
             if ( ! $process_callback ) {
-
                 // If no callback reference it's a built-in.
-                if ( in_array( $fn_name, self::$functionList ) ) {
-                    $fn_name_clean = str_replace( '-', '_', $fn_name );
-                    $func_returns = call_user_func( array( 'self', "css_fn__$fn_name_clean" ), $args );
+                if ( array_key_exists( $fn_name, self::$customFunctions ) ) {
+                    $func_returns = call_user_func( self::$customFunctions[ $fn_name ], $args );
                 }
             }
             else {
@@ -97,14 +78,22 @@ static public function executeCustomFunctions ( &$str, $patt = null, $process_ca
                 }
             }
 
-            // Join together the result.
-            $str = substr( $str, 0, $offset ) . $minus_before . $func_returns . substr( $str, $closing_paren );
+            // Splice in the function returns.
+            $str = substr_replace( $str, "$before_operator$func_returns", $offset, $closing_paren - $offset );
         }
     }
 
 
     #############################
-    #  Helpers.
+    #  API and helpers.
+
+    static public function register ( $name, $callback ) {
+        csscrush_function::$customFunctions[ $name ] = $callback;
+    }
+
+    static public function deRegister ( $name ) {
+        unset( csscrush_function::$customFunctions[ $name ] );
+    }
 
     static public function parseArgs ( $input, $allowSpaceDelim = false ) {
         return csscrush_util::splitDelimList(
@@ -112,12 +101,12 @@ static public function parseArgs ( $input, $allowSpaceDelim = false ) {
     }
 
     // Intended as a quick arg-list parse for function that take up-to 2 arguments
-    // with the proviso the first argument is a name
+    // with the proviso the first argument is an ident.
     static public function parseArgsSimple ( $input ) {
         return preg_split( csscrush_regex::$patt->argListSplit, $input, 2 );
     }
 
-    static protected function colorAdjust ( $color, array $adjustments ) {
+    static public function colorAdjust ( $color, array $adjustments ) {
 
         $fn_matched = preg_match( '!^(#|rgba?|hsla?)!', $color, $m );
         $keywords =& csscrush_color::loadKeywords();
@@ -203,96 +192,102 @@ static protected function colorAdjust ( $color, array $adjustments ) {
             return $color;
         }
     }
+}
 
 
-    #############################
-    #  CSS functions.
 
-    static protected function css_fn__math ( $input ) {
+#############################
+#  Stock custom CSS functions.
 
-        // Strip blacklisted characters
-        $input = preg_replace( csscrush_regex::$patt->mathBlacklist, '', $input );
+function csscrush_fn__math ( $input ) {
 
-        $result = @eval( "return $input;" );
+    // Strip blacklisted characters
+    $input = preg_replace( csscrush_regex::$patt->mathBlacklist, '', $input );
 
-        return $result === false ? 0 : round( $result, 5 );
-    }
+    $result = @eval( "return $input;" );
 
-    static protected function css_fn__percent ( $input ) {
+    return $result === false ? 0 : round( $result, 5 );
+}
 
-        // Strip non-numeric and non delimiter characters
-        $input = preg_replace( '![^\d\.\s,]!S', '', $input );
+function csscrush_fn__percent ( $input ) {
 
-        $args = preg_split( csscrush_regex::$patt->argListSplit, $input, -1, PREG_SPLIT_NO_EMPTY );
+    // Strip non-numeric and non delimiter characters
+    $input = preg_replace( '![^\d\.\s,]!S', '', $input );
 
-        // Use precision argument if it exists, use default otherwise
-        $precision = isset( $args[2] ) ? $args[2] : 5;
+    $args = preg_split( csscrush_regex::$patt->argListSplit, $input, -1, PREG_SPLIT_NO_EMPTY );
 
-        // Output zero on failure
-        $result = 0;
+    // Use precision argument if it exists, use default otherwise
+    $precision = isset( $args[2] ) ? $args[2] : 5;
 
-        // Need to check arguments or we may see divide by zero errors
-        if ( count( $args ) > 1 && ! empty( $args[0] ) && ! empty( $args[1] ) ) {
+    // Output zero on failure
+    $result = 0;
 
-            // Use bcmath if it's available for higher precision
+    // Need to check arguments or we may see divide by zero errors
+    if ( count( $args ) > 1 && ! empty( $args[0] ) && ! empty( $args[1] ) ) {
 
-            // Arbitary high precision division
-            if ( function_exists( 'bcdiv' ) ) {
-                $div = bcdiv( $args[0], $args[1], 25 );
-            }
-            else {
-                $div = $args[0] / $args[1];
-            }
+        // Use bcmath if it's available for higher precision
 
-            // Set precision percentage value
-            if ( function_exists( 'bcmul' ) ) {
-                $result = bcmul( (string) $div, '100', $precision );
-            }
-            else {
-                $result = round( $div * 100, $precision );
-            }
+        // Arbitary high precision division
+        if ( function_exists( 'bcdiv' ) ) {
+            $div = bcdiv( $args[0], $args[1], 25 );
+        }
+        else {
+            $div = $args[0] / $args[1];
+        }
 
-            // Trim unnecessary zeros and decimals
-            $result = trim( (string) $result, '0' );
-            $result = rtrim( $result, '.' );
+        // Set precision percentage value
+        if ( function_exists( 'bcmul' ) ) {
+            $result = bcmul( (string) $div, '100', $precision );
+        }
+        else {
+            $result = round( $div * 100, $precision );
         }
 
-        return $result . '%';
+        // Trim unnecessary zeros and decimals
+        $result = trim( (string) $result, '0' );
+        $result = rtrim( $result, '.' );
     }
 
-    // Percent function alias.
-    static protected function css_fn__pc ( $input ) {
-        return self::css_fn__percent( $input );
-    }
+    return $result . '%';
+}
 
-    static protected function css_fn__hsla_adjust ( $input ) {
-        list( $color, $h, $s, $l, $a ) = array_pad( self::parseArgs( $input, true ), 5, 0 );
-        return self::colorAdjust( $color, array( $h, $s, $l, $a ) );
-    }
+function csscrush_fn__hsla_adjust ( $input ) {
+    list( $color, $h, $s, $l, $a ) = array_pad( csscrush_function::parseArgs( $input, true ), 5, 0 );
+    return csscrush_function::colorAdjust( $color, array( $h, $s, $l, $a ) );
+}
 
-    static protected function css_fn__hsl_adjust ( $input ) {
-        list( $color, $h, $s, $l ) = array_pad( self::parseArgs( $input, true ), 4, 0 );
-        return self::colorAdjust( $color, array( $h, $s, $l, 0 ) );
-    }
+function csscrush_fn__hsl_adjust ( $input ) {
+    list( $color, $h, $s, $l ) = array_pad( csscrush_function::parseArgs( $input, true ), 4, 0 );
+    return csscrush_function::colorAdjust( $color, array( $h, $s, $l, 0 ) );
+}
 
-    static protected function css_fn__h_adjust ( $input ) {
-        list( $color, $h ) = array_pad( self::parseArgs( $input, true ), 2, 0 );
-        return self::colorAdjust( $color, array( $h, 0, 0, 0 ) );
-    }
+function csscrush_fn__h_adjust ( $input ) {
+    list( $color, $h ) = array_pad( csscrush_function::parseArgs( $input, true ), 2, 0 );
+    return csscrush_function::colorAdjust( $color, array( $h, 0, 0, 0 ) );
+}
 
-    static protected function css_fn__s_adjust ( $input ) {
-        list( $color, $s ) = array_pad( self::parseArgs( $input, true ), 2, 0 );
-        return self::colorAdjust( $color, array( 0, $s, 0, 0 ) );
-    }
+function csscrush_fn__s_adjust ( $input ) {
+    list( $color, $s ) = array_pad( csscrush_function::parseArgs( $input, true ), 2, 0 );
+    return csscrush_function::colorAdjust( $color, array( 0, $s, 0, 0 ) );
+}
 
-    static protected function css_fn__l_adjust ( $input ) {
-        list( $color, $l ) = array_pad( self::parseArgs( $input, true ), 2, 0 );
-        return self::colorAdjust( $color, array( 0, 0, $l, 0 ) );
-    }
+function csscrush_fn__l_adjust ( $input ) {
+    list( $color, $l ) = array_pad( csscrush_function::parseArgs( $input, true ), 2, 0 );
+    return csscrush_function::colorAdjust( $color, array( 0, 0, $l, 0 ) );
+}
 
-    static protected function css_fn__a_adjust ( $input ) {
-        list( $color, $a ) = array_pad( self::parseArgs( $input, true ), 2, 0 );
-        return self::colorAdjust( $color, array( 0, 0, 0, $a ) );
-    }
+function csscrush_fn__a_adjust ( $input ) {
+    list( $color, $a ) = array_pad( csscrush_function::parseArgs( $input, true ), 2, 0 );
+    return csscrush_function::colorAdjust( $color, array( 0, 0, 0, $a ) );
 }
 
+csscrush_function::register( 'math', 'csscrush_fn__math' );
+csscrush_function::register( 'percent', 'csscrush_fn__percent' );
+csscrush_function::register( 'pc', 'csscrush_fn__percent' );
+csscrush_function::register( 'hsla-adjust', 'csscrush_fn__hsla_adjust' );
+csscrush_function::register( 'hsl-adjust', 'csscrush_fn__hsl_adjust' );
+csscrush_function::register( 'h-adjust', 'csscrush_fn__h_adjust' );
+csscrush_function::register( 's-adjust', 'csscrush_fn__s_adjust' );
+csscrush_function::register( 'l-adjust', 'csscrush_fn__l_adjust' );
+csscrush_function::register( 'a-adjust', 'csscrush_fn__a_adjust' );
+
diff --git a/lib/Hook.php b/lib/Hook.php
index 3eb72d9..f33efeb 100644
--- a/lib/Hook.php
+++ b/lib/Hook.php
@@ -1,34 +1,26 @@
 location . "/plugins/$plugin_name.php";
 
@@ -31,29 +29,31 @@ static public function enable ( $plugin_name ) {
 
                 trigger_error( __METHOD__ .
                     ": $plugin_name plugin not found.\n", E_USER_NOTICE );
-                return false;
             }
-            require_once $path;
+            else {
+                require_once $path;
+            }
         }
+        return isset( self::$plugins[ $plugin_name ] ) ? self::$plugins[ $plugin_name ] : null;;
+    }
+
+    static public function enable ( $plugin_name ) {
 
-        // If the plugin is associated with a hook, we make sure it is hooked
-        if ( isset( self::$associated_hooks[ $plugin_name ] ) ) {
+        $plugin = self::load( $plugin_name );
 
-            csscrush_hook::add(
-                self::$associated_hooks[ $plugin_name ],
-                $plugin_function );
+        if ( is_callable( $plugin[ 'enable' ] ) ) {
+            $plugin[ 'enable' ]();
         }
+
         return true;
     }
 
     static public function disable ( $plugin_name ) {
 
-        // If the plugin is associated with a hook, we 'un-hook' it
-        if ( isset( self::$associated_hooks[ $plugin_name ] ) ) {
+        $plugin = isset( self::$plugins[ $plugin_name ] ) ? self::$plugins[ $plugin_name ] : null;
 
-            csscrush_hook::remove(
-                self::$associated_hooks[ $plugin_name ],
-                self::$prefix . str_replace( '-', '_', $plugin_name ) );
+        if ( is_callable( $plugin[ 'disable' ] ) ) {
+            $plugin[ 'disable' ]();
         }
     }
 }
diff --git a/lib/Process.php b/lib/Process.php
index 8758ab3..b6e3404 100644
--- a/lib/Process.php
+++ b/lib/Process.php
@@ -42,7 +42,7 @@ public function __construct ( $options ) {
         // Copy config values.
         $this->plugins = $config->plugins;
         $this->aliases = $config->aliases;
-        $this->selectorAliases = $config->selectorAliases;
+        $this->selectorAliases = array();
         $this->selectorAliasesPatt = null;
 
         // Pick a doc root.
@@ -157,16 +157,31 @@ public function addToken ( $value, $type ) {
     }
 
     public function fetchToken ( $token ) {
-        $type = substr( $token, 1, 1 );
-        $path =& $this->tokens->{ $type };
+        $path =& $this->tokens->{ $token[1] };
         if ( isset( $path[ $token ] ) ) {
             return $path[ $token ];
         }
         return null;
     }
 
+    public function cloneToken ( $token ) {
+        $path =& $this->tokens->{ $token[1] };
+        if ( isset( $path[ $token ] ) ) {
+            return $this->addToken( $path[ $token ], $token[1] );
+        }
+        // Return empty token if passed token is unset.
+        return $this->addToken( '', $token[1] );
+    }
+
+    public function updateToken ( $token, $new_value ) {
+        $path =& $this->tokens->{ $token[1] };
+        if ( isset( $path[ $token ] ) ) {
+            $path[ $token ] = $new_value;
+        }
+    }
+
     public function releaseToken ( $token ) {
-        unset( $this->tokens->{ substr( $token, 1, 1 ) }[ $token ] );
+        unset( $this->tokens->{ $token[1] }[ $token ] );
     }
 
     public function restoreTokens ( $str, $type = 'p' ) {
@@ -283,8 +298,7 @@ static protected function applySelectorAliases ( &$str ) {
                     return isset( $table[ $m[1] ] ) ? $table[ $m[1] ] : "";
                 ');
             }
-            $str = preg_replace_callback(
-                csscrush::$process->selectorAliasesPatt, $callback, $str );
+            $str = preg_replace_callback( csscrush::$process->selectorAliasesPatt, $callback, $str );
         }
     }
 
@@ -298,6 +312,9 @@ protected function extractSelectorAliases () {
 
         $this->stream->pregReplaceCallback( csscrush_regex::$patt->selectorAlias, $callback );
 
+        // Merge in global selector aliases.
+        $this->selectorAliases += csscrush::$config->selectorAliases;
+
         // Create the selector aliases pattern and store it.
         if ( $this->selectorAliases ) {
             $names = implode( '|', array_keys( $this->selectorAliases ) );
@@ -992,6 +1009,9 @@ public function compile () {
         $this->filterPlugins();
         $this->filterAliases();
 
+        // Create function matching regex.
+        csscrush_function::setMatchPatt();
+
         // Collate hostfile and imports.
         $this->stream = new csscrush_stream( csscrush_importer::hostfile( $this->input ) );
 
diff --git a/lib/Regex.php b/lib/Regex.php
index 1e389a7..a30e07c 100644
--- a/lib/Regex.php
+++ b/lib/Regex.php
@@ -104,9 +104,14 @@ static public function matchAll ( $patt, $subject, $preprocess_patt = false, $of
         return $count ? $matches : array();
     }
 
-    static public function createFunctionMatchPatt ( $list, $include_unnamed_function = false ) {
+    static public function createFunctionMatchPatt ( $list, $include_math_function = false ) {
 
-        $question = $include_unnamed_function ? '?' : '';
+        $question = '';
+        if ( $include_math_function ) {
+            $question = '?';
+            // Signing on math bare parens.
+            $list[] = '-';
+        }
 
         foreach ( $list as &$fn_name ) {
             $fn_name = preg_quote( $fn_name );
diff --git a/lib/Rule.php b/lib/Rule.php
index a4c4775..b347c1f 100644
--- a/lib/Rule.php
+++ b/lib/Rule.php
@@ -398,32 +398,30 @@ public function addPropertyAliases () {
     public function addFunctionAliases () {
 
         $function_aliases =& csscrush::$process->aliases[ 'functions' ];
-        $aliased_functions = array_keys( $function_aliases );
+        $aliased_func_names = array_keys( $function_aliases );
 
-        if ( empty( $aliased_functions ) ) {
+        if ( empty( $aliased_func_names ) ) {
             return;
         }
 
+        // The new modified set of declarations.
         $new_set = array();
 
         // Keep track of the function aliases we apply and to which property
         // they belong, so we can avoid un-unecessary duplications.
         $used_fn_aliases = array();
 
-        // Shim in aliased functions
+        // Shim in aliased functions.
         foreach ( $this->declarations as $declaration ) {
 
-            // No functions, skip
-            if (
-                $declaration->skip ||
-                empty( $declaration->functions )
-            ) {
+            // No functions, bail.
+            if ( $declaration->skip || empty( $declaration->functions ) ) {
                 $new_set[] = $declaration;
                 continue;
             }
 
-            // Get list of functions used in declaration that are alias-able, if none skip.
-            $intersect = array_intersect( $declaration->functions, $aliased_functions );
+            // Get list of functions used in declaration that are alias-able, if none bail.
+            $intersect = array_intersect( $declaration->functions, $aliased_func_names );
             if ( empty( $intersect ) ) {
                 $new_set[] = $declaration;
                 continue;
@@ -432,47 +430,41 @@ public function addFunctionAliases () {
             // Loop the aliasable functions.
             foreach ( $intersect as $fn_name ) {
 
-                if ( $declaration->vendor ) {
-                    // If the property is vendor prefixed we use the vendor prefixed version
-                    // of the function if it exists.
-                    // Else we just skip and use the unprefixed version
-                    $fn_search = "-{$declaration->vendor}-$fn_name";
-                    if ( in_array( $fn_search, $function_aliases[ $fn_name ] ) ) {
-                        $declaration->value = preg_replace(
-                            '!(^| |,)' . $fn_name . '!',
-                            '${1}' . $fn_search,
-                            $declaration->value
-                        );
-                        $used_fn_aliases[ $declaration->canonicalProperty ][] = $fn_search;
-                    }
-                }
-                else {
-
-                    // Duplicate the rule for each alias
-                    foreach ( $function_aliases[ $fn_name ] as $fn_alias ) {
+                // For each aliased function dupe the declaration.
+                // This pretty much limits the aliasing to one function per declaration.
+                $prefixed_copies = array();
+                foreach ( $function_aliases[ $fn_name ] as $fn_alias ) {
 
-                        if (
-                            isset( $used_fn_aliases[ $declaration->canonicalProperty ] ) &&
-                            in_array( $fn_alias, $used_fn_aliases[ $declaration->canonicalProperty ] )
-                        ) {
-                            // If the function alias has already been applied in a vendor property
-                            // for the same declaration property assume all is good
+                    // If the declaration is vendor specific only create aliases for the same vendor.
+                    if ( $declaration->vendor ) {
+                        preg_match( csscrush_regex::$patt->vendorPrefix, $fn_alias, $m );
+                        if ( strtolower( $m[1] ) !== $declaration->vendor ) {
                             continue;
                         }
-                        $copy = clone $declaration;
-                        $copy->value = preg_replace(
-                            '!(^| |,)' . $fn_name . '!',
-                            '${1}' . $fn_alias,
-                            $copy->value
-                        );
-                        $new_set[] = $copy;
                     }
+
+                    $copy = clone $declaration;
+
+                    // Swap in the aliased function name.
+                    $copy->value = preg_replace(
+                        '~(?value
+                    );
+                    $prefixed_copies[] = $copy;
                 }
+
+                // Aliased function may require some additional fiddling.
+                if ( isset( csscrush_legacySyntax::$functions[ $fn_name ] ) ) {
+                    call_user_func( csscrush_legacySyntax::$functions[ $fn_name ], $prefixed_copies, $fn_name );
+                }
+
+                $new_set = array_merge( $new_set, $prefixed_copies );
             }
             $new_set[] = $declaration;
         }
 
-        // Re-assign
+        // Re-assign.
         $this->declarations = $new_set;
     }
 
@@ -731,6 +723,97 @@ static public function get ( $token ) {
 }
 
 
+/**
+ *
+ * Fixes for aliasing to legacy syntaxes.
+ *
+ */
+class csscrush_legacySyntax {
+
+    static public $functions = array(
+        'linear-gradient'           => array( __CLASS__, 'linearGradients' ),
+        'linear-repeating-gradient' => array( __CLASS__, 'linearGradients' ),
+        'radial-gradient'           => array( __CLASS__, 'radialGradients' ),
+        'radial-repeating-gradient' => array( __CLASS__, 'radialGradients' ),
+    );
+
+    static public function linearGradients ( $prefixed_copies, $fn_name ) {
+
+        static $angles_new, $angles_old;
+        if ( ! $angles_new ) {
+            $angles = array(
+                'to top' => 'bottom',
+                'to right' => 'left',
+                'to bottom' => 'top',
+                'to left' => 'right',
+                // 'magic' corners.
+                'to top left' => 'bottom right',
+                'to left top' => 'bottom right',
+                'to top right' => 'bottom left',
+                'to right top' => 'bottom left',
+                'to bottom left' => 'top right',
+                'to left bottom' => 'top right',
+                'to bottom right' => 'top left',
+                'to right bottom' => 'top left',
+            );
+            $angles_new = array_keys( $angles );
+            $angles_old = array_values( $angles );
+        }
+
+        // Swap the new 'to' gradient syntax to the old 'from' syntax for the prefixed versions.
+        // 1. Create new paren tokens based on the first prefixed declaration.
+        // 2. Replace the new syntax with the legacy syntax.
+        // 3. Swap in the new tokens on all the prefixed declarations.
+
+        // 1, 2.
+        $patt = '~(?value ) as $m ) {
+            $original_parens[] = $m[1][0];
+            $replacement_parens[] = csscrush::$process->addToken(
+                str_ireplace(
+                    $angles_new,
+                    $angles_old,
+                    csscrush::$process->fetchToken( $m[1][0] )
+                ), 'p' );
+        }
+
+        // 3.
+        foreach ( $prefixed_copies as $prefixed_copy ) {
+            $prefixed_copy->value = str_replace( $original_parens, $replacement_parens, $prefixed_copy->value );
+        }
+    }
+
+    static public function radialGradients ( $prefixed_copies, $fn_name ) {
+
+        // Remove the new 'at' keyword from gradient syntax for legacy implementations.
+        // 1. Create new paren tokens based on the first prefixed declaration.
+        // 2. Replace the new syntax with the legacy syntax.
+        // 3. Swap in the new tokens on all the prefixed declarations.
+
+        // 1, 2.
+        $patt = '~(?value ) as $m ) {
+            $original_parens[] = $m[1][0];
+            $replacement_parens[] = csscrush::$process->addToken(
+                preg_replace(
+                    '~\bat +(top|left|bottom|right|center)\b~i',
+                    '$1',
+                    csscrush::$process->fetchToken( $m[1][0] )
+                ), 'p' );
+        }
+
+        // 3.
+        foreach ( $prefixed_copies as $prefixed_copy ) {
+            $prefixed_copy->value = str_replace( $original_parens, $replacement_parens, $prefixed_copy->value );
+        }
+    }
+}
+
+
 /**
  *
  * Declaration objects.
@@ -802,9 +885,9 @@ public function __construct ( $prop, $value, $contextIndex = 0 ) {
         if ( preg_match_all( $regex->function, $value, $functions ) > 0 ) {
             $out = array();
             foreach ( $functions[2] as $index => $fn_name ) {
-                $out[] = $fn_name;
+                $out[ strtolower( $fn_name ) ] = true;
             }
-            $functions = array_unique( $out );
+            $functions = array_keys( $out );
         }
         else {
             $functions = array();
diff --git a/plugins/hocus-pocus.php b/plugins/hocus-pocus.php
index f638832..b8d0f19 100644
--- a/plugins/hocus-pocus.php
+++ b/plugins/hocus-pocus.php
@@ -13,5 +13,17 @@
  * 
  */
 
-csscrush::$config->selectorAliases[ 'hocus' ] = ':any(:hover,:focus)';
-csscrush::$config->selectorAliases[ 'pocus' ] = ':any(:hover,:focus,:active)';
+csscrush_plugin::register( 'hocus-pocus', array(
+    'enable' => 'csscrush__enable_hocus_pocus',
+    'disable' => 'csscrush__disable_hocus_pocus',
+));
+
+function csscrush__enable_hocus_pocus () {
+    csscrush::$config->selectorAliases[ 'hocus' ] = ':any(:hover,:focus)';
+    csscrush::$config->selectorAliases[ 'pocus' ] = ':any(:hover,:focus,:active)';
+}
+
+function csscrush__disable_hocus_pocus () {
+    unset( csscrush::$config->selectorAliases[ 'hocus' ] );
+    unset( csscrush::$config->selectorAliases[ 'pocus' ] );
+}
diff --git a/plugins/hsl-to-hex.php b/plugins/hsl-to-hex.php
index 9180cf2..8d1a5b4 100644
--- a/plugins/hsl-to-hex.php
+++ b/plugins/hsl-to-hex.php
@@ -10,7 +10,18 @@
  *    color: #6abf40
  */
 
-csscrush_hook::add( 'rule_postalias', 'csscrush__hsl_to_hex' );
+csscrush_plugin::register( 'hsl-to-hex', array(
+    'enable' => 'csscrush__enable_hsl_to_hex',
+    'disable' => 'csscrush__disable_hsl_to_hex',
+));
+
+function csscrush__enable_hsl_to_hex () {
+    csscrush_hook::add( 'rule_postalias', 'csscrush__hsl_to_hex' );
+}
+
+function csscrush__disable_hsl_to_hex () {
+    csscrush_hook::remove( 'rule_postalias', 'csscrush__hsl_to_hex' );
+}
 
 function csscrush__hsl_to_hex ( csscrush_rule $rule ) {
 
diff --git a/plugins/ie-clip.php b/plugins/ie-clip.php
index 1a70ea5..a09c049 100644
--- a/plugins/ie-clip.php
+++ b/plugins/ie-clip.php
@@ -10,7 +10,18 @@
  *     *clip: rect(1px 1px 1px 1px);
  */
 
-csscrush_hook::add( 'rule_postalias', 'csscrush__ie_clip' );
+csscrush_plugin::register( 'ie-clip', array(
+    'enable' => 'csscrush__enable_ie_clip',
+    'disable' => 'csscrush__disable_ie_clip',
+));
+
+function csscrush__enable_ie_clip () {
+    csscrush_hook::add( 'rule_postalias', 'csscrush__ie_clip' );
+}
+
+function csscrush__disable_ie_clip () {
+    csscrush_hook::remove( 'rule_postalias', 'csscrush__ie_clip' );
+}
 
 function csscrush__ie_clip ( csscrush_rule $rule ) {
 
diff --git a/plugins/ie-filter.php b/plugins/ie-filter.php
index a0ebb2e..53033a4 100644
--- a/plugins/ie-filter.php
+++ b/plugins/ie-filter.php
@@ -16,7 +16,18 @@
  *     zoom: 1;
  */
 
-csscrush_hook::add( 'rule_postalias', 'csscrush__ie_filter' );
+csscrush_plugin::register( 'ie-filter', array(
+    'enable' => 'csscrush__enable_ie_filter',
+    'disable' => 'csscrush__disable_ie_filter',
+));
+
+function csscrush__enable_ie_filter () {
+    csscrush_hook::add( 'rule_postalias', 'csscrush__ie_filter' );
+}
+
+function csscrush__disable_ie_filter () {
+    csscrush_hook::remove( 'rule_postalias', 'csscrush__ie_filter' );
+}
 
 function csscrush__ie_filter ( csscrush_rule $rule ) {
 
diff --git a/plugins/ie-inline-block.php b/plugins/ie-inline-block.php
index f61770c..4004dcb 100644
--- a/plugins/ie-inline-block.php
+++ b/plugins/ie-inline-block.php
@@ -11,7 +11,18 @@
  *     *zoom: 1;
  */
 
-csscrush_hook::add( 'rule_postalias', 'csscrush__ie_inline_block' );
+csscrush_plugin::register( 'ie-inline-block', array(
+    'enable' => 'csscrush__enable_ie_inline_block',
+    'disable' => 'csscrush__disable_ie_inline_block',
+));
+
+function csscrush__enable_ie_inline_block () {
+    csscrush_hook::add( 'rule_postalias', 'csscrush__ie_inline_block' );
+}
+
+function csscrush__disable_ie_inline_block () {
+    csscrush_hook::remove( 'rule_postalias', 'csscrush__ie_inline_block' );
+}
 
 function csscrush__ie_inline_block ( csscrush_rule $rule ) {
 
diff --git a/plugins/ie-min-height.php b/plugins/ie-min-height.php
index 29e130d..6f15416 100644
--- a/plugins/ie-min-height.php
+++ b/plugins/ie-min-height.php
@@ -10,7 +10,18 @@
  *     _height: 100px;
  */
 
-csscrush_hook::add( 'rule_postalias', 'csscrush__ie_min_height' );
+csscrush_plugin::register( 'ie-min-height', array(
+    'enable' => 'csscrush__enable_ie_min_height',
+    'disable' => 'csscrush__disable_ie_min_height',
+));
+
+function csscrush__enable_ie_min_height () {
+    csscrush_hook::add( 'rule_postalias', 'csscrush__ie_min_height' );
+}
+
+function csscrush__disable_ie_min_height () {
+    csscrush_hook::remove( 'rule_postalias', 'csscrush__ie_min_height' );
+}
 
 function csscrush__ie_min_height ( csscrush_rule $rule ) {
 
diff --git a/plugins/ie-opacity.php b/plugins/ie-opacity.php
index 352647e..05dc3db 100755
--- a/plugins/ie-opacity.php
+++ b/plugins/ie-opacity.php
@@ -12,7 +12,18 @@
  *     zoom: 1;
  */
 
-csscrush_hook::add( 'rule_postalias', 'csscrush__ie_opacity' );
+csscrush_plugin::register( 'ie-opacity', array(
+    'enable' => 'csscrush__enable_ie_opacity',
+    'disable' => 'csscrush__disable_ie_opacity',
+));
+
+function csscrush__enable_ie_opacity () {
+    csscrush_hook::add( 'rule_postalias', 'csscrush__ie_opacity' );
+}
+
+function csscrush__disable_ie_opacity () {
+    csscrush_hook::remove( 'rule_postalias', 'csscrush__ie_opacity' );
+}
 
 function csscrush__ie_opacity ( csscrush_rule $rule ) {
 
diff --git a/plugins/initial.php b/plugins/initial.php
index 095e89d..ff17648 100644
--- a/plugins/initial.php
+++ b/plugins/initial.php
@@ -15,7 +15,18 @@
  * 
  */
 
-csscrush_hook::add( 'rule_prealias', 'csscrush__initial' );
+csscrush_plugin::register( 'initial', array(
+    'enable' => 'csscrush__enable_initial',
+    'disable' => 'csscrush__disable_initial',
+));
+
+function csscrush__enable_initial () {
+    csscrush_hook::add( 'rule_prealias', 'csscrush__initial' );
+}
+
+function csscrush__disable_initial () {
+    csscrush_hook::remove( 'rule_prealias', 'csscrush__initial' );
+}
 
 function csscrush__initial ( csscrush_rule $rule ) {
 
diff --git a/plugins/property-sorter.php b/plugins/property-sorter.php
index 1f994dd..7f29420 100644
--- a/plugins/property-sorter.php
+++ b/plugins/property-sorter.php
@@ -24,7 +24,18 @@
  *
  */
 
-csscrush_hook::add( 'rule_prealias', 'csscrush__property_sorter' );
+csscrush_plugin::register( 'property-sorter', array(
+    'enable' => 'csscrush__enable_property_sorter',
+    'disable' => 'csscrush__disable_property_sorter',
+));
+
+function csscrush__enable_property_sorter () {
+    csscrush_hook::add( 'rule_prealias', 'csscrush__property_sorter' );
+}
+
+function csscrush__disable_property_sorter () {
+    csscrush_hook::remove( 'rule_prealias', 'csscrush__property_sorter' );
+}
 
 function csscrush__property_sorter ( csscrush_rule $rule ) {
 
diff --git a/plugins/rgba-fallback.php b/plugins/rgba-fallback.php
index c2d93e3..2b2c57a 100644
--- a/plugins/rgba-fallback.php
+++ b/plugins/rgba-fallback.php
@@ -12,8 +12,18 @@
  *     background: rgba(0,0,0,.5);
  */
 
-csscrush_hook::add( 'rule_postalias', 'csscrush__rgba_fallback' );
+csscrush_plugin::register( 'rgba-fallback', array(
+    'enable' => 'csscrush__enable_rgba_fallback',
+    'disable' => 'csscrush__disable_rgba_fallback',
+));
 
+function csscrush__enable_rgba_fallback () {
+    csscrush_hook::add( 'rule_postalias', 'csscrush__rgba_fallback' );
+}
+
+function csscrush__disable_rgba_fallback () {
+    csscrush_hook::remove( 'rule_postalias', 'csscrush__rgba_fallback' );
+}
 
 function csscrush__rgba_fallback ( csscrush_rule $rule ) {
 
diff --git a/plugins/spiffing.php b/plugins/spiffing.php
index 436a0bb..06db438 100644
--- a/plugins/spiffing.php
+++ b/plugins/spiffing.php
@@ -2,23 +2,34 @@
 /**
  * Spiffing (http://spiffingcss.com), by @idiot.
  * Transforms correctly-spelt Queen's English into valid CSS.
- * 
- * @before 
+ *
+ * @before
  *     background-colour: grey !please;
  *     transparency: 0.5;
- * 
+ *
  * @after
  *     background-color: gray !important;
  *     opacity: 0.5;
- * 
+ *
  */
 
-csscrush_hook::add( 'rule_preprocess', 'csscrush__spiffing' );
+csscrush_plugin::register( 'spiffing', array(
+    'enable' => 'csscrush__enable_spiffing',
+    'disable' => 'csscrush__disable_spiffing',
+));
+
+function csscrush__enable_spiffing () {
+    csscrush_hook::add( 'rule_preprocess', 'csscrush__spiffing' );
+}
+
+function csscrush__disable_spiffing () {
+    csscrush_hook::add( 'rule_preprocess', 'csscrush__spiffing' );
+}
 
 function csscrush__spiffing ( $rule ) {
 
     $find = array( 'colour', 'grey', '!please', 'transparency', 'centre', 'plump', 'photograph', 'capitalise' );
     $replace = array( 'color', 'gray', '!important', 'opacity', 'center', 'bold', 'image', 'capitalize' );
-    
+
     $rule->declaration_raw = str_ireplace( $find, $replace, $rule->declaration_raw );
 }
diff --git a/plugins/svg-gradients.php b/plugins/svg-gradients.php
new file mode 100644
index 0000000..dfc19e2
--- /dev/null
+++ b/plugins/svg-gradients.php
@@ -0,0 +1,337 @@
+ | to  ,]?  [, ]+ )
+ * 
+ * Returns:
+ *      A base64 encoded svg data-uri.
+ * 
+ * Known issues:
+ *      Color stops can only take percentage value offsets.
+ * 
+ * Examples:
+ *      background-image: svg-linear-gradient( to top left, #fff, rgba(255,255,255,0) 80% );
+ *      background-image: svg-linear-gradient( 35deg, red, gold 20%, powderblue );
+ * 
+ * svg-radial-gradent()
+ * --------------------
+ * Syntax is similar to but more limited than CR radial-gradient(): http://dev.w3.org/csswg/css3-images/#radial-gradient
+ * 
+ *      svg-radial-gradent( [  | at  ,]?  [, ]+ )
+ * 
+ * Returns:
+ *      A base64 encoded svg data-uri.
+ * 
+ * Known issues:
+ *      Color stops can only take percentage value offsets.
+ *      No control over shape - only circular gradients - however, the generated image can be stretched
+ *      with background-size.
+ * 
+ * Examples:
+ *      background-image: svg-radial-gradient( at center, red, blue 50%, yellow );
+ *      background-image: svg-radial-gradient( 100% 50%, rgba(255,255,255,.5), rgba(255,255,255,0) );
+ *
+ */
+
+csscrush_plugin::register( 'svg-gradients', array(
+    'enable' => 'csscrush__enable_svg_gradients',
+    'disable' => 'csscrush__disable_svg_gradients',
+));
+
+function csscrush__enable_svg_gradients () {
+    csscrush_function::register( 'svg-linear-gradient', 'csscrush_fn__svg_linear_gradient' );
+    csscrush_function::register( 'svg-radial-gradient', 'csscrush_fn__svg_radial_gradient' );
+}
+
+function csscrush__disable_svg_gradients () {
+    csscrush_function::deRegister( 'svg-linear-gradient', 'csscrush_fn__svg_linear_gradient' );
+    csscrush_function::deRegister( 'svg-radial-gradient', 'csscrush_fn__svg_radial_gradient' );
+}
+
+function csscrush_fn__svg_linear_gradient ( $input ) {
+
+    // For inline SVG debugging.
+    // static $uid = 0; $uid++;
+    static $uid = '';
+
+    static $angle_keywords;
+    if ( ! $angle_keywords ) {
+        $angle_keywords = array(
+            'to top'    => 180,
+            'to right'  => 270,
+            'to bottom' => 0,
+            'to left'   => 90,
+            // Not very magic corners.
+            'to top right'    => array( array(0, 100), array(100, 0) ),
+            'to top left'     => array( array(100, 100), array(0, 0) ),
+            'to bottom right' => array( array(0, 0), array(100, 100) ),
+            'to bottom left'  => array( array(100, 0), array(0, 100) ),
+        );
+        $angle_keywords[ 'to right top' ] = $angle_keywords[ 'to top right' ];
+        $angle_keywords[ 'to left top' ] = $angle_keywords[ 'to top left' ];
+        $angle_keywords[ 'to right bottom' ] = $angle_keywords[ 'to bottom right' ];
+        $angle_keywords[ 'to left bottom' ] = $angle_keywords[ 'to bottom left' ];
+    }
+
+    $args = csscrush_function::parseArgs( $input );
+
+    // If no angle argument is passed the default used is 0.
+    $angle = 0;
+
+    // Parse starting and ending coordinates from the first argument if it's an angle.
+    $coords = null;
+    $first_arg = $args[0];
+    $first_arg_is_angle = false;
+
+    // Try to parse an angle value.
+    if ( preg_match( '~-?[\d\.]+deg~i', $first_arg ) ) {
+        $angle = floatval( $first_arg );
+        $first_arg_is_angle = true;
+    }
+    elseif ( isset( $angle_keywords[ $first_arg ] ) ) {
+        if ( is_array( $angle_keywords[ $first_arg ] ) ) {
+            $coords = $angle_keywords[ $first_arg ];
+        }
+        else {
+            $angle = $angle_keywords[ $first_arg ];
+        }
+        $first_arg_is_angle = true;
+    }
+
+    // Shift off the first argument if it has been recognised as an angle.
+    if ( $first_arg_is_angle ) {
+        array_shift( $args );
+    }
+
+    // If not using a magic corner, create start/end coordinates from the angle.
+    if ( ! $coords ) {
+        // Normalize the angle.
+        $angle = fmod( $angle, 360 );
+        if ( $angle < 0 ) {
+            $angle = 360 + $angle;
+        }
+        $angle = round( $angle, 2 );
+
+        $start_x = 0;
+        $end_x = 0;
+        $start_y = 0;
+        $end_y = 100;
+
+        if ( $angle >= 0 && $angle <= 45 ) {
+            $start_x = ( ( $angle / 45 ) * 50 ) + 50;
+            $end_x = 100 - $start_x;
+            $start_y = 0;
+            $end_y = 100;
+        }
+        elseif ( $angle > 45 && $angle <= 135 ) {
+            $angle_delta = $angle - 45;
+            $start_x = 100;
+            $end_x = 0;
+            $start_y = ( $angle_delta / 90 ) * 100;
+            $end_y = 100 - $start_y;
+        }
+        elseif ( $angle > 135 && $angle <= 225 ) {
+            $angle_delta = $angle - 135;
+            $start_x = 100 - ( ( $angle_delta / 90 ) * 100 );
+            $end_x = 100 - $start_x;
+            $start_y = 100;
+            $end_y = 0;
+        }
+        elseif ( $angle > 225 && $angle <= 315 ) {
+            $angle_delta = $angle - 225;
+            $start_x = 0;
+            $end_x = 100;
+            $start_y = 100 - ( ( $angle_delta / 90 ) * 100 );
+            $end_y = 100 - $start_y;
+        }
+        elseif ( $angle > 315 && $angle <= 360 ) {
+            $angle_delta = $angle - 315;
+            $start_x = ( $angle_delta / 90 ) * 100;
+            $end_x = 100 - $start_x;
+            $start_y = 0;
+            $end_y = 100;
+        }
+        $coords = array(
+            array( round( $start_x, 1 ), round( $start_y, 1 ) ),
+            array( round( $end_x, 1 ), round( $end_y, 1 ) ),
+        );
+    }
+
+    // The remaining arguments are treated as color stops.
+    // - Capture their color values and if specified color offset percentages.
+    // - Only percentages are supported as SVG gradients to accept other length values
+    //   for color stop offsets.
+    $color_stops = csscrush__parse_gradient_color_stops( $args );
+
+    // Creating the svg.
+    $svg = '';
+    $svg .= '';
+    $svg .= "";
+
+    foreach ( $color_stops as $offset => $color ) {
+        $svg .= "";
+    }
+    $svg .= '';
+    $svg .= '';
+    $svg .= "";
+    $svg .= '';
+
+    // Create data-uri url and return token label.
+    $url = new csscrush_url(http://github.com/'data:image/svg+xml;base64,'%20.%20base64_encode(%20$svg) );
+
+    return $url->label;
+}
+
+function csscrush_fn__svg_radial_gradient ( $input ) {
+
+    // For inline SVG debugging.
+    // static $uid = 0; $uid++;
+    static $uid = '';
+
+    static $position_keywords;
+    if ( ! $position_keywords ) {
+        $position_keywords = array(
+            'at top'    => array('50%', '0%'),
+            'at right'  => array('100%', '50%'),
+            'at bottom' => array('50%', '100%'),
+            'at left'   => array('0%', '50%'),
+            'at center' => array('50%', '50%'),
+            // Not very magic corners.
+            'at top right'    => array('100%', '0%'),
+            'at top left'     => array('0%', '0%'),
+            'at bottom right' => array('100%', '100%'),
+            'at bottom left'  => array('0%', '100%'),
+        );
+        $position_keywords[ 'at right top' ] = $position_keywords[ 'at top right' ];
+        $position_keywords[ 'at left top' ] = $position_keywords[ 'at top left' ];
+        $position_keywords[ 'at right bottom' ] = $position_keywords[ 'at bottom right' ];
+        $position_keywords[ 'at left bottom' ] = $position_keywords[ 'at bottom left' ];
+    }
+
+    $args = csscrush_function::parseArgs( $input );
+
+    // Default origin,
+    $position = $position_keywords[ 'at center' ];
+
+    // Parse origin coordinates from the first argument if it's an origin.
+    $first_arg = $args[0];
+    $first_arg_is_position = false;
+
+    // Try to parse an origin value.
+    if ( preg_match( '~^(-?[\d\.]+%?) +(-?[\d\.]+%?)$~', $first_arg, $m ) ) {
+        $position = array( $m[1], $m[2] );
+        $first_arg_is_position = true;
+    }
+    elseif ( isset( $position_keywords[ $first_arg ] ) ) {
+        $position = $position_keywords[ $first_arg ];
+        $first_arg_is_position = true;
+    }
+
+    // Shift off the first argument if it has been recognised as an origin.
+    if ( $first_arg_is_position ) {
+        array_shift( $args );
+    }
+
+    // The remaining arguments are treated as color stops.
+    // - Capture their color values and if specified color offset percentages.
+    // - Only percentages are supported as SVG gradients to accept other length values
+    //   for color stop offsets.
+    $color_stops = csscrush__parse_gradient_color_stops( $args );
+
+    // Creating the svg.
+    $svg = '';
+    $svg .= '';
+    $svg .= "";
+
+    foreach ( $color_stops as $offset => $color ) {
+        $svg .= "";
+    }
+    $svg .= '';
+    $svg .= '';
+    $svg .= "";
+    $svg .= '';
+
+    // Create data-uri url and return token label.
+    $url = new csscrush_url(http://github.com/'data:image/svg+xml;base64,'%20.%20base64_encode(%20$svg) );
+
+    return $url->label;
+}
+
+function csscrush__parse_gradient_color_stops ( array $color_stop_args ) {
+
+    $offsets = array();
+    $colors = array();
+    $offset_patt = '~ +([\d\.]+%)$~';
+    $last_index = count( $color_stop_args ) - 1;
+
+    foreach ( $color_stop_args as $index => $color_arg ) {
+
+        if ( preg_match( $offset_patt, $color_arg, $m ) ) {
+            $offsets[] = floatval( $m[1] );
+            $colors[] = preg_replace( $offset_patt, '', $color_arg );
+        }
+        else {
+            if ( $index === 0 ) {
+                $offsets[] = 0;
+            }
+            elseif ( $index === $last_index ) {
+                $offsets[] = 100;
+            }
+            else {
+                $offsets[] = null;
+            }
+            $colors[] = $color_arg;
+        }
+    }
+
+    // For unspecified color offsets fill in the blanks.
+    $next_index_not_null = 0;
+    $prev_index_not_null = 0;
+    $n = count( $offsets );
+
+    foreach ( $offsets as $index => $offset ) {
+
+        if ( is_null( $offset ) ) {
+
+            // Scan for next non-null offset.
+            for ( $i = $index; $i < $n; $i++ ) {
+                if ( ! is_null( $offsets[$i] ) ) {
+                    $next_index_not_null = $i;
+                    break;
+                }
+            }
+
+            // Get the difference between previous 'not null' offset and the next 'not null' offset.
+            // Divide by the number of null offsets to get a value for padding between them.
+            $padding_increment =
+                ( $offsets[ $next_index_not_null ] - $offsets[ $prev_index_not_null ] ) /
+                ( $next_index_not_null - $index + 1 );
+            $padding = $padding_increment;
+
+            for ( $i = $index; $i < $n; $i++ ) {
+                if ( ! is_null( $offsets[$i] ) ) {
+                    break;
+                }
+                // Replace the null offset with the new padded value.
+                $offsets[$i] = $offsets[ $prev_index_not_null ] + $padding;
+                // Bump the padding for the next null offset.
+                $padding += $padding_increment;
+            }
+        }
+        else {
+            $prev_index_not_null = $index;
+        }
+    }
+
+    return array_combine( $offsets, $colors );
+}

From 0d92ba3c9f9af295384caabf38250e3d447cd78f Mon Sep 17 00:00:00 2001
From: Pete Boere 
Date: Thu, 6 Dec 2012 15:01:26 +0000
Subject: [PATCH 057/421] Refactored color handling.

Colors are now parsed and manipulated in an oop fashion.
---
 lib/Color.php          | 210 +++++++++++++++--
 lib/Function.php       |  87 +------
 lib/Mixin.php          |  12 +-
 lib/Process.php        |  16 --
 lib/Regex.php          |   2 +-
 lib/Rule.php           | 507 ++++++++++++++++++++---------------------
 plugins/hsl-to-hex.php |  15 +-
 7 files changed, 454 insertions(+), 395 deletions(-)

diff --git a/lib/Color.php b/lib/Color.php
index 8b59c12..758a235 100644
--- a/lib/Color.php
+++ b/lib/Color.php
@@ -56,6 +56,62 @@ static public function &loadMinifyableKeywords () {
         return self::$minifyableKeywords;
     }
 
+    static public function parse ( $color ) {
+
+        $rgba = null;
+        $color = strtolower( $color );
+
+        // First match a hex value or the start of a function.
+        if ( preg_match( '~^(
+                \#(?=[[:xdigit:]]{3}) |
+                \#(?=[[:xdigit:]]{6}) |
+                rgba?(?=[?(]) |
+                hsla?(?=[?(])
+            )~xS', $color, $m ) ) {
+
+            // Get an RGB array from the color argument.
+            switch ( $m[1] ) {
+
+                case '#':
+                    $rgba = csscrush_color::hexToRgb( $color );
+                    break;
+
+                case 'rgb':
+                case 'rgba':
+                case 'hsl':
+                case 'hsla':
+                    $function = $m[1];
+                    $vals = substr( $color, strlen( $function ) + 1 );  // Trim function name and start paren.
+                    $vals = substr( $vals, 0, strlen( $vals ) - 1 );    // Trim end paren.
+                    $vals = array_map( 'trim', explode( ',', $vals ) ); // Explode to array of arguments.
+
+                    // Always set the alpha channel.
+                    $vals[3] = isset( $vals[3] ) ? floatval( $vals[3] ) : 1;
+
+                    if ( strpos( $function, 'rgb' ) === 0 ) {
+                        $rgba = csscrush_color::normalizeCssRgb( $vals );
+                    }
+                    else {
+                        $rgba = csscrush_color::cssHslToRgb( $vals );
+                    }
+                    break;
+            }
+        }
+
+        // Secondly try to match a color keyword.
+        else {
+
+            $keywords =& self::loadKeywords();
+            if ( isset( $keywords[ $color ] ) ) {
+                $rgba = $keywords[ $color ];
+                // Manually add the alpha component.
+                $rgba[] = 1;
+            }
+        }
+
+        return $rgba;
+    }
+
     /**
      * http://mjijackson.com/2008/02/
      * rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
@@ -65,9 +121,9 @@ static public function &loadMinifyableKeywords () {
      * Assumes r, g, and b are contained in the set [0, 255] and
      * returns h, s, and l in the set [0, 1].
      */
-    static public function rgbToHsl ( array $rgb ) {
+    static public function rgbToHsl ( array $rgba ) {
 
-        list( $r, $g, $b ) = $rgb;
+        list( $r, $g, $b, $a ) = $rgba;
         $r /= 255;
         $g /= 255;
         $b /= 255;
@@ -97,7 +153,7 @@ static public function rgbToHsl ( array $rgb ) {
             $h /= 6;
         }
 
-        return array( $h, $s, $l );
+        return array( $h, $s, $l, $a );
     }
 
     /**
@@ -109,8 +165,14 @@ static public function rgbToHsl ( array $rgb ) {
      * Assumes h, s, and l are contained in the set [0, 1] and
      * returns r, g, and b in the set [0, 255].
      */
-    static public function hslToRgb ( array $hsl ) {
-        list( $h, $s, $l ) = $hsl;
+    static public function hslToRgb ( array $hsla ) {
+
+        // Populate unspecified alpha value.
+        if ( ! isset( $hsla[3] ) ) {
+            $hsla[3] = 1;
+        }
+
+        list( $h, $s, $l, $a ) = $hsla;
         $r;
         $g;
         $b;
@@ -124,44 +186,50 @@ static public function hslToRgb ( array $hsl ) {
             $g = self::hueToRgb( $p, $q, $h );
             $b = self::hueToRgb( $p, $q, $h - 1 / 3 );
         }
-        return array( round( $r * 255 ), round( $g * 255 ), round( $b * 255 ) );
+        return array( round( $r * 255 ), round( $g * 255 ), round( $b * 255 ), $a );
     }
 
-    // Convert percentages to points (0-255)
-    static public function normalizeCssRgb ( array $rgb ) {
-        foreach ( $rgb as &$val ) {
+    // Convert percentages to points (0-255).
+    static public function normalizeCssRgb ( array $rgba ) {
+        foreach ( $rgba as &$val ) {
             if ( strpos( $val, '%' ) !== false ) {
                 $val = str_replace( '%', '', $val );
                 $val = round( $val * 2.55 );
             }
         }
-        return $rgb;
+        return $rgba;
     }
 
-    static public function cssHslToRgb ( array $hsl ) {
+    static public function cssHslToRgb ( array $hsla ) {
+
+        // Populate unspecified alpha value.
+        if ( ! isset( $hsla[3] ) ) {
+            $hsla[3] = 1;
+        }
+
+        // Alpha is carried over.
+        $a = array_pop( $hsla );
 
-        // Normalize the hue degree value then convert to float
-        $h = array_shift( $hsl );
+        // Normalize the hue degree value then convert to float.
+        $h = array_shift( $hsla );
         $h = $h % 360;
         if ( $h < 0 ) {
-            $h = 360 + $h;
+            $h = 360 + $hue;
         }
         $h = $h / 360;
 
-        // Convert s and l to floats
-        foreach ( $hsl as &$val ) {
+        // Convert saturation and lightness to floats.
+        foreach ( $hsla as &$val ) {
             $val = str_replace( '%', '', $val );
             $val /= 100;
         }
-        list( $s, $l ) = $hsl;
+        list( $s, $l ) = $hsla;
 
-        $hsl = array( $h, $s, $l );
-        $rgb = self::hslToRgb( $hsl );
-
-        return $rgb;
+        return self::hslToRgb( array( $h, $s, $l, $a ) );
     }
 
     static public function hueToRgb ( $p, $q, $t ) {
+
         if ( $t < 0 ) $t += 1;
         if ( $t > 1 ) $t -= 1;
         if ( $t < 1/6 ) return $p + ( $q - $p ) * 6 * $t;
@@ -170,18 +238,23 @@ static public function hueToRgb ( $p, $q, $t ) {
         return $p;
     }
 
-    static public function rgbToHex ( array $rgb ) {
+    static public function rgbToHex ( array $rgba ) {
+
+        // Drop alpha component.
+        array_pop( $rgba );
+
         $hex_out = '#';
-        foreach ( $rgb as $val ) {
+        foreach ( $rgba as $val ) {
             $hex_out .= str_pad( dechex( $val ), 2, '0', STR_PAD_LEFT );
         }
         return $hex_out;
     }
 
     static public function hexToRgb ( $hex ) {
+
         $hex = substr( $hex, 1 );
 
-        // Handle shortened format
+        // Handle shortened format.
         if ( strlen( $hex ) === 3 ) {
             $long_hex = array();
             foreach ( str_split( $hex ) as $val ) {
@@ -192,7 +265,94 @@ static public function hexToRgb ( $hex ) {
         else {
             $hex = str_split( $hex, 2 );
         }
-        return array_map( 'hexdec', $hex );
+
+        // Return RGBa
+        $rgba = array_map( 'hexdec', $hex );
+        $rgba[] = 1;
+        return $rgba;
     }
 
+
+    #############################
+    #  Instances.
+
+    protected $value;
+    protected $hslColorSpace;
+    public $isValid;
+
+    public function __construct ( $color, $use_hsl_color_space = false ) {
+        $this->value = is_array( $color ) ? $color : self::parse( $color );
+        $this->isValid = isset( $this->value );
+        if ( $use_hsl_color_space && $this->isValid ) {
+            $this->toHsl();
+        }
+    }
+
+    public function __toString () {
+        if ( $this->value[3] !== 1 ) {
+            return 'rgba(' . implode( ',', $this->hslColorSpace ? $this->getRgb() : $this->value ) . ')';
+        }
+        else {
+            return $this->getHex();
+        }
+    }
+
+    public function toRgb () {
+        if ( $this->hslColorSpace ) {
+            $this->hslColorSpace = false;
+            $this->value = self::hslToRgb( $this->value );
+        }
+        return $this;
+    }
+
+    public function toHsl () {
+        if ( ! $this->hslColorSpace ) {
+            $this->hslColorSpace = true;
+            $this->value = self::rgbToHsl( $this->value );
+        }
+        return $this;
+    }
+
+    public function getHex () {
+        return self::rgbToHex( $this->getRgb() );
+    }
+
+    public function getHsl () {
+        return ! $this->hslColorSpace ? self::rgbToHsl( $this->value ) : $this->value;
+    }
+
+    public function getRgb () {
+        return $this->hslColorSpace ? self::hslToRgb( $this->value ) : $this->value;
+    }
+
+    public function getComponent ( $index ) {
+        return $this->value[ $index ];
+    }
+
+    public function setComponent ( $index, $new_component_value ) {
+        $this->value[ $index ] = $new_component_value;
+    }
+
+    public function adjust ( array $adjustments ) {
+
+        $was_hsl_color_space = $this->hslColorSpace;
+
+        $this->toHsl();
+
+        // Normalize percentage adjustment parameters to floating point numbers.
+        foreach ( $adjustments as $index => $val ) {
+
+            // Normalize argument.
+            $val = $val ? trim( str_replace( '%', '', $val ) ) : 0;
+
+            if ( $val ) {
+                // Reduce value to float.
+                $val /= 100;
+                // Update the color component.
+                $this->setComponent( $index, max( 0, min( 1, $this->getComponent( $index ) + $val ) ) );
+            }
+        }
+
+        return ! $was_hsl_color_space ? $this->toRgb() : $this;
+    }
 }
diff --git a/lib/Function.php b/lib/Function.php
index 6bd2ee2..0660ff0 100644
--- a/lib/Function.php
+++ b/lib/Function.php
@@ -106,91 +106,12 @@ static public function parseArgsSimple ( $input ) {
         return preg_split( csscrush_regex::$patt->argListSplit, $input, 2 );
     }
 
-    static public function colorAdjust ( $color, array $adjustments ) {
-
-        $fn_matched = preg_match( '!^(#|rgba?|hsla?)!', $color, $m );
-        $keywords =& csscrush_color::loadKeywords();
-
-        // Support for Hex, RGB, RGBa and keywords
-        // HSL and HSLa are passed over
-        if ( $fn_matched || isset( $keywords[ $color ] ) ) {
-
-            $alpha = 1;
-            $rgb = null;
-
-            // Get an RGB array from the color argument
-            if ( $fn_matched ) {
-                switch ( $m[1] ) {
-                    case '#':
-                        $rgb = csscrush_color::hexToRgb( $color );
-                        break;
-
-                    case 'rgb':
-                    case 'rgba':
-                    case 'hsl':
-                    case 'hsla':
-                        $function = $m[1];
-                        $alpha_channel = 4 === strlen( $function ) ? true : false;
-                        $vals = substr( $color, strlen( $function ) + 1 );  // Trim function name and start paren
-                        $vals = substr( $vals, 0, strlen( $vals ) - 1 );    // Trim end paren
-                        $vals = array_map( 'trim', explode( ',', $vals ) ); // Explode to array of arguments
-                        if ( $alpha_channel ) {
-                            $alpha = array_pop( $vals );
-                        }
-                        if ( 0 === strpos( $function, 'rgb' ) ) {
-                            $rgb = csscrush_color::normalizeCssRgb( $vals );
-                        }
-                        else {
-                            $rgb = csscrush_color::cssHslToRgb( $vals );
-                        }
-                        break;
-                }
-            }
-            else {
-                $rgb = $keywords[ $color ];
-            }
+    static public function colorAdjust ( $raw_color, array $adjustments ) {
 
-            $hsl = csscrush_color::rgbToHsl( $rgb );
+        $hsla = new csscrush_color( $raw_color, true );
 
-            // Normalize adjustment parameters to floating point numbers
-            // then calculate the new HSL value
-            $index = 0;
-            foreach ( $adjustments as $val ) {
-                // Normalize argument
-                $_val = $val ? trim( str_replace( '%', '', $val ) ) : 0;
-
-                // Reduce value to float
-                $_val /= 100;
-
-                // Adjust alpha component if necessary
-                if ( 3 === $index ) {
-                    if ( 0 != $val ) {
-                        $alpha = max( 0, min( 1, $alpha + $_val ) );
-                    }
-                }
-                // Adjust HSL component value if necessary
-                else {
-                    if ( 0 != $val ) {
-                        $hsl[ $index ] = max( 0, min( 1, $hsl[ $index ] + $_val ) );
-                    }
-                }
-                $index++;
-            }
-
-            // Finally convert new HSL value to RGB
-            $rgb = csscrush_color::hslToRgb( $hsl );
-
-            // Return as hex if there is no modified alpha channel
-            // Otherwise return RGBA string
-            if ( 1 === $alpha ) {
-                return csscrush_color::rgbToHex( $rgb );
-            }
-            $rgb[] = $alpha;
-            return 'rgba(' . implode( ',', $rgb ) . ')';
-        }
-        else {
-            return $color;
-        }
+        // On failure to parse return input.
+        return $hsla->isValid ? $hsla->adjust( $adjustments )->__toString() : $raw_color;
     }
 }
 
diff --git a/lib/Mixin.php b/lib/Mixin.php
index f421d35..9148ed4 100644
--- a/lib/Mixin.php
+++ b/lib/Mixin.php
@@ -1,7 +1,7 @@
 tokens->{ $token[1] };
-        if ( isset( $path[ $token ] ) ) {
-            return $this->addToken( $path[ $token ], $token[1] );
-        }
-        // Return empty token if passed token is unset.
-        return $this->addToken( '', $token[1] );
-    }
-
-    public function updateToken ( $token, $new_value ) {
-        $path =& $this->tokens->{ $token[1] };
-        if ( isset( $path[ $token ] ) ) {
-            $path[ $token ] = $new_value;
-        }
-    }
-
     public function releaseToken ( $token ) {
         unset( $this->tokens->{ $token[1] }[ $token ] );
     }
diff --git a/lib/Regex.php b/lib/Regex.php
index a30e07c..9d6f03e 100644
--- a/lib/Regex.php
+++ b/lib/Regex.php
@@ -80,7 +80,7 @@ static public function init () {
         $patt->argListSplit  = '!\s*[,\s]\s*!S';
         $patt->mathBlacklist = '![^\.0-9\*\/\+\-\(\)]!S';
         $patt->charset       = '!@charset\s+(\?s\d+\?)\s*;!iS';
-        $patt->cruftyHex     = '!\#([0-9a-f])\1([0-9a-f])\2([0-9a-f])\3!iS';
+        $patt->cruftyHex     = '!\#([[:xdigit:]])\1([[:xdigit:]])\2([[:xdigit:]])\3!S';
     }
 
     static public function create ( $pattern_template, $flags = '', $delim = '!' ) {
diff --git a/lib/Rule.php b/lib/Rule.php
index b347c1f..527dc8d 100644
--- a/lib/Rule.php
+++ b/lib/Rule.php
@@ -4,7 +4,7 @@
  * CSS rule API.
  *
  */
-class csscrush_rule implements IteratorAggregate, Countable {
+class csscrush_rule implements IteratorAggregate {
 
     public $vendorContext;
     public $label;
@@ -97,21 +97,23 @@ public function __construct ( $selector_string = null, $declarations_string ) {
             if ( preg_match( $regex->mixinExtend, $declaration, $m ) ) {
 
                 $prop = isset( $m[2] ) ? 'extends' : 'mixin';
-                $value = substr( $declaration, strlen( $m[0] ) );
+                $value = trim( substr( $declaration, strlen( $m[0] ) ) );
             }
             elseif ( ( $colonPos = strpos( $declaration, ':' ) ) !== false ) {
 
                 $prop = trim( substr( $declaration, 0, $colonPos ) );
                 // Extract the value part of the declaration.
-                $value = substr( $declaration, $colonPos + 1 );
+                $value = trim( substr( $declaration, $colonPos + 1 ) );
             }
             else {
                 // Must be malformed.
                 continue;
             }
 
-            // Some cleanup.
-            $value = $value !== false ? trim( $value ) : $value;
+            // Reject empty values.
+            if ( empty( $prop ) || ( empty( $value ) && $value != '0' ) ) {
+                continue;
+            }
 
             if ( $prop === 'mixin' ) {
 
@@ -133,6 +135,8 @@ public function __construct ( $selector_string = null, $declarations_string ) {
             }
             else {
 
+                // Lowercase property values.
+                $prop = strtolower( $prop );
                 $this->declarationCheckin( $prop, $value, $pairs );
             }
         }
@@ -213,45 +217,154 @@ public function __toString () {
 
     public function declarationCheckin ( $prop, $value, &$pairs ) {
 
-        if ( $prop !== '' && $value !== '' ) {
+        // First resolve query() calls that reference earlier rules.
+        if ( preg_match( csscrush_regex::$patt->queryFunction, $value ) ) {
 
-            // First resolve query() calls that reference earlier rules
-            if ( preg_match( csscrush_regex::$patt->queryFunction, $value ) ) {
+            csscrush_function::executeCustomFunctions( $value,
+                csscrush_regex::$patt->queryFunction, array(
+                    'query' => array( $this, 'cssQueryFunction' ),
+                ), $prop );
+        }
 
-                csscrush_function::executeCustomFunctions( $value,
-                    csscrush_regex::$patt->queryFunction, array(
-                        'query' => array( $this, 'cssQueryFunction' ),
-                    ), $prop );
+        // Now all referencing is done convert *most* values to lowercase.
+        // Internet Explorer JavaScript expressions need case preserved.
+        if ( strpos( $prop, 'expression' ) !== false ) {
+            $value = strtolower( $value );
+        }
+
+        if ( strpos( $prop, 'data-' ) === 0 ) {
+
+            // If it's with data prefix, we don't want to print it
+            // Just remove the prefix
+            $prop = substr( $prop, strlen( 'data-' ) );
+
+            // On first pass we want to store data properties on $this->data,
+            // as well as on local
+            $this->data[ $prop ] = $value;
+        }
+        else {
+
+            // Add to the stack
+            $pairs[] = array( $prop, $value );
+        }
+
+        // Set on $this->localData
+        $this->localData[ $prop ] = $value;
+
+        // Unset on data tables if the value has a this() call:
+        //   - Restriction to avoid circular references
+        if ( preg_match( csscrush_regex::$patt->thisFunction, $value ) ) {
+
+            unset( $this->localData[ $prop ], $this->data[ $prop ] );
+        }
+    }
+
+    public function updatePropertyTable () {
+
+        // Create a new table of properties.
+        $new_properties_table = array();
+
+        foreach ( $this as $declaration ) {
+
+            $name = $declaration->property;
+
+            if ( isset( $new_properties_table[ $name ] ) ) {
+                $new_properties_table[ $name ]++;
             }
+            else {
+                $new_properties_table[ $name ] = 1;
+            }
+        }
+        $this->properties = $new_properties_table;
+    }
 
-            if ( strpos( $prop, 'data-' ) === 0 ) {
 
-                // If it's with data prefix, we don't want to print it
-                // Just remove the prefix
-                $prop = substr( $prop, strlen( 'data-' ) );
+    #############################
+    #  Inheritance.
+
+    public function setExtendSelectors ( $raw_value ) {
+
+        $abstracts =& csscrush::$process->abstracts;
+        $selectorRelationships =& csscrush::$process->selectorRelationships;
+
+        // Reset if called earlier, last call wins by intention.
+        $this->extendArgs = array();
+
+        foreach ( csscrush_util::splitDelimList( $raw_value ) as $arg ) {
+            $this->extendArgs[] = new csscrush_extendArg( $arg );
+        }
+    }
+
+    public function applyExtendables () {
+
+        if ( ! $this->extendArgs ) {
+            return;
+        }
+
+        $abstracts =& csscrush::$process->abstracts;
+        $selectorRelationships =& csscrush::$process->selectorRelationships;
+
+        // Filter the extendArgs list to usable references
+        foreach ( $this->extendArgs as $key => $extend_arg ) {
+
+            $name = $extend_arg->name;
+
+            if ( isset( $abstracts[ $name ] ) ) {
+
+                $parent_rule = $abstracts[ $name ];
+                $extend_arg->pointer = $parent_rule;
+
+            }
+            elseif ( isset( $selectorRelationships[ $name ] ) ) {
+
+                $parent_rule = $selectorRelationships[ $name ];
+                $extend_arg->pointer = $parent_rule;
 
-                // On first pass we want to store data properties on $this->data,
-                // as well as on local
-                $this->data[ $prop ] = $value;
             }
             else {
 
-                // Add to the stack
-                $pairs[] = array( $prop, $value );
+                // Unusable, so unset it
+                unset( $this->extendArgs[ $key ] );
             }
+        }
+
+        // Create a stack of all parent rule args
+        $parent_extend_args = array();
+        foreach ( $this->extendArgs as $extend_arg ) {
+            $parent_extend_args = array_merge( $parent_extend_args, $extend_arg->pointer->extendArgs );
+        }
 
-            // Set on $this->localData
-            $this->localData[ $prop ] = $value;
+        // Merge this rule's extendArgs with parent extendArgs
+        $this->extendArgs = array_merge( $this->extendArgs, $parent_extend_args );
 
-            // Unset on data tables if the value has a this() call:
-            //   - Restriction to avoid circular references
-            if ( preg_match( csscrush_regex::$patt->thisFunction, $value ) ) {
+        // Filter now?
 
-                unset( $this->localData[ $prop ], $this->data[ $prop ] );
+        // Add this rule's selectors to all extendArgs
+        foreach ( $this->extendArgs as $extend_arg ) {
+
+            $ancestor = $extend_arg->pointer;
+
+            $extend_selectors = $this->selectorList;
+
+            // If there is a pseudo class extension create a new set accordingly
+            if ( $extend_arg->pseudo ) {
+
+                $extend_selectors = array();
+                foreach ( $this->selectorList as $readable => $selector ) {
+                    $new_selector = clone $selector;
+                    $new_readable = $new_selector->appendPseudo( $extend_arg->pseudo );
+                    $extend_selectors[ $new_readable ] = $new_selector;
+                }
             }
+
+            $ancestor->addSelectors( $extend_selectors );
         }
     }
 
+
+    #############################
+    #  Referencing.
+
     public function cssThisFunction ( $input, $fn_name ) {
 
         $args = csscrush_function::parseArgsSimple( $input );
@@ -329,47 +442,110 @@ public function cssQueryFunction ( $input, $fn_name, $call_property ) {
         return $result;
     }
 
-    public function updatePropertyTable () {
 
-        // Create a new table of properties
-        $new_properties_table = array();
+    #############################
+    #  Selectors.
 
-        foreach ( $this as $declaration ) {
+    public function expandSelectors () {
 
-            $name = $declaration->property;
+        $new_set = array();
+        $reg_comma = '!\s*,\s*!';
 
-            if ( isset( $new_properties_table[ $name ] ) ) {
-                $new_properties_table[ $name ]++;
+        foreach ( $this->selectorList as $readableValue => $selector ) {
+
+            $pos = strpos( $selector->value, ':any?' );
+
+            if ( $pos !== false ) {
+
+                // Contains an :any statement so we expand
+                $chain = array( '' );
+                do {
+                    if ( $pos === 0 ) {
+                        preg_match( '!:any(\?p\d+\?)!', $selector->value, $m );
+
+                        // Parse the arguments
+                        $expression = trim( csscrush::$process->tokens->p[ $m[1] ], '()' );
+                        $parts = preg_split( $reg_comma, $expression, null, PREG_SPLIT_NO_EMPTY );
+
+                        $tmp = array();
+                        foreach ( $chain as $rowCopy ) {
+                            foreach ( $parts as $part ) {
+                                $tmp[] = $rowCopy . $part;
+                            }
+                        }
+                        $chain = $tmp;
+                        $selector->value = substr( $selector->value, strlen( $m[0] ) );
+                    }
+                    else {
+                        foreach ( $chain as &$row ) {
+                            $row .= substr( $selector->value, 0, $pos );
+                        }
+                        $selector->value = substr( $selector->value, $pos );
+                    }
+                } while ( ( $pos = strpos( $selector->value, ':any?' ) ) !== false );
+
+                // Finish off
+                foreach ( $chain as &$row ) {
+
+                    // Not creating a named rule association with this expanded selector
+                    $new_set[] = new csscrush_selector( $row . $selector->value );
+                }
+
+                // Store the unexpanded selector to selectorRelationships
+                csscrush::$process->selectorRelationships[ $readableValue ] = $this;
             }
             else {
-                $new_properties_table[ $name ] = 1;
+
+                // Nothing to expand
+                $new_set[ $readableValue ] = $selector;
             }
+
+        } // foreach
+
+        $this->selectorList = $new_set;
+    }
+
+    public function indexSelectors () {
+
+        foreach ( $this->selectorList as $selector ) {
+            csscrush::$process->selectorRelationships[ $selector->readableValue ] = $this;
         }
+    }
 
-        $this->properties = $new_properties_table;
+    public function addSelector ( $selector ) {
+
+        $this->selectorList[ $selector->readableValue ] = $selector;
+    }
+
+    public function addSelectors ( $list ) {
+
+        $this->selectorList = array_merge( $this->selectorList, $list );
     }
 
+
+    #############################
+    #  Aliases
+
     public function addPropertyAliases () {
 
-        $regex = csscrush_regex::$patt;
-        $aliasedProperties =& csscrush::$process->aliases[ 'properties' ];
+        $aliased_properties =& csscrush::$process->aliases[ 'properties' ];
 
-        // First test for the existence of any aliased properties
-        $intersect = array_intersect( array_keys( $aliasedProperties ), array_keys( $this->properties ) );
-        if ( empty( $intersect ) ) {
+        // First test for the existence of any aliased properties.
+        if ( ! array_intersect_key( $aliased_properties, $this->properties ) ) {
             return;
         }
 
-        // Shim in aliased properties
+        $regex = csscrush_regex::$patt;
         $new_set = array();
+
+        // Shim in aliased properties
         foreach ( $this->declarations as $declaration ) {
+
             $prop = $declaration->property;
-            if (
-                ! $declaration->skip &&
-                isset( $aliasedProperties[ $prop ] )
-            ) {
+            if ( ! $declaration->skip && isset( $aliased_properties[ $prop ] ) ) {
+
                 // There are aliases for the current property
-                foreach ( $aliasedProperties[ $prop ] as $prop_alias ) {
+                foreach ( $aliased_properties[ $prop ] as $prop_alias ) {
 
                     // If an aliased version already exists to not create one.
                     if ( $this->propertyCount( $prop_alias ) ) {
@@ -398,37 +574,32 @@ public function addPropertyAliases () {
     public function addFunctionAliases () {
 
         $function_aliases =& csscrush::$process->aliases[ 'functions' ];
-        $aliased_func_names = array_keys( $function_aliases );
 
-        if ( empty( $aliased_func_names ) ) {
+        if ( ! $function_aliases ) {
             return;
         }
 
         // The new modified set of declarations.
         $new_set = array();
 
-        // Keep track of the function aliases we apply and to which property
-        // they belong, so we can avoid un-unecessary duplications.
-        $used_fn_aliases = array();
-
         // Shim in aliased functions.
         foreach ( $this->declarations as $declaration ) {
 
             // No functions, bail.
-            if ( $declaration->skip || empty( $declaration->functions ) ) {
+            if ( $declaration->skip || ! $declaration->functions ) {
                 $new_set[] = $declaration;
                 continue;
             }
 
-            // Get list of functions used in declaration that are alias-able, if none bail.
-            $intersect = array_intersect( $declaration->functions, $aliased_func_names );
-            if ( empty( $intersect ) ) {
+            // Get list of functions used in declaration that are alias-able, bail if none.
+            $intersect = array_intersect_key( $declaration->functions, $function_aliases );
+            if ( ! $intersect ) {
                 $new_set[] = $declaration;
                 continue;
             }
 
             // Loop the aliasable functions.
-            foreach ( $intersect as $fn_name ) {
+            foreach ( array_keys( $intersect ) as $fn_name ) {
 
                 // For each aliased function dupe the declaration.
                 // This pretty much limits the aliasing to one function per declaration.
@@ -438,7 +609,7 @@ public function addFunctionAliases () {
                     // If the declaration is vendor specific only create aliases for the same vendor.
                     if ( $declaration->vendor ) {
                         preg_match( csscrush_regex::$patt->vendorPrefix, $fn_alias, $m );
-                        if ( strtolower( $m[1] ) !== $declaration->vendor ) {
+                        if ( $m[1] !== $declaration->vendor ) {
                             continue;
                         }
                     }
@@ -470,19 +641,17 @@ public function addFunctionAliases () {
 
     public function addValueAliases () {
 
-        $aliasedValues =& csscrush::$process->aliases[ 'values' ];
+        $aliased_values =& csscrush::$process->aliases[ 'values' ];
 
-        // First test for the existence of any aliased properties
-        $intersect = array_intersect( array_keys( $aliasedValues ), array_keys( $this->properties ) );
-
-        if ( empty( $intersect ) ) {
+        // First test for the existence of any aliased properties.
+        if ( ! array_intersect_key( $aliased_values, $this->properties ) ) {
             return;
         }
 
         $new_set = array();
         foreach ( $this->declarations as $declaration ) {
-            if ( !$declaration->skip ) {
-                foreach ( $aliasedValues as $value_prop => $value_aliases ) {
+            if ( ! $declaration->skip ) {
+                foreach ( $aliased_values as $value_prop => $value_aliases ) {
                     if ( $this->propertyCount( $value_prop ) < 1 ) {
                         continue;
                     }
@@ -499,192 +668,26 @@ public function addValueAliases () {
             }
             $new_set[] = $declaration;
         }
-        // Re-assign
-        $this->declarations = $new_set;
-    }
-
-    public function expandSelectors () {
-
-        $new_set = array();
-        $reg_comma = '!\s*,\s*!';
-
-        foreach ( $this->selectorList as $readableValue => $selector ) {
-
-            $pos = strpos( $selector->value, ':any?' );
-
-            if ( $pos !== false ) {
-
-                // Contains an :any statement so we expand
-                $chain = array( '' );
-                do {
-                    if ( $pos === 0 ) {
-                        preg_match( '!:any(\?p\d+\?)!', $selector->value, $m );
-
-                        // Parse the arguments
-                        $expression = trim( csscrush::$process->tokens->p[ $m[1] ], '()' );
-                        $parts = preg_split( $reg_comma, $expression, null, PREG_SPLIT_NO_EMPTY );
-
-                        $tmp = array();
-                        foreach ( $chain as $rowCopy ) {
-                            foreach ( $parts as $part ) {
-                                $tmp[] = $rowCopy . $part;
-                            }
-                        }
-                        $chain = $tmp;
-                        $selector->value = substr( $selector->value, strlen( $m[0] ) );
-                    }
-                    else {
-                        foreach ( $chain as &$row ) {
-                            $row .= substr( $selector->value, 0, $pos );
-                        }
-                        $selector->value = substr( $selector->value, $pos );
-                    }
-                } while ( ( $pos = strpos( $selector->value, ':any?' ) ) !== false );
-
-                // Finish off
-                foreach ( $chain as &$row ) {
-
-                    // Not creating a named rule association with this expanded selector
-                    $new_set[] = new csscrush_selector( $row . $selector->value );
-                }
-
-                // Store the unexpanded selector to selectorRelationships
-                csscrush::$process->selectorRelationships[ $readableValue ] = $this;
-            }
-            else {
-
-                // Nothing to expand
-                $new_set[ $readableValue ] = $selector;
-            }
-
-        } // foreach
-
-        $this->selectorList = $new_set;
-    }
-
-    public function indexSelectors () {
-
-        foreach ( $this->selectorList as $selector ) {
-            csscrush::$process->selectorRelationships[ $selector->readableValue ] = $this;
-        }
-    }
-
-    public function setExtendSelectors ( $raw_value ) {
 
-        $abstracts =& csscrush::$process->abstracts;
-        $selectorRelationships =& csscrush::$process->selectorRelationships;
-
-        // Reset if called earlier, last call wins by intention.
-        $this->extendArgs = array();
-
-        foreach ( csscrush_util::splitDelimList( $raw_value ) as $arg ) {
-            $this->extendArgs[] = new csscrush_extendArg( $arg );
-        }
-    }
-
-    public function applyExtendables () {
-
-        if ( ! $this->extendArgs ) {
-            return;
-        }
-
-        $abstracts =& csscrush::$process->abstracts;
-        $selectorRelationships =& csscrush::$process->selectorRelationships;
-
-        // Filter the extendArgs list to usable references
-        foreach ( $this->extendArgs as $key => $extend_arg ) {
-
-            $name = $extend_arg->name;
-
-            if ( isset( $abstracts[ $name ] ) ) {
-
-                $parent_rule = $abstracts[ $name ];
-                $extend_arg->pointer = $parent_rule;
-
-            }
-            elseif ( isset( $selectorRelationships[ $name ] ) ) {
-
-                $parent_rule = $selectorRelationships[ $name ];
-                $extend_arg->pointer = $parent_rule;
-
-            }
-            else {
-
-                // Unusable, so unset it
-                unset( $this->extendArgs[ $key ] );
-            }
-        }
-
-        // Create a stack of all parent rule args
-        $parent_extend_args = array();
-        foreach ( $this->extendArgs as $extend_arg ) {
-            $parent_extend_args = array_merge( $parent_extend_args, $extend_arg->pointer->extendArgs );
-        }
-
-        // Merge this rule's extendArgs with parent extendArgs
-        $this->extendArgs = array_merge( $this->extendArgs, $parent_extend_args );
-
-        // Filter now?
-
-        // Add this rule's selectors to all extendArgs
-        foreach ( $this->extendArgs as $extend_arg ) {
-
-            $ancestor = $extend_arg->pointer;
-
-            $extend_selectors = $this->selectorList;
-
-            // If there is a pseudo class extension create a new set accordingly
-            if ( $extend_arg->pseudo ) {
-
-                $extend_selectors = array();
-                foreach ( $this->selectorList as $readable => $selector ) {
-                    $new_selector = clone $selector;
-                    $new_readable = $new_selector->appendPseudo( $extend_arg->pseudo );
-                    $extend_selectors[ $new_readable ] = $new_selector;
-                }
-            }
-
-            $ancestor->addSelectors( $extend_selectors );
-        }
-    }
-
-    public function addSelector ( $selector ) {
-
-        $this->selectorList[ $selector->readableValue ] = $selector;
-    }
-
-    public function addSelectors ( $list ) {
-
-        $this->selectorList = array_merge( $this->selectorList, $list );
+        // Re-assign.
+        $this->declarations = $new_set;
     }
 
 
-    ############
-    #  IteratorAggregate
+    #############################
+    #  IteratorAggregate interface.
 
     public function getIterator () {
         return new ArrayIterator( $this->declarations );
     }
 
 
-    ############
-    #  Countable
-
-    public function count() {
-
-        return count( $this->_declarations );
-    }
-
-
-    ############
-    #  Rule API
+    #############################
+    #  Rule API.
 
     public function propertyCount ( $prop ) {
 
-        if ( isset( $this->properties[ $prop ] ) ) {
-            return $this->properties[ $prop ];
-        }
-        return 0;
+        return isset( $this->properties[ $prop ] ) ? $this->properties[ $prop ] : 0;
     }
 
     public function addProperty ( $prop ) {
@@ -835,17 +838,17 @@ public function __construct ( $prop, $value, $contextIndex = 0 ) {
 
         $regex = csscrush_regex::$patt;
 
-        // Normalize input. Lowercase the property name
+        // Normalize input. Lowercase the property name.
         $prop = strtolower( trim( $prop ) );
         $value = trim( $value );
 
-        // Check the input
+        // Check the input.
         if ( $prop === '' || $value === '' || $value === null ) {
             $this->isValid = false;
             return;
         }
 
-        // Test for escape tilde
+        // Test for escape tilde.
         if ( $skip = strpos( $prop, '~' ) === 0 ) {
             $prop = substr( $prop, 1 );
         }
@@ -861,19 +864,19 @@ public function __construct ( $prop, $value, $contextIndex = 0 ) {
             $canonical_property = $prop;
         }
 
-        // Check for !important keywords
-        if ( ( $important = strpos( $value, '!important' ) ) !== false ) {
+        // Check for !important.
+        if ( ( $important = stripos( $value, '!important' ) ) !== false ) {
             $value = rtrim( substr( $value, 0, $important ) );
             $important = true;
         }
 
-        // Ignore declarations with null css values
+        // Ignore declarations with null css values.
         if ( $value === false || $value === '' ) {
             $this->isValid = false;
             return;
         }
 
-        // Apply custom functions
+        // Apply custom functions.
         if ( ! $skip ) {
             csscrush_function::executeCustomFunctions( $value );
         }
@@ -882,15 +885,11 @@ public function __construct ( $prop, $value, $contextIndex = 0 ) {
         csscrush::$process->captureParens( $value );
 
         // Create an index of all regular functions in the value.
-        if ( preg_match_all( $regex->function, $value, $functions ) > 0 ) {
-            $out = array();
-            foreach ( $functions[2] as $index => $fn_name ) {
-                $out[ strtolower( $fn_name ) ] = true;
+        $functions = array();
+        if ( preg_match_all( $regex->function, $value, $m ) ) {
+            foreach ( $m[2] as $index => $fn_name ) {
+                $functions[ strtolower( $fn_name ) ] = true;
             }
-            $functions = array_keys( $out );
-        }
-        else {
-            $functions = array();
         }
 
         $this->property          = $prop;
diff --git a/plugins/hsl-to-hex.php b/plugins/hsl-to-hex.php
index 8d1a5b4..f263785 100644
--- a/plugins/hsl-to-hex.php
+++ b/plugins/hsl-to-hex.php
@@ -26,18 +26,13 @@ function csscrush__disable_hsl_to_hex () {
 function csscrush__hsl_to_hex ( csscrush_rule $rule ) {
 
     foreach ( $rule as &$declaration ) {
-        if ( 
-            ! $declaration->skip &&
-            ( ! empty( $declaration->functions ) && in_array( 'hsl', $declaration->functions ) )
-        ) {
+
+        if ( ! $declaration->skip && isset( $declaration->functions[ 'hsl' ] ) ) {
             while ( preg_match( '!hsl(\?p\d+\?)!', $declaration->value, $m ) ) {
-                $full_match = $m[0];
                 $token = $m[1];
-                $hsl = trim( csscrush::$process->tokens->p[ $token ], '()' );
-                $hsl = array_map( 'trim', explode( ',', $hsl ) );
-                $rgb = csscrush_color::cssHslToRgb( $hsl );
-                $hex = csscrush_color::rgbToHex( $rgb );
-                $declaration->value = str_replace( $full_match, $hex, $declaration->value );
+                $color = new csscrush_color( 'hsl' . csscrush::$process->fetchToken( $token ) );
+                csscrush::$process->releaseToken( $token );
+                $declaration->value = str_replace( $m[0], $color->getHex(), $declaration->value );
             }
         }
     }

From 3fd598c6992440114b82038893c0f555508d5790 Mon Sep 17 00:00:00 2001
From: Pete Boere 
Date: Sat, 29 Dec 2012 19:30:55 +0000
Subject: [PATCH 058/421] Implemented autoloader for loading all classes
 (moving towards PSR-0 compliance). Moved all classes into seperate files with
 no 'side-effects' for PSR compliance. Updated casing of all classname
 references for the autoloader; all classnaming needs to be StudlyCaps now.
 Added options class (with getters and setters) to abstract out some of the
 fiddly options. Added new options to the command line utility and a little
 refactoring. Added flexbox aliases for to cover all common implementations.
 Added formatters option and some default formatters. Added newlines option to
 provide consistent output when required. Selector aliases now accept
 arguments. `disable` and `enable` options have been flipped in order of
 priority so you can easily disable all then enable just what you need.

---
 Aliases.ini                 | 164 ++++++---
 CHANGELOG.txt               |  18 +-
 CssCrush.php                |  41 ++-
 cli.php                     | 144 ++++----
 lib/ArgList.php             |  96 ++++++
 lib/BalancedMatch.php       |  68 ++++
 lib/Color.php               |  86 ++---
 lib/Core.php                | 192 ++++++++---
 lib/Declaration.php         | 104 ++++++
 lib/ExtendArg.php           |  32 ++
 lib/Fragment.php            |  36 ++
 lib/Function.php            |  97 +++---
 lib/Hook.php                |  15 +-
 lib/IO.php                  | 116 +++----
 lib/Importer.php            |  85 +++--
 lib/Mixin.php               | 180 ++--------
 lib/Options.php             |  91 +++++
 lib/Plugin.php              |  26 +-
 lib/PostAliasFix.php        | 116 +++++++
 lib/Process.php             | 484 ++++++++++++++------------
 lib/Regex.php               |  38 +--
 lib/Rule.php                | 656 ++++++++++--------------------------
 lib/Selector.php            |  64 ++++
 lib/Stream.php              | 120 +++++++
 lib/Url.php                 | 129 +++++++
 lib/Util.php                | 422 ++---------------------
 lib/Version.php             |  71 ++++
 plugins/hocus-pocus.php     |  10 +-
 plugins/hsl-to-hex.php      |  12 +-
 plugins/ie-clip.php         |  14 +-
 plugins/ie-filter.php       |  16 +-
 plugins/ie-inline-block.php |  31 +-
 plugins/ie-min-height.php   |  12 +-
 plugins/ie-opacity.php      |  16 +-
 plugins/initial.php         |  22 +-
 plugins/legacy-flexbox.php  | 376 +++++++++++++++++++++
 plugins/property-sorter.php |  12 +-
 plugins/rgba-fallback.php   |  12 +-
 plugins/spiffing.php        |   6 +-
 plugins/svg-gradients.php   |  18 +-
 40 files changed, 2512 insertions(+), 1736 deletions(-)
 create mode 100644 lib/ArgList.php
 create mode 100644 lib/BalancedMatch.php
 create mode 100644 lib/Declaration.php
 create mode 100644 lib/ExtendArg.php
 create mode 100644 lib/Fragment.php
 create mode 100644 lib/Options.php
 create mode 100644 lib/PostAliasFix.php
 create mode 100644 lib/Selector.php
 create mode 100644 lib/Stream.php
 create mode 100644 lib/Url.php
 create mode 100644 lib/Version.php
 create mode 100644 plugins/legacy-flexbox.php

diff --git a/Aliases.ini b/Aliases.ini
index 33d304e..38efa37 100644
--- a/Aliases.ini
+++ b/Aliases.ini
@@ -9,11 +9,11 @@
 ;
 ;----------------------------------------------------------------
 
-; Property aliases
+; Property aliases.
 
 [properties]
 
-    ; Animations
+    ; Animations.
     animation[] = -webkit-animation
     animation[] = -moz-animation
     animation[] = -o-animation
@@ -42,60 +42,43 @@
     animation-timing-function[] = -moz-animation-timing-function
     animation-timing-function[] = -o-animation-timing-function
 
-    ; Backface visibility
+    ; Backface visibility.
     backface-visibility[] = -webkit-backface-visibility
     backface-visibility[] = -moz-backface-visibility
     backface-visibility[] = -ms-backface-visibility
 
-    ; Background clip
+    ; Background clip.
     background-clip[] = -webkit-background-clip
     background-clip[] = -moz-background-clip
 
-    ; Background origin
+    ; Background origin.
     background-origin[] = -webkit-background-origin
     background-origin[] = -moz-background-origin
 
-    ; Background size
+    ; Background size.
     background-size[] = -webkit-background-size
     background-size[] = -moz-background-size
 
-    ; Border radius
+    ; Border radius.
     border-radius[] = -webkit-border-radius
     border-top-left-radius[] = -webkit-border-top-left-radius
     border-top-right-radius[] = -webkit-border-top-right-radius
     border-bottom-left-radius[] = -webkit-border-bottom-left-radius
     border-bottom-right-radius[] = -webkit-border-bottom-right-radius
 
-    ; Border-image
+    ; Border-image.
     border-image[] = -webkit-border-image
     border-image[] = -moz-border-image
     border-image[] = -o-border-image
 
-    ; Flexbox (old, but supported implementation)
-    box-align[] = -webkit-box-align
-    box-align[] = -moz-box-align
-    box-align[] = -ms-box-align
-    box-direction[] = -webkit-box-direction
-    box-direction[] = -moz-box-direction
-    box-direction[] = -ms-box-direction
-    box-flex[] = -webkit-box-flex
-    box-flex[] = -moz-box-flex
-    box-flex[] = -ms-box-flex
-    box-orient[] = -webkit-box-orient
-    box-orient[] = -moz-box-orient
-    box-orient[] = -ms-box-orient
-    box-pack[] = -webkit-box-pack
-    box-pack[] = -moz-box-pack
-    box-pack[] = -ms-box-pack
-
-    ; Box shadow
+    ; Box shadow.
     box-shadow[] = -webkit-box-shadow
 
-    ; Box sizing
+    ; Box sizing.
     box-sizing[] = -webkit-box-sizing
     box-sizing[] = -moz-box-sizing
 
-    ; Columns
+    ; Columns.
     columns[] = -webkit-columns
     columns[] = -moz-columns
     column-count[] = -webkit-column-count
@@ -119,19 +102,69 @@
     column-width[] = -webkit-column-width
     column-width[] = -moz-column-width
 
-    ; Hyphens
+    ; Flexbox (2012).
+    ;
+    ; Merges two similar versions of the flexbox spec:
+    ;  - September 2012 (for non IE): http://www.w3.org/TR/2012/CR-css3-flexbox-20120918
+    ;  - March 2012 (for IE10): http://www.w3.org/TR/2012/WD-css3-flexbox-20120322
+    ;
+    ; The early 2012 spec mostly differs only in syntax to the later one, with the notable
+    ; exception of not supporting seperate properties for , 
+    ; and . These properties are available in both 2012 implementations via
+    ;  shorthand.
+    ;
+    ; Support for the early 2012 syntax implemented in IE10 is achieved here in part with
+    ; property aliases, and in part with property/value aliases later in this file.
+    ;
+    align-content[] = -webkit-align-content
+    align-items[] = -webkit-align-items
+    align-self[] = -webkit-align-self
+    flex[] = -webkit-flex
+    flex[] = -ms-flex
+    flex-basis[] = -webkit-flex-basis
+    flex-direction[] = -webkit-flex-direction
+    flex-direction[] = -ms-flex-direction
+    flex-flow[] = -webkit-flex-flow
+    flex-flow[] = -ms-flex-flow
+    flex-grow[] = -webkit-flex-grow
+    flex-shrink[] = -webkit-flex-shrink
+    flex-wrap[] = -webkit-flex-wrap
+    flex-wrap[] = -ms-flex-wrap
+    justify-content[] = -webkit-justify-content
+    order[] = -webkit-order
+    order[] = -ms-flex-order
+
+    ; Flexbox (2009).
+    box-align[] = -webkit-box-align
+    box-align[] = -moz-box-align
+    box-direction[] = -webkit-box-direction
+    box-direction[] = -moz-box-direction
+    box-flex[] = -webkit-box-flex
+    box-flex[] = -moz-box-flex
+    box-flex-group[] = -webkit-box-flex-group
+    box-flex-group[] = -moz-box-flex-group
+    box-lines[] = -webkit-box-lines
+    box-lines[] = -moz-box-lines
+    box-ordinal-group[] = -webkit-box-ordinal-group
+    box-ordinal-group[] = -moz-box-ordinal-group
+    box-orient[] = -webkit-box-orient
+    box-orient[] = -moz-box-orient
+    box-pack[] = -webkit-box-pack
+    box-pack[] = -moz-box-pack
+
+    ; Hyphens.
     hyphens[] = -webkit-hyphens
     hyphens[] = -moz-hyphens
     hyphens[] = -ms-hyphens
 
-    ; Outline radius
+    ; Outline radius.
     outline-radius[] = -moz-outline-radius
     outline-top-left-radius[] = -moz-outline-radius-topleft
     outline-top-right-radius[] = -moz-outline-radius-topright
     outline-bottom-left-radius[] = -moz-outline-radius-bottomleft
     outline-bottom-right-radius[] = -moz-outline-radius-bottomright
 
-    ; Perspective
+    ; Perspective.
     perspective[] = -webkit-perspective
     perspective[] = -moz-perspective
     perspective[] = -ms-perspective
@@ -139,23 +172,23 @@
     perspective-origin[] = -moz-perspective-origin
     perspective-origin[] = -ms-perspective-origin
 
-    ; Tab size
+    ; Tab size.
     tab-size[] = -moz-tab-size
     tab-size[] = -o-tab-size
 
-    ; Text align last
+    ; Text align last.
     text-align-last[] = -webkit-text-align-last
     text-align-last[] = -moz-text-align-last
 
-    ; Text decoration
+    ; Text decoration.
     text-decoration-color[] = -moz-text-decoration-color
     text-decoration-line[] = -moz-text-decoration-line
     text-decoration-style[] = -moz-text-decoration-style
 
-    ; Text overflow (Opera mini support)
+    ; Text overflow (Opera mini support).
     text-overflow[] = -o-text-overflow
 
-    ; Transforms
+    ; Transforms.
     transform[] = -webkit-transform
     transform[] = -moz-transform
     transform[] = -ms-transform
@@ -168,7 +201,7 @@
     transform-style[] = -moz-transform-style
     transform-style[] = -ms-transform-style
 
-    ; Transitions
+    ; Transitions.
     transition[] = -webkit-transition
     transition[] = -moz-transition
     transition[] = -o-transition
@@ -185,7 +218,7 @@
     transition-timing-function[] = -moz-transition-timing-function
     transition-timing-function[] = -o-transition-timing-function
 
-    ; User select (non standard)
+    ; User select (non standard).
     user-select[] = -webkit-user-select
     user-select[] = -moz-user-select
     user-select[] = -ms-user-select
@@ -194,26 +227,61 @@
 
 
 ;----------------------------------------------------------------
-; Property:value aliases
+; Property:value aliases.
 
 [values]
 
-    ; Flexbox TBC.
+    ; Flexbox (2012).
+    display:flex[] = display:-webkit-flex
+    display:flex[] = display:-ms-flexbox
+    display:inline-flex[] = display:-webkit-inline-flex
+    display:inline-flex[] = display:-ms-inline-flexbox
+
+    ; Flexbox (early 2012).
+    align-content:flex-start[] = -ms-flex-line-pack:start
+    align-content:flex-end[] = -ms-flex-line-pack:end
+    align-content:center[] = -ms-flex-line-pack:center
+    align-content:space-between[] = -ms-flex-line-pack:justify
+    align-content:space-around[] = -ms-flex-line-pack:distribute
+    align-content:stretch[] = -ms-flex-line-pack:stretch
+
+    align-items:flex-start[] = -ms-flex-align:start
+    align-items:flex-end[] = -ms-flex-align:end
+    align-items:center[] = -ms-flex-align:center
+    align-items:baseline[] = -ms-flex-align:baseline
+    align-items:stretch[] = -ms-flex-align:stretch
+
+    align-self:auto[] = -ms-flex-item-align:auto
+    align-self:flex-start[] = -ms-flex-item-align:start
+    align-self:flex-end[] = -ms-flex-item-align:end
+    align-self:center[] = -ms-flex-item-align:center
+    align-self:baseline[] = -ms-flex-item-align:baseline
+    align-self:stretch[] = -ms-flex-item-align:stretch
+
+    justify-content:flex-start[] = -ms-flex-pack:start
+    justify-content:flex-end[] = -ms-flex-pack:end
+    justify-content:center[] = -ms-flex-pack:center
+    justify-content:space-between[] = -ms-flex-pack:justify
+    justify-content:space-around[] = -ms-flex-pack:distribute
+
+    ; Flexbox (2009).
+    display:box[] = display:-webkit-box
+    display:box[] = display:-moz-box
 
 
 ;----------------------------------------------------------------
-; Function aliases
+; Function aliases.
 
 [functions]
 
-    ; Calc
+    ; Calc.
     calc[] = -webkit-calc
     calc[] = -moz-calc
 
-    ; Element
+    ; Element.
     element[] = -moz-element
 
-    ; Gradients
+    ; Gradients.
     linear-gradient[] = -webkit-linear-gradient
     linear-gradient[] = -moz-linear-gradient
     linear-gradient[] = -ms-linear-gradient
@@ -223,7 +291,7 @@
     radial-gradient[] = -ms-radial-gradient
     radial-gradient[] = -o-radial-gradient
 
-    ; Repeating gradients
+    ; Repeating gradients.
     repeating-linear-gradient[] = -webkit-repeating-linear-gradient
     repeating-linear-gradient[] = -moz-repeating-linear-gradient
     repeating-linear-gradient[] = -ms-repeating-linear-gradient
@@ -235,16 +303,16 @@
 
 
 ;----------------------------------------------------------------
-; @rule aliases
+; @rule aliases.
 
 [at-rules]
 
-    ; Keyframes
+    ; Keyframes.
     keyframes[] = -webkit-keyframes
     keyframes[] = -moz-keyframes
     keyframes[] = -o-keyframes
 
-    ; Viewport
+    ; Viewport.
     viewport[] = -webkit-viewport
     viewport[] = -moz-viewport
     viewport[] = -ms-viewport
diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index e33730e..ac811ff 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -1,9 +1,17 @@
 1.9
 ---
-Added functions API for defining custom functions inside plugins.
+Added flexbox aliases for both 2009 and 2012 edition specs.
+Added a legacy-flexbox plugin for auto-generating the flexbox 2009 spec equivilant properties.
+Updated selector aliases to take arguments at runtime.
 Updated plugin API to use distinct 'enable' and 'disable' handlers.
+The disable option is now resolved before the enable option so you can easily disable all plugins
+and then specify the plugins you want to apply.
+Added functions API for defining custom functions inside plugins.
 Improved gradient function aliasing to handle new angle keywords ('to left', 'at center' etc.).
 Added svg-gradients plugin for simulating CSS3 gradients with data-uris.
+Added formatting option so custom formatters can be defined for un-minified output (see wiki for options).
+Added newlines option to force the style of newlines in output (see wiki for options).
+Updated command line utility to employ the new options.
 
 
 1.8
@@ -16,7 +24,7 @@ Debug option renamed to 'minify'; debug option will still work as before but is
 New minify option optionally takes an array of advanced minification parameters.
 Expanded trace option to take an optional array of log parameters;
 log params available are stubs, selector_count, errors and compile_time.
-Added csscrush::stat method to retrieve logged parameters.
+Added CssCrush::stat method to retrieve logged parameters.
 Improved cross OS support.
 Improved minification.
 Major refactoring.
@@ -31,7 +39,7 @@ Added options for enabling and disabling plugins at runtime.
 Added property sorter plugin.
 Added support for SASS-like @include/@extend syntax for invoking mixins and extends.
 Boilerplate option now accepts a filename string as a boilerplate template.
-csscrush::string method now uses document_root as a default context for finding linked resources.
+CssCrush::string method now uses document_root as a default context for finding linked resources.
 Updated command line appication.
 Updated aliases and initial value files.
 Fixed parsing issue introduced in 1.6.1.
@@ -65,7 +73,7 @@ Fixed some test cases.
 1.5.2
 -----
 Resolved issue #32.
-csscrush::inline method now defaults to not printing a boilerplate.
+CssCrush::inline method now defaults to not printing a boilerplate.
 Updated aliases file.
 
 
@@ -108,7 +116,7 @@ Added Prepend.css - Optionally prepend css to every input.
 Fix for issue #21.
 Reorganized aliases file with some additions.
 Initial-values updated.
-Updated csscrush::string method to correctly handle import statements.
+Updated CssCrush::string method to correctly handle import statements.
 
 
 1.4
diff --git a/CssCrush.php b/CssCrush.php
index 2af10fc..21a7377 100644
--- a/CssCrush.php
+++ b/CssCrush.php
@@ -2,25 +2,36 @@
 /**
  *
  * CSS Crush
- * Extensible CSS preprocessor
+ * Extensible CSS preprocessor.
  *
  * @version    1.9
  * @link       https://github.com/peteboere/css-crush
  * @license    http://www.opensource.org/licenses/mit-license.php (MIT)
- * @copyright  (c) 2010-2012 Pete Boere
+ * @copyright  (c) 2010-2013 Pete Boere
  */
 
-require_once 'lib/Util.php';
-require_once 'lib/IO.php';
-require_once 'lib/Core.php';
-require_once 'lib/Process.php';
-require_once 'lib/Rule.php';
-require_once 'lib/Mixin.php';
-require_once 'lib/Function.php';
-require_once 'lib/Importer.php';
-require_once 'lib/Color.php';
-require_once 'lib/Regex.php';
-require_once 'lib/Hook.php';
-require_once 'lib/Plugin.php';
+function csscrush_autoload ( $class ) {
 
-csscrush::init( __FILE__ );
+    // Only autoload classes with the library prefix.
+    if ( stripos( $class, 'csscrush' ) !== 0 ) {
+        return;
+    }
+    $class = ltrim( substr( $class, 8 ), '_' );
+
+    if ( empty( $class ) ) {
+        $subpath = 'Core';
+    }
+    // Tolerate some cases of lowercasing from external use.
+    elseif ( strpos( $class, '_' ) !== false ) {
+        $subpath = implode( '/', array_map( 'ucfirst', explode( '_', $class ) ) );
+    }
+    else {
+        $subpath = ucfirst( $class );
+    }
+
+    require dirname( __FILE__ ) . "/lib/$subpath.php";
+}
+
+spl_autoload_register( 'csscrush_autoload' );
+
+CssCrush::init( __FILE__ );
diff --git a/cli.php b/cli.php
index 8fefae5..4efdcbe 100755
--- a/cli.php
+++ b/cli.php
@@ -2,23 +2,18 @@
 version );
+    stdout( 'CSS Crush ' . CssCrush::$config->version );
     exit( STATUS_OK );
 }
 
@@ -179,7 +190,7 @@ function stdout ( $lines, $closing_newline = true ) {
 
 
 ##################################################################
-##  Input
+##  Input.
 
 $input = null;
 
@@ -197,21 +208,27 @@ function stdout ( $lines, $closing_newline = true ) {
 }
 else {
 
-    // No input, just output help screen
+    // No input, just output help screen.
     stdout( $help );
     exit( STATUS_OK );
 }
 
 
 ##################################################################
-##  Processing
+##  Processing.
 
 $process_opts = array();
 $process_opts[ 'boilerplate' ] = $boilerplate ? true : false;
 $process_opts[ 'minify' ] = $pretty ? false : true;
+$process_opts[ 'formatter' ] = $formatter ?: null;
 $process_opts[ 'rewrite_import_urls' ] = true;
 
-// Enable plugin args
+// Newlines.
+if ( isset( $newlines ) ) {
+    $process_opts[ 'newlines' ] = $newlines;
+}
+
+// Enable plugin args.
 if ( $enable_plugins ) {
     foreach ( $enable_plugins as $arg ) {
         foreach ( preg_split( '!\s*,\s*!', $arg ) as $plugin ) {
@@ -255,11 +272,12 @@ function stdout ( $lines, $closing_newline = true ) {
     $process_opts[ 'doc_root' ] = $context;
     $process_opts[ 'context' ] = $context;
 }
-$output = csscrush::string( $input, $process_opts );
+
+$output = CssCrush::string( $input, $process_opts );
 
 
 ##################################################################
-##  Output
+##  Output.
 
 if ( $output_file ) {
 
@@ -277,8 +295,8 @@ function stdout ( $lines, $closing_newline = true ) {
 }
 else {
 
-    if ( csscrush::$process->errors ) {
-        stderr( csscrush::$process->errors );
+    if ( CssCrush::$process->errors ) {
+        stderr( CssCrush::$process->errors );
     }
 
     stdout( $output );
diff --git a/lib/ArgList.php b/lib/ArgList.php
new file mode 100644
index 0000000..6cf362d
--- /dev/null
+++ b/lib/ArgList.php
@@ -0,0 +1,96 @@
+argFunction, array(
+                    'arg' => array( $this, 'store' )
+                ));
+        $this->string = $str;
+    }
+
+    public function store ( $raw_argument )
+    {
+        $args = CssCrush_Function::parseArgsSimple( $raw_argument );
+
+        // Match the argument index integer
+        if ( ! ctype_digit( $args[0] ) ) {
+
+            // On failure to match an integer, return an empty string
+            return '';
+        }
+
+        // Get the match from the array
+        $position_match = $args[0];
+
+        // Store the default value
+        $default_value = isset( $args[1] ) ? $args[1] : null;
+
+        if ( ! is_null( $default_value ) ) {
+            $this->defaults[ $position_match ] = trim( $default_value );
+        }
+
+        // Update the mixin argument count
+        $argNumber = ( (int) $position_match ) + 1;
+        $this->argCount = max( $this->argCount, $argNumber );
+
+        // Return the argument token
+        return "?arg$position_match?";
+    }
+
+    public function getArgValue ( $index, &$args )
+    {
+        // First lookup a passed value
+        if ( isset( $args[ $index ] ) && $args[ $index ] !== 'default' ) {
+            return $args[ $index ];
+        }
+
+        // Get a default value
+        $default = isset( $this->defaults[ $index ] ) ? $this->defaults[ $index ] : '';
+
+        // Recurse for nested arg() calls
+        if ( preg_match( CssCrush_Regex::$patt->aToken, $default, $m ) ) {
+
+            $default = $this->getArgValue( (int) $m[1], $args );
+        }
+        return $default;
+    }
+
+    public function getSubstitutions ( $args )
+    {
+        $argIndexes = range( 0, $this->argCount-1 );
+
+        // Create table of substitutions
+        $find = array();
+        $replace = array();
+
+        foreach ( $argIndexes as $index ) {
+
+            $find[] = "?arg$index?";
+            $replace[] = $this->getArgValue( $index, $args );
+        }
+
+        return array( $find, $replace );
+    }
+
+    public function count ()
+    {
+        return $this->argCount;
+    }
+}
diff --git a/lib/BalancedMatch.php b/lib/BalancedMatch.php
new file mode 100644
index 0000000..215b08f
--- /dev/null
+++ b/lib/BalancedMatch.php
@@ -0,0 +1,68 @@
+stream = $stream;
+        $this->offset = $offset;
+        $this->match = null;
+        $this->length = 0;
+
+        list( $opener, $closer ) = str_split( $brackets, 1 );
+
+        if ( strpos( $stream->raw, $opener, $this->offset ) === false ) {
+            return;
+        }
+
+        if ( substr_count( $stream->raw, $opener ) !== substr_count( $stream->raw, $closer ) ) {
+            $sample = substr( $stream->raw, $this->offset, 25 );
+            trigger_error( __METHOD__ . ": Unmatched token near '$sample'.\n", E_USER_WARNING );
+            return;
+        }
+
+        $patt = $opener === '{' ?
+            CssCrush_Regex::$patt->balancedCurlies : CssCrush_Regex::$patt->balancedParens;
+
+        if ( preg_match( $patt, $stream->raw, $m, PREG_OFFSET_CAPTURE, $this->offset ) ) {
+
+            $this->match = $m;
+            $this->matchLength = strlen( $m[0][0] );
+            $this->matchStart = $m[0][1];
+            $this->matchEnd = $this->matchStart + $this->matchLength;
+            $this->length = $this->matchEnd - $this->offset;
+        }
+        else {
+            trigger_error( __METHOD__ . ": Could not match '$opener'. Exiting.\n", E_USER_WARNING );
+        }
+    }
+
+    public function inside ()
+    {
+        return $this->match[1][0];
+    }
+
+    public function whole ()
+    {
+        return substr( $this->stream->raw, $this->offset, $this->length );
+    }
+
+    public function replace ( $replacement )
+    {
+        $this->stream->splice( $replacement, $this->offset, $this->length );
+    }
+
+    public function unWrap ()
+    {
+        $this->stream->splice( $this->inside(), $this->offset, $this->length );
+    }
+
+    public function nextIndexOf ( $needle )
+    {
+        return strpos( $this->stream->raw, $needle, $this->offset );
+    }
+}
diff --git a/lib/Color.php b/lib/Color.php
index 758a235..9472f77 100644
--- a/lib/Color.php
+++ b/lib/Color.php
@@ -4,18 +4,18 @@
  * Colour parsing and conversion.
  *
  */
-class csscrush_color {
-
+class CssCrush_Color
+{
     // Cached color keyword tables.
     static public $keywords;
     static public $minifyableKeywords;
 
-    static public function &loadKeywords () {
-
+    static public function &loadKeywords ()
+    {
         if ( is_null( self::$keywords ) ) {
 
             $table = array();
-            $path = csscrush::$config->location . '/misc/color-keywords.ini';
+            $path = CssCrush::$config->location . '/misc/color-keywords.ini';
             if ( $keywords = parse_ini_file( $path ) ) {
                 foreach ( $keywords as $word => $rgb ) {
                     $rgb = array_map( 'intval', explode( ',', $rgb ) );
@@ -26,14 +26,14 @@ static public function &loadKeywords () {
         return self::$keywords;
     }
 
-    static public function &loadMinifyableKeywords () {
-
+    static public function &loadMinifyableKeywords ()
+    {
         if ( is_null( self::$minifyableKeywords ) ) {
 
             // If color name is longer than 4 and less than 8 test to see if its hex
             // representation could be shortened.
             $table = array();
-            $keywords =& csscrush_color::loadKeywords();
+            $keywords =& CssCrush_Color::loadKeywords();
 
             foreach ( $keywords as $name => &$rgb ) {
                 $name_len = strlen( $name );
@@ -47,7 +47,7 @@ static public function &loadMinifyableKeywords () {
                     self::$minifyableKeywords[ $name ] = $hex;
                 }
                 else {
-                    if ( preg_match( csscrush_regex::$patt->cruftyHex, $hex ) ) {
+                    if ( preg_match( CssCrush_Regex::$patt->cruftyHex, $hex ) ) {
                         self::$minifyableKeywords[ $name ] = $hex;
                     }
                 }
@@ -56,8 +56,8 @@ static public function &loadMinifyableKeywords () {
         return self::$minifyableKeywords;
     }
 
-    static public function parse ( $color ) {
-
+    static public function parse ( $color )
+    {
         $rgba = null;
         $color = strtolower( $color );
 
@@ -73,7 +73,7 @@ static public function parse ( $color ) {
             switch ( $m[1] ) {
 
                 case '#':
-                    $rgba = csscrush_color::hexToRgb( $color );
+                    $rgba = CssCrush_Color::hexToRgb( $color );
                     break;
 
                 case 'rgb':
@@ -89,10 +89,10 @@ static public function parse ( $color ) {
                     $vals[3] = isset( $vals[3] ) ? floatval( $vals[3] ) : 1;
 
                     if ( strpos( $function, 'rgb' ) === 0 ) {
-                        $rgba = csscrush_color::normalizeCssRgb( $vals );
+                        $rgba = CssCrush_Color::normalizeCssRgb( $vals );
                     }
                     else {
-                        $rgba = csscrush_color::cssHslToRgb( $vals );
+                        $rgba = CssCrush_Color::cssHslToRgb( $vals );
                     }
                     break;
             }
@@ -121,8 +121,8 @@ static public function parse ( $color ) {
      * Assumes r, g, and b are contained in the set [0, 255] and
      * returns h, s, and l in the set [0, 1].
      */
-    static public function rgbToHsl ( array $rgba ) {
-
+    static public function rgbToHsl ( array $rgba )
+    {
         list( $r, $g, $b, $a ) = $rgba;
         $r /= 255;
         $g /= 255;
@@ -165,7 +165,8 @@ static public function rgbToHsl ( array $rgba ) {
      * Assumes h, s, and l are contained in the set [0, 1] and
      * returns r, g, and b in the set [0, 255].
      */
-    static public function hslToRgb ( array $hsla ) {
+    static public function hslToRgb ( array $hsla )
+    {
 
         // Populate unspecified alpha value.
         if ( ! isset( $hsla[3] ) ) {
@@ -190,7 +191,8 @@ static public function hslToRgb ( array $hsla ) {
     }
 
     // Convert percentages to points (0-255).
-    static public function normalizeCssRgb ( array $rgba ) {
+    static public function normalizeCssRgb ( array $rgba )
+    {
         foreach ( $rgba as &$val ) {
             if ( strpos( $val, '%' ) !== false ) {
                 $val = str_replace( '%', '', $val );
@@ -200,8 +202,8 @@ static public function normalizeCssRgb ( array $rgba ) {
         return $rgba;
     }
 
-    static public function cssHslToRgb ( array $hsla ) {
-
+    static public function cssHslToRgb ( array $hsla )
+    {
         // Populate unspecified alpha value.
         if ( ! isset( $hsla[3] ) ) {
             $hsla[3] = 1;
@@ -228,8 +230,8 @@ static public function cssHslToRgb ( array $hsla ) {
         return self::hslToRgb( array( $h, $s, $l, $a ) );
     }
 
-    static public function hueToRgb ( $p, $q, $t ) {
-
+    static public function hueToRgb ( $p, $q, $t )
+    {
         if ( $t < 0 ) $t += 1;
         if ( $t > 1 ) $t -= 1;
         if ( $t < 1/6 ) return $p + ( $q - $p ) * 6 * $t;
@@ -238,8 +240,8 @@ static public function hueToRgb ( $p, $q, $t ) {
         return $p;
     }
 
-    static public function rgbToHex ( array $rgba ) {
-
+    static public function rgbToHex ( array $rgba )
+    {
         // Drop alpha component.
         array_pop( $rgba );
 
@@ -250,8 +252,8 @@ static public function rgbToHex ( array $rgba ) {
         return $hex_out;
     }
 
-    static public function hexToRgb ( $hex ) {
-
+    static public function hexToRgb ( $hex )
+    {
         $hex = substr( $hex, 1 );
 
         // Handle shortened format.
@@ -280,7 +282,8 @@ static public function hexToRgb ( $hex ) {
     protected $hslColorSpace;
     public $isValid;
 
-    public function __construct ( $color, $use_hsl_color_space = false ) {
+    public function __construct ( $color, $use_hsl_color_space = false )
+    {
         $this->value = is_array( $color ) ? $color : self::parse( $color );
         $this->isValid = isset( $this->value );
         if ( $use_hsl_color_space && $this->isValid ) {
@@ -288,7 +291,8 @@ public function __construct ( $color, $use_hsl_color_space = false ) {
         }
     }
 
-    public function __toString () {
+    public function __toString ()
+    {
         if ( $this->value[3] !== 1 ) {
             return 'rgba(' . implode( ',', $this->hslColorSpace ? $this->getRgb() : $this->value ) . ')';
         }
@@ -297,7 +301,8 @@ public function __toString () {
         }
     }
 
-    public function toRgb () {
+    public function toRgb ()
+    {
         if ( $this->hslColorSpace ) {
             $this->hslColorSpace = false;
             $this->value = self::hslToRgb( $this->value );
@@ -305,7 +310,8 @@ public function toRgb () {
         return $this;
     }
 
-    public function toHsl () {
+    public function toHsl ()
+    {
         if ( ! $this->hslColorSpace ) {
             $this->hslColorSpace = true;
             $this->value = self::rgbToHsl( $this->value );
@@ -313,32 +319,36 @@ public function toHsl () {
         return $this;
     }
 
-    public function getHex () {
+    public function getHex ()
+    {
         return self::rgbToHex( $this->getRgb() );
     }
 
-    public function getHsl () {
+    public function getHsl ()
+    {
         return ! $this->hslColorSpace ? self::rgbToHsl( $this->value ) : $this->value;
     }
 
-    public function getRgb () {
+    public function getRgb ()
+    {
         return $this->hslColorSpace ? self::hslToRgb( $this->value ) : $this->value;
     }
 
-    public function getComponent ( $index ) {
+    public function getComponent ( $index )
+    {
         return $this->value[ $index ];
     }
 
-    public function setComponent ( $index, $new_component_value ) {
+    public function setComponent ( $index, $new_component_value )
+    {
         $this->value[ $index ] = $new_component_value;
     }
 
-    public function adjust ( array $adjustments ) {
-
+    public function adjust ( array $adjustments )
+    {
         $was_hsl_color_space = $this->hslColorSpace;
 
         $this->toHsl();
-
         // Normalize percentage adjustment parameters to floating point numbers.
         foreach ( $adjustments as $index => $val ) {
 
diff --git a/lib/Core.php b/lib/Core.php
index 604da68..446b929 100644
--- a/lib/Core.php
+++ b/lib/Core.php
@@ -4,8 +4,8 @@
  * Main script. Includes core public API.
  *
  */
-class csscrush {
-
+class CssCrush
+{
     // Global settings.
     static public $config;
 
@@ -13,8 +13,8 @@ class csscrush {
     static public $process;
 
     // Init called once manually post class definition.
-    static public function init ( $seed_file ) {
-
+    static public function init ( $seed_file )
+    {
         self::$config = new stdclass();
 
         // Path to this installation.
@@ -22,27 +22,30 @@ static public function init ( $seed_file ) {
 
         // Get version ID from seed file.
         $seed_file_contents = file_get_contents( $seed_file );
-        $match_count = preg_match( '!@version\s+([\d\.\w-]+)!', $seed_file_contents, $version_match );
-        self::$config->version = $match_count ? new csscrush_version( $version_match[1] ) : null;
+        $match_count = preg_match( '~@version\s+([\d\.\w-]+)~', $seed_file_contents, $version_match );
+        self::$config->version = $match_count ? new CssCrush_Version( $version_match[1] ) : null;
 
         // Set the docRoot reference.
         self::setDocRoot();
 
         // Set the default IO handler.
-        self::$config->io = 'csscrush_io';
+        self::$config->io = 'CssCrush_IO';
 
-        // Global storage.
+        // Shared resources.
         self::$config->vars = array();
         self::$config->aliases = array();
         self::$config->selectorAliases = array();
         self::$config->plugins = array();
 
         // Default options.
-        self::$config->options = (object) array(
+        self::$config->options = new CssCrush_Options( array(
 
             // Minify. Set false for formatting and comments.
             'minify' => true,
 
+            // Alternative formatter to use for un-minified output.
+            'formatter' => null,
+
             // Append 'checksum' to output file name.
             'versioning' => true,
 
@@ -79,14 +82,27 @@ static public function init ( $seed_file ) {
             // Debugging options.
             // Set true to output sass debug-info stubs that work with development tools like FireSass.
             'trace' => array(),
+
+            // Force newline type on output files. Defaults to the current platform newline.
+            // Options: 'windows' (or 'win'), 'unix', 'use-platform'
+            'newlines' => 'use-platform',
+        ));
+
+        // Register default formatters.
+        self::$config->formatters = array(
+            'single-line' => 'csscrush__fmtr_single',
+            'padded' => 'csscrush__fmtr_padded',
+            'nested' => 'csscrush__fmtr_nested',
         );
 
         // Initialise other classes.
-        csscrush_regex::init();
+        CssCrush_Regex::init();
+        CssCrush_Function::init();
+        CssCrush_PostAliasFix::init();
     }
 
-    static protected function setDocRoot ( $doc_root = null ) {
-
+    static protected function setDocRoot ( $doc_root = null )
+    {
         // Get document_root reference
         // $_SERVER['DOCUMENT_ROOT'] is unreliable in certain CGI/Apache/IIS setups
 
@@ -120,17 +136,17 @@ static protected function setDocRoot ( $doc_root = null ) {
 
                 // If doc_root is still falsy, log an error
                 $error = "Could not get a document_root reference.";
-                csscrush::logError( $error );
+                CssCrush::logError( $error );
                 trigger_error( __METHOD__ . ": $error\n", E_USER_NOTICE );
             }
         }
 
-        self::$config->docRoot = csscrush_util::normalizePath( $doc_root );
+        self::$config->docRoot = CssCrush_Util::normalizePath( $doc_root );
     }
 
     // Aliases and macros loader.
-    static public function loadAssets () {
-
+    static public function loadAssets ()
+    {
         static $called;
         if ( $called ) {
             return;
@@ -138,7 +154,7 @@ static public function loadAssets () {
 
         // Find an aliases file in the root directory
         // a local file overrides the default
-        $aliases_file = csscrush_util::find( 'Aliases-local.ini', 'Aliases.ini' );
+        $aliases_file = CssCrush_Util::find( 'Aliases-local.ini', 'Aliases.ini' );
 
         // Load aliases file if it exists
         if ( $aliases_file ) {
@@ -147,23 +163,27 @@ static public function loadAssets () {
 
                 self::$config->aliases = $result;
 
-                // Value aliases require a little preprocessing
+                // Value aliases require a little preprocessing.
                 if ( isset( self::$config->aliases[ 'values' ] ) ) {
                     $store = array();
                     foreach ( self::$config->aliases[ 'values' ] as $prop_val => $aliases ) {
                         list( $prop, $value ) = array_map( 'trim', explode( ':', $prop_val ) );
+                        foreach ( $aliases as &$alias ) {
+                            $alias = explode( ':', $alias );
+                        }
                         $store[ $prop ][ $value ] = $aliases;
                     }
                     self::$config->aliases[ 'values' ] = $store;
                 }
 
                 // Ensure all alias groups are at least set (issue #34)
-                self::$config->aliases += array(
+                self::$config->bareAliasGroups = array(
                     'properties' => array(),
                     'functions'  => array(),
                     'values'     => array(),
                     'at-rules'   => array(),
                 );
+                self::$config->aliases += self::$config->bareAliasGroups;
             }
             else {
                 trigger_error( __METHOD__ . ": Aliases file could not be parsed.\n", E_USER_NOTICE );
@@ -175,7 +195,7 @@ static public function loadAssets () {
 
         // Find a plugins file in the root directory,
         // a local file overrides the default
-        $plugins_file = csscrush_util::find( 'Plugins-local.ini', 'Plugins.ini' );
+        $plugins_file = CssCrush_Util::find( 'Plugins-local.ini', 'Plugins.ini' );
 
         // Load plugins
         if ( $plugins_file ) {
@@ -183,7 +203,7 @@ static public function loadAssets () {
                 foreach ( $result[ 'plugins' ] as $plugin_name ) {
                     // Backwards compat.
                     $plugin_name = basename( $plugin_name, '.php' );
-                    if ( csscrush_plugin::load( $plugin_name ) ) {
+                    if ( CssCrush_Plugin::load( $plugin_name ) ) {
                         self::$config->plugins[ $plugin_name ] = true;
                     }
                 }
@@ -207,9 +227,9 @@ static public function loadAssets () {
      * @param mixed $options  An array of options or null.
      * @return string  The public path to the compiled file or an empty string.
      */
-    static public function file ( $file, $options = null ) {
-
-        self::$process = new csscrush_process( $options );
+    static public function file ( $file, $options = null )
+    {
+        self::$process = new CssCrush_Process( $options );
 
         $config = self::$config;
         $process = self::$process;
@@ -240,7 +260,7 @@ static public function file ( $file, $options = null ) {
         }
 
         // Validate file input.
-        if ( ! csscrush_io::registerInputFile( $file ) ) {
+        if ( ! CssCrush_IO::registerInputFile( $file ) ) {
             return '';
         }
 
@@ -283,8 +303,8 @@ static public function file ( $file, $options = null ) {
      * @param array $attributes  An array of HTML attributes.
      * @return string  HTML link tag or error message inside HTML comment.
      */
-    static public function tag ( $file, $options = null, $attributes = array() ) {
-
+    static public function tag ( $file, $options = null, $attributes = array() )
+    {
         $file = self::file( $file, $options );
 
         if ( ! empty( $file ) ) {
@@ -297,7 +317,7 @@ static public function tag ( $file, $options = null, $attributes = array() ) {
             if ( ! isset( $attributes[ 'media' ] ) ) {
                 $attributes[ 'media' ] = 'all';
             }
-            $attr_string = csscrush_util::htmlAttributes( $attributes );
+            $attr_string = CssCrush_Util::htmlAttributes( $attributes );
             return "\n";
         }
         else {
@@ -317,8 +337,8 @@ static public function tag ( $file, $options = null, $attributes = array() ) {
      * @param array $attributes  An array of HTML attributes, set false to return CSS text without tag.
      * @return string  HTML link tag or error message inside HTML comment.
      */
-    static public function inline ( $file, $options = null, $attributes = array() ) {
-
+    static public function inline ( $file, $options = null, $attributes = array() )
+    {
         // For inline output set boilerplate to not display by default
         if ( ! is_array( $options ) ) {
             $options = array();
@@ -338,7 +358,7 @@ static public function inline ( $file, $options = null, $attributes = array() )
             $tag_close = '';
 
             if ( is_array( $attributes ) ) {
-                $attr_string = csscrush_util::htmlAttributes( $attributes );
+                $attr_string = CssCrush_Util::htmlAttributes( $attributes );
                 $tag_open = "";
                 $tag_close = '';
             }
@@ -360,14 +380,14 @@ static public function inline ( $file, $options = null, $attributes = array() )
      * @param mixed $options  An array of options or null.
      * @return string  CSS text.
      */
-    static public function string ( $string, $options = null ) {
-
+    static public function string ( $string, $options = null )
+    {
         // For strings set boilerplate to not display by default
         if ( ! isset( $options[ 'boilerplate' ] ) ) {
             $options[ 'boilerplate' ] = false;
         }
 
-        self::$process = new csscrush_process( $options );
+        self::$process = new CssCrush_Process( $options );
 
         $config = self::$config;
         $process = self::$process;
@@ -385,7 +405,7 @@ static public function string ( $string, $options = null ) {
         // Set the string on the input object.
         $process->input->string = $string;
 
-        // Import files may be ignored
+        // Import files may be ignored.
         if ( isset( $options->no_import ) ) {
             $process->input->importIgnore = true;
         }
@@ -399,8 +419,8 @@ static public function string ( $string, $options = null ) {
      *
      * @param mixed $var  Assoc array of variable names and values, a php ini filename or null.
      */
-    static public function globalVars ( $vars ) {
-
+    static public function globalVars ( $vars )
+    {
         $config = self::$config;
 
         // Merge into the stack, overrides existing variables of the same name
@@ -424,7 +444,8 @@ static public function globalVars ( $vars ) {
      *
      * @param string $dir  System path to the directory.
      */
-    static public function clearCache ( $dir = '' ) {
+    static public function clearCache ( $dir = '' )
+    {
         return $process->ioCall( 'clearCache', $dir );
     }
 
@@ -434,9 +455,9 @@ static public function clearCache ( $dir = '' ) {
      *
      * @param string $name  Name of stat to retrieve. Leave blank to retrieve all.
      */
-    static public function stat ( $name = null ) {
-
-        $process = csscrush::$process;
+    static public function stat ( $name = null )
+    {
+        $process = CssCrush::$process;
         $stat = $process->stat;
 
         // Get logged errors as late as possible.
@@ -460,8 +481,8 @@ static public function stat ( $name = null ) {
 
     static public $logging = false;
 
-    static public function log ( $arg = null, $label = '' ) {
-
+    static public function log ( $arg = null, $label = '' )
+    {
         if ( ! self::$logging ) {
             return;
         }
@@ -490,16 +511,18 @@ static public function log ( $arg = null, $label = '' ) {
         }
     }
 
-    static public function logError ( $msg ) {
+    static public function logError ( $msg )
+    {
         self::$process->errors[] = $msg;
         self::log( $msg );
     }
 
-    static public function runStat ( $name ) {
-
-        $process = csscrush::$process;
+    static public function runStat ( $name )
+    {
+        $process = CssCrush::$process;
+        $trace = $process->options->trace;
 
-        if ( ! $process->options->trace || ! in_array( $name, $process->options->trace ) ) {
+        if ( ! $trace || ! in_array( $name, $trace ) ) {
             return;
         }
 
@@ -508,7 +531,7 @@ static public function runStat ( $name ) {
             case 'selector_count':
                 $process->stat[ 'selector_count' ] = 0;
                 foreach ( $process->tokens->r as $rule ) {
-                    $process->stat[ 'selector_count' ] += count( $rule->selectorList );
+                    $process->stat[ 'selector_count' ] += count( $rule->selectors );
                 }
                 break;
 
@@ -525,24 +548,83 @@ static public function runStat ( $name ) {
 }
 
 
+#############################
+#  Default formatters.
+
+function csscrush__fmtr_single ( $rule ) {
+
+    $EOL = CssCrush::$process->newline;
+    if ( $stub = $rule->tracingStub ) {
+        $stub .= $EOL;
+    }
+
+    $comments = implode( '', $rule->comments );
+    if ( $comments ) {
+      $comments = "$EOL$comments";
+    }
+    $selectors = implode( ", ", $rule->selectors );
+    $block = implode( "; ", $rule->declarations );
+    return "$comments$stub$selectors { $block; }$EOL";
+}
+
+function csscrush__fmtr_padded ( $rule ) {
+
+    $EOL = CssCrush::$process->newline;
+    if ( $stub = $rule->tracingStub ) {
+        $stub .= $EOL;
+    }
+
+    $comments = implode( '', $rule->comments );
+    if ( $comments ) {
+        $comments = "$EOL$comments";
+    }
+
+    $cutoff = 40;
+    $selectors = implode( ", ", $rule->selectors );
+    $block = implode( "; ", $rule->declarations );
+
+    if ( strlen( $selectors ) > $cutoff ) {
+        $padding = str_repeat( ' ', $cutoff );
+        return "$comments$stub$selectors$EOL$padding { $block; }$EOL";
+    }
+    else {
+        $selectors = str_pad( $selectors, $cutoff );
+        return "$comments$stub$selectors { $block; }$EOL";
+    }
+}
+
+function csscrush__fmtr_block ( $rule, $indent = '    ' ) {
+
+    $EOL = CssCrush::$process->newline;
+    if ( $stub = $rule->tracingStub ) {
+        $stub .= $EOL;
+    }
+
+    $comments = implode( '', $rule->comments );
+    $selectors = implode( ",$EOL", $rule->selectors );
+    $block = implode( ";$EOL$indent", $rule->declarations );
+    return "$comments$stub$selectors {{$EOL}$indent$block;$EOL$indent}$EOL$EOL";
+}
+
+
 #############################
 #  Procedural style external API.
 
 function csscrush_file ( $file, $options = null ) {
-    return csscrush::file( $file, $options );
+    return CssCrush::file( $file, $options );
 }
 function csscrush_tag ( $file, $options = null, $attributes = array() ) {
-    return csscrush::tag( $file, $options, $attributes );
+    return CssCrush::tag( $file, $options, $attributes );
 }
 function csscrush_inline ( $file, $options = null, $attributes = array() ) {
-    return csscrush::inline( $file, $options, $attributes );
+    return CssCrush::inline( $file, $options, $attributes );
 }
 function csscrush_string ( $string, $options = null ) {
-    return csscrush::string( $string, $options );
+    return CssCrush::string( $string, $options );
 }
 function csscrush_globalvars ( $vars ) {
-    return csscrush::globalVars( $vars );
+    return CssCrush::globalVars( $vars );
 }
 function csscrush_clearcache ( $dir = '' ) {
-    return csscrush::clearcache( $dir );
+    return CssCrush::clearcache( $dir );
 }
diff --git a/lib/Declaration.php b/lib/Declaration.php
new file mode 100644
index 0000000..3e14034
--- /dev/null
+++ b/lib/Declaration.php
@@ -0,0 +1,104 @@
+isValid = false;
+            return;
+        }
+
+        // Test for escape tilde.
+        if ( $skip = strpos( $prop, '~' ) === 0 ) {
+            $prop = substr( $prop, 1 );
+        }
+
+        // Store the canonical property name.
+        // Store the vendor mark if one is present.
+        if ( preg_match( $regex->vendorPrefix, $prop, $vendor ) ) {
+            $canonical_property = $vendor[2];
+            $vendor = $vendor[1];
+        }
+        else {
+            $vendor = null;
+            $canonical_property = $prop;
+        }
+
+        // Check for !important.
+        if ( ( $important = stripos( $value, '!important' ) ) !== false ) {
+            $value = rtrim( substr( $value, 0, $important ) );
+            $important = true;
+        }
+
+        // Ignore declarations with null css values.
+        if ( $value === false || $value === '' ) {
+            $this->isValid = false;
+            return;
+        }
+
+        // Apply custom functions.
+        if ( ! $skip ) {
+            CssCrush_Function::executeCustomFunctions( $value );
+        }
+
+        // Capture all remaining paren pairs.
+        CssCrush::$process->captureParens( $value );
+
+        // Create an index of all regular functions in the value.
+        $functions = array();
+        if ( preg_match_all( $regex->function, $value, $m ) ) {
+            foreach ( $m[2] as $index => $fn_name ) {
+                $functions[ strtolower( $fn_name ) ] = true;
+            }
+        }
+
+        $this->property          = $prop;
+        $this->canonicalProperty = $canonical_property;
+        $this->vendor            = $vendor;
+        $this->functions         = $functions;
+        $this->index             = $contextIndex;
+        $this->value             = $value;
+        $this->skip              = $skip;
+        $this->important         = $important;
+    }
+
+    public function __toString ()
+    {
+        if ( CssCrush::$process->minifyOutput ) {
+            $whitespace = '';
+        }
+        else {
+            $whitespace = ' ';
+        }
+        $important = $this->important ? "$whitespace!important" : '';
+
+        return "$this->property:$whitespace$this->value$important";
+    }
+
+    public function getFullValue ()
+    {
+        return CssCrush::$process->restoreTokens( $this->value, 'p' );
+    }
+}
diff --git a/lib/ExtendArg.php b/lib/ExtendArg.php
new file mode 100644
index 0000000..413ddbd
--- /dev/null
+++ b/lib/ExtendArg.php
@@ -0,0 +1,32 @@
+name = $name;
+
+        if ( ! preg_match( CssCrush_Regex::$patt->ident, $this->name ) ) {
+
+            // Not a regular name: Some kind of selector so normalize it for later comparison
+            $this->name = CssCrush_Selector::makeReadableSelector( $this->name );
+
+            // If applying the pseudo on output store
+            if ( substr( $this->name, -1 ) === '!' ) {
+
+                $this->name = rtrim( $this->name, ' !' );
+                if ( preg_match( '!\:\:?[\w-]+$!', $this->name, $m ) ) {
+                    $this->pseudo = $m[0];
+                }
+            }
+        }
+    }
+}
diff --git a/lib/Fragment.php b/lib/Fragment.php
new file mode 100644
index 0000000..80fe22f
--- /dev/null
+++ b/lib/Fragment.php
@@ -0,0 +1,36 @@
+arguments = new CssCrush_ArgList( $block );
+
+        // Re-assign with the parsed arguments string
+        $this->template = $this->arguments->string;
+    }
+
+    public function call ( array $args )
+    {
+        // Copy the template
+        $template = $this->template;
+
+        if ( count( $this->arguments ) ) {
+
+            list( $find, $replace ) = $this->arguments->getSubstitutions( $args );
+            $template = str_replace( $find, $replace, $template );
+        }
+
+        // Return fragment css
+        return $template;
+    }
+}
diff --git a/lib/Function.php b/lib/Function.php
index 0660ff0..8f9faac 100644
--- a/lib/Function.php
+++ b/lib/Function.php
@@ -4,7 +4,20 @@
  * Custom CSS functions
  *
  */
-class csscrush_function {
+class CssCrush_Function
+{
+    static function init ()
+    {
+        CssCrush_Function::register( 'math', 'csscrush_fn__math' );
+        CssCrush_Function::register( 'percent', 'csscrush_fn__percent' );
+        CssCrush_Function::register( 'pc', 'csscrush_fn__percent' );
+        CssCrush_Function::register( 'hsla-adjust', 'csscrush_fn__hsla_adjust' );
+        CssCrush_Function::register( 'hsl-adjust', 'csscrush_fn__hsl_adjust' );
+        CssCrush_Function::register( 'h-adjust', 'csscrush_fn__h_adjust' );
+        CssCrush_Function::register( 's-adjust', 'csscrush_fn__s_adjust' );
+        CssCrush_Function::register( 'l-adjust', 'csscrush_fn__l_adjust' );
+        CssCrush_Function::register( 'a-adjust', 'csscrush_fn__a_adjust' );
+    }
 
     // Regex pattern for finding custom functions.
     static public $functionPatt;
@@ -12,13 +25,13 @@ class csscrush_function {
     // Stack for function names.
     static protected $customFunctions;
 
-    static public function setMatchPatt () {
-
-        self::$functionPatt = csscrush_regex::createFunctionMatchPatt( array_keys( self::$customFunctions ), true );
+    static public function setMatchPatt ()
+    {
+        self::$functionPatt = CssCrush_Regex::createFunctionMatchPatt( array_keys( self::$customFunctions ), true );
     }
 
-    static public function executeCustomFunctions ( &$str, $patt = null, $process_callback = null, $property = null ) {
-
+    static public function executeCustomFunctions ( &$str, $patt = null, $process_callback = null, $property = null )
+    {
         // No bracketed expressions, early return.
         if ( false === strpos( $str, '(' ) ) {
             return;
@@ -26,7 +39,7 @@ static public function executeCustomFunctions ( &$str, $patt = null, $process_ca
 
         // Set default pattern if not set.
         if ( is_null( $patt ) ) {
-            $patt = csscrush_function::$functionPatt;
+            $patt = CssCrush_Function::$functionPatt;
         }
 
         // No custom functions, early return.
@@ -35,14 +48,14 @@ static public function executeCustomFunctions ( &$str, $patt = null, $process_ca
         }
 
         // Find custom function matches.
-        $matches = csscrush_regex::matchAll( $patt, $str );
+        $matches = CssCrush_Regex::matchAll( $patt, $str );
 
         // Step through the matches from last to first.
         while ( $match = array_pop( $matches ) ) {
 
             $offset = $match[0][1];
 
-            if ( ! preg_match( csscrush_regex::$patt->balancedParens,
+            if ( ! preg_match( CssCrush_Regex::$patt->balancedParens,
                 $str, $parens, PREG_OFFSET_CAPTURE, $offset ) ) {
                 continue;
             }
@@ -87,28 +100,32 @@ static public function executeCustomFunctions ( &$str, $patt = null, $process_ca
     #############################
     #  API and helpers.
 
-    static public function register ( $name, $callback ) {
-        csscrush_function::$customFunctions[ $name ] = $callback;
+    static public function register ( $name, $callback )
+    {
+        CssCrush_Function::$customFunctions[ $name ] = $callback;
     }
 
-    static public function deRegister ( $name ) {
-        unset( csscrush_function::$customFunctions[ $name ] );
+    static public function deRegister ( $name )
+    {
+        unset( CssCrush_Function::$customFunctions[ $name ] );
     }
 
-    static public function parseArgs ( $input, $allowSpaceDelim = false ) {
-        return csscrush_util::splitDelimList(
+    static public function parseArgs ( $input, $allowSpaceDelim = false )
+    {
+        return CssCrush_Util::splitDelimList(
             $input, ( $allowSpaceDelim ? '\s*[,\s]\s*' : ',' ) );
     }
 
     // Intended as a quick arg-list parse for function that take up-to 2 arguments
     // with the proviso the first argument is an ident.
-    static public function parseArgsSimple ( $input ) {
-        return preg_split( csscrush_regex::$patt->argListSplit, $input, 2 );
+    static public function parseArgsSimple ( $input )
+    {
+        return preg_split( CssCrush_Regex::$patt->argListSplit, $input, 2 );
     }
 
-    static public function colorAdjust ( $raw_color, array $adjustments ) {
-
-        $hsla = new csscrush_color( $raw_color, true );
+    static public function colorAdjust ( $raw_color, array $adjustments )
+    {
+        $hsla = new CssCrush_Color( $raw_color, true );
 
         // On failure to parse return input.
         return $hsla->isValid ? $hsla->adjust( $adjustments )->__toString() : $raw_color;
@@ -116,14 +133,13 @@ static public function colorAdjust ( $raw_color, array $adjustments ) {
 }
 
 
-
 #############################
 #  Stock custom CSS functions.
 
 function csscrush_fn__math ( $input ) {
 
     // Strip blacklisted characters
-    $input = preg_replace( csscrush_regex::$patt->mathBlacklist, '', $input );
+    $input = preg_replace( CssCrush_Regex::$patt->mathBlacklist, '', $input );
 
     $result = @eval( "return $input;" );
 
@@ -135,7 +151,7 @@ function csscrush_fn__percent ( $input ) {
     // Strip non-numeric and non delimiter characters
     $input = preg_replace( '![^\d\.\s,]!S', '', $input );
 
-    $args = preg_split( csscrush_regex::$patt->argListSplit, $input, -1, PREG_SPLIT_NO_EMPTY );
+    $args = preg_split( CssCrush_Regex::$patt->argListSplit, $input, -1, PREG_SPLIT_NO_EMPTY );
 
     // Use precision argument if it exists, use default otherwise
     $precision = isset( $args[2] ) ? $args[2] : 5;
@@ -173,42 +189,31 @@ function csscrush_fn__percent ( $input ) {
 }
 
 function csscrush_fn__hsla_adjust ( $input ) {
-    list( $color, $h, $s, $l, $a ) = array_pad( csscrush_function::parseArgs( $input, true ), 5, 0 );
-    return csscrush_function::colorAdjust( $color, array( $h, $s, $l, $a ) );
+    list( $color, $h, $s, $l, $a ) = array_pad( CssCrush_Function::parseArgs( $input, true ), 5, 0 );
+    return CssCrush_Function::colorAdjust( $color, array( $h, $s, $l, $a ) );
 }
 
 function csscrush_fn__hsl_adjust ( $input ) {
-    list( $color, $h, $s, $l ) = array_pad( csscrush_function::parseArgs( $input, true ), 4, 0 );
-    return csscrush_function::colorAdjust( $color, array( $h, $s, $l, 0 ) );
+    list( $color, $h, $s, $l ) = array_pad( CssCrush_Function::parseArgs( $input, true ), 4, 0 );
+    return CssCrush_Function::colorAdjust( $color, array( $h, $s, $l, 0 ) );
 }
 
 function csscrush_fn__h_adjust ( $input ) {
-    list( $color, $h ) = array_pad( csscrush_function::parseArgs( $input, true ), 2, 0 );
-    return csscrush_function::colorAdjust( $color, array( $h, 0, 0, 0 ) );
+    list( $color, $h ) = array_pad( CssCrush_Function::parseArgs( $input, true ), 2, 0 );
+    return CssCrush_Function::colorAdjust( $color, array( $h, 0, 0, 0 ) );
 }
 
 function csscrush_fn__s_adjust ( $input ) {
-    list( $color, $s ) = array_pad( csscrush_function::parseArgs( $input, true ), 2, 0 );
-    return csscrush_function::colorAdjust( $color, array( 0, $s, 0, 0 ) );
+    list( $color, $s ) = array_pad( CssCrush_Function::parseArgs( $input, true ), 2, 0 );
+    return CssCrush_Function::colorAdjust( $color, array( 0, $s, 0, 0 ) );
 }
 
 function csscrush_fn__l_adjust ( $input ) {
-    list( $color, $l ) = array_pad( csscrush_function::parseArgs( $input, true ), 2, 0 );
-    return csscrush_function::colorAdjust( $color, array( 0, 0, $l, 0 ) );
+    list( $color, $l ) = array_pad( CssCrush_Function::parseArgs( $input, true ), 2, 0 );
+    return CssCrush_Function::colorAdjust( $color, array( 0, 0, $l, 0 ) );
 }
 
 function csscrush_fn__a_adjust ( $input ) {
-    list( $color, $a ) = array_pad( csscrush_function::parseArgs( $input, true ), 2, 0 );
-    return csscrush_function::colorAdjust( $color, array( 0, 0, 0, $a ) );
+    list( $color, $a ) = array_pad( CssCrush_Function::parseArgs( $input, true ), 2, 0 );
+    return CssCrush_Function::colorAdjust( $color, array( 0, 0, 0, $a ) );
 }
-
-csscrush_function::register( 'math', 'csscrush_fn__math' );
-csscrush_function::register( 'percent', 'csscrush_fn__percent' );
-csscrush_function::register( 'pc', 'csscrush_fn__percent' );
-csscrush_function::register( 'hsla-adjust', 'csscrush_fn__hsla_adjust' );
-csscrush_function::register( 'hsl-adjust', 'csscrush_fn__hsl_adjust' );
-csscrush_function::register( 'h-adjust', 'csscrush_fn__h_adjust' );
-csscrush_function::register( 's-adjust', 'csscrush_fn__s_adjust' );
-csscrush_function::register( 'l-adjust', 'csscrush_fn__l_adjust' );
-csscrush_function::register( 'a-adjust', 'csscrush_fn__a_adjust' );
-
diff --git a/lib/Hook.php b/lib/Hook.php
index f33efeb..2c6c018 100644
--- a/lib/Hook.php
+++ b/lib/Hook.php
@@ -4,13 +4,13 @@
  *  Access to the execution flow.
  * 
  */
-class csscrush_hook {
-
+class CssCrush_Hook
+{
     // Table of hooks and the functions attached to them.
     static public $register = array();
 
-    static public function add ( $hook, $fn_name ) {
-
+    static public function add ( $hook, $fn_name )
+    {
         // Bail early is the named hook and callback combination is already loaded.
         if ( isset( self::$register[ $hook ][ $fn_name ] ) ) {
             return;
@@ -24,12 +24,13 @@ static public function add ( $hook, $fn_name ) {
         }
     }
 
-    static public function remove ( $hook, $fn_name ) {
+    static public function remove ( $hook, $fn_name )
+    {
         unset( self::$register[ $hook ][ $fn_name ] );
     }
 
-    static public function run ( $hook, $arg_obj = null ) {
-
+    static public function run ( $hook, $arg_obj = null )
+    {
         // Run all callbacks attached to the hook.
         if ( ! isset( self::$register[ $hook ] ) ) {
             return;
diff --git a/lib/IO.php b/lib/IO.php
index b1a02af..351baa8 100644
--- a/lib/IO.php
+++ b/lib/IO.php
@@ -4,24 +4,25 @@
  * Interface for writing files, retrieving files and checking caches
  *
  */
-class csscrush_io {
-
+class CssCrush_IO
+{
     // Any setup that needs to be done
-    static public function init () {
-
-        $process = csscrush::$process;
+    static public function init ()
+    {
+        $process = CssCrush::$process;
         $process->cacheFile = "{$process->output->dir}/.csscrush";
     }
 
-    static public function getOutputDir () {
-        $process = csscrush::$process;
-        return $process->options->output_dir ?
-            $process->options->output_dir : $process->input->dir;
+    static public function getOutputDir ()
+    {
+        $process = CssCrush::$process;
+        $output_dir = $process->options->output_dir;
+        return $output_dir ? $output_dir : $process->input->dir;
     }
 
-    static public function testOutputDir () {
-
-        $output_dir = csscrush::$process->output->dir;
+    static public function testOutputDir ()
+    {
+        $output_dir = CssCrush::$process->output->dir;
         $pathtest = true;
         $error = false;
 
@@ -32,7 +33,7 @@ static public function testOutputDir () {
         }
         else if ( ! is_writable( $output_dir ) ) {
 
-            csscrush::log( 'Attempting to change permissions.' );
+            CssCrush::log( 'Attempting to change permissions.' );
 
             if ( ! @chmod( $output_dir, 0755 ) ) {
 
@@ -40,21 +41,21 @@ static public function testOutputDir () {
                 $pathtest = false;
             }
             else {
-                csscrush::log( 'Permissions updated.' );
+                CssCrush::log( 'Permissions updated.' );
             }
         }
 
         if ( $error ) {
-            csscrush::logError( $error );
+            CssCrush::logError( $error );
             trigger_error( __METHOD__ . ": $error\n", E_USER_WARNING );
         }
 
         return $pathtest;
     }
 
-    static public function getOutputFileName () {
-
-        $process = csscrush::$process;
+    static public function getOutputFileName ()
+    {
+        $process = CssCrush::$process;
         $options = $process->options;
 
         $output_basename = basename( $process->input->filename, '.css' );
@@ -66,11 +67,11 @@ static public function getOutputFileName () {
         return "$output_basename.crush.css";
     }
 
-    static public function validateExistingOutput () {
-
-        $process = csscrush::$process;
+    static public function validateExistingOutput ()
+    {
+        $process = CssCrush::$process;
         $options = $process->options;
-        $config = csscrush::$config;
+        $config = CssCrush::$config;
         $input = $process->input;
 
         // Search base directory for an existing compiled file.
@@ -81,7 +82,7 @@ static public function validateExistingOutput () {
             }
 
             // Cached file exists.
-            csscrush::log( 'Cached file exists.' );
+            CssCrush::log( 'Cached file exists.' );
 
             $existingfile = (object) array();
             $existingfile->filename = $filename;
@@ -94,11 +95,11 @@ static public function validateExistingOutput () {
             if ( file_exists( $existingfile->path ) && isset( $process->cacheData[ $process->output->filename ] ) ) {
 
                 // File exists and has config
-                csscrush::log( 'Cached file is registered.' );
+                CssCrush::log( 'Cached file is registered.' );
 
                 foreach ( $process->cacheData[ $existingfile->filename ][ 'imports' ] as $import_file ) {
 
-                    // Check if this is docroot relative or input dir relative
+                    // Check if this is docroot relative or input dir relative.
                     $root = strpos( $import_file, '/' ) === 0 ? $process->docRoot : $process->input->dir;
                     $import_filepath = realpath( $root ) . "/$import_file";
 
@@ -106,8 +107,8 @@ static public function validateExistingOutput () {
                         $all_files[] = filemtime( $import_filepath );
                     }
                     else {
-                        // File has been moved, remove old file and skip to compile
-                        csscrush::log( 'Import file has been moved, removing existing file.' );
+                        // File has been moved, remove old file and skip to compile.
+                        CssCrush::log( 'Import file has been moved, removing existing file.' );
                         unlink( $existingfile->path );
                         return false;
                     }
@@ -115,36 +116,37 @@ static public function validateExistingOutput () {
 
                 // Cast because the cached options may be a stdClass if an IO adapter has been used.
                 $cached_options = (array) $process->cacheData[ $existingfile->filename ][ 'options' ];
-                $existing_datesum = $process->cacheData[ $existingfile->filename ][ 'datem_sum' ];
+                $active_options = $options->get();
 
-                // Check for runtime options that are different to cached options.
+                // Compare runtime options and cached options for differences.
                 $options_changed = false;
                 foreach ( $cached_options as $key => &$value ) {
-                    if ( property_exists( $options, $key ) && $options->{ $key } !== $value ) {
+                    if ( isset( $active_options[ $key ] ) && $active_options[ $key ] !== $value ) {
                         $options_changed = true;
                         break;
                     }
                 }
 
                 // Check if any of the files have changed.
+                $existing_datesum = $process->cacheData[ $existingfile->filename ][ 'datem_sum' ];
                 $files_changed = $existing_datesum != array_sum( $all_files );
 
                 if ( ! $options_changed && ! $files_changed ) {
 
                     // Files have not been modified and config is the same: return the old file.
-                    csscrush::log( "Files and options have not been modified, returning existing
+                    CssCrush::log( "Files and options have not been modified, returning existing
                          file '$existingfile->URL'." );
                     return $existingfile->URL . ( $options->versioning !== false  ? "?$existing_datesum" : '' );
                 }
                 else {
 
                     if ( $options_changed ) {
-                        csscrush::log( 'Options have been modified.' );
+                        CssCrush::log( 'Options have been modified.' );
                     }
                     if ( $files_changed ) {
-                        csscrush::log( 'Files have been modified.' );
+                        CssCrush::log( 'Files have been modified.' );
                     }
-                    csscrush::log( 'Removing existing file.' );
+                    CssCrush::log( 'Removing existing file.' );
 
                     // Remove old file and continue making a new one...
                     unlink( $existingfile->path );
@@ -152,7 +154,7 @@ static public function validateExistingOutput () {
             }
             else if ( file_exists( $existingfile->path ) ) {
                 // File exists but has no config.
-                csscrush::log( 'File exists but no config, removing existing file.' );
+                CssCrush::log( 'File exists but no config, removing existing file.' );
                 unlink( $existingfile->path );
             }
             return false;
@@ -162,8 +164,8 @@ static public function validateExistingOutput () {
         return false;
     }
 
-    static public function clearCache ( $dir ) {
-
+    static public function clearCache ( $dir )
+    {
         if ( empty( $dir ) ) {
             $dir = dirname( __FILE__ );
         }
@@ -171,7 +173,7 @@ static public function clearCache ( $dir ) {
             return;
         }
 
-        $configPath = $dir . '/' . csscrush::$process->cacheFile;
+        $configPath = $dir . '/' . CssCrush::$process->cacheFile;
         if ( file_exists( $configPath ) ) {
             unlink( $configPath );
         }
@@ -189,10 +191,10 @@ static public function clearCache ( $dir ) {
         }
     }
 
-    static public function getCacheData () {
-
-        $config = csscrush::$config;
-        $process = csscrush::$process;
+    static public function getCacheData ()
+    {
+        $config = CssCrush::$config;
+        $process = CssCrush::$process;
 
         if (
             file_exists( $process->cacheFile ) &&
@@ -212,7 +214,7 @@ static public function getCacheData () {
             $cache_data = json_decode( file_get_contents( $process->cacheFile ), true )
         ) {
             // Successfully loaded config file.
-            csscrush::log( 'Cache data loaded.' );
+            CssCrush::log( 'Cache data loaded.' );
         }
         else {
             // Config file may exist but not be writable (may not be visible in some ftp situations?)
@@ -220,13 +222,13 @@ static public function getCacheData () {
                 if ( ! @unlink( $process->cacheFile ) ) {
 
                     $error = "Could not delete config data file.";
-                    csscrush::logError( $error );
+                    CssCrush::logError( $error );
                     trigger_error( __METHOD__ . ": $error\n", E_USER_NOTICE );
                 }
             }
             else {
                 // Create config file.
-                csscrush::log( 'Creating cache data file.' );
+                CssCrush::log( 'Creating cache data file.' );
             }
             file_put_contents( $process->cacheFile, json_encode( array() ) );
         }
@@ -234,32 +236,32 @@ static public function getCacheData () {
         return $cache_data;
     }
 
-    static public function saveCacheData () {
+    static public function saveCacheData ()
+    {
+        $process = CssCrush::$process;
 
-        $process = csscrush::$process;
-
-        csscrush::log( 'Saving config.' );
+        CssCrush::log( 'Saving config.' );
         file_put_contents( $process->cacheFile, json_encode( $process->cacheData ) );
     }
 
-    static public function write ( csscrush_stream $stream ) {
-
-        $process = csscrush::$process;
+    static public function write ( CssCrush_Stream $stream )
+    {
+        $process = CssCrush::$process;
         $target = "{$process->output->dir}/{$process->output->filename}";
         if ( @file_put_contents( $target, $stream ) ) {
             return "{$process->output->dirUrl}/{$process->output->filename}";
         }
         else {
             $error = "Could not write file '$target'.";
-            csscrush::logError( $error );
+            CssCrush::logError( $error );
             trigger_error( __METHOD__ . ": $error\n", E_USER_WARNING );
         }
         return false;
     }
 
-    static final function registerInputFile ( $file ) {
-
-        $input = csscrush::$process->input;
+    static final function registerInputFile ( $file )
+    {
+        $input = CssCrush::$process->input;
 
         $input->filename = basename( $file );
         $input->path = "$input->dir/$input->filename";
@@ -268,7 +270,7 @@ static final function registerInputFile ( $file ) {
 
             // On failure return false.
             $error = "Input file '$input->filename' not found.";
-            csscrush::logError( $error );
+            CssCrush::logError( $error );
             trigger_error( __METHOD__ . ": $error\n", E_USER_WARNING );
             return false;
         }
diff --git a/lib/Importer.php b/lib/Importer.php
index 460ea71..aad87a2 100644
--- a/lib/Importer.php
+++ b/lib/Importer.php
@@ -4,14 +4,14 @@
  * Recursive file importing
  *
  */
-class csscrush_importer {
-
-    static public function hostfile () {
-
-        $config = csscrush::$config;
-        $process = csscrush::$process;
+class CssCrush_Importer
+{
+    static public function hostfile ()
+    {
+        $config = CssCrush::$config;
+        $process = CssCrush::$process;
         $options = $process->options;
-        $regex = csscrush_regex::$patt;
+        $regex = CssCrush_Regex::$patt;
         $input = $process->input;
 
         // Keep track of all import file info for cache data.
@@ -22,7 +22,7 @@ static public function hostfile () {
         $prepend_file_contents = '';
 
         // The prepend file.
-        if ( $prepend_file = csscrush_util::find( 'Prepend-local.css', 'Prepend.css' ) ) {
+        if ( $prepend_file = CssCrush_Util::find( 'Prepend-local.css', 'Prepend.css' ) ) {
             $prepend_file_contents = file_get_contents( $prepend_file );
             $process->currentFile = 'file://' . $prepend_file;
 
@@ -60,14 +60,14 @@ static public function hostfile () {
             $match_start = $match[0][1];
             $match_end = $match_start + $match_len;
 
-            // If just stripping the import statements
+            // If just stripping the import statements.
             if ( isset( $input->importIgnore ) ) {
                 $str = substr_replace( $str, '', $match_start, $match_len );
                 continue;
             }
 
             // Fetch the URL object.
-            $url = csscrush_url::get( $match[1][0] );
+            $url = CssCrush_Url::get( $match[1][0] );
 
             // Pass over protocoled import urls.
             if ( $url->protocol ) {
@@ -93,7 +93,7 @@ static public function hostfile () {
 
             // Get the import contents, if unsuccessful just continue with the import line removed.
             if ( ! ( $import->content = @file_get_contents( $import->path ) ) ) {
-                csscrush::log( "Import file '{$import->url->value}' not found" );
+                CssCrush::log( "Import file '{$import->url->value}' not found" );
                 $str = substr_replace( $str, '', $match_start, $match_len );
                 continue;
             }
@@ -116,10 +116,10 @@ static public function hostfile () {
             $filenames[] = $import->url->value;
 
             // Alter all the @import urls to be paths relative to the hostfile.
-            foreach ( csscrush_regex::matchAll( $regex->import, $import->content ) as $m ) {
+            foreach ( CssCrush_Regex::matchAll( $regex->import, $import->content ) as $m ) {
 
                 // Fetch the matched URL.
-                $url2 = csscrush_url::get( $m[1][0] );
+                $url2 = CssCrush_Url::get( $m[1][0] );
 
                 // Try to resolve absolute paths.
                 // On failure strip the @import statement.
@@ -136,7 +136,7 @@ static public function hostfile () {
                 self::rewriteImportedUrls( $import );
             }
 
-            // Add media context if it exists
+            // Add media context if it exists.
             if ( $import->mediaContext ) {
                 $import->content = "@media $import->mediaContext {{$import->content}}";
             }
@@ -150,7 +150,7 @@ static public function hostfile () {
             $process->cacheData[ $process->output->filename ] = array(
                 'imports'   => $filenames,
                 'datem_sum' => array_sum( $mtimes ) + $input->mtime,
-                'options'   => clone $options,
+                'options'   => $options->get(),
             );
 
             // Save config changes.
@@ -160,10 +160,10 @@ static public function hostfile () {
         return $str;
     }
 
-    static protected function rewriteImportedUrls ( $import ) {
-
-        $link = csscrush_util::getLinkBetweenDirs(
-            csscrush::$process->input->dir, dirname( $import->path ) );
+    static protected function rewriteImportedUrls ( $import )
+    {
+        $link = CssCrush_Util::getLinkBetweenDirs(
+            CssCrush::$process->input->dir, dirname( $import->path ) );
 
         if ( empty( $link ) ) {
             return;
@@ -175,7 +175,7 @@ static protected function rewriteImportedUrls ( $import ) {
         foreach ( $matches[0] as $token ) {
 
             // Fetch the matched URL.
-            $url = csscrush_url::get( $token );
+            $url = CssCrush_Url::get( $token );
 
             if ( $url->isRelative ) {
                 // Prepend the relative url prefix.
@@ -184,10 +184,10 @@ static protected function rewriteImportedUrls ( $import ) {
         }
     }
 
-    static protected function prepareForStream ( &$str ) {
-
-        $regex = csscrush_regex::$patt;
-        $process = csscrush::$process;
+    static protected function prepareForStream ( &$str )
+    {
+        $regex = CssCrush_Regex::$patt;
+        $process = CssCrush::$process;
 
         // Convert all end-of-lines to unix style.
         $str = preg_replace( '~\r\n?~', "\n", $str );
@@ -221,27 +221,27 @@ static protected function prepareForStream ( &$str ) {
 
         if ( $parse_errors ) {
             foreach ( $parse_errors as $error_msg ) {
-                csscrush::logError( $error_msg );
+                CssCrush::logError( $error_msg );
                 trigger_error( "$error_msg\n", E_USER_WARNING );
             }
             return false;
         }
 
         // Optionally add tracing stubs.
-        if ( in_array( 'stubs', $process->options->trace ) ) {
+        if ( $process->addTracingStubs ) {
             self::addTracingStubs( $str );
         }
 
         // Strip unneeded whitespace.
-        $str = csscrush_util::normalizeWhiteSpace( $str );
+        $str = CssCrush_Util::normalizeWhiteSpace( $str );
 
         self::captureUrls( $str );
 
         return true;
     }
 
-    static protected function captureUrls ( &$str ) {
-
+    static protected function captureUrls ( &$str )
+    {
         $patt = '#
             @import\x{20}(\?s\d+\?)
             |
@@ -255,14 +255,14 @@ static protected function captureUrls ( &$str ) {
             $is_import_url = ! isset( $outer_m[2] );
 
             if ( $is_import_url ) {
-                $url = new csscrush_url(http://github.com/$outer_m[1][0]);
+                $url = new CssCrush_Url( $outer_m[1][0] );
                 $str = str_replace( $outer_m[1][0], $url->label, $str );
             }
             // Match parenthesis if not a string token.
             elseif (
-                preg_match( csscrush_regex::$patt->balancedParens, $str, $inner_m, PREG_OFFSET_CAPTURE, $outer_offset )
+                preg_match( CssCrush_Regex::$patt->balancedParens, $str, $inner_m, PREG_OFFSET_CAPTURE, $outer_offset )
             ) {
-                $url = new csscrush_url(http://github.com/$inner_m[1][0]);
+                $url = new CssCrush_Url( $inner_m[1][0] );
                 $func_name = strtolower( $outer_m[2][0] );
                 $url->convertToData = 'data-uri' === $func_name;
                 $str = substr_replace( $str, $url->label, $outer_offset,
@@ -275,19 +275,19 @@ static protected function captureUrls ( &$str ) {
         }
     }
 
-    static protected function cb_extractCommentAndString ( $match ) {
-
+    static protected function cb_extractCommentAndString ( $match )
+    {
         $full_match = $match[0];
-        $process = csscrush::$process;
+        $process = CssCrush::$process;
 
         // We return the newlines to maintain line numbering when tracing.
         $newlines = str_repeat( "\n", substr_count( $full_match, "\n" ) );
 
         if ( strpos( $full_match, '/*' ) === 0 ) {
 
-            // Bail without storing comment if in debug mode or a private comment.
+            // Bail without storing comment if output is minified or a private comment.
             if (
-                $process->options->minify ||
+                $process->minifyOutput ||
                 strpos( $full_match, '/*$' ) === 0
             ) {
                 return $newlines;
@@ -313,12 +313,12 @@ static protected function cb_extractCommentAndString ( $match ) {
         return $newlines . $label;
     }
 
-    static protected function addTracingStubs ( &$str ) {
-
+    static protected function addTracingStubs ( &$str )
+    {
         $selector_patt = '! (^|;|\})+ ([^;{}]+) (\{) !xmS';
         $token_or_whitespace = '!(\s*\?c\d+\?\s*|\s+)!S';
 
-        $matches = csscrush_regex::matchAll( $selector_patt, $str );
+        $matches = CssCrush_Regex::matchAll( $selector_patt, $str );
 
         // Start from last match and move backwards.
         while ( $m = array_pop( $matches ) ) {
@@ -353,11 +353,11 @@ static protected function addTracingStubs ( &$str ) {
                         }
 
                         // Get the currently processed file path, and escape it.
-                        $current_file = str_replace( ' ', '%20', csscrush::$process->currentFile );
+                        $current_file = str_replace( ' ', '%20', CssCrush::$process->currentFile );
                         $current_file = preg_replace( '![^\w-]!', '\\\\$0', $current_file );
 
                         // Splice in tracing stub.
-                        $label = csscrush::$process->addToken( "@media -sass-debug-info{filename{font-family:$current_file}line{font-family:\\00003$line_num}}", 't' );
+                        $label = CssCrush::$process->addToken( "@media -sass-debug-info{filename{font-family:$current_file}line{font-family:\\00003$line_num}}", 't' );
 
                         $str = $str_before . $label . substr( $str, $selector_index );
                     }
@@ -373,5 +373,4 @@ static protected function addTracingStubs ( &$str ) {
             }
         }
     }
-
 }
diff --git a/lib/Mixin.php b/lib/Mixin.php
index 9148ed4..40145fd 100644
--- a/lib/Mixin.php
+++ b/lib/Mixin.php
@@ -4,21 +4,21 @@
  *  Mixin objects.
  *
  */
-class csscrush_mixin {
-
+class CssCrush_Mixin
+{
     public $declarationsTemplate = array();
 
     public $arguments;
 
     public $data = array();
 
-    public function __construct ( $block ) {
-
+    public function __construct ( $block )
+    {
         // Strip comment markers
-        $block = csscrush_util::stripCommentTokens( $block );
+        $block = CssCrush_Util::stripCommentTokens( $block );
 
         // Prepare the arguments object
-        $this->arguments = new csscrush_arglist( $block );
+        $this->arguments = new CssCrush_ArgList( $block );
 
         // Re-assign with the parsed arguments string
         $block = $this->arguments->string;
@@ -42,7 +42,7 @@ public function __construct ( $block ) {
             if ( $declaration['property'] === 'mixin' ) {
 
                 // Mixin can contain other mixins if they are available
-                if ( $mixin_declarations = csscrush_mixin::parseValue( $declaration['value'] ) ) {
+                if ( $mixin_declarations = CssCrush_Mixin::parseValue( $declaration['value'] ) ) {
 
                     // Add mixin result to the stack
                     $this->declarationsTemplate = array_merge( $this->declarationsTemplate, $mixin_declarations );
@@ -56,15 +56,15 @@ public function __construct ( $block ) {
         // Create data table for the mixin.
         // Values that use arg() are excluded
         foreach ( $this->declarationsTemplate as &$declaration ) {
-            if ( ! preg_match( csscrush_regex::$patt->aToken, $declaration['value'] ) ) {
+            if ( ! preg_match( CssCrush_Regex::$patt->aToken, $declaration['value'] ) ) {
                 $this->data[ $declaration['property'] ] = $declaration['value'];
             }
         }
         return '';
     }
 
-    public function call ( array $args ) {
-
+    public function call ( array $args )
+    {
         // Copy the template
         $declarations = $this->declarationsTemplate;
 
@@ -82,8 +82,8 @@ public function call ( array $args ) {
         return $declarations;
     }
 
-    static public function parseSingleValue ( $message ) {
-
+    static public function parseSingleValue ( $message )
+    {
         $message = ltrim( $message );
         $mixin = null;
         $non_mixin = null;
@@ -98,25 +98,25 @@ static public function parseSingleValue ( $message ) {
 
             $name = $name_match[0];
 
-            if ( isset( csscrush::$process->mixins[ $name ] ) ) {
+            if ( isset( CssCrush::$process->mixins[ $name ] ) ) {
 
                 // Mixin match
-                $mixin = csscrush::$process->mixins[ $name ];
+                $mixin = CssCrush::$process->mixins[ $name ];
             }
-            elseif ( isset( csscrush::$process->abstracts[ $name ] ) ) {
+            elseif ( isset( CssCrush::$process->abstracts[ $name ] ) ) {
 
                 // Abstract rule match
-                $non_mixin = csscrush::$process->abstracts[ $name ];
+                $non_mixin = CssCrush::$process->abstracts[ $name ];
             }
         }
 
         // If no mixin or abstract rule matched, look for matching selector
         if ( ! $mixin && ! $non_mixin ) {
 
-            $selector_test = csscrush_selector::makeReadableSelector( $message );
+            $selector_test = CssCrush_Selector::makeReadableSelector( $message );
 
-            if ( isset( csscrush::$process->selectorRelationships[ $selector_test ] ) ) {
-                $non_mixin = csscrush::$process->selectorRelationships[ $selector_test ];
+            if ( isset( CssCrush::$process->selectorRelationships[ $selector_test ] ) ) {
+                $non_mixin = CssCrush::$process->selectorRelationships[ $selector_test ];
             }
         }
 
@@ -152,18 +152,18 @@ static public function parseSingleValue ( $message ) {
         // Determine what raw arguments there are to pass to the mixin
         $args = array();
         if ( $message !== '' ) {
-            $args = csscrush_util::splitDelimList( $message );
+            $args = CssCrush_Util::splitDelimList( $message );
         }
 
         return $mixin->call( $args );
     }
 
-    static public function parseValue ( $message ) {
-
+    static public function parseValue ( $message )
+    {
         // Call the mixin and return the list of declarations
         $declarations = array();
 
-        foreach ( csscrush_util::splitDelimList( $message ) as $item ) {
+        foreach ( CssCrush_Util::splitDelimList( $message ) as $item ) {
 
             if ( $result = self::parseSingleValue( $item ) ) {
 
@@ -173,137 +173,3 @@ static public function parseValue ( $message ) {
         return $declarations;
     }
 }
-
-
-/**
- *
- *  Fragment objects.
- *
- */
-class csscrush_fragment {
-
-    public $template = array();
-
-    public $arguments;
-
-    public function __construct ( $block ) {
-
-        // Prepare the arguments object
-        $this->arguments = new csscrush_arglist( $block );
-
-        // Re-assign with the parsed arguments string
-        $this->template = $this->arguments->string;
-    }
-
-    public function call ( array $args ) {
-
-        // Copy the template
-        $template = $this->template;
-
-        if ( count( $this->arguments ) ) {
-
-            list( $find, $replace ) = $this->arguments->getSubstitutions( $args );
-            $template = str_replace( $find, $replace, $template );
-        }
-
-        // Return fragment css
-        return $template;
-    }
-}
-
-
-/**
- *
- *  Argument list management for mixins and fragments.
- *
- */
-class csscrush_arglist implements Countable {
-
-    // Positional argument default values.
-    public $defaults = array();
-
-    // The number of expected arguments.
-    public $argCount = 0;
-
-    // The string passed in with arg calls replaced by tokens.
-    public $string;
-
-    public function __construct ( $str ) {
-
-        // Parse all arg function calls in the passed string, callback creates default values
-        csscrush_function::executeCustomFunctions( $str, 
-                csscrush_regex::$patt->argFunction, array(
-                    'arg' => array( $this, 'store' )
-                ));
-        $this->string = $str;
-    }
-
-    public function store ( $raw_argument ) {
-
-        $args = csscrush_function::parseArgsSimple( $raw_argument );
-
-        // Match the argument index integer
-        if ( ! ctype_digit( $args[0] ) ) {
-
-            // On failure to match an integer, return an empty string
-            return '';
-        }
-
-        // Get the match from the array
-        $position_match = $args[0];
-
-        // Store the default value
-        $default_value = isset( $args[1] ) ? $args[1] : null;
-
-        if ( ! is_null( $default_value ) ) {
-            $this->defaults[ $position_match ] = trim( $default_value );
-        }
-
-        // Update the mixin argument count
-        $argNumber = ( (int) $position_match ) + 1;
-        $this->argCount = max( $this->argCount, $argNumber );
-
-        // Return the argument token
-        return "?arg$position_match?";
-    }
-
-    public function getArgValue ( $index, &$args ) {
-
-        // First lookup a passed value
-        if ( isset( $args[ $index ] ) && $args[ $index ] !== 'default' ) {
-            return $args[ $index ];
-        }
-
-        // Get a default value
-        $default = isset( $this->defaults[ $index ] ) ? $this->defaults[ $index ] : '';
-
-        // Recurse for nested arg() calls
-        if ( preg_match( csscrush_regex::$patt->aToken, $default, $m ) ) {
-
-            $default = $this->getArgValue( (int) $m[1], $args );
-        }
-        return $default;
-    }
-
-    public function getSubstitutions ( $args ) {
-
-        $argIndexes = range( 0, $this->argCount-1 );
-
-        // Create table of substitutions
-        $find = array();
-        $replace = array();
-
-        foreach ( $argIndexes as $index ) {
-
-            $find[] = "?arg$index?";
-            $replace[] = $this->getArgValue( $index, $args );
-        }
-
-        return array( $find, $replace );
-    }
-
-    public function count () {
-        return $this->argCount;
-    }
-}
-
diff --git a/lib/Options.php b/lib/Options.php
new file mode 100644
index 0000000..1fdbed5
--- /dev/null
+++ b/lib/Options.php
@@ -0,0 +1,91 @@
+ $value ) {
+                $this->__set( $key, $value );
+            }
+        }
+    }
+
+    public function __set ( $name, $value )
+    {
+        switch ( $name ) {
+
+            // For legacy debug option, check minify has not been set then
+            // flip the value and change property to minify.
+            case 'debug':
+                if ( ! array_key_exists( 'minify', $this->data ) ) {
+                    $name = 'minify';
+                    $value = ! $value;
+                }
+                break;
+
+            // If trace value is truthy set to stubs.
+            case 'trace':
+                if ( ! is_array( $value ) ) {
+                    $value = $value ? array( 'stubs' ) : array();
+                }
+                break;
+
+            // Resolve a formatter callback name and check it's callable.
+            case 'formatter':
+                if ( isset( CssCrush::$config->formatters[ $value ] ) ) {
+                    $value = CssCrush::$config->formatters[ $value ];
+                }
+                if ( ! is_callable( $value ) ) {
+                    $value = null;
+                }
+                break;
+
+            // Sanitize path options.
+            case 'doc_root':
+                if ( is_string( $value ) ) {
+                    $value = CssCrush_Util::normalizePath( $value );
+                }
+                break;
+
+            // Normalize options that can be passed as strings but internally
+            // are used as arrays.
+            case 'enable':
+            case 'disable':
+                $value = (array) $value;
+                break;
+        }
+
+        $this->data[ $name ] = $value;
+    }
+
+    public function __get ( $name )
+    {
+        return isset( $this->data[ $name ] ) ? $this->data[ $name ] : null;
+    }
+
+    public function __isset ( $name )
+    {
+        return isset( $this->data[ $name ] );
+    }
+
+    public function merge ( CssCrush_Options $options_instance )
+    {
+        foreach ( $options_instance->data as $key => $value ) {
+            if ( ! array_key_exists( $key, $this->data ) ) {
+                $this->__set( $key, $value );
+            }
+        }
+    }
+
+    public function get ()
+    {
+        return $this->data;
+    }
+}
diff --git a/lib/Plugin.php b/lib/Plugin.php
index 90d0e02..23b9d25 100644
--- a/lib/Plugin.php
+++ b/lib/Plugin.php
@@ -4,26 +4,26 @@
  *  Plugin API
  * 
  */
-class csscrush_plugin {
-
+class CssCrush_Plugin
+{
     static protected $plugins = array();
 
-    static public function show () {
-
+    static public function show ()
+    {
         return self::$plugins;
     }
 
-    static public function register ( $plugin_name, $callbacks ) {
-
+    static public function register ( $plugin_name, $callbacks )
+    {
         self::$plugins[ $plugin_name ] = $callbacks;
     }
 
-    static public function load ( $plugin_name ) {
-
+    static public function load ( $plugin_name )
+    {
         // Assume the the plugin file is not loaded if null.
         if ( ! isset( self::$plugins[ $plugin_name ] ) ) {
 
-            $path = csscrush::$config->location . "/plugins/$plugin_name.php";
+            $path = CssCrush::$config->location . "/plugins/$plugin_name.php";
 
             if ( ! file_exists( $path ) ) {
 
@@ -37,8 +37,8 @@ static public function load ( $plugin_name ) {
         return isset( self::$plugins[ $plugin_name ] ) ? self::$plugins[ $plugin_name ] : null;;
     }
 
-    static public function enable ( $plugin_name ) {
-
+    static public function enable ( $plugin_name )
+    {
         $plugin = self::load( $plugin_name );
 
         if ( is_callable( $plugin[ 'enable' ] ) ) {
@@ -48,8 +48,8 @@ static public function enable ( $plugin_name ) {
         return true;
     }
 
-    static public function disable ( $plugin_name ) {
-
+    static public function disable ( $plugin_name )
+    {
         $plugin = isset( self::$plugins[ $plugin_name ] ) ? self::$plugins[ $plugin_name ] : null;
 
         if ( is_callable( $plugin[ 'disable' ] ) ) {
diff --git a/lib/PostAliasFix.php b/lib/PostAliasFix.php
new file mode 100644
index 0000000..7f4afb4
--- /dev/null
+++ b/lib/PostAliasFix.php
@@ -0,0 +1,116 @@
+ 'bottom',
+            'to right' => 'left',
+            'to bottom' => 'top',
+            'to left' => 'right',
+            // 'magic' corners.
+            'to top left' => 'bottom right',
+            'to left top' => 'bottom right',
+            'to top right' => 'bottom left',
+            'to right top' => 'bottom left',
+            'to bottom left' => 'top right',
+            'to left bottom' => 'top right',
+            'to bottom right' => 'top left',
+            'to right bottom' => 'top left',
+        );
+        $angles_new = array_keys( $angles );
+        $angles_old = array_values( $angles );
+    }
+
+    // 1, 2.
+    $patt = '~(?value ) as $m ) {
+        $original_parens[] = $m[1][0];
+        $replacement_parens[] = CssCrush::$process->addToken(
+            str_ireplace(
+                $angles_new,
+                $angles_old,
+                CssCrush::$process->fetchToken( $m[1][0] )
+            ), 'p' );
+    }
+
+    // 3.
+    foreach ( $declaration_copies as $prefixed_copy ) {
+        $prefixed_copy->value = str_replace( $original_parens, $replacement_parens, $prefixed_copy->value );
+    }
+}
+
+function csscrush__post_alias_fix_radialgradients ( $declaration_copies, $fn_name ) {
+
+    // Remove the new 'at' keyword from gradient syntax for legacy implementations.
+    // 1. Create new paren tokens based on the first prefixed declaration.
+    // 2. Replace the new syntax with the legacy syntax.
+    // 3. Swap in the new tokens on all the prefixed declarations.
+
+    // 1, 2.
+    $patt = '~(?value ) as $m ) {
+        $original_parens[] = $m[1][0];
+        $replacement_parens[] = CssCrush::$process->addToken(
+            preg_replace(
+                '~\bat +(top|left|bottom|right|center)\b~i',
+                '$1',
+                CssCrush::$process->fetchToken( $m[1][0] )
+            ), 'p' );
+    }
+
+    // 3.
+    foreach ( $declaration_copies as $prefixed_copy ) {
+        $prefixed_copy->value = str_replace( $original_parens, $replacement_parens, $prefixed_copy->value );
+    }
+}
+
diff --git a/lib/Process.php b/lib/Process.php
index 7e6cfb7..15b0f8c 100644
--- a/lib/Process.php
+++ b/lib/Process.php
@@ -4,17 +4,23 @@
  *  The main class for compiling.
  *
  */
-class csscrush_process {
+class CssCrush_Process
+{
+    public function __construct ( $options )
+    {
+        $config = CssCrush::$config;
 
-    public function __construct ( $options ) {
+        // Load in aliases and plugins.
+        CssCrush::loadAssets();
 
-        $config = csscrush::$config;
+        // Create options instance for this process.
+        $this->options = new CssCrush_Options( $options );
 
-        // Load in aliases and plugins.
-        csscrush::loadAssets();
+        // Populate option defaults.
+        $this->options->merge( $config->options );
 
-        // Resolve process options.
-        $this->setOptions( $options );
+        // Keep track of global vars to maintain cache integrity.
+        $this->options->global_vars = $config->vars;
 
         // Initialize properties.
         $this->uid = 0;
@@ -45,15 +51,37 @@ public function __construct ( $options ) {
         $this->selectorAliases = array();
         $this->selectorAliasesPatt = null;
 
+        // Shortcut commonly used options to avoid overhead with __get calls.
+        $this->minifyOutput = $this->options->minify;
+        $this->addTracingStubs = in_array( 'stubs', $this->options->trace );
+        $this->ruleFormatter = $this->options->formatter;
+
         // Pick a doc root.
         $this->docRoot = isset( $this->options->doc_root ) ?
-            csscrush_util::normalizePath( $this->options->doc_root ) : $config->docRoot;
+            $this->options->doc_root : $config->docRoot;
+
+        // Shortcut the newline option and attach it to the process.
+        switch ( $this->options->newlines ) {
+            case 'windows':
+            case 'win':
+                $this->newline = "\r\n";
+                break;
+            case 'unix':
+                $this->newline = "\n";
+                break;
+            case 'use-platform':
+            default:
+                // Fall through and use default (platform) newline.
+                $this->newline = PHP_EOL;
+                break;
+        }
 
         // Run process_init hook.
-        csscrush_hook::run( 'process_init' );
+        CssCrush_Hook::run( 'process_init' );
     }
 
-    public function release () {
+    public function release ()
+    {
         unset(
             $this->tokens,
             $this->variables,
@@ -68,8 +96,8 @@ public function release () {
     }
 
     // Establish the input and output directories and optionally test output dir.
-    public function setContext ( $input_dir, $test_output_dir = true ) {
-
+    public function setContext ( $input_dir, $test_output_dir = true )
+    {
         $doc_root = $this->docRoot;
 
         if ( strpos( $input_dir, $doc_root ) !== 0 ) {
@@ -99,64 +127,38 @@ public function setContext ( $input_dir, $test_output_dir = true ) {
         return $output_dir_ok;
     }
 
-    public function ioCall ( $method ) {
-
+    public function ioCall ( $method )
+    {
         // Fetch the argument list, shift off the first item
         $args = func_get_args();
         array_shift( $args );
 
         // The method address
-        $the_method = array( csscrush::$config->io, $method );
+        $the_method = array( CssCrush::$config->io, $method );
 
         // Return the call result
         return call_user_func_array( $the_method, $args );
     }
 
-    public function setOptions ( $options ) {
-
-        $config = csscrush::$config;
-
-        if ( ! is_array( $options ) ) {
-            $options = array();
-        }
-
-        // Backwards compat for change option name from debug to minify.
-        if ( array_key_exists( 'debug', $options ) && ! array_key_exists( 'minify', $options ) ) {
-            $options[ 'minify' ] = ! $options[ 'debug' ];
-        }
-
-        // Resolve trace options.
-        if ( array_key_exists( 'trace', $options ) && ! is_array( $options[ 'trace' ] ) ) {
-            $options[ 'trace' ] = $options[ 'trace' ] ? array( 'stubs' ) : array();
-        }
-        // Legacy trace config option.
-        if ( ! is_array( $config->options->trace ) ) {
-            $config->options->trace = $config->options->trace ? array( 'stubs' ) : array();
-        }
-
-        // Keeping track of global vars internally to maintain cache integrity.
-        $options[ '_globalVars' ] = $config->vars;
-
-        // Populate unset options with defaults.
-        $this->options = (object) ( $options + (array) $config->options );
-    }
-
 
     #############################
     #  Tokens.
 
-    public function createTokenLabel ( $type ) {
+    public function createTokenLabel ( $type )
+    {
         $counter = ++$this->uid;
         return "?$type$counter?";
     }
 
-    public function addToken ( $value, $type ) {
+    public function addToken ( $value, $type )
+    {
         $label = $this->createTokenLabel( $type );
         $this->tokens->{ $type }[ $label ] = $value;
         return $label;
     }
 
-    public function fetchToken ( $token ) {
+    public function fetchToken ( $token )
+    {
         $path =& $this->tokens->{ $token[1] };
         if ( isset( $path[ $token ] ) ) {
             return $path[ $token ];
@@ -164,17 +166,25 @@ public function fetchToken ( $token ) {
         return null;
     }
 
-    public function releaseToken ( $token ) {
-        unset( $this->tokens->{ $token[1] }[ $token ] );
+    public function popToken ( $token )
+    {
+        $val = $this->fetchToken( $token );
+        $this->releaseToken( $token );
+        return $val;
     }
 
-    public function restoreTokens ( $str, $type = 'p' ) {
+    public function releaseToken ( $token )
+    {
+        unset( $this->tokens->{ $token[1] }[ $token ] );
+    }
 
+    public function restoreTokens ( $str, $type = 'p' )
+    {
         // Reference the token table.
         $token_table =& $this->tokens->{ $type };
 
         // Find matching tokens.
-        $matches = csscrush_regex::matchAll( csscrush_regex::$patt->{ "{$type}Token" }, $str );
+        $matches = CssCrush_Regex::matchAll( CssCrush_Regex::$patt->{ "{$type}Token" }, $str );
 
         foreach ( $matches as $m ) {
             $token = $m[0][0];
@@ -189,20 +199,20 @@ public function restoreTokens ( $str, $type = 'p' ) {
     #############################
     #  Parens.
 
-    public function captureParens ( &$str ) {
-
+    public function captureParens ( &$str )
+    {
         static $callback;
         if ( ! $callback ) {
-            $callback = create_function( '$m', 'return csscrush::$process->addToken( $m[0], \'p\' );' );
+            $callback = create_function( '$m', 'return CssCrush::$process->addToken( $m[0], \'p\' );' );
         }
-        $str = preg_replace_callback( csscrush_regex::$patt->balancedParens, $callback, $str );
+        $str = preg_replace_callback( CssCrush_Regex::$patt->balancedParens, $callback, $str );
     }
 
-    public function restoreParens ( &$str, $release = true ) {
-
+    public function restoreParens ( &$str, $release = true )
+    {
         $token_table =& $this->tokens->p;
 
-        foreach ( csscrush_regex::matchAll( csscrush_regex::$patt->pToken, $str ) as $m ) {
+        foreach ( CssCrush_Regex::matchAll( CssCrush_Regex::$patt->pToken, $str ) as $m ) {
             $token = $m[0][0];
             if ( isset( $token_table[ $token ] ) ) {
                 $str = str_replace( $token, $token_table[ $token ], $str );
@@ -217,13 +227,13 @@ public function restoreParens ( &$str, $release = true ) {
     #############################
     #  Boilerplate.
 
-    protected function getBoilerplate () {
-
+    protected function getBoilerplate ()
+    {
         $file = false;
         $boilerplate_option = $this->options->boilerplate;
 
         if ( $boilerplate_option === true ) {
-            $file = csscrush_util::find(
+            $file = CssCrush_Util::find(
                 'CssCrush-local.boilerplate', 'CssCrush.boilerplate' );
         }
         elseif ( is_string( $boilerplate_option ) ) {
@@ -250,7 +260,7 @@ protected function getBoilerplate () {
                     $replacements[] = @date( 'Y-m-d H:i:s O' );
                 }
                 elseif ( $tag_name === 'version' ) {
-                    $replacements[] = 'v' . csscrush::$config->version;
+                    $replacements[] = 'v' . CssCrush::$config->version;
                 }
                 else {
                     $replacements[] = '?';
@@ -260,7 +270,7 @@ protected function getBoilerplate () {
         }
 
         // Pretty print.
-        $EOL = PHP_EOL;
+        $EOL = $this->newline;
         $boilerplate = preg_split( '![\t ]*(\r\n?|\n)[\t ]*!S', $boilerplate );
         $boilerplate = array_map( 'trim', $boilerplate );
         $boilerplate = "$EOL * " . implode( "$EOL * ", $boilerplate );
@@ -271,63 +281,100 @@ protected function getBoilerplate () {
     #############################
     #  Aliases.
 
-    static protected function applySelectorAliases ( &$str ) {
+    static protected function applySelectorAliases ( &$str )
+    {
+        if ( CssCrush::$process->selectorAliases ) {
 
-        if ( csscrush::$process->selectorAliases ) {
+            $process = CssCrush::$process;
+            $has_parens = strpos( $str, '(' ) !== false;
+
+            if ( $has_parens ) {
+                $process->captureParens( $str );
+            }
 
             static $callback;
             if ( ! $callback ) {
+
+                // Thankfully this will be updated to use a real callback when support for
+                // php 5.2 is dropped.
                 $callback = create_function( '$m',
-                    '$table =& csscrush::$process->selectorAliases;
-                    return isset( $table[ $m[1] ] ) ? $table[ $m[1] ] : "";
-                ');
+
+                    '$process = CssCrush::$process;
+                    $table =& $process->selectorAliases;
+                    $value = isset( $table[ $m[1] ] ) ? $table[ $m[1] ] : "";
+
+                    // Test for available arguments.
+                    if ( isset( $m[2] ) && $value ) {
+
+                        // Create search and replace arrays from the arguments.
+                        $args = trim( $process->popToken( $m[2] ), "()" );
+                        $args = CssCrush_Util::splitDelimList( $args );
+                        foreach ( $args as $index => $arg ) {
+                            $search[] = "#($index)";
+                        }
+
+                        // Apply arguments to the selector-alias value.
+                        $value = str_replace( $search, $args, $value );
+
+                        // Apply arguments to string tokens within the selector-alias value.
+                        preg_match_all( CssCrush_Regex::$patt->sToken, $value, $matches );
+                        foreach ( $matches as $m ) {
+                            $label = $m[0];
+                            if ( isset( $process->tokens->s[ $label ] ) ) {
+                                $process->tokens->s[ $label ] =
+                                    str_replace( $search, $args, $process->tokens->s[ $label ] );
+                            }
+                        }
+                    }
+                    return $value;'
+                );
+            }
+
+            $str = preg_replace_callback( CssCrush::$process->selectorAliasesPatt, $callback, $str );
+            if ( $has_parens ) {
+                $process->restoreParens( $str );
             }
-            $str = preg_replace_callback( csscrush::$process->selectorAliasesPatt, $callback, $str );
         }
     }
 
-    protected function extractSelectorAliases () {
-
+    protected function resolveSelectorAliases ()
+    {
         static $callback;
         if ( ! $callback ) {
-            $callback = create_function( '$m',
-                'csscrush::$process->selectorAliases[ $m[1] ] = $m[2];' );
+            $callback = create_function( '$m', 'CssCrush::$process->selectorAliases[ $m[1] ] = $m[2];' );
         }
-
-        $this->stream->pregReplaceCallback( csscrush_regex::$patt->selectorAlias, $callback );
+        $this->stream->pregReplaceCallback( CssCrush_Regex::$patt->selectorAlias, $callback );
 
         // Merge in global selector aliases.
-        $this->selectorAliases += csscrush::$config->selectorAliases;
+        $this->selectorAliases += CssCrush::$config->selectorAliases;
 
         // Create the selector aliases pattern and store it.
         if ( $this->selectorAliases ) {
             $names = implode( '|', array_keys( $this->selectorAliases ) );
-            $this->selectorAliasesPatt = '#\:(' . $names . ')\b(?!-)#iS';
+            $this->selectorAliasesPatt = '#\:(' . $names . ')\b(?!-)(\?p\d+\?)?#iS';
         }
     }
 
-    protected function filterAliases () {
-
-        $options = $this->options;
-
-        // If a vendor target is given, we prune the aliases array
-        $vendor = $options->vendor_target;
+    protected function filterAliases ()
+    {
+        // If a vendor target is given, we prune the aliases array.
+        $vendor = $this->options->vendor_target;
 
-        // Default vendor argument, use all aliases as normal
+        // Default vendor argument, so use all aliases as normal.
         if ( 'all' === $vendor ) {
             return;
         }
 
-        // For expicit 'none' argument turn off aliases
+        // For expicit 'none' argument turn off aliases.
         if ( 'none' === $vendor ) {
-            $this->aliases = array();
+            $this->aliases = CssCrush::$config->bareAliasGroups;
             return;
         }
 
-        // Normalize vendor_target argument
+        // Normalize vendor_target argument.
         $vendor = '-' . str_replace( '-', '', $vendor ) . '-';
 
-        // Loop the aliases array, filter down to the target vendor
+        // Loop the aliases array, filter down to the target vendor.
         foreach ( $this->aliases as $group_name => $group_array ) {
 
             // Property/value aliases are special.
@@ -335,8 +382,12 @@ protected function filterAliases () {
                 foreach ( $group_array as $property => $values ) {
                     $result = array();
                     foreach ( $values as $value => $prefix_values ) {
-                        foreach ( $prefix_values as $prefix ) {
-                            if ( strpos( $prefix, $vendor ) === 0 ) {
+                        foreach ( $prefix_values as $declaration ) {
+                            list( $prop, $value ) = $declaration;
+                            if (
+                                strpos( $prefix, $prop ) === 0 ||
+                                strpos( $prefix, $value ) === 0
+                            ) {
                                 $result[] = $prefix;
                             }
                         }
@@ -369,32 +420,38 @@ protected function filterAliases () {
     #############################
     #  Plugins.
 
-    protected function filterPlugins () {
+    protected function filterPlugins ()
+    {
+        $options = $this->options;
+        $config = CssCrush::$config;
 
-        $options =& $this->options;
+        // Checking for table keys is more convenient than array searching.
+        $disable = array_flip( $options->disable );
+        $enable = array_flip( $options->enable );
 
-        // Load and unload plugins.
-        // Add option enabled plugins to the list.
-        if ( is_array( $options->enable ) ) {
-            foreach ( $options->enable as $plugin_name ) {
-                $this->plugins[ $plugin_name ] = true;
-            }
+        // Disable has the special 'all' option.
+        if ( isset( $disable[ 'all' ] ) ) {
+            $disable = $config->plugins;
         }
 
         // Remove option disabled plugins from the list, and disable them.
-        if ( $options->disable === 'all' ) {
-            $options->disable = array_keys( $config->plugins );
-        }
-        if ( is_array( $options->disable ) ) {
-            foreach ( $options->disable as $plugin_name ) {
-                csscrush_plugin::disable( $plugin_name );
+        if ( $disable ) {
+            foreach ( $disable as $plugin_name => $index ) {
+                CssCrush_Plugin::disable( $plugin_name );
                 unset( $this->plugins[ $plugin_name ] );
             }
         }
 
+        // Secondly add option enabled plugins to the list.
+        if ( $enable ) {
+            foreach ( $enable as $plugin_name => $index ) {
+                $this->plugins[ $plugin_name ] = true;
+            }
+        }
+
         // Enable all plugins in the remaining list.
         foreach ( $this->plugins as $plugin_name => $bool ) {
-            csscrush_plugin::enable( $plugin_name );
+            CssCrush_Plugin::enable( $plugin_name );
         }
     }
 
@@ -402,21 +459,21 @@ protected function filterPlugins () {
     #############################
     #  Variables.
 
-    protected function calculateVariables () {
-
-        $regex = csscrush_regex::$patt;
-        $options = $this->options;
-        $config = csscrush::$config;
+    protected function calculateVariables ()
+    {
+        $config = CssCrush::$config;
+        $regex = CssCrush_Regex::$patt;
+        $option_vars = $this->options->vars;
 
         $this->stream->pregReplaceCallback( $regex->variables,
-            array( 'csscrush_process', 'cb_extractVariables' ) );
+            array( 'CssCrush_Process', 'cb_extractVariables' ) );
 
         // In-file variables override global variables.
         $this->variables = array_merge( $config->vars, $this->variables );
 
         // Runtime variables override in-file variables.
-        if ( ! empty( $options->vars ) ) {
-            $this->variables = array_merge( $this->variables, $options->vars );
+        if ( ! empty( $option_vars ) ) {
+            $this->variables = array_merge( $this->variables, $option_vars );
         }
 
         // Place variables referenced inside variables. Excecute custom functions.
@@ -427,13 +484,13 @@ protected function calculateVariables () {
 
             // Variable values can be escaped from function parsing with a tilde prefix.
             if ( strpos( $value, '~' ) !== 0 ) {
-                csscrush_function::executeCustomFunctions( $value );
+                CssCrush_Function::executeCustomFunctions( $value );
             }
         }
     }
 
-    protected function placeAllVariables () {
-
+    protected function placeAllVariables ()
+    {
         // Place variables in main stream.
         self::placeVariables( $this->stream->raw );
 
@@ -451,19 +508,19 @@ protected function placeAllVariables () {
         }
     }
 
-    static protected function placeVariables ( &$value ) {
-
-        $regex = csscrush_regex::$patt;
+    static protected function placeVariables ( &$value )
+    {
+        $regex = CssCrush_Regex::$patt;
 
         // Variables with no default value.
         $value = preg_replace_callback( $regex->varFunction,
-            array( 'csscrush_process', 'cb_placeVariables' ), $value, -1, $count );
+            array( 'CssCrush_Process', 'cb_placeVariables' ), $value, -1, $count );
 
         if ( strpos( $value, '$(' ) !== false ) {
 
             // Variables with default value.
-            csscrush_function::executeCustomFunctions( $value, $regex->varFunctionStart,
-                array( '$' => array( 'csscrush_process', 'cb_placeVariablesWithDefault' ) ) );
+            CssCrush_Function::executeCustomFunctions( $value, $regex->varFunctionStart,
+                array( '$' => array( 'CssCrush_Process', 'cb_placeVariablesWithDefault' ) ) );
 
             // Assume at least 1 replace.
             $count = 1;
@@ -473,12 +530,12 @@ static protected function placeVariables ( &$value ) {
         return $count;
     }
 
-    static public function cb_extractVariables ( $m ) {
-
-        $regex = csscrush_regex::$patt;
+    static public function cb_extractVariables ( $m )
+    {
+        $regex = CssCrush_Regex::$patt;
 
         // Strip comment markers.
-        $block = trim( csscrush_util::stripCommentTokens( $m[2] ) );
+        $block = trim( CssCrush_Util::stripCommentTokens( $m[2] ) );
 
         $pairs = preg_split( '!\s*;\s*!', $block, null, PREG_SPLIT_NO_EMPTY );
 
@@ -490,23 +547,24 @@ static public function cb_extractVariables ( $m ) {
             }
             $name = trim( substr( $var, 0, $colon ) );
             $value = trim( substr( $var, $colon + 1 ) );
-            csscrush::$process->variables[ trim( $name ) ] = $value;
+            CssCrush::$process->variables[ trim( $name ) ] = $value;
         }
     }
 
-    static protected function cb_placeVariables ( $m ) {
+    static protected function cb_placeVariables ( $m )
+    {
         $variable_name = $m[1];
-        if ( isset( csscrush::$process->variables[ $variable_name ] ) ) {
-            return csscrush::$process->variables[ $variable_name ];
+        if ( isset( CssCrush::$process->variables[ $variable_name ] ) ) {
+            return CssCrush::$process->variables[ $variable_name ];
         }
     }
 
-    static public function cb_placeVariablesWithDefault ( $raw_arg ) {
+    static public function cb_placeVariablesWithDefault ( $raw_arg )
+    {
+        list( $name, $default_value ) = CssCrush_Function::parseArgsSimple( $raw_arg );
 
-        list( $name, $default_value ) = csscrush_function::parseArgsSimple( $raw_arg );
-
-        if ( isset( csscrush::$process->variables[ $name ] ) ) {
-            return csscrush::$process->variables[ $name ];
+        if ( isset( CssCrush::$process->variables[ $name ] ) ) {
+            return CssCrush::$process->variables[ $name ];
         }
         else {
             return $default_value;
@@ -517,14 +575,14 @@ static public function cb_placeVariablesWithDefault ( $raw_arg ) {
     #############################
     #  @ifdefine blocks.
 
-    protected function resolveIfDefines () {
-
-        $matches = $this->stream->matchAll( csscrush_regex::$patt->ifDefine );
+    protected function resolveIfDefines ()
+    {
+        $matches = $this->stream->matchAll( CssCrush_Regex::$patt->ifDefine );
 
         // Move through the matches last to first.
         while ( $match = array_pop( $matches ) ) {
 
-            $curly_match = new csscrush_balancedMatch( $this->stream, $match[0][1] );
+            $curly_match = new CssCrush_BalancedMatch( $this->stream, $match[0][1] );
 
             if ( ! $curly_match->match ) {
                 // Couldn't match the block.
@@ -550,29 +608,29 @@ protected function resolveIfDefines () {
     #############################
     #  Mixins.
 
-    protected function extractMixins () {
-
+    protected function extractMixins ()
+    {
         static $callback;
         if ( ! $callback ) {
             $callback = create_function( '$m', '
                 $name = trim( $m[1] );
                 $block = trim( $m[2] );
                 if ( ! empty( $name ) && ! empty( $block ) ) {
-                    csscrush::$process->mixins[ $name ] = new csscrush_mixin( $block );
+                    CssCrush::$process->mixins[ $name ] = new CssCrush_Mixin( $block );
                 }
             ' );
         }
 
-        $this->stream->pregReplaceCallback( csscrush_regex::$patt->mixin, $callback );
+        $this->stream->pregReplaceCallback( CssCrush_Regex::$patt->mixin, $callback );
     }
 
 
     #############################
     #  Fragments.
 
-    protected function resolveFragments () {
-
-        $regex = csscrush_regex::$patt;
+    protected function resolveFragments ()
+    {
+        $regex = CssCrush_Regex::$patt;
         $matches = $this->stream->matchAll( $regex->fragmentDef );
         $fragments = array();
 
@@ -582,7 +640,7 @@ protected function resolveFragments () {
             $match_start_pos = $match[0][1];
             $fragment_name = $match[1][0];
 
-            $curly_match = new csscrush_balancedMatch( $this->stream, $match_start_pos );
+            $curly_match = new CssCrush_BalancedMatch( $this->stream, $match_start_pos );
 
             if ( ! $curly_match->match ) {
                 // Couldn't match the block.
@@ -593,7 +651,7 @@ protected function resolveFragments () {
                 $curly_match->replace( '' );
 
                 // Create the fragment and store it.
-                $fragments[ $fragment_name ] = new csscrush_fragment( $curly_match->inside() );
+                $fragments[ $fragment_name ] = new CssCrush_Fragment( $curly_match->inside() );
             }
         }
 
@@ -615,7 +673,7 @@ protected function resolveFragments () {
             $with_arguments = $match[2][0] === '(';
 
             if ( $with_arguments ) {
-                $paren_match = new csscrush_balancedMatch( $this->stream, $match_start_pos, '()' );
+                $paren_match = new CssCrush_BalancedMatch( $this->stream, $match_start_pos, '()' );
                 // Get offset of statement terminating semi-colon.
                 $match_end = $paren_match->nextIndexOf( ';' ) + 1;
                 $match_length = $match_end - $match_start_pos;
@@ -635,7 +693,7 @@ protected function resolveFragments () {
                 $args = array();
                 if ( $with_arguments ) {
                     // Get the argument array to pass to the fragment.
-                    $args = csscrush_util::splitDelimList( $paren_match->inside() );
+                    $args = CssCrush_Util::splitDelimList( $paren_match->inside() );
                 }
 
                 // Execute the fragment and get the return value.
@@ -651,63 +709,64 @@ protected function resolveFragments () {
     #############################
     #  Rules.
 
-    public function extractRules () {
-        $this->stream->pregReplaceCallback( csscrush_regex::$patt->rule, array( 'csscrush_process', 'cb_extractRules' ) );
+    public function extractRules ()
+    {
+        $this->stream->pregReplaceCallback( CssCrush_Regex::$patt->rule, array( 'CssCrush_Process', 'cb_extractRules' ) );
     }
 
-    protected function processRules () {
-
-        // Reset the selector relationships
+    protected function processRules ()
+    {
+        // Reset the selector relationships.
         $this->selectorRelationships = array();
 
         $aliases =& $this->aliases;
 
         foreach ( $this->tokens->r as $rule ) {
 
-            // Store selector relationships
+            // Store selector relationships.
             $rule->indexSelectors();
 
-            csscrush_hook::run( 'rule_prealias', $rule );
+            CssCrush_Hook::run( 'rule_prealias', $rule );
 
-            if ( ! empty( $aliases[ 'properties' ] ) ) {
+            if ( $aliases[ 'properties' ] ) {
                 $rule->addPropertyAliases();
             }
-            if ( ! empty( $aliases[ 'functions' ] ) ) {
+            if ( $aliases[ 'functions' ] ) {
                 $rule->addFunctionAliases();
             }
-            if ( ! empty( $aliases[ 'values' ] ) ) {
-                $rule->addValueAliases();
+            if ( $aliases[ 'values' ] ) {
+                $rule->addPropertyValueAliases();
             }
 
-            csscrush_hook::run( 'rule_postalias', $rule );
+            CssCrush_Hook::run( 'rule_postalias', $rule );
 
             $rule->expandSelectors();
 
-            // Find previous selectors and apply them
+            // Find previous selectors and apply them.
             $rule->applyExtendables();
 
-            csscrush_hook::run( 'rule_postprocess', $rule );
+            CssCrush_Hook::run( 'rule_postprocess', $rule );
         }
     }
 
-    static public function cb_extractRules ( $m ) {
-
+    static public function cb_extractRules ( $m )
+    {
         $rule = (object) array();
         $rule->selector_raw = trim( $m[1] );
         $rule->declaration_raw = trim( $m[2] );
 
         // Apply any selector aliases.
-        csscrush_process::applySelectorAliases( $rule->selector_raw );
+        CssCrush_Process::applySelectorAliases( $rule->selector_raw );
 
         // Run rule_preprocess hook.
-        csscrush_hook::run( 'rule_preprocess', $rule );
+        CssCrush_Hook::run( 'rule_preprocess', $rule );
 
-        $rule = new csscrush_rule( $rule->selector_raw, $rule->declaration_raw );
+        $rule = new CssCrush_Rule( $rule->selector_raw, $rule->declaration_raw );
 
         // Store rules if they have declarations or extend arguments.
-        if ( ! empty( $rule->_declarations ) || $rule->extendArgs ) {
+        if ( ! empty( $rule->declarations ) || $rule->extendArgs ) {
 
-            csscrush::$process->tokens->r[ $rule->label ] = $rule;
+            CssCrush::$process->tokens->r[ $rule->label ] = $rule;
 
             // If only using extend still return a label.
             return $rule->label;
@@ -718,8 +777,8 @@ static public function cb_extractRules ( $m ) {
     #############################
     #  @in blocks.
 
-    protected function prefixSelectors () {
-
+    protected function prefixSelectors ()
+    {
         $matches = $this->stream->matchAll( '~@in\s+([^{]+)\{~iS' );
 
         // Move through the matches in reverse order.
@@ -728,12 +787,12 @@ protected function prefixSelectors () {
             $match_start_pos = $match[0][1];
             $raw_argument = trim( $match[1][0] );
 
-            csscrush_process::applySelectorAliases( $raw_argument );
+            CssCrush_Process::applySelectorAliases( $raw_argument );
 
             $this->captureParens( $raw_argument );
-            $arguments = csscrush_util::splitDelimList( $raw_argument );
+            $arguments = CssCrush_Util::splitDelimList( $raw_argument );
 
-            $curly_match = new csscrush_balancedMatch( $this->stream, $match_start_pos );
+            $curly_match = new CssCrush_BalancedMatch( $this->stream, $match_start_pos );
 
             if ( ! $curly_match->match || empty( $raw_argument ) ) {
                 // Couldn't match the block.
@@ -741,20 +800,20 @@ protected function prefixSelectors () {
             }
 
             // Match all the rule tokens.
-            $rule_matches = csscrush_regex::matchAll(
-                csscrush_regex::$patt->rToken, $curly_match->inside() );
+            $rule_matches = CssCrush_Regex::matchAll(
+                CssCrush_Regex::$patt->rToken, $curly_match->inside() );
 
             foreach ( $rule_matches as $rule_match ) {
 
                 // Get the rule instance.
-                $rule = csscrush_rule::get( $rule_match[0][0] );
+                $rule = CssCrush_Rule::get( $rule_match[0][0] );
 
                 // Using arguments create new selector list for the rule.
                 $new_selector_list = array();
 
                 foreach ( $arguments as $arg_selector ) {
 
-                    foreach ( $rule->selectorList as $rule_selector ) {
+                    foreach ( $rule->selectors as $rule_selector ) {
 
                         if ( ! $rule_selector->allowPrefix ) {
 
@@ -773,17 +832,17 @@ protected function prefixSelectors () {
                                     1 );
 
                             // Not storing the selector as named.
-                            $new_selector_list[] = new csscrush_selector( $new_value );
+                            $new_selector_list[] = new CssCrush_Selector( $new_value );
                         }
                         else {
 
                             // Not storing the selector as named.
                             $new_selector_list[]
-                                = new csscrush_selector( "$arg_selector {$rule_selector->value}" );
+                                = new CssCrush_Selector( "$arg_selector {$rule_selector->value}" );
                         }
                     }
                 }
-                $rule->selectorList = $new_selector_list;
+                $rule->selectors = $new_selector_list;
             }
 
             $curly_match->unWrap();
@@ -794,14 +853,14 @@ protected function prefixSelectors () {
     #############################
     #  @-rule aliasing.
 
-    protected function aliasAtRules () {
-
+    protected function aliasAtRules ()
+    {
         if ( empty( $this->aliases[ 'at-rules' ] ) ) {
             return;
         }
 
         $aliases = $this->aliases[ 'at-rules' ];
-        $regex = csscrush_regex::$patt;
+        $regex = CssCrush_Regex::$patt;
 
         foreach ( $aliases as $at_rule => $at_rule_aliases ) {
 
@@ -810,7 +869,7 @@ protected function aliasAtRules () {
             // Find at-rules that we want to alias.
             while ( $match = array_pop( $matches ) ) {
 
-                $curly_match = new csscrush_balancedMatch( $this->stream, $match[0][1] );
+                $curly_match = new CssCrush_BalancedMatch( $this->stream, $match[0][1] );
 
                 if ( ! $curly_match->match ) {
                     // Couldn't match the block.
@@ -853,7 +912,7 @@ protected function aliasAtRules () {
                                     $new_set[] = $declaration;
                                 }
                             }
-                            $cloneRule->declarations = $new_set;
+                            $cloneRule->setDeclarations( $new_set );
 
                             // Store the clone.
                             $replacements[] = $this->addToken( $cloneRule, 'r' );
@@ -880,13 +939,13 @@ protected function aliasAtRules () {
     #############################
     #  Compile / collate.
 
-    protected function collate () {
-
+    protected function collate ()
+    {
         $options = $this->options;
         $minify = $options->minify;
-        $regex = csscrush_regex::$patt;
+        $regex = CssCrush_Regex::$patt;
         $regex_replacements = array();
-        $EOL = PHP_EOL;
+        $EOL = $this->newline;
 
         // Strip newlines added during parsing.
         $regex_replacements[ '!\n+!' ] = '';
@@ -908,8 +967,8 @@ protected function collate () {
 
         // Print out rules.
         $this->stream->replaceHash( $this->tokens->r );
-        csscrush::runStat( 'selector_count' );
-        csscrush::runStat( 'rule_count' );
+        CssCrush::runStat( 'selector_count' );
+        CssCrush::runStat( 'rule_count' );
 
         // Insert parens.
         $this->stream->replaceHash( $this->tokens->p );
@@ -945,13 +1004,14 @@ protected function collate () {
         // Insert URLs.
         if ( $this->tokens->u ) {
 
-            $link = csscrush_util::getLinkBetweenDirs( $this->output->dir, $this->input->dir );
+            $link = CssCrush_Util::getLinkBetweenDirs( $this->output->dir, $this->input->dir );
+            $make_urls_absolute = $options->rewrite_import_urls === 'absolute';
 
             foreach ( $this->tokens->u as $token => $url ) {
 
                 if ( $url->isRelative ) {
                     // Optionally set the URLs to absolute.
-                    if ( $options->rewrite_import_urls === 'absolute' ) {
+                    if ( $make_urls_absolute ) {
                         $url->prepend( $this->input->dirUrl . '/' );
                     }
                     // If output dir is different to input dir prepend a link between the two.
@@ -984,8 +1044,8 @@ protected function collate () {
         }
     }
 
-    public function compile () {
-
+    public function compile ()
+    {
         // Always store start time.
         $this->stat[ 'compile_start_time' ] = microtime( true );
 
@@ -994,10 +1054,10 @@ public function compile () {
         $this->filterAliases();
 
         // Create function matching regex.
-        csscrush_function::setMatchPatt();
+        CssCrush_Function::setMatchPatt();
 
         // Collate hostfile and imports.
-        $this->stream = new csscrush_stream( csscrush_importer::hostfile( $this->input ) );
+        $this->stream = new CssCrush_Stream( CssCrush_Importer::hostfile( $this->input ) );
 
         // Extract and calculate variables.
         $this->calculateVariables();
@@ -1009,7 +1069,7 @@ public function compile () {
         $this->resolveIfDefines();
 
         // Get selector aliases.
-        $this->extractSelectorAliases();
+        $this->resolveSelectorAliases();
 
         // Pull out @mixin definitions.
         $this->extractMixins();
@@ -1043,7 +1103,7 @@ public function compile () {
         // Release memory.
         $this->release();
 
-        csscrush::runStat( 'compile_time' );
+        CssCrush::runStat( 'compile_time' );
 
         return $this->stream;
     }
@@ -1052,8 +1112,8 @@ public function compile () {
     #############################
     #  Decruft.
 
-    protected function decruft () {
-
+    protected function decruft ()
+    {
         return $this->stream->pregReplaceHash( array(
 
             // Strip leading zeros on floats.
@@ -1073,7 +1133,7 @@ protected function decruft () {
             '!(\: *)0 0 (-?(?:\d+)?\.?\d+[a-z]{1,4}) 0 *([;}])!iS' => '${1}0 0 $2$3',
 
             // Compress hex codes.
-            csscrush_regex::$patt->cruftyHex => '#$1$2$3',
+            CssCrush_Regex::$patt->cruftyHex => '#$1$2$3',
         ));
     }
 
@@ -1081,11 +1141,11 @@ protected function decruft () {
     #############################
     #  Advanced minification.
 
-    protected function minifyColors () {
-
+    protected function minifyColors ()
+    {
         static $keywords_patt;
         if ( ! $keywords_patt ) {
-            $keywords =& csscrush_color::loadMinifyableKeywords();
+            $keywords =& CssCrush_Color::loadMinifyableKeywords();
             $keywords_patt = '~(?stream->pregReplaceCallback( $keywords_patt, $keywords_callback );
@@ -1101,11 +1161,11 @@ protected function minifyColors () {
         static $functions_callback;
         if ( ! $functions_callback ) {
             $functions_callback = create_function( '$m', '
-                $args = csscrush_function::parseArgs( trim( $m[2] ) );
+                $args = CssCrush_Function::parseArgs( trim( $m[2] ) );
                 if ( stripos( $m[1], \'hsl\' ) === 0 ) {
-                    $args = csscrush_color::cssHslToRgb( $args );
+                    $args = CssCrush_Color::cssHslToRgb( $args );
                 }
-                return csscrush_color::rgbToHex( $args );
+                return CssCrush_Color::rgbToHex( $args );
             ');
         }
 
diff --git a/lib/Regex.php b/lib/Regex.php
index 9d6f03e..c854bc0 100644
--- a/lib/Regex.php
+++ b/lib/Regex.php
@@ -4,16 +4,16 @@
  * Regex management.
  *
  */
-class csscrush_regex {
-
+class CssCrush_Regex
+{
     // Patterns.
     static public $patt;
 
     // Character classes.
     static public $classes;
 
-    static public function init () {
-
+    static public function init ()
+    {
         self::$patt = $patt = new stdclass();
         self::$classes = $classes = new stdclass();
 
@@ -27,11 +27,11 @@ static public function init () {
         $patt->import        = '~@import\s+(\?u\d+\?)\s?([^;]*);~iS';
         $patt->variables     = '~@(?:define|variables) *([^\{]*)\{ *(.*?) *\};?~iS';
         $patt->mixin         = '~@mixin *([^\{]*)\{ *(.*?) *\};?~iS';
-        $patt->abstract      = csscrush_regex::create( '^@abstract\s+()', 'i' );
-        $patt->selectorAlias = csscrush_regex::create( '@selector-alias +\:() +([^;]+) *;', 'iS' );
-        $patt->ifDefine      = csscrush_regex::create( '@ifdefine +(not +)?() *\{', 'iS' );
-        $patt->fragmentDef   = csscrush_regex::create( '@fragment +() *\{', 'iS' );
-        $patt->fragmentCall  = csscrush_regex::create( '@fragment +() *(\(|;)', 'iS' );
+        $patt->abstract      = CssCrush_Regex::create( '^@abstract\s+()', 'i' );
+        $patt->selectorAlias = CssCrush_Regex::create( '@selector-alias +\:() +([^;]+) *;', 'iS' );
+        $patt->ifDefine      = CssCrush_Regex::create( '@ifdefine +(not +)?() *\{', 'iS' );
+        $patt->fragmentDef   = CssCrush_Regex::create( '@fragment +() *\{', 'iS' );
+        $patt->fragmentCall  = CssCrush_Regex::create( '@fragment +() *(\(|;)', 'iS' );
 
         $patt->commentAndString = '~
             # Quoted string (to EOF if unmatched).
@@ -68,11 +68,11 @@ static public function init () {
 
         // Functions.
         $patt->function = '!(^|[^a-z0-9_-])([a-z_-]+)(\?p\d+\?)!iS';
-        $patt->varFunction = csscrush_regex::create( '\$\(\s*()\s*\)', 'iS' );
+        $patt->varFunction = CssCrush_Regex::create( '\$\(\s*()\s*\)', 'iS' );
         $patt->varFunctionStart = '!(\$)\(!';
-        $patt->argFunction = csscrush_regex::createFunctionMatchPatt( array( 'arg' ) );
-        $patt->queryFunction = csscrush_regex::createFunctionMatchPatt( array( 'query' ) );
-        $patt->thisFunction = csscrush_regex::createFunctionMatchPatt( array( 'this' ) );
+        $patt->argFunction = CssCrush_Regex::createFunctionMatchPatt( array( 'arg' ) );
+        $patt->queryFunction = CssCrush_Regex::createFunctionMatchPatt( array( 'query' ) );
+        $patt->thisFunction = CssCrush_Regex::createFunctionMatchPatt( array( 'this' ) );
 
         // Misc.
         $patt->vendorPrefix  = '!^-([a-z]+)-([a-z-]+)!iS';
@@ -83,8 +83,8 @@ static public function init () {
         $patt->cruftyHex     = '!\#([[:xdigit:]])\1([[:xdigit:]])\2([[:xdigit:]])\3!S';
     }
 
-    static public function create ( $pattern_template, $flags = '', $delim = '!' ) {
-
+    static public function create ( $pattern_template, $flags = '', $delim = '!' )
+    {
         // Sugar.
         $pattern = str_replace(
                         array( '' ),
@@ -93,8 +93,8 @@ static public function create ( $pattern_template, $flags = '', $delim = '!' ) {
         return "$delim{$pattern}$delim{$flags}";
     }
 
-    static public function matchAll ( $patt, $subject, $preprocess_patt = false, $offset = 0 ) {
-
+    static public function matchAll ( $patt, $subject, $preprocess_patt = false, $offset = 0 )
+    {
         if ( $preprocess_patt ) {
             // Assume case-insensitive.
             $patt = self::create( $patt, 'i' );
@@ -104,8 +104,8 @@ static public function matchAll ( $patt, $subject, $preprocess_patt = false, $of
         return $count ? $matches : array();
     }
 
-    static public function createFunctionMatchPatt ( $list, $include_math_function = false ) {
-
+    static public function createFunctionMatchPatt ( $list, $include_math_function = false )
+    {
         $question = '';
         if ( $include_math_function ) {
             $question = '?';
diff --git a/lib/Rule.php b/lib/Rule.php
index 527dc8d..aa9a43b 100644
--- a/lib/Rule.php
+++ b/lib/Rule.php
@@ -4,44 +4,47 @@
  * CSS rule API.
  *
  */
-class csscrush_rule implements IteratorAggregate {
-
+class CssCrush_Rule implements IteratorAggregate
+{
     public $vendorContext;
     public $label;
     public $tracingStub;
 
-    public $properties = array();
-
-    public $selectorList = array();
+    public $selectors = array();
+    public $declarations = array();
 
-    // The comments associated with the rule
+    // The comments associated with the rule.
     public $comments = array();
 
-    // Arugments passed in via 'extend' property
-    public $extendArgs = array();
+    // Index of properties used in the rule for fast lookup.
+    public $properties = array();
 
-    public $_declarations = array();
+    // Arugments passed via @extend.
+    public $extendArgs = array();
 
-    // A table for storing the declarations as data for this() referencing
+    // A table for storing the declarations as data for this() referencing.
     public $localData = array();
 
-    // A table for storing the declarations as data for external query() referencing
+    // A table for storing the declarations as data for external query() referencing.
     public $data = array();
 
-    public function __construct ( $selector_string = null, $declarations_string ) {
-
-        $regex = csscrush_regex::$patt;
-        $process = csscrush::$process;
-        $options = $process->options;
+    public function __construct ( $selector_string = null, $declarations_string )
+    {
+        $regex = CssCrush_Regex::$patt;
+        $process = CssCrush::$process;
         $this->label = $process->createTokenLabel( 'r' );
 
         // If tracing store the last tracing stub, then strip all.
         if (
-            $options->trace &&
-            $trace_tokens = csscrush_regex::matchAll( $regex->tToken, $selector_string )
+            $process->addTracingStubs &&
+            preg_match_all( $regex->tToken, $selector_string, $trace_tokens )
         ) {
             $trace_token = array_pop( $trace_tokens );
-            $this->tracingStub = $trace_token[0][0];
+            $this->tracingStub = $process->fetchToken( $trace_token[0] );
+            foreach ( $trace_tokens as $trace_token ) {
+                $process->releaseToken( $trace_token[0] );
+            }
+
             $selector_string = preg_replace( $regex->tToken, '', $selector_string );
         }
 
@@ -49,7 +52,7 @@ public function __construct ( $selector_string = null, $declarations_string ) {
         if ( ! empty( $selector_string ) ) {
 
             $process->captureParens( $selector_string );
-            $selectors = csscrush_util::splitDelimList( $selector_string );
+            $selectors = CssCrush_Util::splitDelimList( $selector_string );
 
             // Remove and store comments that sit above the first selector
             // remove all comments between the other selectors
@@ -61,7 +64,7 @@ public function __construct ( $selector_string = null, $declarations_string ) {
             // Strip any other comments then create selector instances
             foreach ( $selectors as $selector ) {
 
-                $selector = trim( csscrush_util::stripCommentTokens( $selector ) );
+                $selector = trim( CssCrush_Util::stripCommentTokens( $selector ) );
 
                 // If the selector matches an absract directive
                 if ( preg_match( $regex->abstract, $selector, $m ) ) {
@@ -73,7 +76,7 @@ public function __construct ( $selector_string = null, $declarations_string ) {
                     break;
                 }
 
-                $this->addSelector( new csscrush_selector( $selector ) );
+                $this->addSelector( new CssCrush_Selector( $selector ) );
 
                 // Store selector relationships
                 //  - This happens twice; on first pass for mixins, second pass is for inheritance
@@ -91,7 +94,7 @@ public function __construct ( $selector_string = null, $declarations_string ) {
         foreach ( $declarations as $declaration ) {
 
             // Strip comments around the property
-            $declaration = csscrush_util::stripCommentTokens( $declaration );
+            $declaration = CssCrush_Util::stripCommentTokens( $declaration );
 
             // Accept several different syntaxes for mixin and extends.
             if ( preg_match( $regex->mixinExtend, $declaration, $m ) ) {
@@ -118,7 +121,7 @@ public function __construct ( $selector_string = null, $declarations_string ) {
             if ( $prop === 'mixin' ) {
 
                 // Mixins are a special case
-                if ( $mixin_declarations = csscrush_mixin::parseValue( $value ) ) {
+                if ( $mixin_declarations = CssCrush_Mixin::parseValue( $value ) ) {
 
                     // Add mixin declarations to the stack
                     while ( $mixin_declaration = array_shift( $mixin_declarations ) ) {
@@ -147,8 +150,8 @@ public function __construct ( $selector_string = null, $declarations_string ) {
             list( $prop, $value ) = $pair;
 
             // Resolve self references, aka this()
-            csscrush_function::executeCustomFunctions( $value,
-                    csscrush_regex::$patt->thisFunction, array(
+            CssCrush_Function::executeCustomFunctions( $value,
+                    CssCrush_Regex::$patt->thisFunction, array(
                         'this'  => array( $this, 'cssThisFunction' ),
                     ), $prop );
 
@@ -164,64 +167,42 @@ public function __construct ( $selector_string = null, $declarations_string ) {
         $this->localData = null;
     }
 
-    public function __set ( $name, $value ) {
-
-        if ( $name === 'declarations' ) {
-            $this->_declarations = $value;
-
-            // Update the table of properties
-            $this->updatePropertyTable();
-        }
-    }
-
-    public function __get ( $name ) {
-
-        if ( $name === 'declarations' ) {
-            return $this->_declarations;
-        }
-    }
-
-    public function __toString () {
+    public function __toString ()
+    {
+        $process = CssCrush::$process;
 
         // If there are no selectors or declarations associated with the rule return empty string.
-        if ( empty( $this->selectorList ) || empty( $this->_declarations ) ) {
-            // De-referencing self.
-            unset( csscrush::$process->tokens->r[ $this->label ] );
-            return '';
-        }
+        if ( empty( $this->selectors ) || empty( $this->declarations ) ) {
 
-        // Tracing stubs.
-        $tracing_stub = '';
-        if ( $this->tracingStub ) {
-            $tracing_stub =& csscrush::$process->tokens->t[ $this->tracingStub ];
+            // De-reference this instance.
+            unset( $process->tokens->r[ $this->label ] );
+            return '';
         }
 
         // Concat and return.
-        if ( csscrush::$process->options->minify ) {
-            $selectors = implode( ',', $this->selectorList );
-            $block = implode( ';', $this->_declarations );
-            return "$tracing_stub$selectors{{$block}}";
+        if ( $process->minifyOutput ) {
+            $selectors = implode( ',', $this->selectors );
+            $block = implode( ';', $this->declarations );
+            return "$this->tracingStub$selectors{{$block}}";
         }
         else {
-            $EOL = PHP_EOL;
-            if ( $tracing_stub ) {
-                $tracing_stub .= $EOL;
+
+            if ( $formatter = $process->ruleFormatter ) {
+                return $formatter( $this );
             }
-            // Include pre-rule comments.
-            $comments = implode( '', $this->comments );
-            $selectors = implode( ",$EOL", $this->selectorList );
-            $block = implode( ";$EOL\t", $this->_declarations );
-            return "$comments$tracing_stub$selectors {{$EOL}\t$block;$EOL\t}$EOL$EOL";
+
+            // Default block formatter.
+            return csscrush__fmtr_block( $this );
         }
     }
 
-    public function declarationCheckin ( $prop, $value, &$pairs ) {
-
+    public function declarationCheckin ( $prop, $value, &$pairs )
+    {
         // First resolve query() calls that reference earlier rules.
-        if ( preg_match( csscrush_regex::$patt->queryFunction, $value ) ) {
+        if ( preg_match( CssCrush_Regex::$patt->queryFunction, $value ) ) {
 
-            csscrush_function::executeCustomFunctions( $value,
-                csscrush_regex::$patt->queryFunction, array(
+            CssCrush_Function::executeCustomFunctions( $value,
+                CssCrush_Regex::$patt->queryFunction, array(
                     'query' => array( $this, 'cssQueryFunction' ),
                 ), $prop );
         }
@@ -253,14 +234,22 @@ public function declarationCheckin ( $prop, $value, &$pairs ) {
 
         // Unset on data tables if the value has a this() call:
         //   - Restriction to avoid circular references
-        if ( preg_match( csscrush_regex::$patt->thisFunction, $value ) ) {
+        if ( preg_match( CssCrush_Regex::$patt->thisFunction, $value ) ) {
 
             unset( $this->localData[ $prop ], $this->data[ $prop ] );
         }
     }
 
-    public function updatePropertyTable () {
+    public function setDeclarations ( array $declaration_stack )
+    {
+        $this->declarations = $declaration_stack;
+
+        // Update the property index.
+        $this->updatePropertyIndex();
+    }
 
+    public function updatePropertyIndex ()
+    {
         // Create a new table of properties.
         $new_properties_table = array();
 
@@ -282,27 +271,27 @@ public function updatePropertyTable () {
     #############################
     #  Inheritance.
 
-    public function setExtendSelectors ( $raw_value ) {
-
-        $abstracts =& csscrush::$process->abstracts;
-        $selectorRelationships =& csscrush::$process->selectorRelationships;
+    public function setExtendSelectors ( $raw_value )
+    {
+        $abstracts =& CssCrush::$process->abstracts;
+        $selectorRelationships =& CssCrush::$process->selectorRelationships;
 
         // Reset if called earlier, last call wins by intention.
         $this->extendArgs = array();
 
-        foreach ( csscrush_util::splitDelimList( $raw_value ) as $arg ) {
-            $this->extendArgs[] = new csscrush_extendArg( $arg );
+        foreach ( CssCrush_Util::splitDelimList( $raw_value ) as $arg ) {
+            $this->extendArgs[] = new CssCrush_ExtendArg( $arg );
         }
     }
 
-    public function applyExtendables () {
-
+    public function applyExtendables ()
+    {
         if ( ! $this->extendArgs ) {
             return;
         }
 
-        $abstracts =& csscrush::$process->abstracts;
-        $selectorRelationships =& csscrush::$process->selectorRelationships;
+        $abstracts =& CssCrush::$process->abstracts;
+        $selectorRelationships =& CssCrush::$process->selectorRelationships;
 
         // Filter the extendArgs list to usable references
         foreach ( $this->extendArgs as $key => $extend_arg ) {
@@ -344,13 +333,13 @@ public function applyExtendables () {
 
             $ancestor = $extend_arg->pointer;
 
-            $extend_selectors = $this->selectorList;
+            $extend_selectors = $this->selectors;
 
             // If there is a pseudo class extension create a new set accordingly
             if ( $extend_arg->pseudo ) {
 
                 $extend_selectors = array();
-                foreach ( $this->selectorList as $readable => $selector ) {
+                foreach ( $this->selectors as $readable => $selector ) {
                     $new_selector = clone $selector;
                     $new_readable = $new_selector->appendPseudo( $extend_arg->pseudo );
                     $extend_selectors[ $new_readable ] = $new_selector;
@@ -365,9 +354,9 @@ public function applyExtendables () {
     #############################
     #  Referencing.
 
-    public function cssThisFunction ( $input, $fn_name ) {
-
-        $args = csscrush_function::parseArgsSimple( $input );
+    public function cssThisFunction ( $input, $fn_name )
+    {
+        $args = CssCrush_Function::parseArgsSimple( $input );
 
         if ( isset( $this->localData[ $args[0] ] ) ) {
 
@@ -383,18 +372,18 @@ public function cssThisFunction ( $input, $fn_name ) {
         }
     }
 
-    public function cssQueryFunction ( $input, $fn_name, $call_property ) {
-
+    public function cssQueryFunction ( $input, $fn_name, $call_property )
+    {
         $result = '';
-        $args = csscrush_function::parseArgs( $input );
+        $args = CssCrush_Function::parseArgs( $input );
 
         if ( count( $args ) < 1 ) {
             return $result;
         }
 
-        $abstracts =& csscrush::$process->abstracts;
-        $mixins =& csscrush::$process->mixins;
-        $selectorRelationships =& csscrush::$process->selectorRelationships;
+        $abstracts =& CssCrush::$process->abstracts;
+        $mixins =& CssCrush::$process->mixins;
+        $selectorRelationships =& CssCrush::$process->selectorRelationships;
 
         // Resolve arguments
         $name = array_shift( $args );
@@ -410,7 +399,7 @@ public function cssQueryFunction ( $input, $fn_name, $call_property ) {
         $default = isset( $args[0] ) ? $args[0] : null;
 
         // Try to match a abstract rule first
-        if ( preg_match( csscrush_regex::$patt->ident, $name ) ) {
+        if ( preg_match( CssCrush_Regex::$patt->ident, $name ) ) {
 
             // Search order: abstracts, mixins, rules
             if ( isset( $abstracts[ $name ]->data[ $property ] ) ) {
@@ -429,7 +418,7 @@ public function cssQueryFunction ( $input, $fn_name, $call_property ) {
         else {
 
             // Look for a rule match
-            $name = csscrush_selector::makeReadableSelector( $name );
+            $name = CssCrush_Selector::makeReadableSelector( $name );
             if ( isset( $selectorRelationships[ $name ]->data[ $property ] ) ) {
 
                 $result = $selectorRelationships[ $name ]->data[ $property ];
@@ -446,12 +435,12 @@ public function cssQueryFunction ( $input, $fn_name, $call_property ) {
     #############################
     #  Selectors.
 
-    public function expandSelectors () {
-
+    public function expandSelectors ()
+    {
         $new_set = array();
         $reg_comma = '!\s*,\s*!';
 
-        foreach ( $this->selectorList as $readableValue => $selector ) {
+        foreach ( $this->selectors as $readableValue => $selector ) {
 
             $pos = strpos( $selector->value, ':any?' );
 
@@ -464,7 +453,7 @@ public function expandSelectors () {
                         preg_match( '!:any(\?p\d+\?)!', $selector->value, $m );
 
                         // Parse the arguments
-                        $expression = trim( csscrush::$process->tokens->p[ $m[1] ], '()' );
+                        $expression = trim( CssCrush::$process->tokens->p[ $m[1] ], '()' );
                         $parts = preg_split( $reg_comma, $expression, null, PREG_SPLIT_NO_EMPTY );
 
                         $tmp = array();
@@ -488,11 +477,11 @@ public function expandSelectors () {
                 foreach ( $chain as &$row ) {
 
                     // Not creating a named rule association with this expanded selector
-                    $new_set[] = new csscrush_selector( $row . $selector->value );
+                    $new_set[] = new CssCrush_Selector( $row . $selector->value );
                 }
 
                 // Store the unexpanded selector to selectorRelationships
-                csscrush::$process->selectorRelationships[ $readableValue ] = $this;
+                CssCrush::$process->selectorRelationships[ $readableValue ] = $this;
             }
             else {
 
@@ -502,50 +491,53 @@ public function expandSelectors () {
 
         } // foreach
 
-        $this->selectorList = $new_set;
+        $this->selectors = $new_set;
     }
 
-    public function indexSelectors () {
-
-        foreach ( $this->selectorList as $selector ) {
-            csscrush::$process->selectorRelationships[ $selector->readableValue ] = $this;
+    public function indexSelectors ()
+    {
+        foreach ( $this->selectors as $selector ) {
+            CssCrush::$process->selectorRelationships[ $selector->readableValue ] = $this;
         }
     }
 
-    public function addSelector ( $selector ) {
-
-        $this->selectorList[ $selector->readableValue ] = $selector;
+    public function addSelector ( $selector )
+    {
+        $this->selectors[ $selector->readableValue ] = $selector;
     }
 
-    public function addSelectors ( $list ) {
-
-        $this->selectorList = array_merge( $this->selectorList, $list );
+    public function addSelectors ( $list )
+    {
+        $this->selectors = array_merge( $this->selectors, $list );
     }
 
 
     #############################
-    #  Aliases
+    #  Aliasing.
 
-    public function addPropertyAliases () {
+    public function addPropertyAliases ()
+    {
+        $aliased_properties =& CssCrush::$process->aliases[ 'properties' ];
 
-        $aliased_properties =& csscrush::$process->aliases[ 'properties' ];
-
-        // First test for the existence of any aliased properties.
+        // Bail early if nothing doing.
         if ( ! array_intersect_key( $aliased_properties, $this->properties ) ) {
             return;
         }
 
-        $regex = csscrush_regex::$patt;
-        $new_set = array();
+        $stack = array();
+        $rule_updated = false;
 
-        // Shim in aliased properties
         foreach ( $this->declarations as $declaration ) {
 
-            $prop = $declaration->property;
-            if ( ! $declaration->skip && isset( $aliased_properties[ $prop ] ) ) {
+            if ( $declaration->skip ) {
+                $stack[] = $declaration;
+                continue;
+            }
+
+            // Shim in aliased properties.
+            if ( isset( $aliased_properties[ $declaration->property ] ) ) {
 
-                // There are aliases for the current property
-                foreach ( $aliased_properties[ $prop ] as $prop_alias ) {
+                foreach ( $aliased_properties[ $declaration->property ] as $prop_alias ) {
 
                     // If an aliased version already exists to not create one.
                     if ( $this->propertyCount( $prop_alias ) ) {
@@ -558,35 +550,38 @@ public function addPropertyAliases () {
 
                     // Set the aliased declaration vendor property.
                     $copy->vendor = null;
-                    if ( preg_match( $regex->vendorPrefix, $prop_alias, $vendor ) ) {
+                    if ( preg_match( CssCrush_Regex::$patt->vendorPrefix, $prop_alias, $vendor ) ) {
                         $copy->vendor = $vendor[1];
                     }
-                    $new_set[] = $copy;
+
+                    $stack[] = $copy;
+                    $rule_updated = true;
                 }
             }
+
             // Un-aliased property or a property alias that has been manually set.
-            $new_set[] = $declaration;
+            $stack[] = $declaration;
         }
-        // Re-assign.
-        $this->declarations = $new_set;
-    }
-
-    public function addFunctionAliases () {
 
-        $function_aliases =& csscrush::$process->aliases[ 'functions' ];
-
-        if ( ! $function_aliases ) {
-            return;
+        // Re-assign if any updates have been made.
+        if ( $rule_updated ) {
+            $this->setDeclarations( $stack );
         }
+    }
+
+    public function addFunctionAliases ()
+    {
+        $function_aliases =& CssCrush::$process->aliases[ 'functions' ];
 
         // The new modified set of declarations.
         $new_set = array();
+        $rule_updated = false;
 
         // Shim in aliased functions.
         foreach ( $this->declarations as $declaration ) {
 
             // No functions, bail.
-            if ( $declaration->skip || ! $declaration->functions ) {
+            if ( ! $declaration->functions || $declaration->skip ) {
                 $new_set[] = $declaration;
                 continue;
             }
@@ -608,7 +603,7 @@ public function addFunctionAliases () {
 
                     // If the declaration is vendor specific only create aliases for the same vendor.
                     if ( $declaration->vendor ) {
-                        preg_match( csscrush_regex::$patt->vendorPrefix, $fn_alias, $m );
+                        preg_match( CssCrush_Regex::$patt->vendorPrefix, $fn_alias, $m );
                         if ( $m[1] !== $declaration->vendor ) {
                             continue;
                         }
@@ -623,11 +618,12 @@ public function addFunctionAliases () {
                         $copy->value
                     );
                     $prefixed_copies[] = $copy;
+                    $rule_updated = true;
                 }
 
                 // Aliased function may require some additional fiddling.
-                if ( isset( csscrush_legacySyntax::$functions[ $fn_name ] ) ) {
-                    call_user_func( csscrush_legacySyntax::$functions[ $fn_name ], $prefixed_copies, $fn_name );
+                if ( isset( CssCrush_PostAliasFix::$functions[ $fn_name ] ) ) {
+                    call_user_func( CssCrush_PostAliasFix::$functions[ $fn_name ], $prefixed_copies, $fn_name );
                 }
 
                 $new_set = array_merge( $new_set, $prefixed_copies );
@@ -635,33 +631,46 @@ public function addFunctionAliases () {
             $new_set[] = $declaration;
         }
 
-        // Re-assign.
-        $this->declarations = $new_set;
+        // Re-assign if any updates have been made.
+        if ( $rule_updated ) {
+            $this->setDeclarations( $new_set );
+        }
     }
 
-    public function addValueAliases () {
-
-        $aliased_values =& csscrush::$process->aliases[ 'values' ];
+    public function addPropertyValueAliases ()
+    {
+        $prop_value_aliases =& CssCrush::$process->aliases[ 'values' ];
 
         // First test for the existence of any aliased properties.
-        if ( ! array_intersect_key( $aliased_values, $this->properties ) ) {
+        if ( ! ( $intersect = array_intersect_key( $prop_value_aliases, $this->properties ) ) ) {
             return;
         }
 
+        // Table lookups are faster.
+        $intersect = array_flip( array_keys( $intersect ) );
+
         $new_set = array();
+        $rule_updated = false;
+
         foreach ( $this->declarations as $declaration ) {
-            if ( ! $declaration->skip ) {
-                foreach ( $aliased_values as $value_prop => $value_aliases ) {
-                    if ( $this->propertyCount( $value_prop ) < 1 ) {
-                        continue;
-                    }
-                    foreach ( $value_aliases as $value => $aliases ) {
-                        if ( $declaration->value === $value ) {
-                            foreach ( $aliases as $alias ) {
-                                $copy = clone $declaration;
-                                $copy->value = $alias;
-                                $new_set[] = $copy;
-                            }
+
+            // Check the current declaration property is actually aliased.
+            if ( isset( $intersect[ $declaration->property ] ) && ! $declaration->skip ) {
+
+                // Iterate on the current declaration property for value matches.
+                foreach ( $prop_value_aliases[ $declaration->property ] as $value_match => $replacements ) {
+
+                    // Create new alias declaration if the property and value match.
+                    if ( $declaration->value === $value_match ) {
+
+                        foreach ( $replacements as $pair ) {
+
+                            // If the replacement property is null use the original declaration property.
+                            $new_set[] = new CssCrush_Declaration(
+                                ! empty( $pair[0] ) ? $pair[0] : $declaration->property,
+                                $pair[1]
+                                );
+                            $rule_updated = true;
                         }
                     }
                 }
@@ -669,15 +678,18 @@ public function addValueAliases () {
             $new_set[] = $declaration;
         }
 
-        // Re-assign.
-        $this->declarations = $new_set;
+        // Re-assign if any updates have been made.
+        if ( $rule_updated ) {
+            $this->setDeclarations( $new_set );
+        }
     }
 
 
     #############################
     #  IteratorAggregate interface.
 
-    public function getIterator () {
+    public function getIterator ()
+    {
         return new ArrayIterator( $this->declarations );
     }
 
@@ -685,13 +697,13 @@ public function getIterator () {
     #############################
     #  Rule API.
 
-    public function propertyCount ( $prop ) {
-
+    public function propertyCount ( $prop )
+    {
         return isset( $this->properties[ $prop ] ) ? $this->properties[ $prop ] : 0;
     }
 
-    public function addProperty ( $prop ) {
-
+    public function indexProperty ( $prop )
+    {
         if ( isset( $this->properties[ $prop ] ) ) {
             $this->properties[ $prop ]++;
         }
@@ -700,323 +712,27 @@ public function addProperty ( $prop ) {
         }
     }
 
-    public function addDeclaration ( $prop, $value, $contextIndex = 0 ) {
-
+    public function addDeclaration ( $prop, $value, $contextIndex = 0 )
+    {
         // Create declaration, add to the stack if it's valid
-        $declaration = new csscrush_declaration( $prop, $value, $contextIndex );
+        $declaration = new CssCrush_Declaration( $prop, $value, $contextIndex );
 
         if ( $declaration->isValid ) {
 
             // Manually increment the property name since we're directly updating the _declarations list
-            $this->addProperty( $prop );
-            $this->_declarations[] = $declaration;
+            $this->indexProperty( $prop );
+            $this->declarations[] = $declaration;
             return $declaration;
         }
 
         return false;
     }
 
-    static public function get ( $token ) {
-
-        if ( isset( csscrush::$process->tokens->r[ $token ] ) ) {
-            return csscrush::$process->tokens->r[ $token ];
+    static public function get ( $token )
+    {
+        if ( isset( CssCrush::$process->tokens->r[ $token ] ) ) {
+            return CssCrush::$process->tokens->r[ $token ];
         }
         return null;
     }
 }
-
-
-/**
- *
- * Fixes for aliasing to legacy syntaxes.
- *
- */
-class csscrush_legacySyntax {
-
-    static public $functions = array(
-        'linear-gradient'           => array( __CLASS__, 'linearGradients' ),
-        'linear-repeating-gradient' => array( __CLASS__, 'linearGradients' ),
-        'radial-gradient'           => array( __CLASS__, 'radialGradients' ),
-        'radial-repeating-gradient' => array( __CLASS__, 'radialGradients' ),
-    );
-
-    static public function linearGradients ( $prefixed_copies, $fn_name ) {
-
-        static $angles_new, $angles_old;
-        if ( ! $angles_new ) {
-            $angles = array(
-                'to top' => 'bottom',
-                'to right' => 'left',
-                'to bottom' => 'top',
-                'to left' => 'right',
-                // 'magic' corners.
-                'to top left' => 'bottom right',
-                'to left top' => 'bottom right',
-                'to top right' => 'bottom left',
-                'to right top' => 'bottom left',
-                'to bottom left' => 'top right',
-                'to left bottom' => 'top right',
-                'to bottom right' => 'top left',
-                'to right bottom' => 'top left',
-            );
-            $angles_new = array_keys( $angles );
-            $angles_old = array_values( $angles );
-        }
-
-        // Swap the new 'to' gradient syntax to the old 'from' syntax for the prefixed versions.
-        // 1. Create new paren tokens based on the first prefixed declaration.
-        // 2. Replace the new syntax with the legacy syntax.
-        // 3. Swap in the new tokens on all the prefixed declarations.
-
-        // 1, 2.
-        $patt = '~(?value ) as $m ) {
-            $original_parens[] = $m[1][0];
-            $replacement_parens[] = csscrush::$process->addToken(
-                str_ireplace(
-                    $angles_new,
-                    $angles_old,
-                    csscrush::$process->fetchToken( $m[1][0] )
-                ), 'p' );
-        }
-
-        // 3.
-        foreach ( $prefixed_copies as $prefixed_copy ) {
-            $prefixed_copy->value = str_replace( $original_parens, $replacement_parens, $prefixed_copy->value );
-        }
-    }
-
-    static public function radialGradients ( $prefixed_copies, $fn_name ) {
-
-        // Remove the new 'at' keyword from gradient syntax for legacy implementations.
-        // 1. Create new paren tokens based on the first prefixed declaration.
-        // 2. Replace the new syntax with the legacy syntax.
-        // 3. Swap in the new tokens on all the prefixed declarations.
-
-        // 1, 2.
-        $patt = '~(?value ) as $m ) {
-            $original_parens[] = $m[1][0];
-            $replacement_parens[] = csscrush::$process->addToken(
-                preg_replace(
-                    '~\bat +(top|left|bottom|right|center)\b~i',
-                    '$1',
-                    csscrush::$process->fetchToken( $m[1][0] )
-                ), 'p' );
-        }
-
-        // 3.
-        foreach ( $prefixed_copies as $prefixed_copy ) {
-            $prefixed_copy->value = str_replace( $original_parens, $replacement_parens, $prefixed_copy->value );
-        }
-    }
-}
-
-
-/**
- *
- * Declaration objects.
- *
- */
-class csscrush_declaration {
-
-    public $property;
-    public $canonicalProperty;
-    public $vendor;
-    public $functions;
-    public $value;
-    public $index;
-    public $skip;
-    public $important;
-    public $isValid = true;
-
-    public function __construct ( $prop, $value, $contextIndex = 0 ) {
-
-        $regex = csscrush_regex::$patt;
-
-        // Normalize input. Lowercase the property name.
-        $prop = strtolower( trim( $prop ) );
-        $value = trim( $value );
-
-        // Check the input.
-        if ( $prop === '' || $value === '' || $value === null ) {
-            $this->isValid = false;
-            return;
-        }
-
-        // Test for escape tilde.
-        if ( $skip = strpos( $prop, '~' ) === 0 ) {
-            $prop = substr( $prop, 1 );
-        }
-
-        // Store the canonical property name.
-        // Store the vendor mark if one is present.
-        if ( preg_match( $regex->vendorPrefix, $prop, $vendor ) ) {
-            $canonical_property = $vendor[2];
-            $vendor = $vendor[1];
-        }
-        else {
-            $vendor = null;
-            $canonical_property = $prop;
-        }
-
-        // Check for !important.
-        if ( ( $important = stripos( $value, '!important' ) ) !== false ) {
-            $value = rtrim( substr( $value, 0, $important ) );
-            $important = true;
-        }
-
-        // Ignore declarations with null css values.
-        if ( $value === false || $value === '' ) {
-            $this->isValid = false;
-            return;
-        }
-
-        // Apply custom functions.
-        if ( ! $skip ) {
-            csscrush_function::executeCustomFunctions( $value );
-        }
-
-        // Capture all remaining paren pairs.
-        csscrush::$process->captureParens( $value );
-
-        // Create an index of all regular functions in the value.
-        $functions = array();
-        if ( preg_match_all( $regex->function, $value, $m ) ) {
-            foreach ( $m[2] as $index => $fn_name ) {
-                $functions[ strtolower( $fn_name ) ] = true;
-            }
-        }
-
-        $this->property          = $prop;
-        $this->canonicalProperty = $canonical_property;
-        $this->vendor            = $vendor;
-        $this->functions         = $functions;
-        $this->index             = $contextIndex;
-        $this->value             = $value;
-        $this->skip              = $skip;
-        $this->important         = $important;
-    }
-
-    public function __toString () {
-
-        if ( csscrush::$process->options->minify ) {
-            $whitespace = '';
-        }
-        else {
-            $whitespace = ' ';
-        }
-        $important = $this->important ? "$whitespace!important" : '';
-
-        return "$this->property:$whitespace$this->value$important";
-    }
-
-    public function getFullValue () {
-
-        return csscrush::$process->restoreTokens( $this->value, 'p' );
-    }
-
-}
-
-
-/**
- *
- * Selector objects.
- *
- */
-class csscrush_selector {
-
-    public $value;
-    public $readableValue;
-    public $allowPrefix = true;
-
-    static function makeReadableSelector ( $selector_string ) {
-
-        // Quick test for paren tokens.
-        if ( strpos( $selector_string, '?p' ) !== false ) {
-            $selector_string = csscrush::$process->restoreTokens( $selector_string, 'p' );
-        }
-
-        // Create space around combinators, then normalize whitespace.
-        $selector_string = preg_replace( '#([>+]|~(?!=))#', ' $1 ', $selector_string );
-        $selector_string = csscrush_util::normalizeWhiteSpace( $selector_string );
-
-        // Quick test for string tokens.
-        if ( strpos( $selector_string, '?s' ) !== false ) {
-            $selector_string = csscrush::$process->restoreTokens( $selector_string, 's' );
-        }
-
-        // Quick test for double-colons for backwards compat.
-        if ( strpos( $selector_string, '::' ) !== false ) {
-            $selector_string = preg_replace( '!::(after|before|first-(?:letter|line))!iS', ':$1', $selector_string );
-        }
-
-        return $selector_string;
-    }
-
-    public function __construct ( $raw_selector, $associated_rule = null ) {
-
-        if ( strpos( $raw_selector, '^' ) === 0 ) {
-
-            $raw_selector = ltrim( $raw_selector, "^ \n\r\t" );
-            $this->allowPrefix = false;
-        }
-
-        $this->readableValue = self::makeReadableSelector( $raw_selector );
-        $this->value = $raw_selector;
-    }
-
-    public function __toString () {
-
-        return $this->readableValue;
-    }
-
-    public function appendPseudo ( $pseudo ) {
-
-        // Check to avoid doubling-up
-        if ( ! csscrush_stream::endsWith( $this->readableValue, $pseudo ) ) {
-
-            $this->readableValue .= $pseudo;
-            $this->value .= $pseudo;
-        }
-        return $this->readableValue;
-    }
-}
-
-
-/**
- *
- * Extend argument objects.
- *
- */
-class csscrush_extendArg {
-
-    public $pointer;
-    public $name;
-    public $pseudo;
-
-    public function __construct ( $name ) {
-
-        $this->name = $name;
-
-        if ( ! preg_match( csscrush_regex::$patt->ident, $this->name ) ) {
-
-            // Not a regular name: Some kind of selector so normalize it for later comparison
-            $this->name = csscrush_selector::makeReadableSelector( $this->name );
-
-            // If applying the pseudo on output store
-            if ( substr( $this->name, -1 ) === '!' ) {
-
-                $this->name = rtrim( $this->name, ' !' );
-                if ( preg_match( '!\:\:?[\w-]+$!', $this->name, $m ) ) {
-                    $this->pseudo = $m[0];
-                }
-            }
-        }
-    }
-}
-
diff --git a/lib/Selector.php b/lib/Selector.php
new file mode 100644
index 0000000..1ce4451
--- /dev/null
+++ b/lib/Selector.php
@@ -0,0 +1,64 @@
+restoreTokens( $selector_string, 'p' );
+        }
+
+        // Create space around combinators, then normalize whitespace.
+        $selector_string = preg_replace( '#([>+]|~(?!=))#', ' $1 ', $selector_string );
+        $selector_string = CssCrush_Util::normalizeWhiteSpace( $selector_string );
+
+        // Quick test for string tokens.
+        if ( strpos( $selector_string, '?s' ) !== false ) {
+            $selector_string = CssCrush::$process->restoreTokens( $selector_string, 's' );
+        }
+
+        // Quick test for double-colons for backwards compat.
+        if ( strpos( $selector_string, '::' ) !== false ) {
+            $selector_string = preg_replace( '!::(after|before|first-(?:letter|line))!iS', ':$1', $selector_string );
+        }
+
+        return $selector_string;
+    }
+
+    public function __construct ( $raw_selector, $associated_rule = null )
+    {
+        if ( strpos( $raw_selector, '^' ) === 0 ) {
+
+            $raw_selector = ltrim( $raw_selector, "^ \n\r\t" );
+            $this->allowPrefix = false;
+        }
+
+        $this->readableValue = self::makeReadableSelector( $raw_selector );
+        $this->value = $raw_selector;
+    }
+
+    public function __toString ()
+    {
+        return $this->readableValue;
+    }
+
+    public function appendPseudo ( $pseudo )
+    {
+        // Check to avoid doubling-up
+        if ( ! CssCrush_Stream::endsWith( $this->readableValue, $pseudo ) ) {
+
+            $this->readableValue .= $pseudo;
+            $this->value .= $pseudo;
+        }
+        return $this->readableValue;
+    }
+}
diff --git a/lib/Stream.php b/lib/Stream.php
new file mode 100644
index 0000000..d76cc9f
--- /dev/null
+++ b/lib/Stream.php
@@ -0,0 +1,120 @@
+raw = $str;
+    }
+
+    public function __toString ()
+    {
+        return $this->raw;
+    }
+
+    static public function endsWith ( $haystack, $needle )
+    {
+        return substr( $haystack, -strlen( $needle ) ) === $needle;
+    }
+
+    public function update ( $str )
+    {
+        $this->raw = $str;
+        return $this;
+    }
+
+    public function substr ( $start, $length = null )
+    {
+        if ( is_null( $length ) ) {
+            return substr( $this->raw, $start );
+        }
+        else {
+            return substr( $this->raw, $start, $length );
+        }
+    }
+
+    public function matchAll ( $patt, $preprocess_patt = false )
+    {
+        return CssCrush_Regex::matchAll( $patt, $this->raw, $preprocess_patt );
+    }
+
+    public function replace ( $find, $replacement )
+    {
+        $this->raw = str_replace( $find, $replacement, $this->raw );
+        return $this;
+    }
+
+    public function replaceHash ( $replacements )
+    {
+        if ( $replacements ) {
+            $this->raw = str_replace(
+                array_keys( $replacements ),
+                array_values( $replacements ),
+                $this->raw );
+        }
+        return $this;
+    }
+
+    public function pregReplace ( $patt, $replacement )
+    {
+        $this->raw = preg_replace( $patt, $replacement, $this->raw );
+        return $this;
+    }
+
+    public function pregReplaceCallback ( $patt, $callback )
+    {
+        $this->raw = preg_replace_callback( $patt, $callback, $this->raw );
+        return $this;
+    }
+
+    public function pregReplaceHash ( $replacements )
+    {
+        if ( $replacements ) {
+            $this->raw = preg_replace(
+                array_keys( $replacements ),
+                array_values( $replacements ),
+                $this->raw );
+        }
+        return $this;
+    }
+
+    public function append ( $append )
+    {
+        $this->raw .= $append;
+        return $this;
+    }
+
+    public function prepend ( $prepend )
+    {
+        $this->raw = $prepend . $this->raw;
+        return $this;
+    }
+
+    public function splice ( $replacement, $offset, $length = null )
+    {
+        $this->raw = substr_replace( $this->raw, $replacement, $offset, $length );
+        return $this;
+    }
+
+    public function trim ()
+    {
+        $this->raw = trim( $this->raw );
+        return $this;
+    }
+
+    public function rTrim ()
+    {
+        $this->raw = rtrim( $this->raw );
+        return $this;
+    }
+
+    public function lTrim ()
+    {
+        $this->raw = ltrim( $this->raw );
+        return $this;
+    }
+}
diff --git a/lib/Url.php b/lib/Url.php
new file mode 100644
index 0000000..afe58db
--- /dev/null
+++ b/lib/Url.php
@@ -0,0 +1,129 @@
+sToken, $raw_value ) ) {
+            $this->value = trim( $process->fetchToken( $raw_value ), '\'"' );
+            $process->releaseToken( $raw_value );
+        }
+        else {
+            $this->value = $raw_value;
+        }
+
+        $this->evaluate();
+        $this->label = $process->addToken( $this, 'u' );
+    }
+
+    public function __toString ()
+    {
+        $quote = '';
+        if ( preg_match( '![()*]!', $this->value ) || 'data' === $this->protocol ) {
+            $quote = '"';
+        }
+        return "url(http://github.com/$quote$this-%3Evalue$quote)";
+    }
+
+    static public function get ( $token )
+    {
+        return CssCrush::$process->tokens->u[ $token ];
+    }
+
+    public function evaluate ()
+    {
+        $leading_variable = strpos( $this->value, '$(' ) === 0;
+
+        if ( preg_match( '!^([a-z]+)\:!i', $this->value, $m ) ) {
+            $this->protocol = strtolower( $m[1] );
+        }
+        else {
+            // Normalize './' led paths.
+            $this->value = preg_replace( '!^\.\/+!i', '', $this->value );
+            if ( $this->value !== '' && $this->value[0] === '/' ) {
+                $this->isRooted = true;
+            }
+            elseif ( ! $leading_variable ) {
+                $this->isRelative = true;
+            }
+            // Normalize slashes.
+            $this->value = rtrim( preg_replace( '![\\\\/]+!', '/', $this->value ), '/' );
+        }
+        return $this;
+    }
+
+    public function toData ()
+    {
+        if ( $this->isRooted ) {
+            $file = CssCrush::$process->docRoot . $this->value;
+        }
+        else {
+            $file = CssCrush::$process->input->dir . "/$this->value";
+        }
+
+        // File not found.
+        if ( ! file_exists( $file ) ) {
+            return;
+        }
+
+        $file_ext = pathinfo( $file, PATHINFO_EXTENSION );
+
+        // Only allow certain extensions
+        static $allowed_file_extensions = array(
+            'woff' => 'application/x-font-woff;charset=utf-8',
+            'ttf'  => 'font/truetype;charset=utf-8',
+            'svg'  => 'image/svg+xml',
+            'svgz' => 'image/svg+xml',
+            'gif'  => 'image/gif',
+            'jpeg' => 'image/jpg',
+            'jpg'  => 'image/jpg',
+            'png'  => 'image/png',
+        );
+
+        if ( ! isset( $allowed_file_extensions[ $file_ext ] ) ) {
+            return;
+        }
+
+        $mime_type = $allowed_file_extensions[ $file_ext ];
+        $base64 = base64_encode( file_get_contents( $file ) );
+        $this->value = "data:$mime_type;base64,$base64";
+        $this->protocol = 'data';
+    }
+
+    public function prepend ( $path_fragment )
+    {
+        $this->value = $path_fragment . $this->value;
+    }
+
+    public function resolveRootedPath ()
+    {
+        $process = CssCrush::$process;
+
+        if ( ! file_exists ( $process->docRoot . $this->value ) ) {
+            return false;
+        }
+
+        // Move upwards '..' by the number of slashes in baseURL to get a relative path.
+        $this->value = str_repeat( '../', substr_count( $process->input->dirUrl, '/' ) ) .
+            substr( $this->value, 1 );
+    }
+
+    public function simplify ()
+    {
+        $this->value = CssCrush_Util::simplifyPath( $this->value );
+    }
+}
diff --git a/lib/Util.php b/lib/Util.php
index 2def9d4..6eb0b74 100644
--- a/lib/Util.php
+++ b/lib/Util.php
@@ -4,11 +4,11 @@
  *  General utilities.
  *
  */
-class csscrush_util {
-
+class CssCrush_Util
+{
     // Create html attribute string from array.
-    static public function htmlAttributes ( array $attributes ) {
-
+    static public function htmlAttributes ( array $attributes )
+    {
         $attr_string = '';
         foreach ( $attributes as $name => $value ) {
             $value = htmlspecialchars( $value, ENT_COMPAT, 'UTF-8', false );
@@ -17,8 +17,8 @@ static public function htmlAttributes ( array $attributes ) {
         return $attr_string;
     }
 
-    static public function normalizePath ( $path, $strip_drive_letter = false ) {
-
+    static public function normalizePath ( $path, $strip_drive_letter = false )
+    {
         if ( $strip_drive_letter ) {
             $path = preg_replace( '!^[a-z]\:!i', '', $path );
         }
@@ -31,11 +31,11 @@ static public function normalizePath ( $path, $strip_drive_letter = false ) {
             $path = substr( $path, 2 );
         }
 
-        return csscrush_util::simplifyPath( $path );
+        return CssCrush_Util::simplifyPath( $path );
     }
 
-    static public function simplifyPath ( $path ) {
-
+    static public function simplifyPath ( $path )
+    {
         // Reduce redundant path segments (issue #32):
         // e.g 'foo/../bar' => 'bar'
         $patt = '~[^/.]+/\.\./~S';
@@ -45,10 +45,10 @@ static public function simplifyPath ( $path ) {
         return $path;
     }
 
-    static public function find () {
-
+    static public function find ()
+    {
         foreach ( func_get_args() as $file ) {
-            $file_path = csscrush::$config->location . '/' . $file;
+            $file_path = CssCrush::$config->location . '/' . $file;
             if ( file_exists( $file_path ) ) {
                 return $file_path;
             }
@@ -56,13 +56,13 @@ static public function find () {
         return false;
     }
 
-    static public function stripCommentTokens ( $str ) {
-
-        return preg_replace( csscrush_regex::$patt->cToken, '', $str );
+    static public function stripCommentTokens ( $str )
+    {
+        return preg_replace( CssCrush_Regex::$patt->cToken, '', $str );
     }
 
-    static public function normalizeWhiteSpace ( $str ) {
-
+    static public function normalizeWhiteSpace ( $str )
+    {
         $replacements = array(
             // Convert all whitespace sequences to a single space.
             '!\s+!S' => ' ',
@@ -75,8 +75,8 @@ static public function normalizeWhiteSpace ( $str ) {
             array_keys( $replacements ), array_values( $replacements ), $str );
     }
 
-    static public function splitDelimList ( $str, $delim = ',', $trim = true ) {
-
+    static public function splitDelimList ( $str, $delim = ',', $trim = true )
+    {
         $do_preg_split = strlen( $delim ) > 1 ? true : false;
 
         if ( ! $do_preg_split && strpos( $str, $delim ) === false ) {
@@ -88,7 +88,7 @@ static public function splitDelimList ( $str, $delim = ',', $trim = true ) {
 
         if ( strpos( $str, '(' ) !== false ) {
             $match_count
-                = preg_match_all( csscrush_regex::$patt->balancedParens, $str, $matches );
+                = preg_match_all( CssCrush_Regex::$patt->balancedParens, $str, $matches );
         }
         else {
             $match_count = 0;
@@ -122,11 +122,11 @@ static public function splitDelimList ( $str, $delim = ',', $trim = true ) {
         return $list;
     }
 
-    static public function getLinkBetweenDirs ( $dir1, $dir2 ) {
-
+    static public function getLinkBetweenDirs ( $dir1, $dir2 )
+    {
         // Normalise the paths.
-        $dir1 = trim( csscrush_util::normalizePath( $dir1, true ), '/' );
-        $dir2 = trim( csscrush_util::normalizePath( $dir2, true ), '/' );
+        $dir1 = trim( CssCrush_Util::normalizePath( $dir1, true ), '/' );
+        $dir2 = trim( CssCrush_Util::normalizePath( $dir2, true ), '/' );
 
         // The link between.
         $link = '';
@@ -150,377 +150,3 @@ static public function getLinkBetweenDirs ( $dir1, $dir2 ) {
         return $link !== '' ? rtrim( $link, '/' ) . '/' : '';
     }
 }
-
-
-/**
- *
- *  Balanced bracket matching on the main stream.
- *
- */
-class csscrush_balancedMatch {
-
-    public function __construct ( csscrush_stream $stream, $offset, $brackets = '{}' ) {
-
-        $this->stream = $stream;
-        $this->offset = $offset;
-        $this->match = null;
-        $this->length = 0;
-
-        list( $opener, $closer ) = str_split( $brackets, 1 );
-
-        if ( strpos( $stream->raw, $opener, $this->offset ) === false ) {
-            return;
-        }
-
-        if ( substr_count( $stream->raw, $opener ) !== substr_count( $stream->raw, $closer ) ) {
-            $sample = substr( $stream->raw, $this->offset, 25 );
-            trigger_error( __METHOD__ . ": Unmatched token near '$sample'.\n", E_USER_WARNING );
-            return;
-        }
-
-        $patt = $opener === '{' ?
-            csscrush_regex::$patt->balancedCurlies : csscrush_regex::$patt->balancedParens;
-
-        if ( preg_match( $patt, $stream->raw, $m, PREG_OFFSET_CAPTURE, $this->offset ) ) {
-
-            $this->match = $m;
-            $this->matchLength = strlen( $m[0][0] );
-            $this->matchStart = $m[0][1];
-            $this->matchEnd = $this->matchStart + $this->matchLength;
-            $this->length = $this->matchEnd - $this->offset;
-        }
-        else {
-            trigger_error( __METHOD__ . ": Could not match '$opener'. Exiting.\n", E_USER_WARNING );
-        }
-    }
-
-    public function inside () {
-
-        return $this->match[1][0];
-    }
-
-    public function whole () {
-
-        return substr( $this->stream->raw, $this->offset, $this->length );
-    }
-
-    public function replace ( $replacement ) {
-
-        $this->stream->splice( $replacement, $this->offset, $this->length );
-    }
-
-    public function unWrap () {
-
-        $this->stream->splice( $this->inside(), $this->offset, $this->length );
-    }
-
-    public function nextIndexOf ( $needle ) {
-
-        return strpos( $this->stream->raw, $needle, $this->offset );
-    }
-}
-
-
-/**
- *
- *  URL tokens.
- *
- */
-class csscrush_url {
-
-    public $protocol;
-    public $isRelative;
-    public $isRooted;
-    public $convertToData;
-    public $value;
-    public $label;
-
-    public function __construct ( $raw_value, $convert_to_data = false ) {
-
-        $regex = csscrush_regex::$patt;
-        $process = csscrush::$process;
-
-        if ( preg_match( $regex->sToken, $raw_value ) ) {
-            $this->value = trim( $process->fetchToken( $raw_value ), '\'"' );
-            $process->releaseToken( $raw_value );
-        }
-        else {
-            $this->value = $raw_value;
-        }
-
-        $this->evaluate();
-        $this->label = $process->addToken( $this, 'u' );
-    }
-
-    public function __toString () {
-        $quote = '';
-        if ( preg_match( '![()*]!', $this->value ) || 'data' === $this->protocol ) {
-            $quote = '"';
-        }
-        return "url(http://github.com/$quote$this-%3Evalue$quote)";
-    }
-
-    static public function get ( $token ) {
-        return csscrush::$process->tokens->u[ $token ];
-    }
-
-    public function evaluate () {
-
-        $leading_variable = strpos( $this->value, '$(' ) === 0;
-
-        if ( preg_match( '!^([a-z]+)\:!i', $this->value, $m ) ) {
-            $this->protocol = strtolower( $m[1] );
-        }
-        else {
-            // Normalize './' led paths.
-            $this->value = preg_replace( '!^\.\/+!i', '', $this->value );
-            if ( $this->value !== '' && $this->value[0] === '/' ) {
-                $this->isRooted = true;
-            }
-            elseif ( ! $leading_variable ) {
-                $this->isRelative = true;
-            }
-            // Normalize slashes.
-            $this->value = rtrim( preg_replace( '![\\\\/]+!', '/', $this->value ), '/' );
-        }
-        return $this;
-    }
-
-    public function toData () {
-
-        if ( $this->isRooted ) {
-            $file = csscrush::$process->docRoot . $this->value;
-        }
-        else {
-            $file = csscrush::$process->input->dir . "/$this->value";
-        }
-
-        // File not found.
-        if ( ! file_exists( $file ) ) {
-            return;
-        }
-
-        $file_ext = pathinfo( $file, PATHINFO_EXTENSION );
-
-        // Only allow certain extensions
-        static $allowed_file_extensions = array(
-            'woff' => 'application/x-font-woff;charset=utf-8',
-            'ttf'  => 'font/truetype;charset=utf-8',
-            'svg'  => 'image/svg+xml',
-            'svgz' => 'image/svg+xml',
-            'gif'  => 'image/gif',
-            'jpeg' => 'image/jpg',
-            'jpg'  => 'image/jpg',
-            'png'  => 'image/png',
-        );
-
-        if ( ! isset( $allowed_file_extensions[ $file_ext ] ) ) {
-            return;
-        }
-
-        $mime_type = $allowed_file_extensions[ $file_ext ];
-        $base64 = base64_encode( file_get_contents( $file ) );
-        $this->value = "data:$mime_type;base64,$base64";
-        $this->protocol = 'data';
-    }
-
-    public function prepend ( $path_fragment ) {
-        $this->value = $path_fragment . $this->value;
-    }
-
-    public function resolveRootedPath () {
-
-        $process = csscrush::$process;
-
-        if ( ! file_exists ( $process->docRoot . $this->value ) ) {
-            return false;
-        }
-
-        // Move upwards '..' by the number of slashes in baseURL to get a relative path.
-        $this->value = str_repeat( '../', substr_count( $process->input->dirUrl, '/' ) ) .
-            substr( $this->value, 1 );
-    }
-
-    public function simplify () {
-
-        $this->value = csscrush_util::simplifyPath( $this->value );
-    }
-}
-
-
-/**
- *
- *  Stream sugar.
- *
- */
-class csscrush_stream {
-
-    public function __construct ( $str ) {
-        $this->raw = $str;
-    }
-
-    public function __toString () {
-        return $this->raw;
-    }
-
-    static public function endsWith ( $haystack, $needle ) {
-
-        return substr( $haystack, -strlen( $needle ) ) === $needle;
-    }
-
-    public function update ( $str ) {
-        $this->raw = $str;
-        return $this;
-    }
-
-    public function substr ( $start, $length = null ) {
-        if ( is_null( $length ) ) {
-            return substr( $this->raw, $start );
-        }
-        else {
-            return substr( $this->raw, $start, $length );
-        }
-    }
-
-    public function matchAll ( $patt, $preprocess_patt = false ) {
-        return csscrush_regex::matchAll( $patt, $this->raw, $preprocess_patt );
-    }
-
-    public function replace ( $find, $replacement ) {
-        $this->raw = str_replace( $find, $replacement, $this->raw );
-        return $this;
-    }
-
-    public function replaceHash ( $replacements ) {
-        if ( $replacements ) {
-            $this->raw = str_replace(
-                array_keys( $replacements ),
-                array_values( $replacements ),
-                $this->raw );
-        }
-        return $this;
-    }
-
-    public function pregReplace ( $patt, $replacement ) {
-        $this->raw = preg_replace( $patt, $replacement, $this->raw );
-        return $this;
-    }
-
-    public function pregReplaceCallback ( $patt, $callback ) {
-        $this->raw = preg_replace_callback( $patt, $callback, $this->raw );
-        return $this;
-    }
-
-    public function pregReplaceHash ( $replacements ) {
-        if ( $replacements ) {
-            $this->raw = preg_replace(
-                array_keys( $replacements ),
-                array_values( $replacements ),
-                $this->raw );
-        }
-        return $this;
-    }
-
-    public function append ( $append ) {
-        $this->raw .= $append;
-        return $this;
-    }
-
-    public function prepend ( $prepend ) {
-        $this->raw = $prepend . $this->raw;
-        return $this;
-    }
-
-    public function splice ( $replacement, $offset, $length = null ) {
-        $this->raw = substr_replace( $this->raw, $replacement, $offset, $length );
-        return $this;
-    }
-
-    public function trim () {
-        $this->raw = trim( $this->raw );
-        return $this;
-    }
-
-    public function rTrim () {
-        $this->raw = rtrim( $this->raw );
-        return $this;
-    }
-
-    public function lTrim () {
-        $this->raw = ltrim( $this->raw );
-        return $this;
-    }
-}
-
-
-/**
- *
- *  Version string.
- *
- */
-class csscrush_version {
-
-    public $major = 0;
-    public $minor = 0;
-    public $revision = 0;
-    public $extra;
-
-    public function __construct ( $version_string ) {
-
-        if ( ( $hyphen_pos = strpos( $version_string, '-' ) ) !== false ) {
-            $this->extra = substr( $version_string, $hyphen_pos + 1 );
-            $version_string = substr( $version_string, 0, $hyphen_pos );
-        }
-
-        $parts = explode( '.', $version_string );
-
-        if ( ( $major = array_shift( $parts ) ) !== null ) {
-            $this->major = (int) $major;
-        }
-        if ( ( $minor = array_shift( $parts ) ) !== null ) {
-            $this->minor = (int) $minor;
-        }
-        if ( ( $revision = array_shift( $parts ) ) !== null ) {
-            $this->revision = (int) $revision;
-        }
-    }
-
-    public function __toString () {
-
-        $out = (string) $this->major;
-
-        if ( ! is_null( $this->minor ) ) {
-            $out .= ".$this->minor";
-        }
-        if ( ! is_null( $this->revision ) ) {
-            $out .= ".$this->revision";
-        }
-        if ( ! is_null( $this->extra ) ) {
-            $out .= "-$this->extra";
-        }
-
-        return $out;
-    }
-
-    public function compare ( $version_string ) {
-
-        $LESS  = -1;
-        $MORE  = 1;
-        $EQUAL = 0;
-
-        $test = new csscrush_version( $version_string );
-
-        foreach ( array( 'major', 'minor', 'revision' ) as $level ) {
-
-            if ( $this->{ $level } < $test->{ $level } ) {
-                return $LESS;
-            }
-            elseif ( $this->{ $level } > $test->{ $level } ) {
-                return $MORE;
-            }
-        }
-
-        return $EQUAL;
-    }
-}
-
diff --git a/lib/Version.php b/lib/Version.php
new file mode 100644
index 0000000..5e845b5
--- /dev/null
+++ b/lib/Version.php
@@ -0,0 +1,71 @@
+extra = substr( $version_string, $hyphen_pos + 1 );
+            $version_string = substr( $version_string, 0, $hyphen_pos );
+        }
+
+        $parts = explode( '.', $version_string );
+
+        if ( ( $major = array_shift( $parts ) ) !== null ) {
+            $this->major = (int) $major;
+        }
+        if ( ( $minor = array_shift( $parts ) ) !== null ) {
+            $this->minor = (int) $minor;
+        }
+        if ( ( $revision = array_shift( $parts ) ) !== null ) {
+            $this->revision = (int) $revision;
+        }
+    }
+
+    public function __toString ()
+    {
+        $out = (string) $this->major;
+
+        if ( ! is_null( $this->minor ) ) {
+            $out .= ".$this->minor";
+        }
+        if ( ! is_null( $this->revision ) ) {
+            $out .= ".$this->revision";
+        }
+        if ( ! is_null( $this->extra ) ) {
+            $out .= "-$this->extra";
+        }
+
+        return $out;
+    }
+
+    public function compare ( $version_string )
+    {
+        $LESS  = -1;
+        $MORE  = 1;
+        $EQUAL = 0;
+
+        $test = new CssCrush_Version( $version_string );
+
+        foreach ( array( 'major', 'minor', 'revision' ) as $level ) {
+
+            if ( $this->{ $level } < $test->{ $level } ) {
+                return $LESS;
+            }
+            elseif ( $this->{ $level } > $test->{ $level } ) {
+                return $MORE;
+            }
+        }
+
+        return $EQUAL;
+    }
+}
diff --git a/plugins/hocus-pocus.php b/plugins/hocus-pocus.php
index b8d0f19..85fd9ab 100644
--- a/plugins/hocus-pocus.php
+++ b/plugins/hocus-pocus.php
@@ -13,17 +13,17 @@
  * 
  */
 
-csscrush_plugin::register( 'hocus-pocus', array(
+CssCrush_Plugin::register( 'hocus-pocus', array(
     'enable' => 'csscrush__enable_hocus_pocus',
     'disable' => 'csscrush__disable_hocus_pocus',
 ));
 
 function csscrush__enable_hocus_pocus () {
-    csscrush::$config->selectorAliases[ 'hocus' ] = ':any(:hover,:focus)';
-    csscrush::$config->selectorAliases[ 'pocus' ] = ':any(:hover,:focus,:active)';
+    CssCrush::$config->selectorAliases[ 'hocus' ] = ':any(:hover,:focus)';
+    CssCrush::$config->selectorAliases[ 'pocus' ] = ':any(:hover,:focus,:active)';
 }
 
 function csscrush__disable_hocus_pocus () {
-    unset( csscrush::$config->selectorAliases[ 'hocus' ] );
-    unset( csscrush::$config->selectorAliases[ 'pocus' ] );
+    unset( CssCrush::$config->selectorAliases[ 'hocus' ] );
+    unset( CssCrush::$config->selectorAliases[ 'pocus' ] );
 }
diff --git a/plugins/hsl-to-hex.php b/plugins/hsl-to-hex.php
index f263785..807185c 100644
--- a/plugins/hsl-to-hex.php
+++ b/plugins/hsl-to-hex.php
@@ -10,28 +10,28 @@
  *    color: #6abf40
  */
 
-csscrush_plugin::register( 'hsl-to-hex', array(
+CssCrush_Plugin::register( 'hsl-to-hex', array(
     'enable' => 'csscrush__enable_hsl_to_hex',
     'disable' => 'csscrush__disable_hsl_to_hex',
 ));
 
 function csscrush__enable_hsl_to_hex () {
-    csscrush_hook::add( 'rule_postalias', 'csscrush__hsl_to_hex' );
+    CssCrush_Hook::add( 'rule_postalias', 'csscrush__hsl_to_hex' );
 }
 
 function csscrush__disable_hsl_to_hex () {
-    csscrush_hook::remove( 'rule_postalias', 'csscrush__hsl_to_hex' );
+    CssCrush_Hook::remove( 'rule_postalias', 'csscrush__hsl_to_hex' );
 }
 
-function csscrush__hsl_to_hex ( csscrush_rule $rule ) {
+function csscrush__hsl_to_hex ( CssCrush_Rule $rule ) {
 
     foreach ( $rule as &$declaration ) {
 
         if ( ! $declaration->skip && isset( $declaration->functions[ 'hsl' ] ) ) {
             while ( preg_match( '!hsl(\?p\d+\?)!', $declaration->value, $m ) ) {
                 $token = $m[1];
-                $color = new csscrush_color( 'hsl' . csscrush::$process->fetchToken( $token ) );
-                csscrush::$process->releaseToken( $token );
+                $color = new CssCrush_Color( 'hsl' . CssCrush::$process->fetchToken( $token ) );
+                CssCrush::$process->releaseToken( $token );
                 $declaration->value = str_replace( $m[0], $color->getHex(), $declaration->value );
             }
         }
diff --git a/plugins/ie-clip.php b/plugins/ie-clip.php
index a09c049..74bbda7 100644
--- a/plugins/ie-clip.php
+++ b/plugins/ie-clip.php
@@ -10,22 +10,22 @@
  *     *clip: rect(1px 1px 1px 1px);
  */
 
-csscrush_plugin::register( 'ie-clip', array(
+CssCrush_Plugin::register( 'ie-clip', array(
     'enable' => 'csscrush__enable_ie_clip',
     'disable' => 'csscrush__disable_ie_clip',
 ));
 
 function csscrush__enable_ie_clip () {
-    csscrush_hook::add( 'rule_postalias', 'csscrush__ie_clip' );
+    CssCrush_Hook::add( 'rule_postalias', 'csscrush__ie_clip' );
 }
 
 function csscrush__disable_ie_clip () {
-    csscrush_hook::remove( 'rule_postalias', 'csscrush__ie_clip' );
+    CssCrush_Hook::remove( 'rule_postalias', 'csscrush__ie_clip' );
 }
 
-function csscrush__ie_clip ( csscrush_rule $rule ) {
+function csscrush__ie_clip ( CssCrush_Rule $rule ) {
 
-    // Assume it's been dealt with if the property occurs more than once 
+    // Assume it's been dealt with if the property occurs more than once.
     if ( $rule->propertyCount( 'clip' ) !== 1 ) {
         return;
     }
@@ -38,7 +38,7 @@ function csscrush__ie_clip ( csscrush_rule $rule ) {
         ) {
             continue;
         }
-        $new_set[] = new csscrush_declaration( '*clip', str_replace( ',', ' ', $declaration->getFullValue() ) );
+        $new_set[] = new CssCrush_Declaration( '*clip', str_replace( ',', ' ', $declaration->getFullValue() ) );
     }
-    $rule->declarations = $new_set;
+    $rule->setDeclarations( $new_set );
 }
diff --git a/plugins/ie-filter.php b/plugins/ie-filter.php
index 53033a4..e01befa 100644
--- a/plugins/ie-filter.php
+++ b/plugins/ie-filter.php
@@ -16,20 +16,20 @@
  *     zoom: 1;
  */
 
-csscrush_plugin::register( 'ie-filter', array(
+CssCrush_Plugin::register( 'ie-filter', array(
     'enable' => 'csscrush__enable_ie_filter',
     'disable' => 'csscrush__disable_ie_filter',
 ));
 
 function csscrush__enable_ie_filter () {
-    csscrush_hook::add( 'rule_postalias', 'csscrush__ie_filter' );
+    CssCrush_Hook::add( 'rule_postalias', 'csscrush__ie_filter' );
 }
 
 function csscrush__disable_ie_filter () {
-    csscrush_hook::remove( 'rule_postalias', 'csscrush__ie_filter' );
+    CssCrush_Hook::remove( 'rule_postalias', 'csscrush__ie_filter' );
 }
 
-function csscrush__ie_filter ( csscrush_rule $rule ) {
+function csscrush__ie_filter ( CssCrush_Rule $rule ) {
 
     if ( $rule->propertyCount( '-ms-filter' ) < 1 ) {
         return;
@@ -56,12 +56,12 @@ function csscrush__ie_filter ( csscrush_rule $rule ) {
         $declaration->value = implode( ',', $list );
         if ( ! $rule->propertyCount( 'zoom' ) ) {
             // Filters need hasLayout
-            $new_set[] = new csscrush_declaration( 'zoom', 1 );
+            $new_set[] = new CssCrush_Declaration( 'zoom', 1 );
         }
         // Quoted version for -ms-filter IE >= 8
-        $new_set[] = new csscrush_declaration( '-ms-filter', "\"$declaration->value\"" );
+        $new_set[] = new CssCrush_Declaration( '-ms-filter', "\"$declaration->value\"" );
         // Star escaped property for IE < 8
-        $new_set[] = new csscrush_declaration( '*filter', $declaration->value );
+        $new_set[] = new CssCrush_Declaration( '*filter', $declaration->value );
     }
-    $rule->declarations = $new_set;
+    $rule->setDeclarations( $new_set );
 }
diff --git a/plugins/ie-inline-block.php b/plugins/ie-inline-block.php
index 4004dcb..e0ba25d 100644
--- a/plugins/ie-inline-block.php
+++ b/plugins/ie-inline-block.php
@@ -1,46 +1,47 @@
  'csscrush__enable_ie_inline_block',
     'disable' => 'csscrush__disable_ie_inline_block',
 ));
 
 function csscrush__enable_ie_inline_block () {
-    csscrush_hook::add( 'rule_postalias', 'csscrush__ie_inline_block' );
+    CssCrush_Hook::add( 'rule_postalias', 'csscrush__ie_inline_block' );
 }
 
 function csscrush__disable_ie_inline_block () {
-    csscrush_hook::remove( 'rule_postalias', 'csscrush__ie_inline_block' );
+    CssCrush_Hook::remove( 'rule_postalias', 'csscrush__ie_inline_block' );
 }
 
-function csscrush__ie_inline_block ( csscrush_rule $rule ) {
+function csscrush__ie_inline_block ( CssCrush_Rule $rule ) {
 
     if ( $rule->propertyCount( 'display' ) < 1 ) {
         return;
     }
-    $new_set = array();
+
+    $stack = array();
     foreach ( $rule as $declaration ) {
-        $new_set[] = $declaration;
+        $stack[] = $declaration;
         $is_display = $declaration->property === 'display';
-        if ( 
-            $declaration->skip || 
-            ! $is_display || 
+        if (
+            $declaration->skip ||
+            ! $is_display ||
             $is_display && $declaration->value !== 'inline-block' ) {
             continue;
         }
-        $new_set[] = new csscrush_declaration( '*display', 'inline' );
-        $new_set[] = new csscrush_declaration( '*zoom', 1 );
+        $stack[] = new CssCrush_Declaration( '*display', 'inline' );
+        $stack[] = new CssCrush_Declaration( '*zoom', 1 );
     }
-    $rule->declarations = $new_set;
+    $rule->setDeclarations( $stack );
 }
diff --git a/plugins/ie-min-height.php b/plugins/ie-min-height.php
index 6f15416..146a6e1 100644
--- a/plugins/ie-min-height.php
+++ b/plugins/ie-min-height.php
@@ -10,20 +10,20 @@
  *     _height: 100px;
  */
 
-csscrush_plugin::register( 'ie-min-height', array(
+CssCrush_Plugin::register( 'ie-min-height', array(
     'enable' => 'csscrush__enable_ie_min_height',
     'disable' => 'csscrush__disable_ie_min_height',
 ));
 
 function csscrush__enable_ie_min_height () {
-    csscrush_hook::add( 'rule_postalias', 'csscrush__ie_min_height' );
+    CssCrush_Hook::add( 'rule_postalias', 'csscrush__ie_min_height' );
 }
 
 function csscrush__disable_ie_min_height () {
-    csscrush_hook::remove( 'rule_postalias', 'csscrush__ie_min_height' );
+    CssCrush_Hook::remove( 'rule_postalias', 'csscrush__ie_min_height' );
 }
 
-function csscrush__ie_min_height ( csscrush_rule $rule ) {
+function csscrush__ie_min_height ( CssCrush_Rule $rule ) {
 
     if ( $rule->propertyCount( 'min-height' ) < 1 ) {
         return;
@@ -36,7 +36,7 @@ function csscrush__ie_min_height ( csscrush_rule $rule ) {
             $declaration->property !== 'min-height' ) {
             continue;
         }
-        $new_set[] = new csscrush_declaration( '_height', $declaration->value );
+        $new_set[] = new CssCrush_Declaration( '_height', $declaration->value );
     }
-    $rule->declarations = $new_set;
+    $rule->setDeclarations( $new_set );
 }
diff --git a/plugins/ie-opacity.php b/plugins/ie-opacity.php
index 05dc3db..10a8f91 100755
--- a/plugins/ie-opacity.php
+++ b/plugins/ie-opacity.php
@@ -12,20 +12,20 @@
  *     zoom: 1;
  */
 
-csscrush_plugin::register( 'ie-opacity', array(
+CssCrush_Plugin::register( 'ie-opacity', array(
     'enable' => 'csscrush__enable_ie_opacity',
     'disable' => 'csscrush__disable_ie_opacity',
 ));
 
 function csscrush__enable_ie_opacity () {
-    csscrush_hook::add( 'rule_postalias', 'csscrush__ie_opacity' );
+    CssCrush_Hook::add( 'rule_postalias', 'csscrush__ie_opacity' );
 }
 
 function csscrush__disable_ie_opacity () {
-    csscrush_hook::remove( 'rule_postalias', 'csscrush__ie_opacity' );
+    CssCrush_Hook::remove( 'rule_postalias', 'csscrush__ie_opacity' );
 }
 
-function csscrush__ie_opacity ( csscrush_rule $rule ) {
+function csscrush__ie_opacity ( CssCrush_Rule $rule ) {
 
     if ( $rule->propertyCount( 'opacity' ) < 1 ) {
         return;
@@ -45,11 +45,11 @@ function csscrush__ie_opacity ( csscrush_rule $rule ) {
 
         if ( ! $rule->propertyCount( 'zoom' ) ) {
             // Filters need hasLayout
-            $new_set[] = new csscrush_declaration( 'zoom', 1 );
+            $new_set[] = new CssCrush_Declaration( 'zoom', 1 );
         }
         $value = "alpha(opacity=$opacity)";
-        $new_set[] = new csscrush_declaration( '-ms-filter', "\"$value\"" );
-        $new_set[] = new csscrush_declaration( '*filter', $value );
+        $new_set[] = new CssCrush_Declaration( '-ms-filter', "\"$value\"" );
+        $new_set[] = new CssCrush_Declaration( '*filter', $value );
     }
-    $rule->declarations = $new_set;
+    $rule->setDeclarations( $new_set );
 }
diff --git a/plugins/initial.php b/plugins/initial.php
index ff17648..fae630e 100644
--- a/plugins/initial.php
+++ b/plugins/initial.php
@@ -15,36 +15,36 @@
  * 
  */
 
-csscrush_plugin::register( 'initial', array(
+CssCrush_Plugin::register( 'initial', array(
     'enable' => 'csscrush__enable_initial',
     'disable' => 'csscrush__disable_initial',
 ));
 
 function csscrush__enable_initial () {
-    csscrush_hook::add( 'rule_prealias', 'csscrush__initial' );
+    CssCrush_Hook::add( 'rule_prealias', 'csscrush__initial' );
 }
 
 function csscrush__disable_initial () {
-    csscrush_hook::remove( 'rule_prealias', 'csscrush__initial' );
+    CssCrush_Hook::remove( 'rule_prealias', 'csscrush__initial' );
 }
 
-function csscrush__initial ( csscrush_rule $rule ) {
+function csscrush__initial ( CssCrush_Rule $rule ) {
 
-    static $initialValues = null;
-    if ( ! $initialValues ) {
-        if ( ! ( $initialValues = @parse_ini_file( CssCrush::$config->location . '/misc/initial-values.ini' ) ) ) {
+    static $initial_values;
+    if ( ! $initial_values ) {
+        if ( ! ( $initial_values = @parse_ini_file( CssCrush::$config->location . '/misc/initial-values.ini' ) ) ) {
             trigger_error( __METHOD__ . ": Initial keywords file could not be parsed.\n", E_USER_NOTICE );
             return;
         }
     }
 
     foreach ( $rule as &$declaration ) {
-        if ( !$declaration->skip && 'initial' === $declaration->value ) {
-            if ( isset( $initialValues[ $declaration->property ] ) ) {
-                $declaration->value = $initialValues[ $declaration->property ];
+        if ( ! $declaration->skip && 'initial' === $declaration->value ) {
+            if ( isset( $initial_values[ $declaration->property ] ) ) {
+                $declaration->value = $initial_values[ $declaration->property ];
             }
             else {
-                // Fallback to 'inherit'
+                // Fallback to 'inherit'.
                 $declaration->value = 'inherit';
             }
         }
diff --git a/plugins/legacy-flexbox.php b/plugins/legacy-flexbox.php
new file mode 100644
index 0000000..7ac669d
--- /dev/null
+++ b/plugins/legacy-flexbox.php
@@ -0,0 +1,376 @@
+ 'csscrush__enable_legacy_flexbox',
+    'disable' => 'csscrush__disable_legacy_flexbox',
+));
+
+function csscrush__enable_legacy_flexbox () {
+    CssCrush_Hook::add( 'rule_prealias', 'csscrush__legacy_flexbox' );
+}
+
+function csscrush__disable_legacy_flexbox () {
+    CssCrush_Hook::remove( 'rule_prealias', 'csscrush__legacy_flexbox' );
+}
+
+function csscrush__legacy_flexbox ( CssCrush_Rule $rule ) {
+
+    static $flex_related_props = array(
+        'align-items' => true,
+        'flex' => true,
+        'flex-direction' => true,
+        'flex-flow' => true,
+        'flex-grow' => true,
+        'flex-wrap' => true,
+        'justify-content' => true,
+        'order' => true,
+
+        // The following properties have no legacy equivalent:
+        //  - align-content
+        //  - align-self
+        //  - flex-shrink
+        //  - flex-basis
+    );
+
+    $properties =& $rule->properties;
+    $intersect_props = array_intersect_key( $properties, $flex_related_props );
+
+    // Checking for flex related properties or 'display:flex'.
+    // First checking the display property as it's pretty common.
+    if ( isset( $properties[ 'display' ] ) ) {
+        foreach ( $rule->declarations as $declaration ) {
+            if ( $declaration->property === 'display' &&
+                ( $declaration->value === 'flex' || $declaration->value === 'inline-flex' ) ) {
+                // Add 'display' to the intersected properties.
+                $intersect_props[ 'display' ] = true;
+                break;
+            }
+        }
+    }
+
+    // Bail early if the rule has no flex related properties.
+    if ( ! $intersect_props ) {
+        return;
+    }
+
+    $prop_value_aliases =& CssCrush::$process->aliases[ 'values' ];
+
+    $stack = array();
+    $rule_updated = false;
+
+    foreach ( $rule as $declaration ) {
+
+        $prop = $declaration->property;
+        $value = $declaration->value;
+
+        if ( ! isset( $intersect_props[ $prop ] ) ) {
+            $stack[] = $declaration;
+            continue;
+        }
+
+        switch ( $prop ) {
+
+            // display:flex => display:-*-box.
+            case 'display':
+                if (
+                    // Treat flex and inline-flex the same in this case.
+                    ( $value === 'flex' || $value === 'inline-flex' ) &&
+                    isset( $prop_value_aliases[ 'display' ][ 'box' ] ) ) {
+                    foreach ( $prop_value_aliases[ 'display' ][ 'box' ] as $pair ) {
+                        $stack[] = new CssCrush_Declaration( $pair[0], $pair[1] );
+                        $rule_updated = true;
+                    }
+                }
+                break;
+
+            case 'align-items':
+                $rule_updated = csscrush__flex_align_items( $value, $stack );
+                break;
+
+            case 'flex':
+                $rule_updated = csscrush__flex( $value, $stack );
+                break;
+
+            case 'flex-direction':
+                $rule_updated = csscrush__flex_direction( $value, $stack );
+                break;
+
+            case 'flex-grow':
+                $rule_updated = csscrush__flex_grow( $value, $stack );
+                break;
+
+            case 'flex-wrap':
+                // No browser seems to have implemented the box-lines property,
+                // definitely not firefox.
+                // - https://bugzilla.mozilla.org/show_bug.cgi?id=562073
+                // - http://stackoverflow.com/questions/5010083/\
+                //   css3-flex-box-specifying-multiple-box-lines-doesnt-work
+
+                // $rule_updated = csscrush__flex_wrap( $value, $stack );
+                break;
+
+            case 'justify-content':
+                $rule_updated = csscrush__flex_justify_content( $value, $stack );
+                break;
+
+            case 'order':
+                $rule_updated = csscrush__flex_order( $value, $stack );
+                break;
+
+            // Shorthand values.
+            case 'flex-flow':
+
+                // <‘flex-direction’> || <‘flex-wrap’>
+
+                $args = explode( ' ', $value );
+                $direction = isset( $args[0] ) ? $args[0] : 'initial';
+                $wrap = isset( $args[1] ) ? $args[1] : 'initial';
+
+                $rule_updated = csscrush__flex_direction( $direction, $stack );
+                $rule_updated = csscrush__flex_wrap( $wrap, $stack );
+                break;
+        }
+
+        // The existing declaration.
+        $stack[] = $declaration;
+    }
+
+    // Re-assign if any updates have been made.
+    if ( $rule_updated ) {
+        $rule->setDeclarations( $stack );
+    }
+}
+
+
+function csscrush__flex_direction ( $value, &$stack ) {
+
+    // flex-direction: row | row-reverse | column | column-reverse
+    // box-orient:     horizontal | vertical | inline-axis | block-axis | inherit
+    // box-direction:  normal | reverse | inherit
+
+    $prop_aliases =& CssCrush::$process->aliases[ 'properties' ];
+
+    static $directions = array(
+        'row'            => 'normal',
+        'row-reverse'    => 'reverse',
+        'column'         => 'normal',
+        'column-reverse' => 'reverse',
+        'inherit'        => 'inherit',
+        'initial'        => 'normal',
+    );
+    static $orientations = array(
+        'row'            => 'horizontal',
+        'row-reverse'    => 'horizontal',
+        'column'         => 'vertical',
+        'column-reverse' => 'vertical',
+        'inherit'        => 'inherit',
+    );
+    $rule_updated = false;
+
+    if ( isset( $prop_aliases[ 'box-direction' ] ) ) {
+        foreach ( $prop_aliases[ 'box-direction' ] as $prop_alias ) {
+            $stack[] = new CssCrush_Declaration( $prop_alias, $directions[ $value ] );
+            $rule_updated = true;
+        }
+    }
+    if ( isset( $prop_aliases[ 'box-orient' ] ) ) {
+        foreach ( $prop_aliases[ 'box-orient' ] as $prop_alias ) {
+            $stack[] = new CssCrush_Declaration( $prop_alias, $orientations[ $value ] );
+            $rule_updated = true;
+        }
+    }
+    return $rule_updated;
+}
+
+
+function csscrush__flex_justify_content ( $value, &$stack ) {
+
+    // justify-content: flex-start | flex-end | center | space-between | space-around
+    // box-pack:        start | end | center | justify
+
+    $prop_aliases =& CssCrush::$process->aliases[ 'properties' ];
+
+    static $positions = array(
+        'flex-start'    => 'start',
+        'flex-end'      => 'end',
+        'center'        => 'center',
+        'space-between' => 'justify',
+        'space-around'  => 'justify',
+    );
+    $rule_updated = false;
+
+    if ( isset( $prop_aliases[ 'box-pack' ] ) && isset( $positions[ $value ] ) ) {
+        foreach ( $prop_aliases[ 'box-pack' ] as $prop_alias ) {
+            $stack[] = new CssCrush_Declaration( $prop_alias, $positions[ $value ] );
+            $rule_updated = true;
+        }
+    }
+    return $rule_updated;
+}
+
+
+function csscrush__flex_align_items ( $value, &$stack ) {
+
+    // align-items: flex-start | flex-end | center | baseline | stretch
+    // box-align:   start | end | center | baseline | stretch
+
+    $prop_aliases =& CssCrush::$process->aliases[ 'properties' ];
+
+    static $positions = array(
+        'flex-start'    => 'start',
+        'flex-end'      => 'end',
+        'center'        => 'center',
+        'baseline'      => 'baseline',
+        'stretch'       => 'stretch',
+    );
+    $rule_updated = false;
+
+    if ( isset( $prop_aliases[ 'box-align' ] ) && isset( $positions[ $value ] ) ) {
+        foreach ( $prop_aliases[ 'box-align' ] as $prop_alias ) {
+            $stack[] = new CssCrush_Declaration( $prop_alias, $positions[ $value ] );
+            $rule_updated = true;
+        }
+    }
+    return $rule_updated;
+}
+
+
+function csscrush__flex_order ( $value, &$stack ) {
+
+    // order:             
+    // box-ordinal-group: 
+
+    $prop_aliases =& CssCrush::$process->aliases[ 'properties' ];
+
+    $rule_updated = false;
+    if ( isset( $prop_aliases[ 'box-ordinal-group' ] ) ) {
+        foreach ( $prop_aliases[ 'box-ordinal-group' ] as $prop_alias ) {
+            $stack[] = new CssCrush_Declaration( $prop_alias, $value );
+            $rule_updated = true;
+        }
+    }
+    return $rule_updated;
+}
+
+
+function csscrush__flex_wrap ( $value, &$stack ) {
+
+    // flex-wrap: nowrap | wrap | wrap-reverse
+    // box-lines: single | multiple
+
+    $prop_aliases =& CssCrush::$process->aliases[ 'properties' ];
+
+    static $wrap_behaviours = array(
+        'nowrap'       => 'single',
+        'wrap'         => 'multiple',
+        'wrap-reverse' => 'multiple',
+        'initial'      => 'single',
+    );
+    $rule_updated = false;
+
+    if ( isset( $prop_aliases[ 'box-lines' ] ) && isset( $wrap_behaviours[ $value ] ) ) {
+        foreach ( $prop_aliases[ 'box-lines' ] as $prop_alias ) {
+            $stack[] = new CssCrush_Declaration( $prop_alias, $wrap_behaviours[ $value ] );
+            $rule_updated = true;
+        }
+    }
+    return $rule_updated;
+}
+
+
+function csscrush__flex ( $value, &$stack ) {
+
+    // flex:     none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
+    // box-flex: 
+
+    $prop_aliases =& CssCrush::$process->aliases[ 'properties' ];
+
+    // Normalize keyword arguments.
+    static $keywords = array(
+        'none'    => '0 0 auto',
+        'auto'    => '1 1 auto',
+        'initial' => '0 1 auto',
+    );
+    if ( isset( $keywords[ $value ] ) ) {
+        $value = $keywords[ $value ];
+    }
+
+    // Grow (first value) not have a unit to avoid being interpreted as a basis:
+    // https://developer.mozilla.org/en-US/docs/CSS/flex
+    $grow = 1;
+    if ( preg_match( '~^(\d*\.?\d+) ~', $value, $m ) ) {
+        $grow = $m[1];
+    }
+
+    $rule_updated = false;
+    if ( isset( $prop_aliases[ 'box-flex' ] ) ) {
+        foreach ( $prop_aliases[ 'box-flex' ] as $prop_alias ) {
+            $stack[] = new CssCrush_Declaration( $prop_alias, $grow );
+            $rule_updated = true;
+        }
+    }
+    return $rule_updated;
+}
+
+
+function csscrush__flex_grow ( $value, &$stack ) {
+
+    // flex-grow: 
+    // box-flex:  
+
+    $prop_aliases =& CssCrush::$process->aliases[ 'properties' ];
+
+    $rule_updated = false;
+    if ( isset( $prop_aliases[ 'box-flex' ] ) ) {
+        foreach ( $prop_aliases[ 'box-flex' ] as $prop_alias ) {
+            $stack[] = new CssCrush_Declaration( $prop_alias, $value );
+            $rule_updated = true;
+        }
+    }
+    return $rule_updated;
+}
+
diff --git a/plugins/property-sorter.php b/plugins/property-sorter.php
index 7f29420..319819f 100644
--- a/plugins/property-sorter.php
+++ b/plugins/property-sorter.php
@@ -24,20 +24,20 @@
  *
  */
 
-csscrush_plugin::register( 'property-sorter', array(
+CssCrush_Plugin::register( 'property-sorter', array(
     'enable' => 'csscrush__enable_property_sorter',
     'disable' => 'csscrush__disable_property_sorter',
 ));
 
 function csscrush__enable_property_sorter () {
-    csscrush_hook::add( 'rule_prealias', 'csscrush__property_sorter' );
+    CssCrush_Hook::add( 'rule_prealias', 'csscrush__property_sorter' );
 }
 
 function csscrush__disable_property_sorter () {
-    csscrush_hook::remove( 'rule_prealias', 'csscrush__property_sorter' );
+    CssCrush_Hook::remove( 'rule_prealias', 'csscrush__property_sorter' );
 }
 
-function csscrush__property_sorter ( csscrush_rule $rule ) {
+function csscrush__property_sorter ( CssCrush_Rule $rule ) {
 
     $new_set = array();
 
@@ -48,7 +48,7 @@ function csscrush__property_sorter ( csscrush_rule $rule ) {
 
     usort( $new_set, '_csscrush__property_sorter_callback' );
 
-    $rule->declarations = $new_set;
+    $rule->setDeclarations( $new_set );
 }
 
 
@@ -154,7 +154,7 @@ function &_csscrush__property_sorter_get_table () {
 
         // Load from property-sorting.ini.
         if ( $sorting_file_contents
-            = file_get_contents( csscrush::$config->location . '/misc/property-sorting.ini' ) ) {
+            = file_get_contents( CssCrush::$config->location . '/misc/property-sorting.ini' ) ) {
 
             $sorting_file_contents = preg_replace( '!;[^\r\n]*!', '', $sorting_file_contents );
             $table = preg_split( '!\s+!', trim( $sorting_file_contents ) );
diff --git a/plugins/rgba-fallback.php b/plugins/rgba-fallback.php
index 2b2c57a..89d315c 100644
--- a/plugins/rgba-fallback.php
+++ b/plugins/rgba-fallback.php
@@ -12,20 +12,20 @@
  *     background: rgba(0,0,0,.5);
  */
 
-csscrush_plugin::register( 'rgba-fallback', array(
+CssCrush_Plugin::register( 'rgba-fallback', array(
     'enable' => 'csscrush__enable_rgba_fallback',
     'disable' => 'csscrush__disable_rgba_fallback',
 ));
 
 function csscrush__enable_rgba_fallback () {
-    csscrush_hook::add( 'rule_postalias', 'csscrush__rgba_fallback' );
+    CssCrush_Hook::add( 'rule_postalias', 'csscrush__rgba_fallback' );
 }
 
 function csscrush__disable_rgba_fallback () {
-    csscrush_hook::remove( 'rule_postalias', 'csscrush__rgba_fallback' );
+    CssCrush_Hook::remove( 'rule_postalias', 'csscrush__rgba_fallback' );
 }
 
-function csscrush__rgba_fallback ( csscrush_rule $rule ) {
+function csscrush__rgba_fallback ( CssCrush_Rule $rule ) {
 
     $props = array_keys( $rule->properties );
 
@@ -57,8 +57,8 @@ function csscrush__rgba_fallback ( csscrush_rule $rule ) {
         list( $r, $g, $b, $a ) = explode( ',', $raw_value );
         
         // Add rgb value to the stack, followed by rgba 
-        $new_set[] = new csscrush_declaration( $declaration->property, "rgb($r,$g,$b)" );
+        $new_set[] = new CssCrush_Declaration( $declaration->property, "rgb($r,$g,$b)" );
         $new_set[] = $declaration;
     }
-    $rule->declarations = $new_set;
+    $rule->setDeclarations( $new_set );
 }
diff --git a/plugins/spiffing.php b/plugins/spiffing.php
index 06db438..6436e92 100644
--- a/plugins/spiffing.php
+++ b/plugins/spiffing.php
@@ -13,17 +13,17 @@
  *
  */
 
-csscrush_plugin::register( 'spiffing', array(
+CssCrush_Plugin::register( 'spiffing', array(
     'enable' => 'csscrush__enable_spiffing',
     'disable' => 'csscrush__disable_spiffing',
 ));
 
 function csscrush__enable_spiffing () {
-    csscrush_hook::add( 'rule_preprocess', 'csscrush__spiffing' );
+    CssCrush_Hook::add( 'rule_preprocess', 'csscrush__spiffing' );
 }
 
 function csscrush__disable_spiffing () {
-    csscrush_hook::add( 'rule_preprocess', 'csscrush__spiffing' );
+    CssCrush_Hook::add( 'rule_preprocess', 'csscrush__spiffing' );
 }
 
 function csscrush__spiffing ( $rule ) {
diff --git a/plugins/svg-gradients.php b/plugins/svg-gradients.php
index dfc19e2..2fa6d5d 100644
--- a/plugins/svg-gradients.php
+++ b/plugins/svg-gradients.php
@@ -41,19 +41,19 @@
  *
  */
 
-csscrush_plugin::register( 'svg-gradients', array(
+CssCrush_Plugin::register( 'svg-gradients', array(
     'enable' => 'csscrush__enable_svg_gradients',
     'disable' => 'csscrush__disable_svg_gradients',
 ));
 
 function csscrush__enable_svg_gradients () {
-    csscrush_function::register( 'svg-linear-gradient', 'csscrush_fn__svg_linear_gradient' );
-    csscrush_function::register( 'svg-radial-gradient', 'csscrush_fn__svg_radial_gradient' );
+    CssCrush_Function::register( 'svg-linear-gradient', 'csscrush_fn__svg_linear_gradient' );
+    CssCrush_Function::register( 'svg-radial-gradient', 'csscrush_fn__svg_radial_gradient' );
 }
 
 function csscrush__disable_svg_gradients () {
-    csscrush_function::deRegister( 'svg-linear-gradient', 'csscrush_fn__svg_linear_gradient' );
-    csscrush_function::deRegister( 'svg-radial-gradient', 'csscrush_fn__svg_radial_gradient' );
+    CssCrush_Function::deRegister( 'svg-linear-gradient', 'csscrush_fn__svg_linear_gradient' );
+    CssCrush_Function::deRegister( 'svg-radial-gradient', 'csscrush_fn__svg_radial_gradient' );
 }
 
 function csscrush_fn__svg_linear_gradient ( $input ) {
@@ -81,7 +81,7 @@ function csscrush_fn__svg_linear_gradient ( $input ) {
         $angle_keywords[ 'to left bottom' ] = $angle_keywords[ 'to bottom left' ];
     }
 
-    $args = csscrush_function::parseArgs( $input );
+    $args = CssCrush_Function::parseArgs( $input );
 
     // If no angle argument is passed the default used is 0.
     $angle = 0;
@@ -186,7 +186,7 @@ function csscrush_fn__svg_linear_gradient ( $input ) {
     $svg .= '';
 
     // Create data-uri url and return token label.
-    $url = new csscrush_url(http://github.com/'data:image/svg+xml;base64,'%20.%20base64_encode(%20$svg) );
+    $url = new CssCrush_Url( 'data:image/svg+xml;base64,' . base64_encode( $svg ) );
 
     return $url->label;
 }
@@ -217,7 +217,7 @@ function csscrush_fn__svg_radial_gradient ( $input ) {
         $position_keywords[ 'at left bottom' ] = $position_keywords[ 'at bottom left' ];
     }
 
-    $args = csscrush_function::parseArgs( $input );
+    $args = CssCrush_Function::parseArgs( $input );
 
     // Default origin,
     $position = $position_keywords[ 'at center' ];
@@ -262,7 +262,7 @@ function csscrush_fn__svg_radial_gradient ( $input ) {
     $svg .= '';
 
     // Create data-uri url and return token label.
-    $url = new csscrush_url(http://github.com/'data:image/svg+xml;base64,'%20.%20base64_encode(%20$svg) );
+    $url = new CssCrush_Url( 'data:image/svg+xml;base64,' . base64_encode( $svg ) );
 
     return $url->label;
 }

From 07aff2eefe329043f278cc4989486f493d1cb49c Mon Sep 17 00:00:00 2001
From: Pete Boere 
Date: Sat, 29 Dec 2012 19:52:25 +0000
Subject: [PATCH 059/421] #40 - Manual merge. Adding zoom aliases from
 teohhanhui`s commit.

---
 Aliases.ini | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/Aliases.ini b/Aliases.ini
index 38efa37..e64e57b 100644
--- a/Aliases.ini
+++ b/Aliases.ini
@@ -268,6 +268,16 @@
     display:box[] = display:-webkit-box
     display:box[] = display:-moz-box
 
+    ; Experimental cursor values.
+    cursor:zoom-in[] = cursor:-webkit-zoom-in
+    cursor:zoom-in[] = cursor:-moz-zoom-in
+    cursor:zoom-in[] = cursor:-ms-zoom-in
+    cursor:zoom-in[] = cursor:-o-zoom-in
+    cursor:zoom-out[] = cursor:-webkit-zoom-out
+    cursor:zoom-out[] = cursor:-moz-zoom-out
+    cursor:zoom-out[] = cursor:-ms-zoom-out
+    cursor:zoom-out[] = cursor:-o-zoom-out
+
 
 ;----------------------------------------------------------------
 ; Function aliases.

From 2b0304fab45aa72952b5711c82024dcf3f4465e6 Mon Sep 17 00:00:00 2001
From: Pete Boere 
Date: Sat, 29 Dec 2012 19:57:51 +0000
Subject: [PATCH 060/421] Updated changelog.

---
 CHANGELOG.txt | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index ac811ff..f4d70f4 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -11,7 +11,8 @@ Improved gradient function aliasing to handle new angle keywords ('to left', 'at
 Added svg-gradients plugin for simulating CSS3 gradients with data-uris.
 Added formatting option so custom formatters can be defined for un-minified output (see wiki for options).
 Added newlines option to force the style of newlines in output (see wiki for options).
-Updated command line utility to employ the new options.
+Updated command line utility to use the new options.
+Classes now loaded via an autoloader, also some other refactoring for moving towards PSR-0 compliance.
 
 
 1.8

From 4169e2aae700640c77ba6ee63b5c6302b118a50e Mon Sep 17 00:00:00 2001
From: Pete Boere 
Date: Wed, 2 Jan 2013 11:21:01 +0000
Subject: [PATCH 061/421] Internally renamed property/value aliases to
 declaration aliases. Removed the Core.php include out of the auto loader til
 2.x since it doesn't really fit now. Moved proc API functions and formatter
 functions into their own files for including via CssCrush::init(). Fix for
 flex-order 'natural number' confusion in legacy-flexbox plugin.

---
 Aliases.ini                 |   8 +-
 CHANGELOG.txt               | 340 +++++++++++++++++-------------------
 CssCrush.php                |  16 +-
 Prepend.css                 |   4 +-
 lib/Core.php                | 107 ++----------
 lib/Process.php             |   8 +-
 lib/Rule.php                |   6 +-
 misc/formatters.php         |  66 +++++++
 misc/proc-api-functions.php |  29 +++
 plugins/legacy-flexbox.php  |  16 +-
 10 files changed, 296 insertions(+), 304 deletions(-)
 create mode 100644 misc/formatters.php
 create mode 100644 misc/proc-api-functions.php

diff --git a/Aliases.ini b/Aliases.ini
index e64e57b..8a6730e 100644
--- a/Aliases.ini
+++ b/Aliases.ini
@@ -227,15 +227,15 @@
 
 
 ;----------------------------------------------------------------
-; Property:value aliases.
+; Declaration aliases.
 
-[values]
+[declarations]
 
     ; Flexbox (2012).
-    display:flex[] = display:-webkit-flex
     display:flex[] = display:-ms-flexbox
-    display:inline-flex[] = display:-webkit-inline-flex
+    display:flex[] = display:-webkit-flex
     display:inline-flex[] = display:-ms-inline-flexbox
+    display:inline-flex[] = display:-webkit-inline-flex
 
     ; Flexbox (early 2012).
     align-content:flex-start[] = -ms-flex-line-pack:start
diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index f4d70f4..99c711f 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -1,218 +1,192 @@
-1.9
+1.9 (5th January 2013)
 ---
-Added flexbox aliases for both 2009 and 2012 edition specs.
-Added a legacy-flexbox plugin for auto-generating the flexbox 2009 spec equivilant properties.
-Updated selector aliases to take arguments at runtime.
-Updated plugin API to use distinct 'enable' and 'disable' handlers.
-The disable option is now resolved before the enable option so you can easily disable all plugins
-and then specify the plugins you want to apply.
-Added functions API for defining custom functions inside plugins.
-Improved gradient function aliasing to handle new angle keywords ('to left', 'at center' etc.).
-Added svg-gradients plugin for simulating CSS3 gradients with data-uris.
-Added formatting option so custom formatters can be defined for un-minified output (see wiki for options).
-Added newlines option to force the style of newlines in output (see wiki for options).
-Updated command line utility to use the new options.
-Classes now loaded via an autoloader, also some other refactoring for moving towards PSR-0 compliance.
-
-
-1.8
+- Added flexbox aliases for both 2009 and 2012 edition specs.
+- Added a legacy-flexbox plugin for auto-generating the flexbox 2009 spec equivilant properties.
+- Updated selector aliases to take arguments at runtime.
+- Updated plugin API to use distinct 'enable' and 'disable' handlers.
+- The disable option is now resolved before the enable option so you can easily disable all plugins
+  and then specify the plugins you want to apply.
+- Added functions API for defining custom functions inside plugins.
+- Improved gradient function aliasing to handle new angle keywords ('to left', 'at center' etc.).
+- Added svg-gradients plugin for simulating CSS3 gradients with data-uris.
+- Added formatting option so custom formatters can be defined for un-minified output (see wiki for options).
+- Added newlines option to force the style of newlines in output (see wiki for options).
+- Updated command line utility to use the new options.
+- Property/value aliases renamed as declaration aliases.
+- Classes now loaded via an autoloader, also some other refactoring for moving towards PSR-0 compliance.
+
+1.8 (13th November 2012)
 ---
-Added selector aliasing with @selector-alias directive.
-Added output_dir option for specifying the destination of compiled files.
-Added doc_root option for working around problems with server aliases or path rewrites.
-Added viewport @-rule aliases.
-Debug option renamed to 'minify'; debug option will still work as before but is deprecated.
-New minify option optionally takes an array of advanced minification parameters.
-Expanded trace option to take an optional array of log parameters;
-log params available are stubs, selector_count, errors and compile_time.
-Added CssCrush::stat method to retrieve logged parameters.
-Improved cross OS support.
-Improved minification.
-Major refactoring.
-
-
-1.7
+- Added selector aliasing with the @selector-alias directive.
+- Added output_dir option for specifying the destination of compiled files.
+- Added doc_root option for working around problems with server aliases or path rewrites.
+- Added viewport @-rule aliases.
+- Debug option renamed to 'minify'; debug option will still work as before but is deprecated.
+- New minify option optionally takes an array of advanced minification parameters.
+- Expanded trace option to take an optional array of log parameters;
+- log params available are stubs, selector_count, errors and compile_time.
+- Added CssCrush::stat method to retrieve logged parameters.
+- Improved cross OS support.
+- Improved minification.
+- Major refactoring.
+
+1.7 (28th September 2012)
 ---
-Added trace option to output SASS compatible debug-info stubs for use with tools like FireSass.
-Added @ifdefine directive for dynamically including/excluding parts of a CSS file based on the existence of variables.
-Updated plugin API.
-Added options for enabling and disabling plugins at runtime.
-Added property sorter plugin.
-Added support for SASS-like @include/@extend syntax for invoking mixins and extends.
-Boilerplate option now accepts a filename string as a boilerplate template.
-CssCrush::string method now uses document_root as a default context for finding linked resources.
-Updated command line appication.
-Updated aliases and initial value files.
-Fixed parsing issue introduced in 1.6.1.
-
-
-1.6.1
+- Added trace option to output SASS compatible debug-info stubs for use with tools like FireSass.
+- Added @ifdefine directive for dynamically including/excluding parts of a CSS file based on the existence of variables.
+- Updated plugin API.
+- Added options for enabling and disabling plugins at runtime.
+- Added property sorter plugin.
+- Added support for SASS-like @include/@extend syntax for invoking mixins and extends.
+- Boilerplate option now accepts a filename string as a boilerplate template.
+- CssCrush::string method now uses document_root as a default context for finding linked resources.
+- Updated command line appication.
+- Updated aliases and initial value files.
+- Fixed parsing issue introduced in 1.6.1.
+
+1.6.1 (22nd August 2012)
 -----
-Resolved issue #35.
-Resolved issue #34.
+- Resolved issues #34 and #35.
 
-
-1.6
+1.6 (1st August 2012)
 ---
-Inheritance model improved to support adoption of pseudo classes and elements (see wiki).
-Added rule self-referencing function this() and complimentary data-* properties.
-Added rule referencing function query().
-Added default value argument for variables.
-Added hsl-adjust() and hsla-adjust() color functions.
-Mixin and fragment arg() function can now be nested.
-Commas are now optional when specifying arguments for most custom functions.
-Double-colon plugin moved to core.
-Option rewrite_import_urls now defaults to true.
-
-
-1.5.3
+- Inheritance model improved to support adoption of pseudo classes and elements (see wiki).
+- Added rule self-referencing function this() and complimentary data-- properties.
+- Added rule referencing function query().
+- Added default value argument for variables.
+- Added hsl-adjust() and hsla-adjust() color functions.
+- Mixin and fragment arg() function can now be nested.
+- Commas are now optional when specifying arguments for most custom functions.
+- Double-colon plugin moved to core.
+- Option rewrite_import_urls now defaults to true.
+
+1.5.3 (13th June 2012)
 -----
-Refactoring.
-Fixed some test cases.
-
+- Refactoring.
+- Fixed some test cases.
 
-1.5.2
+1.5.2 (8th June 2012)
 -----
-Resolved issue #32.
-CssCrush::inline method now defaults to not printing a boilerplate.
-Updated aliases file.
-
+- Resolved issue #32.
+- CssCrush::inline method now defaults to not printing a boilerplate.
+- Updated aliases file.
 
-1.5.1
+1.5.1 (1st June 2012)
 -----
-Extended mixins to work with abstract rules and regular rules.
-Fixed issue with selector grouping and inheritance in combination.
+- Extended mixins to work with abstract rules and regular rules.
+- Fixed issue with selector grouping and inheritance in combination.
 
-
-1.5
+1.5 (21st May 2012)
 ---
-New feature: Rule inheritance / abstract rules.
-New feature: Block nesting.
-New feature: Mixins.
-New feature: Fragments.
-Abstracted IO interface.
-Added some error reporting.
-Added spiffing.css plugin.
-csscrush_tag method now uses media type 'all' by default.
-Updated alias and initial-value tables.
-Internal refactoring.
-Resolved issue #23.
-Resolved issue #24.
-Resolved issue #27.
-Resolved issue #28.
-Resolved issue #29.
-
-
-1.4.2
+- New feature: Rule inheritance / abstract rules.
+- New feature: Block nesting.
+- New feature: Mixins.
+- New feature: Fragments.
+- Abstracted IO interface.
+- Added some error reporting.
+- Added spiffing.css plugin.
+- csscrush_tag method now uses media type 'all' by default.
+- Updated alias and initial-value tables.
+- Internal refactoring.
+- Resolved issues #23, #24, #27, #28 and #29.
+
+1.4.2 (14th March 2012)
 -----
-Fixed bug with @import statement parsing.
-Some minor under the hood changes.
-
+- Fixed bug with @import statement parsing.
+- Some minor under the hood changes.
 
-1.4.1
+1.4.1 (10th February 2012)
 -----
-Added command line application.
-Added 'rewrite_import_urls' option - Ability to rewrite relative url references inside imported css files.
-Added Prepend.css - Optionally prepend css to every input.
-Fix for issue #21.
-Reorganized aliases file with some additions.
-Initial-values updated.
-Updated CssCrush::string method to correctly handle import statements.
-
-
-1.4
+- Added command line application.
+- Added 'rewrite_import_urls' option - Ability to rewrite relative url references inside imported css files.
+- Added Prepend.css - Optionally prepend css to every input.
+- Fix for issue #21.
+- Reorganized aliases file with some additions.
+- Initial-values updated.
+- Updated CssCrush::string method to correctly handle import statements.
+
+1.4 (24th January 2012)
 ---
-Added initial-keyword plugin (shim for the CSS3 keyword).
-Added inline method (Issue #18).
-Added ability to escape declarations from aliasing or plugins by prefixing with tilde.
-Added procedural style public API to mirror the static class API.
-Deprecated @variables syntax for @define. @variables still supported.
-Adjusted color functions to accept a space delimiter (as well as comma) in the arguments list.
-Surpressed some benign PHP warning messages.
-Some internal cleaning up.
-Disabled IE6 min-height plugin by default.
-
-.
-1.3.6
+- Added initial-keyword plugin (shim for the CSS3 keyword).
+- Added inline method (Issue #18).
+- Added ability to escape declarations from aliasing or plugins by prefixing with tilde.
+- Added procedural style public API to mirror the static class API.
+- Deprecated @variables syntax for @define. @variables still supported.
+- Adjusted color functions to accept a space delimiter (as well as comma) in the arguments list.
+- Surpressed some benign PHP warning messages.
+- Some internal cleaning up.
+- Disabled IE6 min-height plugin by default.
+
+1.3.6 (9th November 2011)
 -----
-Improved color functions.
-Added a-adjust function for altering a color's opacity.
-Deprecated hsl-adjust function (you can use nested color functions instead).
-Added the ability to use local versions of alias and plugin files so pull updates don't clobber local settings.
+- Improved color functions.
+- Added a-adjust function for altering a color's opacity.
+- Deprecated hsl-adjust function (you can use nested color functions instead).
+- Added the ability to use local versions of alias and plugin files so pull updates don't clobber local settings.
 
-.
-1.3.5
+1.3.5 (8th November 2011)
 -----
-Added hook system for plugins.
-Plugins split into seperate files.
-Aliases and Plugins files renamed with '.ini' file extensions to be editor friendly.
-Added opacity plugin.
-Updated filter plugin.
-Fixed nested custom function parsing (issue #14).
-
-
-1.3.4
+- Added hook system for plugins.
+- Plugins split into seperate files.
+- Aliases and Plugins files renamed with '.ini' file extensions to be editor friendly.
+- Added opacity plugin.
+- Updated filter plugin.
+- Fixed nested custom function parsing (issue #14).
+
+1.3.4 (29th October 2011)
 -----
-Added output_filename option.
-Added vendor_target option.
-Renamed 'macros' to the more general 'plugins' and split them into their own files.
-Removed superfluous outer containing directory (update your include paths).
+- Added output_filename option.
+- Added vendor_target option.
+- Renamed 'macros' to the more general 'plugins' and split them into their own files.
+- Removed superfluous outer containing directory (update your include paths).
 
-
-1.3.3
+1.3.3 (28th October 2011)
 -----
-Fixed regression with absolute URL file imports (issue #12).
-Fixed minification bug (issue #13).
-
+- Fixed regression with absolute URL file imports (issue #12).
+- Fixed minification bug (issue #13).
 
-1.3.2
+1.3.2 (18th October 2011)
 -----
-Updated variable syntax.
-Fixed minification bug.
+- Updated variable syntax.
+- Fixed minification bug.
 
-
-1.3.1
+1.3.1 (9th October 2011)
 -----
-Added support for svg and svgz data uris.
-Added animation shorthand alias.
-Added user-select alias.
-
+- Added support for svg and svgz data uris.
+- Added animation shorthand alias.
+- Added user-select alias.
 
-1.3
+1.3 (20th September 2011)
 ---
-Added the public function CssCrush::string for processing raw strings of CSS.
-Added color functions.
-Added aliases for IE10.
+- Added the public function CssCrush::string for processing raw strings of CSS.
+- Added color functions.
+- Added aliases for IE10.
 
-
-1.2
+1.2 (8th September 2011)
 ---
-Rewritten the file importer.
-
+- Rewritten the file importer.
 
-1.1
+1.1 (2nd September 2011)
 ---
-Added global variables support.
-Added support for variable interpolation within string literals.
-Added 'tag' method for outputting an html link tag instead of returning a filename.
-Added values aliases, dynamic 'runtime' variables.
-Added RGBA macro.
-Added IE clip macro.
-Added data uri function.
-Minor correction to WAMP support.
-Minor fix to rule API.
-
-
-1.0
+- Added support for global variables.
+- Added support for variable interpolation within string literals.
+- Added 'tag' method for outputting an html link tag instead of returning a filename.
+- Added values aliases, dynamic 'runtime' variables.
+- Added RGBA macro.
+- Added IE clip macro.
+- Added data uri function.
+- Minor correction to WAMP support.
+- Minor fix to rule API.
+
+1.0 (14th July 2011)
 ---
-Major refactoring.
-Custom functions.
-Optional boilerplate.
-Double colon syntax shim.
-Resolved document root issues.
-Minification improvements.
-
-
-0.9
+- Major refactoring.
+- Custom functions.
+- Optional boilerplate.
+- Double colon syntax shim.
+- Resolved document root issues.
+- Minification improvements.
+
+0.9 (20th September 2010)
 ---
-Initial release.
+- Initial release.
diff --git a/CssCrush.php b/CssCrush.php
index 21a7377..0c25c14 100644
--- a/CssCrush.php
+++ b/CssCrush.php
@@ -18,20 +18,16 @@ function csscrush_autoload ( $class ) {
     }
     $class = ltrim( substr( $class, 8 ), '_' );
 
-    if ( empty( $class ) ) {
-        $subpath = 'Core';
-    }
     // Tolerate some cases of lowercasing from external use.
-    elseif ( strpos( $class, '_' ) !== false ) {
-        $subpath = implode( '/', array_map( 'ucfirst', explode( '_', $class ) ) );
-    }
-    else {
-        $subpath = ucfirst( $class );
-    }
+    $subpath = implode( '/', array_map( 'ucfirst', explode( '_', $class ) ) );
 
-    require dirname( __FILE__ ) . "/lib/$subpath.php";
+    require_once dirname( __FILE__ ) . "/lib/$subpath.php";
 }
 
 spl_autoload_register( 'csscrush_autoload' );
 
+
+// Core.php will also be PSR-0 autoloaded with API changes in v2.x
+require_once 'lib/Core.php';
+
 CssCrush::init( __FILE__ );
diff --git a/Prepend.css b/Prepend.css
index a5c7514..d9982bc 100644
--- a/Prepend.css
+++ b/Prepend.css
@@ -1,6 +1,8 @@
 /*$
 
-Prepend.css contains library variables by default, but it could also contain reset styles such as reset.css or normalize.css that you would want prepended to every output file.
+Prepend.css contains library variables by default, but it could also contain
+reset styles such as reset.css or normalize.css that you would want prepended
+to every output file.
 
 */
 @define {
diff --git a/lib/Core.php b/lib/Core.php
index 446b929..689eb52 100644
--- a/lib/Core.php
+++ b/lib/Core.php
@@ -88,12 +88,11 @@ static public function init ( $seed_file )
             'newlines' => 'use-platform',
         ));
 
-        // Register default formatters.
-        self::$config->formatters = array(
-            'single-line' => 'csscrush__fmtr_single',
-            'padded' => 'csscrush__fmtr_padded',
-            'nested' => 'csscrush__fmtr_nested',
-        );
+        // Include and register stock formatters.
+        require_once self::$config->location . '/misc/formatters.php';
+
+        // Include the procedural API functions.
+        require_once self::$config->location . '/misc/proc-api-functions.php';
 
         // Initialise other classes.
         CssCrush_Regex::init();
@@ -163,25 +162,25 @@ static public function loadAssets ()
 
                 self::$config->aliases = $result;
 
-                // Value aliases require a little preprocessing.
-                if ( isset( self::$config->aliases[ 'values' ] ) ) {
+                // Declaration aliases require a little preprocessing.
+                if ( isset( self::$config->aliases[ 'declarations' ] ) ) {
                     $store = array();
-                    foreach ( self::$config->aliases[ 'values' ] as $prop_val => $aliases ) {
+                    foreach ( self::$config->aliases[ 'declarations' ] as $prop_val => $aliases ) {
                         list( $prop, $value ) = array_map( 'trim', explode( ':', $prop_val ) );
                         foreach ( $aliases as &$alias ) {
                             $alias = explode( ':', $alias );
                         }
                         $store[ $prop ][ $value ] = $aliases;
                     }
-                    self::$config->aliases[ 'values' ] = $store;
+                    self::$config->aliases[ 'declarations' ] = $store;
                 }
 
                 // Ensure all alias groups are at least set (issue #34)
                 self::$config->bareAliasGroups = array(
                     'properties' => array(),
-                    'functions'  => array(),
-                    'values'     => array(),
-                    'at-rules'   => array(),
+                    'functions' => array(),
+                    'declarations' => array(),
+                    'at-rules' => array(),
                 );
                 self::$config->aliases += self::$config->bareAliasGroups;
             }
@@ -546,85 +545,3 @@ static public function runStat ( $name )
         }
     }
 }
-
-
-#############################
-#  Default formatters.
-
-function csscrush__fmtr_single ( $rule ) {
-
-    $EOL = CssCrush::$process->newline;
-    if ( $stub = $rule->tracingStub ) {
-        $stub .= $EOL;
-    }
-
-    $comments = implode( '', $rule->comments );
-    if ( $comments ) {
-      $comments = "$EOL$comments";
-    }
-    $selectors = implode( ", ", $rule->selectors );
-    $block = implode( "; ", $rule->declarations );
-    return "$comments$stub$selectors { $block; }$EOL";
-}
-
-function csscrush__fmtr_padded ( $rule ) {
-
-    $EOL = CssCrush::$process->newline;
-    if ( $stub = $rule->tracingStub ) {
-        $stub .= $EOL;
-    }
-
-    $comments = implode( '', $rule->comments );
-    if ( $comments ) {
-        $comments = "$EOL$comments";
-    }
-
-    $cutoff = 40;
-    $selectors = implode( ", ", $rule->selectors );
-    $block = implode( "; ", $rule->declarations );
-
-    if ( strlen( $selectors ) > $cutoff ) {
-        $padding = str_repeat( ' ', $cutoff );
-        return "$comments$stub$selectors$EOL$padding { $block; }$EOL";
-    }
-    else {
-        $selectors = str_pad( $selectors, $cutoff );
-        return "$comments$stub$selectors { $block; }$EOL";
-    }
-}
-
-function csscrush__fmtr_block ( $rule, $indent = '    ' ) {
-
-    $EOL = CssCrush::$process->newline;
-    if ( $stub = $rule->tracingStub ) {
-        $stub .= $EOL;
-    }
-
-    $comments = implode( '', $rule->comments );
-    $selectors = implode( ",$EOL", $rule->selectors );
-    $block = implode( ";$EOL$indent", $rule->declarations );
-    return "$comments$stub$selectors {{$EOL}$indent$block;$EOL$indent}$EOL$EOL";
-}
-
-
-#############################
-#  Procedural style external API.
-
-function csscrush_file ( $file, $options = null ) {
-    return CssCrush::file( $file, $options );
-}
-function csscrush_tag ( $file, $options = null, $attributes = array() ) {
-    return CssCrush::tag( $file, $options, $attributes );
-}
-function csscrush_inline ( $file, $options = null, $attributes = array() ) {
-    return CssCrush::inline( $file, $options, $attributes );
-}
-function csscrush_string ( $string, $options = null ) {
-    return CssCrush::string( $string, $options );
-}
-function csscrush_globalvars ( $vars ) {
-    return CssCrush::globalVars( $vars );
-}
-function csscrush_clearcache ( $dir = '' ) {
-    return CssCrush::clearcache( $dir );
-}
diff --git a/lib/Process.php b/lib/Process.php
index 15b0f8c..1bcab89 100644
--- a/lib/Process.php
+++ b/lib/Process.php
@@ -377,8 +377,8 @@ protected function filterAliases ()
         // Loop the aliases array, filter down to the target vendor.
         foreach ( $this->aliases as $group_name => $group_array ) {
 
-            // Property/value aliases are special.
-            if ( 'values' === $group_name ) {
+            // Declarations aliases are special.
+            if ( 'declarations' === $group_name ) {
                 foreach ( $group_array as $property => $values ) {
                     $result = array();
                     foreach ( $values as $value => $prefix_values ) {
@@ -392,7 +392,7 @@ protected function filterAliases ()
                             }
                         }
                     }
-                    $this->aliases[ 'values' ][ $property ][ $value ] = $result;
+                    $this->aliases[ 'declarations' ][ $property ][ $value ] = $result;
                 }
                 continue;
             }
@@ -734,7 +734,7 @@ protected function processRules ()
             if ( $aliases[ 'functions' ] ) {
                 $rule->addFunctionAliases();
             }
-            if ( $aliases[ 'values' ] ) {
+            if ( $aliases[ 'declarations' ] ) {
                 $rule->addPropertyValueAliases();
             }
 
diff --git a/lib/Rule.php b/lib/Rule.php
index aa9a43b..dc0b514 100644
--- a/lib/Rule.php
+++ b/lib/Rule.php
@@ -639,10 +639,10 @@ public function addFunctionAliases ()
 
     public function addPropertyValueAliases ()
     {
-        $prop_value_aliases =& CssCrush::$process->aliases[ 'values' ];
+        $declaration_aliases =& CssCrush::$process->aliases[ 'declarations' ];
 
         // First test for the existence of any aliased properties.
-        if ( ! ( $intersect = array_intersect_key( $prop_value_aliases, $this->properties ) ) ) {
+        if ( ! ( $intersect = array_intersect_key( $declaration_aliases, $this->properties ) ) ) {
             return;
         }
 
@@ -658,7 +658,7 @@ public function addPropertyValueAliases ()
             if ( isset( $intersect[ $declaration->property ] ) && ! $declaration->skip ) {
 
                 // Iterate on the current declaration property for value matches.
-                foreach ( $prop_value_aliases[ $declaration->property ] as $value_match => $replacements ) {
+                foreach ( $declaration_aliases[ $declaration->property ] as $value_match => $replacements ) {
 
                     // Create new alias declaration if the property and value match.
                     if ( $declaration->value === $value_match ) {
diff --git a/misc/formatters.php b/misc/formatters.php
new file mode 100644
index 0000000..0dcfb44
--- /dev/null
+++ b/misc/formatters.php
@@ -0,0 +1,66 @@
+formatters = array(
+    'single-line' => 'csscrush__fmtr_single',
+    'padded' => 'csscrush__fmtr_padded',
+    'block' => 'csscrush__fmtr_block',
+);
+
+function csscrush__fmtr_single ( $rule ) {
+
+    $EOL = CssCrush::$process->newline;
+    if ( $stub = $rule->tracingStub ) {
+        $stub .= $EOL;
+    }
+
+    $comments = implode( '', $rule->comments );
+    if ( $comments ) {
+      $comments = "$EOL$comments";
+    }
+    $selectors = implode( ", ", $rule->selectors );
+    $block = implode( "; ", $rule->declarations );
+    return "$comments$stub$selectors { $block; }$EOL";
+}
+
+function csscrush__fmtr_padded ( $rule ) {
+
+    $EOL = CssCrush::$process->newline;
+    if ( $stub = $rule->tracingStub ) {
+        $stub .= $EOL;
+    }
+
+    $comments = implode( '', $rule->comments );
+    if ( $comments ) {
+        $comments = "$EOL$comments";
+    }
+
+    $cutoff = 40;
+    $selectors = implode( ", ", $rule->selectors );
+    $block = implode( "; ", $rule->declarations );
+
+    if ( strlen( $selectors ) > $cutoff ) {
+        $padding = str_repeat( ' ', $cutoff );
+        return "$comments$stub$selectors$EOL$padding { $block; }$EOL";
+    }
+    else {
+        $selectors = str_pad( $selectors, $cutoff );
+        return "$comments$stub$selectors { $block; }$EOL";
+    }
+}
+
+function csscrush__fmtr_block ( $rule, $indent = '    ' ) {
+
+    $EOL = CssCrush::$process->newline;
+    if ( $stub = $rule->tracingStub ) {
+        $stub .= $EOL;
+    }
+
+    $comments = implode( '', $rule->comments );
+    $selectors = implode( ",$EOL", $rule->selectors );
+    $block = implode( ";$EOL$indent", $rule->declarations );
+    return "$comments$stub$selectors {{$EOL}$indent$block;$EOL$indent}$EOL$EOL";
+}
diff --git a/misc/proc-api-functions.php b/misc/proc-api-functions.php
new file mode 100644
index 0000000..b11ed64
--- /dev/null
+++ b/misc/proc-api-functions.php
@@ -0,0 +1,29 @@
+aliases[ 'values' ];
+    $declaration_aliases =& CssCrush::$process->aliases[ 'declarations' ];
 
     $stack = array();
     $rule_updated = false;
@@ -119,8 +119,8 @@ function csscrush__legacy_flexbox ( CssCrush_Rule $rule ) {
                 if (
                     // Treat flex and inline-flex the same in this case.
                     ( $value === 'flex' || $value === 'inline-flex' ) &&
-                    isset( $prop_value_aliases[ 'display' ][ 'box' ] ) ) {
-                    foreach ( $prop_value_aliases[ 'display' ][ 'box' ] as $pair ) {
+                    isset( $declaration_aliases[ 'display' ][ 'box' ] ) ) {
+                    foreach ( $declaration_aliases[ 'display' ][ 'box' ] as $pair ) {
                         $stack[] = new CssCrush_Declaration( $pair[0], $pair[1] );
                         $rule_updated = true;
                     }
@@ -171,7 +171,7 @@ function csscrush__legacy_flexbox ( CssCrush_Rule $rule ) {
                 $wrap = isset( $args[1] ) ? $args[1] : 'initial';
 
                 $rule_updated = csscrush__flex_direction( $direction, $stack );
-                $rule_updated = csscrush__flex_wrap( $wrap, $stack );
+                // $rule_updated = csscrush__flex_wrap( $wrap, $stack );
                 break;
         }
 
@@ -286,6 +286,14 @@ function csscrush__flex_order ( $value, &$stack ) {
 
     $prop_aliases =& CssCrush::$process->aliases[ 'properties' ];
 
+    // Bump value as box-ordinal-group requires a positive integer:
+    // http://www.w3.org/TR/2009/WD-css3-flexbox-20090723/#displayorder
+    //
+    // Spec suggests a 'natural number' as valid value which Webkit seems
+    // to interpret as integer > 0, whereas Firefox interprets as
+    // integer > -1.
+    $value = $value < 1 ? 1 : $value + 1;
+
     $rule_updated = false;
     if ( isset( $prop_aliases[ 'box-ordinal-group' ] ) ) {
         foreach ( $prop_aliases[ 'box-ordinal-group' ] as $prop_alias ) {

From faefc01a7ead5d6e6dcbd5be2481bba075c8617d Mon Sep 17 00:00:00 2001
From: Pete Boere 
Date: Sat, 5 Jan 2013 17:47:29 +0000
Subject: [PATCH 062/421] Converted changelog to markdown for github
 friendliness.

---
 CHANGELOG.md                                  | 192 ++++++++++++++++++
 CHANGELOG.txt                                 | 192 ------------------
 lib/Core.php                                  |   2 +-
 .../{proc-api-functions.php => functions.php} |   0
 4 files changed, 193 insertions(+), 193 deletions(-)
 create mode 100644 CHANGELOG.md
 delete mode 100644 CHANGELOG.txt
 rename misc/{proc-api-functions.php => functions.php} (100%)

diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..ac09e27
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,192 @@
+1.9 (5th January 2013)
+---
+* Added flexbox aliases for both 2009 and 2012 edition specs.
+* Added a legacy-flexbox plugin for auto-generating the flexbox 2009 spec equivilant properties.
+* Updated selector aliases to take arguments at runtime.
+* Updated plugin API to use distinct 'enable' and 'disable' handlers.
+* The disable option is now resolved before the enable option so you can easily disable all plugins
+  and then specify the plugins you want to apply.
+* Added functions API for defining custom functions inside plugins.
+* Improved gradient function aliasing to handle new angle keywords ('to left', 'at center' etc.).
+* Added svg-gradients plugin for simulating CSS3 gradients with data-uris.
+* Added formatting option so custom formatters can be defined for un-minified output (see wiki for options).
+* Added newlines option to force the style of newlines in output (see wiki for options).
+* Updated command line utility to use the new options.
+* Property/value aliases renamed as declaration aliases.
+* Classes now loaded via an autoloader, also some other refactoring for moving towards PSR-0 compliance.
+
+1.8 (13th November 2012)
+---
+* Added selector aliasing with the @selector-alias directive.
+* Added output_dir option for specifying the destination of compiled files.
+* Added doc_root option for working around problems with server aliases or path rewrites.
+* Added viewport @-rule aliases.
+* Debug option renamed to 'minify'; debug option will still work as before but is deprecated.
+* New minify option optionally takes an array of advanced minification parameters.
+* Expanded trace option to take an optional array of log parameters;
+* log params available are stubs, selector_count, errors and compile_time.
+* Added CssCrush::stat method to retrieve logged parameters.
+* Improved cross OS support.
+* Improved minification.
+* Major refactoring.
+
+1.7 (28th September 2012)
+---
+* Added trace option to output SASS compatible debug-info stubs for use with tools like FireSass.
+* Added @ifdefine directive for dynamically including/excluding parts of a CSS file based on the existence of variables.
+* Updated plugin API.
+* Added options for enabling and disabling plugins at runtime.
+* Added property sorter plugin.
+* Added support for SASS-like @include/@extend syntax for invoking mixins and extends.
+* Boilerplate option now accepts a filename string as a boilerplate template.
+* CssCrush::string method now uses document_root as a default context for finding linked resources.
+* Updated command line appication.
+* Updated aliases and initial value files.
+* Fixed parsing issue introduced in 1.6.1.
+
+1.6.1 (22nd August 2012)
+-----
+* Resolved issues #34 and #35.
+
+1.6 (1st August 2012)
+---
+* Inheritance model improved to support adoption of pseudo classes and elements (see wiki).
+* Added rule self-referencing function this() and complimentary data-- properties.
+* Added rule referencing function query().
+* Added default value argument for variables.
+* Added hsl-adjust() and hsla-adjust() color functions.
+* Mixin and fragment arg() function can now be nested.
+* Commas are now optional when specifying arguments for most custom functions.
+* Double-colon plugin moved to core.
+* Option rewrite_import_urls now defaults to true.
+
+1.5.3 (13th June 2012)
+-----
+* Refactoring.
+* Fixed some test cases.
+
+1.5.2 (8th June 2012)
+-----
+* Resolved issue #32.
+* CssCrush::inline method now defaults to not printing a boilerplate.
+* Updated aliases file.
+
+1.5.1 (1st June 2012)
+-----
+* Extended mixins to work with abstract rules and regular rules.
+* Fixed issue with selector grouping and inheritance in combination.
+
+1.5 (21st May 2012)
+---
+* New feature: Rule inheritance / abstract rules.
+* New feature: Block nesting.
+* New feature: Mixins.
+* New feature: Fragments.
+* Abstracted IO interface.
+* Added some error reporting.
+* Added spiffing.css plugin.
+* csscrush_tag method now uses media type 'all' by default.
+* Updated alias and initial-value tables.
+* Internal refactoring.
+* Resolved issues #23, #24, #27, #28 and #29.
+
+1.4.2 (14th March 2012)
+-----
+* Fixed bug with @import statement parsing.
+* Some minor under the hood changes.
+
+1.4.1 (10th February 2012)
+-----
+* Added command line application.
+* Added 'rewrite_import_urls' option - Ability to rewrite relative url references inside imported css files.
+* Added Prepend.css - Optionally prepend css to every input.
+* Fix for issue #21.
+* Reorganized aliases file with some additions.
+* Initial-values updated.
+* Updated CssCrush::string method to correctly handle import statements.
+
+1.4 (24th January 2012)
+---
+* Added initial-keyword plugin (shim for the CSS3 keyword).
+* Added inline method (Issue #18).
+* Added ability to escape declarations from aliasing or plugins by prefixing with tilde.
+* Added procedural style public API to mirror the static class API.
+* Deprecated @variables syntax for @define. @variables still supported.
+* Adjusted color functions to accept a space delimiter (as well as comma) in the arguments list.
+* Surpressed some benign PHP warning messages.
+* Some internal cleaning up.
+* Disabled IE6 min-height plugin by default.
+
+1.3.6 (9th November 2011)
+-----
+* Improved color functions.
+* Added a-adjust function for altering a color's opacity.
+* Deprecated hsl-adjust function (you can use nested color functions instead).
+* Added the ability to use local versions of alias and plugin files so pull updates don't clobber local settings.
+
+1.3.5 (8th November 2011)
+-----
+* Added hook system for plugins.
+* Plugins split into seperate files.
+* Aliases and Plugins files renamed with '.ini' file extensions to be editor friendly.
+* Added opacity plugin.
+* Updated filter plugin.
+* Fixed nested custom function parsing (issue #14).
+
+1.3.4 (29th October 2011)
+-----
+* Added output_filename option.
+* Added vendor_target option.
+* Renamed 'macros' to the more general 'plugins' and split them into their own files.
+* Removed superfluous outer containing directory (update your include paths).
+
+1.3.3 (28th October 2011)
+-----
+* Fixed regression with absolute URL file imports (issue #12).
+* Fixed minification bug (issue #13).
+
+1.3.2 (18th October 2011)
+-----
+* Updated variable syntax.
+* Fixed minification bug.
+
+1.3.1 (9th October 2011)
+-----
+* Added support for svg and svgz data uris.
+* Added animation shorthand alias.
+* Added user-select alias.
+
+1.3 (20th September 2011)
+---
+* Added the public function CssCrush::string for processing raw strings of CSS.
+* Added color functions.
+* Added aliases for IE10.
+
+1.2 (8th September 2011)
+---
+* Rewritten the file importer.
+
+1.1 (2nd September 2011)
+---
+* Added support for global variables.
+* Added support for variable interpolation within string literals.
+* Added 'tag' method for outputting an html link tag instead of returning a filename.
+* Added values aliases, dynamic 'runtime' variables.
+* Added RGBA macro.
+* Added IE clip macro.
+* Added data uri function.
+* Minor correction to WAMP support.
+* Minor fix to rule API.
+
+1.0 (14th July 2011)
+---
+* Major refactoring.
+* Custom functions.
+* Optional boilerplate.
+* Double colon syntax shim.
+* Resolved document root issues.
+* Minification improvements.
+
+0.9 (20th September 2010)
+---
+* Initial release.
diff --git a/CHANGELOG.txt b/CHANGELOG.txt
deleted file mode 100644
index 99c711f..0000000
--- a/CHANGELOG.txt
+++ /dev/null
@@ -1,192 +0,0 @@
-1.9 (5th January 2013)
----
-- Added flexbox aliases for both 2009 and 2012 edition specs.
-- Added a legacy-flexbox plugin for auto-generating the flexbox 2009 spec equivilant properties.
-- Updated selector aliases to take arguments at runtime.
-- Updated plugin API to use distinct 'enable' and 'disable' handlers.
-- The disable option is now resolved before the enable option so you can easily disable all plugins
-  and then specify the plugins you want to apply.
-- Added functions API for defining custom functions inside plugins.
-- Improved gradient function aliasing to handle new angle keywords ('to left', 'at center' etc.).
-- Added svg-gradients plugin for simulating CSS3 gradients with data-uris.
-- Added formatting option so custom formatters can be defined for un-minified output (see wiki for options).
-- Added newlines option to force the style of newlines in output (see wiki for options).
-- Updated command line utility to use the new options.
-- Property/value aliases renamed as declaration aliases.
-- Classes now loaded via an autoloader, also some other refactoring for moving towards PSR-0 compliance.
-
-1.8 (13th November 2012)
----
-- Added selector aliasing with the @selector-alias directive.
-- Added output_dir option for specifying the destination of compiled files.
-- Added doc_root option for working around problems with server aliases or path rewrites.
-- Added viewport @-rule aliases.
-- Debug option renamed to 'minify'; debug option will still work as before but is deprecated.
-- New minify option optionally takes an array of advanced minification parameters.
-- Expanded trace option to take an optional array of log parameters;
-- log params available are stubs, selector_count, errors and compile_time.
-- Added CssCrush::stat method to retrieve logged parameters.
-- Improved cross OS support.
-- Improved minification.
-- Major refactoring.
-
-1.7 (28th September 2012)
----
-- Added trace option to output SASS compatible debug-info stubs for use with tools like FireSass.
-- Added @ifdefine directive for dynamically including/excluding parts of a CSS file based on the existence of variables.
-- Updated plugin API.
-- Added options for enabling and disabling plugins at runtime.
-- Added property sorter plugin.
-- Added support for SASS-like @include/@extend syntax for invoking mixins and extends.
-- Boilerplate option now accepts a filename string as a boilerplate template.
-- CssCrush::string method now uses document_root as a default context for finding linked resources.
-- Updated command line appication.
-- Updated aliases and initial value files.
-- Fixed parsing issue introduced in 1.6.1.
-
-1.6.1 (22nd August 2012)
------
-- Resolved issues #34 and #35.
-
-1.6 (1st August 2012)
----
-- Inheritance model improved to support adoption of pseudo classes and elements (see wiki).
-- Added rule self-referencing function this() and complimentary data-- properties.
-- Added rule referencing function query().
-- Added default value argument for variables.
-- Added hsl-adjust() and hsla-adjust() color functions.
-- Mixin and fragment arg() function can now be nested.
-- Commas are now optional when specifying arguments for most custom functions.
-- Double-colon plugin moved to core.
-- Option rewrite_import_urls now defaults to true.
-
-1.5.3 (13th June 2012)
------
-- Refactoring.
-- Fixed some test cases.
-
-1.5.2 (8th June 2012)
------
-- Resolved issue #32.
-- CssCrush::inline method now defaults to not printing a boilerplate.
-- Updated aliases file.
-
-1.5.1 (1st June 2012)
------
-- Extended mixins to work with abstract rules and regular rules.
-- Fixed issue with selector grouping and inheritance in combination.
-
-1.5 (21st May 2012)
----
-- New feature: Rule inheritance / abstract rules.
-- New feature: Block nesting.
-- New feature: Mixins.
-- New feature: Fragments.
-- Abstracted IO interface.
-- Added some error reporting.
-- Added spiffing.css plugin.
-- csscrush_tag method now uses media type 'all' by default.
-- Updated alias and initial-value tables.
-- Internal refactoring.
-- Resolved issues #23, #24, #27, #28 and #29.
-
-1.4.2 (14th March 2012)
------
-- Fixed bug with @import statement parsing.
-- Some minor under the hood changes.
-
-1.4.1 (10th February 2012)
------
-- Added command line application.
-- Added 'rewrite_import_urls' option - Ability to rewrite relative url references inside imported css files.
-- Added Prepend.css - Optionally prepend css to every input.
-- Fix for issue #21.
-- Reorganized aliases file with some additions.
-- Initial-values updated.
-- Updated CssCrush::string method to correctly handle import statements.
-
-1.4 (24th January 2012)
----
-- Added initial-keyword plugin (shim for the CSS3 keyword).
-- Added inline method (Issue #18).
-- Added ability to escape declarations from aliasing or plugins by prefixing with tilde.
-- Added procedural style public API to mirror the static class API.
-- Deprecated @variables syntax for @define. @variables still supported.
-- Adjusted color functions to accept a space delimiter (as well as comma) in the arguments list.
-- Surpressed some benign PHP warning messages.
-- Some internal cleaning up.
-- Disabled IE6 min-height plugin by default.
-
-1.3.6 (9th November 2011)
------
-- Improved color functions.
-- Added a-adjust function for altering a color's opacity.
-- Deprecated hsl-adjust function (you can use nested color functions instead).
-- Added the ability to use local versions of alias and plugin files so pull updates don't clobber local settings.
-
-1.3.5 (8th November 2011)
------
-- Added hook system for plugins.
-- Plugins split into seperate files.
-- Aliases and Plugins files renamed with '.ini' file extensions to be editor friendly.
-- Added opacity plugin.
-- Updated filter plugin.
-- Fixed nested custom function parsing (issue #14).
-
-1.3.4 (29th October 2011)
------
-- Added output_filename option.
-- Added vendor_target option.
-- Renamed 'macros' to the more general 'plugins' and split them into their own files.
-- Removed superfluous outer containing directory (update your include paths).
-
-1.3.3 (28th October 2011)
------
-- Fixed regression with absolute URL file imports (issue #12).
-- Fixed minification bug (issue #13).
-
-1.3.2 (18th October 2011)
------
-- Updated variable syntax.
-- Fixed minification bug.
-
-1.3.1 (9th October 2011)
------
-- Added support for svg and svgz data uris.
-- Added animation shorthand alias.
-- Added user-select alias.
-
-1.3 (20th September 2011)
----
-- Added the public function CssCrush::string for processing raw strings of CSS.
-- Added color functions.
-- Added aliases for IE10.
-
-1.2 (8th September 2011)
----
-- Rewritten the file importer.
-
-1.1 (2nd September 2011)
----
-- Added support for global variables.
-- Added support for variable interpolation within string literals.
-- Added 'tag' method for outputting an html link tag instead of returning a filename.
-- Added values aliases, dynamic 'runtime' variables.
-- Added RGBA macro.
-- Added IE clip macro.
-- Added data uri function.
-- Minor correction to WAMP support.
-- Minor fix to rule API.
-
-1.0 (14th July 2011)
----
-- Major refactoring.
-- Custom functions.
-- Optional boilerplate.
-- Double colon syntax shim.
-- Resolved document root issues.
-- Minification improvements.
-
-0.9 (20th September 2010)
----
-- Initial release.
diff --git a/lib/Core.php b/lib/Core.php
index 689eb52..71e6581 100644
--- a/lib/Core.php
+++ b/lib/Core.php
@@ -92,7 +92,7 @@ static public function init ( $seed_file )
         require_once self::$config->location . '/misc/formatters.php';
 
         // Include the procedural API functions.
-        require_once self::$config->location . '/misc/proc-api-functions.php';
+        require_once self::$config->location . '/misc/functions.php';
 
         // Initialise other classes.
         CssCrush_Regex::init();
diff --git a/misc/proc-api-functions.php b/misc/functions.php
similarity index 100%
rename from misc/proc-api-functions.php
rename to misc/functions.php

From 05416547984f740beae543a7d58f04eb5c3f378a Mon Sep 17 00:00:00 2001
From: Pete Boere 
Date: Fri, 11 Jan 2013 11:41:18 +0000
Subject: [PATCH 063/421] Moved core files into a vendor named directory for
 PSR-0 compliance. Moved license and version out of the bootstrap file.

---
 Aliases.ini                          |  2 +-
 CHANGELOG.md                         | 72 ++++++++++++++++------------
 CssCrush.php                         | 25 ++++------
 LICENSE.txt                          |  2 +-
 Prepend.css                          |  1 -
 lib/{ => CssCrush}/ArgList.php       |  2 +-
 lib/{ => CssCrush}/BalancedMatch.php |  0
 lib/{ => CssCrush}/Color.php         |  0
 lib/{ => CssCrush}/Core.php          | 24 ++++------
 lib/{ => CssCrush}/Declaration.php   |  4 +-
 lib/{ => CssCrush}/ExtendArg.php     |  0
 lib/{ => CssCrush}/Fragment.php      |  0
 lib/{ => CssCrush}/Function.php      |  4 +-
 lib/{ => CssCrush}/Hook.php          |  0
 lib/{ => CssCrush}/IO.php            |  0
 lib/{ => CssCrush}/Importer.php      |  0
 lib/{ => CssCrush}/Mixin.php         |  0
 lib/{ => CssCrush}/Options.php       |  0
 lib/{ => CssCrush}/Plugin.php        |  0
 lib/{ => CssCrush}/PostAliasFix.php  |  1 +
 lib/{ => CssCrush}/Process.php       |  6 +--
 lib/{ => CssCrush}/Regex.php         |  3 ++
 lib/{ => CssCrush}/Rule.php          | 26 +++++-----
 lib/{ => CssCrush}/Selector.php      |  0
 lib/{ => CssCrush}/Stream.php        |  0
 lib/{ => CssCrush}/Url.php           |  0
 lib/{ => CssCrush}/Util.php          |  0
 lib/{ => CssCrush}/Version.php       |  0
 {misc => lib}/functions.php          |  4 ++
 misc/formatters.php                  |  9 ++--
 30 files changed, 98 insertions(+), 87 deletions(-)
 rename lib/{ => CssCrush}/ArgList.php (97%)
 rename lib/{ => CssCrush}/BalancedMatch.php (100%)
 rename lib/{ => CssCrush}/Color.php (100%)
 rename lib/{ => CssCrush}/Core.php (96%)
 rename lib/{ => CssCrush}/Declaration.php (97%)
 rename lib/{ => CssCrush}/ExtendArg.php (100%)
 rename lib/{ => CssCrush}/Fragment.php (100%)
 rename lib/{ => CssCrush}/Function.php (98%)
 rename lib/{ => CssCrush}/Hook.php (100%)
 rename lib/{ => CssCrush}/IO.php (100%)
 rename lib/{ => CssCrush}/Importer.php (100%)
 rename lib/{ => CssCrush}/Mixin.php (100%)
 rename lib/{ => CssCrush}/Options.php (100%)
 rename lib/{ => CssCrush}/Plugin.php (100%)
 rename lib/{ => CssCrush}/PostAliasFix.php (99%)
 rename lib/{ => CssCrush}/Process.php (99%)
 rename lib/{ => CssCrush}/Regex.php (99%)
 rename lib/{ => CssCrush}/Rule.php (99%)
 rename lib/{ => CssCrush}/Selector.php (100%)
 rename lib/{ => CssCrush}/Stream.php (100%)
 rename lib/{ => CssCrush}/Url.php (100%)
 rename lib/{ => CssCrush}/Util.php (100%)
 rename lib/{ => CssCrush}/Version.php (100%)
 rename {misc => lib}/functions.php (89%)

diff --git a/Aliases.ini b/Aliases.ini
index 8a6730e..4e34d5b 100644
--- a/Aliases.ini
+++ b/Aliases.ini
@@ -114,7 +114,7 @@
     ;  shorthand.
     ;
     ; Support for the early 2012 syntax implemented in IE10 is achieved here in part with
-    ; property aliases, and in part with property/value aliases later in this file.
+    ; property aliases, and in part with declaration aliases later in this file.
     ;
     align-content[] = -webkit-align-content
     align-items[] = -webkit-align-items
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ac09e27..c898029 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,43 +3,55 @@
 * Added flexbox aliases for both 2009 and 2012 edition specs.
 * Added a legacy-flexbox plugin for auto-generating the flexbox 2009 spec equivilant properties.
 * Updated selector aliases to take arguments at runtime.
-* Updated plugin API to use distinct 'enable' and 'disable' handlers.
-* The disable option is now resolved before the enable option so you can easily disable all plugins
+* Updated plugin API to use distinct "enable" and "disable" handlers.
+* `disable` option is now resolved before the `enable` option so you can easily disable all plugins
   and then specify the plugins you want to apply.
 * Added functions API for defining custom functions inside plugins.
-* Improved gradient function aliasing to handle new angle keywords ('to left', 'at center' etc.).
+* Improved gradient function aliasing to handle new angle keywords (to left, at center, etc.).
 * Added svg-gradients plugin for simulating CSS3 gradients with data-uris.
-* Added formatting option so custom formatters can be defined for un-minified output (see wiki for options).
-* Added newlines option to force the style of newlines in output (see wiki for options).
+* Added `formatting` option for un-minified output. Possible values (custom formatters can also be defined):
+    * "block" (default) - Rules are block formatted.
+    * "single-line" - Rules are printed in single lines.
+    * "padded" - Rules are printed in single lines with right padded selectors.
+  Custom formatters can also be defined.
+* Added `newlines` option to set the style of newlines in output. Possible values:
+    * "use-platform" (default)
+    * "unix"
+    * "windows" or "win"
 * Updated command line utility to use the new options.
-* Property/value aliases renamed as declaration aliases.
+* Property/value aliases expanded and renamed as declaration aliases.
 * Classes now loaded via an autoloader, also some other refactoring for moving towards PSR-0 compliance.
 
 1.8 (13th November 2012)
 ---
-* Added selector aliasing with the @selector-alias directive.
-* Added output_dir option for specifying the destination of compiled files.
-* Added doc_root option for working around problems with server aliases or path rewrites.
+* Added selector aliasing with the `@selector-alias` directive.
+* Added `output_dir` option for specifying the destination of compiled files.
+* Added `doc_root` option for working around problems with server aliases or path rewrites.
 * Added viewport @-rule aliases.
-* Debug option renamed to 'minify'; debug option will still work as before but is deprecated.
-* New minify option optionally takes an array of advanced minification parameters.
-* Expanded trace option to take an optional array of log parameters;
-* log params available are stubs, selector_count, errors and compile_time.
-* Added CssCrush::stat method to retrieve logged parameters.
+* `debug` option renamed to `minify`; `debug` option will still work as before but is deprecated.
+* `minify` option takes an optional array of advanced minification parameters. Possible values:
+    * `colors`
+* Expanded `trace` option to take an optional array of log parameters. Possible values:
+    * `stubs`
+    * `selector_count`
+    * `errors`
+    * `compile_time`
+* Added `CssCrush::stat` method to retrieve logged parameters.
 * Improved cross OS support.
 * Improved minification.
 * Major refactoring.
 
 1.7 (28th September 2012)
 ---
-* Added trace option to output SASS compatible debug-info stubs for use with tools like FireSass.
-* Added @ifdefine directive for dynamically including/excluding parts of a CSS file based on the existence of variables.
+* Added `trace` option to output SASS compatible debug-info stubs for use with tools like FireSass.
+* Added `@ifdefine` directive for dynamically including/excluding parts of a CSS file based on the
+  existence of variables.
 * Updated plugin API.
 * Added options for enabling and disabling plugins at runtime.
 * Added property sorter plugin.
 * Added support for SASS-like @include/@extend syntax for invoking mixins and extends.
 * Boilerplate option now accepts a filename string as a boilerplate template.
-* CssCrush::string method now uses document_root as a default context for finding linked resources.
+* `CssCrush::string` method now uses document\_root as a default context for finding linked resources.
 * Updated command line appication.
 * Updated aliases and initial value files.
 * Fixed parsing issue introduced in 1.6.1.
@@ -51,14 +63,14 @@
 1.6 (1st August 2012)
 ---
 * Inheritance model improved to support adoption of pseudo classes and elements (see wiki).
-* Added rule self-referencing function this() and complimentary data-- properties.
-* Added rule referencing function query().
+* Added rule self-referencing function `this()` and complimentary data-* properties.
+* Added rule referencing function `query()`.
 * Added default value argument for variables.
-* Added hsl-adjust() and hsla-adjust() color functions.
-* Mixin and fragment arg() function can now be nested.
+* Added `hsl-adjust()` and `hsla-adjust()` color functions.
+* Mixin and fragment `arg()` function can now be nested.
 * Commas are now optional when specifying arguments for most custom functions.
 * Double-colon plugin moved to core.
-* Option rewrite_import_urls now defaults to true.
+* Option `rewrite_import_urls` now defaults to true.
 
 1.5.3 (13th June 2012)
 -----
@@ -68,7 +80,7 @@
 1.5.2 (8th June 2012)
 -----
 * Resolved issue #32.
-* CssCrush::inline method now defaults to not printing a boilerplate.
+* `CssCrush::inline` method now defaults to not printing a boilerplate.
 * Updated aliases file.
 
 1.5.1 (1st June 2012)
@@ -85,7 +97,7 @@
 * Abstracted IO interface.
 * Added some error reporting.
 * Added spiffing.css plugin.
-* csscrush_tag method now uses media type 'all' by default.
+* `CssCrush::tag` method now uses media type 'all' by default.
 * Updated alias and initial-value tables.
 * Internal refactoring.
 * Resolved issues #23, #24, #27, #28 and #29.
@@ -98,12 +110,12 @@
 1.4.1 (10th February 2012)
 -----
 * Added command line application.
-* Added 'rewrite_import_urls' option - Ability to rewrite relative url references inside imported css files.
+* Added `rewrite_import_urls` option - Ability to rewrite relative url references inside imported css files.
 * Added Prepend.css - Optionally prepend css to every input.
 * Fix for issue #21.
 * Reorganized aliases file with some additions.
 * Initial-values updated.
-* Updated CssCrush::string method to correctly handle import statements.
+* Updated `CssCrush::string` method to correctly handle import statements.
 
 1.4 (24th January 2012)
 ---
@@ -111,7 +123,7 @@
 * Added inline method (Issue #18).
 * Added ability to escape declarations from aliasing or plugins by prefixing with tilde.
 * Added procedural style public API to mirror the static class API.
-* Deprecated @variables syntax for @define. @variables still supported.
+* Deprecated `@variables` directive for `@define`. @variables still supported for next few releases.
 * Adjusted color functions to accept a space delimiter (as well as comma) in the arguments list.
 * Surpressed some benign PHP warning messages.
 * Some internal cleaning up.
@@ -120,7 +132,7 @@
 1.3.6 (9th November 2011)
 -----
 * Improved color functions.
-* Added a-adjust function for altering a color's opacity.
+* Added `a-adjust()` function for altering a color's opacity.
 * Deprecated hsl-adjust function (you can use nested color functions instead).
 * Added the ability to use local versions of alias and plugin files so pull updates don't clobber local settings.
 
@@ -158,7 +170,7 @@
 
 1.3 (20th September 2011)
 ---
-* Added the public function CssCrush::string for processing raw strings of CSS.
+* Added the public function `CssCrush::string` for processing raw strings of CSS.
 * Added color functions.
 * Added aliases for IE10.
 
@@ -170,7 +182,7 @@
 ---
 * Added support for global variables.
 * Added support for variable interpolation within string literals.
-* Added 'tag' method for outputting an html link tag instead of returning a filename.
+* Added `CssCrush::tag` method for outputting an html link tag instead of returning a filename.
 * Added values aliases, dynamic 'runtime' variables.
 * Added RGBA macro.
 * Added IE clip macro.
diff --git a/CssCrush.php b/CssCrush.php
index 0c25c14..dbd3220 100644
--- a/CssCrush.php
+++ b/CssCrush.php
@@ -1,24 +1,18 @@
 argFunction, array(
                     'arg' => array( $this, 'store' )
                 ));
diff --git a/lib/BalancedMatch.php b/lib/CssCrush/BalancedMatch.php
similarity index 100%
rename from lib/BalancedMatch.php
rename to lib/CssCrush/BalancedMatch.php
diff --git a/lib/Color.php b/lib/CssCrush/Color.php
similarity index 100%
rename from lib/Color.php
rename to lib/CssCrush/Color.php
diff --git a/lib/Core.php b/lib/CssCrush/Core.php
similarity index 96%
rename from lib/Core.php
rename to lib/CssCrush/Core.php
index 71e6581..fc1a965 100644
--- a/lib/Core.php
+++ b/lib/CssCrush/Core.php
@@ -6,6 +6,8 @@
  */
 class CssCrush
 {
+    const VERSION = '1.9';
+
     // Global settings.
     static public $config;
 
@@ -13,17 +15,15 @@ class CssCrush
     static public $process;
 
     // Init called once manually post class definition.
-    static public function init ( $seed_file )
+    static public function init ()
     {
         self::$config = new stdclass();
 
-        // Path to this installation.
-        self::$config->location = dirname( $seed_file );
+        // Path to the project root folder.
+        self::$config->location = dirname(dirname(dirname(__FILE__)));
 
-        // Get version ID from seed file.
-        $seed_file_contents = file_get_contents( $seed_file );
-        $match_count = preg_match( '~@version\s+([\d\.\w-]+)~', $seed_file_contents, $version_match );
-        self::$config->version = $match_count ? new CssCrush_Version( $version_match[1] ) : null;
+        // Establish version id.
+        self::$config->version = new CssCrush_Version( self::VERSION );
 
         // Set the docRoot reference.
         self::setDocRoot();
@@ -90,14 +90,6 @@ static public function init ( $seed_file )
 
         // Include and register stock formatters.
         require_once self::$config->location . '/misc/formatters.php';
-
-        // Include the procedural API functions.
-        require_once self::$config->location . '/misc/functions.php';
-
-        // Initialise other classes.
-        CssCrush_Regex::init();
-        CssCrush_Function::init();
-        CssCrush_PostAliasFix::init();
     }
 
     static protected function setDocRoot ( $doc_root = null )
@@ -545,3 +537,5 @@ static public function runStat ( $name )
         }
     }
 }
+
+CssCrush::init();
diff --git a/lib/Declaration.php b/lib/CssCrush/Declaration.php
similarity index 97%
rename from lib/Declaration.php
rename to lib/CssCrush/Declaration.php
index 3e14034..49dcea5 100644
--- a/lib/Declaration.php
+++ b/lib/CssCrush/Declaration.php
@@ -27,6 +27,7 @@ public function __construct ( $prop, $value, $contextIndex = 0 )
         // Check the input.
         if ( $prop === '' || $value === '' || $value === null ) {
             $this->isValid = false;
+
             return;
         }
 
@@ -55,12 +56,13 @@ public function __construct ( $prop, $value, $contextIndex = 0 )
         // Ignore declarations with null css values.
         if ( $value === false || $value === '' ) {
             $this->isValid = false;
+
             return;
         }
 
         // Apply custom functions.
         if ( ! $skip ) {
-            CssCrush_Function::executeCustomFunctions( $value );
+            CssCrush_Function::executeOnString( $value );
         }
 
         // Capture all remaining paren pairs.
diff --git a/lib/ExtendArg.php b/lib/CssCrush/ExtendArg.php
similarity index 100%
rename from lib/ExtendArg.php
rename to lib/CssCrush/ExtendArg.php
diff --git a/lib/Fragment.php b/lib/CssCrush/Fragment.php
similarity index 100%
rename from lib/Fragment.php
rename to lib/CssCrush/Fragment.php
diff --git a/lib/Function.php b/lib/CssCrush/Function.php
similarity index 98%
rename from lib/Function.php
rename to lib/CssCrush/Function.php
index 8f9faac..e5c5a6b 100644
--- a/lib/Function.php
+++ b/lib/CssCrush/Function.php
@@ -30,7 +30,7 @@ static public function setMatchPatt ()
         self::$functionPatt = CssCrush_Regex::createFunctionMatchPatt( array_keys( self::$customFunctions ), true );
     }
 
-    static public function executeCustomFunctions ( &$str, $patt = null, $process_callback = null, $property = null )
+    static public function executeOnString ( &$str, $patt = null, $process_callback = null, $property = null )
     {
         // No bracketed expressions, early return.
         if ( false === strpos( $str, '(' ) ) {
@@ -217,3 +217,5 @@ function csscrush_fn__a_adjust ( $input ) {
     list( $color, $a ) = array_pad( CssCrush_Function::parseArgs( $input, true ), 2, 0 );
     return CssCrush_Function::colorAdjust( $color, array( 0, 0, 0, $a ) );
 }
+
+CssCrush_Function::init();
diff --git a/lib/Hook.php b/lib/CssCrush/Hook.php
similarity index 100%
rename from lib/Hook.php
rename to lib/CssCrush/Hook.php
diff --git a/lib/IO.php b/lib/CssCrush/IO.php
similarity index 100%
rename from lib/IO.php
rename to lib/CssCrush/IO.php
diff --git a/lib/Importer.php b/lib/CssCrush/Importer.php
similarity index 100%
rename from lib/Importer.php
rename to lib/CssCrush/Importer.php
diff --git a/lib/Mixin.php b/lib/CssCrush/Mixin.php
similarity index 100%
rename from lib/Mixin.php
rename to lib/CssCrush/Mixin.php
diff --git a/lib/Options.php b/lib/CssCrush/Options.php
similarity index 100%
rename from lib/Options.php
rename to lib/CssCrush/Options.php
diff --git a/lib/Plugin.php b/lib/CssCrush/Plugin.php
similarity index 100%
rename from lib/Plugin.php
rename to lib/CssCrush/Plugin.php
diff --git a/lib/PostAliasFix.php b/lib/CssCrush/PostAliasFix.php
similarity index 99%
rename from lib/PostAliasFix.php
rename to lib/CssCrush/PostAliasFix.php
index 7f4afb4..f814037 100644
--- a/lib/PostAliasFix.php
+++ b/lib/CssCrush/PostAliasFix.php
@@ -114,3 +114,4 @@ function csscrush__post_alias_fix_radialgradients ( $declaration_copies, $fn_nam
     }
 }
 
+CssCrush_PostAliasFix::init();
diff --git a/lib/Process.php b/lib/CssCrush/Process.php
similarity index 99%
rename from lib/Process.php
rename to lib/CssCrush/Process.php
index 1bcab89..000f0a7 100644
--- a/lib/Process.php
+++ b/lib/CssCrush/Process.php
@@ -484,7 +484,7 @@ protected function calculateVariables ()
 
             // Variable values can be escaped from function parsing with a tilde prefix.
             if ( strpos( $value, '~' ) !== 0 ) {
-                CssCrush_Function::executeCustomFunctions( $value );
+                CssCrush_Function::executeOnString( $value );
             }
         }
     }
@@ -519,7 +519,7 @@ static protected function placeVariables ( &$value )
         if ( strpos( $value, '$(' ) !== false ) {
 
             // Variables with default value.
-            CssCrush_Function::executeCustomFunctions( $value, $regex->varFunctionStart,
+            CssCrush_Function::executeOnString( $value, $regex->varFunctionStart,
                 array( '$' => array( 'CssCrush_Process', 'cb_placeVariablesWithDefault' ) ) );
 
             // Assume at least 1 replace.
@@ -735,7 +735,7 @@ protected function processRules ()
                 $rule->addFunctionAliases();
             }
             if ( $aliases[ 'declarations' ] ) {
-                $rule->addPropertyValueAliases();
+                $rule->addDeclarationAliases();
             }
 
             CssCrush_Hook::run( 'rule_postalias', $rule );
diff --git a/lib/Regex.php b/lib/CssCrush/Regex.php
similarity index 99%
rename from lib/Regex.php
rename to lib/CssCrush/Regex.php
index c854bc0..f249474 100644
--- a/lib/Regex.php
+++ b/lib/CssCrush/Regex.php
@@ -119,3 +119,6 @@ static public function createFunctionMatchPatt ( $list, $include_math_function =
         return '~(?thisFunction, array(
                         'this'  => array( $this, 'cssThisFunction' ),
                     ), $prop );
@@ -176,6 +176,7 @@ public function __toString ()
 
             // De-reference this instance.
             unset( $process->tokens->r[ $this->label ] );
+
             return '';
         }
 
@@ -201,7 +202,7 @@ public function declarationCheckin ( $prop, $value, &$pairs )
         // First resolve query() calls that reference earlier rules.
         if ( preg_match( CssCrush_Regex::$patt->queryFunction, $value ) ) {
 
-            CssCrush_Function::executeCustomFunctions( $value,
+            CssCrush_Function::executeOnString( $value,
                 CssCrush_Regex::$patt->queryFunction, array(
                     'query' => array( $this, 'cssQueryFunction' ),
                 ), $prop );
@@ -240,14 +241,6 @@ public function declarationCheckin ( $prop, $value, &$pairs )
         }
     }
 
-    public function setDeclarations ( array $declaration_stack )
-    {
-        $this->declarations = $declaration_stack;
-
-        // Update the property index.
-        $this->updatePropertyIndex();
-    }
-
     public function updatePropertyIndex ()
     {
         // Create a new table of properties.
@@ -269,7 +262,7 @@ public function updatePropertyIndex ()
 
 
     #############################
-    #  Inheritance.
+    #  Rule inheritance.
 
     public function setExtendSelectors ( $raw_value )
     {
@@ -637,12 +630,13 @@ public function addFunctionAliases ()
         }
     }
 
-    public function addPropertyValueAliases ()
+    public function addDeclarationAliases ()
     {
         $declaration_aliases =& CssCrush::$process->aliases[ 'declarations' ];
 
         // First test for the existence of any aliased properties.
         if ( ! ( $intersect = array_intersect_key( $declaration_aliases, $this->properties ) ) ) {
+
             return;
         }
 
@@ -728,6 +722,14 @@ public function addDeclaration ( $prop, $value, $contextIndex = 0 )
         return false;
     }
 
+    public function setDeclarations ( array $declaration_stack )
+    {
+        $this->declarations = $declaration_stack;
+
+        // Update the property index.
+        $this->updatePropertyIndex();
+    }
+
     static public function get ( $token )
     {
         if ( isset( CssCrush::$process->tokens->r[ $token ] ) ) {
diff --git a/lib/Selector.php b/lib/CssCrush/Selector.php
similarity index 100%
rename from lib/Selector.php
rename to lib/CssCrush/Selector.php
diff --git a/lib/Stream.php b/lib/CssCrush/Stream.php
similarity index 100%
rename from lib/Stream.php
rename to lib/CssCrush/Stream.php
diff --git a/lib/Url.php b/lib/CssCrush/Url.php
similarity index 100%
rename from lib/Url.php
rename to lib/CssCrush/Url.php
diff --git a/lib/Util.php b/lib/CssCrush/Util.php
similarity index 100%
rename from lib/Util.php
rename to lib/CssCrush/Util.php
diff --git a/lib/Version.php b/lib/CssCrush/Version.php
similarity index 100%
rename from lib/Version.php
rename to lib/CssCrush/Version.php
diff --git a/misc/functions.php b/lib/functions.php
similarity index 89%
rename from misc/functions.php
rename to lib/functions.php
index b11ed64..d0daf2e 100644
--- a/misc/functions.php
+++ b/lib/functions.php
@@ -27,3 +27,7 @@ function csscrush_globalvars ( $vars ) {
 function csscrush_clearcache ( $dir = '' ) {
     return CssCrush::clearcache( $dir );
 }
+
+function csscrush_stat ( $name = null ) {
+    return CssCrush::stat( $name );
+}
diff --git a/misc/formatters.php b/misc/formatters.php
index 0dcfb44..209d1c0 100644
--- a/misc/formatters.php
+++ b/misc/formatters.php
@@ -26,7 +26,7 @@ function csscrush__fmtr_single ( $rule ) {
     return "$comments$stub$selectors { $block; }$EOL";
 }
 
-function csscrush__fmtr_padded ( $rule ) {
+function csscrush__fmtr_padded ( $rule, $padding = 40 ) {
 
     $EOL = CssCrush::$process->newline;
     if ( $stub = $rule->tracingStub ) {
@@ -38,16 +38,15 @@ function csscrush__fmtr_padded ( $rule ) {
         $comments = "$EOL$comments";
     }
 
-    $cutoff = 40;
     $selectors = implode( ", ", $rule->selectors );
     $block = implode( "; ", $rule->declarations );
 
-    if ( strlen( $selectors ) > $cutoff ) {
-        $padding = str_repeat( ' ', $cutoff );
+    if ( strlen( $selectors ) > $padding ) {
+        $padding = str_repeat( ' ', $padding );
         return "$comments$stub$selectors$EOL$padding { $block; }$EOL";
     }
     else {
-        $selectors = str_pad( $selectors, $cutoff );
+        $selectors = str_pad( $selectors, $padding );
         return "$comments$stub$selectors { $block; }$EOL";
     }
 }

From bcf8b56daf5f4ff43d2827e4bf17ef725aa99f13 Mon Sep 17 00:00:00 2001
From: Pete Boere 
Date: Fri, 11 Jan 2013 13:05:01 +0000
Subject: [PATCH 064/421] Fix for command line utility when OS X terminal
 truncates visible output.

---
 CHANGELOG.md | 2 +-
 cli.php      | 6 ++++--
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index c898029..d8cbbb6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,4 @@
-1.9 (5th January 2013)
+1.9 (11th January 2013)
 ---
 * Added flexbox aliases for both 2009 and 2012 edition specs.
 * Added a legacy-flexbox plugin for auto-generating the flexbox 2009 spec equivilant properties.
diff --git a/cli.php b/cli.php
index 4efdcbe..e4ed59d 100755
--- a/cli.php
+++ b/cli.php
@@ -31,7 +31,9 @@ function stderr ( $lines, $closing_newline = true ) {
 
 function stdout ( $lines, $closing_newline = true ) {
     $out = implode( PHP_EOL, (array) $lines ) . ( $closing_newline ? PHP_EOL : '' );
-    fwrite( STDOUT, $out );
+    // On OSX terminal is sometimes truncating 'visual' output to terminal
+    // with fwrite to STDOUT.
+    echo $out;
 }
 
 
@@ -197,7 +199,7 @@ function stdout ( $lines, $closing_newline = true ) {
 if ( $input_file ) {
 
     if ( ! file_exists( $input_file ) ) {
-        stdout( 'Input file not found' . PHP_EOL );
+        stderr( 'Input file not found' . PHP_EOL );
         exit( STATUS_ERROR );
     }
     $input = file_get_contents( $input_file );

From c2f6903c28dc19f26a8b58fd46d407e8cc442e9e Mon Sep 17 00:00:00 2001
From: Pete Boere 
Date: Fri, 11 Jan 2013 17:03:02 +0000
Subject: [PATCH 065/421] Added a workaround for issue #41 (stream_set_blocking
 not working with php://stdin on Windows)

---
 cli.php | 19 +++++++++----------
 1 file changed, 9 insertions(+), 10 deletions(-)

diff --git a/cli.php b/cli.php
index e4ed59d..dee7014 100755
--- a/cli.php
+++ b/cli.php
@@ -11,15 +11,6 @@
 define( 'STATUS_OK', 0 );
 define( 'STATUS_ERROR', 1 );
 
-// Get stdin content.
-$stdin = fopen( 'php://stdin', 'r' );
-if ( ! stream_set_blocking( $stdin, false ) ) {
-    stderr( 'Failed to disable stdin blocking' );
-    exit( STATUS_ERROR );
-}
-$stdin_contents = stream_get_contents( $stdin );
-fclose( $stdin );
-
 
 ##################################################################
 ##  Helpers.
@@ -36,6 +27,14 @@ function stdout ( $lines, $closing_newline = true ) {
     echo $out;
 }
 
+function get_stdin_contents () {
+    $stdin = fopen( 'php://stdin', 'r' );
+    stream_set_blocking( $stdin, false );
+    $stdin_contents = stream_get_contents( $stdin );
+    fclose( $stdin );
+    return $stdin_contents;
+}
+
 
 ##################################################################
 ##  Version detection.
@@ -204,7 +203,7 @@ function stdout ( $lines, $closing_newline = true ) {
     }
     $input = file_get_contents( $input_file );
 }
-elseif ( $stdin_contents ) {
+elseif ( $stdin_contents = get_stdin_contents() ) {
 
     $input = $stdin_contents;
 }

From 9e2e51080a2b447321363da0151b40fb8fce7005 Mon Sep 17 00:00:00 2001
From: Pete Boere 
Date: Fri, 11 Jan 2013 17:03:02 +0000
Subject: [PATCH 066/421] Added a workaround for issue #41 (stream_set_blocking
 not working with php://stdin on Windows)

---
 CHANGELOG.md |  2 +-
 cli.php      | 29 +++++++++++++----------------
 2 files changed, 14 insertions(+), 17 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index d8cbbb6..e3a1a81 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,4 @@
-1.9 (11th January 2013)
+1.9 (12th January 2013)
 ---
 * Added flexbox aliases for both 2009 and 2012 edition specs.
 * Added a legacy-flexbox plugin for auto-generating the flexbox 2009 spec equivilant properties.
diff --git a/cli.php b/cli.php
index e4ed59d..ca3e38f 100755
--- a/cli.php
+++ b/cli.php
@@ -11,15 +11,6 @@
 define( 'STATUS_OK', 0 );
 define( 'STATUS_ERROR', 1 );
 
-// Get stdin content.
-$stdin = fopen( 'php://stdin', 'r' );
-if ( ! stream_set_blocking( $stdin, false ) ) {
-    stderr( 'Failed to disable stdin blocking' );
-    exit( STATUS_ERROR );
-}
-$stdin_contents = stream_get_contents( $stdin );
-fclose( $stdin );
-
 
 ##################################################################
 ##  Helpers.
@@ -36,6 +27,14 @@ function stdout ( $lines, $closing_newline = true ) {
     echo $out;
 }
 
+function get_stdin_contents () {
+    $stdin = fopen( 'php://stdin', 'r' );
+    stream_set_blocking( $stdin, false );
+    $stdin_contents = stream_get_contents( $stdin );
+    fclose( $stdin );
+    return $stdin_contents;
+}
+
 
 ##################################################################
 ##  Version detection.
@@ -102,8 +101,6 @@ function stdout ( $lines, $closing_newline = true ) {
 ##################################################################
 ##  Help page.
 
-$command = 'csscrush';
-
 $help = <<
Date: Mon, 14 Jan 2013 16:49:56 +0000
Subject: [PATCH 067/421] Issue #42 (@import don't work under Windows using
 cli.php)

Additionally the --context option was casting to array (typo).
---
 CHANGELOG.md             |  5 +++++
 cli.php                  |  2 +-
 lib/CssCrush/Core.php    | 13 +++++++++++--
 lib/CssCrush/Options.php |  1 +
 4 files changed, 18 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index e3a1a81..eca4b66 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,8 @@
+1.9.1 (? January 2013)
+-----
+* Resolved issue #42.
+* Fixed command line context option.
+
 1.9 (12th January 2013)
 ---
 * Added flexbox aliases for both 2009 and 2012 edition specs.
diff --git a/cli.php b/cli.php
index ca3e38f..5ca774f 100755
--- a/cli.php
+++ b/cli.php
@@ -95,7 +95,7 @@ function get_stdin_contents () {
 $newlines = @$opts['newlines'];
 $enable_plugins = isset( $opts['enable'] ) ? (array) $opts['enable'] : null;
 $disable_plugins = isset( $opts['disable'] ) ? (array) $opts['disable'] : null;
-$context = isset( $opts['context'] ) ? (array) $opts['context'] : null;
+$context = isset( $opts['context'] ) ? $opts['context'] : null;
 
 
 ##################################################################
diff --git a/lib/CssCrush/Core.php b/lib/CssCrush/Core.php
index fc1a965..5194e0f 100644
--- a/lib/CssCrush/Core.php
+++ b/lib/CssCrush/Core.php
@@ -6,7 +6,7 @@
  */
 class CssCrush
 {
-    const VERSION = '1.9';
+    const VERSION = '1.9.1';
 
     // Global settings.
     static public $config;
@@ -113,7 +113,16 @@ static protected function setDocRoot ( $doc_root = null )
                 // Check $script_filename ends with $script_name
                 if ( substr( $script_filename, $len_diff ) === $script_name ) {
 
-                    $doc_root = realpath( substr( $script_filename, 0, $len_diff ) );
+                    $path = substr( $script_filename, 0, $len_diff );
+
+                    // An empty string passed to realpath gives unpredictable results
+                    // on command line.
+                    if ( $path === '' ) {
+                        $doc_root = dirname( realpath( $script_filename ) );
+                    }
+                    else {
+                        $doc_root = realpath( $path );
+                    }
                 }
             }
 
diff --git a/lib/CssCrush/Options.php b/lib/CssCrush/Options.php
index 1fdbed5..72ee3e0 100644
--- a/lib/CssCrush/Options.php
+++ b/lib/CssCrush/Options.php
@@ -48,6 +48,7 @@ public function __set ( $name, $value )
                 break;
 
             // Sanitize path options.
+            case 'context':
             case 'doc_root':
                 if ( is_string( $value ) ) {
                     $value = CssCrush_Util::normalizePath( $value );

From b295c865a77f7b67d0d8d4ec0408c6745b059889 Mon Sep 17 00:00:00 2001
From: Pete Boere 
Date: Fri, 18 Jan 2013 12:24:49 +0000
Subject: [PATCH 068/421] Added noise/texture generating functions with the
 noise plugin. Set Plugins.ini to enable no plugins by default, seems more
 consistant this way.

---
 CHANGELOG.md              |   1 +
 Plugins.ini               |  32 ++++--
 lib/CssCrush/Regex.php    |   4 +-
 lib/CssCrush/Util.php     |   3 +-
 plugins/noise.php         | 233 ++++++++++++++++++++++++++++++++++++++
 plugins/svg-gradients.php |   4 +-
 6 files changed, 262 insertions(+), 15 deletions(-)
 create mode 100644 plugins/noise.php

diff --git a/CHANGELOG.md b/CHANGELOG.md
index eca4b66..381f0d4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,7 @@
 -----
 * Resolved issue #42.
 * Fixed command line context option.
+* Added noise/texture generating functions with the noise plugin.
 
 1.9 (12th January 2013)
 ---
diff --git a/Plugins.ini b/Plugins.ini
index 17e97ee..a832fee 100644
--- a/Plugins.ini
+++ b/Plugins.ini
@@ -1,38 +1,48 @@
 ;----------------------------------------------------------------
 ;
 ; Enable or disable plugins to suit your needs.
+; Plugins can be enabled and disabled at runtime with "disable" and "enable" options.
 ;
 ; See plugin docs for details: https://github.com/peteboere/css-crush/wiki/Plugins
 ;
 ;----------------------------------------------------------------
 
 ; Property sorting
-; plugins[] = property-sorter
+; plugins[] = property-sorter.
 
-; min-height shim for IE < 7
+; min-height shim for IE < 7.
 ; plugins[] = ie-min-height
 
-; inline-block shim for IE < 8
+; inline-block shim for IE < 8.
 ; plugins[] = ie-inline-block
 
-; clip property shim for IE < 8
+; clip property shim for IE < 8.
 ; plugins[] = ie-clip
 
-; IE filter shorthand
+; IE filter shorthand.
 ; plugins[] = ie-filter
 
-; Opacity for IE < 9 (uses filter)
+; Opacity for IE < 9 (uses filter).
 ; plugins[] = ie-opacity
 
-; HSL shim - converts HSL values to hex codes
+; HSL shim - converts HSL values to hex codes.
 ; plugins[] = hsl-to-hex
 
-; Non-standard composite pseudo classes
-plugins[] = hocus-pocus
+; Non-standard composite pseudo classes.
+; plugins[] = hocus-pocus
 
-; CSS3 'initial' keyword shim
-plugins[] = initial
+; CSS3 'initial' keyword shim.
+; plugins[] = initial
 
 ; Transforms correctly-spelt Queen's English into valid CSS (http://spiffingcss.com)
 ; plugins[] = spiffing
 
+; Auto generates legacy flexbox equivilant syntax.
+; plugins[] = legacy-flexbox
+
+; Functions for creating gradients with svg data-uris.
+; plugins[] = svg-gradients
+
+; Functions for generating noise and textures with svg data-uris.
+; plugins[] = noise
+
diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php
index f249474..271d102 100644
--- a/lib/CssCrush/Regex.php
+++ b/lib/CssCrush/Regex.php
@@ -19,9 +19,11 @@ static public function init ()
 
         // Character classes.
         $classes->ident = '[a-zA-Z0-9_-]+';
+        $classes->number = '[+-]?\d*\.?\d+';
 
         // Patterns.
-        $patt->ident = '!^' . $classes->ident . '$!';
+        $patt->ident = '~^' . $classes->ident . '$~';
+        $patt->number = '~^' . $classes->number . '$~';
 
         // @-rule blocks.
         $patt->import        = '~@import\s+(\?u\d+\?)\s?([^;]*);~iS';
diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php
index 6eb0b74..8c64c03 100644
--- a/lib/CssCrush/Util.php
+++ b/lib/CssCrush/Util.php
@@ -116,7 +116,8 @@ static public function splitDelimList ( $str, $delim = ',', $trim = true )
         }
 
         if ( $trim ) {
-            $list = array_map( 'trim', $list );
+            // Trim items and remove empty strings.
+            $list = array_filter( array_map( 'trim', $list ), 'strlen' );
         }
 
         return $list;
diff --git a/plugins/noise.php b/plugins/noise.php
new file mode 100644
index 0000000..b13d1bb
--- /dev/null
+++ b/plugins/noise.php
@@ -0,0 +1,233 @@
+ ||  ]?
+ *          [ ,  ? ? ]?
+ *          [ ,  ||  ]?
+ *          [ ,   ]?
+ *      )
+ * 
+ *      
+ *          Any valid CSS color value.
+ *      
+ *          Pixel size of canvas in format WxH (e.g. 320x480).
+ *      
+ *          Number. Noise frequency; useful values are between 0 and 1.
+ *          x and y frequencies can be specified by joining two numbers with a colon.
+ *      
+ *          Number. Noise complexity.
+ *      
+ *          Noise sharpening; possible values:
+ *          "normal", "sharpen"
+ *      
+ *          Blend mode for overlaying noise filter; possible values:
+ *          "normal", "multiply", "screen", "darken", "lighten"
+ *      
+ *          Ranged number (0-1). Opacity of noise effect.
+ *      
+ *          Color filter type; possible values:
+ *          "hueRotate", "saturate"
+ *      
+ *          Mixed. For "hueRotate" a degree as number. For "saturate" a ranged number (0-1).
+ * 
+ * Returns:
+ *      A base64 encoded svg data-uri.
+ * 
+ * References:
+ *      http://www.w3.org/TR/SVG/filters.html
+ *      http://srufaculty.sru.edu/david.dailey/svg/SVGOpen2010/Filters2.htm#S11
+ * 
+ * Examples:
+ *      // Grainy noise with 50% opacity and de-saturated.
+ *      // Demonstrates the "default" keyword for skipping arguments.
+ *      background-image: noise( slategray, default, .5, saturate 0 );
+ * 
+ *      // Cloud effect.
+ *      background: noise( 700x700 skyblue, .01 4 normal, screen, saturate 0 );
+ * 
+ * turbulence()
+ * ------------
+ * As noise() except uses default turbulance type 'turbulance' and not 'fractalNoise'
+ * 
+ * Syntax:
+ *      See noise().
+ *
+ * Returns:
+ *      See noise().
+ * 
+ * References:
+ *      See noise().
+ *
+ * Examples:
+ *      // Typical turbulence effect.
+ *      background: turbulence();
+ * 
+ *      // Sand effect.
+ *      background: turbulence( wheat 400x400, .35:.2 4 sharpen, normal, saturate .4 );;
+ */
+
+CssCrush_Plugin::register( 'noise', array(
+    'enable' => 'csscrush__enable_noise',
+    'disable' => 'csscrush__disable_noise',
+));
+
+function csscrush__enable_noise () {
+    CssCrush_Function::register( 'noise', 'csscrush_fn__noise' );
+    CssCrush_Function::register( 'turbulence', 'csscrush_fn__turbulence' );
+}
+
+function csscrush__disable_noise () {
+    CssCrush_Function::deRegister( 'noise' );
+    CssCrush_Function::deRegister( 'turbulence' );
+}
+
+function csscrush_fn__noise ( $input ) {
+    return csscrush__noise_generator( $input, array(
+        'type' => 'fractalNoise',
+        'frequency' => .7,
+        'sharpen' => 'sharpen',
+        'dimensions' => array( 150, 150 ),
+    ));
+}
+
+function csscrush_fn__turbulence ( $input ) {
+    return csscrush__noise_generator( $input, array(
+        'type' => 'turbulence',
+        'frequency' => .01,
+        'sharpen' => 'normal',
+        'dimensions' => array( 200, 200 ),
+    ));
+}
+
+function csscrush__noise_generator ( $input, $defaults ) {
+
+    $args = array_pad( CssCrush_Function::parseArgs( $input ), 4, 'default' );
+
+    $type = $defaults[ 'type' ];
+
+    // Color-fill and dimensions.
+    $fill_color = 'transparent';
+    $dimensions = $defaults[ 'dimensions' ];
+    if ( ( $arg = array_shift( $args ) ) !== 'default' ) {
+        // May be a color function so explode(' ', $value) is not sufficient.
+        foreach( CssCrush_Function::parseArgs( $arg, true ) as $part ) {
+            if ( preg_match( '~^(\d+)x(\d+)$~i', $part, $m ) ) {
+                $dimensions = array_slice( $m, 1 );
+            }
+            else {
+                $fill_color = $part;
+            }
+        }
+    }
+
+    // Frequency, octaves and sharpening.
+    static $sharpen_modes = array( 'normal', 'sharpen' );
+    $frequency = $defaults[ 'frequency' ];
+    $octaves = 1;
+    $sharpen = $defaults[ 'sharpen' ];
+    $number_patt = CssCrush_Regex::$patt->number;
+
+    if ( ( $arg = array_shift( $args ) ) !== 'default' ) {
+        foreach( explode( ' ', $arg ) as $index => $value ) {
+            switch ( $index ) {
+                case 0:
+                    // x and y frequency values can be specified by joining with a colon.
+                    $frequency = str_replace( ':', ',', $value );
+                    break;
+                case 1:
+                case 2:
+                    if ( preg_match( $number_patt, $value ) ) {
+                        $octaves = $value;
+                    }
+                    elseif ( in_array( $value, $sharpen_modes ) ) {
+                        $sharpen = $value;
+                    }
+            }
+        }
+    }
+
+    // Blend-mode and fade.
+    static $blend_modes = array( 'normal', 'multiply', 'screen', 'darken', 'lighten' );
+    $blend_mode = 'normal';
+    $opacity = 1;
+    if ( ( $arg = array_shift( $args ) ) !== 'default' ) {
+        foreach( explode( ' ', $arg ) as $part ) {
+            if ( ctype_alpha( $part ) ) {
+                if ( in_array( $part, $blend_modes ) ) {
+                    $blend_mode = $part;
+                }
+            }
+            else {
+                $opacity = $part;
+            }
+        }
+    }
+
+    // Color filter.
+    static $color_filters = array( 'saturate', 'hueRotate', 'luminanceToAlpha' );
+    $color_filter = null;
+    if ( ( $arg = array_shift( $args ) ) !== 'default' ) {
+        // Saturate by default.
+        $color_filter = array( 'saturate', 1 );
+        foreach( explode( ' ', $arg ) as $part ) {
+            if ( ctype_alpha( $part ) ) {
+                if ( in_array( $part, $color_filters ) ) {
+                    $color_filter[0] = $part;
+                }
+            }
+            else {
+                $color_filter[1] = $part;
+            }
+        }
+    }
+
+    // Creating the svg.
+    $svg = "";
+    $svg .= '';
+    $svg .= "";
+
+    $svg .= "";
+
+    $component_adjustments = array();
+    if ( $sharpen === 'sharpen' ) {
+        // It's more posterizing than sharpening, but it hits a sweet spot.
+        $component_adjustments[] = "";
+        $component_adjustments[] = "";
+        // Some unpredictable results with this:
+        // $component_adjustments[] = "";
+    }
+    if ( $opacity != '1' ) {
+        $component_adjustments[] = "";
+    }
+    if ( $component_adjustments ) {
+        $svg .= "";
+        $svg .= implode( '', $component_adjustments );
+        $svg .= "";
+    }
+
+    if ( $color_filter ) {
+        $svg .= "";
+    }
+    if ( $blend_mode !== 'normal' ) {
+        $svg .= "";
+    }
+    $svg .= '';
+    $svg .= '';
+    $svg .= "";
+    $svg .= "";
+    $svg .= '';
+    // csscrush::log($svg);
+
+    // Create data-uri url and return token label.
+    $url = new CssCrush_Url( 'data:image/svg+xml;base64,' . base64_encode( $svg ) );
+
+    return $url->label;
+}
diff --git a/plugins/svg-gradients.php b/plugins/svg-gradients.php
index 2fa6d5d..eec04db 100644
--- a/plugins/svg-gradients.php
+++ b/plugins/svg-gradients.php
@@ -52,8 +52,8 @@ function csscrush__enable_svg_gradients () {
 }
 
 function csscrush__disable_svg_gradients () {
-    CssCrush_Function::deRegister( 'svg-linear-gradient', 'csscrush_fn__svg_linear_gradient' );
-    CssCrush_Function::deRegister( 'svg-radial-gradient', 'csscrush_fn__svg_radial_gradient' );
+    CssCrush_Function::deRegister( 'svg-linear-gradient' );
+    CssCrush_Function::deRegister( 'svg-radial-gradient' );
 }
 
 function csscrush_fn__svg_linear_gradient ( $input ) {

From 2a466f821cd709e2b83af5351eb39b2d5d1144a5 Mon Sep 17 00:00:00 2001
From: Pete Boere 
Date: Fri, 25 Jan 2013 17:05:08 +0000
Subject: [PATCH 069/421] linear-gradient functions with angles specified in
 degrees now convert child vendor aliases to match (#43)

---
 CHANGELOG.md                  |  2 +-
 lib/CssCrush/PostAliasFix.php | 86 ++++++++++++++++++++++++-----------
 plugins/noise.php             |  2 +-
 3 files changed, 62 insertions(+), 28 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 381f0d4..c5312c9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,7 +15,7 @@
 * Added functions API for defining custom functions inside plugins.
 * Improved gradient function aliasing to handle new angle keywords (to left, at center, etc.).
 * Added svg-gradients plugin for simulating CSS3 gradients with data-uris.
-* Added `formatting` option for un-minified output. Possible values (custom formatters can also be defined):
+* Added `formatter` option for un-minified output. Possible values (custom formatters can also be defined):
     * "block" (default) - Rules are block formatted.
     * "single-line" - Rules are printed in single lines.
     * "padded" - Rules are printed in single lines with right padded selectors.
diff --git a/lib/CssCrush/PostAliasFix.php b/lib/CssCrush/PostAliasFix.php
index f814037..745a300 100644
--- a/lib/CssCrush/PostAliasFix.php
+++ b/lib/CssCrush/PostAliasFix.php
@@ -39,12 +39,14 @@ static public function remove ( $alias_type, $key )
     }
 }
 
-function csscrush__post_alias_fix_lineargradients ( $declaration_copies, $fn_name ) {
+CssCrush_PostAliasFix::init();
 
-    // Swap the new 'to' gradient syntax to the old 'from' syntax for the prefixed versions.
-    // 1. Create new paren tokens based on the first prefixed declaration.
-    // 2. Replace the new syntax with the legacy syntax.
-    // 3. Swap in the new tokens on all the prefixed declarations.
+
+/**
+ * Convert the new angle syntax (keyword and degree) on -x-linear-gradient() functions
+ * to legacy equivalents.
+ */
+function csscrush__post_alias_fix_lineargradients ( $declaration_copies, $fn_name ) {
 
     static $angles_new, $angles_old;
     if ( ! $angles_new ) {
@@ -67,34 +69,64 @@ function csscrush__post_alias_fix_lineargradients ( $declaration_copies, $fn_nam
         $angles_old = array_values( $angles );
     }
 
-    // 1, 2.
-    $patt = '~(?number . ')deg\b~i';
+        // Legacy angles move anti-clockwise and start from East, not North.
+        $deg_convert_callback = create_function( '$m', '
+            $angle = floatval( $m[1] );
+            $angle = ( $angle + 90 ) - ( $angle * 2 );
+            return ( $angle < 0 ? $angle + 360 : $angle ) . \'deg\';
+        ');
+    }
+
+    // Create new paren tokens based on the first prefixed declaration.
+    // Replace the new syntax with the legacy syntax.
     $original_parens = array();
     $replacement_parens = array();
-    foreach ( CssCrush_Regex::matchAll( $patt, $declaration_copies[0]->value ) as $m ) {
+    $fn_patt = '~(?value ) as $m ) {
+
         $original_parens[] = $m[1][0];
-        $replacement_parens[] = CssCrush::$process->addToken(
-            str_ireplace(
-                $angles_new,
-                $angles_old,
-                CssCrush::$process->fetchToken( $m[1][0] )
-            ), 'p' );
+        $original_paren_value = CssCrush::$process->fetchToken( $m[1][0] );
+
+        // Convert keyword angle values.
+        $updated_paren_value = str_ireplace(
+            $angles_new,
+            $angles_old,
+            $original_paren_value
+        );
+
+        // Convert degree angle values.
+        $updated_paren_value = preg_replace_callback(
+            $deg_patt,
+            $deg_convert_callback,
+            $updated_paren_value
+        );
+
+        $replacement_parens[] = CssCrush::$process->addToken( $updated_paren_value, 'p' );
     }
 
-    // 3.
+    // Swap in the new tokens on all the prefixed declarations.
     foreach ( $declaration_copies as $prefixed_copy ) {
-        $prefixed_copy->value = str_replace( $original_parens, $replacement_parens, $prefixed_copy->value );
+        $prefixed_copy->value = str_replace(
+            $original_parens,
+            $replacement_parens,
+            $prefixed_copy->value
+        );
     }
 }
 
+/**
+ * Remove the 'at' keyword from -x-radial-gradient() for legacy implementations.
+ */
 function csscrush__post_alias_fix_radialgradients ( $declaration_copies, $fn_name ) {
 
-    // Remove the new 'at' keyword from gradient syntax for legacy implementations.
-    // 1. Create new paren tokens based on the first prefixed declaration.
-    // 2. Replace the new syntax with the legacy syntax.
-    // 3. Swap in the new tokens on all the prefixed declarations.
-
-    // 1, 2.
+    // Create new paren tokens based on the first prefixed declaration.
+    // Replace the new syntax with the legacy syntax.
     $patt = '~(?value = str_replace( $original_parens, $replacement_parens, $prefixed_copy->value );
+        $prefixed_copy->value = str_replace(
+            $original_parens,
+            $replacement_parens,
+            $prefixed_copy->value
+        );
     }
 }
-
-CssCrush_PostAliasFix::init();
diff --git a/plugins/noise.php b/plugins/noise.php
index b13d1bb..c53adea 100644
--- a/plugins/noise.php
+++ b/plugins/noise.php
@@ -71,7 +71,7 @@
  *      background: turbulence();
  * 
  *      // Sand effect.
- *      background: turbulence( wheat 400x400, .35:.2 4 sharpen, normal, saturate .4 );;
+ *      background: turbulence( wheat 400x400, .35:.2 4 sharpen, normal, saturate .4 );
  */
 
 CssCrush_Plugin::register( 'noise', array(

From 608f5807b474b623f29e1d476cb94d4a2d9c05f0 Mon Sep 17 00:00:00 2001
From: Pete Boere 
Date: Fri, 25 Jan 2013 17:05:08 +0000
Subject: [PATCH 070/421] linear-gradient functions with angles specified in
 degrees now convert child vendor aliases to match (#43). Updated changelog.

---
 CHANGELOG.md                  |  8 ++--
 cli.php                       |  2 +-
 lib/CssCrush/PostAliasFix.php | 86 ++++++++++++++++++++++++-----------
 plugins/noise.php             |  2 +-
 4 files changed, 66 insertions(+), 32 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 381f0d4..1d4ca51 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,8 +1,8 @@
-1.9.1 (? January 2013)
+1.9.1 (30th January 2013)
 -----
-* Resolved issue #42.
+* Resolved issues #42 and #43.
 * Fixed command line context option.
-* Added noise/texture generating functions with the noise plugin.
+* Added noise plugin (noise/texture generating functions).
 
 1.9 (12th January 2013)
 ---
@@ -15,7 +15,7 @@
 * Added functions API for defining custom functions inside plugins.
 * Improved gradient function aliasing to handle new angle keywords (to left, at center, etc.).
 * Added svg-gradients plugin for simulating CSS3 gradients with data-uris.
-* Added `formatting` option for un-minified output. Possible values (custom formatters can also be defined):
+* Added `formatter` option for un-minified output. Possible values (custom formatters can also be defined):
     * "block" (default) - Rules are block formatted.
     * "single-line" - Rules are printed in single lines.
     * "padded" - Rules are printed in single lines with right padded selectors.
diff --git a/cli.php b/cli.php
index 5ca774f..5e5cda6 100755
--- a/cli.php
+++ b/cli.php
@@ -263,7 +263,7 @@ function get_stdin_contents () {
 
 // Resolve a context for URLs.
 if ( ! $context ) {
-    $context = $input_file ? dirname( realpath( $input_file ) ) : null;
+    $context = $input_file ? dirname( realpath( $input_file ) ) : getcwd();
 }
 
 // If there is an import context set document root also.
diff --git a/lib/CssCrush/PostAliasFix.php b/lib/CssCrush/PostAliasFix.php
index f814037..745a300 100644
--- a/lib/CssCrush/PostAliasFix.php
+++ b/lib/CssCrush/PostAliasFix.php
@@ -39,12 +39,14 @@ static public function remove ( $alias_type, $key )
     }
 }
 
-function csscrush__post_alias_fix_lineargradients ( $declaration_copies, $fn_name ) {
+CssCrush_PostAliasFix::init();
 
-    // Swap the new 'to' gradient syntax to the old 'from' syntax for the prefixed versions.
-    // 1. Create new paren tokens based on the first prefixed declaration.
-    // 2. Replace the new syntax with the legacy syntax.
-    // 3. Swap in the new tokens on all the prefixed declarations.
+
+/**
+ * Convert the new angle syntax (keyword and degree) on -x-linear-gradient() functions
+ * to legacy equivalents.
+ */
+function csscrush__post_alias_fix_lineargradients ( $declaration_copies, $fn_name ) {
 
     static $angles_new, $angles_old;
     if ( ! $angles_new ) {
@@ -67,34 +69,64 @@ function csscrush__post_alias_fix_lineargradients ( $declaration_copies, $fn_nam
         $angles_old = array_values( $angles );
     }
 
-    // 1, 2.
-    $patt = '~(?number . ')deg\b~i';
+        // Legacy angles move anti-clockwise and start from East, not North.
+        $deg_convert_callback = create_function( '$m', '
+            $angle = floatval( $m[1] );
+            $angle = ( $angle + 90 ) - ( $angle * 2 );
+            return ( $angle < 0 ? $angle + 360 : $angle ) . \'deg\';
+        ');
+    }
+
+    // Create new paren tokens based on the first prefixed declaration.
+    // Replace the new syntax with the legacy syntax.
     $original_parens = array();
     $replacement_parens = array();
-    foreach ( CssCrush_Regex::matchAll( $patt, $declaration_copies[0]->value ) as $m ) {
+    $fn_patt = '~(?value ) as $m ) {
+
         $original_parens[] = $m[1][0];
-        $replacement_parens[] = CssCrush::$process->addToken(
-            str_ireplace(
-                $angles_new,
-                $angles_old,
-                CssCrush::$process->fetchToken( $m[1][0] )
-            ), 'p' );
+        $original_paren_value = CssCrush::$process->fetchToken( $m[1][0] );
+
+        // Convert keyword angle values.
+        $updated_paren_value = str_ireplace(
+            $angles_new,
+            $angles_old,
+            $original_paren_value
+        );
+
+        // Convert degree angle values.
+        $updated_paren_value = preg_replace_callback(
+            $deg_patt,
+            $deg_convert_callback,
+            $updated_paren_value
+        );
+
+        $replacement_parens[] = CssCrush::$process->addToken( $updated_paren_value, 'p' );
     }
 
-    // 3.
+    // Swap in the new tokens on all the prefixed declarations.
     foreach ( $declaration_copies as $prefixed_copy ) {
-        $prefixed_copy->value = str_replace( $original_parens, $replacement_parens, $prefixed_copy->value );
+        $prefixed_copy->value = str_replace(
+            $original_parens,
+            $replacement_parens,
+            $prefixed_copy->value
+        );
     }
 }
 
+/**
+ * Remove the 'at' keyword from -x-radial-gradient() for legacy implementations.
+ */
 function csscrush__post_alias_fix_radialgradients ( $declaration_copies, $fn_name ) {
 
-    // Remove the new 'at' keyword from gradient syntax for legacy implementations.
-    // 1. Create new paren tokens based on the first prefixed declaration.
-    // 2. Replace the new syntax with the legacy syntax.
-    // 3. Swap in the new tokens on all the prefixed declarations.
-
-    // 1, 2.
+    // Create new paren tokens based on the first prefixed declaration.
+    // Replace the new syntax with the legacy syntax.
     $patt = '~(?value = str_replace( $original_parens, $replacement_parens, $prefixed_copy->value );
+        $prefixed_copy->value = str_replace(
+            $original_parens,
+            $replacement_parens,
+            $prefixed_copy->value
+        );
     }
 }
-
-CssCrush_PostAliasFix::init();
diff --git a/plugins/noise.php b/plugins/noise.php
index b13d1bb..c53adea 100644
--- a/plugins/noise.php
+++ b/plugins/noise.php
@@ -71,7 +71,7 @@
  *      background: turbulence();
  * 
  *      // Sand effect.
- *      background: turbulence( wheat 400x400, .35:.2 4 sharpen, normal, saturate .4 );;
+ *      background: turbulence( wheat 400x400, .35:.2 4 sharpen, normal, saturate .4 );
  */
 
 CssCrush_Plugin::register( 'noise', array(

From 2094bcde7eea4f8e81421510c950b72e9c901701 Mon Sep 17 00:00:00 2001
From: Pete Boere 
Date: Thu, 31 Jan 2013 14:07:32 +0000
Subject: [PATCH 071/421] Fixed error notice with no enabled plugins in
 Plugins.ini file. Updated aliases file.

---
 Aliases.ini           |  1 -
 CHANGELOG.md          |  5 +++++
 lib/CssCrush/Core.php | 40 ++++++++++++++++++----------------------
 3 files changed, 23 insertions(+), 23 deletions(-)

diff --git a/Aliases.ini b/Aliases.ini
index 4e34d5b..7430460 100644
--- a/Aliases.ini
+++ b/Aliases.ini
@@ -49,7 +49,6 @@
 
     ; Background clip.
     background-clip[] = -webkit-background-clip
-    background-clip[] = -moz-background-clip
 
     ; Background origin.
     background-origin[] = -webkit-background-origin
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1d4ca51..662d1fd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,8 @@
+1.9.2 (? 2013)
+-----
+* Fixed error notice with no enabled plugins in Plugins.ini file.
+* Updated aliases file.
+
 1.9.1 (30th January 2013)
 -----
 * Resolved issues #42 and #43.
diff --git a/lib/CssCrush/Core.php b/lib/CssCrush/Core.php
index 5194e0f..ad6ac50 100644
--- a/lib/CssCrush/Core.php
+++ b/lib/CssCrush/Core.php
@@ -6,7 +6,7 @@
  */
 class CssCrush
 {
-    const VERSION = '1.9.1';
+    const VERSION = '1.9.2';
 
     // Global settings.
     static public $config;
@@ -114,15 +114,7 @@ static protected function setDocRoot ( $doc_root = null )
                 if ( substr( $script_filename, $len_diff ) === $script_name ) {
 
                     $path = substr( $script_filename, 0, $len_diff );
-
-                    // An empty string passed to realpath gives unpredictable results
-                    // on command line.
-                    if ( $path === '' ) {
-                        $doc_root = dirname( realpath( $script_filename ) );
-                    }
-                    else {
-                        $doc_root = realpath( $path );
-                    }
+                    $doc_root = realpath( $path );
                 }
             }
 
@@ -159,23 +151,24 @@ static public function loadAssets ()
         // Load aliases file if it exists
         if ( $aliases_file ) {
 
-            if ( $result = @parse_ini_file( $aliases_file, true ) ) {
-
-                self::$config->aliases = $result;
+            $result = @parse_ini_file( $aliases_file, true );
+            if ( $result !== false ) {
 
                 // Declaration aliases require a little preprocessing.
-                if ( isset( self::$config->aliases[ 'declarations' ] ) ) {
+                if ( isset( $result[ 'declarations' ] ) ) {
                     $store = array();
-                    foreach ( self::$config->aliases[ 'declarations' ] as $prop_val => $aliases ) {
+                    foreach ( $result[ 'declarations' ] as $prop_val => $aliases ) {
                         list( $prop, $value ) = array_map( 'trim', explode( ':', $prop_val ) );
                         foreach ( $aliases as &$alias ) {
                             $alias = explode( ':', $alias );
                         }
                         $store[ $prop ][ $value ] = $aliases;
                     }
-                    self::$config->aliases[ 'declarations' ] = $store;
+                    $result[ 'declarations' ] = $store;
                 }
 
+                self::$config->aliases = $result;
+
                 // Ensure all alias groups are at least set (issue #34)
                 self::$config->bareAliasGroups = array(
                     'properties' => array(),
@@ -199,12 +192,15 @@ static public function loadAssets ()
 
         // Load plugins
         if ( $plugins_file ) {
-            if ( $result = @parse_ini_file( $plugins_file ) ) {
-                foreach ( $result[ 'plugins' ] as $plugin_name ) {
-                    // Backwards compat.
-                    $plugin_name = basename( $plugin_name, '.php' );
-                    if ( CssCrush_Plugin::load( $plugin_name ) ) {
-                        self::$config->plugins[ $plugin_name ] = true;
+            $result = @parse_ini_file( $plugins_file );
+            if ( $result !== false ) {
+                if ( isset( $result[ 'plugins' ] ) ) {
+                    foreach ( $result[ 'plugins' ] as $plugin_name ) {
+                        // Backwards compat.
+                        $plugin_name = basename( $plugin_name, '.php' );
+                        if ( CssCrush_Plugin::load( $plugin_name ) ) {
+                            self::$config->plugins[ $plugin_name ] = true;
+                        }
                     }
                 }
             }

From 6244f6104c3837a72eae8ab9ab0f9faee0616254 Mon Sep 17 00:00:00 2001
From: Pete Boere 
Date: Thu, 31 Jan 2013 14:07:32 +0000
Subject: [PATCH 072/421] Fixed error notice with no enabled plugins in
 Plugins.ini file. Updated aliases file.

---
 CHANGELOG.md | 11 ++++-------
 1 file changed, 4 insertions(+), 7 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 662d1fd..d62cbb3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,13 +1,10 @@
-1.9.2 (? 2013)
------
-* Fixed error notice with no enabled plugins in Plugins.ini file.
-* Updated aliases file.
-
-1.9.1 (30th January 2013)
+1.9.1 (31th January 2013)
 -----
+* Added noise plugin (noise/texture generating functions).
 * Resolved issues #42 and #43.
 * Fixed command line context option.
-* Added noise plugin (noise/texture generating functions).
+* Fixed error notice with no enabled plugins in Plugins.ini file.
+* Updated aliases file.
 
 1.9 (12th January 2013)
 ---

From 98289846d05385a9feb28360ef68fd7ba43f901d Mon Sep 17 00:00:00 2001
From: Pete Boere 
Date: Thu, 31 Jan 2013 14:07:32 +0000
Subject: [PATCH 073/421] Fixed error notice with no enabled plugins in
 Plugins.ini file. Updated aliases file.

---
 lib/CssCrush/Core.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/CssCrush/Core.php b/lib/CssCrush/Core.php
index ad6ac50..a337ceb 100644
--- a/lib/CssCrush/Core.php
+++ b/lib/CssCrush/Core.php
@@ -6,7 +6,7 @@
  */
 class CssCrush
 {
-    const VERSION = '1.9.2';
+    const VERSION = '1.9.1';
 
     // Global settings.
     static public $config;

From aee7e2ca0d118aa324245f770ab17f0c189578c8 Mon Sep 17 00:00:00 2001
From: Pete Boere 
Date: Thu, 14 Feb 2013 13:27:37 +0000
Subject: [PATCH 074/421] First commit for 1.10

Added `-i` alias to `--file` option for the command line utility.
Branding: Converted all instances of 'CSS Crush' to 'CSS-Crush'.
---
 Aliases.ini           | 3 +++
 CHANGELOG.md          | 4 ++++
 CssCrush.boilerplate  | 2 +-
 README.md             | 6 +++---
 cli.php               | 9 +++++----
 lib/CssCrush/Core.php | 2 +-
 6 files changed, 17 insertions(+), 9 deletions(-)

diff --git a/Aliases.ini b/Aliases.ini
index 7430460..a3c0ee4 100644
--- a/Aliases.ini
+++ b/Aliases.ini
@@ -290,6 +290,9 @@
     ; Element.
     element[] = -moz-element
 
+
+; [functions:gradients]
+
     ; Gradients.
     linear-gradient[] = -webkit-linear-gradient
     linear-gradient[] = -moz-linear-gradient
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d62cbb3..f6f762c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+1.10 (?)
+----
+* Added `-i` alias to `--file` option for the command line utility.
+
 1.9.1 (31th January 2013)
 -----
 * Added noise plugin (noise/texture generating functions).
diff --git a/CssCrush.boilerplate b/CssCrush.boilerplate
index f656f11..3feeafb 100644
--- a/CssCrush.boilerplate
+++ b/CssCrush.boilerplate
@@ -1,2 +1,2 @@
-CSS Crush(ed) on {{datetime}}
+CSS-Crush(ed) on {{datetime}}
 http://github.com/peteboere/css-crush ({{version}})
\ No newline at end of file
diff --git a/README.md b/README.md
index 3d0a607..2a8edeb 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
-CSS Crush
+CSS-Crush
 ===================================
 
-CSS Crush is an extensible PHP based CSS preprocessor that aims to alleviate many of the hacks and workarounds necessary in modern CSS development.
+CSS-Crush is an extensible PHP based CSS preprocessor that aims to alleviate many of the hacks and workarounds necessary in modern CSS development.
 
 
 Overview
@@ -34,4 +34,4 @@ If you think you've found a bug, please visit the Issue tracker — https://gith
 Submitting patches
 ===================================
 
-To contribute code and bug fixes fork this project on Github, make changes to the code in your fork, and then send a "pull request" to be reviewed for inclusion.
+To contribute code and bug fixes fork this project on Github, make changes to the code in your fork, and then send a "pull request" for review.
diff --git a/cli.php b/cli.php
index 5e5cda6..7c9c2e9 100755
--- a/cli.php
+++ b/cli.php
@@ -57,6 +57,7 @@ function get_stdin_contents () {
 
 $short_opts = array(
     "f:",  // Input file. Defaults to sdtin.
+    "i:",  // Input file alias.
     "o:",  // Output file. Defaults to stdout.
     "p",   // Pretty formatting.
     'b',   // Output boilerplate.
@@ -82,7 +83,7 @@ function get_stdin_contents () {
 
 $opts = getopt( implode( $short_opts ), $long_opts );
 
-$input_file = @( $opts['f'] ?: $opts['file'] );
+$input_file = @( $opts['f'] ?: $opts['i'] ?: $opts['file'] );
 $output_file = @( $opts['o'] ?: $opts['output'] );
 $pretty = @( isset( $opts['p'] ) ?: isset( $opts['pretty'] ) );
 $boilerplate = @( isset( $opts['b'] ) ?: isset( $opts['boilerplate'] ) );
@@ -104,12 +105,12 @@ function get_stdin_contents () {
 $help = <<version );
+    stdout( 'CSS-Crush ' . CssCrush::$config->version );
     exit( STATUS_OK );
 }
 
diff --git a/lib/CssCrush/Core.php b/lib/CssCrush/Core.php
index a337ceb..14f2f15 100644
--- a/lib/CssCrush/Core.php
+++ b/lib/CssCrush/Core.php
@@ -6,7 +6,7 @@
  */
 class CssCrush
 {
-    const VERSION = '1.9.1';
+    const VERSION = '1.10';
 
     // Global settings.
     static public $config;

From fa6cbd157f4d3ac05fce524abdc00f244ff4507a Mon Sep 17 00:00:00 2001
From: Pete Boere 
Date: Sat, 16 Feb 2013 20:17:47 +0000
Subject: [PATCH 075/421] Added parsing for @name rule directive to make
 referencing rules a bit more flexible ant robust.

Some rewriting of the internal referencing features; rule references previously looked for the closest
previous match. This behaviour has been changed to a 'last wins' match to be more consistent with the way
CSS works. This may affect users of `@extend` or the `query()` function.
---
 CHANGELOG.md                 |   5 +
 lib/CssCrush/Color.php       |   8 +
 lib/CssCrush/Declaration.php |  87 ++++++++---
 lib/CssCrush/Function.php    | 136 ++++++++++++-----
 lib/CssCrush/Mixin.php       |  48 +++---
 lib/CssCrush/Process.php     |  15 +-
 lib/CssCrush/Regex.php       |   3 +-
 lib/CssCrush/Rule.php        | 287 +++++++++++------------------------
 8 files changed, 293 insertions(+), 296 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index f6f762c..fe92a2d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,11 @@
 1.10 (?)
 ----
 * Added `-i` alias to `--file` option for the command line utility.
+* Added `@name` in-rule directive for more flexible and robust rule referencing.
+* Rule references previously looked for the closest previous match. This behaviour has been changed
+  to a 'last wins' match to be more consistent with the way CSS works. This may affect users of `@extend`
+  or the `query()` function.
+* Removed data-* properties.
 
 1.9.1 (31th January 2013)
 -----
diff --git a/lib/CssCrush/Color.php b/lib/CssCrush/Color.php
index 9472f77..3fcdcf4 100644
--- a/lib/CssCrush/Color.php
+++ b/lib/CssCrush/Color.php
@@ -274,6 +274,14 @@ static public function hexToRgb ( $hex )
         return $rgba;
     }
 
+    static public function colorAdjust ( $raw_color, array $adjustments )
+    {
+        $hsla = new CssCrush_Color( $raw_color, true );
+
+        // On failure to parse return input.
+        return $hsla->isValid ? $hsla->adjust( $adjustments )->__toString() : $raw_color;
+    }
+
 
     #############################
     #  Instances.
diff --git a/lib/CssCrush/Declaration.php b/lib/CssCrush/Declaration.php
index 49dcea5..ddb2ac7 100644
--- a/lib/CssCrush/Declaration.php
+++ b/lib/CssCrush/Declaration.php
@@ -14,7 +14,6 @@ class CssCrush_Declaration
     public $index;
     public $skip;
     public $important;
-    public $isValid = true;
 
     public function __construct ( $prop, $value, $contextIndex = 0 )
     {
@@ -26,7 +25,7 @@ public function __construct ( $prop, $value, $contextIndex = 0 )
 
         // Check the input.
         if ( $prop === '' || $value === '' || $value === null ) {
-            $this->isValid = false;
+            $this->inValid = true;
 
             return;
         }
@@ -53,33 +52,16 @@ public function __construct ( $prop, $value, $contextIndex = 0 )
             $important = true;
         }
 
-        // Ignore declarations with null css values.
+        // Reject declarations with empty CSS values.
         if ( $value === false || $value === '' ) {
-            $this->isValid = false;
+            $this->inValid = true;
 
             return;
         }
 
-        // Apply custom functions.
-        if ( ! $skip ) {
-            CssCrush_Function::executeOnString( $value );
-        }
-
-        // Capture all remaining paren pairs.
-        CssCrush::$process->captureParens( $value );
-
-        // Create an index of all regular functions in the value.
-        $functions = array();
-        if ( preg_match_all( $regex->function, $value, $m ) ) {
-            foreach ( $m[2] as $index => $fn_name ) {
-                $functions[ strtolower( $fn_name ) ] = true;
-            }
-        }
-
         $this->property          = $prop;
         $this->canonicalProperty = $canonical_property;
         $this->vendor            = $vendor;
-        $this->functions         = $functions;
         $this->index             = $contextIndex;
         $this->value             = $value;
         $this->skip              = $skip;
@@ -99,6 +81,69 @@ public function __toString ()
         return "$this->property:$whitespace$this->value$important";
     }
 
+    /*
+        Execute functions on value.
+        Capture parens.
+        Index functions.
+    */
+    public function process ( $parent_rule )
+    {
+        // Apply custom functions.
+        if ( ! $this->skip ) {
+
+            // this() function needs to be called exclusively because
+            // it's self referencing.
+            CssCrush_Function::executeOnString(
+                $this->value,
+                CssCrush_Regex::$patt->thisFunction,
+                array(
+                    'this' => 'csscrush_fn__this',
+                ),
+                array(
+                    'rule' => $parent_rule,
+                ));
+
+            CssCrush_Function::executeOnString( 
+                $this->value,
+                null,
+                null,
+                array(
+                    'rule' => $parent_rule,
+                    'property' => $this->property,
+                ));
+        }
+
+        // Trim whitespace that may have been introduced by functions.
+        $this->value = trim( $this->value );
+
+        // After functions have applied value may be empty.
+        if ( $this->value === '' ) {
+
+            $this->inValid = true;
+            return;
+        }
+
+        // Store raw value as data on the parent rule.
+        $parent_rule->queryData[ $this->property ] = $this->value;
+
+        // Capture top-level paren pairs.
+        CssCrush::$process->captureParens( $this->value );
+
+        $this->indexFunctions();
+    }
+
+    public function indexFunctions ()
+    {
+        // Create an index of all regular functions in the value.
+        $functions = array();
+        if ( preg_match_all( CssCrush_Regex::$patt->function, $this->value, $m ) ) {
+            foreach ( $m[2] as $index => $fn_name ) {
+                $functions[ strtolower( $fn_name ) ] = true;
+            }
+        }
+        $this->functions = $functions;
+    }
+
     public function getFullValue ()
     {
         return CssCrush::$process->restoreTokens( $this->value, 'p' );
diff --git a/lib/CssCrush/Function.php b/lib/CssCrush/Function.php
index e5c5a6b..2fe775e 100644
--- a/lib/CssCrush/Function.php
+++ b/lib/CssCrush/Function.php
@@ -6,34 +6,42 @@
  */
 class CssCrush_Function
 {
-    static function init ()
-    {
-        CssCrush_Function::register( 'math', 'csscrush_fn__math' );
-        CssCrush_Function::register( 'percent', 'csscrush_fn__percent' );
-        CssCrush_Function::register( 'pc', 'csscrush_fn__percent' );
-        CssCrush_Function::register( 'hsla-adjust', 'csscrush_fn__hsla_adjust' );
-        CssCrush_Function::register( 'hsl-adjust', 'csscrush_fn__hsl_adjust' );
-        CssCrush_Function::register( 'h-adjust', 'csscrush_fn__h_adjust' );
-        CssCrush_Function::register( 's-adjust', 'csscrush_fn__s_adjust' );
-        CssCrush_Function::register( 'l-adjust', 'csscrush_fn__l_adjust' );
-        CssCrush_Function::register( 'a-adjust', 'csscrush_fn__a_adjust' );
-    }
-
     // Regex pattern for finding custom functions.
     static public $functionPatt;
 
-    // Stack for function names.
-    static protected $customFunctions;
+    static protected $functions;
+
+    static protected $customFunctions = array();
+
+    static protected $builtinFunctions = array(
+
+        // These functions must come first in this order.
+        'query' => 'csscrush_fn__query',
+
+        // These functions can be any order.
+        'math' => 'csscrush_fn__math',
+        'percent' => 'csscrush_fn__percent',
+        'pc' => 'csscrush_fn__percent',
+        'hsla-adjust' => 'csscrush_fn__hsla_adjust',
+        'hsl-adjust' => 'csscrush_fn__hsl_adjust',
+        'h-adjust' => 'csscrush_fn__h_adjust',
+        's-adjust' => 'csscrush_fn__s_adjust',
+        'l-adjust' => 'csscrush_fn__l_adjust',
+        'a-adjust' => 'csscrush_fn__a_adjust',
+    );
 
     static public function setMatchPatt ()
     {
-        self::$functionPatt = CssCrush_Regex::createFunctionMatchPatt( array_keys( self::$customFunctions ), true );
+        self::$functions = self::$builtinFunctions + self::$customFunctions;
+        self::$functionPatt = CssCrush_Regex::createFunctionMatchPatt(
+            array_keys( self::$functions ), true );
     }
 
-    static public function executeOnString ( &$str, $patt = null, $process_callback = null, $property = null )
+    static public function executeOnString ( &$str, $patt = null, $process_callback = null, $extra = null )
     {
+
         // No bracketed expressions, early return.
-        if ( false === strpos( $str, '(' ) ) {
+        if ( strpos( $str, '(' ) === false ) {
             return;
         }
 
@@ -81,17 +89,17 @@ static public function executeOnString ( &$str, $patt = null, $process_callback
 
             if ( ! $process_callback ) {
                 // If no callback reference it's a built-in.
-                if ( array_key_exists( $fn_name, self::$customFunctions ) ) {
-                    $func_returns = call_user_func( self::$customFunctions[ $fn_name ], $args );
+                if ( array_key_exists( $fn_name, self::$functions ) ) {
+                    $func_returns = call_user_func( self::$functions[ $fn_name ], $args, $extra );
                 }
             }
             else {
                 if ( isset( $process_callback[ $fn_name ] ) ) {
-                    $func_returns = call_user_func( $process_callback[ $fn_name ], $args, $fn_name, $property );
+                    $func_returns = call_user_func( $process_callback[ $fn_name ], $args, $extra );
                 }
             }
 
-            // Splice in the function returns.
+            // Splice in the function result.
             $str = substr_replace( $str, "$before_operator$func_returns", $offset, $closing_paren - $offset );
         }
     }
@@ -122,14 +130,6 @@ static public function parseArgsSimple ( $input )
     {
         return preg_split( CssCrush_Regex::$patt->argListSplit, $input, 2 );
     }
-
-    static public function colorAdjust ( $raw_color, array $adjustments )
-    {
-        $hsla = new CssCrush_Color( $raw_color, true );
-
-        // On failure to parse return input.
-        return $hsla->isValid ? $hsla->adjust( $adjustments )->__toString() : $raw_color;
-    }
 }
 
 
@@ -190,32 +190,94 @@ function csscrush_fn__percent ( $input ) {
 
 function csscrush_fn__hsla_adjust ( $input ) {
     list( $color, $h, $s, $l, $a ) = array_pad( CssCrush_Function::parseArgs( $input, true ), 5, 0 );
-    return CssCrush_Function::colorAdjust( $color, array( $h, $s, $l, $a ) );
+    return CssCrush_Color::colorAdjust( $color, array( $h, $s, $l, $a ) );
 }
 
 function csscrush_fn__hsl_adjust ( $input ) {
     list( $color, $h, $s, $l ) = array_pad( CssCrush_Function::parseArgs( $input, true ), 4, 0 );
-    return CssCrush_Function::colorAdjust( $color, array( $h, $s, $l, 0 ) );
+    return CssCrush_Color::colorAdjust( $color, array( $h, $s, $l, 0 ) );
 }
 
 function csscrush_fn__h_adjust ( $input ) {
     list( $color, $h ) = array_pad( CssCrush_Function::parseArgs( $input, true ), 2, 0 );
-    return CssCrush_Function::colorAdjust( $color, array( $h, 0, 0, 0 ) );
+    return CssCrush_Color::colorAdjust( $color, array( $h, 0, 0, 0 ) );
 }
 
 function csscrush_fn__s_adjust ( $input ) {
     list( $color, $s ) = array_pad( CssCrush_Function::parseArgs( $input, true ), 2, 0 );
-    return CssCrush_Function::colorAdjust( $color, array( 0, $s, 0, 0 ) );
+    return CssCrush_Color::colorAdjust( $color, array( 0, $s, 0, 0 ) );
 }
 
 function csscrush_fn__l_adjust ( $input ) {
     list( $color, $l ) = array_pad( CssCrush_Function::parseArgs( $input, true ), 2, 0 );
-    return CssCrush_Function::colorAdjust( $color, array( 0, 0, $l, 0 ) );
+    return CssCrush_Color::colorAdjust( $color, array( 0, 0, $l, 0 ) );
 }
 
 function csscrush_fn__a_adjust ( $input ) {
     list( $color, $a ) = array_pad( CssCrush_Function::parseArgs( $input, true ), 2, 0 );
-    return CssCrush_Function::colorAdjust( $color, array( 0, 0, 0, $a ) );
+    return CssCrush_Color::colorAdjust( $color, array( 0, 0, 0, $a ) );
 }
 
-CssCrush_Function::init();
+function csscrush_fn__this ( $input, $extra ) {
+
+    $args = CssCrush_Function::parseArgsSimple( $input );
+    $rule = $extra[ 'rule' ];
+
+    if ( isset( $rule->thisData[ $args[0] ] ) ) {
+
+        return $rule->thisData[ $args[0] ];
+    }
+    // Fallback value.
+    elseif ( isset( $args[1] ) ) {
+
+        return $args[1];
+    }
+    else {
+
+        return '';
+    }
+}
+
+function csscrush_fn__query ( $input, $extra ) {
+
+    $result = '';
+    $args = CssCrush_Function::parseArgs( $input );
+
+    if ( count( $args ) < 1 ) {
+        return $result;
+    }
+
+    $call_property = $extra[ 'property' ];
+    $references =& CssCrush::$process->references;
+
+    // Resolve arguments.
+    $name = array_shift( $args );
+    $property = $call_property;
+    if ( isset( $args[0] ) ) {
+        if ( $args[0] !== 'default' ) {
+            $property = array_shift( $args );
+        }
+        else {
+            array_shift( $args );
+        }
+    }
+    $default = isset( $args[0] ) ? $args[0] : null;
+
+    if ( ! preg_match( CssCrush_Regex::$patt->ident, $name ) ) {
+        $name = CssCrush_Selector::makeReadableSelector( $name );
+    }
+
+    // If a rule reference is found, query its data.
+    if ( isset( $references[ $name ] ) ) {
+        $query_rule = $references[ $name ];
+        $query_rule->processDeclarations();
+        if ( isset( $query_rule->queryData[ $property ] ) ) {
+            $result = $query_rule->queryData[ $property ];
+        }
+    }
+
+    if ( $result === '' && ! is_null( $default ) ) {
+        $result = $default;
+    }
+    return $result;
+}
diff --git a/lib/CssCrush/Mixin.php b/lib/CssCrush/Mixin.php
index 40145fd..09589f6 100644
--- a/lib/CssCrush/Mixin.php
+++ b/lib/CssCrush/Mixin.php
@@ -10,17 +10,15 @@ class CssCrush_Mixin
 
     public $arguments;
 
-    public $data = array();
-
     public function __construct ( $block )
     {
-        // Strip comment markers
+        // Strip comment markers.
         $block = CssCrush_Util::stripCommentTokens( $block );
 
-        // Prepare the arguments object
+        // Prepare the arguments object.
         $this->arguments = new CssCrush_ArgList( $block );
 
-        // Re-assign with the parsed arguments string
+        // Re-assign with the parsed arguments string.
         $block = $this->arguments->string;
 
         // Split the block around semi-colons.
@@ -33,33 +31,31 @@ public function __construct ( $block )
                 continue;
             }
 
-            // Store template declarations as arrays as they are copied by value not reference
+            // Store template declarations as arrays as they are copied by value not reference.
             $declaration = array();
 
-            $declaration['property'] = trim( substr( $raw_declaration, 0, $colon ) );
-            $declaration['value'] = trim( substr( $raw_declaration, $colon + 1 ) );
+            $property = strtolower( trim( substr( $raw_declaration, 0, $colon ) ) );
+            $value = trim( substr( $raw_declaration, $colon + 1 ) );
 
-            if ( $declaration['property'] === 'mixin' ) {
+            if ( $property === 'mixin' ) {
 
-                // Mixin can contain other mixins if they are available
-                if ( $mixin_declarations = CssCrush_Mixin::parseValue( $declaration['value'] ) ) {
+                // Mixin can contain other mixins if they are available.
+                if ( $mixin_declarations = CssCrush_Mixin::parseValue( $value ) ) {
 
-                    // Add mixin result to the stack
-                    $this->declarationsTemplate = array_merge( $this->declarationsTemplate, $mixin_declarations );
+                    // Add mixin result to the stack.
+                    $this->declarationsTemplate = array_merge(
+                        $this->declarationsTemplate, $mixin_declarations );
                 }
             }
-            elseif ( ! empty( $declaration['value'] ) ) {
-                $this->declarationsTemplate[] = $declaration;
-            }
-        }
+            elseif ( ! empty( $value ) ) {
 
-        // Create data table for the mixin.
-        // Values that use arg() are excluded
-        foreach ( $this->declarationsTemplate as &$declaration ) {
-            if ( ! preg_match( CssCrush_Regex::$patt->aToken, $declaration['value'] ) ) {
-                $this->data[ $declaration['property'] ] = $declaration['value'];
+                $this->declarationsTemplate[] = array(
+                    'property' => $property,
+                    'value' => $value,
+                );
             }
         }
+
         return '';
     }
 
@@ -103,10 +99,10 @@ static public function parseSingleValue ( $message )
                 // Mixin match
                 $mixin = CssCrush::$process->mixins[ $name ];
             }
-            elseif ( isset( CssCrush::$process->abstracts[ $name ] ) ) {
+            elseif ( isset( CssCrush::$process->references[ $name ] ) ) {
 
                 // Abstract rule match
-                $non_mixin = CssCrush::$process->abstracts[ $name ];
+                $non_mixin = CssCrush::$process->references[ $name ];
             }
         }
 
@@ -115,8 +111,8 @@ static public function parseSingleValue ( $message )
 
             $selector_test = CssCrush_Selector::makeReadableSelector( $message );
 
-            if ( isset( CssCrush::$process->selectorRelationships[ $selector_test ] ) ) {
-                $non_mixin = CssCrush::$process->selectorRelationships[ $selector_test ];
+            if ( isset( CssCrush::$process->references[ $selector_test ] ) ) {
+                $non_mixin = CssCrush::$process->references[ $selector_test ];
             }
         }
 
diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php
index 000f0a7..26c7c7f 100644
--- a/lib/CssCrush/Process.php
+++ b/lib/CssCrush/Process.php
@@ -26,10 +26,9 @@ public function __construct ( $options )
         $this->uid = 0;
         $this->cacheData = array();
         $this->mixins = array();
-        $this->abstracts = array();
+        $this->references = array();
         $this->errors = array();
         $this->stat = array();
-        $this->selectorRelationships = array();
         $this->charset = null;
         $this->currentFile = null;
         $this->tokens = (object) array(
@@ -86,8 +85,7 @@ public function release ()
             $this->tokens,
             $this->variables,
             $this->mixins,
-            $this->abstracts,
-            $this->selectorRelationships,
+            $this->references,
             $this->misc,
             $this->plugins,
             $this->aliases,
@@ -716,15 +714,11 @@ public function extractRules ()
 
     protected function processRules ()
     {
-        // Reset the selector relationships.
-        $this->selectorRelationships = array();
-
         $aliases =& $this->aliases;
 
         foreach ( $this->tokens->r as $rule ) {
 
-            // Store selector relationships.
-            $rule->indexSelectors();
+            $rule->processDeclarations();
 
             CssCrush_Hook::run( 'rule_prealias', $rule );
 
@@ -824,7 +818,7 @@ protected function prefixSelectors ()
                             // Ampersand is the positional symbol for where the
                             // prefix will be placed.
 
-                            // Find and replace (once) the ampersand.
+                            // Find and replace the ampersand (once).
                             $new_value = preg_replace(
                                     '!&!',
                                     $arg_selector,
@@ -1087,6 +1081,7 @@ public function compile ()
 
         // Parse rules.
         $this->extractRules();
+        // csscrush::log(array_keys( $this->references) );
 
         // Process @in blocks.
         $this->prefixSelectors();
diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php
index 271d102..c1f720e 100644
--- a/lib/CssCrush/Regex.php
+++ b/lib/CssCrush/Regex.php
@@ -73,12 +73,11 @@ static public function init ()
         $patt->varFunction = CssCrush_Regex::create( '\$\(\s*()\s*\)', 'iS' );
         $patt->varFunctionStart = '!(\$)\(!';
         $patt->argFunction = CssCrush_Regex::createFunctionMatchPatt( array( 'arg' ) );
-        $patt->queryFunction = CssCrush_Regex::createFunctionMatchPatt( array( 'query' ) );
         $patt->thisFunction = CssCrush_Regex::createFunctionMatchPatt( array( 'this' ) );
 
         // Misc.
         $patt->vendorPrefix  = '!^-([a-z]+)-([a-z-]+)!iS';
-        $patt->mixinExtend   = '!^(?:(@include|mixin)|(@?extends?))[\s\:]+!iS';
+        $patt->ruleDirective = '!^(?:(@include|mixin)|(@?extends?)|(@name))[\s\:]+!iS';
         $patt->argListSplit  = '!\s*[,\s]\s*!S';
         $patt->mathBlacklist = '![^\.0-9\*\/\+\-\(\)]!S';
         $patt->charset       = '!@charset\s+(\?s\d+\?)\s*;!iS';
diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php
index 0ac7fd0..f78d390 100644
--- a/lib/CssCrush/Rule.php
+++ b/lib/CssCrush/Rule.php
@@ -22,11 +22,11 @@ class CssCrush_Rule implements IteratorAggregate
     // Arugments passed via @extend.
     public $extendArgs = array();
 
-    // A table for storing the declarations as data for this() referencing.
-    public $localData = array();
+    // Declarations hash table for inter-rule this() referencing.
+    public $thisData = array();
 
-    // A table for storing the declarations as data for external query() referencing.
-    public $data = array();
+    // Declarations hash table for external query() referencing.
+    public $queryData = array();
 
     public function __construct ( $selector_string = null, $declarations_string )
     {
@@ -71,16 +71,17 @@ public function __construct ( $selector_string = null, $declarations_string )
 
                     $abstract_name = $m[1];
 
-                    // Link the rule to the abstract name and skip forward to declaration parsing
-                    $process->abstracts[ $abstract_name ] = $this;
+                    // Link the rule to the abstract name and skip forward to declaration parsing.
+                    $process->references[ $abstract_name ] = $this;
                     break;
                 }
 
                 $this->addSelector( new CssCrush_Selector( $selector ) );
 
-                // Store selector relationships
-                //  - This happens twice; on first pass for mixins, second pass is for inheritance
-                $this->indexSelectors();
+                // Link selectors as references.
+                foreach ( $this->selectors as $selector ) {
+                    $process->references[ $selector->readableValue ] = $this;
+                }
             }
         }
 
@@ -96,15 +97,24 @@ public function __construct ( $selector_string = null, $declarations_string )
             // Strip comments around the property
             $declaration = CssCrush_Util::stripCommentTokens( $declaration );
 
-            // Accept several different syntaxes for mixin and extends.
-            if ( preg_match( $regex->mixinExtend, $declaration, $m ) ) {
+            // Rule directives. Accept several different syntaxes for mixin and extends.
+            if ( preg_match( $regex->ruleDirective, $declaration, $m ) ) {
 
-                $prop = isset( $m[2] ) ? 'extends' : 'mixin';
+                if ( ! empty( $m[1] ) ) {
+                    $prop = 'mixin';
+                }
+                elseif ( ! empty( $m[2] ) ) {
+                    $prop = 'extends';
+                }
+                else {
+                    $prop = 'name';
+                }
                 $value = trim( substr( $declaration, strlen( $m[0] ) ) );
             }
             elseif ( ( $colonPos = strpos( $declaration, ':' ) ) !== false ) {
 
                 $prop = trim( substr( $declaration, 0, $colonPos ) );
+
                 // Extract the value part of the declaration.
                 $value = trim( substr( $declaration, $colonPos + 1 ) );
             }
@@ -114,57 +124,55 @@ public function __construct ( $selector_string = null, $declarations_string )
             }
 
             // Reject empty values.
-            if ( empty( $prop ) || ( empty( $value ) && $value != '0' ) ) {
+            if ( empty( $prop ) || ! strlen( $value ) ) {
                 continue;
             }
 
             if ( $prop === 'mixin' ) {
 
-                // Mixins are a special case
+                // Mixins are a special case.
                 if ( $mixin_declarations = CssCrush_Mixin::parseValue( $value ) ) {
 
-                    // Add mixin declarations to the stack
+                    // Add mixin declarations to the stack.
                     while ( $mixin_declaration = array_shift( $mixin_declarations ) ) {
 
-                        $this->declarationCheckin(
-                            $mixin_declaration['property'], $mixin_declaration['value'], $pairs );
+                        $pairs[] = array( $mixin_declaration['property'], $mixin_declaration['value'] );
                     }
                 }
             }
             elseif ( $prop === 'extends' ) {
 
-                // Extends are also a special case
+                // Extends are also a special case.
                 $this->setExtendSelectors( $value );
             }
+            elseif ( $prop === 'name' ) {
+
+                // Link the rule as a reference.
+                $process->references[ $value ] = $this;
+            }
             else {
 
-                // Lowercase property values.
-                $prop = strtolower( $prop );
-                $this->declarationCheckin( $prop, $value, $pairs );
+                $pairs[] = array( $prop, $value );
             }
         }
 
-        // Bind declaration objects on the rule
+        // Bind declaration objects on the rule.
         foreach ( $pairs as $index => &$pair ) {
 
             list( $prop, $value ) = $pair;
 
-            // Resolve self references, aka this()
-            CssCrush_Function::executeOnString( $value,
-                    CssCrush_Regex::$patt->thisFunction, array(
-                        'this'  => array( $this, 'cssThisFunction' ),
-                    ), $prop );
-
             if ( trim( $value ) !== '' ) {
 
-                // Add declaration and update the data table
-                $this->data[ $prop ] = $value;
+                // Only store to $this->thisData if the value does not itself make a
+                // this() call to avoid circular references.
+                if ( ! preg_match( CssCrush_Regex::$patt->thisFunction, $value ) ) {
+                    $this->thisData[ strtolower( $prop ) ] = $value;
+                }
+
+                // Add declaration.
                 $this->addDeclaration( $prop, $value, $index );
             }
         }
-
-        // localData no longer required
-        $this->localData = null;
     }
 
     public function __toString ()
@@ -197,67 +205,25 @@ public function __toString ()
         }
     }
 
-    public function declarationCheckin ( $prop, $value, &$pairs )
+    public $declarationsProcessed = false;
+    public function processDeclarations ()
     {
-        // First resolve query() calls that reference earlier rules.
-        if ( preg_match( CssCrush_Regex::$patt->queryFunction, $value ) ) {
-
-            CssCrush_Function::executeOnString( $value,
-                CssCrush_Regex::$patt->queryFunction, array(
-                    'query' => array( $this, 'cssQueryFunction' ),
-                ), $prop );
-        }
-
-        // Now all referencing is done convert *most* values to lowercase.
-        // Internet Explorer JavaScript expressions need case preserved.
-        if ( strpos( $prop, 'expression' ) !== false ) {
-            $value = strtolower( $value );
-        }
-
-        if ( strpos( $prop, 'data-' ) === 0 ) {
-
-            // If it's with data prefix, we don't want to print it
-            // Just remove the prefix
-            $prop = substr( $prop, strlen( 'data-' ) );
-
-            // On first pass we want to store data properties on $this->data,
-            // as well as on local
-            $this->data[ $prop ] = $value;
-        }
-        else {
-
-            // Add to the stack
-            $pairs[] = array( $prop, $value );
-        }
-
-        // Set on $this->localData
-        $this->localData[ $prop ] = $value;
-
-        // Unset on data tables if the value has a this() call:
-        //   - Restriction to avoid circular references
-        if ( preg_match( CssCrush_Regex::$patt->thisFunction, $value ) ) {
-
-            unset( $this->localData[ $prop ], $this->data[ $prop ] );
+        if ( $this->declarationsProcessed ) {
+            return;
         }
-    }
-
-    public function updatePropertyIndex ()
-    {
-        // Create a new table of properties.
-        $new_properties_table = array();
 
-        foreach ( $this as $declaration ) {
+        foreach ( $this->declarations as $index => $declaration ) {
 
-            $name = $declaration->property;
+            // Execute functions, store as data etc.
+            $declaration->process( $this );
 
-            if ( isset( $new_properties_table[ $name ] ) ) {
-                $new_properties_table[ $name ]++;
-            }
-            else {
-                $new_properties_table[ $name ] = 1;
+            // Drop declaration if value is now empty.
+            if ( ! empty( $declaration->inValid ) ) {
+                unset( $this->declarations[ $index ] );
             }
         }
-        $this->properties = $new_properties_table;
+
+        $this->declarationsProcessed = true;
     }
 
 
@@ -266,9 +232,6 @@ public function updatePropertyIndex ()
 
     public function setExtendSelectors ( $raw_value )
     {
-        $abstracts =& CssCrush::$process->abstracts;
-        $selectorRelationships =& CssCrush::$process->selectorRelationships;
-
         // Reset if called earlier, last call wins by intention.
         $this->extendArgs = array();
 
@@ -283,25 +246,17 @@ public function applyExtendables ()
             return;
         }
 
-        $abstracts =& CssCrush::$process->abstracts;
-        $selectorRelationships =& CssCrush::$process->selectorRelationships;
+        $references =& CssCrush::$process->references;
 
         // Filter the extendArgs list to usable references
         foreach ( $this->extendArgs as $key => $extend_arg ) {
 
             $name = $extend_arg->name;
 
-            if ( isset( $abstracts[ $name ] ) ) {
-
-                $parent_rule = $abstracts[ $name ];
-                $extend_arg->pointer = $parent_rule;
-
-            }
-            elseif ( isset( $selectorRelationships[ $name ] ) ) {
+            if ( isset( $references[ $name ] ) ) {
 
-                $parent_rule = $selectorRelationships[ $name ];
+                $parent_rule = $references[ $name ];
                 $extend_arg->pointer = $parent_rule;
-
             }
             else {
 
@@ -344,87 +299,6 @@ public function applyExtendables ()
     }
 
 
-    #############################
-    #  Referencing.
-
-    public function cssThisFunction ( $input, $fn_name )
-    {
-        $args = CssCrush_Function::parseArgsSimple( $input );
-
-        if ( isset( $this->localData[ $args[0] ] ) ) {
-
-            return $this->localData[ $args[0] ];
-        }
-        elseif ( isset( $args[1] ) ) {
-
-            return $args[1];
-        }
-        else {
-
-            return '';
-        }
-    }
-
-    public function cssQueryFunction ( $input, $fn_name, $call_property )
-    {
-        $result = '';
-        $args = CssCrush_Function::parseArgs( $input );
-
-        if ( count( $args ) < 1 ) {
-            return $result;
-        }
-
-        $abstracts =& CssCrush::$process->abstracts;
-        $mixins =& CssCrush::$process->mixins;
-        $selectorRelationships =& CssCrush::$process->selectorRelationships;
-
-        // Resolve arguments
-        $name = array_shift( $args );
-        $property = $call_property;
-        if ( isset( $args[0] ) ) {
-            if ( $args[0] !== 'default' ) {
-                $property = array_shift( $args );
-            }
-            else {
-                array_shift( $args );
-            }
-        }
-        $default = isset( $args[0] ) ? $args[0] : null;
-
-        // Try to match a abstract rule first
-        if ( preg_match( CssCrush_Regex::$patt->ident, $name ) ) {
-
-            // Search order: abstracts, mixins, rules
-            if ( isset( $abstracts[ $name ]->data[ $property ] ) ) {
-
-                $result = $abstracts[ $name ]->data[ $property ];
-            }
-            elseif ( isset( $mixins[ $name ]->data[ $property ] ) ) {
-
-                $result = $mixins[ $name ]->data[ $property ];
-            }
-            elseif ( isset( $selectorRelationships[ $name ]->data[ $property ] ) ) {
-
-                $result = $selectorRelationships[ $name ]->data[ $property ];
-            }
-        }
-        else {
-
-            // Look for a rule match
-            $name = CssCrush_Selector::makeReadableSelector( $name );
-            if ( isset( $selectorRelationships[ $name ]->data[ $property ] ) ) {
-
-                $result = $selectorRelationships[ $name ]->data[ $property ];
-            }
-        }
-
-        if ( $result === '' && ! is_null( $default ) ) {
-            $result = $default;
-        }
-        return $result;
-    }
-
-
     #############################
     #  Selectors.
 
@@ -472,9 +346,6 @@ public function expandSelectors ()
                     // Not creating a named rule association with this expanded selector
                     $new_set[] = new CssCrush_Selector( $row . $selector->value );
                 }
-
-                // Store the unexpanded selector to selectorRelationships
-                CssCrush::$process->selectorRelationships[ $readableValue ] = $this;
             }
             else {
 
@@ -487,13 +358,6 @@ public function expandSelectors ()
         $this->selectors = $new_set;
     }
 
-    public function indexSelectors ()
-    {
-        foreach ( $this->selectors as $selector ) {
-            CssCrush::$process->selectorRelationships[ $selector->readableValue ] = $this;
-        }
-    }
-
     public function addSelector ( $selector )
     {
         $this->selectors[ $selector->readableValue ] = $selector;
@@ -689,12 +553,7 @@ public function getIterator ()
 
 
     #############################
-    #  Rule API.
-
-    public function propertyCount ( $prop )
-    {
-        return isset( $this->properties[ $prop ] ) ? $this->properties[ $prop ] : 0;
-    }
+    #  Property indexing.
 
     public function indexProperty ( $prop )
     {
@@ -706,14 +565,42 @@ public function indexProperty ( $prop )
         }
     }
 
+    public function updatePropertyIndex ()
+    {
+        // Create a new table of properties.
+        $new_properties_table = array();
+
+        foreach ( $this as $declaration ) {
+
+            $name = $declaration->property;
+
+            if ( isset( $new_properties_table[ $name ] ) ) {
+                $new_properties_table[ $name ]++;
+            }
+            else {
+                $new_properties_table[ $name ] = 1;
+            }
+        }
+        $this->properties = $new_properties_table;
+    }
+
+
+    #############################
+    #  Rule API.
+
+    public function propertyCount ( $prop )
+    {
+        return isset( $this->properties[ $prop ] ) ? $this->properties[ $prop ] : 0;
+    }
+
     public function addDeclaration ( $prop, $value, $contextIndex = 0 )
     {
         // Create declaration, add to the stack if it's valid
         $declaration = new CssCrush_Declaration( $prop, $value, $contextIndex );
 
-        if ( $declaration->isValid ) {
+        if ( empty( $declaration->inValid ) ) {
 
-            // Manually increment the property name since we're directly updating the _declarations list
+            // Increment the property name index.
             $this->indexProperty( $prop );
             $this->declarations[] = $declaration;
             return $declaration;

From a7fdfe2b0398b1674ea62ff84768b9ee98bca1a0 Mon Sep 17 00:00:00 2001
From: Pete Boere 
Date: Sun, 17 Feb 2013 13:09:51 +0000
Subject: [PATCH 076/421] Added generic parseBlock() utility method.

---
 lib/CssCrush/Mixin.php   | 27 ++++++---------------------
 lib/CssCrush/Process.php | 14 ++------------
 lib/CssCrush/Rule.php    |  6 ++----
 lib/CssCrush/Util.php    | 27 +++++++++++++++++++++++++++
 4 files changed, 37 insertions(+), 37 deletions(-)

diff --git a/lib/CssCrush/Mixin.php b/lib/CssCrush/Mixin.php
index 09589f6..e3e4278 100644
--- a/lib/CssCrush/Mixin.php
+++ b/lib/CssCrush/Mixin.php
@@ -12,30 +12,14 @@ class CssCrush_Mixin
 
     public function __construct ( $block )
     {
-        // Strip comment markers.
-        $block = CssCrush_Util::stripCommentTokens( $block );
-
         // Prepare the arguments object.
         $this->arguments = new CssCrush_ArgList( $block );
 
-        // Re-assign with the parsed arguments string.
-        $block = $this->arguments->string;
-
-        // Split the block around semi-colons.
-        $declarations = preg_split( '!\s*;\s*!', trim( $block ), null, PREG_SPLIT_NO_EMPTY );
-
-        foreach ( $declarations as $raw_declaration ) {
-
-            $colon = strpos( $raw_declaration, ':' );
-            if ( $colon === -1 ) {
-                continue;
-            }
-
-            // Store template declarations as arrays as they are copied by value not reference.
-            $declaration = array();
+        // Parse into mixin template.
+        foreach ( CssCrush_Util::parseBlock( $this->arguments->string ) as $pair ) {
 
-            $property = strtolower( trim( substr( $raw_declaration, 0, $colon ) ) );
-            $value = trim( substr( $raw_declaration, $colon + 1 ) );
+            list( $property, $value ) = $pair;
+            $property = strtolower( $property );
 
             if ( $property === 'mixin' ) {
 
@@ -47,8 +31,9 @@ public function __construct ( $block )
                         $this->declarationsTemplate, $mixin_declarations );
                 }
             }
-            elseif ( ! empty( $value ) ) {
+            elseif ( $value !== '' ) {
 
+                // Store template declarations as arrays as they are copied by value not reference.
                 $this->declarationsTemplate[] = array(
                     'property' => $property,
                     'value' => $value,
diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php
index 26c7c7f..bb342ab 100644
--- a/lib/CssCrush/Process.php
+++ b/lib/CssCrush/Process.php
@@ -535,18 +535,8 @@ static public function cb_extractVariables ( $m )
         // Strip comment markers.
         $block = trim( CssCrush_Util::stripCommentTokens( $m[2] ) );
 
-        $pairs = preg_split( '!\s*;\s*!', $block, null, PREG_SPLIT_NO_EMPTY );
-
-        // Loop through the pairs.
-        foreach ( $pairs as $var ) {
-            $colon = strpos( $var, ':' );
-            if ( $colon === -1 ) {
-                continue;
-            }
-            $name = trim( substr( $var, 0, $colon ) );
-            $value = trim( substr( $var, $colon + 1 ) );
-            CssCrush::$process->variables[ trim( $name ) ] = $value;
-        }
+        CssCrush::$process->variables =
+            array_merge( CssCrush::$process->variables, CssCrush_Util::parseBlock( $block, true ) );
     }
 
     static protected function cb_placeVariables ( $m )
diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php
index f78d390..32c7230 100644
--- a/lib/CssCrush/Rule.php
+++ b/lib/CssCrush/Rule.php
@@ -86,7 +86,8 @@ public function __construct ( $selector_string = null, $declarations_string )
         }
 
         // Parse the declarations chunk.
-        $declarations = preg_split( '!\s*;\s*!', trim( $declarations_string ), null, PREG_SPLIT_NO_EMPTY );
+        $declarations_string = trim( CssCrush_Util::stripCommentTokens( $declarations_string ) );
+        $declarations = preg_split( '!\s*;\s*!', $declarations_string, null, PREG_SPLIT_NO_EMPTY );
 
         // First create a simple array of all properties and value pairs in raw state
         $pairs = array();
@@ -94,9 +95,6 @@ public function __construct ( $selector_string = null, $declarations_string )
         // Split declarations in to property/value pairs
         foreach ( $declarations as $declaration ) {
 
-            // Strip comments around the property
-            $declaration = CssCrush_Util::stripCommentTokens( $declaration );
-
             // Rule directives. Accept several different syntaxes for mixin and extends.
             if ( preg_match( $regex->ruleDirective, $declaration, $m ) ) {
 
diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php
index 8c64c03..c9f61c9 100644
--- a/lib/CssCrush/Util.php
+++ b/lib/CssCrush/Util.php
@@ -123,6 +123,33 @@ static public function splitDelimList ( $str, $delim = ',', $trim = true )
         return $list;
     }
 
+    static public function parseBlock ( $str, $keyed = false, $strip_comment_tokens = true )
+    {
+        if ( $strip_comment_tokens ) {
+            $str = CssCrush_Util::stripCommentTokens( $str );
+        }
+
+        $declarations = preg_split( '~\s*;\s*~', $str, null, PREG_SPLIT_NO_EMPTY );
+        $out = array();
+
+        foreach ( $declarations as $declaration ) {
+            $colon_pos = strpos( $declaration, ':' );
+            if ( $colon_pos === -1 ) {
+                continue;
+            }
+            $property = trim( substr( $declaration, 0, $colon_pos ) );
+            $value = trim( substr( $declaration, $colon_pos + 1 ) );
+
+            if ( $keyed ) {
+                $out[ $property ] = $value;
+            }
+            else {
+                $out[] = array( $property, $value );
+            }
+        }
+        return $out;
+    }
+
     static public function getLinkBetweenDirs ( $dir1, $dir2 )
     {
         // Normalise the paths.

From 162754f8e35621c9ff895dbfcb39da4222ead83d Mon Sep 17 00:00:00 2001
From: Pete Boere 
Date: Fri, 22 Feb 2013 19:31:46 +0000
Subject: [PATCH 077/421] Added grouping for function aliases so more than one
 function can be aliased in a value if it is safe to so; e.g. the four
 gradient functions are groupable since they will almost certainly be
 un-prefixed as a group.

---
 Aliases.ini                   |  2 +-
 CHANGELOG.md                  |  2 +
 lib/CssCrush/Core.php         | 44 +++++++++++++----
 lib/CssCrush/PostAliasFix.php | 33 ++++++-------
 lib/CssCrush/Process.php      | 77 +++++++++++++++++++++---------
 lib/CssCrush/Regex.php        |  1 -
 lib/CssCrush/Rule.php         | 89 ++++++++++++++++++++++++++---------
 lib/CssCrush/Util.php         | 25 ++++++----
 8 files changed, 189 insertions(+), 84 deletions(-)

diff --git a/Aliases.ini b/Aliases.ini
index a3c0ee4..3a60091 100644
--- a/Aliases.ini
+++ b/Aliases.ini
@@ -291,7 +291,7 @@
     element[] = -moz-element
 
 
-; [functions:gradients]
+[functions:gradients]
 
     ; Gradients.
     linear-gradient[] = -webkit-linear-gradient
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fe92a2d..0c7b49e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,8 @@
 ----
 * Added `-i` alias to `--file` option for the command line utility.
 * Added `@name` in-rule directive for more flexible and robust rule referencing.
+* Added grouping for function aliases so multiple related functions (e.g. gradients) can now be
+  applied to one value.
 * Rule references previously looked for the closest previous match. This behaviour has been changed
   to a 'last wins' match to be more consistent with the way CSS works. This may affect users of `@extend`
   or the `query()` function.
diff --git a/lib/CssCrush/Core.php b/lib/CssCrush/Core.php
index 14f2f15..97c0be4 100644
--- a/lib/CssCrush/Core.php
+++ b/lib/CssCrush/Core.php
@@ -154,17 +154,42 @@ static public function loadAssets ()
             $result = @parse_ini_file( $aliases_file, true );
             if ( $result !== false ) {
 
-                // Declaration aliases require a little preprocessing.
-                if ( isset( $result[ 'declarations' ] ) ) {
-                    $store = array();
-                    foreach ( $result[ 'declarations' ] as $prop_val => $aliases ) {
-                        list( $prop, $value ) = array_map( 'trim', explode( ':', $prop_val ) );
-                        foreach ( $aliases as &$alias ) {
-                            $alias = explode( ':', $alias );
+                foreach ( $result as $section => $items ) {
+
+                    // Declaration aliases require a little preparation.
+                    if ( $section === 'declarations' ) {
+                        $store = array();
+                        foreach ( $items as $prop_val => $aliases ) {
+                            list( $prop, $value ) = array_map( 'trim', explode( ':', $prop_val ) );
+                            foreach ( $aliases as &$alias ) {
+                                $alias = explode( ':', $alias );
+                            }
+                            $store[ $prop ][ $value ] = $aliases;
                         }
-                        $store[ $prop ][ $value ] = $aliases;
+                        $result[ 'declarations' ] = $store;
+                    }
+
+                    // Function groups.
+                    elseif ( strpos( $section, 'functions:' ) === 0 ) {
+                        $group = substr( $section, strlen( 'functions' ) );
+
+                        $vendor_grouped_aliases = array();
+                        foreach ( $items as $func_name => $aliases ) {
+
+                            // Assign group name to the aliasable function.
+                            $result[ 'functions' ][ $func_name ] = $group;
+
+                            foreach ( $aliases as $alias_func ) {
+                                // Only supporting vendor prefixed aliases, for now.
+                                if ( preg_match( CssCrush_Regex::$patt->vendorPrefix, $alias_func, $m ) ) {
+                                    // We'll cache the function matching regex here.
+                                    $vendor_grouped_aliases[$m[1]]['find'][] = '~(?aliases = $result;
@@ -173,6 +198,7 @@ static public function loadAssets ()
                 self::$config->bareAliasGroups = array(
                     'properties' => array(),
                     'functions' => array(),
+                    'function_groups' => array(),
                     'declarations' => array(),
                     'at-rules' => array(),
                 );
diff --git a/lib/CssCrush/PostAliasFix.php b/lib/CssCrush/PostAliasFix.php
index 745a300..db88503 100644
--- a/lib/CssCrush/PostAliasFix.php
+++ b/lib/CssCrush/PostAliasFix.php
@@ -7,20 +7,9 @@
 class CssCrush_PostAliasFix
 {
     // Currently only post fixing aliased functions.
-    static public $functions = array();
-
-    static public function init ()
-    {
-        // Register fix callbacks.
-        CssCrush_PostAliasFix::add( 'function', 'linear-gradient',
-            'csscrush__post_alias_fix_lineargradients' );
-        CssCrush_PostAliasFix::add( 'function', 'linear-repeating-gradient',
-            'csscrush__post_alias_fix_lineargradients' );
-        CssCrush_PostAliasFix::add( 'function', 'radial-gradient',
-            'csscrush__post_alias_fix_radialgradients' );
-        CssCrush_PostAliasFix::add( 'function', 'radial-repeating-gradient',
-            'csscrush__post_alias_fix_radialgradients' );
-    }
+    static public $functions = array(
+        ':gradients' => 'csscrush__post_alias_fix_gradients',
+    );
 
     static public function add ( $alias_type, $key, $callback )
     {
@@ -39,14 +28,20 @@ static public function remove ( $alias_type, $key )
     }
 }
 
-CssCrush_PostAliasFix::init();
+/**
+ * Post alias fix callback for all gradients.
+ */
+function csscrush__post_alias_fix_gradients ( $declaration_copies ) {
 
+    csscrush__post_alias_fix_lineargradients( $declaration_copies );
+    csscrush__post_alias_fix_radialgradients( $declaration_copies );
+}
 
 /**
  * Convert the new angle syntax (keyword and degree) on -x-linear-gradient() functions
  * to legacy equivalents.
  */
-function csscrush__post_alias_fix_lineargradients ( $declaration_copies, $fn_name ) {
+function csscrush__post_alias_fix_lineargradients ( $declaration_copies ) {
 
     static $angles_new, $angles_old;
     if ( ! $angles_new ) {
@@ -86,7 +81,7 @@ function csscrush__post_alias_fix_lineargradients ( $declaration_copies, $fn_nam
     // Replace the new syntax with the legacy syntax.
     $original_parens = array();
     $replacement_parens = array();
-    $fn_patt = '~(?value ) as $m ) {
 
@@ -123,11 +118,11 @@ function csscrush__post_alias_fix_lineargradients ( $declaration_copies, $fn_nam
 /**
  * Remove the 'at' keyword from -x-radial-gradient() for legacy implementations.
  */
-function csscrush__post_alias_fix_radialgradients ( $declaration_copies, $fn_name ) {
+function csscrush__post_alias_fix_radialgradients ( $declaration_copies ) {
 
     // Create new paren tokens based on the first prefixed declaration.
     // Replace the new syntax with the legacy syntax.
-    $patt = '~(?value ) as $m ) {
diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php
index bb342ab..2ef0d48 100644
--- a/lib/CssCrush/Process.php
+++ b/lib/CssCrush/Process.php
@@ -373,45 +373,76 @@ protected function filterAliases ()
         $vendor = '-' . str_replace( '-', '', $vendor ) . '-';
 
         // Loop the aliases array, filter down to the target vendor.
-        foreach ( $this->aliases as $group_name => $group_array ) {
+        foreach ( $this->aliases as $section => $group_array ) {
+
+            // Declarations aliases.
+            if ( $section === 'declarations' ) {
 
-            // Declarations aliases are special.
-            if ( 'declarations' === $group_name ) {
                 foreach ( $group_array as $property => $values ) {
-                    $result = array();
                     foreach ( $values as $value => $prefix_values ) {
-                        foreach ( $prefix_values as $declaration ) {
-                            list( $prop, $value ) = $declaration;
-                            if (
-                                strpos( $prefix, $prop ) === 0 ||
-                                strpos( $prefix, $value ) === 0
+                        foreach ( $prefix_values as $index => $declaration ) {
+
+                            list( $prop, $val ) = $declaration;
+                            if ( !(
+                                strpos( $prop, $vendor ) === 0 ||
+                                strpos( $val, $vendor ) === 0
+                                )
                             ) {
-                                $result[] = $prefix;
+                                // Unset uneeded aliases.
+                                unset( $this->aliases[$section][$property][$value][$index] );
+
+                                if ( empty( $this->aliases[$section][$property][$value] ) ) {
+                                    unset( $this->aliases[$section][$property][$value] );
+                                }
+                                if ( empty( $this->aliases[$section][$property] ) ) {
+                                    unset( $this->aliases[$section][$property] );
+                                }
                             }
                         }
                     }
-                    $this->aliases[ 'declarations' ][ $property ][ $value ] = $result;
                 }
-                continue;
             }
 
-            foreach ( $group_array as $alias_keyword => $prefix_array ) {
+            // Function group aliases.
+            elseif ( $section === 'function_groups' ) {
+
+                foreach ( $group_array as $func_group => $vendors ) {
+                    foreach ( $vendors as $fn_vendor => $replacements ) {
 
-                $result = array();
-                foreach ( $prefix_array as $prefix ) {
-                    if ( strpos( $prefix, $vendor ) === 0 ) {
-                        $result[] = $prefix;
+                        if ( "-$fn_vendor-" !== $vendor ) {
+                            unset( $this->aliases[ 'function_groups' ][ $func_group ][ $fn_vendor ] );
+                        }
                     }
                 }
-                // Prune the whole alias keyword if there is no result.
-                if ( empty( $result ) ) {
-                    unset( $this->aliases[ $group_name ][ $alias_keyword ] );
-                }
-                else {
-                    $this->aliases[ $group_name ][ $alias_keyword ] = $result;
+            }
+
+            // Everything else.
+            else {
+                foreach ( $group_array as $alias_keyword => $prefix_array ) {
+
+                    // Skip over pointers to function groups.
+                    if ( $prefix_array[0] === ':' ) {
+                        continue;
+                    }
+
+                    $result = array();
+
+                    foreach ( $prefix_array as $prefix ) {
+                        if ( strpos( $prefix, $vendor ) === 0 ) {
+                            $result[] = $prefix;
+                        }
+                    }
+                    // Prune the whole alias keyword if there is no result.
+                    if ( empty( $result ) ) {
+                        unset( $this->aliases[ $section ][ $alias_keyword ] );
+                    }
+                    else {
+                        $this->aliases[ $section ][ $alias_keyword ] = $result;
+                    }
                 }
             }
         }
+        csscrush::log($this->aliases);
     }
 
 
diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php
index c1f720e..78b15eb 100644
--- a/lib/CssCrush/Regex.php
+++ b/lib/CssCrush/Regex.php
@@ -122,4 +122,3 @@ static public function createFunctionMatchPatt ( $list, $include_math_function =
 }
 
 CssCrush_Regex::init();
-
diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php
index 32c7230..f0fc576 100644
--- a/lib/CssCrush/Rule.php
+++ b/lib/CssCrush/Rule.php
@@ -427,6 +427,7 @@ public function addPropertyAliases ()
     public function addFunctionAliases ()
     {
         $function_aliases =& CssCrush::$process->aliases[ 'functions' ];
+        $function_alias_groups =& CssCrush::$process->aliases[ 'function_groups' ];
 
         // The new modified set of declarations.
         $new_set = array();
@@ -448,37 +449,83 @@ public function addFunctionAliases ()
                 continue;
             }
 
-            // Loop the aliasable functions.
+            // Keep record of which groups have been applied.
+            $processed_groups = array();
+
             foreach ( array_keys( $intersect ) as $fn_name ) {
 
-                // For each aliased function dupe the declaration.
-                // This pretty much limits the aliasing to one function per declaration.
+                // Store for all the duplicated declarations.
                 $prefixed_copies = array();
-                foreach ( $function_aliases[ $fn_name ] as $fn_alias ) {
 
-                    // If the declaration is vendor specific only create aliases for the same vendor.
-                    if ( $declaration->vendor ) {
-                        preg_match( CssCrush_Regex::$patt->vendorPrefix, $fn_alias, $m );
-                        if ( $m[1] !== $declaration->vendor ) {
+                // Grouped function aliases.
+                if ( $function_aliases[ $fn_name ][0] === ':' ) {
+
+                    $group_id = $function_aliases[ $fn_name ];
+
+                    // If this group has been applied we can skip over.
+                    if ( isset( $processed_groups[ $group_id ] ) ) {
+                        continue;
+                    }
+
+                    // Mark group as applied.
+                    $processed_groups[ $group_id ] = true;
+
+                    $groups =& $function_alias_groups[ $group_id ];
+
+                    foreach ( $groups as $group_key => $replacements ) {
+
+                        // If the declaration is vendor specific only create aliases for the same vendor.
+                        if ( $declaration->vendor && $group_key !== $declaration->vendor ) {
                             continue;
                         }
-                    }
 
-                    $copy = clone $declaration;
+                        $copy = clone $declaration;
 
-                    // Swap in the aliased function name.
-                    $copy->value = preg_replace(
-                        '~(?value
-                    );
-                    $prefixed_copies[] = $copy;
-                    $rule_updated = true;
+                        // Make swaps.
+                        $copy->value = preg_replace(
+                            $replacements['find'],
+                            $replacements['replace'],
+                            $copy->value
+                        );
+                        $prefixed_copies[] = $copy;
+                        $rule_updated = true;
+                    }
+
+                    // Post fixes.
+                    if ( isset( CssCrush_PostAliasFix::$functions[ $group_id ] ) ) {
+                        call_user_func( CssCrush_PostAliasFix::$functions[ $group_id ], $prefixed_copies, $group_id );
+                    }
                 }
 
-                // Aliased function may require some additional fiddling.
-                if ( isset( CssCrush_PostAliasFix::$functions[ $fn_name ] ) ) {
-                    call_user_func( CssCrush_PostAliasFix::$functions[ $fn_name ], $prefixed_copies, $fn_name );
+                // Single function aliases.
+                else {
+
+                    foreach ( $function_aliases[ $fn_name ] as $fn_alias ) {
+
+                        // If the declaration is vendor specific only create aliases for the same vendor.
+                        if ( $declaration->vendor ) {
+                            preg_match( CssCrush_Regex::$patt->vendorPrefix, $fn_alias, $m );
+                            if ( $m[1] !== $declaration->vendor ) {
+                                continue;
+                            }
+                        }
+
+                        $copy = clone $declaration;
+
+                        // Make swaps.
+                        $copy->value = preg_replace(
+                            '~(?value
+                        );
+                        $prefixed_copies[] = $copy;
+                        $rule_updated = true;
+                    }
+
+                    // Post fixes.
+                    if ( isset( CssCrush_PostAliasFix::$functions[ $fn_name ] ) ) {
+                        call_user_func( CssCrush_PostAliasFix::$functions[ $fn_name ], $prefixed_copies, $fn_name );
+                    }
                 }
 
                 $new_set = array_merge( $new_set, $prefixed_copies );
diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php
index c9f61c9..fd7e46e 100644
--- a/lib/CssCrush/Util.php
+++ b/lib/CssCrush/Util.php
@@ -63,16 +63,21 @@ static public function stripCommentTokens ( $str )
 
     static public function normalizeWhiteSpace ( $str )
     {
-        $replacements = array(
-            // Convert all whitespace sequences to a single space.
-            '!\s+!S' => ' ',
-            // Trim bracket whitespace where it's safe to do it.
-            '!([\[(]) | ([\])])| ?([{}]) ?!S' => '${1}${2}${3}',
-            // Trim whitespace around delimiters and special characters.
-            '! ?([;,]) ?!S' => '$1',
-        );
-        return preg_replace(
-            array_keys( $replacements ), array_values( $replacements ), $str );
+        static $find, $replace;
+        if ( ! $find ) {
+            $replacements = array(
+                // Convert all whitespace sequences to a single space.
+                '!\s+!S' => ' ',
+                // Trim bracket whitespace where it's safe to do it.
+                '!([\[(]) | ([\])])| ?([{}]) ?!S' => '${1}${2}${3}',
+                // Trim whitespace around delimiters and special characters.
+                '! ?([;,]) ?!S' => '$1',
+            );
+            $find = array_keys( $replacements );
+            $replace = array_values( $replacements );
+        }
+
+        return preg_replace( $find, $replace, $str );
     }
 
     static public function splitDelimList ( $str, $delim = ',', $trim = true )

From 3864a627c77f7378e739b421163d05b5f817b6dc Mon Sep 17 00:00:00 2001
From: Pete Boere 
Date: Sat, 23 Feb 2013 12:33:06 +0000
Subject: [PATCH 078/421] With nesting rules the parent symbol "&" can now work
 in conjunction with the rooting symbol "^".

---
 CHANGELOG.md              |  1 +
 lib/CssCrush/Importer.php |  2 +-
 lib/CssCrush/Process.php  | 16 ++++++++++------
 lib/CssCrush/Regex.php    |  2 +-
 4 files changed, 13 insertions(+), 8 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0c7b49e..e19c69f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@
   to a 'last wins' match to be more consistent with the way CSS works. This may affect users of `@extend`
   or the `query()` function.
 * Removed data-* properties.
+* Nested rules that use the parent symbol (&) can now work in conjunction with the rooting symbol (^).
 
 1.9.1 (31th January 2013)
 -----
diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php
index aad87a2..d8bc6d6 100644
--- a/lib/CssCrush/Importer.php
+++ b/lib/CssCrush/Importer.php
@@ -340,7 +340,7 @@ static protected function addTracingStubs ( &$str )
                 if ( ! preg_match( $token_or_whitespace, $part ) ) {
 
                     // Match to a valid selector.
-                    if ( preg_match( '!^([^@]|@(?:page|abstract))!iS', $part ) ) {
+                    if ( preg_match( '!^([^@]|@(?:abstract|page))!iS', $part ) ) {
 
                         // Count line breaks between the start of stream and
                         // the matched selector to get the line number.
diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php
index 2ef0d48..9c01366 100644
--- a/lib/CssCrush/Process.php
+++ b/lib/CssCrush/Process.php
@@ -830,18 +830,20 @@ protected function prefixSelectors ()
 
                     foreach ( $rule->selectors as $rule_selector ) {
 
-                        if ( ! $rule_selector->allowPrefix ) {
+                        $use_parent_symbol = strpos( $rule_selector->value, '&' ) !== false;
+
+                        // Skipping the prefix.
+                        if ( ! $rule_selector->allowPrefix && ! $use_parent_symbol ) {
 
                             $new_selector_list[ $rule_selector->readableValue ] = $rule_selector;
                         }
-                        elseif ( strpos( $rule_selector->value, '&' ) !== false ) {
 
-                            // Ampersand is the positional symbol for where the
-                            // prefix will be placed.
+                        // Positioning the prefix with parent symbol "&".
+                        elseif ( $use_parent_symbol ) {
 
-                            // Find and replace the ampersand (once).
+                            // Find and replace the ampersand (only once).
                             $new_value = preg_replace(
-                                    '!&!',
+                                    '~&~',
                                     $arg_selector,
                                     $rule_selector->value,
                                     1 );
@@ -849,6 +851,8 @@ protected function prefixSelectors ()
                             // Not storing the selector as named.
                             $new_selector_list[] = new CssCrush_Selector( $new_value );
                         }
+
+                        // Prepending the prefix.
                         else {
 
                             // Not storing the selector as named.
diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php
index 78b15eb..5bbd6b8 100644
--- a/lib/CssCrush/Regex.php
+++ b/lib/CssCrush/Regex.php
@@ -49,7 +49,7 @@ static public function init ()
             \n(
                 [^@{}]+
                 |
-                (?: [^@{}]+ )? @(?: font-face|page|abstract ) (?!-)\b [^{]*
+                (?: [^@{}]+ )? @(?: font-face|abstract|page ) (?!-)\b [^{]*
             )
             # The declaration block.
             \{ ([^{}]*) \}

From a535ed493e391b540ad58bbc055ce4d4449a1f11 Mon Sep 17 00:00:00 2001
From: Pete Boere 
Date: Sat, 23 Feb 2013 18:41:40 +0000
Subject: [PATCH 079/421] Fixed issue with empty imported files not registering
 and reporting error.

---
 CHANGELOG.md                | 1 +
 lib/CssCrush/Importer.php   | 5 ++++-
 plugins/property-sorter.php | 5 +++--
 3 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index e19c69f..dc5b3f3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@
   or the `query()` function.
 * Removed data-* properties.
 * Nested rules that use the parent symbol (&) can now work in conjunction with the rooting symbol (^).
+* Fixed issue with empty imported files not registering.
 
 1.9.1 (31th January 2013)
 -----
diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php
index d8bc6d6..00f0901 100644
--- a/lib/CssCrush/Importer.php
+++ b/lib/CssCrush/Importer.php
@@ -92,7 +92,9 @@ static public function hostfile ()
             }
 
             // Get the import contents, if unsuccessful just continue with the import line removed.
-            if ( ! ( $import->content = @file_get_contents( $import->path ) ) ) {
+            $import->content = @file_get_contents( $import->path );
+            if ( $import->content === false ) {
+
                 CssCrush::log( "Import file '{$import->url->value}' not found" );
                 $str = substr_replace( $str, '', $match_start, $match_len );
                 continue;
@@ -105,6 +107,7 @@ static public function hostfile ()
 
             // If there are unmatched brackets inside the import, strip it.
             if ( ! self::prepareForStream( $import->content ) ) {
+
                 $str = substr_replace( $str, '', $match_start, $match_len );
                 continue;
             }
diff --git a/plugins/property-sorter.php b/plugins/property-sorter.php
index 319819f..3b89c36 100644
--- a/plugins/property-sorter.php
+++ b/plugins/property-sorter.php
@@ -153,8 +153,9 @@ function &_csscrush__property_sorter_get_table () {
     else {
 
         // Load from property-sorting.ini.
-        if ( $sorting_file_contents
-            = file_get_contents( CssCrush::$config->location . '/misc/property-sorting.ini' ) ) {
+        $sorting_file_contents =
+            file_get_contents( CssCrush::$config->location . '/misc/property-sorting.ini' );
+        if ( $sorting_file_contents !== false ) {
 
             $sorting_file_contents = preg_replace( '!;[^\r\n]*!', '', $sorting_file_contents );
             $table = preg_split( '!\s+!', trim( $sorting_file_contents ) );

From 0027a20e3ecf23d09fe39b33f745c05d43990924 Mon Sep 17 00:00:00 2001
From: Pete Boere 
Date: Sat, 2 Mar 2013 19:46:44 +0000
Subject: [PATCH 080/421] Addressed issue with selector aliases not working
 inside :any() selector groups. this() and query() functions can now expand
 some shorthand values to make more individual properties available as data.
 e.g. margin:5px => margin-top:5px,margin-right:5px etc. Started shapes
 plugin.

---
 Plugins.ini                  |   3 +
 lib/CssCrush/Declaration.php |  11 ++-
 lib/CssCrush/ExtendArg.php   |   2 +-
 lib/CssCrush/Function.php    |  58 +++++++-------
 lib/CssCrush/Mixin.php       |   2 +-
 lib/CssCrush/Process.php     | 133 +++++++++++++++++--------------
 lib/CssCrush/Rule.php        | 147 +++++++++++++++++++++++++++++++----
 lib/CssCrush/Selector.php    |  74 +++++++++++-------
 plugins/shapes.php           |  45 +++++++++++
 9 files changed, 342 insertions(+), 133 deletions(-)
 create mode 100644 plugins/shapes.php

diff --git a/Plugins.ini b/Plugins.ini
index a832fee..1c3cfb7 100644
--- a/Plugins.ini
+++ b/Plugins.ini
@@ -46,3 +46,6 @@
 ; Functions for generating noise and textures with svg data-uris.
 ; plugins[] = noise
 
+; Create SVG shapes in CSS.
+; plugins[] = shapes
+
diff --git a/lib/CssCrush/Declaration.php b/lib/CssCrush/Declaration.php
index ddb2ac7..fc67357 100644
--- a/lib/CssCrush/Declaration.php
+++ b/lib/CssCrush/Declaration.php
@@ -103,6 +103,9 @@ public function process ( $parent_rule )
                     'rule' => $parent_rule,
                 ));
 
+            // Add result to $rule->selfData.
+            $parent_rule->selfData += array($this->property => $this->value);
+
             CssCrush_Function::executeOnString( 
                 $this->value,
                 null,
@@ -114,20 +117,20 @@ public function process ( $parent_rule )
         }
 
         // Trim whitespace that may have been introduced by functions.
-        $this->value = trim( $this->value );
+        $this->value = trim($this->value);
 
         // After functions have applied value may be empty.
-        if ( $this->value === '' ) {
+        if ($this->value === '') {
 
             $this->inValid = true;
             return;
         }
 
         // Store raw value as data on the parent rule.
-        $parent_rule->queryData[ $this->property ] = $this->value;
+        $parent_rule->queryData[$this->property] = $this->value;
 
         // Capture top-level paren pairs.
-        CssCrush::$process->captureParens( $this->value );
+        CssCrush::$process->captureParens($this->value);
 
         $this->indexFunctions();
     }
diff --git a/lib/CssCrush/ExtendArg.php b/lib/CssCrush/ExtendArg.php
index 413ddbd..9fe21ae 100644
--- a/lib/CssCrush/ExtendArg.php
+++ b/lib/CssCrush/ExtendArg.php
@@ -17,7 +17,7 @@ public function __construct ( $name )
         if ( ! preg_match( CssCrush_Regex::$patt->ident, $this->name ) ) {
 
             // Not a regular name: Some kind of selector so normalize it for later comparison
-            $this->name = CssCrush_Selector::makeReadableSelector( $this->name );
+            $this->name = CssCrush_Selector::makeReadable( $this->name );
 
             // If applying the pseudo on output store
             if ( substr( $this->name, -1 ) === '!' ) {
diff --git a/lib/CssCrush/Function.php b/lib/CssCrush/Function.php
index 2fe775e..7da10e4 100644
--- a/lib/CssCrush/Function.php
+++ b/lib/CssCrush/Function.php
@@ -218,65 +218,71 @@ function csscrush_fn__a_adjust ( $input ) {
     return CssCrush_Color::colorAdjust( $color, array( 0, 0, 0, $a ) );
 }
 
-function csscrush_fn__this ( $input, $extra ) {
+function csscrush_fn__this ($input, $extra) {
 
     $args = CssCrush_Function::parseArgsSimple( $input );
-    $rule = $extra[ 'rule' ];
+    $property = $args[0];
+    $rule = $extra['rule'];
 
-    if ( isset( $rule->thisData[ $args[0] ] ) ) {
+    $rule->expandDataSet('selfData', $property);
 
-        return $rule->thisData[ $args[0] ];
+    if (isset($rule->selfData[$property])) {
+
+        return $rule->selfData[$property];
     }
+
     // Fallback value.
-    elseif ( isset( $args[1] ) ) {
+    elseif (isset($args[1])) {
 
         return $args[1];
     }
-    else {
 
-        return '';
-    }
+    return '';
 }
 
-function csscrush_fn__query ( $input, $extra ) {
+function csscrush_fn__query ($input, $extra) {
 
-    $result = '';
-    $args = CssCrush_Function::parseArgs( $input );
+    $args = CssCrush_Function::parseArgs($input);
 
-    if ( count( $args ) < 1 ) {
-        return $result;
+    if (count($args) < 1) {
+        return '';
     }
 
-    $call_property = $extra[ 'property' ];
+    $call_property = $extra['property'];
     $references =& CssCrush::$process->references;
 
     // Resolve arguments.
-    $name = array_shift( $args );
+    $name = array_shift($args);
     $property = $call_property;
-    if ( isset( $args[0] ) ) {
-        if ( $args[0] !== 'default' ) {
-            $property = array_shift( $args );
+
+    if (isset($args[0])) {
+        $args[0] = strtolower($args[0]);
+        if ($args[0] !== 'default') {
+            $property = array_shift($args);
         }
         else {
-            array_shift( $args );
+            array_shift($args);
         }
     }
-    $default = isset( $args[0] ) ? $args[0] : null;
+    $default = isset($args[0]) ? $args[0] : null;
 
-    if ( ! preg_match( CssCrush_Regex::$patt->ident, $name ) ) {
-        $name = CssCrush_Selector::makeReadableSelector( $name );
+    if (! preg_match(CssCrush_Regex::$patt->ident, $name)) {
+        $name = CssCrush_Selector::makeReadable($name);
     }
 
     // If a rule reference is found, query its data.
-    if ( isset( $references[ $name ] ) ) {
+    $result = '';
+    if (isset($references[$name])) {
         $query_rule = $references[ $name ];
         $query_rule->processDeclarations();
-        if ( isset( $query_rule->queryData[ $property ] ) ) {
-            $result = $query_rule->queryData[ $property ];
+        $query_rule->expandDataSet('queryData', $property);
+
+        if (isset($query_rule->queryData[$property])) {
+            $result = $query_rule->queryData[$property];
         }
     }
 
-    if ( $result === '' && ! is_null( $default ) ) {
+    if ($result === '' && ! is_null($default)) {
         $result = $default;
     }
     return $result;
diff --git a/lib/CssCrush/Mixin.php b/lib/CssCrush/Mixin.php
index e3e4278..46ad215 100644
--- a/lib/CssCrush/Mixin.php
+++ b/lib/CssCrush/Mixin.php
@@ -94,7 +94,7 @@ static public function parseSingleValue ( $message )
         // If no mixin or abstract rule matched, look for matching selector
         if ( ! $mixin && ! $non_mixin ) {
 
-            $selector_test = CssCrush_Selector::makeReadableSelector( $message );
+            $selector_test = CssCrush_Selector::makeReadable( $message );
 
             if ( isset( CssCrush::$process->references[ $selector_test ] ) ) {
                 $non_mixin = CssCrush::$process->references[ $selector_test ];
diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php
index 9c01366..20096e4 100644
--- a/lib/CssCrush/Process.php
+++ b/lib/CssCrush/Process.php
@@ -277,82 +277,95 @@ protected function getBoilerplate ()
 
 
     #############################
-    #  Aliases.
+    #  Selector aliases.
 
-    static protected function applySelectorAliases ( &$str )
+    protected function resolveSelectorAliases ()
     {
-        if ( CssCrush::$process->selectorAliases ) {
+        static $callback;
+        if ( ! $callback ) {
+            $callback = create_function( '$m', 'CssCrush::$process->selectorAliases[ $m[1] ] = $m[2];' );
+        }
+        $this->stream->pregReplaceCallback( CssCrush_Regex::$patt->selectorAlias, $callback );
 
-            $process = CssCrush::$process;
-            $has_parens = strpos( $str, '(' ) !== false;
+        // Merge in global selector aliases.
+        $this->selectorAliases += CssCrush::$config->selectorAliases;
 
-            if ( $has_parens ) {
-                $process->captureParens( $str );
-            }
+        // Create the selector aliases pattern and store it.
+        if ( $this->selectorAliases ) {
+            $names = implode( '|', array_keys( $this->selectorAliases ) );
+            $this->selectorAliasesPatt = '~
+              \:(' . $names . ')\b(?!-)
+              (\()?
+            ~xiS';
+        }
+    }
 
-            static $callback;
-            if ( ! $callback ) {
+    static public function applySelectorAliases ( &$str )
+    {
+        $process = CssCrush::$process;
 
-                // Thankfully this will be updated to use a real callback when support for
-                // php 5.2 is dropped.
-                $callback = create_function( '$m',
+        // Early bail conditions.
+        if (! $process->selectorAliases || ! preg_match($process->selectorAliasesPatt, $str)) {
+            return;
+        }
 
-                    '$process = CssCrush::$process;
-                    $table =& $process->selectorAliases;
-                    $value = isset( $table[ $m[1] ] ) ? $table[ $m[1] ] : "";
+        // Find all selector-alias matches.
+        $matches = CssCrush_Regex::matchAll($process->selectorAliasesPatt, $str);
 
-                    // Test for available arguments.
-                    if ( isset( $m[2] ) && $value ) {
+        $table =& $process->selectorAliases;
 
-                        // Create search and replace arrays from the arguments.
-                        $args = trim( $process->popToken( $m[2] ), "()" );
-                        $args = CssCrush_Util::splitDelimList( $args );
-                        foreach ( $args as $index => $arg ) {
-                            $search[] = "#($index)";
-                        }
+        // Step through the matches from last to first.
+        while ($match = array_pop($matches)) {
 
-                        // Apply arguments to the selector-alias value.
-                        $value = str_replace( $search, $args, $value );
+            $selector_alias_name = $match[1][0];
 
-                        // Apply arguments to string tokens within the selector-alias value.
-                        preg_match_all( CssCrush_Regex::$patt->sToken, $value, $matches );
-                        foreach ( $matches as $m ) {
-                            $label = $m[0];
-                            if ( isset( $process->tokens->s[ $label ] ) ) {
-                                $process->tokens->s[ $label ] =
-                                    str_replace( $search, $args, $process->tokens->s[ $label ] );
-                            }
-                        }
-                    }
-                    return $value;'
-                );
+            if (! isset($table[$selector_alias_name])) {
+                continue;
             }
 
-            $str = preg_replace_callback( CssCrush::$process->selectorAliasesPatt, $callback, $str );
-            if ( $has_parens ) {
-                $process->restoreParens( $str );
-            }
-        }
-    }
+            $value = $table[$selector_alias_name];
+            $start = $match[0][1];
+            $length = strlen($match[0][0]);
 
-    protected function resolveSelectorAliases ()
-    {
-        static $callback;
-        if ( ! $callback ) {
-            $callback = create_function( '$m', 'CssCrush::$process->selectorAliases[ $m[1] ] = $m[2];' );
-        }
-        $this->stream->pregReplaceCallback( CssCrush_Regex::$patt->selectorAlias, $callback );
+            // It's a function alias if a start paren is matched.
+            if (isset($match[2])) {
 
-        // Merge in global selector aliases.
-        $this->selectorAliases += CssCrush::$config->selectorAliases;
+                if (! preg_match(CssCrush_Regex::$patt->balancedParens, $str,
+                    $parens, PREG_OFFSET_CAPTURE, $start)) {
+                    continue;
+                }
+                $paren_start = $parens[0][1];
+                $paren_len = strlen($parens[0][0]);
+                $length = ($paren_start + $paren_len) - $start;
+
+                // Create substitutions list and apply.
+                $args = CssCrush_Util::splitDelimList($parens[1][0]);
+                foreach ($args as $index => $arg) {
+                    $search[] = "#($index)";
+                }
+                $value = str_replace($search, $args, $value);
+
+                // Apply substitutions to string tokens within the value.
+                preg_match_all(CssCrush_Regex::$patt->sToken, $value, $_matches);
+                foreach ($_matches as $m) {
+                    $label = $m[0];
+                    if (isset($process->tokens->s[$label])) {
+                        $process->tokens->s[$label] =
+                            str_replace($search, $args, $process->tokens->s[$label]);
+                    }
+                }
+            }
 
-        // Create the selector aliases pattern and store it.
-        if ( $this->selectorAliases ) {
-            $names = implode( '|', array_keys( $this->selectorAliases ) );
-            $this->selectorAliasesPatt = '#\:(' . $names . ')\b(?!-)(\?p\d+\?)?#iS';
+            // Splice in the result.
+            $str = substr_replace($str, $value, $start, $length);
         }
+        csscrush::log($str);
     }
 
+
+    #############################
+    #  Aliases.
+
     protected function filterAliases ()
     {
         // If a vendor target is given, we prune the aliases array.
@@ -770,9 +783,6 @@ static public function cb_extractRules ( $m )
         $rule->selector_raw = trim( $m[1] );
         $rule->declaration_raw = trim( $m[2] );
 
-        // Apply any selector aliases.
-        CssCrush_Process::applySelectorAliases( $rule->selector_raw );
-
         // Run rule_preprocess hook.
         CssCrush_Hook::run( 'rule_preprocess', $rule );
 
@@ -1096,6 +1106,9 @@ public function compile ()
         // Pull out @fragment blocks, and invoke.
         $this->resolveFragments();
 
+        // Run extract phase hooks.
+        CssCrush_Hook::run( 'process_extract', $this );
+
         // Adjust meta characters so we can extract the rules cleanly.
         $this->stream->replaceHash( array(
             '@' => "\n@",
diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php
index f0fc576..4a002d7 100644
--- a/lib/CssCrush/Rule.php
+++ b/lib/CssCrush/Rule.php
@@ -23,7 +23,7 @@ class CssCrush_Rule implements IteratorAggregate
     public $extendArgs = array();
 
     // Declarations hash table for inter-rule this() referencing.
-    public $thisData = array();
+    public $selfData = array();
 
     // Declarations hash table for external query() referencing.
     public $queryData = array();
@@ -51,7 +51,6 @@ public function __construct ( $selector_string = null, $declarations_string )
         // Parse the selectors chunk
         if ( ! empty( $selector_string ) ) {
 
-            $process->captureParens( $selector_string );
             $selectors = CssCrush_Util::splitDelimList( $selector_string );
 
             // Remove and store comments that sit above the first selector
@@ -73,14 +72,9 @@ public function __construct ( $selector_string = null, $declarations_string )
 
                     // Link the rule to the abstract name and skip forward to declaration parsing.
                     $process->references[ $abstract_name ] = $this;
-                    break;
                 }
-
-                $this->addSelector( new CssCrush_Selector( $selector ) );
-
-                // Link selectors as references.
-                foreach ( $this->selectors as $selector ) {
-                    $process->references[ $selector->readableValue ] = $this;
+                else {
+                    $this->addSelector( new CssCrush_Selector( $selector ) );
                 }
             }
         }
@@ -161,10 +155,10 @@ public function __construct ( $selector_string = null, $declarations_string )
 
             if ( trim( $value ) !== '' ) {
 
-                // Only store to $this->thisData if the value does not itself make a
+                // Only store to $this->selfData if the value does not itself make a
                 // this() call to avoid circular references.
                 if ( ! preg_match( CssCrush_Regex::$patt->thisFunction, $value ) ) {
-                    $this->thisData[ strtolower( $prop ) ] = $value;
+                    $this->selfData[ strtolower( $prop ) ] = $value;
                 }
 
                 // Add declaration.
@@ -221,9 +215,115 @@ public function processDeclarations ()
             }
         }
 
+        // selfData is done with, reclaim memory.
+        unset($this->selfData);
+
         $this->declarationsProcessed = true;
     }
 
+    public function expandDataSet ($dataset, $property)
+    {
+        // Expand shorthand properties to make them available
+        // as data for this() and query().
+        static $expandables = array(
+            'margin-top' => 'margin',
+            'margin-right' => 'margin',
+            'margin-bottom' => 'margin',
+            'margin-left' => 'margin',
+            'padding-top' => 'padding',
+            'padding-right' => 'padding',
+            'padding-bottom' => 'padding',
+            'padding-left' => 'padding',
+            'border-top-width' => 'border-width',
+            'border-right-width' => 'border-width',
+            'border-bottom-width' => 'border-width',
+            'border-left-width' => 'border-width',
+            'border-top-left-radius' => 'border-radius',
+            'border-top-right-radius' => 'border-radius',
+            'border-bottom-right-radius' => 'border-radius',
+            'border-bottom-left-radius' => 'border-radius',
+            'border-top-color' => 'border-color',
+            'border-right-color' => 'border-color',
+            'border-bottom-color' => 'border-color',
+            'border-left-color' => 'border-color',
+        );
+
+        $dataset =& $this->{$dataset};
+        $property_group = isset($expandables[$property]) ? $expandables[$property] : null;
+
+        // Bail if property non-expandable or already set.
+        if (! $property_group || isset($dataset[$property]) || ! isset($dataset[$property_group])) {
+            return;
+        }
+
+        // Get the expandable property value.
+        $value = $dataset[$property_group];
+
+        // Top-Right-Bottom-Left "trbl" expandable properties.
+        $trbl_fmt = null;
+        switch ($property_group) {
+            case 'margin':
+                $trbl_fmt = 'margin-%s';
+                break;
+            case 'padding':
+                $trbl_fmt = 'padding-%s';
+                break;
+            case 'border-width':
+                $trbl_fmt = 'border-%s-width';
+                break;
+            case 'border-radius':
+                $trbl_fmt = 'border-%s-radius';
+                break;
+            case 'border-color':
+                $trbl_fmt = 'border-%s-color';
+                break;
+        }
+        if ($trbl_fmt) {
+            $parts = explode(' ', $value);
+            $placeholders = array();
+
+            // 4 values.
+            if (isset($parts[3])) {
+                $placeholders = $parts;
+            }
+            // 3 values.
+            elseif (isset($parts[2])) {
+                $placeholders = array($parts[0], $parts[1], $parts[2], $parts[1]);
+            }
+            // 2 values.
+            elseif (isset($parts[1])) {
+                $placeholders = array($parts[0], $parts[1], $parts[0], $parts[1]);
+            }
+            // 1 value.
+            else {
+                $placeholders = array_pad($placeholders, 4, $parts[0]);
+            }
+
+            // Set positional variants.
+            if ($property_group === 'border-radius') {
+                $positions = array(
+                    'top-left',
+                    'top-right',
+                    'bottom-right',
+                    'bottom-left',
+                );
+            }
+            else {
+                $positions = array(
+                    'top',
+                    'right',
+                    'bottom',
+                    'left',
+                );
+            }
+
+            foreach ($positions as $index => $position) {
+                $prop = sprintf($trbl_fmt, $position);
+                $dataset += array($prop => $placeholders[$index]);
+            }
+        }
+    }
+
 
     #############################
     #  Rule inheritance.
@@ -272,8 +372,6 @@ public function applyExtendables ()
         // Merge this rule's extendArgs with parent extendArgs
         $this->extendArgs = array_merge( $this->extendArgs, $parent_extend_args );
 
-        // Filter now?
-
         // Add this rule's selectors to all extendArgs
         foreach ( $this->extendArgs as $extend_arg ) {
 
@@ -318,12 +416,30 @@ public function expandSelectors ()
                         preg_match( '!:any(\?p\d+\?)!', $selector->value, $m );
 
                         // Parse the arguments
-                        $expression = trim( CssCrush::$process->tokens->p[ $m[1] ], '()' );
+                        $expression = CssCrush::$process->tokens->p[$m[1]];
+
+                        // Remove outer parens.
+                        $expression = substr($expression, 1, strlen($expression) - 2);
+
+                        // Test for nested :any() expressions.
+                        $has_nesting = strpos($expression, ':any(') !== false;
+
                         $parts = preg_split( $reg_comma, $expression, null, PREG_SPLIT_NO_EMPTY );
 
                         $tmp = array();
                         foreach ( $chain as $rowCopy ) {
                             foreach ( $parts as $part ) {
+
+                                // Flatten nested :any() expressions in a hacky kind of way.
+                                if ($has_nesting) {
+                                    $part = str_replace(':any(', '', $part);
+
+                                    // If $part has unbalanced parens trim closing parens to match.
+                                    $diff = substr_count($part, ')') - substr_count($part, '(');
+                                    if ($diff > 0) {
+                                        $part = preg_replace('~\){1,'. $diff .'}$~', '', $part);
+                                    }
+                                }
                                 $tmp[] = $rowCopy . $part;
                             }
                         }
@@ -359,6 +475,9 @@ public function expandSelectors ()
     public function addSelector ( $selector )
     {
         $this->selectors[ $selector->readableValue ] = $selector;
+
+        // Link reference.
+        CssCrush::$process->references[ $selector->readableValue ] = $this;
     }
 
     public function addSelectors ( $list )
diff --git a/lib/CssCrush/Selector.php b/lib/CssCrush/Selector.php
index 1ce4451..79c9112 100644
--- a/lib/CssCrush/Selector.php
+++ b/lib/CssCrush/Selector.php
@@ -10,45 +10,31 @@ class CssCrush_Selector
     public $readableValue;
     public $allowPrefix = true;
 
-    static function makeReadableSelector ( $selector_string )
-    {
-        // Quick test for paren tokens.
-        if ( strpos( $selector_string, '?p' ) !== false ) {
-            $selector_string = CssCrush::$process->restoreTokens( $selector_string, 'p' );
-        }
-
-        // Create space around combinators, then normalize whitespace.
-        $selector_string = preg_replace( '#([>+]|~(?!=))#', ' $1 ', $selector_string );
-        $selector_string = CssCrush_Util::normalizeWhiteSpace( $selector_string );
-
-        // Quick test for string tokens.
-        if ( strpos( $selector_string, '?s' ) !== false ) {
-            $selector_string = CssCrush::$process->restoreTokens( $selector_string, 's' );
-        }
-
-        // Quick test for double-colons for backwards compat.
-        if ( strpos( $selector_string, '::' ) !== false ) {
-            $selector_string = preg_replace( '!::(after|before|first-(?:letter|line))!iS', ':$1', $selector_string );
-        }
-
-        return $selector_string;
-    }
-
     public function __construct ( $raw_selector, $associated_rule = null )
     {
+        // Look for rooting prefix.
         if ( strpos( $raw_selector, '^' ) === 0 ) {
-
             $raw_selector = ltrim( $raw_selector, "^ \n\r\t" );
             $this->allowPrefix = false;
         }
 
-        $this->readableValue = self::makeReadableSelector( $raw_selector );
+        // Take readable value from original un-altered state.
+        $this->readableValue = CssCrush_Selector::makeReadable( $raw_selector );
+
+        CssCrush_Process::applySelectorAliases( $raw_selector );
+
+        // Capture top-level paren groups.
+        CssCrush::$process->captureParens( $raw_selector );
+
         $this->value = $raw_selector;
     }
 
     public function __toString ()
     {
-        return $this->readableValue;
+        if ( ! CssCrush::$process->minifyOutput ) {
+            $this->value = CssCrush_Selector::normalizeWhiteSpace( $this->value );
+        }
+        return CssCrush_Selector::compatFilter( $this->value );
     }
 
     public function appendPseudo ( $pseudo )
@@ -61,4 +47,38 @@ public function appendPseudo ( $pseudo )
         }
         return $this->readableValue;
     }
+
+    static public function compatFilter ( $str )
+    {
+        // Replace double-colons for backwards compatability.
+        if ( strpos( $str, '::' ) !== false ) {
+            $str = preg_replace(
+                '~::(after|before|first-(?:letter|line))~iS', ':$1', $str );
+        }
+        return $str;
+    }
+
+    static public function normalizeWhiteSpace ( $str )
+    {
+        // Create space around combinators, then normalize whitespace.
+        $str = preg_replace( '~([>+]|\~(?!=))~S', ' $1 ', $str );
+        return CssCrush_Util::normalizeWhiteSpace( $str );
+    }
+
+    static function makeReadable ( $str )
+    {
+        // Quick test for paren tokens.
+        if ( strpos( $str, '?p' ) !== false ) {
+            $str = CssCrush::$process->restoreTokens( $str, 'p' );
+        }
+
+        $str = CssCrush_Selector::normalizeWhiteSpace( $str );
+
+        // Quick test for string tokens.
+        if ( strpos( $str, '?s' ) !== false ) {
+            $str = CssCrush::$process->restoreTokens( $str, 's' );
+        }
+
+        return $str;
+    }
 }
diff --git a/plugins/shapes.php b/plugins/shapes.php
new file mode 100644
index 0000000..7e88cef
--- /dev/null
+++ b/plugins/shapes.php
@@ -0,0 +1,45 @@
+ 'csscrush__enable_shapes',
+    'disable' => 'csscrush__disable_shapes',
+));
+
+function csscrush__enable_shapes () {
+    CssCrush_Hook::add( 'process_extract', 'csscrush__shapes' );
+    CssCrush_Function::register( 'shape', 'csscrush_fn__shape' );
+}
+
+function csscrush__disable_shapes () {
+    CssCrush_Hook::remove( 'process_extract', 'csscrush__shapes' );
+    CssCrush_Function::deRegister( 'shape' );
+}
+
+function csscrush__shapes ( $process ) {
+
+    static $callback, $patt;
+    if ( ! $callback ) {
+        $patt = CssCrush_Regex::create( '@shape *() *\{ *(.*?) *\};?', 'iS' );
+        $callback = create_function( '$m', '
+            $name = $m[1];
+            $block = $m[2];
+            if ( ! empty( $name ) && ! empty( $block ) ) {
+                CssCrush::$process->misc->shape_defs[ $name ] =
+                    CssCrush_Util::parseBlock( $block, true );
+            }
+        ');
+    }
+
+    // Extract shape definitions and stash them.
+    $process->stream->pregReplaceCallback( $patt, $callback );
+
+    // csscrush::log( $process->misc->shape_defs );
+}
+
+function csscrush_fn__shape () {
+    // ...
+}

From e0b7827bc20ce83876828f9a165c9a3b6798728a Mon Sep 17 00:00:00 2001
From: Pete Boere 
Date: Tue, 5 Mar 2013 10:11:25 +0000
Subject: [PATCH 081/421] Expanding the regex API, and general regex style
 cleanup.

---
 cli.php                       |   4 +-
 lib/CssCrush/ArgList.php      |   2 +-
 lib/CssCrush/Core.php         |   4 +-
 lib/CssCrush/Declaration.php  |   4 +-
 lib/CssCrush/ExtendArg.php    |   8 +--
 lib/CssCrush/Function.php     |   4 +-
 lib/CssCrush/Importer.php     |  30 +++++----
 lib/CssCrush/Mixin.php        |   4 +-
 lib/CssCrush/PostAliasFix.php |  19 +++---
 lib/CssCrush/Process.php      | 106 +++++++++++++++----------------
 lib/CssCrush/Regex.php        | 113 ++++++++++++++++++++--------------
 lib/CssCrush/Rule.php         |  27 ++++----
 lib/CssCrush/Url.php          |  11 ++--
 lib/CssCrush/Util.php         |  14 ++---
 plugins/hsl-to-hex.php        |   7 ++-
 plugins/noise.php             |   4 +-
 plugins/property-sorter.php   |   4 +-
 plugins/rgba-fallback.php     |  25 +++++---
 plugins/shapes.php            |   2 +-
 19 files changed, 219 insertions(+), 173 deletions(-)

diff --git a/cli.php b/cli.php
index 7c9c2e9..abb4061 100755
--- a/cli.php
+++ b/cli.php
@@ -231,7 +231,7 @@ function get_stdin_contents () {
 // Enable plugin args.
 if ( $enable_plugins ) {
     foreach ( $enable_plugins as $arg ) {
-        foreach ( preg_split( '!\s*,\s*!', $arg ) as $plugin ) {
+        foreach ( preg_split( '~\s*,\s*~', $arg ) as $plugin ) {
             $process_opts[ 'enable' ][] = $plugin;
         }
     }
@@ -240,7 +240,7 @@ function get_stdin_contents () {
 // Disable plugin args.
 if ( $disable_plugins ) {
     foreach ( $disable_plugins as $arg ) {
-        foreach ( preg_split( '!\s*,\s*!', $arg ) as $plugin ) {
+        foreach ( preg_split( '~\s*,\s*~', $arg ) as $plugin ) {
             $process_opts[ 'disable' ][] = $plugin;
         }
     }
diff --git a/lib/CssCrush/ArgList.php b/lib/CssCrush/ArgList.php
index cbbb7e1..f1b5cec 100644
--- a/lib/CssCrush/ArgList.php
+++ b/lib/CssCrush/ArgList.php
@@ -65,7 +65,7 @@ public function getArgValue ( $index, &$args )
         $default = isset( $this->defaults[ $index ] ) ? $this->defaults[ $index ] : '';
 
         // Recurse for nested arg() calls
-        if ( preg_match( CssCrush_Regex::$patt->aToken, $default, $m ) ) {
+        if ( preg_match( CssCrush_Regex::$patt->a_token, $default, $m ) ) {
 
             $default = $this->getArgValue( (int) $m[1], $args );
         }
diff --git a/lib/CssCrush/Core.php b/lib/CssCrush/Core.php
index 97c0be4..c2da5cc 100644
--- a/lib/CssCrush/Core.php
+++ b/lib/CssCrush/Core.php
@@ -180,10 +180,12 @@ static public function loadAssets ()
                             $result[ 'functions' ][ $func_name ] = $group;
 
                             foreach ( $aliases as $alias_func ) {
+
                                 // Only supporting vendor prefixed aliases, for now.
                                 if ( preg_match( CssCrush_Regex::$patt->vendorPrefix, $alias_func, $m ) ) {
+
                                     // We'll cache the function matching regex here.
-                                    $vendor_grouped_aliases[$m[1]]['find'][] = '~(?' . $func_name . '', 'i' );
                                     $vendor_grouped_aliases[$m[1]]['replace'][] = $alias_func;
                                 }
                             }
diff --git a/lib/CssCrush/Declaration.php b/lib/CssCrush/Declaration.php
index fc67357..af00b63 100644
--- a/lib/CssCrush/Declaration.php
+++ b/lib/CssCrush/Declaration.php
@@ -140,7 +140,7 @@ public function indexFunctions ()
         // Create an index of all regular functions in the value.
         $functions = array();
         if ( preg_match_all( CssCrush_Regex::$patt->function, $this->value, $m ) ) {
-            foreach ( $m[2] as $index => $fn_name ) {
+            foreach ( $m[1] as $index => $fn_name ) {
                 $functions[ strtolower( $fn_name ) ] = true;
             }
         }
@@ -149,6 +149,6 @@ public function indexFunctions ()
 
     public function getFullValue ()
     {
-        return CssCrush::$process->restoreTokens( $this->value, 'p' );
+        return CssCrush::$process->restoreTokens($this->value, 'p');
     }
 }
diff --git a/lib/CssCrush/ExtendArg.php b/lib/CssCrush/ExtendArg.php
index 9fe21ae..1f5ed8a 100644
--- a/lib/CssCrush/ExtendArg.php
+++ b/lib/CssCrush/ExtendArg.php
@@ -14,16 +14,16 @@ public function __construct ( $name )
     {
         $this->name = $name;
 
-        if ( ! preg_match( CssCrush_Regex::$patt->ident, $this->name ) ) {
+        if ( ! preg_match( CssCrush_Regex::$patt->rooted_ident, $this->name ) ) {
 
-            // Not a regular name: Some kind of selector so normalize it for later comparison
+            // Not a regular name: Some kind of selector so normalize it for later comparison.
             $this->name = CssCrush_Selector::makeReadable( $this->name );
 
-            // If applying the pseudo on output store
+            // If applying the pseudo on output store.
             if ( substr( $this->name, -1 ) === '!' ) {
 
                 $this->name = rtrim( $this->name, ' !' );
-                if ( preg_match( '!\:\:?[\w-]+$!', $this->name, $m ) ) {
+                if ( preg_match( '~\:\:?[\w-]+$~', $this->name, $m ) ) {
                     $this->pseudo = $m[0];
                 }
             }
diff --git a/lib/CssCrush/Function.php b/lib/CssCrush/Function.php
index 7da10e4..bac327b 100644
--- a/lib/CssCrush/Function.php
+++ b/lib/CssCrush/Function.php
@@ -149,7 +149,7 @@ function csscrush_fn__math ( $input ) {
 function csscrush_fn__percent ( $input ) {
 
     // Strip non-numeric and non delimiter characters
-    $input = preg_replace( '![^\d\.\s,]!S', '', $input );
+    $input = preg_replace( '~[^\d\.\s,]~S', '', $input );
 
     $args = preg_split( CssCrush_Regex::$patt->argListSplit, $input, -1, PREG_SPLIT_NO_EMPTY );
 
@@ -266,7 +266,7 @@ function csscrush_fn__query ($input, $extra) {
     }
     $default = isset($args[0]) ? $args[0] : null;
 
-    if (! preg_match(CssCrush_Regex::$patt->ident, $name)) {
+    if (! preg_match(CssCrush_Regex::$patt->rooted_ident, $name)) {
         $name = CssCrush_Selector::makeReadable($name);
     }
 
diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php
index 00f0901..259b5c2 100644
--- a/lib/CssCrush/Importer.php
+++ b/lib/CssCrush/Importer.php
@@ -165,6 +165,11 @@ static public function hostfile ()
 
     static protected function rewriteImportedUrls ( $import )
     {
+        static $non_import_urls_patt;
+        if (! $non_import_urls_patt) {
+            $non_import_urls_patt = CssCrush_Regex::create( '(?', 'iS' );
+        }
+
         $link = CssCrush_Util::getLinkBetweenDirs(
             CssCrush::$process->input->dir, dirname( $import->path ) );
 
@@ -173,7 +178,7 @@ static protected function rewriteImportedUrls ( $import )
         }
 
         // Match all urls that are not imports.
-        preg_match_all( '#(?content, $matches );
+        preg_match_all($non_import_urls_patt, $import->content, $matches);
 
         foreach ( $matches[0] as $token ) {
 
@@ -245,14 +250,13 @@ static protected function prepareForStream ( &$str )
 
     static protected function captureUrls ( &$str )
     {
-        $patt = '#
-            @import\x{20}(\?s\d+\?)
-            |
-            (?)|(url|data-uri)\(', 'iS');
+        }
 
         $offset = 0;
-        while ( preg_match( $patt, $str, $outer_m, PREG_OFFSET_CAPTURE, $offset ) ) {
+        while (preg_match($url_patt, $str, $outer_m, PREG_OFFSET_CAPTURE, $offset)) {
 
             $outer_offset = $outer_m[0][1];
             $is_import_url = ! isset( $outer_m[2] );
@@ -261,6 +265,7 @@ static protected function captureUrls ( &$str )
                 $url = new CssCrush_Url( $outer_m[1][0] );
                 $str = str_replace( $outer_m[1][0], $url->label, $str );
             }
+
             // Match parenthesis if not a string token.
             elseif (
                 preg_match( CssCrush_Regex::$patt->balancedParens, $str, $inner_m, PREG_OFFSET_CAPTURE, $outer_offset )
@@ -271,6 +276,7 @@ static protected function captureUrls ( &$str )
                 $str = substr_replace( $str, $url->label, $outer_offset,
                     strlen( $func_name ) + strlen( $inner_m[0][0] ) );
             }
+
             // If brackets cannot be matched, skip over the original match.
             else {
                 $offset += strlen( $outer_m[0][0] );
@@ -298,7 +304,7 @@ static protected function cb_extractCommentAndString ( $match )
 
             // Fix broken comments as they will break any subsquent
             // imported files that are inlined.
-            if ( ! preg_match( '!\*/$!', $full_match ) ) {
+            if ( ! preg_match( '~\*/$~', $full_match ) ) {
                 $full_match .= '*/';
             }
             $label = $process->addToken( $full_match, 'c' );
@@ -318,8 +324,8 @@ static protected function cb_extractCommentAndString ( $match )
 
     static protected function addTracingStubs ( &$str )
     {
-        $selector_patt = '! (^|;|\})+ ([^;{}]+) (\{) !xmS';
-        $token_or_whitespace = '!(\s*\?c\d+\?\s*|\s+)!S';
+        $selector_patt = '~ (^|;|\})+ ([^;{}]+) (\{) ~xmS';
+        $token_or_whitespace = '~(\s*\?c\d+\?\s*|\s+)~S';
 
         $matches = CssCrush_Regex::matchAll( $selector_patt, $str );
 
@@ -343,7 +349,7 @@ static protected function addTracingStubs ( &$str )
                 if ( ! preg_match( $token_or_whitespace, $part ) ) {
 
                     // Match to a valid selector.
-                    if ( preg_match( '!^([^@]|@(?:abstract|page))!iS', $part ) ) {
+                    if ( preg_match( '~^([^@]|@(?:abstract|page))~iS', $part ) ) {
 
                         // Count line breaks between the start of stream and
                         // the matched selector to get the line number.
@@ -357,7 +363,7 @@ static protected function addTracingStubs ( &$str )
 
                         // Get the currently processed file path, and escape it.
                         $current_file = str_replace( ' ', '%20', CssCrush::$process->currentFile );
-                        $current_file = preg_replace( '![^\w-]!', '\\\\$0', $current_file );
+                        $current_file = preg_replace( '~[^\w-]~', '\\\\$0', $current_file );
 
                         // Splice in tracing stub.
                         $label = CssCrush::$process->addToken( "@media -sass-debug-info{filename{font-family:$current_file}line{font-family:\\00003$line_num}}", 't' );
diff --git a/lib/CssCrush/Mixin.php b/lib/CssCrush/Mixin.php
index 46ad215..c59ab42 100644
--- a/lib/CssCrush/Mixin.php
+++ b/lib/CssCrush/Mixin.php
@@ -75,7 +75,7 @@ static public function parseSingleValue ( $message )
         //   - #selector
 
         // Test for leading name
-        if ( preg_match( '!^[\w-]+!', $message, $name_match ) ) {
+        if ( preg_match( '~^[\w-]+~', $message, $name_match ) ) {
 
             $name = $name_match[0];
 
@@ -126,7 +126,7 @@ static public function parseSingleValue ( $message )
         // We have a valid mixin.
         // Discard the name part and any wrapping parens and whitespace
         $message = substr( $message, strlen( $name ) );
-        $message = preg_replace( '!^\s*\(?\s*|\s*\)?\s*$!', '', $message );
+        $message = preg_replace( '~^\s*\(?\s*|\s*\)?\s*$~', '', $message );
 
         // e.g. "value, rgba(0,0,0,0), left 100%"
 
diff --git a/lib/CssCrush/PostAliasFix.php b/lib/CssCrush/PostAliasFix.php
index db88503..a718927 100644
--- a/lib/CssCrush/PostAliasFix.php
+++ b/lib/CssCrush/PostAliasFix.php
@@ -64,25 +64,22 @@ function csscrush__post_alias_fix_lineargradients ( $declaration_copies ) {
         $angles_old = array_values( $angles );
     }
 
-    // Degree angle regex and replace callback.
-    static $deg_patt; 
-    static $deg_convert_callback;
-    if ( ! $deg_convert_callback ) {
-        $deg_patt = '~(?<=[\( ])(' . CssCrush_Regex::$classes->number . ')deg\b~i';
+    static $deg_patt, $deg_convert_callback, $fn_patt;
+    if ( ! $deg_patt ) {
+        $deg_patt = CssCrush_Regex::create('(?<=[\( ])()deg', 'i');
         // Legacy angles move anti-clockwise and start from East, not North.
         $deg_convert_callback = create_function( '$m', '
             $angle = floatval( $m[1] );
             $angle = ( $angle + 90 ) - ( $angle * 2 );
             return ( $angle < 0 ? $angle + 360 : $angle ) . \'deg\';
         ');
+        $fn_patt = CssCrush_Regex::create('(?:(?:repeating-)?linear-gradient)()', 'iS');
     }
 
     // Create new paren tokens based on the first prefixed declaration.
     // Replace the new syntax with the legacy syntax.
     $original_parens = array();
     $replacement_parens = array();
-    $fn_patt = '~(?value ) as $m ) {
 
         $original_parens[] = $m[1][0];
@@ -122,10 +119,14 @@ function csscrush__post_alias_fix_radialgradients ( $declaration_copies ) {
 
     // Create new paren tokens based on the first prefixed declaration.
     // Replace the new syntax with the legacy syntax.
-    $patt = '~(?(?:(?:repeating-)?radial-gradient)()', 'iS');
+    }
     $original_parens = array();
     $replacement_parens = array();
-    foreach ( CssCrush_Regex::matchAll( $patt, $declaration_copies[0]->value ) as $m ) {
+    foreach (CssCrush_Regex::matchAll($fn_patt, $declaration_copies[0]->value) as $m) {
+
         $original_parens[] = $m[1][0];
         $replacement_parens[] = CssCrush::$process->addToken(
             preg_replace(
diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php
index 20096e4..1829389 100644
--- a/lib/CssCrush/Process.php
+++ b/lib/CssCrush/Process.php
@@ -148,10 +148,10 @@ public function createTokenLabel ( $type )
         return "?$type$counter?";
     }
 
-    public function addToken ( $value, $type )
+    public function addToken ($value, $type)
     {
-        $label = $this->createTokenLabel( $type );
-        $this->tokens->{ $type }[ $label ] = $value;
+        $label = $this->createTokenLabel($type);
+        $this->tokens->{$type}[$label] = $value;
         return $label;
     }
 
@@ -179,10 +179,10 @@ public function releaseToken ( $token )
     public function restoreTokens ( $str, $type = 'p' )
     {
         // Reference the token table.
-        $token_table =& $this->tokens->{ $type };
+        $token_table =& $this->tokens->{$type};
 
         // Find matching tokens.
-        $matches = CssCrush_Regex::matchAll( CssCrush_Regex::$patt->{ "{$type}Token" }, $str );
+        $matches = CssCrush_Regex::matchAll(CssCrush_Regex::$patt->{"{$type}_token"}, $str);
 
         foreach ( $matches as $m ) {
             $token = $m[0][0];
@@ -210,7 +210,7 @@ public function restoreParens ( &$str, $release = true )
     {
         $token_table =& $this->tokens->p;
 
-        foreach ( CssCrush_Regex::matchAll( CssCrush_Regex::$patt->pToken, $str ) as $m ) {
+        foreach ( CssCrush_Regex::matchAll( CssCrush_Regex::$patt->p_token, $str ) as $m ) {
             $token = $m[0][0];
             if ( isset( $token_table[ $token ] ) ) {
                 $str = str_replace( $token, $token_table[ $token ], $str );
@@ -249,7 +249,7 @@ protected function getBoilerplate ()
         $boilerplate = file_get_contents( $file );
 
         // Substitute any tags
-        if ( preg_match_all( '!\{\{([^}]+)\}\}!', $boilerplate, $boilerplate_matches ) ) {
+        if ( preg_match_all( '~\{\{([^}]+)\}\}~', $boilerplate, $boilerplate_matches ) ) {
 
             $replacements = array();
             foreach ( $boilerplate_matches[0] as $index => $tag ) {
@@ -269,7 +269,7 @@ protected function getBoilerplate ()
 
         // Pretty print.
         $EOL = $this->newline;
-        $boilerplate = preg_split( '![\t ]*(\r\n?|\n)[\t ]*!S', $boilerplate );
+        $boilerplate = preg_split( '~[\t ]*(\r\n?|\n)[\t ]*~', $boilerplate );
         $boilerplate = array_map( 'trim', $boilerplate );
         $boilerplate = "$EOL * " . implode( "$EOL * ", $boilerplate );
         return "/*{$boilerplate}$EOL */$EOL";
@@ -323,7 +323,7 @@ static public function applySelectorAliases ( &$str )
                 continue;
             }
 
-            $value = $table[$selector_alias_name];
+            $replacement = $table[$selector_alias_name];
             $start = $match[0][1];
             $length = strlen($match[0][0]);
 
@@ -343,23 +343,27 @@ static public function applySelectorAliases ( &$str )
                 foreach ($args as $index => $arg) {
                     $search[] = "#($index)";
                 }
-                $value = str_replace($search, $args, $value);
+                $replacement = str_replace($search, $args, $replacement);
 
-                // Apply substitutions to string tokens within the value.
-                preg_match_all(CssCrush_Regex::$patt->sToken, $value, $_matches);
+                // Apply substitutions to copies of string tokens within the replacement.
+                preg_match_all(CssCrush_Regex::$patt->s_token, $replacement, $_matches);
                 foreach ($_matches as $m) {
                     $label = $m[0];
                     if (isset($process->tokens->s[$label])) {
-                        $process->tokens->s[$label] =
-                            str_replace($search, $args, $process->tokens->s[$label]);
+
+                        // Create new token based on the value.
+                        $token_value = str_replace($search, $args, $process->tokens->s[$label]);
+                        $new_label = $process->addToken($token_value, 's');
+
+                        // Swap the old token label with new.
+                        $replacement = str_replace($label, $new_label, $replacement);
                     }
                 }
             }
 
             // Splice in the result.
-            $str = substr_replace($str, $value, $start, $length);
+            $str = substr_replace($str, $replacement, $start, $length);
         }
-        csscrush::log($str);
     }
 
 
@@ -455,7 +459,6 @@ protected function filterAliases ()
                 }
             }
         }
-        csscrush::log($this->aliases);
     }
 
 
@@ -561,7 +564,7 @@ static protected function placeVariables ( &$value )
         if ( strpos( $value, '$(' ) !== false ) {
 
             // Variables with default value.
-            CssCrush_Function::executeOnString( $value, $regex->varFunctionStart,
+            CssCrush_Function::executeOnString( $value, '~(\$)\(~',
                 array( '$' => array( 'CssCrush_Process', 'cb_placeVariablesWithDefault' ) ) );
 
             // Assume at least 1 replace.
@@ -577,7 +580,7 @@ static public function cb_extractVariables ( $m )
         $regex = CssCrush_Regex::$patt;
 
         // Strip comment markers.
-        $block = trim( CssCrush_Util::stripCommentTokens( $m[2] ) );
+        $block = trim(CssCrush_Util::stripCommentTokens($m[1]));
 
         CssCrush::$process->variables =
             array_merge( CssCrush::$process->variables, CssCrush_Util::parseBlock( $block, true ) );
@@ -826,7 +829,7 @@ protected function prefixSelectors ()
 
             // Match all the rule tokens.
             $rule_matches = CssCrush_Regex::matchAll(
-                CssCrush_Regex::$patt->rToken, $curly_match->inside() );
+                CssCrush_Regex::$patt->r_token, $curly_match->inside() );
 
             foreach ( $rule_matches as $rule_match ) {
 
@@ -920,7 +923,7 @@ protected function aliasAtRules ()
                     $vendor = $vendor ? $vendor[1] : null;
 
                     // Duplicate rules.
-                    if ( preg_match_all( $regex->rToken, $copy_block, $copy_matches ) ) {
+                    if ( preg_match_all( $regex->r_token, $copy_block, $copy_matches ) ) {
 
                         $originals = array();
                         $replacements = array();
@@ -976,19 +979,19 @@ protected function collate ()
         $regex_replacements = array();
         $EOL = $this->newline;
 
-        // Strip newlines added during parsing.
-        $regex_replacements[ '!\n+!' ] = '';
+        // Strip newlines added during processing.
+        $regex_replacements[ '~\n+~' ] = '';
 
         if ( $minify ) {
             // Strip whitespace around colons used in @-rule arguments.
-            $regex_replacements[ '! ?\: ?!' ] = ':';
+            $regex_replacements[ '~ ?\: ?~' ] = ':';
         }
         else {
             // Pretty printing.
-            $regex_replacements[ '!}!' ] = "$0$EOL$EOL";
-            $regex_replacements[ '!([^\s])\{!' ] = "$1 {";
-            $regex_replacements[ '! ?(@[^{]+\{)!' ] = "$1$EOL";
-            $regex_replacements[ '! ?(@[^;]+\;)!' ] = "$1$EOL";
+            $regex_replacements[ '~}~' ] = "$0$EOL$EOL";
+            $regex_replacements[ '~([^\s])\{~' ] = "$1 {";
+            $regex_replacements[ '~ ?(@[^{]+\{)~' ] = "$1$EOL";
+            $regex_replacements[ '~ ?(@[^;]+\;)~' ] = "$1$EOL";
         }
 
         // Apply all replacements.
@@ -1014,7 +1017,7 @@ protected function collate ()
 
         if ( $minify ) {
             // Trim whitespace around selector combinators.
-            $this->stream->pregReplace( '! ?([>~+]) ?!S', '$1' );
+            $this->stream->pregReplace( '~ ?([>\~+]) ?~S', '$1' );
         }
         else {
 
@@ -1147,26 +1150,29 @@ public function compile ()
 
     protected function decruft ()
     {
+        $patt =& CssCrush_Regex::$patt;
+        $classes =& CssCrush_Regex::$classes;
+
         return $this->stream->pregReplaceHash( array(
 
             // Strip leading zeros on floats.
-            '!([: \(,])(-?)0(\.\d+)!S' => '$1$2$3',
+            '~([: \(,])(-?)0(\.\d+)~S' => '$1$2$3',
 
             // Strip unnecessary units on zero values for length types.
-            '!([: \(,])\.?0(?:e[mx]|c[hm]|rem|v[hwm]|in|p[tcx])!iS' => '${1}0',
+            '~([: \(,])\.?0' . $classes->length_unit . '~iS' => '${1}0',
 
             // Collapse zero lists.
-            '!(\: *)(?:0 0 0|0 0 0 0) *([;}])!S' => '${1}0$2',
+            '~(\: *)(?:0 0 0|0 0 0 0) *([;}])~S' => '${1}0$2',
 
             // Collapse zero lists 2nd pass.
-            '!(padding|margin|border-radius) ?(\: *)0 0 *([;}])!iS' => '${1}${2}0$3',
+            '~(padding|margin|border-radius) ?(\: *)0 0 *([;}])~iS' => '${1}${2}0$3',
 
             // Dropping redundant trailing zeros on TRBL lists.
-            '!(\: *)(-?(?:\d+)?\.?\d+[a-z]{1,4}) 0 0 0 *([;}])!iS' => '$1$2 0 0$3',
-            '!(\: *)0 0 (-?(?:\d+)?\.?\d+[a-z]{1,4}) 0 *([;}])!iS' => '${1}0 0 $2$3',
+            '~(\: *)(-?(?:\d+)?\.?\d+[a-z]{1,4}) 0 0 0 *([;}])~iS' => '$1$2 0 0$3',
+            '~(\: *)0 0 (-?(?:\d+)?\.?\d+[a-z]{1,4}) 0 *([;}])~iS' => '${1}0 0 $2$3',
 
             // Compress hex codes.
-            CssCrush_Regex::$patt->cruftyHex => '#$1$2$3',
+            $patt->cruftyHex => '#$1$2$3',
         ));
     }
 
@@ -1176,24 +1182,18 @@ protected function decruft ()
 
     protected function minifyColors ()
     {
-        static $keywords_patt;
-        if ( ! $keywords_patt ) {
-            $keywords =& CssCrush_Color::loadMinifyableKeywords();
-            $keywords_patt = '~(?stream->pregReplaceCallback( $keywords_patt, $keywords_callback );
+            $keywords_patt = '~(?(rgb|hsl)\(([^\)]{5,})\)', 'iS');
+            $functions_callback = create_function('$m', '
                 $args = CssCrush_Function::parseArgs( trim( $m[2] ) );
                 if ( stripos( $m[1], \'hsl\' ) === 0 ) {
                     $args = CssCrush_Color::cssHslToRgb( $args );
@@ -1202,7 +1202,7 @@ protected function minifyColors ()
             ');
         }
 
-        $this->stream->pregReplaceCallback(
-            '~(?stream->pregReplaceCallback($keywords_patt, $keywords_callback);
+        $this->stream->pregReplaceCallback($functions_patt, $functions_callback);
     }
 }
diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php
index 5bbd6b8..92032bb 100644
--- a/lib/CssCrush/Regex.php
+++ b/lib/CssCrush/Regex.php
@@ -12,28 +12,64 @@ class CssCrush_Regex
     // Character classes.
     static public $classes;
 
+    static public $classSwaps = array();
+
     static public function init ()
     {
         self::$patt = $patt = new stdclass();
         self::$classes = $classes = new stdclass();
 
-        // Character classes.
+        // CSS type classes.
         $classes->ident = '[a-zA-Z0-9_-]+';
         $classes->number = '[+-]?\d*\.?\d+';
+        $classes->percentage = $classes->number . '%';
+        $classes->length_unit = '(?i)(?:e[mx]|c[hm]|rem|v[hwm]|in|p[tcx])(?-i)';
+        $classes->length = $classes->number . $classes->length_unit;
+        $classes->color_hex = '#[[:xdigit:]]{3}(?:[[:xdigit:]]{3})?';
+
+        // Tokens.
+        $classes->c_token = '\?c\d+\?'; // Comments.
+        $classes->s_token = '\?s\d+\?'; // Strings.
+        $classes->r_token = '\?r\d+\?'; // Rules.
+        $classes->p_token = '\?p\d+\?'; // Parens.
+        $classes->u_token = '\?u\d+\?'; // URLs.
+        $classes->t_token = '\?t\d+\?'; // Traces.
+        $classes->a_token = '\?arg(\d+)\?'; // Args.
+
+        // Boundries.
+        $classes->LB = '(?RB = '(?![\w-])'; // Right ident boundry.
+        $classes->RTB = '(?=\?[a-z])'; // Right token boundry.
+
+        // Misc.
+        $classes->vendor = '-[a-zA-Z]+-';
 
-        // Patterns.
-        $patt->ident = '~^' . $classes->ident . '$~';
-        $patt->number = '~^' . $classes->number . '$~';
+        // Create standalone class patterns, add classes as class swaps.
+        foreach ($classes as $name => $class) {
+            self::$classSwaps['<' . str_replace('_', '-', $name) . '>'] = $class;
+            $patt->{$name} = '~' . $class . '~';
+        }
+
+        // Rooted classes.
+        $patt->rooted_ident = '~^' . $classes->ident . '$~';
+        $patt->rooted_number = '~^' . $classes->number . '$~';
 
-        // @-rule blocks.
-        $patt->import        = '~@import\s+(\?u\d+\?)\s?([^;]*);~iS';
-        $patt->variables     = '~@(?:define|variables) *([^\{]*)\{ *(.*?) *\};?~iS';
-        $patt->mixin         = '~@mixin *([^\{]*)\{ *(.*?) *\};?~iS';
-        $patt->abstract      = CssCrush_Regex::create( '^@abstract\s+()', 'i' );
+        // @-rules.
+        $patt->import = CssCrush_Regex::create( '@import\s+()\s?([^;]*);', 'iS' );
+        $patt->charset = CssCrush_Regex::create('@charset\s+()\s*;', 'iS');
+        $patt->variables = CssCrush_Regex::create( '@(?:define|variables) *\{ *(.*?) *\};?', 'iS' );
+        $patt->mixin = CssCrush_Regex::create( '@mixin +() *\{ *(.*?) *\};?', 'iS' );
+        $patt->abstract = CssCrush_Regex::create( '^@abstract +()', 'i' );
         $patt->selectorAlias = CssCrush_Regex::create( '@selector-alias +\:() +([^;]+) *;', 'iS' );
-        $patt->ifDefine      = CssCrush_Regex::create( '@ifdefine +(not +)?() *\{', 'iS' );
-        $patt->fragmentDef   = CssCrush_Regex::create( '@fragment +() *\{', 'iS' );
-        $patt->fragmentCall  = CssCrush_Regex::create( '@fragment +() *(\(|;)', 'iS' );
+        $patt->ifDefine = CssCrush_Regex::create( '@ifdefine +(not +)?() *\{', 'iS' );
+        $patt->fragmentDef = CssCrush_Regex::create( '@fragment +() *\{', 'iS' );
+        $patt->fragmentCall = CssCrush_Regex::create( '@fragment +() *(\(|;)', 'iS' );
+
+        // Functions.
+        $patt->function = CssCrush_Regex::create( '()()', 'S' );
+        $patt->varFunction = CssCrush_Regex::create( '\$\( *() *\)', 'S' );
+        $patt->argFunction = CssCrush_Regex::createFunctionMatchPatt( array( 'arg' ) );
+        $patt->thisFunction = CssCrush_Regex::createFunctionMatchPatt( array( 'this' ) );
 
         $patt->commentAndString = '~
             # Quoted string (to EOF if unmatched).
@@ -56,52 +92,37 @@ static public function init ()
         ~xiS';
 
         // Balanced bracket matching.
-        $patt->balancedParens  = '!\(\s* ( (?: (?>[^()]+) | (?R) )* ) \s*\)!xS';
-        $patt->balancedCurlies = '!\{\s* ( (?: (?>[^{}]+) | (?R) )* ) \s*\}!xS';
-
-        // Tokens.
-        $patt->cToken = '!\?c\d+\?!'; // Comments
-        $patt->sToken = '!\?s\d+\?!'; // Strings
-        $patt->rToken = '!\?r\d+\?!'; // Rules
-        $patt->pToken = '!\?p\d+\?!'; // Parens
-        $patt->uToken = '!\?u\d+\?!'; // URLs
-        $patt->tToken = '!\?t\d+\?!'; // Traces
-        $patt->aToken = '!\?arg(\d+)\?!'; // Args
-
-        // Functions.
-        $patt->function = '!(^|[^a-z0-9_-])([a-z_-]+)(\?p\d+\?)!iS';
-        $patt->varFunction = CssCrush_Regex::create( '\$\(\s*()\s*\)', 'iS' );
-        $patt->varFunctionStart = '!(\$)\(!';
-        $patt->argFunction = CssCrush_Regex::createFunctionMatchPatt( array( 'arg' ) );
-        $patt->thisFunction = CssCrush_Regex::createFunctionMatchPatt( array( 'this' ) );
+        $patt->balancedParens  = '~\(\s* ( (?: (?>[^()]+) | (?R) )* ) \s*\)~xS';
+        $patt->balancedCurlies = '~\{\s* ( (?: (?>[^{}]+) | (?R) )* ) \s*\}~xS';
 
         // Misc.
-        $patt->vendorPrefix  = '!^-([a-z]+)-([a-z-]+)!iS';
-        $patt->ruleDirective = '!^(?:(@include|mixin)|(@?extends?)|(@name))[\s\:]+!iS';
-        $patt->argListSplit  = '!\s*[,\s]\s*!S';
-        $patt->mathBlacklist = '![^\.0-9\*\/\+\-\(\)]!S';
-        $patt->charset       = '!@charset\s+(\?s\d+\?)\s*;!iS';
-        $patt->cruftyHex     = '!\#([[:xdigit:]])\1([[:xdigit:]])\2([[:xdigit:]])\3!S';
+        $patt->vendorPrefix = '~^-([a-z]+)-([a-z-]+)~iS';
+        $patt->ruleDirective = '~^(?:(@include|mixin)|(@?extends?)|(@name))[\s\:]+~iS';
+        $patt->argListSplit = '~\s*[,\s]\s*~S';
+        $patt->mathBlacklist = '~[^\.0-9\*\/\+\-\(\)]~S';
+        $patt->cruftyHex = '~\#([[:xdigit:]])\1([[:xdigit:]])\2([[:xdigit:]])\3~S';
     }
 
-    static public function create ( $pattern_template, $flags = '', $delim = '!' )
+    static public function create ( $pattern_template, $flags = '', $delim = '~' )
     {
-        // Sugar.
-        $pattern = str_replace(
-                        array( '' ),
-                        array( self::$classes->ident ),
-                        $pattern_template );
+        static $find, $replace;
+        if (! $find) {
+            $find = array_keys(self::$classSwaps);
+            $replace = array_values(self::$classSwaps);
+        }
+
+        $pattern = str_replace($find, $replace, $pattern_template);
         return "$delim{$pattern}$delim{$flags}";
     }
 
     static public function matchAll ( $patt, $subject, $preprocess_patt = false, $offset = 0 )
     {
-        if ( $preprocess_patt ) {
+        if ($preprocess_patt) {
             // Assume case-insensitive.
-            $patt = self::create( $patt, 'i' );
+            $patt = self::create($patt, 'i');
         }
 
-        $count = preg_match_all( $patt, $subject, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER, $offset );
+        $count = preg_match_all($patt, $subject, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER, $offset);
         return $count ? $matches : array();
     }
 
@@ -117,7 +138,7 @@ static public function createFunctionMatchPatt ( $list, $include_math_function =
         foreach ( $list as &$fn_name ) {
             $fn_name = preg_quote( $fn_name );
         }
-        return '~(?(' . implode( '|', $list ) . ')' . $question . '\(', 'iS' );
     }
 }
 
diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php
index 4a002d7..c56b0ce 100644
--- a/lib/CssCrush/Rule.php
+++ b/lib/CssCrush/Rule.php
@@ -37,7 +37,7 @@ public function __construct ( $selector_string = null, $declarations_string )
         // If tracing store the last tracing stub, then strip all.
         if (
             $process->addTracingStubs &&
-            preg_match_all( $regex->tToken, $selector_string, $trace_tokens )
+            preg_match_all( $regex->t_token, $selector_string, $trace_tokens )
         ) {
             $trace_token = array_pop( $trace_tokens );
             $this->tracingStub = $process->fetchToken( $trace_token[0] );
@@ -45,7 +45,7 @@ public function __construct ( $selector_string = null, $declarations_string )
                 $process->releaseToken( $trace_token[0] );
             }
 
-            $selector_string = preg_replace( $regex->tToken, '', $selector_string );
+            $selector_string = preg_replace( $regex->t_token, '', $selector_string );
         }
 
         // Parse the selectors chunk
@@ -56,7 +56,7 @@ public function __construct ( $selector_string = null, $declarations_string )
             // Remove and store comments that sit above the first selector
             // remove all comments between the other selectors
             if ( strpos( $selectors[0], '?c' ) !== false ) {
-                preg_match_all( $regex->cToken, $selectors[0], $m );
+                preg_match_all( $regex->c_token, $selectors[0], $m );
                 $this->comments = $m[0];
             }
 
@@ -81,7 +81,7 @@ public function __construct ( $selector_string = null, $declarations_string )
 
         // Parse the declarations chunk.
         $declarations_string = trim( CssCrush_Util::stripCommentTokens( $declarations_string ) );
-        $declarations = preg_split( '!\s*;\s*!', $declarations_string, null, PREG_SPLIT_NO_EMPTY );
+        $declarations = preg_split( '~\s*;\s*~', $declarations_string, null, PREG_SPLIT_NO_EMPTY );
 
         // First create a simple array of all properties and value pairs in raw state
         $pairs = array();
@@ -401,19 +401,24 @@ public function applyExtendables ()
     public function expandSelectors ()
     {
         $new_set = array();
-        $reg_comma = '!\s*,\s*!';
+
+        static $any_patt, $reg_comma;
+        if (! $any_patt) {
+            $any_patt = CssCrush_Regex::create(':any()', 'i');
+            $reg_comma = '~\s*,\s*~';
+        }
 
         foreach ( $this->selectors as $readableValue => $selector ) {
 
-            $pos = strpos( $selector->value, ':any?' );
+            $pos = stripos( $selector->value, ':any?' );
 
             if ( $pos !== false ) {
 
                 // Contains an :any statement so we expand
-                $chain = array( '' );
+                $chain = array('');
                 do {
                     if ( $pos === 0 ) {
-                        preg_match( '!:any(\?p\d+\?)!', $selector->value, $m );
+                        preg_match( $any_patt, $selector->value, $m );
 
                         // Parse the arguments
                         $expression = CssCrush::$process->tokens->p[$m[1]];
@@ -422,7 +427,7 @@ public function expandSelectors ()
                         $expression = substr($expression, 1, strlen($expression) - 2);
 
                         // Test for nested :any() expressions.
-                        $has_nesting = strpos($expression, ':any(') !== false;
+                        $has_nesting = stripos($expression, ':any(') !== false;
 
                         $parts = preg_split( $reg_comma, $expression, null, PREG_SPLIT_NO_EMPTY );
 
@@ -432,7 +437,7 @@ public function expandSelectors ()
 
                                 // Flatten nested :any() expressions in a hacky kind of way.
                                 if ($has_nesting) {
-                                    $part = str_replace(':any(', '', $part);
+                                    $part = str_ireplace(':any(', '', $part);
 
                                     // If $part has unbalanced parens trim closing parens to match.
                                     $diff = substr_count($part, ')') - substr_count($part, '(');
@@ -452,7 +457,7 @@ public function expandSelectors ()
                         }
                         $selector->value = substr( $selector->value, $pos );
                     }
-                } while ( ( $pos = strpos( $selector->value, ':any?' ) ) !== false );
+                } while ( ( $pos = stripos( $selector->value, ':any?' ) ) !== false );
 
                 // Finish off
                 foreach ( $chain as &$row ) {
diff --git a/lib/CssCrush/Url.php b/lib/CssCrush/Url.php
index afe58db..bd24f65 100644
--- a/lib/CssCrush/Url.php
+++ b/lib/CssCrush/Url.php
@@ -18,7 +18,7 @@ public function __construct ( $raw_value, $convert_to_data = false )
         $regex = CssCrush_Regex::$patt;
         $process = CssCrush::$process;
 
-        if ( preg_match( $regex->sToken, $raw_value ) ) {
+        if ( preg_match( $regex->s_token, $raw_value ) ) {
             $this->value = trim( $process->fetchToken( $raw_value ), '\'"' );
             $process->releaseToken( $raw_value );
         }
@@ -33,7 +33,7 @@ public function __construct ( $raw_value, $convert_to_data = false )
     public function __toString ()
     {
         $quote = '';
-        if ( preg_match( '![()*]!', $this->value ) || 'data' === $this->protocol ) {
+        if ( preg_match( '~[()*]~', $this->value ) || 'data' === $this->protocol ) {
             $quote = '"';
         }
         return "url(http://github.com/$quote$this-%3Evalue$quote)";
@@ -48,12 +48,13 @@ public function evaluate ()
     {
         $leading_variable = strpos( $this->value, '$(' ) === 0;
 
-        if ( preg_match( '!^([a-z]+)\:!i', $this->value, $m ) ) {
+        // Match a protocol.
+        if ( preg_match( '~^([a-z]+)\:~i', $this->value, $m ) ) {
             $this->protocol = strtolower( $m[1] );
         }
         else {
             // Normalize './' led paths.
-            $this->value = preg_replace( '!^\.\/+!i', '', $this->value );
+            $this->value = preg_replace( '~^\.\/+~i', '', $this->value );
             if ( $this->value !== '' && $this->value[0] === '/' ) {
                 $this->isRooted = true;
             }
@@ -61,7 +62,7 @@ public function evaluate ()
                 $this->isRelative = true;
             }
             // Normalize slashes.
-            $this->value = rtrim( preg_replace( '![\\\\/]+!', '/', $this->value ), '/' );
+            $this->value = rtrim( preg_replace( '~[\\\\/]+~', '/', $this->value ), '/' );
         }
         return $this;
     }
diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php
index fd7e46e..791fddb 100644
--- a/lib/CssCrush/Util.php
+++ b/lib/CssCrush/Util.php
@@ -20,10 +20,10 @@ static public function htmlAttributes ( array $attributes )
     static public function normalizePath ( $path, $strip_drive_letter = false )
     {
         if ( $strip_drive_letter ) {
-            $path = preg_replace( '!^[a-z]\:!i', '', $path );
+            $path = preg_replace( '~^[a-z]\:~i', '', $path );
         }
         // Backslashes and repeat slashes to a single forward slash.
-        $path = rtrim( preg_replace( '![\\\\/]+!', '/', $path ), '/' );
+        $path = rtrim( preg_replace( '~[\\\\/]+~', '/', $path ), '/' );
 
         // Removing redundant './'.
         $path = str_replace( '/./', '/', $path );
@@ -58,7 +58,7 @@ static public function find ()
 
     static public function stripCommentTokens ( $str )
     {
-        return preg_replace( CssCrush_Regex::$patt->cToken, '', $str );
+        return preg_replace( CssCrush_Regex::$patt->c_token, '', $str );
     }
 
     static public function normalizeWhiteSpace ( $str )
@@ -67,11 +67,11 @@ static public function normalizeWhiteSpace ( $str )
         if ( ! $find ) {
             $replacements = array(
                 // Convert all whitespace sequences to a single space.
-                '!\s+!S' => ' ',
+                '~\s+~S' => ' ',
                 // Trim bracket whitespace where it's safe to do it.
-                '!([\[(]) | ([\])])| ?([{}]) ?!S' => '${1}${2}${3}',
+                '~([\[(]) | ([\])])| ?([{}]) ?~S' => '${1}${2}${3}',
                 // Trim whitespace around delimiters and special characters.
-                '! ?([;,]) ?!S' => '$1',
+                '~ ?([;,]) ?~S' => '$1',
             );
             $find = array_keys( $replacements );
             $replace = array_values( $replacements );
@@ -108,7 +108,7 @@ static public function splitDelimList ( $str, $delim = ',', $trim = true )
         }
 
         if ( $do_preg_split ) {
-            $list = preg_split( '!' . $delim . '!', $str );
+            $list = preg_split( '~' . $delim . '~', $str );
         }
         else {
             $list = explode( $delim, $str );
diff --git a/plugins/hsl-to-hex.php b/plugins/hsl-to-hex.php
index 807185c..df3511e 100644
--- a/plugins/hsl-to-hex.php
+++ b/plugins/hsl-to-hex.php
@@ -25,10 +25,15 @@ function csscrush__disable_hsl_to_hex () {
 
 function csscrush__hsl_to_hex ( CssCrush_Rule $rule ) {
 
+    static $hsl_patt;
+    if (! $hsl_patt) {
+        $hsl_patt = CssCrush_Regex::create('hsl()', 'i');
+    }
+
     foreach ( $rule as &$declaration ) {
 
         if ( ! $declaration->skip && isset( $declaration->functions[ 'hsl' ] ) ) {
-            while ( preg_match( '!hsl(\?p\d+\?)!', $declaration->value, $m ) ) {
+            while ( preg_match( $hsl_patt, $declaration->value, $m ) ) {
                 $token = $m[1];
                 $color = new CssCrush_Color( 'hsl' . CssCrush::$process->fetchToken( $token ) );
                 CssCrush::$process->releaseToken( $token );
diff --git a/plugins/noise.php b/plugins/noise.php
index c53adea..2b82ef3 100644
--- a/plugins/noise.php
+++ b/plugins/noise.php
@@ -133,7 +133,6 @@ function csscrush__noise_generator ( $input, $defaults ) {
     $frequency = $defaults[ 'frequency' ];
     $octaves = 1;
     $sharpen = $defaults[ 'sharpen' ];
-    $number_patt = CssCrush_Regex::$patt->number;
 
     if ( ( $arg = array_shift( $args ) ) !== 'default' ) {
         foreach( explode( ' ', $arg ) as $index => $value ) {
@@ -144,7 +143,7 @@ function csscrush__noise_generator ( $input, $defaults ) {
                     break;
                 case 1:
                 case 2:
-                    if ( preg_match( $number_patt, $value ) ) {
+                    if ( preg_match( CssCrush_Regex::$patt->rooted_number, $value ) ) {
                         $octaves = $value;
                     }
                     elseif ( in_array( $value, $sharpen_modes ) ) {
@@ -224,7 +223,6 @@ function csscrush__noise_generator ( $input, $defaults ) {
     $svg .= "";
     $svg .= "";
     $svg .= '';
-    // csscrush::log($svg);
 
     // Create data-uri url and return token label.
     $url = new CssCrush_Url( 'data:image/svg+xml;base64,' . base64_encode( $svg ) );
diff --git a/plugins/property-sorter.php b/plugins/property-sorter.php
index 3b89c36..bb167f0 100644
--- a/plugins/property-sorter.php
+++ b/plugins/property-sorter.php
@@ -157,8 +157,8 @@ function &_csscrush__property_sorter_get_table () {
             file_get_contents( CssCrush::$config->location . '/misc/property-sorting.ini' );
         if ( $sorting_file_contents !== false ) {
 
-            $sorting_file_contents = preg_replace( '!;[^\r\n]*!', '', $sorting_file_contents );
-            $table = preg_split( '!\s+!', trim( $sorting_file_contents ) );
+            $sorting_file_contents = preg_replace( '~;[^\r\n]*~', '', $sorting_file_contents );
+            $table = preg_split( '~\s+~', trim( $sorting_file_contents ) );
         }
         else {
             trigger_error( __METHOD__ . ": Property sorting file not found.\n", E_USER_NOTICE );
diff --git a/plugins/rgba-fallback.php b/plugins/rgba-fallback.php
index 89d315c..47e1da2 100644
--- a/plugins/rgba-fallback.php
+++ b/plugins/rgba-fallback.php
@@ -1,12 +1,12 @@
 $', 'i');
+    }
+
     $new_set = array();
     foreach ( $rule as $declaration ) {
+
         $is_viable = in_array( $declaration->property, $rgba_props );
-        if ( 
+        if (
             $declaration->skip ||
-            ! $is_viable || 
-            $is_viable && !preg_match( '!^rgba\?p\d+\?$!', $declaration->value )
+            ! $is_viable ||
+            $is_viable && ! preg_match( $rgb_patt, $declaration->value )
         ) {
             $new_set[] = $declaration;
             continue;
         }
-        // Create rgb value from rgba
+
+        // Create rgb value from rgba.
         $raw_value = $declaration->getFullValue();
         $raw_value = substr( $raw_value, 5, strlen( $raw_value ) - 1 );
         list( $r, $g, $b, $a ) = explode( ',', $raw_value );
-        
-        // Add rgb value to the stack, followed by rgba 
+
+        // Add rgb value to the stack, followed by rgba.
         $new_set[] = new CssCrush_Declaration( $declaration->property, "rgb($r,$g,$b)" );
         $new_set[] = $declaration;
     }
diff --git a/plugins/shapes.php b/plugins/shapes.php
index 7e88cef..2a5495a 100644
--- a/plugins/shapes.php
+++ b/plugins/shapes.php
@@ -23,7 +23,7 @@ function csscrush__shapes ( $process ) {
 
     static $callback, $patt;
     if ( ! $callback ) {
-        $patt = CssCrush_Regex::create( '@shape *() *\{ *(.*?) *\};?', 'iS' );
+        $patt = CssCrush_Regex::create( '@shape +() *\{ *(.*?) *\};?', 'iS' );
         $callback = create_function( '$m', '
             $name = $m[1];
             $block = $m[2];

From 77b1246a3e141a0e240beb9120a1e495953f3ff9 Mon Sep 17 00:00:00 2001
From: Pete Boere 
Date: Thu, 7 Mar 2013 14:02:18 +0000
Subject: [PATCH 082/421] Renamed shapes plugin as 'svg' and created most of
 the element definitions. Updated changelog.

---
 CHANGELOG.md       |   3 +-
 Plugins.ini        |   4 +-
 plugins/shapes.php |  45 ------
 plugins/svg.php    | 395 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 399 insertions(+), 48 deletions(-)
 delete mode 100644 plugins/shapes.php
 create mode 100644 plugins/svg.php

diff --git a/CHANGELOG.md b/CHANGELOG.md
index dc5b3f3..712c6f7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,12 +1,13 @@
 1.10 (?)
 ----
-* Added `-i` alias to `--file` option for the command line utility.
+* Added SVG plugin (define and embed SVG elements in CSS).
 * Added `@name` in-rule directive for more flexible and robust rule referencing.
 * Added grouping for function aliases so multiple related functions (e.g. gradients) can now be
   applied to one value.
 * Rule references previously looked for the closest previous match. This behaviour has been changed
   to a 'last wins' match to be more consistent with the way CSS works. This may affect users of `@extend`
   or the `query()` function.
+* Added `-i` alias to `--file` option for the command line utility.
 * Removed data-* properties.
 * Nested rules that use the parent symbol (&) can now work in conjunction with the rooting symbol (^).
 * Fixed issue with empty imported files not registering.
diff --git a/Plugins.ini b/Plugins.ini
index 1c3cfb7..f100863 100644
--- a/Plugins.ini
+++ b/Plugins.ini
@@ -46,6 +46,6 @@
 ; Functions for generating noise and textures with svg data-uris.
 ; plugins[] = noise
 
-; Create SVG shapes in CSS.
-; plugins[] = shapes
+; Define and embed SVG shapes in CSS.
+; plugins[] = svg
 
diff --git a/plugins/shapes.php b/plugins/shapes.php
deleted file mode 100644
index 2a5495a..0000000
--- a/plugins/shapes.php
+++ /dev/null
@@ -1,45 +0,0 @@
- 'csscrush__enable_shapes',
-    'disable' => 'csscrush__disable_shapes',
-));
-
-function csscrush__enable_shapes () {
-    CssCrush_Hook::add( 'process_extract', 'csscrush__shapes' );
-    CssCrush_Function::register( 'shape', 'csscrush_fn__shape' );
-}
-
-function csscrush__disable_shapes () {
-    CssCrush_Hook::remove( 'process_extract', 'csscrush__shapes' );
-    CssCrush_Function::deRegister( 'shape' );
-}
-
-function csscrush__shapes ( $process ) {
-
-    static $callback, $patt;
-    if ( ! $callback ) {
-        $patt = CssCrush_Regex::create( '@shape +() *\{ *(.*?) *\};?', 'iS' );
-        $callback = create_function( '$m', '
-            $name = $m[1];
-            $block = $m[2];
-            if ( ! empty( $name ) && ! empty( $block ) ) {
-                CssCrush::$process->misc->shape_defs[ $name ] =
-                    CssCrush_Util::parseBlock( $block, true );
-            }
-        ');
-    }
-
-    // Extract shape definitions and stash them.
-    $process->stream->pregReplaceCallback( $patt, $callback );
-
-    // csscrush::log( $process->misc->shape_defs );
-}
-
-function csscrush_fn__shape () {
-    // ...
-}
diff --git a/plugins/svg.php b/plugins/svg.php
new file mode 100644
index 0000000..22532a0
--- /dev/null
+++ b/plugins/svg.php
@@ -0,0 +1,395 @@
+ 'csscrush__enable_svg',
+    'disable' => 'csscrush__disable_svg',
+));
+
+function csscrush__enable_svg () {
+    CssCrush_Hook::add('process_extract', 'csscrush__svg');
+    CssCrush_Function::register('svg', 'csscrush_fn__svg');
+}
+
+function csscrush__disable_svg () {
+    CssCrush_Hook::remove('process_extract', 'csscrush__svg');
+    CssCrush_Function::deRegister('svg');
+}
+
+function csscrush__svg ($process) {
+
+    static $callback, $patt;
+    if (! $callback) {
+        $patt = CssCrush_Regex::create('@svg +() *\{ *(.*?) *\};?', 'iS');
+        $callback = create_function('$m', '
+            $name = strtolower($m[1]);
+            $block = $m[2];
+            if (! empty($name) && ! empty($block)) {
+                CssCrush::$process->misc->svg_defs[$name] =
+                    array_change_key_case(CssCrush_Util::parseBlock($block, true));
+            }
+        ');
+    }
+
+    // Extract svg definitions.
+    $process->stream->pregReplaceCallback($patt, $callback);
+}
+
+function csscrush_fn__svg ($input) {
+
+    $svg_deinitions =& CssCrush::$process->misc->svg_defs;
+    static $types = array(
+        'circle' => array('element' => 'circle'),
+        'ellipse' => array('element' => 'ellipse'),
+        'rect' => array('element' => 'rect'),
+        'polygon' => array('element' => 'polygon'),
+        'path' => array('element' => 'path'),
+        'line' => array('element' => 'line'),
+        'polyline' => array('element' => 'polyline'),
+        'star' => array('element' => 'path'),
+    );
+
+    $name = strtolower($input);
+
+    // Bail if no SVG registered by this name.
+    if (! isset($svg_deinitions[$name])) {
+        return '';
+    }
+
+    $data = $svg_deinitions[$name];
+
+    // Bail if type not recognised.
+    $type = isset($data['type']) ? strtolower($data['type']) : 'rect';
+    if (! isset($types[$type])) {
+        return '';
+    }
+
+    // Keys that represent custom svg properties.
+    static $element_props = array(
+        'type' => true,
+        'data' => true,
+        'twist' => true,
+        'radius' => true,
+        'corner-radius' => true,
+        'star-points' => true,
+        'points' => true,
+        'margin' => true,
+        'drop-shadow' => true,
+        'drop-shadow-opacity' => true,
+        'sides' => true,
+        'width' => true,
+        'height' => true,
+    );
+
+    // Keys that go to direct to element attributes.
+    static $attribute_props = array(
+        'transform' => true,
+    );
+
+    // Initialize SVG attributes.
+    $svg_attrs = array('xmlns' => 'http://www.w3.org/2000/svg');
+    $svg_attrs['width'] = 0;
+    $svg_attrs['height'] = 0;
+
+    // Initialize element attributes.
+    $element_attrs = array('id' => 'e') + array_intersect_key($data, $attribute_props);
+    $element_data = array_intersect_key($data, $element_props);
+
+    // Everything remaining is treated as CSS.
+    $style_props = array_diff_key($data, $element_props, $attribute_props);
+
+    // Prepopulate common attributes.
+    if (isset($element_data['margin'])) {
+        $parts = csscrush__svg_parselist($element_data['margin']);
+        $count = count($parts);
+        if ($count === 1) {
+            $element_data['margin'] = array($parts[0], $parts[0], $parts[0], $parts[0]);
+        }
+        elseif ($count === 2) {
+            $element_data['margin'] = array($parts[0], $parts[1], $parts[0], $parts[1]);
+        }
+        elseif ($count === 3) {
+            $element_data['margin'] = array($parts[0], $parts[1], $parts[2], $parts[1]);
+        }
+        else {
+            $element_data['margin'] = $parts;
+        }
+    }
+    else {
+        $element_data['margin'] = array(0,0,0,0);
+    }
+
+    // Drop-shadow filter.
+    $filters = '';
+    if (isset($element_data['drop-shadow'])) {
+        $parts = csscrush__svg_parselist($element_data['drop-shadow'], false);
+        list($ds_x, $ds_y, $ds_strength, $ds_color) = $parts += array(
+            2, // x offset.
+            2, // y offset.
+            2, // strength.
+            'black', // color.
+        );
+        $filters .= '';
+        $filters .= "";
+        $filters .= "";
+        $filters .= "";
+        $filters .= "";
+        if (isset($element_data['drop-shadow-opacity'])) {
+            $ds_opacity = $element_data['drop-shadow-opacity'];
+            $filters .= '';
+            $filters .= "";
+            $filters .= '';
+        }
+        $filters .= '';
+        $filters .= '';
+        $filters .= '';
+        $filters .= '';
+        $filters .= '';
+        $style_props['filter'] = 'url(#f)';
+    }
+
+    // Apply SVG callback.
+    call_user_func_array("csscrush__svg_$type",
+        array(&$element_data, &$element_attrs, &$svg_attrs, &$style_props));
+
+    // Create SVG markup.
+    $svg_attrs = CssCrush_Util::htmlAttributes($svg_attrs);
+    $styles = array();
+    foreach ($style_props as $property => $value) {
+        $styles[] = "$property:$value";
+    }
+    $styles = implode(';', $styles);
+    $element = $types[$type]['element'];
+    $element_attrs = CssCrush_Util::htmlAttributes($element_attrs);
+
+    $svg[] = "";
+    $svg[] = '';
+    $svg[] = $filters;
+    $svg[] = '';
+    $svg[] = '';
+    $svg[] = "<$element$element_attrs/>";
+    $svg[] = '';
+
+    // Debugging...
+    // $code = implode("\n", $svg);
+    // $test = '
' . htmlspecialchars($code) . '
'; + // echo $test, $code; + + // Create data-uri url and return token label. + $url = new CssCrush_Url('data:image/svg+xml;base64,' . base64_encode(implode('', $svg))); + + return $url->label; +} + + +/* + Circle callback. +*/ +function csscrush__svg_circle (&$element_data, &$element_attrs, &$svg_attrs) { + + // Ensure required attributes have defaults set. + $element_data += array( + 'radius' => 50, + ); + + list($margin_top, $margin_right, $margin_bottom, $margin_left) = $element_data['margin']; + + $element_attrs['r'] = $element_data['radius']; + + $radius = $element_data['radius']; + $diameter = $radius * 2; + + $element_attrs['cx'] = $margin_left + $radius; + $element_attrs['cy'] = $margin_top + $radius; + + $svg_attrs['width'] = $margin_left + $diameter + $margin_right; + $svg_attrs['height'] = $margin_top + $diameter + $margin_bottom; +} + +/* + Rect callback. +*/ +function csscrush__svg_rect (&$element_data, &$element_attrs, &$svg_attrs) { + + // Ensure required attributes have defaults set. + $element_data += array( + 'width' => 50, + 'height' => 50, + ); + + list($margin_top, $margin_right, $margin_bottom, $margin_left) = $element_data['margin']; + + $element_attrs['x'] = $margin_left; + $element_attrs['y'] = $margin_top; + $element_attrs['width'] = $element_data['width']; + $element_attrs['height'] = $element_data['height']; + + if (isset($element_data['corner-radius'])) { + $args = csscrush__svg_parselist($element_data['corner-radius']); + $element_attrs['rx'] = isset($args[0]) ? $args[0] : 0; + $element_attrs['ry'] = isset($args[1]) ? $args[1] : $args[0]; + } + + $svg_attrs['width'] = $margin_left + $element_data['width'] + $margin_right; + $svg_attrs['height'] = $margin_top + $element_data['height'] + $margin_bottom; +} + +/* + Ellipse callback. +*/ +function csscrush__svg_ellipse (&$element_data, &$element_attrs, &$svg_attrs) { + + // Ensure required attributes have defaults set. + $element_data += array( + 'radius' => '100 50', + ); + + list($margin_top, $margin_right, $margin_bottom, $margin_left) = $element_data['margin']; + + $radius = csscrush__svg_parselist($element_data['radius']); + $radius_x = $radius[0]; + $radius_y = isset($radius[1]) ? $radius[1] : $radius[0]; + + $element_attrs['rx'] = $radius_x; + $element_attrs['ry'] = $radius_y; + + $element_attrs['cx'] = $margin_left + $radius_x; + $element_attrs['cy'] = $margin_top + $radius_y; + + $svg_attrs['width'] = $margin_left + ($radius_x * 2) + $margin_right; + $svg_attrs['height'] = $margin_top + ($radius_y * 2) + $margin_bottom; +} + +/* + Path callback. +*/ +function csscrush__svg_path (&$element_data, &$element_attrs, &$svg_attrs, &$style_props) { + + // Ensure required attributes have defaults set. + $element_data += array( + 'data' => 'M 10,10 l 10,0 l 0,10 l 10,0 l 0,10', + 'width' => 40, + 'height' => 40, + ); + + // Unclosed paths have implicit fill. + $style_props += array( + 'fill' => 'none', + ); + + $element_attrs['d'] = $element_data['data']; + + $svg_attrs['width'] = $element_data['width']; + $svg_attrs['height'] = $element_data['height']; +} + +/* + Polyline callback. +*/ +function csscrush__svg_polyline (&$element_data, &$element_attrs, &$svg_attrs, &$style_props) { + + // Ensure required attributes have defaults set. + $element_data += array( + 'points' => '20,20 40,20 40,40 60,40 60,60', + 'width' => 80, + 'height' => 80, + ); + + // Polylines have implicit fill. + $style_props += array( + 'fill' => 'none', + ); + + $element_attrs['points'] = $element_data['points']; + + $svg_attrs['width'] = $element_data['width']; + $svg_attrs['height'] = $element_data['height']; +} + +/* + Line callback. +*/ +function csscrush__svg_line (&$element_data, &$element_attrs, &$svg_attrs, &$style_props) { + + // Ensure required attributes have defaults set. + $element_data += array( + 'points' => '10,10 70,70', + 'width' => 80, + 'height' => 80, + ); + + // Set a default stroke. + $style_props += array( + 'stroke' => '#000', + ); + + $points = preg_split('~[, ]+~', $element_data['points']); + $element_attrs['x1'] = $points[0]; + $element_attrs['y1'] = $points[1]; + $element_attrs['x2'] = $points[2]; + $element_attrs['y2'] = $points[3]; + + $svg_attrs['width'] = $element_data['width']; + $svg_attrs['height'] = $element_data['height']; +} + +/* + Polygon callback. +*/ +function csscrush__svg_polygon (&$element_data, &$element_attrs, &$svg_attrs, &$style_props) { + + // Ensure required attributes have defaults set. + $element_data += array( + 'points' => '60,10 10,60 60,60', + 'width' => 70, + 'height' => 70, + ); + + $element_attrs['points'] = $element_data['points']; + + $svg_attrs['width'] = $element_data['width']; + $svg_attrs['height'] = $element_data['height']; +} + + +/* + Helpers. +*/ +function csscrush__svg_parselist ($str, $numbers = true) { + $list = preg_split('~ +~', trim($str)); + return $numbers ? array_map('floatval', $list) : $list; +} From 63d9f21fd55ff14e3d69c2d0bd19a66979bcdc30 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 13 Mar 2013 12:09:53 +0000 Subject: [PATCH 083/421] ArgList has been refactored and renamed as Template class to closer reflect it's actual function. New Template class makes Fragment class redundant so it has been removed. svg plugin getting closer to completion; many changes including the ability to pass arguments when invoking svg(). svg-gradients plugin has been refactored so its functionality can be shared with the any other svg related plugins. Some (incompleted) style changes for better compatibility with PSR recommendations, will finish these updates at a later commit. --- lib/CssCrush/ArgList.php | 96 ------ lib/CssCrush/Color.php | 320 ++++++++++++-------- lib/CssCrush/Core.php | 2 + lib/CssCrush/Declaration.php | 16 +- lib/CssCrush/Fragment.php | 36 --- lib/CssCrush/Function.php | 45 +-- lib/CssCrush/Mixin.php | 92 +++--- lib/CssCrush/Process.php | 49 +-- lib/CssCrush/Regex.php | 11 +- lib/CssCrush/Stream.php | 10 +- lib/CssCrush/Template.php | 133 +++++++++ lib/CssCrush/Url.php | 42 ++- lib/CssCrush/Util.php | 5 + lib/CssCrush/Version.php | 38 +-- plugins/noise.php | 8 +- plugins/svg-gradients.php | 182 +++++++----- plugins/svg.php | 556 ++++++++++++++++++++++++++++------- 17 files changed, 1081 insertions(+), 560 deletions(-) delete mode 100644 lib/CssCrush/ArgList.php delete mode 100644 lib/CssCrush/Fragment.php create mode 100644 lib/CssCrush/Template.php diff --git a/lib/CssCrush/ArgList.php b/lib/CssCrush/ArgList.php deleted file mode 100644 index f1b5cec..0000000 --- a/lib/CssCrush/ArgList.php +++ /dev/null @@ -1,96 +0,0 @@ -argFunction, array( - 'arg' => array( $this, 'store' ) - )); - $this->string = $str; - } - - public function store ( $raw_argument ) - { - $args = CssCrush_Function::parseArgsSimple( $raw_argument ); - - // Match the argument index integer - if ( ! ctype_digit( $args[0] ) ) { - - // On failure to match an integer, return an empty string - return ''; - } - - // Get the match from the array - $position_match = $args[0]; - - // Store the default value - $default_value = isset( $args[1] ) ? $args[1] : null; - - if ( ! is_null( $default_value ) ) { - $this->defaults[ $position_match ] = trim( $default_value ); - } - - // Update the mixin argument count - $argNumber = ( (int) $position_match ) + 1; - $this->argCount = max( $this->argCount, $argNumber ); - - // Return the argument token - return "?arg$position_match?"; - } - - public function getArgValue ( $index, &$args ) - { - // First lookup a passed value - if ( isset( $args[ $index ] ) && $args[ $index ] !== 'default' ) { - return $args[ $index ]; - } - - // Get a default value - $default = isset( $this->defaults[ $index ] ) ? $this->defaults[ $index ] : ''; - - // Recurse for nested arg() calls - if ( preg_match( CssCrush_Regex::$patt->a_token, $default, $m ) ) { - - $default = $this->getArgValue( (int) $m[1], $args ); - } - return $default; - } - - public function getSubstitutions ( $args ) - { - $argIndexes = range( 0, $this->argCount-1 ); - - // Create table of substitutions - $find = array(); - $replace = array(); - - foreach ( $argIndexes as $index ) { - - $find[] = "?arg$index?"; - $replace[] = $this->getArgValue( $index, $args ); - } - - return array( $find, $replace ); - } - - public function count () - { - return $this->argCount; - } -} diff --git a/lib/CssCrush/Color.php b/lib/CssCrush/Color.php index 3fcdcf4..74110d6 100644 --- a/lib/CssCrush/Color.php +++ b/lib/CssCrush/Color.php @@ -12,88 +12,128 @@ class CssCrush_Color static public function &loadKeywords () { - if ( is_null( self::$keywords ) ) { + if (! isset(self::$keywords)) { $table = array(); $path = CssCrush::$config->location . '/misc/color-keywords.ini'; - if ( $keywords = parse_ini_file( $path ) ) { - foreach ( $keywords as $word => $rgb ) { - $rgb = array_map( 'intval', explode( ',', $rgb ) ); + if ($keywords = parse_ini_file($path)) { + foreach ($keywords as $word => $rgb) { + $rgb = array_map('intval', explode(',', $rgb)); self::$keywords[ $word ] = $rgb; } } } + return self::$keywords; } static public function &loadMinifyableKeywords () { - if ( is_null( self::$minifyableKeywords ) ) { + if (! isset(self::$minifyableKeywords)) { // If color name is longer than 4 and less than 8 test to see if its hex // representation could be shortened. $table = array(); $keywords =& CssCrush_Color::loadKeywords(); - foreach ( $keywords as $name => &$rgb ) { - $name_len = strlen( $name ); - if ( $name_len < 5 ) { + foreach ($keywords as $name => &$rgb) { + $name_len = strlen($name); + if ($name_len < 5) { continue; } - $hex = self::rgbToHex( $rgb ); + $hex = self::rgbToHex($rgb); - if ( $name_len > 7 ) { + if ($name_len > 7) { self::$minifyableKeywords[ $name ] = $hex; } else { - if ( preg_match( CssCrush_Regex::$patt->cruftyHex, $hex ) ) { + if (preg_match(CssCrush_Regex::$patt->cruftyHex, $hex)) { self::$minifyableKeywords[ $name ] = $hex; } } } } + return self::$minifyableKeywords; } - static public function parse ( $color ) + static public function parse ($str) + { + $rgba = false; + + if ($test = CssCrush_Color::test($str)) { + $color = $test['value']; + $type = $test['type']; + } + else { + + return $rgba; + } + + switch ($type) { + + case 'hex': + $rgba = CssCrush_Color::hexToRgb($color); + break; + + case 'rgb': + case 'rgba': + case 'hsl': + case 'hsla': + $function = $type; + $vals = substr($color, strlen($function) + 1); // Trim function name and start paren. + $vals = substr($vals, 0, strlen($vals) - 1); // Trim end paren. + $vals = array_map('trim', explode(',', $vals)); // Explode to array of arguments. + + // Always set the alpha channel. + $vals[3] = isset($vals[3]) ? floatval($vals[3]) : 1; + + if (strpos($function, 'rgb') === 0) { + $rgba = CssCrush_Color::normalizeCssRgb($vals); + } + else { + $rgba = CssCrush_Color::cssHslToRgb($vals); + } + break; + + case 'keyword': + $keywords =& self::loadKeywords(); + $rgba = $keywords[$color]; + + // Manually add the alpha component. + $rgba[] = 1; + break; + } + + return $rgba; + } + + static public function test ($str) { - $rgba = null; - $color = strtolower( $color ); + $color_test = array(); + $str = strtolower(trim($str)); // First match a hex value or the start of a function. - if ( preg_match( '~^( + if (preg_match('~^( \#(?=[[:xdigit:]]{3}) | \#(?=[[:xdigit:]]{6}) | rgba?(?=[?(]) | hsla?(?=[?(]) - )~xS', $color, $m ) ) { + )~xS', $str, $m)) { - // Get an RGB array from the color argument. - switch ( $m[1] ) { + $type_match = $m[1]; + switch ($type_match) { case '#': - $rgba = CssCrush_Color::hexToRgb( $color ); + $color_test['type'] = 'hex'; break; - case 'rgb': - case 'rgba': case 'hsl': case 'hsla': - $function = $m[1]; - $vals = substr( $color, strlen( $function ) + 1 ); // Trim function name and start paren. - $vals = substr( $vals, 0, strlen( $vals ) - 1 ); // Trim end paren. - $vals = array_map( 'trim', explode( ',', $vals ) ); // Explode to array of arguments. - - // Always set the alpha channel. - $vals[3] = isset( $vals[3] ) ? floatval( $vals[3] ) : 1; - - if ( strpos( $function, 'rgb' ) === 0 ) { - $rgba = CssCrush_Color::normalizeCssRgb( $vals ); - } - else { - $rgba = CssCrush_Color::cssHslToRgb( $vals ); - } + case 'rgb': + case 'rgba': + $color_test['type'] = $type_match; break; } } @@ -102,14 +142,16 @@ static public function parse ( $color ) else { $keywords =& self::loadKeywords(); - if ( isset( $keywords[ $color ] ) ) { - $rgba = $keywords[ $color ]; - // Manually add the alpha component. - $rgba[] = 1; + if (isset($keywords[$str])) { + $color_test['type'] = 'keyword'; } } - return $rgba; + if ($color_test) { + $color_test['value'] = $str; + } + + return $color_test ? $color_test : false; } /** @@ -121,39 +163,39 @@ static public function parse ( $color ) * Assumes r, g, and b are contained in the set [0, 255] and * returns h, s, and l in the set [0, 1]. */ - static public function rgbToHsl ( array $rgba ) + static public function rgbToHsl (array $rgba) { - list( $r, $g, $b, $a ) = $rgba; + list($r, $g, $b, $a) = $rgba; $r /= 255; $g /= 255; $b /= 255; - $max = max( $r, $g, $b ); - $min = min( $r, $g, $b ); + $max = max($r, $g, $b); + $min = min($r, $g, $b); $h; $s; - $l = ( $max + $min ) / 2; + $l = ($max + $min) / 2; - if ( $max == $min ) { + if ($max == $min) { $h = $s = 0; } else { $d = $max - $min; - $s = $l > 0.5 ? $d / ( 2 - $max - $min ) : $d / ( $max + $min ); - switch( $max ) { + $s = $l > 0.5 ? $d / (2 - $max - $min) : $d / ($max + $min); + switch($max) { case $r: - $h = ( $g - $b ) / $d + ( $g < $b ? 6 : 0 ); + $h = ($g - $b) / $d + ($g < $b ? 6 : 0); break; case $g: - $h = ( $b - $r ) / $d + 2; + $h = ($b - $r) / $d + 2; break; case $b: - $h = ( $r - $g ) / $d + 4; + $h = ($r - $g) / $d + 4; break; } $h /= 6; } - return array( $h, $s, $l, $a ); + return array($h, $s, $l, $a); } /** @@ -165,121 +207,161 @@ static public function rgbToHsl ( array $rgba ) * Assumes h, s, and l are contained in the set [0, 1] and * returns r, g, and b in the set [0, 255]. */ - static public function hslToRgb ( array $hsla ) + static public function hslToRgb (array $hsla) { - // Populate unspecified alpha value. - if ( ! isset( $hsla[3] ) ) { + if (! isset($hsla[3])) { $hsla[3] = 1; } - list( $h, $s, $l, $a ) = $hsla; + list($h, $s, $l, $a) = $hsla; $r; $g; $b; - if ( $s == 0 ) { + if ($s == 0) { $r = $g = $b = $l; } else { - $q = $l < 0.5 ? $l * ( 1 + $s ) : $l + $s - $l * $s; + $q = $l < 0.5 ? $l * (1 + $s) : $l + $s - $l * $s; $p = 2 * $l - $q; - $r = self::hueToRgb( $p, $q, $h + 1 / 3 ); - $g = self::hueToRgb( $p, $q, $h ); - $b = self::hueToRgb( $p, $q, $h - 1 / 3 ); + $r = self::hueToRgb($p, $q, $h + 1 / 3); + $g = self::hueToRgb($p, $q, $h); + $b = self::hueToRgb($p, $q, $h - 1 / 3); } - return array( round( $r * 255 ), round( $g * 255 ), round( $b * 255 ), $a ); + + return array(round($r * 255), round($g * 255), round($b * 255), $a); } // Convert percentages to points (0-255). - static public function normalizeCssRgb ( array $rgba ) + static public function normalizeCssRgb (array $rgba) { - foreach ( $rgba as &$val ) { - if ( strpos( $val, '%' ) !== false ) { - $val = str_replace( '%', '', $val ); - $val = round( $val * 2.55 ); + foreach ($rgba as &$val) { + if (strpos($val, '%') !== false) { + $val = str_replace('%', '', $val); + $val = round($val * 2.55); } } + return $rgba; } - static public function cssHslToRgb ( array $hsla ) + static public function cssHslToRgb (array $hsla) { // Populate unspecified alpha value. - if ( ! isset( $hsla[3] ) ) { + if (! isset($hsla[3])) { $hsla[3] = 1; } // Alpha is carried over. - $a = array_pop( $hsla ); + $a = array_pop($hsla); // Normalize the hue degree value then convert to float. - $h = array_shift( $hsla ); + $h = array_shift($hsla); $h = $h % 360; - if ( $h < 0 ) { + if ($h < 0) { $h = 360 + $hue; } $h = $h / 360; // Convert saturation and lightness to floats. - foreach ( $hsla as &$val ) { - $val = str_replace( '%', '', $val ); + foreach ($hsla as &$val) { + $val = str_replace('%', '', $val); $val /= 100; } - list( $s, $l ) = $hsla; + list($s, $l) = $hsla; - return self::hslToRgb( array( $h, $s, $l, $a ) ); + return self::hslToRgb(array($h, $s, $l, $a)); } - static public function hueToRgb ( $p, $q, $t ) + static public function hueToRgb ($p, $q, $t) { - if ( $t < 0 ) $t += 1; - if ( $t > 1 ) $t -= 1; - if ( $t < 1/6 ) return $p + ( $q - $p ) * 6 * $t; - if ( $t < 1/2 ) return $q; - if ( $t < 2/3 ) return $p + ( $q - $p ) * ( 2 / 3 - $t ) * 6; + if ($t < 0) $t += 1; + if ($t > 1) $t -= 1; + if ($t < 1/6) return $p + ($q - $p) * 6 * $t; + if ($t < 1/2) return $q; + if ($t < 2/3) return $p + ($q - $p) * (2 / 3 - $t) * 6; return $p; } - static public function rgbToHex ( array $rgba ) + static public function rgbToHex (array $rgba) { // Drop alpha component. - array_pop( $rgba ); + array_pop($rgba); $hex_out = '#'; - foreach ( $rgba as $val ) { - $hex_out .= str_pad( dechex( $val ), 2, '0', STR_PAD_LEFT ); + foreach ($rgba as $val) { + $hex_out .= str_pad(dechex($val), 2, '0', STR_PAD_LEFT); } + return $hex_out; } - static public function hexToRgb ( $hex ) + static public function hexToRgb ($hex) { - $hex = substr( $hex, 1 ); + $hex = substr($hex, 1); // Handle shortened format. - if ( strlen( $hex ) === 3 ) { + if (strlen($hex) === 3) { $long_hex = array(); - foreach ( str_split( $hex ) as $val ) { + foreach (str_split($hex) as $val) { $long_hex[] = $val . $val; } $hex = $long_hex; } else { - $hex = str_split( $hex, 2 ); + $hex = str_split($hex, 2); } // Return RGBa - $rgba = array_map( 'hexdec', $hex ); + $rgba = array_map('hexdec', $hex); $rgba[] = 1; + return $rgba; } - static public function colorAdjust ( $raw_color, array $adjustments ) + static public function colorAdjust ($str, array $adjustments) { - $hsla = new CssCrush_Color( $raw_color, true ); + $hsla = new CssCrush_Color($str, true); // On failure to parse return input. - return $hsla->isValid ? $hsla->adjust( $adjustments )->__toString() : $raw_color; + return $hsla->isValid ? $hsla->adjust($adjustments)->__toString() : $str; + } + + static public function colorSplit ($str) + { + if ($test = CssCrush_Color::test($str)) { + $color = $test['value']; + $type = $test['type']; + } + else { + + return false; + } + + // If non-alpha color return early. + if (! in_array($type, array('hsla', 'rgba'))) { + + return array($color, 1); + } + + static $alpha_color_patt; + if (! $alpha_color_patt) { + $alpha_color_patt = CssCrush_Regex::create( + '^(rgb|hsl)a\((%?,%?,%?),()\)$'); + } + + // Strip all whitespace. + $color = preg_replace('~\s+~', '', $color); + + // Extract alpha component if one is matched. + $opacity = 1; + if (preg_match($alpha_color_patt, $color, $m)) { + $opacity = floatval($m[3]); + $color = "$m[1]($m[2])"; + } + + // Return color value and alpha component seperated. + return array($color, $opacity); } @@ -290,84 +372,88 @@ static public function colorAdjust ( $raw_color, array $adjustments ) protected $hslColorSpace; public $isValid; - public function __construct ( $color, $use_hsl_color_space = false ) + public function __construct ($color, $use_hsl_color_space = false) { - $this->value = is_array( $color ) ? $color : self::parse( $color ); - $this->isValid = isset( $this->value ); - if ( $use_hsl_color_space && $this->isValid ) { + $this->value = is_array($color) ? $color : self::parse($color); + $this->isValid = $this->value; + if ($use_hsl_color_space && $this->isValid) { $this->toHsl(); } } public function __toString () { - if ( $this->value[3] !== 1 ) { - return 'rgba(' . implode( ',', $this->hslColorSpace ? $this->getRgb() : $this->value ) . ')'; + if ($this->value[3] !== 1) { + + return 'rgba(' . implode(',', $this->hslColorSpace ? $this->getRgb() : $this->value) . ')'; } else { + return $this->getHex(); } } public function toRgb () { - if ( $this->hslColorSpace ) { + if ($this->hslColorSpace) { $this->hslColorSpace = false; - $this->value = self::hslToRgb( $this->value ); + $this->value = self::hslToRgb($this->value); } + return $this; } public function toHsl () { - if ( ! $this->hslColorSpace ) { + if (! $this->hslColorSpace) { $this->hslColorSpace = true; - $this->value = self::rgbToHsl( $this->value ); + $this->value = self::rgbToHsl($this->value); } + return $this; } public function getHex () { - return self::rgbToHex( $this->getRgb() ); + return self::rgbToHex($this->getRgb()); } public function getHsl () { - return ! $this->hslColorSpace ? self::rgbToHsl( $this->value ) : $this->value; + return ! $this->hslColorSpace ? self::rgbToHsl($this->value) : $this->value; } public function getRgb () { - return $this->hslColorSpace ? self::hslToRgb( $this->value ) : $this->value; + return $this->hslColorSpace ? self::hslToRgb($this->value) : $this->value; } - public function getComponent ( $index ) + public function getComponent ($index) { - return $this->value[ $index ]; + return $this->value[$index]; } - public function setComponent ( $index, $new_component_value ) + public function setComponent ($index, $new_component_value) { - $this->value[ $index ] = $new_component_value; + $this->value[$index] = $new_component_value; } - public function adjust ( array $adjustments ) + public function adjust (array $adjustments) { $was_hsl_color_space = $this->hslColorSpace; $this->toHsl(); // Normalize percentage adjustment parameters to floating point numbers. - foreach ( $adjustments as $index => $val ) { + foreach ($adjustments as $index => $val) { // Normalize argument. - $val = $val ? trim( str_replace( '%', '', $val ) ) : 0; + $val = $val ? trim(str_replace('%', '', $val)) : 0; - if ( $val ) { + if ($val) { // Reduce value to float. $val /= 100; // Update the color component. - $this->setComponent( $index, max( 0, min( 1, $this->getComponent( $index ) + $val ) ) ); + $this->setComponent($index, max(0, min(1, $this->getComponent($index) + $val))); } } diff --git a/lib/CssCrush/Core.php b/lib/CssCrush/Core.php index c2da5cc..7defde7 100644 --- a/lib/CssCrush/Core.php +++ b/lib/CssCrush/Core.php @@ -451,12 +451,14 @@ static public function globalVars ( $vars ) if ( is_array( $vars ) ) { $config->vars = array_merge( $config->vars, $vars ); } + // Test for a file. If it is attempt to parse it elseif ( is_string( $vars ) && file_exists( $vars ) ) { if ( $result = @parse_ini_file( $vars ) ) { $config->vars = array_merge( $config->vars, $result ); } } + // Clear the stack if the argument is explicitly null elseif ( is_null( $vars ) ) { $config->vars = array(); diff --git a/lib/CssCrush/Declaration.php b/lib/CssCrush/Declaration.php index af00b63..3b35d2f 100644 --- a/lib/CssCrush/Declaration.php +++ b/lib/CssCrush/Declaration.php @@ -93,27 +93,29 @@ public function process ( $parent_rule ) // this() function needs to be called exclusively because // it's self referencing. + $extra = array( + 'rule' => $parent_rule, + ); CssCrush_Function::executeOnString( $this->value, CssCrush_Regex::$patt->thisFunction, array( 'this' => 'csscrush_fn__this', ), - array( - 'rule' => $parent_rule, - )); + $extra); // Add result to $rule->selfData. $parent_rule->selfData += array($this->property => $this->value); + $extra = array( + 'rule' => $parent_rule, + 'property' => $this->property + ); CssCrush_Function::executeOnString( $this->value, null, null, - array( - 'rule' => $parent_rule, - 'property' => $this->property, - )); + $extra); } // Trim whitespace that may have been introduced by functions. diff --git a/lib/CssCrush/Fragment.php b/lib/CssCrush/Fragment.php deleted file mode 100644 index 80fe22f..0000000 --- a/lib/CssCrush/Fragment.php +++ /dev/null @@ -1,36 +0,0 @@ -arguments = new CssCrush_ArgList( $block ); - - // Re-assign with the parsed arguments string - $this->template = $this->arguments->string; - } - - public function call ( array $args ) - { - // Copy the template - $template = $this->template; - - if ( count( $this->arguments ) ) { - - list( $find, $replace ) = $this->arguments->getSubstitutions( $args ); - $template = str_replace( $find, $replace, $template ); - } - - // Return fragment css - return $template; - } -} diff --git a/lib/CssCrush/Function.php b/lib/CssCrush/Function.php index bac327b..44175ac 100644 --- a/lib/CssCrush/Function.php +++ b/lib/CssCrush/Function.php @@ -9,7 +9,7 @@ class CssCrush_Function // Regex pattern for finding custom functions. static public $functionPatt; - static protected $functions; + static public $functions; static protected $customFunctions = array(); @@ -33,25 +33,26 @@ class CssCrush_Function static public function setMatchPatt () { self::$functions = self::$builtinFunctions + self::$customFunctions; - self::$functionPatt = CssCrush_Regex::createFunctionMatchPatt( + self::$functionPatt = CssCrush_Regex::createFunctionPatt( array_keys( self::$functions ), true ); } - static public function executeOnString ( &$str, $patt = null, $process_callback = null, $extra = null ) + static public function executeOnString ( &$str, $patt = null, $process_callback = null, &$extra = null ) { - // No bracketed expressions, early return. - if ( strpos( $str, '(' ) === false ) { + if (strpos($str, '(') === false) { + return; } // Set default pattern if not set. - if ( is_null( $patt ) ) { + if (! isset($patt)) { $patt = CssCrush_Function::$functionPatt; } // No custom functions, early return. - if ( ! preg_match( $patt, $str ) ) { + if (! preg_match($patt, $str)) { + return; } @@ -87,16 +88,15 @@ static public function executeOnString ( &$str, $patt = null, $process_callback $func_returns = ''; - if ( ! $process_callback ) { - // If no callback reference it's a built-in. - if ( array_key_exists( $fn_name, self::$functions ) ) { - $func_returns = call_user_func( self::$functions[ $fn_name ], $args, $extra ); - } + // First look for function as directly passed. + if (isset($process_callback[$fn_name])) { + + $func_returns = call_user_func_array($process_callback[$fn_name], array($args, &$extra)); } - else { - if ( isset( $process_callback[ $fn_name ] ) ) { - $func_returns = call_user_func( $process_callback[ $fn_name ], $args, $extra ); - } + // Secondly look for built-in function. + elseif (isset(self::$functions[$fn_name])) { + + $func_returns = call_user_func_array(self::$functions[$fn_name], array($args, &$extra)); } // Splice in the function result. @@ -134,7 +134,7 @@ static public function parseArgsSimple ( $input ) ############################# -# Stock custom CSS functions. +# Stock CSS functions. function csscrush_fn__math ( $input ) { @@ -222,6 +222,11 @@ function csscrush_fn__this ($input, $extra) { $args = CssCrush_Function::parseArgsSimple( $input ); $property = $args[0]; + + // Function relies on a context rule, bail if none. + if (! isset($extra['rule'])) { + return ''; + } $rule = $extra['rule']; $rule->expandDataSet('selfData', $property); @@ -244,7 +249,8 @@ function csscrush_fn__query ($input, $extra) { $args = CssCrush_Function::parseArgs($input); - if (count($args) < 1) { + // Function relies on a context property, bail if none. + if (count($args) < 1 || ! isset($extra['property'])) { return ''; } @@ -282,8 +288,9 @@ function csscrush_fn__query ($input, $extra) { } } - if ($result === '' && ! is_null($default)) { + if ($result === '' && isset($default)) { $result = $default; } + return $result; } diff --git a/lib/CssCrush/Mixin.php b/lib/CssCrush/Mixin.php index c59ab42..5aab9df 100644 --- a/lib/CssCrush/Mixin.php +++ b/lib/CssCrush/Mixin.php @@ -8,64 +8,62 @@ class CssCrush_Mixin { public $declarationsTemplate = array(); - public $arguments; + public $template; - public function __construct ( $block ) + public function __construct ($block) { - // Prepare the arguments object. - $this->arguments = new CssCrush_ArgList( $block ); + $this->template = new CssCrush_Template($block); // Parse into mixin template. - foreach ( CssCrush_Util::parseBlock( $this->arguments->string ) as $pair ) { + foreach (CssCrush_Util::parseBlock($this->template->string) as $pair) { - list( $property, $value ) = $pair; - $property = strtolower( $property ); + list($property, $value) = $pair; + $property = strtolower($property); - if ( $property === 'mixin' ) { + if ($property === 'mixin') { // Mixin can contain other mixins if they are available. - if ( $mixin_declarations = CssCrush_Mixin::parseValue( $value ) ) { + if ($mixin_declarations = CssCrush_Mixin::parseValue($value)) { // Add mixin result to the stack. $this->declarationsTemplate = array_merge( - $this->declarationsTemplate, $mixin_declarations ); + $this->declarationsTemplate, $mixin_declarations); } } - elseif ( $value !== '' ) { + elseif ($value !== '') { - // Store template declarations as arrays as they are copied by value not reference. + // Store template declarations as arrays as they are copied by + // value not reference. $this->declarationsTemplate[] = array( 'property' => $property, 'value' => $value, ); } } - - return ''; } public function call ( array $args ) { - // Copy the template + // Copy the template. $declarations = $this->declarationsTemplate; - if ( count( $this->arguments ) ) { + if (count($this->template)) { - list( $find, $replace ) = $this->arguments->getSubstitutions( $args ); + $this->template->prepare($args); - // Place the arguments - foreach ( $declarations as &$declaration ) { - $declaration['value'] = str_replace( $find, $replace, $declaration['value'] ); + // Place the arguments. + foreach ($declarations as &$declaration) { + $declaration['value'] = $this->template->apply(null, $declaration['value']); } } - // Return mixin declarations + // Return mixin declarations. return $declarations; } - static public function parseSingleValue ( $message ) + static public function parseSingleValue ($message) { - $message = ltrim( $message ); + $message = ltrim($message); $mixin = null; $non_mixin = null; @@ -75,82 +73,84 @@ static public function parseSingleValue ( $message ) // - #selector // Test for leading name - if ( preg_match( '~^[\w-]+~', $message, $name_match ) ) { + if (preg_match('~^[\w-]+~', $message, $name_match)) { $name = $name_match[0]; - if ( isset( CssCrush::$process->mixins[ $name ] ) ) { + if (isset(CssCrush::$process->mixins[$name])) { // Mixin match - $mixin = CssCrush::$process->mixins[ $name ]; + $mixin = CssCrush::$process->mixins[$name]; } - elseif ( isset( CssCrush::$process->references[ $name ] ) ) { + elseif (isset(CssCrush::$process->references[$name])) { // Abstract rule match - $non_mixin = CssCrush::$process->references[ $name ]; + $non_mixin = CssCrush::$process->references[$name]; } } // If no mixin or abstract rule matched, look for matching selector - if ( ! $mixin && ! $non_mixin ) { + if (! $mixin && ! $non_mixin) { - $selector_test = CssCrush_Selector::makeReadable( $message ); + $selector_test = CssCrush_Selector::makeReadable($message); - if ( isset( CssCrush::$process->references[ $selector_test ] ) ) { - $non_mixin = CssCrush::$process->references[ $selector_test ]; + if (isset(CssCrush::$process->references[$selector_test])) { + $non_mixin = CssCrush::$process->references[$selector_test]; } } // If no mixin matched, but matched alternative, use alternative - if ( ! $mixin ) { + if (! $mixin) { - if ( $non_mixin ) { + if ($non_mixin) { // Return expected format $result = array(); - foreach ( $non_mixin as $declaration ) { + foreach ($non_mixin as $declaration) { $result[] = array( 'property' => $declaration->property, 'value' => $declaration->value, ); } + return $result; } + + // Nothing matches else { - // Nothing matches return false; } } // We have a valid mixin. // Discard the name part and any wrapping parens and whitespace - $message = substr( $message, strlen( $name ) ); - $message = preg_replace( '~^\s*\(?\s*|\s*\)?\s*$~', '', $message ); + $message = substr($message, strlen($name)); + $message = preg_replace('~^\s*\(?\s*|\s*\)?\s*$~', '', $message); // e.g. "value, rgba(0,0,0,0), left 100%" // Determine what raw arguments there are to pass to the mixin $args = array(); - if ( $message !== '' ) { - $args = CssCrush_Util::splitDelimList( $message ); + if ($message !== '') { + $args = CssCrush_Util::splitDelimList($message); } - return $mixin->call( $args ); + return $mixin->call($args); } - static public function parseValue ( $message ) + static public function parseValue ($message) { // Call the mixin and return the list of declarations $declarations = array(); - foreach ( CssCrush_Util::splitDelimList( $message ) as $item ) { + foreach (CssCrush_Util::splitDelimList($message) as $item) { - if ( $result = self::parseSingleValue( $item ) ) { - - $declarations = array_merge( $declarations, $result ); + if ($result = self::parseSingleValue($item)) { + $declarations = array_merge($declarations, $result); } } + return $declarations; } } diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 1829389..f78d427 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -666,76 +666,81 @@ protected function extractMixins () protected function resolveFragments () { $regex = CssCrush_Regex::$patt; - $matches = $this->stream->matchAll( $regex->fragmentDef ); + $matches = $this->stream->matchAll($regex->fragmentDef); $fragments = array(); // Move through the matches last to first. - while ( $match = array_pop( $matches ) ) { + while ($match = array_pop($matches)) { $match_start_pos = $match[0][1]; $fragment_name = $match[1][0]; - $curly_match = new CssCrush_BalancedMatch( $this->stream, $match_start_pos ); + $curly_match = new CssCrush_BalancedMatch($this->stream, $match_start_pos); + + if (! $curly_match->match) { - if ( ! $curly_match->match ) { // Couldn't match the block. continue; } else { // Reconstruct the stream without the fragment. - $curly_match->replace( '' ); + $curly_match->replace(''); // Create the fragment and store it. - $fragments[ $fragment_name ] = new CssCrush_Fragment( $curly_match->inside() ); + $fragments[$fragment_name] = new CssCrush_Template($curly_match->inside()); } } // Now find all the fragment calls. - $matches = $this->stream->matchAll( $regex->fragmentCall ); + $matches = $this->stream->matchAll($regex->fragmentCall); // Move through the matches last to first. - while ( $match = array_pop( $matches ) ) { + while ($match = array_pop($matches)) { - list( $match_string, $match_start_pos ) = $match[0]; + list($match_string, $match_start_pos) = $match[0]; // The matched fragment name. $fragment_name = $match[1][0]; // The fragment object, or null if name not present. - $fragment = isset( $fragments[ $fragment_name ] ) ? $fragments[ $fragment_name ] : null; + $fragment = isset($fragments[$fragment_name]) ? $fragments[$fragment_name] : null; // Fragment may be called without any argument list. $with_arguments = $match[2][0] === '('; - if ( $with_arguments ) { - $paren_match = new CssCrush_BalancedMatch( $this->stream, $match_start_pos, '()' ); + // Resolve end of the match. + if ($with_arguments) { + $paren_match = new CssCrush_BalancedMatch($this->stream, $match_start_pos, '()'); // Get offset of statement terminating semi-colon. - $match_end = $paren_match->nextIndexOf( ';' ) + 1; + $match_end = $paren_match->nextIndexOf(';') + 1; $match_length = $match_end - $match_start_pos; } else { - $match_length = strlen( $match_string ); + $match_length = strlen($match_string); } - if ( ! $fragment || ( $with_arguments && ! $paren_match->match ) ) { + // If invalid fragment or malformed argument list. + if (! $fragment || ($with_arguments && ! $paren_match->match)) { + + $this->stream->splice('', $match_start_pos, $match_length); - // Invalid fragment or malformed argument list. - $this->stream->splice( '', $match_start_pos, $match_length ); continue; } + + // Ok. else { $args = array(); - if ( $with_arguments ) { + if ($with_arguments) { // Get the argument array to pass to the fragment. - $args = CssCrush_Util::splitDelimList( $paren_match->inside() ); + $args = CssCrush_Function::parseArgs($paren_match->inside()); } // Execute the fragment and get the return value. - $fragment_return = $fragment->call( $args ); + $fragment_return = $fragment->apply($args); // Recontruct the stream with the fragment return value. - $this->stream->splice( $fragment_return, $match_start_pos, $match_length ); + $this->stream->splice($fragment_return, $match_start_pos, $match_length); } } } @@ -1044,7 +1049,7 @@ protected function collate () if ( $url->isRelative ) { // Optionally set the URLs to absolute. if ( $make_urls_absolute ) { - $url->prepend( $this->input->dirUrl . '/' ); + $url->toRoot(); } // If output dir is different to input dir prepend a link between the two. elseif ( $link ) { diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php index 92032bb..165c4de 100644 --- a/lib/CssCrush/Regex.php +++ b/lib/CssCrush/Regex.php @@ -34,7 +34,7 @@ static public function init () $classes->p_token = '\?p\d+\?'; // Parens. $classes->u_token = '\?u\d+\?'; // URLs. $classes->t_token = '\?t\d+\?'; // Traces. - $classes->a_token = '\?arg(\d+)\?'; // Args. + $classes->a_token = '\?a(\d+)\?'; // Args. // Boundries. $classes->LB = '(?function = CssCrush_Regex::create( '()()', 'S' ); $patt->varFunction = CssCrush_Regex::create( '\$\( *() *\)', 'S' ); - $patt->argFunction = CssCrush_Regex::createFunctionMatchPatt( array( 'arg' ) ); - $patt->thisFunction = CssCrush_Regex::createFunctionMatchPatt( array( 'this' ) ); + $patt->argFunction = CssCrush_Regex::createFunctionPatt( array( 'arg' ) ); + $patt->thisFunction = CssCrush_Regex::createFunctionPatt( array( 'this' ) ); $patt->commentAndString = '~ # Quoted string (to EOF if unmatched). @@ -112,6 +112,7 @@ static public function create ( $pattern_template, $flags = '', $delim = '~' ) } $pattern = str_replace($find, $replace, $pattern_template); + return "$delim{$pattern}$delim{$flags}"; } @@ -123,10 +124,11 @@ static public function matchAll ( $patt, $subject, $preprocess_patt = false, $of } $count = preg_match_all($patt, $subject, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER, $offset); + return $count ? $matches : array(); } - static public function createFunctionMatchPatt ( $list, $include_math_function = false ) + static public function createFunctionPatt ( $list, $include_math_function = false ) { $question = ''; if ( $include_math_function ) { @@ -138,6 +140,7 @@ static public function createFunctionMatchPatt ( $list, $include_math_function = foreach ( $list as &$fn_name ) { $fn_name = preg_quote( $fn_name ); } + return CssCrush_Regex::create( '(' . implode( '|', $list ) . ')' . $question . '\(', 'iS' ); } } diff --git a/lib/CssCrush/Stream.php b/lib/CssCrush/Stream.php index d76cc9f..018fd6a 100644 --- a/lib/CssCrush/Stream.php +++ b/lib/CssCrush/Stream.php @@ -24,16 +24,19 @@ static public function endsWith ( $haystack, $needle ) public function update ( $str ) { $this->raw = $str; + return $this; } public function substr ( $start, $length = null ) { - if ( is_null( $length ) ) { - return substr( $this->raw, $start ); + if (! isset($length)) { + + return substr($this->raw, $start); } else { - return substr( $this->raw, $start, $length ); + + return substr($this->raw, $start, $length); } } @@ -45,6 +48,7 @@ public function matchAll ( $patt, $preprocess_patt = false ) public function replace ( $find, $replacement ) { $this->raw = str_replace( $find, $replacement, $this->raw ); + return $this; } diff --git a/lib/CssCrush/Template.php b/lib/CssCrush/Template.php new file mode 100644 index 0000000..1372e27 --- /dev/null +++ b/lib/CssCrush/Template.php @@ -0,0 +1,133 @@ +argFunction, array( + 'arg' => array($this, 'capture'), + '#' => array($this, 'capture'), + )); + $this->string = $str; + } + + public function capture ($str) + { + $args = CssCrush_Function::parseArgsSimple($str); + + $position = array_shift($args); + + // Match the argument index integer. + if (! isset($position) || ! ctype_digit($position)) { + + // On failure to match an integer return empty string. + return ''; + } + + // Store the default value. + $default_value = isset($args[0]) ? $args[0] : null; + + if (isset($default_value)) { + $this->defaults[$position] = $default_value; + } + + // Update the argument count. + $argNumber = ((int) $position) + 1; + $this->argCount = max($this->argCount, $argNumber); + + return "?a$position?"; + } + + public function getArgValue ($index, &$args) + { + // First lookup a passed value. + if (isset($args[$index]) && $args[$index] !== 'default') { + + return $args[$index]; + } + + // Get a default value. + $default = isset($this->defaults[$index]) ? $this->defaults[$index] : ''; + + // Recurse for nested arg() calls. + if (preg_match(CssCrush_Regex::$patt->a_token, $default, $m)) { + + $default = $this->getArgValue((int) $m[1], $args); + } + + return $default; + } + + public function prepare (array $args, $persist = true) + { + // Create table of substitutions. + $find = array(); + $replace = array(); + + if ($this->argCount) { + + $argIndexes = range(0, $this->argCount-1); + + foreach ($argIndexes as $index) { + $find[] = "?a$index?"; + $replace[] = $this->getArgValue($index, $args); + } + } + + $substitutions = array($find, $replace); + + // Persist substitutions by default. + if ($persist) { + $this->substitutions = $substitutions; + } + + return $substitutions; + } + + public function reset () + { + unset($this->substitutions); + } + + public function apply (array $args = null, $str = null) + { + $str = isset($str) ? $str : $this->string; + + // Apply passed arguments as priority. + if (isset($args)) { + + list($find, $replace) = $this->prepare($args, false); + } + + // Secondly use prepared substitutions if available. + elseif ($this->substitutions) { + + list($find, $replace) = $this->substitutions; + } + + return isset($find) ? str_replace($find, $replace, $str) : $str; + } + + public function count () + { + return $this->argCount; + } +} diff --git a/lib/CssCrush/Url.php b/lib/CssCrush/Url.php index bd24f65..0af8401 100644 --- a/lib/CssCrush/Url.php +++ b/lib/CssCrush/Url.php @@ -69,19 +69,15 @@ public function evaluate () public function toData () { - if ( $this->isRooted ) { - $file = CssCrush::$process->docRoot . $this->value; - } - else { - $file = CssCrush::$process->input->dir . "/$this->value"; - } + $file = CssCrush::$process->docRoot . $this->toRoot()->value; // File not found. - if ( ! file_exists( $file ) ) { - return; + if (! file_exists($file)) { + + return $this; } - $file_ext = pathinfo( $file, PATHINFO_EXTENSION ); + $file_ext = pathinfo($file, PATHINFO_EXTENSION); // Only allow certain extensions static $allowed_file_extensions = array( @@ -95,19 +91,17 @@ public function toData () 'png' => 'image/png', ); - if ( ! isset( $allowed_file_extensions[ $file_ext ] ) ) { - return; + if (! isset($allowed_file_extensions[$file_ext])) { + + return $this; } $mime_type = $allowed_file_extensions[ $file_ext ]; $base64 = base64_encode( file_get_contents( $file ) ); $this->value = "data:$mime_type;base64,$base64"; $this->protocol = 'data'; - } - public function prepend ( $path_fragment ) - { - $this->value = $path_fragment . $this->value; + return $this; } public function resolveRootedPath () @@ -123,8 +117,26 @@ public function resolveRootedPath () substr( $this->value, 1 ); } + public function prepend ( $path_fragment ) + { + $this->value = $path_fragment . $this->value; + return $this; + } + + public function toRoot () + { + if ($this->isRelative) { + $this->prepend(CssCrush::$process->input->dirUrl . '/'); + $this->isRooted = true; + $this->isRelative = false; + } + + return $this; + } + public function simplify () { $this->value = CssCrush_Util::simplifyPath( $this->value ); + return $this; } } diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php index 791fddb..257532c 100644 --- a/lib/CssCrush/Util.php +++ b/lib/CssCrush/Util.php @@ -145,6 +145,11 @@ static public function parseBlock ( $str, $keyed = false, $strip_comment_tokens $property = trim( substr( $declaration, 0, $colon_pos ) ); $value = trim( substr( $declaration, $colon_pos + 1 ) ); + // Empty strings are ignored. + if (! isset($property[0]) || ! isset($value[0])) { + continue; + } + if ( $keyed ) { $out[ $property ] = $value; } diff --git a/lib/CssCrush/Version.php b/lib/CssCrush/Version.php index 5e845b5..f4c5089 100644 --- a/lib/CssCrush/Version.php +++ b/lib/CssCrush/Version.php @@ -11,22 +11,22 @@ class CssCrush_Version public $revision = 0; public $extra; - public function __construct ( $version_string ) + public function __construct ($version_string) { - if ( ( $hyphen_pos = strpos( $version_string, '-' ) ) !== false ) { - $this->extra = substr( $version_string, $hyphen_pos + 1 ); - $version_string = substr( $version_string, 0, $hyphen_pos ); + if (($hyphen_pos = strpos($version_string, '-' )) !== false) { + $this->extra = substr($version_string, $hyphen_pos + 1); + $version_string = substr($version_string, 0, $hyphen_pos); } - $parts = explode( '.', $version_string ); + $parts = explode('.', $version_string); - if ( ( $major = array_shift( $parts ) ) !== null ) { + if (! is_null($major = array_shift($parts))) { $this->major = (int) $major; } - if ( ( $minor = array_shift( $parts ) ) !== null ) { + if (! is_null($minor = array_shift($parts))) { $this->minor = (int) $minor; } - if ( ( $revision = array_shift( $parts ) ) !== null ) { + if (! is_null($revision = array_shift($parts))) { $this->revision = (int) $revision; } } @@ -35,33 +35,35 @@ public function __toString () { $out = (string) $this->major; - if ( ! is_null( $this->minor ) ) { + if (isset($this->minor)) { $out .= ".$this->minor"; } - if ( ! is_null( $this->revision ) ) { + if (isset($this->revision)) { $out .= ".$this->revision"; } - if ( ! is_null( $this->extra ) ) { + if (isset($this->extra)) { $out .= "-$this->extra"; } return $out; } - public function compare ( $version_string ) + public function compare ($version_string) { - $LESS = -1; - $MORE = 1; + $LESS = -1; + $MORE = 1; $EQUAL = 0; - $test = new CssCrush_Version( $version_string ); + $test = new CssCrush_Version($version_string); - foreach ( array( 'major', 'minor', 'revision' ) as $level ) { + foreach (array('major', 'minor', 'revision') as $level) { + + if ($this->{$level} < $test->{$level}) { - if ( $this->{ $level } < $test->{ $level } ) { return $LESS; } - elseif ( $this->{ $level } > $test->{ $level } ) { + elseif ($this->{$level} > $test->{$level}) { + return $MORE; } } diff --git a/plugins/noise.php b/plugins/noise.php index 2b82ef3..a0e1dc1 100644 --- a/plugins/noise.php +++ b/plugins/noise.php @@ -118,7 +118,7 @@ function csscrush__noise_generator ( $input, $defaults ) { $dimensions = $defaults[ 'dimensions' ]; if ( ( $arg = array_shift( $args ) ) !== 'default' ) { // May be a color function so explode(' ', $value) is not sufficient. - foreach( CssCrush_Function::parseArgs( $arg, true ) as $part ) { + foreach ( CssCrush_Function::parseArgs( $arg, true ) as $part ) { if ( preg_match( '~^(\d+)x(\d+)$~i', $part, $m ) ) { $dimensions = array_slice( $m, 1 ); } @@ -135,7 +135,7 @@ function csscrush__noise_generator ( $input, $defaults ) { $sharpen = $defaults[ 'sharpen' ]; if ( ( $arg = array_shift( $args ) ) !== 'default' ) { - foreach( explode( ' ', $arg ) as $index => $value ) { + foreach ( explode( ' ', $arg ) as $index => $value ) { switch ( $index ) { case 0: // x and y frequency values can be specified by joining with a colon. @@ -158,7 +158,7 @@ function csscrush__noise_generator ( $input, $defaults ) { $blend_mode = 'normal'; $opacity = 1; if ( ( $arg = array_shift( $args ) ) !== 'default' ) { - foreach( explode( ' ', $arg ) as $part ) { + foreach ( explode( ' ', $arg ) as $part ) { if ( ctype_alpha( $part ) ) { if ( in_array( $part, $blend_modes ) ) { $blend_mode = $part; @@ -176,7 +176,7 @@ function csscrush__noise_generator ( $input, $defaults ) { if ( ( $arg = array_shift( $args ) ) !== 'default' ) { // Saturate by default. $color_filter = array( 'saturate', 1 ); - foreach( explode( ' ', $arg ) as $part ) { + foreach ( explode( ' ', $arg ) as $part ) { if ( ctype_alpha( $part ) ) { if ( in_array( $part, $color_filters ) ) { $color_filter[0] = $part; diff --git a/plugins/svg-gradients.php b/plugins/svg-gradients.php index eec04db..7c60e49 100644 --- a/plugins/svg-gradients.php +++ b/plugins/svg-gradients.php @@ -47,23 +47,61 @@ )); function csscrush__enable_svg_gradients () { - CssCrush_Function::register( 'svg-linear-gradient', 'csscrush_fn__svg_linear_gradient' ); - CssCrush_Function::register( 'svg-radial-gradient', 'csscrush_fn__svg_radial_gradient' ); + CssCrush_Function::register('svg-linear-gradient', 'csscrush_fn__svg_linear_gradient'); + CssCrush_Function::register('svg-radial-gradient', 'csscrush_fn__svg_radial_gradient'); } function csscrush__disable_svg_gradients () { - CssCrush_Function::deRegister( 'svg-linear-gradient' ); - CssCrush_Function::deRegister( 'svg-radial-gradient' ); + CssCrush_Function::deRegister('svg-linear-gradient'); + CssCrush_Function::deRegister('svg-radial-gradient'); } -function csscrush_fn__svg_linear_gradient ( $input ) { +function csscrush_fn__svg_linear_gradient ($input) { - // For inline SVG debugging. - // static $uid = 0; $uid++; - static $uid = ''; + $gradient = csscrush__create_svg_linear_gradient($input); + $gradient_markup = reset($gradient); + $gradient_id = key($gradient); - static $angle_keywords; - if ( ! $angle_keywords ) { + // Creating the svg. + $svg = ''; + $svg .= ''; + $svg .= $gradient_markup; + $svg .= ''; + $svg .= ""; + $svg .= ''; + + // Create data-uri url and return token label. + $url = new CssCrush_Url('data:image/svg+xml;base64,' . base64_encode($svg)); + + return $url->label; +} + + +function csscrush_fn__svg_radial_gradient ($input) { + + $gradient = csscrush__create_svg_radial_gradient($input); + $gradient_markup = reset($gradient); + $gradient_id = key($gradient); + + // Creating the svg. + $svg = ''; + $svg .= ''; + $svg .= $gradient_markup; + $svg .= ''; + $svg .= ""; + $svg .= ''; + + // Create data-uri url and return token label. + $url = new CssCrush_Url('data:image/svg+xml;base64,' . base64_encode($svg)); + + return $url->label; +} + + +function csscrush__create_svg_linear_gradient ($input) { + + static $angle_keywords, $deg_patt; + if (! $angle_keywords) { $angle_keywords = array( 'to top' => 180, 'to right' => 270, @@ -79,6 +117,8 @@ function csscrush_fn__svg_linear_gradient ( $input ) { $angle_keywords[ 'to left top' ] = $angle_keywords[ 'to top left' ]; $angle_keywords[ 'to right bottom' ] = $angle_keywords[ 'to bottom right' ]; $angle_keywords[ 'to left bottom' ] = $angle_keywords[ 'to bottom left' ]; + + $deg_patt = CssCrush_Regex::create('^deg$', 'i'); } $args = CssCrush_Function::parseArgs( $input ); @@ -92,7 +132,7 @@ function csscrush_fn__svg_linear_gradient ( $input ) { $first_arg_is_angle = false; // Try to parse an angle value. - if ( preg_match( '~-?[\d\.]+deg~i', $first_arg ) ) { + if ( preg_match( $deg_patt, $first_arg ) ) { $angle = floatval( $first_arg ); $first_arg_is_angle = true; } @@ -106,19 +146,25 @@ function csscrush_fn__svg_linear_gradient ( $input ) { $first_arg_is_angle = true; } + $angle += 180; + // Shift off the first argument if it has been recognised as an angle. if ( $first_arg_is_angle ) { array_shift( $args ); } // If not using a magic corner, create start/end coordinates from the angle. - if ( ! $coords ) { + if (! $coords) { + + // Quick fix to match standard linear-gradient() angle. + // $angle += 180; + // Normalize the angle. - $angle = fmod( $angle, 360 ); - if ( $angle < 0 ) { + $angle = fmod($angle, 360); + if ($angle < 0) { $angle = 360 + $angle; } - $angle = round( $angle, 2 ); + $angle = round($angle, 2); $start_x = 0; $end_x = 0; @@ -171,34 +217,23 @@ function csscrush_fn__svg_linear_gradient ( $input ) { // for color stop offsets. $color_stops = csscrush__parse_gradient_color_stops( $args ); - // Creating the svg. - $svg = ''; - $svg .= ''; - $svg .= ""; - - foreach ( $color_stops as $offset => $color ) { - $svg .= ""; - } - $svg .= ''; - $svg .= ''; - $svg .= ""; - $svg .= ''; + // Create the gradient markup with a unique id. + static $uid = 0; + $uid++; + $gradient_id = "lg$uid"; + $gradient = ""; + $gradient .= $color_stops; + $gradient .= ''; - // Create data-uri url and return token label. - $url = new CssCrush_Url( 'data:image/svg+xml;base64,' . base64_encode( $svg ) ); - - return $url->label; + return array($gradient_id => $gradient); } -function csscrush_fn__svg_radial_gradient ( $input ) { - // For inline SVG debugging. - // static $uid = 0; $uid++; - static $uid = ''; +function csscrush__create_svg_radial_gradient ($input) { - static $position_keywords; - if ( ! $position_keywords ) { + static $position_keywords, $origin_patt; + if (! $position_keywords) { $position_keywords = array( 'at top' => array('50%', '0%'), 'at right' => array('100%', '50%'), @@ -215,6 +250,8 @@ function csscrush_fn__svg_radial_gradient ( $input ) { $position_keywords[ 'at left top' ] = $position_keywords[ 'at top left' ]; $position_keywords[ 'at right bottom' ] = $position_keywords[ 'at bottom right' ]; $position_keywords[ 'at left bottom' ] = $position_keywords[ 'at bottom left' ]; + + $origin_patt = CssCrush_Regex::create('^(%?) +(%?)$'); } $args = CssCrush_Function::parseArgs( $input ); @@ -227,7 +264,7 @@ function csscrush_fn__svg_radial_gradient ( $input ) { $first_arg_is_position = false; // Try to parse an origin value. - if ( preg_match( '~^(-?[\d\.]+%?) +(-?[\d\.]+%?)$~', $first_arg, $m ) ) { + if ( preg_match( $origin_patt, $first_arg, $m ) ) { $position = array( $m[1], $m[2] ); $first_arg_is_position = true; } @@ -247,65 +284,63 @@ function csscrush_fn__svg_radial_gradient ( $input ) { // for color stop offsets. $color_stops = csscrush__parse_gradient_color_stops( $args ); - // Creating the svg. - $svg = ''; - $svg .= ''; - $svg .= ""; - - foreach ( $color_stops as $offset => $color ) { - $svg .= ""; - } - $svg .= ''; - $svg .= ''; - $svg .= ""; - $svg .= ''; + // Create the gradient markup with a unique id. + static $uid = 0; + $uid++; + $gradient_id = "rg$uid"; + $gradient = ""; + $gradient .= $color_stops; + $gradient .= ''; - // Create data-uri url and return token label. - $url = new CssCrush_Url( 'data:image/svg+xml;base64,' . base64_encode( $svg ) ); - - return $url->label; + return array($gradient_id => $gradient); } -function csscrush__parse_gradient_color_stops ( array $color_stop_args ) { + +function csscrush__parse_gradient_color_stops (array $color_stop_args) { $offsets = array(); $colors = array(); $offset_patt = '~ +([\d\.]+%)$~'; - $last_index = count( $color_stop_args ) - 1; + $last_index = count($color_stop_args) - 1; - foreach ( $color_stop_args as $index => $color_arg ) { + foreach ($color_stop_args as $index => $color_arg) { - if ( preg_match( $offset_patt, $color_arg, $m ) ) { - $offsets[] = floatval( $m[1] ); - $colors[] = preg_replace( $offset_patt, '', $color_arg ); + if (preg_match($offset_patt, $color_arg, $m)) { + $offsets[] = floatval($m[1]); + $color = preg_replace($offset_patt, '', $color_arg); } else { - if ( $index === 0 ) { + if ($index === 0) { $offsets[] = 0; } - elseif ( $index === $last_index ) { + elseif ($index === $last_index) { $offsets[] = 100; } else { $offsets[] = null; } - $colors[] = $color_arg; + $color = $color_arg; } + + // For hsla()/rgba() extract alpha component from color values and + // convert to hsl()/rgb(). + // Webkit doesn't support them for SVG colors. + $colors[] = CssCrush_Color::colorSplit($color); } // For unspecified color offsets fill in the blanks. $next_index_not_null = 0; $prev_index_not_null = 0; - $n = count( $offsets ); + $n = count($offsets); foreach ( $offsets as $index => $offset ) { - if ( is_null( $offset ) ) { + if (! isset($offset)) { // Scan for next non-null offset. for ( $i = $index; $i < $n; $i++ ) { - if ( ! is_null( $offsets[$i] ) ) { + if (isset($offsets[$i])) { $next_index_not_null = $i; break; } @@ -319,7 +354,7 @@ function csscrush__parse_gradient_color_stops ( array $color_stop_args ) { $padding = $padding_increment; for ( $i = $index; $i < $n; $i++ ) { - if ( ! is_null( $offsets[$i] ) ) { + if (isset($offsets[$i])) { break; } // Replace the null offset with the new padded value. @@ -333,5 +368,12 @@ function csscrush__parse_gradient_color_stops ( array $color_stop_args ) { } } - return array_combine( $offsets, $colors ); + $stops = ''; + foreach ( array_combine( $offsets, $colors ) as $offset => $color ) { + list($color_value, $opacity) = $color; + $stop_opacity = $opacity < 1 ? " stop-opacity=\"$opacity\"" : ''; + $stops .= ""; + } + + return $stops; } diff --git a/plugins/svg.php b/plugins/svg.php index 22532a0..82b419a 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -9,17 +9,18 @@ * @-rule for defining SVG shapes. Uses custom shortcut properites alongside, * standard SVG properties. * - * + * * Element types * ------------- - * circle, ellipse, rect, polygon, path, line, polyline, star. + * circle, ellipse, rect, polygon, path, line, polyline, star, text. * * * @example * * // Define SVG. * @svg foo { - * type: ellipse; + * type: star; + * star-points: 5; * radius: 100 50; * margin: 20; * stroke: black; @@ -27,10 +28,18 @@ * fill-opacity: .5; * } * - * // Embed SVG with svg() function (creates a data URI). + * // Embed SVG with svg() function (generates a data URI). * body { * background: beige svg(foo); * } + * + * + * Issues + * ------ + * Firefox does not allow linked images (or other svg) when SVG is in "svg as image" mode - + * i.e. Used in an img tag or as a CSS background: + * https://bugzilla.mozilla.org/show_bug.cgi?id=628747#c0 + * */ CssCrush_Plugin::register('svg', array( @@ -58,7 +67,7 @@ function csscrush__svg ($process) { $block = $m[2]; if (! empty($name) && ! empty($block)) { CssCrush::$process->misc->svg_defs[$name] = - array_change_key_case(CssCrush_Util::parseBlock($block, true)); + new CssCrush_Template($block); } '); } @@ -69,33 +78,19 @@ function csscrush__svg ($process) { function csscrush_fn__svg ($input) { - $svg_deinitions =& CssCrush::$process->misc->svg_defs; + // Map types to elements. static $types = array( - 'circle' => array('element' => 'circle'), - 'ellipse' => array('element' => 'ellipse'), - 'rect' => array('element' => 'rect'), - 'polygon' => array('element' => 'polygon'), - 'path' => array('element' => 'path'), - 'line' => array('element' => 'line'), - 'polyline' => array('element' => 'polyline'), - 'star' => array('element' => 'path'), + 'circle' => 'circle', + 'ellipse' => 'ellipse', + 'rect' => 'rect', + 'polygon' => 'polygon', + 'path' => 'path', + 'line' => 'line', + 'polyline' => 'polyline', + 'star' => 'path', + 'text' => 'text', ); - $name = strtolower($input); - - // Bail if no SVG registered by this name. - if (! isset($svg_deinitions[$name])) { - return ''; - } - - $data = $svg_deinitions[$name]; - - // Bail if type not recognised. - $type = isset($data['type']) ? strtolower($data['type']) : 'rect'; - if (! isset($types[$type])) { - return ''; - } - // Keys that represent custom svg properties. static $element_props = array( 'type' => true, @@ -107,107 +102,170 @@ function csscrush_fn__svg ($input) { 'points' => true, 'margin' => true, 'drop-shadow' => true, - 'drop-shadow-opacity' => true, 'sides' => true, 'width' => true, 'height' => true, + 'text' => true, ); // Keys that go to direct to element attributes. static $attribute_props = array( 'transform' => true, + 'x' => true, + 'y' => true, ); + $args = CssCrush_Function::parseArgs($input); + + if (! isset($args[0])) { + return ''; + } + + $name = strtolower(array_shift($args)); + + // Bail if no SVG registered by this name. + $svg_definitions =& CssCrush::$process->misc->svg_defs; + if (! isset($svg_definitions[$name])) { + return ''; + } + + // Apply args to template. + $block = $svg_definitions[$name]->apply($args); + + // Parse the block into a keyed assoc array. + $data = array_change_key_case(CssCrush_Util::parseBlock($block, true)); + + // Bail if type not recognised. + $type = isset($data['type']) ? strtolower($data['type']) : 'rect'; + if (! isset($types[$type])) { + return ''; + } + + // Setup functions for using on values. + // Note using custom versions of svg-*-gradient(). + static $generic_functions_patt, $fill_functions, $fill_functions_patt; + if (! $generic_functions_patt) { + $fill_functions = array( + 'svg-linear-gradient' => 'csscrush__svg_fn_linear_gradient', + 'svg-radial-gradient' => 'csscrush__svg_fn_radial_gradient', + 'pattern' => 'csscrush__svg_fn_pattern', + ); + $generic_functions = array_diff_key(CssCrush_Function::$functions, $fill_functions); + $generic_functions_patt = CssCrush_Regex::createFunctionPatt(array_keys($generic_functions), true); + $fill_functions_patt = CssCrush_Regex::createFunctionPatt(array_keys($fill_functions)); + } + + // Placeholder for capturing generated fills. + $fills = array( + 'gradients' => array(), + 'patterns' => array(), + ); + foreach ($data as $property => &$value) { + CssCrush_Function::executeOnString($value, $generic_functions_patt); + + // Only capturing fills for fill and stoke properties. + if ($property === 'fill' || $property === 'stroke') { + CssCrush_Function::executeOnString($value, $fill_functions_patt, $fill_functions, $fills); + + // If the value is a color with alpha component we split the color + // and set the corresponding *-opacity property because Webkit doesn't + // support rgba()/hsla() in SVG. + if ($components = CssCrush_Color::colorSplit($value)) { + list($color, $opacity) = $components; + $data[$property] = $color; + if ($opacity < 1) { + $data += array("$property-opacity" => $opacity); + } + } + } + } + // Delete loop reference after use. + unset($value); + // Initialize SVG attributes. $svg_attrs = array('xmlns' => 'http://www.w3.org/2000/svg'); $svg_attrs['width'] = 0; $svg_attrs['height'] = 0; + if ($fills['patterns']) { + $svg_attrs['xmlns:xlink'] ="http://www.w3.org/1999/xlink"; + } + + // Filter off prefixed properties that are for the svg element or @font-face. + $svg_styles = array(); + $face_styles = array(); + foreach ($data as $property => $value) { + if (strpos($property, 'svg-') === 0) { + $svg_styles[substr($property, 4)] = $value; + unset($data[$property]); + } + elseif (strpos($property, 'face-') === 0) { + $face_styles[substr($property, 5)] = $value; + unset($data[$property]); + } + } // Initialize element attributes. - $element_attrs = array('id' => 'e') + array_intersect_key($data, $attribute_props); + $element_name = $types[$type]; + $element_attrs = array_intersect_key($data, $attribute_props); $element_data = array_intersect_key($data, $element_props); // Everything remaining is treated as CSS. - $style_props = array_diff_key($data, $element_props, $attribute_props); + $styles = array_diff_key($data, $element_props, $attribute_props); // Prepopulate common attributes. - if (isset($element_data['margin'])) { - $parts = csscrush__svg_parselist($element_data['margin']); - $count = count($parts); - if ($count === 1) { - $element_data['margin'] = array($parts[0], $parts[0], $parts[0], $parts[0]); - } - elseif ($count === 2) { - $element_data['margin'] = array($parts[0], $parts[1], $parts[0], $parts[1]); - } - elseif ($count === 3) { - $element_data['margin'] = array($parts[0], $parts[1], $parts[2], $parts[1]); - } - else { - $element_data['margin'] = $parts; - } - } - else { - $element_data['margin'] = array(0,0,0,0); - } + csscrush__svg_decorate($element_data); - // Drop-shadow filter. - $filters = ''; - if (isset($element_data['drop-shadow'])) { - $parts = csscrush__svg_parselist($element_data['drop-shadow'], false); - list($ds_x, $ds_y, $ds_strength, $ds_color) = $parts += array( - 2, // x offset. - 2, // y offset. - 2, // strength. - 'black', // color. - ); - $filters .= ''; - $filters .= ""; - $filters .= ""; - $filters .= ""; - $filters .= ""; - if (isset($element_data['drop-shadow-opacity'])) { - $ds_opacity = $element_data['drop-shadow-opacity']; - $filters .= ''; - $filters .= ""; - $filters .= ''; - } - $filters .= ''; - $filters .= ''; - $filters .= ''; - $filters .= ''; - $filters .= ''; - $style_props['filter'] = 'url(#f)'; - } + // Filters. + $filter = csscrush__svg_filter($element_data, $styles); // Apply SVG callback. call_user_func_array("csscrush__svg_$type", - array(&$element_data, &$element_attrs, &$svg_attrs, &$style_props)); + array(&$element_data, &$element_attrs, &$svg_attrs, &$styles, &$element_name)); - // Create SVG markup. - $svg_attrs = CssCrush_Util::htmlAttributes($svg_attrs); - $styles = array(); - foreach ($style_props as $property => $value) { - $styles[] = "$property:$value"; + // Flatten CSS styles. + $styles_data = array( + 'svg' => $svg_styles, + $element_name => $styles + ); + $styles_out = ''; + foreach ($styles_data as $selector => $declarations) { + $pairs = array(); + foreach ($declarations as $property => $value) { + $pairs[] = "$property:$value"; + } + if ($pairs) { + $styles_out .= $selector . '{' . implode(';', $pairs) . '}'; + } } - $styles = implode(';', $styles); - $element = $types[$type]['element']; + $styles_out = CssCrush::$process->restoreTokens($styles_out, 'u'); + $element_attrs = CssCrush_Util::htmlAttributes($element_attrs); + $svg_attrs = CssCrush_Util::htmlAttributes($svg_attrs); + // Create SVG markup. $svg[] = ""; - $svg[] = ''; - $svg[] = $filters; - $svg[] = ''; - $svg[] = ''; - $svg[] = "<$element$element_attrs/>"; + // $svg[] = ''; + $svg[] = implode($fills['gradients']); + $svg[] = implode($fills['patterns']); + $svg[] = $filter; + if ($styles_out) { + $svg[] = ''; + } + // $svg[] = ''; + if ($element_name === 'text') { + $svg[] = "{$element_data['text']}"; + } + else { + $svg[] = "<$element_name$element_attrs/>"; + } $svg[] = ''; // Debugging... - // $code = implode("\n", $svg); - // $test = '
' . htmlspecialchars($code) . '
'; - // echo $test, $code; + $code = implode("\n", $svg); + $test = '
' . htmlspecialchars($code) . '
'; + echo $test, $code; // Create data-uri url and return token label. $url = new CssCrush_Url('data:image/svg+xml;base64,' . base64_encode(implode('', $svg))); @@ -370,22 +428,314 @@ function csscrush__svg_line (&$element_data, &$element_attrs, &$svg_attrs, &$sty /* Polygon callback. */ -function csscrush__svg_polygon (&$element_data, &$element_attrs, &$svg_attrs, &$style_props) { +function csscrush__svg_polygon (&$element_data, &$element_attrs, &$svg_attrs, &$style_props, &$element_name) { - // Ensure required attributes have defaults set. + // Check for points. + if (isset($element_data['points'])) { + + $element_data += array( + 'width' => 70, + 'height' => 70, + ); + + $element_attrs['points'] = $element_data['points']; + + $svg_attrs['width'] = $element_data['width']; + $svg_attrs['height'] = $element_data['height']; + } + + // Fallback with sides. + else { + + // Switch to path element. + $element_name = 'path'; + + $element_data += array( + 'sides' => 3, + 'radius' => 100, + ); + + list($margin_top, $margin_right, $margin_bottom, $margin_left) = $element_data['margin']; + + $radius = csscrush__svg_parselist($element_data['radius']); + $radius = $radius[0]; + + $cx = $radius + $margin_left; + $cy = $radius + $margin_top; + $sides = $element_data['sides']; + + $element_attrs['d'] = csscrush__svg_starpath($cx, $cy, $sides, $radius); + + $svg_attrs['width'] = ($radius * 2) + $margin_left + $margin_right; + $svg_attrs['height'] = ($radius * 2) + $margin_top + $margin_bottom; + } +} + +/* + Star callback. +*/ +function csscrush__svg_star (&$element_data, &$element_attrs, &$svg_attrs, &$style_props) { + + // Minimum required attributes have defaults. $element_data += array( - 'points' => '60,10 10,60 60,60', - 'width' => 70, - 'height' => 70, + 'star-points' => 4, + 'radius' => '50 30', + 'twist' => 0, ); - $element_attrs['points'] = $element_data['points']; + list($margin_top, $margin_right, $margin_bottom, $margin_left) = $element_data['margin']; + + $radius = csscrush__svg_parselist($element_data['radius']); + if (! isset($radius[1])) { + $radius[1] = ($radius[0] / 2); + } + list($outer_r, $inner_r) = $radius; + + $cx = $outer_r + $margin_left; + $cy = $outer_r + $margin_top; + $points = $element_data['star-points']; + $twist = $element_data['twist']; + + $element_attrs['d'] = csscrush__svg_starpath($cx, $cy, $points, $outer_r, $inner_r, $twist); + + $svg_attrs['width'] = $margin_left + ($outer_r * 2) + $margin_left; + $svg_attrs['height'] = $margin_top + ($outer_r * 2) + $margin_bottom; +} + +/* + Text callback. +*/ +function csscrush__svg_text (&$element_data, &$element_attrs, &$svg_attrs, &$style_props) { + + // Minimum required attributes have defaults. + $element_data += array( + 'x' => 0, + 'y' => 0, + 'width' => 100, + 'height' => 100, + 'text' => '', + ); + + $text = CssCrush::$process->restoreTokens($element_data['text'], 's'); + + // Remove open and close quotes. + $text = substr($text, 1, strlen($text) - 2); + + // Convert CSS unicode sequences to XML unicode. + $text = preg_replace('~\\\\([[:xdigit:]]{2,6})~', '&#x$1;', $text); + + // Remove excape slashes and encode meta entities. + $text = htmlentities(stripslashes($text), ENT_QUOTES, 'UTF-8', false); + $element_data['text'] = $text; $svg_attrs['width'] = $element_data['width']; $svg_attrs['height'] = $element_data['height']; } + +/* + Star/polygon path builder. + + Adapted from http://svg-whiz.com/svg/StarMaker.svg by Doug Schepers. +*/ +function csscrush__svg_starpath ($cx, $cy, $points, $outer_r, $inner_r = null, $twist = 0, $orient = 'point') { + + $data[] = 'M'; + + // Enforce minimum number of points. + $points = max(3, $points); + + for ($s = 0; $points >= $s; $s++) { + + // Outer angle. + $outer_angle = 2.0 * M_PI * ($s / $points); + + if ($orient === 'point') { + $outer_angle -= (M_PI / 2); + } + elseif ($orient === 'edge') { + $outer_angle = ($outer_angle + (M_PI / $points)) - (M_PI / 2); + } + + // Outer point based on outer angle. + $x = ( $outer_r * cos($outer_angle) ) + $cx; + $y = ( $outer_r * sin($outer_angle) ) + $cy; + + if ($points != $s) { + $data[] = array($x, $y); + } + + // If star shape is required need inner angles too. + if ($inner_r != null && $points != $s) { + + $inner_angle = (2 * M_PI * ($s / $points)) + (M_PI / $points); + + if ($orient === 'point') { + $inner_angle -= (M_PI / 2); + } + $inner_angle += $twist; + + $ix = ( $inner_r * cos($inner_angle) ) + $cx; + $iy = ( $inner_r * sin($inner_angle) ) + $cy; + + $data[] = array($ix, $iy); + } + elseif ($points == $s) { + + $data[] = 'Z'; + } + } + + // Round path coordinates down to save bytes. + foreach ($data as &$item) { + if (is_array($item)) { + $item = round($item[0], 2) . ',' . round($item[1], 2); + } + } + + return implode(' ', $data); +} + + +function csscrush__svg_filter (&$element_data, &$style_props) { + + $filter = ''; + + if (isset($element_data['drop-shadow'])) { + + $parts = csscrush__svg_parselist($element_data['drop-shadow'], false); + + list($ds_x, $ds_y, $ds_strength, $ds_color) = $parts += array( + 2, // x offset. + 2, // y offset. + 2, // strength. + 'black', // color. + ); + + // Opacity. + $drop_shadow_opacity = null; + if ($color_components = CssCrush_Color::colorSplit($ds_color)) { + list($ds_color, $drop_shadow_opacity) = $color_components; + } + + $filter = ''; + $filter .= ""; + $filter .= ""; + $filter .= ""; + $filter .= ""; + if (isset($drop_shadow_opacity)) { + $filter .= ''; + $filter .= ""; + $filter .= ''; + } + $filter .= ''; + $filter .= ''; + $filter .= ''; + $filter .= ''; + $filter .= ''; + $style_props['filter'] = 'url(#f)'; + } + + return $filter; +} + + +function csscrush__svg_decorate (&$element_data) { + + if (isset($element_data['margin'])) { + + $parts = csscrush__svg_parselist($element_data['margin']); + $count = count($parts); + if ($count === 1) { + $element_data['margin'] = array($parts[0], $parts[0], $parts[0], $parts[0]); + } + elseif ($count === 2) { + $element_data['margin'] = array($parts[0], $parts[1], $parts[0], $parts[1]); + } + elseif ($count === 3) { + $element_data['margin'] = array($parts[0], $parts[1], $parts[2], $parts[1]); + } + else { + $element_data['margin'] = $parts; + } + } + else { + $element_data['margin'] = array(0,0,0,0); + } +} + + +/* + Custom versions of svg-*-gradient() for integrating. +*/ +function csscrush__svg_fn_linear_gradient ($input, &$fills) { + + static $booted; + if (! $booted) { + // Relies on functions from svg-gradients plugin. + CssCrush_Plugin::load('svg-gradients'); + } + $generated_gradient = csscrush__create_svg_linear_gradient($input); + $fills['gradients'][] = reset($generated_gradient); + + return 'url(#' . key($generated_gradient) . ')'; +} + + +function csscrush__svg_fn_radial_gradient ($input, &$fills) { + + static $booted; + if (! $booted) { + // Relies on functions from svg-gradients plugin. + CssCrush_Plugin::load('svg-gradients'); + } + $generated_gradient = csscrush__create_svg_radial_gradient($input); + $fills['gradients'][] = reset($generated_gradient); + + return 'url(#' . key($generated_gradient) . ')'; +} + + +function csscrush__svg_fn_pattern ($input, &$fills) { + + static $uid = 0; + $pid = 'p' . (++$uid); + + // Get args in order with defaults. + list($url, $transform_list, $width, $height, $x, $y) = + CssCrush_Function::parseArgs($input) + + array('', '', 0, 0, 0, 0); + + $url = CssCrush::$process->popToken($url); + + // If $width or $height is not specified get image dimensions the slow way. + if (! $width || ! $height) { + if (in_array($url->protocol, array('http', 'https'))) { + $file = $url->value; + } + elseif ($url->isRelative || $url->isRooted) { + $file = CssCrush::$config->docRoot . + ($url->isRelative ? $url->toRoot()->simplify()->value : $url->value); + } + list($width, $height) = getimagesize($file); + } + + // If a data-uri function has been used. + if ($url->convertToData) { + $url->toData(); + } + + $transform_list = $transform_list ? " patternTransform=\"$transform_list\"" : ''; + $generated_pattern = ""; + $generated_pattern .= "value}\" x=\"$x\" y=\"$y\" width=\"$width\" height=\"$height\"/>"; + $generated_pattern .= ''; + + $fills['patterns'][] = $generated_pattern; + return 'url(#' . $pid . ')'; +} + + /* Helpers. */ From ad963ed610853aa9040dae6ab42260934fbb7846 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 21 Mar 2013 18:48:42 +0000 Subject: [PATCH 084/421] Added file watching option to cli. General tidy and refactoring around cli. Fixed selector-aliases bug with multiple string tokens. Expanded url class. Lots of work on the SVG plugin. --- cli.php | 528 ++++++++++++++++++---------- lib/CssCrush/Core.php | 54 ++- lib/CssCrush/Function.php | 8 +- lib/CssCrush/IO.php | 6 +- lib/CssCrush/IOWatch.php | 43 +++ lib/CssCrush/Importer.php | 6 +- lib/CssCrush/Process.php | 105 +++--- lib/CssCrush/Url.php | 149 +++++--- plugins/svg.php | 707 ++++++++++++++++++++++---------------- 9 files changed, 999 insertions(+), 607 deletions(-) create mode 100644 lib/CssCrush/IOWatch.php diff --git a/cli.php b/cli.php index abb4061..d08c19a 100755 --- a/cli.php +++ b/cli.php @@ -7,185 +7,170 @@ */ require_once 'CssCrush.php'; -// Exit status constants. -define( 'STATUS_OK', 0 ); -define( 'STATUS_ERROR', 1 ); - - ################################################################## -## Helpers. - -function stderr ( $lines, $closing_newline = true ) { - $out = implode( PHP_EOL, (array) $lines ) . ( $closing_newline ? PHP_EOL : '' ); - fwrite( STDERR, $out ); -} - -function stdout ( $lines, $closing_newline = true ) { - $out = implode( PHP_EOL, (array) $lines ) . ( $closing_newline ? PHP_EOL : '' ); - // On OSX terminal is sometimes truncating 'visual' output to terminal - // with fwrite to STDOUT. - echo $out; -} +## Exit statuses. -function get_stdin_contents () { - $stdin = fopen( 'php://stdin', 'r' ); - stream_set_blocking( $stdin, false ); - $stdin_contents = stream_get_contents( $stdin ); - fclose( $stdin ); - return $stdin_contents; -} +define('STATUS_OK', 0); +define('STATUS_ERROR', 1); ################################################################## -## Version detection. +## PHP requirements check. $version = PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION; $required_version = 5.3; -if ( $version < $required_version ) { +if ($version < $required_version) { - stderr( array( + stderr(array( "PHP version $required_version or higher is required to use this tool.", - "You are currently running PHP version $version" ) + "You are currently running PHP version $version") ); - exit( STATUS_ERROR ); + + exit(STATUS_ERROR); } ################################################################## -## Options +## Resolve options. $short_opts = array( - "f:", // Input file. Defaults to sdtin. - "i:", // Input file alias. - "o:", // Output file. Defaults to stdout. - "p", // Pretty formatting. - 'b', // Output boilerplate. - 'h', // Display help. -); -$long_opts = array( - 'file:', // Input file. Defaults to sdtin. - 'output:', // Output file. Defaults to stdout. - 'pretty', // Pretty formatting. - 'boilerplate', // Output boilerplate. - 'help', // Display help. - 'version', // Display version. - 'trace', // Output sass tracing stubs. - 'formatter:', // Formatter name for formatted output. - 'vendor-target:', // Vendor target. - 'variables:', // Map of variable names in an http query string format. - 'enable:', // List of plugins to enable. - 'disable:', // List of plugins to disable. - 'context:', // Context for resolving URLs. - 'newlines:', // Newline style. -); + // Required value arguments. + 'f:', // Input file. Defaults to SDTIN. + 'i:', // Input file alias. + 'o:', // Output file. Defaults to STDOUT. -$opts = getopt( implode( $short_opts ), $long_opts ); + // Optional value arguments. + 'b::', // Output boilerplate (optional filepath). -$input_file = @( $opts['f'] ?: $opts['i'] ?: $opts['file'] ); -$output_file = @( $opts['o'] ?: $opts['output'] ); -$pretty = @( isset( $opts['p'] ) ?: isset( $opts['pretty'] ) ); -$boilerplate = @( isset( $opts['b'] ) ?: isset( $opts['boilerplate'] ) ); -$help_flag = @( isset( $opts['h'] ) ?: isset( $opts['help'] ) ); -$version_flag = @isset( $opts['version'] ); -$trace_flag = @isset( $opts['trace'] ); -$formatter = @$opts['formatter']; -$vendor_target = @$opts['vendor-target']; -$variables = @$opts['variables']; -$newlines = @$opts['newlines']; -$enable_plugins = isset( $opts['enable'] ) ? (array) $opts['enable'] : null; -$disable_plugins = isset( $opts['disable'] ) ? (array) $opts['disable'] : null; -$context = isset( $opts['context'] ) ? $opts['context'] : null; + // Flags. + 'p', // Pretty (un-minified) output. + 'w', // Enable file watching mode. + // Deprecated (removed in 2.x). + 'h', // Display help. (deprecated) +); -################################################################## -## Help page. +$long_opts = array( -$help = <<input_file = pick($opts, 'f', 'i', 'file'); +$args->output_file = pick($opts, 'o', 'output'); +$args->context = pick($opts, 'context'); - -o, --output: - The output file, if omitted prints to stdout. +// Flags. +$args->pretty = isset($opts['p']) ?: isset($opts['pretty']); +$args->watch = isset($opts['w']) ?: isset($opts['watch']); +$args->help = isset($opts['h']) ?: isset($opts['help']); +$args->version = isset($opts['version']); +$args->trace = isset($opts['trace']); - -p, --pretty: - Formatted, unminified output. +// Arguments that optionally accept a single value. +$args->boilerplate = pick($opts, 'b', 'boilerplate'); - -b, --boilerplate: - Whether or not to output a boilerplate. +// Arguments that require a single value. +$args->formatter = pick($opts, 'formatter'); +$args->vendor_target = pick($opts, 'vendor-target'); +$args->vars = pick($opts, 'vars', 'variables'); +$args->newlines = pick($opts, 'newlines'); - -h, --help: - Display this help mesasge. +// Arguments that require a value but accept multiple values. +$args->enable_plugins = pick($opts, 'enable'); +$args->disable_plugins = pick($opts, 'disable'); - --context: - Filepath context for resolving URLs. - --disable: - List of plugins to disable. Pass 'all' to disable all. +################################################################## +## Filter option values. - --enable: - List of plugins to enable. Overrides --disable. +// Validate filepath arguments. +if ($args->input_file) { + if (! ($args->input_file = realpath($args->input_file))) { + stderr('Input file does not exist.'); - --formatter: - Formatter to use for formatted (--pretty) output. - Available formatters: + exit(STATUS_ERROR); + } +} - 'block' (default) - - Rules are block formatted. - 'single-line' - - Rules are printed in single lines. - 'padded' - - Rules are printed in single lines with right padded selectors. +if ($args->output_file) { + $out_dir = realpath(dirname($args->output_file)); + if (! $out_dir) { + stderr('Output directory does not exist.'); - --newlines: - Force newline style on output css. Defaults to the current platform - newline. Possible values: 'windows' (or 'win'), 'unix', 'use-platform'. + exit(STATUS_ERROR); + } + $args->output_file = $out_dir . '/' . basename($args->output_file); +} - --trace: - Output debug-info stubs compatible with client-side sass debuggers. +if ($args->context) { + if (! ($args->context = realpath($args->context))) { + stderr('Context path does not exist.'); - --variables: - Map of variable names in an http query string format. + exit(STATUS_ERROR); + } +} - --vendor-target: - Set to 'all' for all vendor prefixes (default). - Set to 'none' for no vendor prefixes. - Set to a specific vendor prefix. +if (is_string($args->boilerplate)) { - --version: - Print version number. + if (! ($args->boilerplate = realpath($args->boilerplate))) { + stderr('Boilerplate file does not exist.'); -Examples: - csscrush -f styles.css --pretty --vendor-target webkit + exit(STATUS_ERROR); + } +} - # Piped input - cat 'styles.css' | csscrush --boilerplate - # Linting - csscrush -f screen.css -p --enable property-sorter -o screen-linted.css +// Run multiple value arguments through array cast. +foreach (array('enable_plugins', 'disable_plugins') as $arg) { + if ($args->{$arg}) { + $args->{$arg} = (array) $args->{$arg}; + } +} -TPL; +################################################################## +## Help and version info. +if ($args->version) { -if ( $version_flag ) { + stdout('CSS-Crush ' . CssCrush::$config->version); - stdout( 'CSS-Crush ' . CssCrush::$config->version ); - exit( STATUS_OK ); + exit(STATUS_OK); } -if ( $help_flag ) { +elseif ($args->help) { - stdout( $help ); - exit( STATUS_OK ); + stdout(manpage()); + + exit(STATUS_OK); } @@ -194,111 +179,280 @@ function get_stdin_contents () { $input = null; -if ( $input_file ) { +// File input. +if ($args->input_file) { - if ( ! file_exists( $input_file ) ) { - stderr( 'Input file not found' . PHP_EOL ); - exit( STATUS_ERROR ); - } - $input = file_get_contents( $input_file ); + $input = file_get_contents($args->input_file); } -elseif ( $stdin_contents = get_stdin_contents() ) { + +// STDIN. +elseif ($stdin_contents = get_stdin_contents()) { $input = $stdin_contents; } + +// Bail with manpage if no input. else { // No input, just output help screen. - stdout( $help ); - exit( STATUS_OK ); + stdout(manpage()); + + exit(STATUS_OK); +} + + +if ($args->watch && ! $args->input_file) { + + stderr('Watch mode requires an input file.'); + + exit(STATUS_ERROR); } ################################################################## -## Processing. +## Set process options. $process_opts = array(); -$process_opts[ 'boilerplate' ] = $boilerplate ? true : false; -$process_opts[ 'minify' ] = $pretty ? false : true; -$process_opts[ 'formatter' ] = $formatter ?: null; -$process_opts[ 'rewrite_import_urls' ] = true; - -// Newlines. -if ( isset( $newlines ) ) { - $process_opts[ 'newlines' ] = $newlines; +$process_opts['boilerplate'] = isset($args->boilerplate) ? $args->boilerplate : false; +$process_opts['minify'] = $args->pretty ? false : true; + +if ($args->formatter) { + $process_opts['formatter'] = $args->formatter; +} + +if ($args->formatter) { + $process_opts['formatter'] = $args->formatter; +} + +// Newlines arg. +if ($args->newlines) { + $process_opts['newlines'] = $args->newlines; } // Enable plugin args. -if ( $enable_plugins ) { - foreach ( $enable_plugins as $arg ) { - foreach ( preg_split( '~\s*,\s*~', $arg ) as $plugin ) { - $process_opts[ 'enable' ][] = $plugin; - } - } +if ($args->enable_plugins) { + $process_opts['enable'] = parse_list($args->enable_plugins); } // Disable plugin args. -if ( $disable_plugins ) { - foreach ( $disable_plugins as $arg ) { - foreach ( preg_split( '~\s*,\s*~', $arg ) as $plugin ) { - $process_opts[ 'disable' ][] = $plugin; - } - } +if ($args->disable_plugins) { + $process_opts['disable'] = parse_list($args->disable_plugins); } -// Tracing. -if ( $trace_flag ) { - $process_opts[ 'trace' ] = true; +// Tracing arg. +if ($args->trace) { + $process_opts['trace'] = true; } -// Vendor target args. -if ( $vendor_target ) { - $process_opts[ 'vendor_target' ] = $vendor_target; +// Vendor target arg. +if ($args->vendor_target) { + $process_opts['vendor_target'] = $args->vendor_target; } // Variables args. -if ( $variables ) { - parse_str( $variables, $in_vars ); - $process_opts[ 'vars' ] = $in_vars; +if ($args->vars) { + parse_str($args->vars, $in_vars); + $process_opts['vars'] = $in_vars; } -// Resolve a context for URLs. -if ( ! $context ) { - $context = $input_file ? dirname( realpath( $input_file ) ) : getcwd(); +// Resolve an input file context for relative filepaths. +if (! $args->context) { + $args->context = $args->input_file ? dirname($args->input_file) : getcwd(); } +$process_opts['context'] = $args->context; -// If there is an import context set document root also. -if ( $context ) { - $process_opts[ 'doc_root' ] = $context; - $process_opts[ 'context' ] = $context; -} +// Set document_root to the current working directory. +$process_opts['doc_root'] = getcwd(); -$output = CssCrush::string( $input, $process_opts ); +// If output file is specified set output directory and output filename. +if ($args->output_file) { + $process_opts['output_dir'] = dirname($args->output_file); + $process_opts['output_file'] = basename($args->output_file); +} ################################################################## ## Output. -if ( $output_file ) { +if ($args->watch) { - if ( ! @file_put_contents( $output_file, $output ) ) { + // Override the IO class. + CssCrush::$config->io = 'CssCrush_IOWatch'; - $message[] = "Could not write to path '$output_file'"; + stdout('CONTROL-C to quit.'); - if ( strpos( $output_file, '~' ) === 0 ) { - $message[] = 'Tilde expansion does not work here'; + while (true) { + + $created_file = CssCrush::file($args->input_file, $process_opts); + + if (CssCrush::$process->errors) { + stderr(CssCrush::$process->errors); + + exit(STATUS_ERROR); } - stderr( $message ); - exit( STATUS_ERROR ); + sleep(1); } } else { - if ( CssCrush::$process->errors ) { - stderr( CssCrush::$process->errors ); + $output = CssCrush::string($input, $process_opts); + + if ($args->output_file) { + + if (! @file_put_contents($args->output_file, $output)) { + + $message[] = "Could not write to path '{$args->output_file}'."; + stderr($message); + + exit(STATUS_ERROR); + } + } + else { + + if (CssCrush::$process->errors) { + stderr(CssCrush::$process->errors); + } + stdout($output); + + exit(STATUS_OK); } +} + + +################################################################## +## Helpers. + +function stderr ($lines, $closing_newline = true) { + $out = implode(PHP_EOL, (array) $lines) . ($closing_newline ? PHP_EOL : ''); + fwrite(STDERR, $out); +} + +function stdout ($lines, $closing_newline = true) { + $out = implode(PHP_EOL, (array) $lines) . ($closing_newline ? PHP_EOL : ''); + + // On OSX terminal is sometimes truncating 'visual' output to terminal + // with fwrite to STDOUT. + echo $out; +} + +function get_stdin_contents () { + $stdin = fopen('php://stdin', 'r'); + stream_set_blocking($stdin, false); + $stdin_contents = stream_get_contents($stdin); + fclose($stdin); + + return $stdin_contents; +} + +function parse_list (array $option) { + $out = array(); + foreach ($option as $arg) { + foreach (preg_split('~\s*,\s*~', $arg) as $item) { + $out[] = $item; + } + } + + return $out; +} + +function pick (array &$arr) { + + $args = func_get_args(); + array_shift($args); + + foreach ($args as $key) { + if (isset($arr[$key])) { + // Optional values return false but we want true is argument is present. + return is_bool($arr[$key]) ? true : $arr[$key]; + } + } + return null; +} + +function manpage () { + + return << alt-styles.css + + # Linting. + csscrush --pretty --enable property-sorter -i styles.css -o linted.css + + # Watch mode. + csscrush --watch -i styles.css -o compiled/styles.css + + # Using custom boilerplate template. + csscrush --boilerplate=css/boilerplate.txt -i css/styles.css +TPL; } diff --git a/lib/CssCrush/Core.php b/lib/CssCrush/Core.php index 7defde7..8e4dcb8 100644 --- a/lib/CssCrush/Core.php +++ b/lib/CssCrush/Core.php @@ -503,44 +503,60 @@ static public function stat ( $name = null ) ############################# - # Internal development. + # Logging and stats. static public $logging = false; - static public function log ( $arg = null, $label = '' ) + static public $log = array(); + + static public function log ($arg = null, $label = null, $var_dump = false) { - if ( ! self::$logging ) { + if (! self::$logging) { + return; } - static $log = ''; - $args = func_get_args(); - if ( ! count( $args ) ) { - // No arguments, return the log - return $log; + // If no arguments are passed return the log. + if (! func_num_args()) { + + if (PHP_SAPI !== 'cli') { + $out = array(); + foreach (CssCrush::$log as $item) { + $out[] = '
' . htmlspecialchars($item) . '
'; + } + return implode('
', $out); + } + else { + return implode(PHP_EOL, CssCrush::$log) . PHP_EOL; + } } - if ( $label ) { - $log .= "

$label

"; + if ($label) { + $label = PHP_EOL . "$label" . PHP_EOL . str_repeat('=', strlen($label)) . PHP_EOL; + } + else { + $label = ''; } - if ( is_string( $arg ) ) { - $log .= htmlspecialchars( $arg ) . '
'; + if (is_string($arg)) { + CssCrush::$log[] = "$label$arg"; } else { - $out = '
';
             ob_start();
-            print_r( $arg );
-            $out .= ob_get_clean();
-            $out .= '
'; - $log .= $out . '
'; + $var_dump ? var_dump($arg) : print_r($arg); + CssCrush::$log[] = $label . ob_get_clean(); } } - static public function logError ( $msg ) + static public function clearLog () + { + CssCrush::$log = array(); + } + + static public function logError ($msg) { self::$process->errors[] = $msg; - self::log( $msg ); + self::log($msg); } static public function runStat ( $name ) diff --git a/lib/CssCrush/Function.php b/lib/CssCrush/Function.php index 44175ac..fff5cd8 100644 --- a/lib/CssCrush/Function.php +++ b/lib/CssCrush/Function.php @@ -37,7 +37,7 @@ static public function setMatchPatt () array_keys( self::$functions ), true ); } - static public function executeOnString ( &$str, $patt = null, $process_callback = null, &$extra = null ) + static public function executeOnString ( &$str, $patt = null, $process_callback = null, $extra = null ) { // No bracketed expressions, early return. if (strpos($str, '(') === false) { @@ -71,7 +71,7 @@ static public function executeOnString ( &$str, $patt = null, $process_callback // No function name default to math expression. // Store the raw function name match. - $raw_fn_name = isset( $match[1] ) ? $match[1][0] : ''; + $raw_fn_name = isset($match[1]) ? strtolower($match[1][0]) : ''; $fn_name = $raw_fn_name ? $raw_fn_name : 'math'; if ( '-' === $fn_name ) { $fn_name = 'math'; @@ -91,12 +91,12 @@ static public function executeOnString ( &$str, $patt = null, $process_callback // First look for function as directly passed. if (isset($process_callback[$fn_name])) { - $func_returns = call_user_func_array($process_callback[$fn_name], array($args, &$extra)); + $func_returns = call_user_func($process_callback[$fn_name], $args, $extra); } // Secondly look for built-in function. elseif (isset(self::$functions[$fn_name])) { - $func_returns = call_user_func_array(self::$functions[$fn_name], array($args, &$extra)); + $func_returns = call_user_func(self::$functions[$fn_name], $args, $extra); } // Splice in the function result. diff --git a/lib/CssCrush/IO.php b/lib/CssCrush/IO.php index 351baa8..f9fd321 100644 --- a/lib/CssCrush/IO.php +++ b/lib/CssCrush/IO.php @@ -134,8 +134,8 @@ static public function validateExistingOutput () if ( ! $options_changed && ! $files_changed ) { // Files have not been modified and config is the same: return the old file. - CssCrush::log( "Files and options have not been modified, returning existing - file '$existingfile->URL'." ); + CssCrush::log( + "Files and options have not been modified, returning existing file '$existingfile->URL'." ); return $existingfile->URL . ( $options->versioning !== false ? "?$existing_datesum" : '' ); } else { @@ -153,10 +153,12 @@ static public function validateExistingOutput () } } else if ( file_exists( $existingfile->path ) ) { + // File exists but has no config. CssCrush::log( 'File exists but no config, removing existing file.' ); unlink( $existingfile->path ); } + return false; } // foreach diff --git a/lib/CssCrush/IOWatch.php b/lib/CssCrush/IOWatch.php new file mode 100644 index 0000000..4fca9a0 --- /dev/null +++ b/lib/CssCrush/IOWatch.php @@ -0,0 +1,43 @@ +options; + + $output_basename = basename($process->input->filename, '.css'); + + if (! empty($options->output_file)) { + $output_basename = basename($options->output_file, '.css'); + } + + $suffix = '.crush'; + if ($process->input->dir !== $process->output->dir) { + $suffix = ''; + } + + return "$output_basename$suffix.css"; + } + + public static function getCacheData () + { + // Clear results from earlier processes. + clearstatcache(); + CssCrush::$process->cacheData = array(); + + return self::$cacheData; + } + + public static function saveCacheData () + { + self::$cacheData = CssCrush::$process->cacheData; + } +} diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index 259b5c2..a90b8d4 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -23,7 +23,8 @@ static public function hostfile () // The prepend file. if ( $prepend_file = CssCrush_Util::find( 'Prepend-local.css', 'Prepend.css' ) ) { - $prepend_file_contents = file_get_contents( $prepend_file ); + + $prepend_file_contents = file_get_contents($prepend_file); $process->currentFile = 'file://' . $prepend_file; // If there's a parsing error inside the prepend file, wipe $prepend_file_contents. @@ -44,6 +45,7 @@ static public function hostfile () // If there's a parsing error go no further. if ( ! self::prepareForStream( $str ) ) { + return $str; } @@ -70,7 +72,7 @@ static public function hostfile () $url = CssCrush_Url::get( $match[1][0] ); // Pass over protocoled import urls. - if ( $url->protocol ) { + if ($url->protocol) { $search_offset = $match_end; continue; } diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index f78d427..8ef6e23 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -155,11 +155,19 @@ public function addToken ($value, $type) return $label; } - public function fetchToken ( $token ) + public function isToken ($str, $of_type = null) { - $path =& $this->tokens->{ $token[1] }; - if ( isset( $path[ $token ] ) ) { - return $path[ $token ]; + if (preg_match('~^\?([a-z])\d+\?$~S', $str, $m)) { + return $of_type ? ($of_type === $m[1]) : true; + } + return false; + } + + public function fetchToken ($token) + { + $path =& $this->tokens->{$token[1]}; + if (isset($path[$token])) { + return $path[$token]; } return null; } @@ -176,7 +184,7 @@ public function releaseToken ( $token ) unset( $this->tokens->{ $token[1] }[ $token ] ); } - public function restoreTokens ( $str, $type = 'p' ) + public function restoreTokens ($str, $type = 'p', $release = false) { // Reference the token table. $token_table =& $this->tokens->{$type}; @@ -184,12 +192,16 @@ public function restoreTokens ( $str, $type = 'p' ) // Find matching tokens. $matches = CssCrush_Regex::matchAll(CssCrush_Regex::$patt->{"{$type}_token"}, $str); - foreach ( $matches as $m ) { + foreach ($matches as $m) { $token = $m[0][0]; - if ( isset( $token_table[ $token ] ) ) { - $str = str_replace( $token, $token_table[ $token ], $str ); + if (isset($token_table[$token])) { + $str = str_replace($token, $token_table[$token], $str); + if ($release) { + unset($token_table[$token]); + } } } + return $str; } @@ -251,18 +263,19 @@ protected function getBoilerplate () // Substitute any tags if ( preg_match_all( '~\{\{([^}]+)\}\}~', $boilerplate, $boilerplate_matches ) ) { - $replacements = array(); + $tags = array( + 'datetime' => @date( 'Y-m-d H:i:s O' ), + 'year' => @date( 'Y' ), + 'version' => 'v' . CssCrush::$config->version, + ); + foreach ( $boilerplate_matches[0] as $index => $tag ) { $tag_name = $boilerplate_matches[1][$index]; - if ( $tag_name === 'datetime' ) { - $replacements[] = @date( 'Y-m-d H:i:s O' ); - } - elseif ( $tag_name === 'version' ) { - $replacements[] = 'v' . CssCrush::$config->version; - } - else { - $replacements[] = '?'; + $replacement = '?'; + if (isset($tags[$tag_name])) { + $replacement = $tags[$tag_name]; } + $replacements[] = $replacement; } $boilerplate = str_replace( $boilerplate_matches[0], $replacements, $boilerplate ); } @@ -309,26 +322,26 @@ static public function applySelectorAliases ( &$str ) return; } - // Find all selector-alias matches. - $matches = CssCrush_Regex::matchAll($process->selectorAliasesPatt, $str); - $table =& $process->selectorAliases; + // Find all selector-alias matches. + $selector_alias_calls = CssCrush_Regex::matchAll($process->selectorAliasesPatt, $str); + // Step through the matches from last to first. - while ($match = array_pop($matches)) { + while ($selector_alias_call = array_pop($selector_alias_calls)) { - $selector_alias_name = $match[1][0]; + $selector_alias_name = $selector_alias_call[1][0]; if (! isset($table[$selector_alias_name])) { continue; } $replacement = $table[$selector_alias_name]; - $start = $match[0][1]; - $length = strlen($match[0][0]); + $start = $selector_alias_call[0][1]; + $length = strlen($selector_alias_call[0][0]); // It's a function alias if a start paren is matched. - if (isset($match[2])) { + if (isset($selector_alias_call[2])) { if (! preg_match(CssCrush_Regex::$patt->balancedParens, $str, $parens, PREG_OFFSET_CAPTURE, $start)) { @@ -346,13 +359,13 @@ static public function applySelectorAliases ( &$str ) $replacement = str_replace($search, $args, $replacement); // Apply substitutions to copies of string tokens within the replacement. - preg_match_all(CssCrush_Regex::$patt->s_token, $replacement, $_matches); - foreach ($_matches as $m) { - $label = $m[0]; - if (isset($process->tokens->s[$label])) { + preg_match_all(CssCrush_Regex::$patt->s_token, $replacement, $s_tokens); + foreach ($s_tokens as $m) { + foreach ($m as $label) { + $old_token_value = $process->fetchToken($label); // Create new token based on the value. - $token_value = str_replace($search, $args, $process->tokens->s[$label]); + $token_value = str_replace($search, $args, $old_token_value); $new_label = $process->addToken($token_value, 's'); // Swap the old token label with new. @@ -521,6 +534,12 @@ protected function calculateVariables () $this->variables = array_merge( $this->variables, $option_vars ); } + // Finally add state variables. + // $this->variables = array( + // 'input-path' => $this->input->dirUrl, + // 'output-path' => $this->output->dirUrl, + // ) + $this->variables; + // Place variables referenced inside variables. Excecute custom functions. foreach ( $this->variables as $name => &$value ) { @@ -546,7 +565,7 @@ protected function placeAllVariables () // Repeat above steps for variables embedded in URL tokens. foreach ( $this->tokens->u as $label => $url ) { - if ( self::placeVariables( $url->value ) ) { + if (! $url->isData && self::placeVariables($url->value)) { // Re-evaluate $url->value if anything has been interpolated. $url->evaluate(); } @@ -1039,32 +1058,24 @@ protected function collate () } // Insert URLs. - if ( $this->tokens->u ) { + if ($this->tokens->u) { - $link = CssCrush_Util::getLinkBetweenDirs( $this->output->dir, $this->input->dir ); + $link = CssCrush_Util::getLinkBetweenDirs($this->output->dir, $this->input->dir); $make_urls_absolute = $options->rewrite_import_urls === 'absolute'; - foreach ( $this->tokens->u as $token => $url ) { + foreach ($this->tokens->u as $token => $url) { - if ( $url->isRelative ) { - // Optionally set the URLs to absolute. - if ( $make_urls_absolute ) { + if ($url->isRelative && ! $url->noRewrite) { + if ($make_urls_absolute) { $url->toRoot(); } // If output dir is different to input dir prepend a link between the two. - elseif ( $link ) { - $url->prepend( $link ); + elseif ($link) { + $url->prepend($link); } } - - if ( $url->convertToData ) { - $url->evaluate()->toData(); - } - else { - $url->simplify(); - } } - $this->stream->replaceHash( $this->tokens->u ); + $this->stream->replaceHash($this->tokens->u); } // Insert string literals. diff --git a/lib/CssCrush/Url.php b/lib/CssCrush/Url.php index 0af8401..7f1b5bb 100644 --- a/lib/CssCrush/Url.php +++ b/lib/CssCrush/Url.php @@ -7,68 +7,132 @@ class CssCrush_Url { public $protocol; + + public $isAbsolute; public $isRelative; public $isRooted; + public $isData; + + public $noRewrite; public $convertToData; public $value; public $label; - public function __construct ( $raw_value, $convert_to_data = false ) + public function __construct ($raw_value, $convert_to_data = false) { $regex = CssCrush_Regex::$patt; $process = CssCrush::$process; - if ( preg_match( $regex->s_token, $raw_value ) ) { - $this->value = trim( $process->fetchToken( $raw_value ), '\'"' ); - $process->releaseToken( $raw_value ); + if (preg_match($regex->s_token, $raw_value)) { + $this->value = trim($process->fetchToken($raw_value), '\'"'); + $process->releaseToken($raw_value); } else { $this->value = $raw_value; } $this->evaluate(); - $this->label = $process->addToken( $this, 'u' ); + $this->label = $process->addToken($this, 'u'); } public function __toString () { + if ($this->convertToData) { + $this->toData(); + } + + if ($this->isRelative || $this->isRooted) { + $this->simplify(); + } + + // Only wrap url with quotes if it contains tricky characters. $quote = ''; - if ( preg_match( '~[()*]~', $this->value ) || 'data' === $this->protocol ) { + if ($this->isData || preg_match('~[()*]~S', $this->value)) { $quote = '"'; } + return "url(http://github.com/$quote$this-%3Evalue$quote)"; } - static public function get ( $token ) + static public function get ($token) { - return CssCrush::$process->tokens->u[ $token ]; + return CssCrush::$process->tokens->u[$token]; } public function evaluate () { - $leading_variable = strpos( $this->value, '$(' ) === 0; - - // Match a protocol. - if ( preg_match( '~^([a-z]+)\:~i', $this->value, $m ) ) { - $this->protocol = strtolower( $m[1] ); + // Protocol based url. + if (preg_match('~^([a-z]+)\:~i', $this->value, $m)) { + + $this->protocol = strtolower($m[1]); + switch ($this->protocol) { + case 'data': + $type = 'data'; + break; + default: + $type = 'absolute'; + break; + } } + + // Relative and rooted urls. else { + $type = 'relative'; + $leading_variable = strpos($this->value, '$(') === 0; + // Normalize './' led paths. $this->value = preg_replace( '~^\.\/+~i', '', $this->value ); - if ( $this->value !== '' && $this->value[0] === '/' ) { - $this->isRooted = true; - } - elseif ( ! $leading_variable ) { - $this->isRelative = true; + + if ($leading_variable || ($this->value !== '' && $this->value[0] === '/')) { + $type = 'rooted'; } + // Normalize slashes. $this->value = rtrim( preg_replace( '~[\\\\/]+~', '/', $this->value ), '/' ); } + + $this->setType($type); + + return $this; + } + + public function resolveRootedPath () + { + $process = CssCrush::$process; + + if (! file_exists ($process->docRoot . $this->value)) { + return false; + } + + // Move upwards '..' by the number of slashes in baseURL to get a relative path. + $this->value = str_repeat('../', substr_count($process->input->dirUrl, '/')) . + substr($this->value, 1); + } + + public function prepend ($path_fragment) + { + if ($this->isRelative) { + $this->value = $path_fragment . $this->value; + } + + return $this; + } + + public function toRoot () + { + if ($this->isRelative) { + $this->prepend(CssCrush::$process->input->dirUrl . '/'); + $this->setType('rooted'); + } + return $this; } public function toData () { + // Only make one conversion attempt. + $this->convertToData = false; + $file = CssCrush::$process->docRoot . $this->toRoot()->value; // File not found. @@ -99,36 +163,33 @@ public function toData () $mime_type = $allowed_file_extensions[ $file_ext ]; $base64 = base64_encode( file_get_contents( $file ) ); $this->value = "data:$mime_type;base64,$base64"; - $this->protocol = 'data'; - - return $this; - } - public function resolveRootedPath () - { - $process = CssCrush::$process; - - if ( ! file_exists ( $process->docRoot . $this->value ) ) { - return false; - } - - // Move upwards '..' by the number of slashes in baseURL to get a relative path. - $this->value = str_repeat( '../', substr_count( $process->input->dirUrl, '/' ) ) . - substr( $this->value, 1 ); - } + $this->setType('data')->protocol = 'data'; - public function prepend ( $path_fragment ) - { - $this->value = $path_fragment . $this->value; return $this; } - public function toRoot () + public function setType ($type = 'absolute') { - if ($this->isRelative) { - $this->prepend(CssCrush::$process->input->dirUrl . '/'); - $this->isRooted = true; - $this->isRelative = false; + $this->isAbsolute = false; + $this->isRooted = false; + $this->isRelative = false; + $this->isData = false; + + switch ($type) { + case 'absolute': + $this->isAbsolute = true; + break; + case 'relative': + $this->isRelative = true; + break; + case 'rooted': + $this->isRooted = true; + break; + case 'data': + $this->isData = true; + $this->convertToData = false; + break; } return $this; @@ -136,7 +197,9 @@ public function toRoot () public function simplify () { - $this->value = CssCrush_Util::simplifyPath( $this->value ); + if (! $this->isData) { + $this->value = CssCrush_Util::simplifyPath($this->value); + } return $this; } } diff --git a/plugins/svg.php b/plugins/svg.php index 82b419a..95fe273 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -12,7 +12,7 @@ * * Element types * ------------- - * circle, ellipse, rect, polygon, path, line, polyline, star, text. + * circle, ellipse, rect, polygon, path, line, polyline, star. * * * @example @@ -48,16 +48,28 @@ )); function csscrush__enable_svg () { - CssCrush_Hook::add('process_extract', 'csscrush__svg'); + CssCrush_Hook::add('process_extract', 'csscrush__svg_extract'); CssCrush_Function::register('svg', 'csscrush_fn__svg'); + CssCrush_Function::register('svg-data', 'csscrush_fn__svg_data'); } function csscrush__disable_svg () { - CssCrush_Hook::remove('process_extract', 'csscrush__svg'); + CssCrush_Hook::remove('process_extract', 'csscrush__svg_extract'); CssCrush_Function::deRegister('svg'); + CssCrush_Function::deRegister('svg-data'); } -function csscrush__svg ($process) { +function csscrush_fn__svg ($input) { + + return csscrush__svg_generator($input, 'svg'); +} + +function csscrush_fn__svg_data ($input) { + + return csscrush__svg_generator($input, 'svg-data'); +} + +function csscrush__svg_extract ($process) { static $callback, $patt; if (! $callback) { @@ -76,199 +88,198 @@ function csscrush__svg ($process) { $process->stream->pregReplaceCallback($patt, $callback); } -function csscrush_fn__svg ($input) { +function csscrush__svg_generator ($input, $fn_name) { - // Map types to elements. - static $types = array( - 'circle' => 'circle', - 'ellipse' => 'ellipse', - 'rect' => 'rect', - 'polygon' => 'polygon', - 'path' => 'path', - 'line' => 'line', - 'polyline' => 'polyline', - 'star' => 'path', - 'text' => 'text', - ); + $process = CssCrush::$process; + + $cache_key = $fn_name . $input; + if (isset($process->misc->svg_cache[$cache_key])) { + + return $process->misc->svg_cache[$cache_key]; + } + + // Map types to element names. + static $ELEMENTS; + if (! $ELEMENTS) { + $ELEMENTS = array( + 'circle' => array( + 'tag' => 'circle', + 'attrs' => 'cx cy r', + ), + 'ellipse' => array( + 'tag' => 'ellipse', + 'attrs' => 'cx cy rx ry', + ), + 'rect' => array( + 'tag' => 'rect', + 'attrs' => 'x y rx ry width height', + ), + 'polygon' => array( + 'tag' => 'polygon', + 'attrs' => 'points', + ), + 'line' => array( + 'tag' => 'line', + 'attrs' => 'x1 y1 x2 y2', + ), + 'polyline' => array( + 'tag' => 'polyline', + 'attrs' => 'points', + ), + 'path' => array( + 'tag' => 'path', + 'attrs' => 'd', + ), + 'star' => array( + 'tag' => 'path', + 'attrs' => '', + ), + 'text' => array( + 'tag' => 'text', + 'attrs' => 'x y dx dy rotate', + ), + ); + + // Convert attributes to keyed array. + // Add global attributes. + foreach ($ELEMENTS as $type => &$schema) { + $schema['attrs'] = array_flip(explode(' ', $schema['attrs'])) + + array( + 'transform' => true, + ); + } + } - // Keys that represent custom svg properties. - static $element_props = array( + // Non standard attributes. + static $CUSTOM_ATTRS = array( 'type' => true, 'data' => true, 'twist' => true, - 'radius' => true, + 'diameter' => true, 'corner-radius' => true, 'star-points' => true, - 'points' => true, 'margin' => true, 'drop-shadow' => true, 'sides' => true, + 'text' => true, 'width' => true, 'height' => true, - 'text' => true, - ); - - // Keys that go to direct to element attributes. - static $attribute_props = array( - 'transform' => true, - 'x' => true, - 'y' => true, ); + // Bail if no args. $args = CssCrush_Function::parseArgs($input); - if (! isset($args[0])) { + return ''; } $name = strtolower(array_shift($args)); // Bail if no SVG registered by this name. - $svg_definitions =& CssCrush::$process->misc->svg_defs; - if (! isset($svg_definitions[$name])) { + $svg_defs =& $process->misc->svg_defs; + if (! isset($svg_defs[$name])) { + return ''; } // Apply args to template. - $block = $svg_definitions[$name]->apply($args); + $block = $svg_defs[$name]->apply($args); // Parse the block into a keyed assoc array. - $data = array_change_key_case(CssCrush_Util::parseBlock($block, true)); + $raw_data = array_change_key_case(CssCrush_Util::parseBlock($block, true)); + // Resolve the type. // Bail if type not recognised. - $type = isset($data['type']) ? strtolower($data['type']) : 'rect'; - if (! isset($types[$type])) { - return ''; - } + $type = isset($raw_data['type']) ? strtolower($raw_data['type']) : 'rect'; + if (! isset($ELEMENTS[$type])) { - // Setup functions for using on values. - // Note using custom versions of svg-*-gradient(). - static $generic_functions_patt, $fill_functions, $fill_functions_patt; - if (! $generic_functions_patt) { - $fill_functions = array( - 'svg-linear-gradient' => 'csscrush__svg_fn_linear_gradient', - 'svg-radial-gradient' => 'csscrush__svg_fn_radial_gradient', - 'pattern' => 'csscrush__svg_fn_pattern', - ); - $generic_functions = array_diff_key(CssCrush_Function::$functions, $fill_functions); - $generic_functions_patt = CssCrush_Regex::createFunctionPatt(array_keys($generic_functions), true); - $fill_functions_patt = CssCrush_Regex::createFunctionPatt(array_keys($fill_functions)); + return ''; } - // Placeholder for capturing generated fills. - $fills = array( - 'gradients' => array(), - 'patterns' => array(), + $ATTRIBUTES = $ELEMENTS[$type]['attrs']; + + // Create element object for attaching all required rendering data. + $element = (object) array( + 'tag' => $ELEMENTS[$type]['tag'], + 'fills' => array( + 'gradients' => array(), + 'patterns' => array(), + ), + 'filters' => array(), + 'data' => array(), + 'attrs' => array(), + 'styles' => array(), + 'svg_attrs' => array( + 'xmlns' => 'http://www.w3.org/2000/svg', + ), + 'svg_styles' => array(), + 'face_styles' => array(), ); - foreach ($data as $property => &$value) { - CssCrush_Function::executeOnString($value, $generic_functions_patt); - - // Only capturing fills for fill and stoke properties. - if ($property === 'fill' || $property === 'stroke') { - CssCrush_Function::executeOnString($value, $fill_functions_patt, $fill_functions, $fills); - - // If the value is a color with alpha component we split the color - // and set the corresponding *-opacity property because Webkit doesn't - // support rgba()/hsla() in SVG. - if ($components = CssCrush_Color::colorSplit($value)) { - list($color, $opacity) = $components; - $data[$property] = $color; - if ($opacity < 1) { - $data += array("$property-opacity" => $opacity); - } - } - } - } - // Delete loop reference after use. - unset($value); - - // Initialize SVG attributes. - $svg_attrs = array('xmlns' => 'http://www.w3.org/2000/svg'); - $svg_attrs['width'] = 0; - $svg_attrs['height'] = 0; - if ($fills['patterns']) { - $svg_attrs['xmlns:xlink'] ="http://www.w3.org/1999/xlink"; - } // Filter off prefixed properties that are for the svg element or @font-face. - $svg_styles = array(); - $face_styles = array(); - foreach ($data as $property => $value) { + foreach ($raw_data as $property => $value) { if (strpos($property, 'svg-') === 0) { - $svg_styles[substr($property, 4)] = $value; - unset($data[$property]); + $element->svg_styles[substr($property, 4)] = $value; + unset($raw_data[$property]); } elseif (strpos($property, 'face-') === 0) { - $face_styles[substr($property, 5)] = $value; - unset($data[$property]); + $element->face_styles[substr($property, 5)] = $value; + unset($raw_data[$property]); } } + csscrush__svg_apply_css_funcs($element, $raw_data); + // Initialize element attributes. - $element_name = $types[$type]; - $element_attrs = array_intersect_key($data, $attribute_props); - $element_data = array_intersect_key($data, $element_props); + $element->attrs = array_intersect_key($raw_data, $ATTRIBUTES); + $element->data = array_intersect_key($raw_data, $CUSTOM_ATTRS); - // Everything remaining is treated as CSS. - $styles = array_diff_key($data, $element_props, $attribute_props); + // Everything else is treated as CSS. + $element->styles = array_diff_key($raw_data, $CUSTOM_ATTRS, $ATTRIBUTES); - // Prepopulate common attributes. - csscrush__svg_decorate($element_data); + // Pre-populate common attributes. + csscrush__svg_preprocess($element); // Filters. - $filter = csscrush__svg_filter($element_data, $styles); + csscrush__svg_apply_filters($element); - // Apply SVG callback. - call_user_func_array("csscrush__svg_$type", - array(&$element_data, &$element_attrs, &$svg_attrs, &$styles, &$element_name)); + // Apply element type callback. + call_user_func("csscrush__svg_$type", $element); - // Flatten CSS styles. - $styles_data = array( - 'svg' => $svg_styles, - $element_name => $styles - ); - $styles_out = ''; - foreach ($styles_data as $selector => $declarations) { - $pairs = array(); - foreach ($declarations as $property => $value) { - $pairs[] = "$property:$value"; - } - if ($pairs) { - $styles_out .= $selector . '{' . implode(';', $pairs) . '}'; - } - } - $styles_out = CssCrush::$process->restoreTokens($styles_out, 'u'); + // Apply optimizations. + csscrush__svg_compress($element); - $element_attrs = CssCrush_Util::htmlAttributes($element_attrs); - $svg_attrs = CssCrush_Util::htmlAttributes($svg_attrs); + // Build markup. + $svg = csscrush__svg_render($element); - // Create SVG markup. - $svg[] = ""; - // $svg[] = ''; - $svg[] = implode($fills['gradients']); - $svg[] = implode($fills['patterns']); - $svg[] = $filter; - if ($styles_out) { - $svg[] = ''; - } - // $svg[] = ''; - if ($element_name === 'text') { - $svg[] = "{$element_data['text']}"; + // Debugging... + // $code = implode("\n", $svg); + // $test = '
' . htmlspecialchars($code) . '
'; + // echo $test; + + // Either write to a file. + if ($fn_name === 'svg') { + + $flattened_svg = implode("\n", $svg); + + // Create fingerprint for the created file. + $generated_filename = substr(md5($flattened_svg), 0, 7) . ".$name.crush.svg"; + + $generated_path = $process->output->dir . '/' . $generated_filename; + file_put_contents($generated_path, $flattened_svg); + + // Write to the same directory as the output css. + $generated_url = $generated_filename; + $url = new CssCrush_Url($generated_url); + $url->noRewrite = true; } + // Or create data uri. else { - $svg[] = "<$element_name$element_attrs/>"; + $url = new CssCrush_Url('data:image/svg+xml;base64,' . base64_encode(implode('', $svg))); } - $svg[] = ''; - - // Debugging... - $code = implode("\n", $svg); - $test = '
' . htmlspecialchars($code) . '
'; - echo $test, $code; - // Create data-uri url and return token label. - $url = new CssCrush_Url('data:image/svg+xml;base64,' . base64_encode(implode('', $svg))); + // Cache the output URL. + $process->misc->svg_cache[$cache_key] = $url->label; return $url->label; } @@ -277,238 +288,204 @@ function csscrush_fn__svg ($input) { /* Circle callback. */ -function csscrush__svg_circle (&$element_data, &$element_attrs, &$svg_attrs) { +function csscrush__svg_circle ($element) { // Ensure required attributes have defaults set. - $element_data += array( - 'radius' => 50, + $element->data += array( + 'diameter' => 50, ); - list($margin_top, $margin_right, $margin_bottom, $margin_left) = $element_data['margin']; + list($margin_top, $margin_right, $margin_bottom, $margin_left) = $element->data['margin']; - $element_attrs['r'] = $element_data['radius']; + $element->attrs['r'] = + $radius = csscrush__ifset($element->attrs['r'], $element->data['diameter'] / 2); - $radius = $element_data['radius']; $diameter = $radius * 2; - $element_attrs['cx'] = $margin_left + $radius; - $element_attrs['cy'] = $margin_top + $radius; + $element->attrs['cx'] = csscrush__ifset($element->attrs['cx'], $margin_left + $radius); + $element->attrs['cy'] = csscrush__ifset($element->attrs['cy'], $margin_top + $radius); - $svg_attrs['width'] = $margin_left + $diameter + $margin_right; - $svg_attrs['height'] = $margin_top + $diameter + $margin_bottom; + $element->svg_attrs['width'] = $margin_left + $diameter + $margin_right; + $element->svg_attrs['height'] = $margin_top + $diameter + $margin_bottom; } /* Rect callback. */ -function csscrush__svg_rect (&$element_data, &$element_attrs, &$svg_attrs) { +function csscrush__svg_rect ($element) { - // Ensure required attributes have defaults set. - $element_data += array( + $element->data += array( 'width' => 50, 'height' => 50, ); - list($margin_top, $margin_right, $margin_bottom, $margin_left) = $element_data['margin']; + list($margin_top, $margin_right, $margin_bottom, $margin_left) = $element->data['margin']; - $element_attrs['x'] = $margin_left; - $element_attrs['y'] = $margin_top; - $element_attrs['width'] = $element_data['width']; - $element_attrs['height'] = $element_data['height']; + $element->attrs['x'] = $margin_left; + $element->attrs['y'] = $margin_top; + $element->attrs['width'] = $element->data['width']; + $element->attrs['height'] = $element->data['height']; - if (isset($element_data['corner-radius'])) { - $args = csscrush__svg_parselist($element_data['corner-radius']); - $element_attrs['rx'] = isset($args[0]) ? $args[0] : 0; - $element_attrs['ry'] = isset($args[1]) ? $args[1] : $args[0]; + if (isset($element->data['corner-radius'])) { + $args = csscrush__svg_parselist($element->data['corner-radius']); + $element->attrs['rx'] = isset($args[0]) ? $args[0] : 0; + $element->attrs['ry'] = isset($args[1]) ? $args[1] : $args[0]; } - $svg_attrs['width'] = $margin_left + $element_data['width'] + $margin_right; - $svg_attrs['height'] = $margin_top + $element_data['height'] + $margin_bottom; + $element->svg_attrs['width'] = $margin_left + $element->data['width'] + $margin_right; + $element->svg_attrs['height'] = $margin_top + $element->data['height'] + $margin_bottom; } /* Ellipse callback. */ -function csscrush__svg_ellipse (&$element_data, &$element_attrs, &$svg_attrs) { +function csscrush__svg_ellipse ($element) { - // Ensure required attributes have defaults set. - $element_data += array( - 'radius' => '100 50', + $element->data += array( + 'diameter' => '100 50', ); - list($margin_top, $margin_right, $margin_bottom, $margin_left) = $element_data['margin']; - - $radius = csscrush__svg_parselist($element_data['radius']); - $radius_x = $radius[0]; - $radius_y = isset($radius[1]) ? $radius[1] : $radius[0]; + if (! isset($element->attrs['rx']) && ! isset($element->attrs['ry'])) { + $diameter = csscrush__svg_parselist($element->data['diameter']); + $element->attrs['rx'] = $diameter[0] / 2; + $element->attrs['ry'] = isset($diameter[1]) ? $diameter[1] / 2 : $diameter[0] / 2; + } - $element_attrs['rx'] = $radius_x; - $element_attrs['ry'] = $radius_y; + list($margin_top, $margin_right, $margin_bottom, $margin_left) = $element->data['margin']; - $element_attrs['cx'] = $margin_left + $radius_x; - $element_attrs['cy'] = $margin_top + $radius_y; + $element->attrs['cx'] = $margin_left + $element->attrs['rx']; + $element->attrs['cy'] = $margin_top + $element->attrs['ry']; - $svg_attrs['width'] = $margin_left + ($radius_x * 2) + $margin_right; - $svg_attrs['height'] = $margin_top + ($radius_y * 2) + $margin_bottom; + $element->svg_attrs['width'] = $margin_left + ($element->attrs['rx'] * 2) + $margin_right; + $element->svg_attrs['height'] = $margin_top + ($element->attrs['ry'] * 2) + $margin_bottom; } /* Path callback. */ -function csscrush__svg_path (&$element_data, &$element_attrs, &$svg_attrs, &$style_props) { +function csscrush__svg_path ($element) { - // Ensure required attributes have defaults set. - $element_data += array( - 'data' => 'M 10,10 l 10,0 l 0,10 l 10,0 l 0,10', - 'width' => 40, - 'height' => 40, + // Ensure minimum required attributes have defaults set. + $element->data += array( + 'd' => 'M 10,10 l 10,0 l 0,10 l 10,0 l 0,10', ); // Unclosed paths have implicit fill. - $style_props += array( + $element->styles += array( 'fill' => 'none', ); - - $element_attrs['d'] = $element_data['data']; - - $svg_attrs['width'] = $element_data['width']; - $svg_attrs['height'] = $element_data['height']; } /* Polyline callback. */ -function csscrush__svg_polyline (&$element_data, &$element_attrs, &$svg_attrs, &$style_props) { +function csscrush__svg_polyline ($element) { // Ensure required attributes have defaults set. - $element_data += array( + $element->data += array( 'points' => '20,20 40,20 40,40 60,40 60,60', - 'width' => 80, - 'height' => 80, ); // Polylines have implicit fill. - $style_props += array( + $element->styles += array( 'fill' => 'none', ); - - $element_attrs['points'] = $element_data['points']; - - $svg_attrs['width'] = $element_data['width']; - $svg_attrs['height'] = $element_data['height']; } /* Line callback. */ -function csscrush__svg_line (&$element_data, &$element_attrs, &$svg_attrs, &$style_props) { +function csscrush__svg_line ($element) { // Ensure required attributes have defaults set. - $element_data += array( + $element->data += array( 'points' => '10,10 70,70', - 'width' => 80, - 'height' => 80, ); // Set a default stroke. - $style_props += array( + $element->styles += array( 'stroke' => '#000', ); - $points = preg_split('~[, ]+~', $element_data['points']); - $element_attrs['x1'] = $points[0]; - $element_attrs['y1'] = $points[1]; - $element_attrs['x2'] = $points[2]; - $element_attrs['y2'] = $points[3]; - - $svg_attrs['width'] = $element_data['width']; - $svg_attrs['height'] = $element_data['height']; + $points = preg_split('~[, ]+~', $element->data['points']); + $element->attrs['x1'] = $points[0]; + $element->attrs['y1'] = $points[1]; + $element->attrs['x2'] = $points[2]; + $element->attrs['y2'] = $points[3]; } /* Polygon callback. */ -function csscrush__svg_polygon (&$element_data, &$element_attrs, &$svg_attrs, &$style_props, &$element_name) { - - // Check for points. - if (isset($element_data['points'])) { - - $element_data += array( - 'width' => 70, - 'height' => 70, - ); - - $element_attrs['points'] = $element_data['points']; - - $svg_attrs['width'] = $element_data['width']; - $svg_attrs['height'] = $element_data['height']; - } +function csscrush__svg_polygon ($element) { - // Fallback with sides. - else { + if (! isset($element->attrs['points'])) { // Switch to path element. - $element_name = 'path'; + $element->tag = 'path'; - $element_data += array( + $element->data += array( 'sides' => 3, - 'radius' => 100, + 'diameter' => 100, ); - list($margin_top, $margin_right, $margin_bottom, $margin_left) = $element_data['margin']; + list($margin_top, $margin_right, $margin_bottom, $margin_left) = $element->data['margin']; - $radius = csscrush__svg_parselist($element_data['radius']); - $radius = $radius[0]; + $diameter = csscrush__svg_parselist($element->data['diameter']); + $diameter = $diameter[0]; + $radius = $diameter / 2; $cx = $radius + $margin_left; $cy = $radius + $margin_top; - $sides = $element_data['sides']; + $sides = $element->data['sides']; - $element_attrs['d'] = csscrush__svg_starpath($cx, $cy, $sides, $radius); + $element->attrs['d'] = csscrush__svg_starpath($cx, $cy, $sides, $radius); - $svg_attrs['width'] = ($radius * 2) + $margin_left + $margin_right; - $svg_attrs['height'] = ($radius * 2) + $margin_top + $margin_bottom; + $element->svg_attrs['width'] = $diameter + $margin_left + $margin_right; + $element->svg_attrs['height'] = $diameter + $margin_top + $margin_bottom; } } /* Star callback. */ -function csscrush__svg_star (&$element_data, &$element_attrs, &$svg_attrs, &$style_props) { +function csscrush__svg_star ($element) { // Minimum required attributes have defaults. - $element_data += array( + $element->data += array( 'star-points' => 4, - 'radius' => '50 30', + 'diameter' => '50 30', 'twist' => 0, ); - list($margin_top, $margin_right, $margin_bottom, $margin_left) = $element_data['margin']; + list($margin_top, $margin_right, $margin_bottom, $margin_left) = $element->data['margin']; - $radius = csscrush__svg_parselist($element_data['radius']); - if (! isset($radius[1])) { - $radius[1] = ($radius[0] / 2); + $diameter = csscrush__svg_parselist($element->data['diameter']); + if (! isset($diameter[1])) { + $diameter[1] = ($diameter[0] / 2); } - list($outer_r, $inner_r) = $radius; + $outer_r = $diameter[0] / 2; + $inner_r = $diameter[1] / 2; $cx = $outer_r + $margin_left; $cy = $outer_r + $margin_top; - $points = $element_data['star-points']; - $twist = $element_data['twist']; + $points = $element->data['star-points']; + $twist = $element->data['twist'] * 10; - $element_attrs['d'] = csscrush__svg_starpath($cx, $cy, $points, $outer_r, $inner_r, $twist); + $element->attrs['d'] = csscrush__svg_starpath($cx, $cy, $points, $outer_r, $inner_r, $twist); - $svg_attrs['width'] = $margin_left + ($outer_r * 2) + $margin_left; - $svg_attrs['height'] = $margin_top + ($outer_r * 2) + $margin_bottom; + $element->svg_attrs['width'] = $margin_left + ($outer_r * 2) + $margin_left; + $element->svg_attrs['height'] = $margin_top + ($outer_r * 2) + $margin_bottom; } /* Text callback. + Warning: Very limited for svg-as-image situations. */ -function csscrush__svg_text (&$element_data, &$element_attrs, &$svg_attrs, &$style_props) { +function csscrush__svg_text ($element) { // Minimum required attributes have defaults. - $element_data += array( + $element->data += array( 'x' => 0, 'y' => 0, 'width' => 100, @@ -516,7 +493,7 @@ function csscrush__svg_text (&$element_data, &$element_attrs, &$svg_attrs, &$sty 'text' => '', ); - $text = CssCrush::$process->restoreTokens($element_data['text'], 's'); + $text = CssCrush::$process->restoreTokens($element->data['text'], 's'); // Remove open and close quotes. $text = substr($text, 1, strlen($text) - 2); @@ -526,10 +503,10 @@ function csscrush__svg_text (&$element_data, &$element_attrs, &$svg_attrs, &$sty // Remove excape slashes and encode meta entities. $text = htmlentities(stripslashes($text), ENT_QUOTES, 'UTF-8', false); - $element_data['text'] = $text; + $element->data['text'] = $text; - $svg_attrs['width'] = $element_data['width']; - $svg_attrs['height'] = $element_data['height']; + $element->svg_attrs['width'] = $element->data['width']; + $element->svg_attrs['height'] = $element->data['height']; } @@ -541,7 +518,7 @@ function csscrush__svg_text (&$element_data, &$element_attrs, &$svg_attrs, &$sty */ function csscrush__svg_starpath ($cx, $cy, $points, $outer_r, $inner_r = null, $twist = 0, $orient = 'point') { - $data[] = 'M'; + $d = array(); // Enforce minimum number of points. $points = max(3, $points); @@ -563,7 +540,7 @@ function csscrush__svg_starpath ($cx, $cy, $points, $outer_r, $inner_r = null, $ $y = ( $outer_r * sin($outer_angle) ) + $cy; if ($points != $s) { - $data[] = array($x, $y); + $d[] = "$x,$y"; } // If star shape is required need inner angles too. @@ -579,32 +556,18 @@ function csscrush__svg_starpath ($cx, $cy, $points, $outer_r, $inner_r = null, $ $ix = ( $inner_r * cos($inner_angle) ) + $cx; $iy = ( $inner_r * sin($inner_angle) ) + $cy; - $data[] = array($ix, $iy); - } - elseif ($points == $s) { - - $data[] = 'Z'; + $d[] = "$ix,$iy"; } } - // Round path coordinates down to save bytes. - foreach ($data as &$item) { - if (is_array($item)) { - $item = round($item[0], 2) . ',' . round($item[1], 2); - } - } - - return implode(' ', $data); + return 'M' . implode(' ', $d) . 'Z'; } +function csscrush__svg_apply_filters ($element) { -function csscrush__svg_filter (&$element_data, &$style_props) { - - $filter = ''; - - if (isset($element_data['drop-shadow'])) { + if (isset($element->data['drop-shadow'])) { - $parts = csscrush__svg_parselist($element_data['drop-shadow'], false); + $parts = csscrush__svg_parselist($element->data['drop-shadow'], false); list($ds_x, $ds_y, $ds_strength, $ds_color) = $parts += array( 2, // x offset. @@ -634,42 +597,165 @@ function csscrush__svg_filter (&$element_data, &$style_props) { $filter .= ''; $filter .= ''; $filter .= ''; - $style_props['filter'] = 'url(#f)'; - } - return $filter; + $element->styles['filter'] = 'url(#f)'; + $element->filters[] = $filter; + } } +function csscrush__svg_preprocess ($element) { -function csscrush__svg_decorate (&$element_data) { + if (isset($element->data['margin'])) { - if (isset($element_data['margin'])) { + $margin =& $element->data['margin']; - $parts = csscrush__svg_parselist($element_data['margin']); + $parts = csscrush__svg_parselist($margin); $count = count($parts); if ($count === 1) { - $element_data['margin'] = array($parts[0], $parts[0], $parts[0], $parts[0]); + $margin = array($parts[0], $parts[0], $parts[0], $parts[0]); } elseif ($count === 2) { - $element_data['margin'] = array($parts[0], $parts[1], $parts[0], $parts[1]); + $margin = array($parts[0], $parts[1], $parts[0], $parts[1]); } elseif ($count === 3) { - $element_data['margin'] = array($parts[0], $parts[1], $parts[2], $parts[1]); + $margin = array($parts[0], $parts[1], $parts[2], $parts[1]); } else { - $element_data['margin'] = $parts; + $margin = $parts; } } else { - $element_data['margin'] = array(0,0,0,0); + $element->data['margin'] = array(0,0,0,0); + } + + // 'Unzip' string tokens on data attributes. + foreach (array('points', 'd') as $point_data_attr) { + + if (isset($element->attrs[$point_data_attr])) { + + $value = $element->attrs[$point_data_attr]; + + if (CssCrush::$process->isToken($value, 's')) { + $element->attrs[$point_data_attr] = + trim(CssCrush::$process->fetchToken($value), '"\'');; + } + } + } + + if (isset($element->data['width'])) { + $element->svg_attrs['width'] = $element->data['width']; + } + if (isset($element->data['height'])) { + $element->svg_attrs['height'] = $element->data['height']; } } +function csscrush__svg_apply_css_funcs ($element, &$raw_data) { + + // Setup functions for using on values. + // Note using custom versions of svg-*-gradient(). + static $generic_functions_patt, $fill_functions, $fill_functions_patt; + if (! $generic_functions_patt) { + $fill_functions = array( + 'svg-linear-gradient' => 'csscrush__svg_fn_linear_gradient', + 'svg-radial-gradient' => 'csscrush__svg_fn_radial_gradient', + 'pattern' => 'csscrush__svg_fn_pattern', + ); + $generic_functions = + array_diff_key(CssCrush_Function::$functions, $fill_functions); + $generic_functions_patt = CssCrush_Regex::createFunctionPatt(array_keys($generic_functions), true); + $fill_functions_patt = CssCrush_Regex::createFunctionPatt(array_keys($fill_functions)); + } + + foreach ($raw_data as $property => &$value) { + CssCrush_Function::executeOnString($value, $generic_functions_patt); + + // Only capturing fills for fill and stoke properties. + if ($property === 'fill' || $property === 'stroke') { + CssCrush_Function::executeOnString( + $value, $fill_functions_patt, $fill_functions, $element); + + // If the value is a color with alpha component we split the color + // and set the corresponding *-opacity property because Webkit doesn't + // support rgba()/hsla() in SVG. + if ($components = CssCrush_Color::colorSplit($value)) { + list($color, $opacity) = $components; + $raw_data[$property] = $color; + if ($opacity < 1) { + $raw_data += array("$property-opacity" => $opacity); + } + } + } + } +} + +function csscrush__svg_compress ($element) { + + foreach ($element->attrs as $key => &$value) { + + // Compress numbers on data attributes. + if (in_array($key, array('points', 'd'))) { + $value = preg_replace_callback( + CssCrush_Regex::$patt->number, 'csscrush__svg_number_compress', $value); + } + } +} + +function csscrush__svg_render ($element) { + + // Flatten styles. + $styles = ''; + $styles_data = array( + '@font-face' => $element->face_styles, + 'svg' => $element->svg_styles, + $element->tag => $element->styles, + ); + foreach ($styles_data as $selector => $declarations) { + if ($declarations) { + $out = array(); + foreach ($declarations as $property => $value) { + $out[] = "$property:$value"; + } + $styles .= $selector . '{' . implode(';', $out) . '}'; + } + } + $styles = CssCrush::$process->restoreTokens($styles, 'u', true); + $styles = CssCrush::$process->restoreTokens($styles, 's'); + + $attrs = CssCrush_Util::htmlAttributes($element->attrs); + $svg_attrs = CssCrush_Util::htmlAttributes($element->svg_attrs); + + // Markup. + $svg[] = ""; + $svg[] = ''; + $svg[] = implode($element->fills['gradients']); + $svg[] = implode($element->fills['patterns']); + $svg[] = implode($element->filters); + if ($styles) { + $cdata = preg_match('~[<>&]~', $styles); + $svg[] = ''; + } + $svg[] = ''; + if ($element->tag === 'text') { + $svg[] = "{$element->data['text']}"; + } + else { + $svg[] = "<{$element->tag}$attrs/>"; + } + $svg[] = ''; + + return array_filter($svg, 'strlen'); +} + /* Custom versions of svg-*-gradient() for integrating. */ -function csscrush__svg_fn_linear_gradient ($input, &$fills) { +function csscrush__svg_fn_linear_gradient ($input, $element) { static $booted; if (! $booted) { @@ -677,13 +763,12 @@ function csscrush__svg_fn_linear_gradient ($input, &$fills) { CssCrush_Plugin::load('svg-gradients'); } $generated_gradient = csscrush__create_svg_linear_gradient($input); - $fills['gradients'][] = reset($generated_gradient); + $element->fills['gradients'][] = reset($generated_gradient); return 'url(#' . key($generated_gradient) . ')'; } - -function csscrush__svg_fn_radial_gradient ($input, &$fills) { +function csscrush__svg_fn_radial_gradient ($input, $element) { static $booted; if (! $booted) { @@ -691,13 +776,12 @@ function csscrush__svg_fn_radial_gradient ($input, &$fills) { CssCrush_Plugin::load('svg-gradients'); } $generated_gradient = csscrush__create_svg_radial_gradient($input); - $fills['gradients'][] = reset($generated_gradient); + $element->fills['gradients'][] = reset($generated_gradient); return 'url(#' . key($generated_gradient) . ')'; } - -function csscrush__svg_fn_pattern ($input, &$fills) { +function csscrush__svg_fn_pattern ($input, $element) { static $uid = 0; $pid = 'p' . (++$uid); @@ -708,10 +792,14 @@ function csscrush__svg_fn_pattern ($input, &$fills) { array('', '', 0, 0, 0, 0); $url = CssCrush::$process->popToken($url); + if (! $url) { + + return ''; + } // If $width or $height is not specified get image dimensions the slow way. if (! $width || ! $height) { - if (in_array($url->protocol, array('http', 'https'))) { + if (in_array($url->protocol, array('http', 'https', 'data'))) { $file = $url->value; } elseif ($url->isRelative || $url->isRooted) { @@ -731,7 +819,9 @@ function csscrush__svg_fn_pattern ($input, &$fills) { $generated_pattern .= "value}\" x=\"$x\" y=\"$y\" width=\"$width\" height=\"$height\"/>"; $generated_pattern .= ''; - $fills['patterns'][] = $generated_pattern; + $element->fills['patterns'][] = $generated_pattern; + $element->svg_attrs['xmlns:xlink'] = "http://www.w3.org/1999/xlink"; + return 'url(#' . $pid . ')'; } @@ -743,3 +833,14 @@ function csscrush__svg_parselist ($str, $numbers = true) { $list = preg_split('~ +~', trim($str)); return $numbers ? array_map('floatval', $list) : $list; } + +function csscrush__svg_number_compress ($m) { + return round($m[0], 2); +} + +function csscrush__ifset (&$var, $fallback = null) { + if (isset($var)) { + return $var; + } + return $fallback; +} From 3e6ba7e818c49d13edffcb2b5150e2c652aa7f57 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 21 Mar 2013 19:36:16 +0000 Subject: [PATCH 085/421] Updated svg-gradients to close match CSS native gradients. --- plugins/svg-gradients.php | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/plugins/svg-gradients.php b/plugins/svg-gradients.php index 7c60e49..41f9ea0 100644 --- a/plugins/svg-gradients.php +++ b/plugins/svg-gradients.php @@ -108,10 +108,10 @@ function csscrush__create_svg_linear_gradient ($input) { 'to bottom' => 0, 'to left' => 90, // Not very magic corners. - 'to top right' => array( array(0, 100), array(100, 0) ), - 'to top left' => array( array(100, 100), array(0, 0) ), + 'to top right' => array( array(0, 100), array(100, 0) ), + 'to top left' => array( array(100, 100), array(0, 0) ), 'to bottom right' => array( array(0, 0), array(100, 100) ), - 'to bottom left' => array( array(100, 0), array(0, 100) ), + 'to bottom left' => array( array(100, 0), array(0, 100) ), ); $angle_keywords[ 'to right top' ] = $angle_keywords[ 'to top right' ]; $angle_keywords[ 'to left top' ] = $angle_keywords[ 'to top left' ]; @@ -123,7 +123,7 @@ function csscrush__create_svg_linear_gradient ($input) { $args = CssCrush_Function::parseArgs( $input ); - // If no angle argument is passed the default used is 0. + // If no angle argument is passed the default. $angle = 0; // Parse starting and ending coordinates from the first argument if it's an angle. @@ -134,6 +134,9 @@ function csscrush__create_svg_linear_gradient ($input) { // Try to parse an angle value. if ( preg_match( $deg_patt, $first_arg ) ) { $angle = floatval( $first_arg ); + + // Quick fix to match standard linear-gradient() angle. + $angle += 180; $first_arg_is_angle = true; } elseif ( isset( $angle_keywords[ $first_arg ] ) ) { @@ -146,8 +149,6 @@ function csscrush__create_svg_linear_gradient ($input) { $first_arg_is_angle = true; } - $angle += 180; - // Shift off the first argument if it has been recognised as an angle. if ( $first_arg_is_angle ) { array_shift( $args ); @@ -156,9 +157,6 @@ function csscrush__create_svg_linear_gradient ($input) { // If not using a magic corner, create start/end coordinates from the angle. if (! $coords) { - // Quick fix to match standard linear-gradient() angle. - // $angle += 180; - // Normalize the angle. $angle = fmod($angle, 360); if ($angle < 0) { From aa1aceef7f2f09e9a4faeaa5cf28504ad3003586 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 22 Mar 2013 11:43:48 +0000 Subject: [PATCH 086/421] Future-proofing. Added csscrush_set function as a future-proof way of setting option defaults and overriding config settings. Added csscrush_version function as a future-proof way of retrieving version information. --- README.md | 2 +- cli.php | 2 +- lib/CssCrush/Core.php | 4 ++-- lib/functions.php | 28 ++++++++++++++++++++++++++++ plugins/svg.php | 16 ++++++---------- 5 files changed, 38 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 2a8edeb..0105016 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Quick start ```php diff --git a/cli.php b/cli.php index d08c19a..a4321a4 100755 --- a/cli.php +++ b/cli.php @@ -277,7 +277,7 @@ if ($args->watch) { // Override the IO class. - CssCrush::$config->io = 'CssCrush_IOWatch'; + csscrush_set('config', array('io' => 'CssCrush_IOWatch')); stdout('CONTROL-C to quit.'); diff --git a/lib/CssCrush/Core.php b/lib/CssCrush/Core.php index 8e4dcb8..e6846e4 100644 --- a/lib/CssCrush/Core.php +++ b/lib/CssCrush/Core.php @@ -17,13 +17,13 @@ class CssCrush // Init called once manually post class definition. static public function init () { - self::$config = new stdclass(); + self::$config = new stdClass(); // Path to the project root folder. self::$config->location = dirname(dirname(dirname(__FILE__))); // Establish version id. - self::$config->version = new CssCrush_Version( self::VERSION ); + self::$config->version = new CssCrush_Version(self::VERSION); // Set the docRoot reference. self::setDocRoot(); diff --git a/lib/functions.php b/lib/functions.php index d0daf2e..b45c540 100644 --- a/lib/functions.php +++ b/lib/functions.php @@ -31,3 +31,31 @@ function csscrush_clearcache ( $dir = '' ) { function csscrush_stat ( $name = null ) { return CssCrush::stat( $name ); } + +function csscrush_version () { + return CssCrush::$config->version; +} + +/** + * Set default options and config settings. + * + * @param string $object_name Name of object you want to modify: 'config' or 'options'. + * @param mixed $modifier Assoc array of keys and values to set, or callable which is passed the object. + */ +function csscrush_set ($object_name, $modifier) { + + if (in_array($object_name, array('options', 'config'))) { + + $pointer = $object_name === 'options' ? + CssCrush::$config->options : CssCrush::$config; + + if (is_callable($modifier)) { + call_user_func($modifier, $pointer); + } + elseif (is_array($modifier)) { + foreach ($modifier as $key => $value) { + $pointer->{$key} = $value; + } + } + } +} diff --git a/plugins/svg.php b/plugins/svg.php index 95fe273..eb0cec0 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -397,21 +397,17 @@ function csscrush__svg_polyline ($element) { */ function csscrush__svg_line ($element) { - // Ensure required attributes have defaults set. - $element->data += array( - 'points' => '10,10 70,70', - ); - // Set a default stroke. $element->styles += array( 'stroke' => '#000', ); - $points = preg_split('~[, ]+~', $element->data['points']); - $element->attrs['x1'] = $points[0]; - $element->attrs['y1'] = $points[1]; - $element->attrs['x2'] = $points[2]; - $element->attrs['y2'] = $points[3]; + $element->attrs += array( + 'x1' => 0, + 'x2' => 0, + 'y1' => 0, + 'y2' => 0, + ); } /* From ffe5405d28364ac3d807ced0d4f2e8309e747219 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 23 Mar 2013 13:35:31 +0000 Subject: [PATCH 087/421] Added ease plugin - Expanded keywords for transitions, ported from rework by @visonmedia. Added colors to cli help page. Added plugin_dirs config option so external plugin directories can be employed. --- cli.php | 80 +++++++++++++++++++++++++++------------ lib/CssCrush/Core.php | 3 ++ lib/CssCrush/Plugin.php | 46 +++++++++++++---------- lib/CssCrush/Rule.php | 33 +++++++--------- plugins/ease.php | 83 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 184 insertions(+), 61 deletions(-) create mode 100644 plugins/ease.php diff --git a/cli.php b/cli.php index a4321a4..3048c37 100755 --- a/cli.php +++ b/cli.php @@ -333,6 +333,7 @@ function stdout ($lines, $closing_newline = true) { // On OSX terminal is sometimes truncating 'visual' output to terminal // with fwrite to STDOUT. + // fwrite(STDOUT, $out); echo $out; } @@ -370,48 +371,78 @@ function pick (array &$arr) { return null; } +function colorize ($str) { + + static $tags = array( + '' => "\033[0;30m", + '' => "\033[0;31m", + '' => "\033[0;32m", + '' => "\033[0;33m", + '' => "\033[0;34m", + '' => "\033[0;35m", + '' => "\033[0;36m", + '' => "\033[0;37m", + + '' => "\033[1;30m", + '' => "\033[1;31m", + '' => "\033[1;32m", + '' => "\033[1;33m", + '' => "\033[1;34m", + '' => "\033[1;35m", + '' => "\033[1;36m", + '' => "\033[1;37m", + + '' => "\033[m", + ); + + $find = array_keys($tags); + $replace = array_values($tags); + + return str_replace($find, $replace, $str); +} + function manpage () { - return <<USAGE: + csscrush [-f|-i] [-o] [-p|--pretty] [-w|--watch] [-b|--boilerplate] [--help] [--formatter] [--vars] [--vendor-target] - [--version] [--newlines] + [--version] [--newlines] -Options: - -f, -i: +OPTIONS: + -f, -i: The input file. If omitted takes input from STDIN. - -o: + -o: The output file. If omitted prints to STDOUT. - -p, --pretty: + -p, --pretty: Formatted, un-minified output. - -w, --watch: + -w, --watch: Watch input file for changes. Writes to file specified with -o option or to the input file directory with a '.crush.css' file extension. - -b, --boilerplate: + -b, --boilerplate: Whether or not to output a boilerplate. Optionally accepts filepath to custom boilerplate template. - --help: + --help: Display this help mesasge. - --context: + --context: Filepath context for resolving URLs. - --disable: + --disable: List of plugins to disable. Pass 'all' to disable all. - --enable: - List of plugins to enable. Overrides --disable. + --enable: + List of plugins to enable. Overrides --disable. - --formatter: - Formatter to use for formatted (--pretty) output. + --formatter: + Formatter to use for formatted (--pretty) output. Available formatters: 'block' (default) - @@ -421,25 +452,25 @@ function manpage () { 'padded' - Rules are printed in single lines with right padded selectors. - --newlines: + --newlines: Force newline style on output css. Defaults to the current platform newline. Possible values: 'windows' (or 'win'), 'unix', 'use-platform'. - --trace: + --trace: Output debug-info stubs compatible with client-side sass debuggers. - --vars: + --vars: Map of variable names in an http query string format. - --vendor-target: + --vendor-target: Set to 'all' for all vendor prefixes (default). Set to 'none' for no vendor prefixes. Set to a specific vendor prefix. - --version: + --version: Print version number. -Examples: +EXAMPLES: # Restrict vendor prefixing. csscrush --pretty --vendor-target webkit -i styles.css @@ -454,5 +485,8 @@ function manpage () { # Using custom boilerplate template. csscrush --boilerplate=css/boilerplate.txt -i css/styles.css + TPL; + + return colorize($manpage); } diff --git a/lib/CssCrush/Core.php b/lib/CssCrush/Core.php index e6846e4..983f8a6 100644 --- a/lib/CssCrush/Core.php +++ b/lib/CssCrush/Core.php @@ -22,6 +22,9 @@ static public function init () // Path to the project root folder. self::$config->location = dirname(dirname(dirname(__FILE__))); + // Plugin directories. + self::$config->plugin_dirs = array(self::$config->location . '/plugins'); + // Establish version id. self::$config->version = new CssCrush_Version(self::VERSION); diff --git a/lib/CssCrush/Plugin.php b/lib/CssCrush/Plugin.php index 23b9d25..1230005 100644 --- a/lib/CssCrush/Plugin.php +++ b/lib/CssCrush/Plugin.php @@ -13,47 +13,55 @@ static public function show () return self::$plugins; } - static public function register ( $plugin_name, $callbacks ) + static public function register ($plugin_name, $callbacks) { - self::$plugins[ $plugin_name ] = $callbacks; + self::$plugins[$plugin_name] = $callbacks; } - static public function load ( $plugin_name ) + static public function load ($plugin_name) { // Assume the the plugin file is not loaded if null. - if ( ! isset( self::$plugins[ $plugin_name ] ) ) { + if (! isset(self::$plugins[$plugin_name])) { - $path = CssCrush::$config->location . "/plugins/$plugin_name.php"; + $found = false; - if ( ! file_exists( $path ) ) { + // Loop plugin_dirs to find the plugin. + foreach (CssCrush::$config->plugin_dirs as $plugin_dir) { - trigger_error( __METHOD__ . - ": $plugin_name plugin not found.\n", E_USER_NOTICE ); + $path = "$plugin_dir/$plugin_name.php"; + if (file_exists($path)) { + require_once $path; + $found = true; + break; + } } - else { - require_once $path; + + if (! $found) { + trigger_error(__METHOD__ . + ": $plugin_name plugin not found.\n", E_USER_NOTICE); } } - return isset( self::$plugins[ $plugin_name ] ) ? self::$plugins[ $plugin_name ] : null;; + + return isset(self::$plugins[$plugin_name]) ? self::$plugins[$plugin_name] : null; } - static public function enable ( $plugin_name ) + static public function enable ($plugin_name) { - $plugin = self::load( $plugin_name ); + $plugin = self::load($plugin_name); - if ( is_callable( $plugin[ 'enable' ] ) ) { - $plugin[ 'enable' ](); + if (is_callable($plugin['enable'])) { + $plugin['enable'](); } return true; } - static public function disable ( $plugin_name ) + static public function disable ($plugin_name) { - $plugin = isset( self::$plugins[ $plugin_name ] ) ? self::$plugins[ $plugin_name ] : null; + $plugin = isset(self::$plugins[$plugin_name]) ? self::$plugins[$plugin_name] : null; - if ( is_callable( $plugin[ 'disable' ] ) ) { - $plugin[ 'disable' ](); + if (is_callable($plugin['disable'])) { + $plugin['disable'](); } } } diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php index c56b0ce..aa76128 100644 --- a/lib/CssCrush/Rule.php +++ b/lib/CssCrush/Rule.php @@ -18,6 +18,7 @@ class CssCrush_Rule implements IteratorAggregate // Index of properties used in the rule for fast lookup. public $properties = array(); + public $canonicalProperties = array(); // Arugments passed via @extend. public $extendArgs = array(); @@ -724,33 +725,28 @@ public function getIterator () ############################# # Property indexing. - public function indexProperty ( $prop ) + public function indexProperty ($declaration) { - if ( isset( $this->properties[ $prop ] ) ) { - $this->properties[ $prop ]++; + $prop = $declaration->property; + + if (isset($this->properties[$prop])) { + $this->properties[$prop]++; } else { - $this->properties[ $prop ] = 1; + $this->properties[$prop] = 1; } + $this->canonicalProperties[$declaration->canonicalProperty] = true; } public function updatePropertyIndex () { - // Create a new table of properties. - $new_properties_table = array(); - - foreach ( $this as $declaration ) { - - $name = $declaration->property; + // Reset tables. + $this->properties = array(); + $this->canonicalProperties = array(); - if ( isset( $new_properties_table[ $name ] ) ) { - $new_properties_table[ $name ]++; - } - else { - $new_properties_table[ $name ] = 1; - } + foreach ($this->declarations as $declaration) { + $this->indexProperty($declaration); } - $this->properties = $new_properties_table; } @@ -769,8 +765,7 @@ public function addDeclaration ( $prop, $value, $contextIndex = 0 ) if ( empty( $declaration->inValid ) ) { - // Increment the property name index. - $this->indexProperty( $prop ); + $this->indexProperty($declaration); $this->declarations[] = $declaration; return $declaration; } diff --git a/plugins/ease.php b/plugins/ease.php new file mode 100644 index 0000000..0e11530 --- /dev/null +++ b/plugins/ease.php @@ -0,0 +1,83 @@ + 'csscrush__enable_ease', + 'disable' => 'csscrush__disable_ease', +)); + +function csscrush__enable_ease () { + CssCrush_Hook::add( 'rule_prealias', 'csscrush__ease' ); +} + +function csscrush__disable_ease () { + CssCrush_Hook::remove( 'rule_prealias', 'csscrush__ease' ); +} + +function csscrush__ease ( CssCrush_Rule $rule ) { + + static $find, $replace, $easing_properties; + if (! $find) { + $easings = array( + 'ease-in-out-back' => 'cubic-bezier(.680,-0.550,.265,1.550)', + 'ease-in-out-circ' => 'cubic-bezier(.785,.135,.150,.860)', + 'ease-in-out-expo' => 'cubic-bezier(1,0,0,1)', + 'ease-in-out-sine' => 'cubic-bezier(.445,.050,.550,.950)', + 'ease-in-out-quint' => 'cubic-bezier(.860,0,.070,1)', + 'ease-in-out-quart' => 'cubic-bezier(.770,0,.175,1)', + 'ease-in-out-cubic' => 'cubic-bezier(.645,.045,.355,1)', + 'ease-in-out-quad' => 'cubic-bezier(.455,.030,.515,.955)', + 'ease-out-back' => 'cubic-bezier(.175,.885,.320,1.275)', + 'ease-out-circ' => 'cubic-bezier(.075,.820,.165,1)', + 'ease-out-expo' => 'cubic-bezier(.190,1,.220,1)', + 'ease-out-sine' => 'cubic-bezier(.390,.575,.565,1)', + 'ease-out-quint' => 'cubic-bezier(.230,1,.320,1)', + 'ease-out-quart' => 'cubic-bezier(.165,.840,.440,1)', + 'ease-out-cubic' => 'cubic-bezier(.215,.610,.355,1)', + 'ease-out-quad' => 'cubic-bezier(.250,.460,.450,.940)', + 'ease-in-back' => 'cubic-bezier(.600,-0.280,.735,.045)', + 'ease-in-circ' => 'cubic-bezier(.600,.040,.980,.335)', + 'ease-in-expo' => 'cubic-bezier(.950,.050,.795,.035)', + 'ease-in-sine' => 'cubic-bezier(.470,0,.745,.715)', + 'ease-in-quint' => 'cubic-bezier(.755,.050,.855,.060)', + 'ease-in-quart' => 'cubic-bezier(.895,.030,.685,.220)', + 'ease-in-cubic' => 'cubic-bezier(.550,.055,.675,.190)', + 'ease-in-quad' => 'cubic-bezier(.550,.085,.680,.530)', + ); + + $easing_properties = array( + 'transition' => true, + 'transition-timing-function' => true, + ); + + foreach ($easings as $property => $value) { + $patt = CssCrush_Regex::create('' . $property . '', 'i'); + $find[] = $patt; + $replace[] = $value; + } + } + + if (! array_intersect_key($rule->canonicalProperties, $easing_properties)) { + return; + } + + foreach ($rule as $declaration) { + if ( + ! $declaration->skip && + isset($easing_properties[$declaration->canonicalProperty]) + ) { + $declaration->value = preg_replace($find, $replace, $declaration->value); + } + } +} From d7d554200071962c55ef3d5a025fc92d8a6e60ba Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 23 Mar 2013 20:41:55 +0000 Subject: [PATCH 088/421] Added two plugins for working with rem units. px2rem contains two functions; px2rem and px2em for converting pixel values. rem2px auto-generates a fallback declaration with rem font-size values converted to pixels. Also made a start with trimming bracket whitespace project wide. --- CssCrush.php | 12 +- lib/CssCrush/BalancedMatch.php | 30 ++-- lib/CssCrush/Core.php | 249 +++++++++++++++++---------------- lib/CssCrush/Declaration.php | 40 +++--- lib/CssCrush/ExtendArg.php | 12 +- lib/CssCrush/Function.php | 110 +++++++-------- lib/CssCrush/Hook.php | 31 ++-- plugins/ease.php | 2 +- plugins/px2rem.php | 49 +++++++ plugins/rem2px.php | 68 +++++++++ 10 files changed, 359 insertions(+), 244 deletions(-) create mode 100644 plugins/px2rem.php create mode 100644 plugins/rem2px.php diff --git a/CssCrush.php b/CssCrush.php index dbd3220..b140f4c 100644 --- a/CssCrush.php +++ b/CssCrush.php @@ -4,21 +4,21 @@ * Bootstrap file with autoloader. * */ -function csscrush_autoload ( $class ) { +function csscrush_autoload ($class) { // Only autoload classes with the library prefix. - if ( stripos( $class, 'csscrush' ) !== 0 ) { + if (stripos($class, 'csscrush') !== 0) { return; } // Tolerate some cases of lowercasing. - $class = str_ireplace( 'csscrush', 'CssCrush', $class ); - $subpath = implode( '/', array_map( 'ucfirst', explode( '_', $class ) ) ); + $class = str_ireplace('csscrush', 'CssCrush', $class); + $subpath = implode('/', array_map('ucfirst', explode('_', $class))); - require_once dirname( __FILE__ ) . "/lib/$subpath.php"; + require_once dirname(__FILE__) . "/lib/$subpath.php"; } -spl_autoload_register( 'csscrush_autoload' ); +spl_autoload_register('csscrush_autoload'); // Core.php will also be autoloaded with API changes in v2.x. diff --git a/lib/CssCrush/BalancedMatch.php b/lib/CssCrush/BalancedMatch.php index 215b08f..a2dea9d 100644 --- a/lib/CssCrush/BalancedMatch.php +++ b/lib/CssCrush/BalancedMatch.php @@ -6,38 +6,38 @@ */ class CssCrush_BalancedMatch { - public function __construct ( CssCrush_Stream $stream, $offset, $brackets = '{}' ) + public function __construct (CssCrush_Stream $stream, $offset, $brackets = '{}') { $this->stream = $stream; $this->offset = $offset; $this->match = null; $this->length = 0; - list( $opener, $closer ) = str_split( $brackets, 1 ); + list($opener, $closer) = str_split($brackets, 1); - if ( strpos( $stream->raw, $opener, $this->offset ) === false ) { + if (strpos($stream->raw, $opener, $this->offset) === false) { return; } - if ( substr_count( $stream->raw, $opener ) !== substr_count( $stream->raw, $closer ) ) { - $sample = substr( $stream->raw, $this->offset, 25 ); - trigger_error( __METHOD__ . ": Unmatched token near '$sample'.\n", E_USER_WARNING ); + if (substr_count($stream->raw, $opener) !== substr_count($stream->raw, $closer)) { + $sample = substr($stream->raw, $this->offset, 25); + trigger_error(__METHOD__ . ": Unmatched token near '$sample'.\n", E_USER_WARNING); return; } $patt = $opener === '{' ? CssCrush_Regex::$patt->balancedCurlies : CssCrush_Regex::$patt->balancedParens; - if ( preg_match( $patt, $stream->raw, $m, PREG_OFFSET_CAPTURE, $this->offset ) ) { + if (preg_match($patt, $stream->raw, $m, PREG_OFFSET_CAPTURE, $this->offset)) { $this->match = $m; - $this->matchLength = strlen( $m[0][0] ); + $this->matchLength = strlen($m[0][0]); $this->matchStart = $m[0][1]; $this->matchEnd = $this->matchStart + $this->matchLength; $this->length = $this->matchEnd - $this->offset; } else { - trigger_error( __METHOD__ . ": Could not match '$opener'. Exiting.\n", E_USER_WARNING ); + trigger_error(__METHOD__ . ": Could not match '$opener'. Exiting.\n", E_USER_WARNING); } } @@ -48,21 +48,21 @@ public function inside () public function whole () { - return substr( $this->stream->raw, $this->offset, $this->length ); + return substr($this->stream->raw, $this->offset, $this->length); } - public function replace ( $replacement ) + public function replace ($replacement) { - $this->stream->splice( $replacement, $this->offset, $this->length ); + $this->stream->splice($replacement, $this->offset, $this->length); } public function unWrap () { - $this->stream->splice( $this->inside(), $this->offset, $this->length ); + $this->stream->splice($this->inside(), $this->offset, $this->length); } - public function nextIndexOf ( $needle ) + public function nextIndexOf ($needle) { - return strpos( $this->stream->raw, $needle, $this->offset ); + return strpos($this->stream->raw, $needle, $this->offset); } } diff --git a/lib/CssCrush/Core.php b/lib/CssCrush/Core.php index 983f8a6..1c26380 100644 --- a/lib/CssCrush/Core.php +++ b/lib/CssCrush/Core.php @@ -41,7 +41,7 @@ static public function init () self::$config->plugins = array(); // Default options. - self::$config->options = new CssCrush_Options( array( + self::$config->options = new CssCrush_Options(array( // Minify. Set false for formatting and comments. 'minify' => true, @@ -89,111 +89,111 @@ static public function init () // Force newline type on output files. Defaults to the current platform newline. // Options: 'windows' (or 'win'), 'unix', 'use-platform' 'newlines' => 'use-platform', - )); + )); // Include and register stock formatters. require_once self::$config->location . '/misc/formatters.php'; } - static protected function setDocRoot ( $doc_root = null ) + static protected function setDocRoot ($doc_root = null) { // Get document_root reference // $_SERVER['DOCUMENT_ROOT'] is unreliable in certain CGI/Apache/IIS setups - if ( ! $doc_root ) { + if (! $doc_root) { - $script_filename = $_SERVER[ 'SCRIPT_FILENAME' ]; - $script_name = $_SERVER[ 'SCRIPT_NAME' ]; + $script_filename = $_SERVER['SCRIPT_FILENAME']; + $script_name = $_SERVER['SCRIPT_NAME']; - if ( $script_filename && $script_name ) { + if ($script_filename && $script_name) { - $len_diff = strlen( $script_filename ) - strlen( $script_name ); + $len_diff = strlen($script_filename) - strlen($script_name); // We're comparing the two strings so normalize OS directory separators - $script_filename = str_replace( '\\', '/', $script_filename ); - $script_name = str_replace( '\\', '/', $script_name ); + $script_filename = str_replace('\\', '/', $script_filename); + $script_name = str_replace('\\', '/', $script_name); // Check $script_filename ends with $script_name - if ( substr( $script_filename, $len_diff ) === $script_name ) { + if (substr($script_filename, $len_diff) === $script_name) { - $path = substr( $script_filename, 0, $len_diff ); - $doc_root = realpath( $path ); + $path = substr($script_filename, 0, $len_diff); + $doc_root = realpath($path); } } - if ( ! $doc_root ) { + if (! $doc_root) { // If doc_root is still falsy, fallback to DOCUMENT_ROOT - $doc_root = realpath( $_SERVER[ 'DOCUMENT_ROOT' ] ); + $doc_root = realpath($_SERVER['DOCUMENT_ROOT']); } - if ( ! $doc_root ) { + if (! $doc_root) { // If doc_root is still falsy, log an error $error = "Could not get a document_root reference."; - CssCrush::logError( $error ); - trigger_error( __METHOD__ . ": $error\n", E_USER_NOTICE ); + CssCrush::logError($error); + trigger_error(__METHOD__ . ": $error\n", E_USER_NOTICE); } } - self::$config->docRoot = CssCrush_Util::normalizePath( $doc_root ); + self::$config->docRoot = CssCrush_Util::normalizePath($doc_root); } // Aliases and macros loader. static public function loadAssets () { static $called; - if ( $called ) { + if ($called) { return; } // Find an aliases file in the root directory // a local file overrides the default - $aliases_file = CssCrush_Util::find( 'Aliases-local.ini', 'Aliases.ini' ); + $aliases_file = CssCrush_Util::find('Aliases-local.ini', 'Aliases.ini'); // Load aliases file if it exists - if ( $aliases_file ) { + if ($aliases_file) { - $result = @parse_ini_file( $aliases_file, true ); - if ( $result !== false ) { + $result = @parse_ini_file($aliases_file, true); + if ($result !== false) { - foreach ( $result as $section => $items ) { + foreach ($result as $section => $items) { // Declaration aliases require a little preparation. - if ( $section === 'declarations' ) { + if ($section === 'declarations') { $store = array(); - foreach ( $items as $prop_val => $aliases ) { - list( $prop, $value ) = array_map( 'trim', explode( ':', $prop_val ) ); - foreach ( $aliases as &$alias ) { - $alias = explode( ':', $alias ); + foreach ($items as $prop_val => $aliases) { + list($prop, $value) = array_map('trim', explode(':', $prop_val)); + foreach ($aliases as &$alias) { + $alias = explode(':', $alias); } - $store[ $prop ][ $value ] = $aliases; + $store[$prop][$value] = $aliases; } - $result[ 'declarations' ] = $store; + $result['declarations'] = $store; } // Function groups. - elseif ( strpos( $section, 'functions:' ) === 0 ) { - $group = substr( $section, strlen( 'functions' ) ); + elseif (strpos($section, 'functions:') === 0) { + $group = substr($section, strlen('functions')); $vendor_grouped_aliases = array(); - foreach ( $items as $func_name => $aliases ) { + foreach ($items as $func_name => $aliases) { // Assign group name to the aliasable function. - $result[ 'functions' ][ $func_name ] = $group; + $result['functions'][$func_name] = $group; - foreach ( $aliases as $alias_func ) { + foreach ($aliases as $alias_func) { // Only supporting vendor prefixed aliases, for now. - if ( preg_match( CssCrush_Regex::$patt->vendorPrefix, $alias_func, $m ) ) { + if (preg_match(CssCrush_Regex::$patt->vendorPrefix, $alias_func, $m)) { // We'll cache the function matching regex here. - $vendor_grouped_aliases[$m[1]]['find'][] = CssCrush_Regex::create( '' . $func_name . '', 'i' ); + $vendor_grouped_aliases[$m[1]]['find'][] = CssCrush_Regex::create('' . $func_name . '', 'i'); $vendor_grouped_aliases[$m[1]]['replace'][] = $alias_func; } } } - $result[ 'function_groups' ][ $group ] = $vendor_grouped_aliases; + $result['function_groups'][$group] = $vendor_grouped_aliases; } } @@ -206,37 +206,37 @@ static public function loadAssets () 'function_groups' => array(), 'declarations' => array(), 'at-rules' => array(), - ); + ); self::$config->aliases += self::$config->bareAliasGroups; } else { - trigger_error( __METHOD__ . ": Aliases file could not be parsed.\n", E_USER_NOTICE ); + trigger_error(__METHOD__ . ": Aliases file could not be parsed.\n", E_USER_NOTICE); } } else { - trigger_error( __METHOD__ . ": Aliases file not found.\n", E_USER_NOTICE ); + trigger_error(__METHOD__ . ": Aliases file not found.\n", E_USER_NOTICE); } // Find a plugins file in the root directory, // a local file overrides the default - $plugins_file = CssCrush_Util::find( 'Plugins-local.ini', 'Plugins.ini' ); + $plugins_file = CssCrush_Util::find('Plugins-local.ini', 'Plugins.ini'); // Load plugins - if ( $plugins_file ) { - $result = @parse_ini_file( $plugins_file ); - if ( $result !== false ) { - if ( isset( $result[ 'plugins' ] ) ) { - foreach ( $result[ 'plugins' ] as $plugin_name ) { + if ($plugins_file) { + $result = @parse_ini_file($plugins_file); + if ($result !== false) { + if (isset($result['plugins'])) { + foreach ($result['plugins'] as $plugin_name) { // Backwards compat. - $plugin_name = basename( $plugin_name, '.php' ); - if ( CssCrush_Plugin::load( $plugin_name ) ) { - self::$config->plugins[ $plugin_name ] = true; + $plugin_name = basename($plugin_name, '.php'); + if (CssCrush_Plugin::load($plugin_name)) { + self::$config->plugins[$plugin_name] = true; } } } } else { - trigger_error( __METHOD__ . ": Plugin file could not be parsed.\n", E_USER_NOTICE ); + trigger_error(__METHOD__ . ": Plugin file could not be parsed.\n", E_USER_NOTICE); } } @@ -254,9 +254,9 @@ static public function loadAssets () * @param mixed $options An array of options or null. * @return string The public path to the compiled file or an empty string. */ - static public function file ( $file, $options = null ) + static public function file ($file, $options = null) { - self::$process = new CssCrush_Process( $options ); + self::$process = new CssCrush_Process($options); $config = self::$config; $process = self::$process; @@ -264,47 +264,48 @@ static public function file ( $file, $options = null ) $doc_root = $process->docRoot; // Since we're comparing strings, we need to iron out OS differences. - $file = str_replace( '\\', '/', $file ); + $file = str_replace('\\', '/', $file); // Finding the system path of the input file and validating it. $pathtest = true; - if ( strpos( $file, $doc_root ) === 0 ) { - // System path. - $pathtest = $process->setContext( dirname( $file ) ); + + // System path. + if (strpos($file, $doc_root) === 0) { + $pathtest = $process->setContext(dirname($file)); } - else if ( strpos( $file, '/' ) === 0 ) { - // WWW root path. - $pathtest = $process->setContext( dirname( $doc_root . $file ) ); + // WWW root path. + else if (strpos($file, '/') === 0) { + $pathtest = $process->setContext(dirname($doc_root . $file)); } + // Relative path. else { - // Relative path. - $pathtest = $process->setContext( dirname( dirname( __FILE__ ) . '/' . $file ) ); + $pathtest = $process->setContext(dirname(dirname(__FILE__) . '/' . $file)); } - if ( ! $pathtest ) { + if (! $pathtest) { // Main directory not found or is not writable return an empty string. return ''; } // Validate file input. - if ( ! CssCrush_IO::registerInputFile( $file ) ) { + if (! CssCrush_IO::registerInputFile($file)) { return ''; } // Create a filename that will be used later // Used in validateCache, and writing to filesystem - $process->output->filename = $process->ioCall( 'getOutputFileName' ); + $process->output->filename = $process->ioCall('getOutputFileName'); // Caching. - if ( $options->cache ) { + if ($options->cache) { // Load the cache data. - $process->cacheData = $process->ioCall( 'getCacheData' ); + $process->cacheData = $process->ioCall('getCacheData'); // If cache is enabled check for a valid compiled file. - $valid_compliled_file = $process->ioCall( 'validateExistingOutput' ); + $valid_compliled_file = $process->ioCall('validateExistingOutput'); - if ( is_string( $valid_compliled_file ) ) { + if (is_string($valid_compliled_file)) { return $valid_compliled_file; } } @@ -313,7 +314,7 @@ static public function file ( $file, $options = null ) $stream = $process->compile(); // Create file and return url. Return empty string on failure. - if ( $url = $process->ioCall( 'write', $stream ) ) { + if ($url = $process->ioCall('write', $stream)) { $timestamp = $options->versioning ? '?' . time() : ''; return "$url$timestamp"; } @@ -330,28 +331,28 @@ static public function file ( $file, $options = null ) * @param array $attributes An array of HTML attributes. * @return string HTML link tag or error message inside HTML comment. */ - static public function tag ( $file, $options = null, $attributes = array() ) + static public function tag ($file, $options = null, $attributes = array()) { - $file = self::file( $file, $options ); + $file = self::file($file, $options); - if ( ! empty( $file ) ) { + if (! empty($file)) { // On success return the tag with any custom attributes - $attributes[ 'rel' ] = 'stylesheet'; - $attributes[ 'href' ] = $file; + $attributes['rel'] = 'stylesheet'; + $attributes['href'] = $file; // Should media type be forced to 'all'? - if ( ! isset( $attributes[ 'media' ] ) ) { - $attributes[ 'media' ] = 'all'; + if (! isset($attributes['media'])) { + $attributes['media'] = 'all'; } - $attr_string = CssCrush_Util::htmlAttributes( $attributes ); + $attr_string = CssCrush_Util::htmlAttributes($attributes); return "\n"; } else { // Return an HTML comment with message on failure $class = __CLASS__; - $errors = implode( "\n", self::$process->errors ); + $errors = implode("\n", self::$process->errors); return "\n"; } } @@ -364,28 +365,28 @@ static public function tag ( $file, $options = null, $attributes = array() ) * @param array $attributes An array of HTML attributes, set false to return CSS text without tag. * @return string HTML link tag or error message inside HTML comment. */ - static public function inline ( $file, $options = null, $attributes = array() ) + static public function inline ($file, $options = null, $attributes = array()) { // For inline output set boilerplate to not display by default - if ( ! is_array( $options ) ) { + if (! is_array($options)) { $options = array(); } - if ( ! isset( $options[ 'boilerplate' ] ) ) { - $options[ 'boilerplate' ] = false; + if (! isset($options['boilerplate'])) { + $options['boilerplate'] = false; } - $file = self::file( $file, $options ); + $file = self::file($file, $options); - if ( ! empty( $file ) ) { + if (! empty($file)) { // On success fetch the CSS text - $content = file_get_contents( self::$process->output->dir . '/' - . self::$process->output->filename ); + $content = file_get_contents(self::$process->output->dir . '/' + . self::$process->output->filename); $tag_open = ''; $tag_close = ''; - if ( is_array( $attributes ) ) { - $attr_string = CssCrush_Util::htmlAttributes( $attributes ); + if (is_array($attributes)) { + $attr_string = CssCrush_Util::htmlAttributes($attributes); $tag_open = ""; $tag_close = ''; } @@ -395,7 +396,7 @@ static public function inline ( $file, $options = null, $attributes = array() ) // Return an HTML comment with message on failure $class = __CLASS__; - $errors = implode( "\n", self::$process->errors ); + $errors = implode("\n", self::$process->errors); return "\n"; } } @@ -407,14 +408,14 @@ static public function inline ( $file, $options = null, $attributes = array() ) * @param mixed $options An array of options or null. * @return string CSS text. */ - static public function string ( $string, $options = null ) + static public function string ($string, $options = null) { // For strings set boilerplate to not display by default - if ( ! isset( $options[ 'boilerplate' ] ) ) { - $options[ 'boilerplate' ] = false; + if (! isset($options['boilerplate'])) { + $options['boilerplate'] = false; } - self::$process = new CssCrush_Process( $options ); + self::$process = new CssCrush_Process($options); $config = self::$config; $process = self::$process; @@ -422,18 +423,18 @@ static public function string ( $string, $options = null ) // Set the path context if one is given. // Fallback to document root. - if ( ! empty( $options->context ) ) { - $process->setContext( $options->context, false ); + if (! empty($options->context)) { + $process->setContext($options->context, false); } else { - $process->setContext( $process->docRoot, false ); + $process->setContext($process->docRoot, false); } // Set the string on the input object. $process->input->string = $string; // Import files may be ignored. - if ( isset( $options->no_import ) ) { + if (isset($options->no_import)) { $process->input->importIgnore = true; } @@ -446,24 +447,24 @@ static public function string ( $string, $options = null ) * * @param mixed $var Assoc array of variable names and values, a php ini filename or null. */ - static public function globalVars ( $vars ) + static public function globalVars ($vars) { $config = self::$config; // Merge into the stack, overrides existing variables of the same name - if ( is_array( $vars ) ) { - $config->vars = array_merge( $config->vars, $vars ); + if (is_array($vars)) { + $config->vars = array_merge($config->vars, $vars); } // Test for a file. If it is attempt to parse it - elseif ( is_string( $vars ) && file_exists( $vars ) ) { - if ( $result = @parse_ini_file( $vars ) ) { - $config->vars = array_merge( $config->vars, $result ); + elseif (is_string($vars) && file_exists($vars)) { + if ($result = @parse_ini_file($vars)) { + $config->vars = array_merge($config->vars, $result); } } // Clear the stack if the argument is explicitly null - elseif ( is_null( $vars ) ) { + elseif (is_null($vars)) { $config->vars = array(); } } @@ -473,9 +474,9 @@ static public function globalVars ( $vars ) * * @param string $dir System path to the directory. */ - static public function clearCache ( $dir = '' ) + static public function clearCache ($dir = '') { - return $process->ioCall( 'clearCache', $dir ); + return $process->ioCall('clearCache', $dir); } /** @@ -484,22 +485,22 @@ static public function clearCache ( $dir = '' ) * * @param string $name Name of stat to retrieve. Leave blank to retrieve all. */ - static public function stat ( $name = null ) + static public function stat ($name = null) { $process = CssCrush::$process; $stat = $process->stat; // Get logged errors as late as possible. - if ( in_array( 'errors', $process->options->trace ) && ( ! $name || 'errors' === $name ) ) { - $stat[ 'errors' ] = $process->errors; + if (in_array('errors', $process->options->trace) && (! $name || 'errors' === $name)) { + $stat['errors'] = $process->errors; } - if ( $name && array_key_exists( $name, $stat ) ) { - return array( $name => $stat[ $name ] ); + if ($name && array_key_exists($name, $stat)) { + return array($name => $stat[$name]); } // Lose stats that are only useful internally. - unset( $stat[ 'compile_start_time' ] ); + unset($stat['compile_start_time']); return $stat; } @@ -562,31 +563,31 @@ static public function logError ($msg) self::log($msg); } - static public function runStat ( $name ) + static public function runStat ($name) { $process = CssCrush::$process; $trace = $process->options->trace; - if ( ! $trace || ! in_array( $name, $trace ) ) { + if (! $trace || ! in_array($name, $trace)) { return; } - switch ( $name ) { + switch ($name) { case 'selector_count': - $process->stat[ 'selector_count' ] = 0; - foreach ( $process->tokens->r as $rule ) { - $process->stat[ 'selector_count' ] += count( $rule->selectors ); + $process->stat['selector_count'] = 0; + foreach ($process->tokens->r as $rule) { + $process->stat['selector_count'] += count($rule->selectors); } break; case 'rule_count': - $process->stat[ 'rule_count' ] = count( $process->tokens->r ); + $process->stat['rule_count'] = count($process->tokens->r); break; case 'compile_time': - $time = microtime( true ); - $process->stat[ 'compile_time' ] = $time - $process->stat[ 'compile_start_time' ]; + $time = microtime(true); + $process->stat['compile_time'] = $time - $process->stat['compile_start_time']; break; } } diff --git a/lib/CssCrush/Declaration.php b/lib/CssCrush/Declaration.php index 3b35d2f..5098961 100644 --- a/lib/CssCrush/Declaration.php +++ b/lib/CssCrush/Declaration.php @@ -15,29 +15,29 @@ class CssCrush_Declaration public $skip; public $important; - public function __construct ( $prop, $value, $contextIndex = 0 ) + public function __construct ($prop, $value, $contextIndex = 0) { $regex = CssCrush_Regex::$patt; // Normalize input. Lowercase the property name. - $prop = strtolower( trim( $prop ) ); - $value = trim( $value ); + $prop = strtolower(trim($prop)); + $value = trim($value); // Check the input. - if ( $prop === '' || $value === '' || $value === null ) { + if ($prop === '' || $value === '' || $value === null) { $this->inValid = true; return; } // Test for escape tilde. - if ( $skip = strpos( $prop, '~' ) === 0 ) { - $prop = substr( $prop, 1 ); + if ($skip = strpos($prop, '~') === 0) { + $prop = substr($prop, 1); } // Store the canonical property name. // Store the vendor mark if one is present. - if ( preg_match( $regex->vendorPrefix, $prop, $vendor ) ) { + if (preg_match($regex->vendorPrefix, $prop, $vendor)) { $canonical_property = $vendor[2]; $vendor = $vendor[1]; } @@ -47,13 +47,13 @@ public function __construct ( $prop, $value, $contextIndex = 0 ) } // Check for !important. - if ( ( $important = stripos( $value, '!important' ) ) !== false ) { - $value = rtrim( substr( $value, 0, $important ) ); + if (($important = stripos($value, '!important')) !== false) { + $value = rtrim(substr($value, 0, $important)); $important = true; } // Reject declarations with empty CSS values. - if ( $value === false || $value === '' ) { + if ($value === false || $value === '') { $this->inValid = true; return; @@ -70,7 +70,7 @@ public function __construct ( $prop, $value, $contextIndex = 0 ) public function __toString () { - if ( CssCrush::$process->minifyOutput ) { + if (CssCrush::$process->minifyOutput) { $whitespace = ''; } else { @@ -86,22 +86,22 @@ public function __toString () Capture parens. Index functions. */ - public function process ( $parent_rule ) + public function process ($parent_rule) { // Apply custom functions. - if ( ! $this->skip ) { + if (! $this->skip) { // this() function needs to be called exclusively because // it's self referencing. $extra = array( 'rule' => $parent_rule, - ); + ); CssCrush_Function::executeOnString( $this->value, CssCrush_Regex::$patt->thisFunction, array( 'this' => 'csscrush_fn__this', - ), + ), $extra); // Add result to $rule->selfData. @@ -110,8 +110,8 @@ public function process ( $parent_rule ) $extra = array( 'rule' => $parent_rule, 'property' => $this->property - ); - CssCrush_Function::executeOnString( + ); + CssCrush_Function::executeOnString( $this->value, null, null, @@ -141,9 +141,9 @@ public function indexFunctions () { // Create an index of all regular functions in the value. $functions = array(); - if ( preg_match_all( CssCrush_Regex::$patt->function, $this->value, $m ) ) { - foreach ( $m[1] as $index => $fn_name ) { - $functions[ strtolower( $fn_name ) ] = true; + if (preg_match_all(CssCrush_Regex::$patt->function, $this->value, $m)) { + foreach ($m[1] as $index => $fn_name) { + $functions[strtolower($fn_name)] = true; } } $this->functions = $functions; diff --git a/lib/CssCrush/ExtendArg.php b/lib/CssCrush/ExtendArg.php index 1f5ed8a..01c7260 100644 --- a/lib/CssCrush/ExtendArg.php +++ b/lib/CssCrush/ExtendArg.php @@ -10,20 +10,20 @@ class CssCrush_ExtendArg public $name; public $pseudo; - public function __construct ( $name ) + public function __construct ($name) { $this->name = $name; - if ( ! preg_match( CssCrush_Regex::$patt->rooted_ident, $this->name ) ) { + if (! preg_match(CssCrush_Regex::$patt->rooted_ident, $this->name)) { // Not a regular name: Some kind of selector so normalize it for later comparison. - $this->name = CssCrush_Selector::makeReadable( $this->name ); + $this->name = CssCrush_Selector::makeReadable($this->name); // If applying the pseudo on output store. - if ( substr( $this->name, -1 ) === '!' ) { + if (substr($this->name, -1) === '!') { - $this->name = rtrim( $this->name, ' !' ); - if ( preg_match( '~\:\:?[\w-]+$~', $this->name, $m ) ) { + $this->name = rtrim($this->name, ' !'); + if (preg_match('~\:\:?[\w-]+$~', $this->name, $m)) { $this->pseudo = $m[0]; } } diff --git a/lib/CssCrush/Function.php b/lib/CssCrush/Function.php index fff5cd8..ee3a363 100644 --- a/lib/CssCrush/Function.php +++ b/lib/CssCrush/Function.php @@ -28,16 +28,16 @@ class CssCrush_Function 's-adjust' => 'csscrush_fn__s_adjust', 'l-adjust' => 'csscrush_fn__l_adjust', 'a-adjust' => 'csscrush_fn__a_adjust', - ); + ); static public function setMatchPatt () { self::$functions = self::$builtinFunctions + self::$customFunctions; self::$functionPatt = CssCrush_Regex::createFunctionPatt( - array_keys( self::$functions ), true ); + array_keys(self::$functions), true); } - static public function executeOnString ( &$str, $patt = null, $process_callback = null, $extra = null ) + static public function executeOnString (&$str, $patt = null, $process_callback = null, $extra = null) { // No bracketed expressions, early return. if (strpos($str, '(') === false) { @@ -57,15 +57,15 @@ static public function executeOnString ( &$str, $patt = null, $process_callback } // Find custom function matches. - $matches = CssCrush_Regex::matchAll( $patt, $str ); + $matches = CssCrush_Regex::matchAll($patt, $str); // Step through the matches from last to first. - while ( $match = array_pop( $matches ) ) { + while ($match = array_pop($matches)) { $offset = $match[0][1]; - if ( ! preg_match( CssCrush_Regex::$patt->balancedParens, - $str, $parens, PREG_OFFSET_CAPTURE, $offset ) ) { + if (! preg_match(CssCrush_Regex::$patt->balancedParens, + $str, $parens, PREG_OFFSET_CAPTURE, $offset)) { continue; } @@ -73,15 +73,15 @@ static public function executeOnString ( &$str, $patt = null, $process_callback // Store the raw function name match. $raw_fn_name = isset($match[1]) ? strtolower($match[1][0]) : ''; $fn_name = $raw_fn_name ? $raw_fn_name : 'math'; - if ( '-' === $fn_name ) { + if ('-' === $fn_name) { $fn_name = 'math'; } $opening_paren = $parens[0][1]; - $closing_paren = $opening_paren + strlen( $parens[0][0] ); + $closing_paren = $opening_paren + strlen($parens[0][0]); // Get the function arguments. - $args = trim( $parens[1][0] ); + $args = trim($parens[1][0]); // Workaround the signs. $before_operator = '-' === $raw_fn_name ? '-' : ''; @@ -100,7 +100,7 @@ static public function executeOnString ( &$str, $patt = null, $process_callback } // Splice in the function result. - $str = substr_replace( $str, "$before_operator$func_returns", $offset, $closing_paren - $offset ); + $str = substr_replace($str, "$before_operator$func_returns", $offset, $closing_paren - $offset); } } @@ -108,27 +108,27 @@ static public function executeOnString ( &$str, $patt = null, $process_callback ############################# # API and helpers. - static public function register ( $name, $callback ) + static public function register ($name, $callback) { - CssCrush_Function::$customFunctions[ $name ] = $callback; + CssCrush_Function::$customFunctions[ $name] = $callback; } - static public function deRegister ( $name ) + static public function deRegister ($name) { - unset( CssCrush_Function::$customFunctions[ $name ] ); + unset(CssCrush_Function::$customFunctions[ $name]); } - static public function parseArgs ( $input, $allowSpaceDelim = false ) + static public function parseArgs ($input, $allowSpaceDelim = false) { return CssCrush_Util::splitDelimList( - $input, ( $allowSpaceDelim ? '\s*[,\s]\s*' : ',' ) ); + $input, ($allowSpaceDelim ? '\s*[,\s]\s*' : ',')); } // Intended as a quick arg-list parse for function that take up-to 2 arguments // with the proviso the first argument is an ident. - static public function parseArgsSimple ( $input ) + static public function parseArgsSimple ($input) { - return preg_split( CssCrush_Regex::$patt->argListSplit, $input, 2 ); + return preg_split(CssCrush_Regex::$patt->argListSplit, $input, 2); } } @@ -136,91 +136,91 @@ static public function parseArgsSimple ( $input ) ############################# # Stock CSS functions. -function csscrush_fn__math ( $input ) { +function csscrush_fn__math ($input) { // Strip blacklisted characters - $input = preg_replace( CssCrush_Regex::$patt->mathBlacklist, '', $input ); + $input = preg_replace(CssCrush_Regex::$patt->mathBlacklist, '', $input); - $result = @eval( "return $input;" ); + $result = @eval("return $input;"); - return $result === false ? 0 : round( $result, 5 ); + return $result === false ? 0 : round($result, 5); } -function csscrush_fn__percent ( $input ) { +function csscrush_fn__percent ($input) { // Strip non-numeric and non delimiter characters - $input = preg_replace( '~[^\d\.\s,]~S', '', $input ); + $input = preg_replace('~[^\d\.\s,]~S', '', $input); - $args = preg_split( CssCrush_Regex::$patt->argListSplit, $input, -1, PREG_SPLIT_NO_EMPTY ); + $args = preg_split(CssCrush_Regex::$patt->argListSplit, $input, -1, PREG_SPLIT_NO_EMPTY); // Use precision argument if it exists, use default otherwise - $precision = isset( $args[2] ) ? $args[2] : 5; + $precision = isset($args[2]) ? $args[2] : 5; // Output zero on failure $result = 0; // Need to check arguments or we may see divide by zero errors - if ( count( $args ) > 1 && ! empty( $args[0] ) && ! empty( $args[1] ) ) { + if (count($args) > 1 && ! empty($args[0]) && ! empty($args[1])) { // Use bcmath if it's available for higher precision // Arbitary high precision division - if ( function_exists( 'bcdiv' ) ) { - $div = bcdiv( $args[0], $args[1], 25 ); + if (function_exists('bcdiv')) { + $div = bcdiv($args[0], $args[1], 25); } else { $div = $args[0] / $args[1]; } // Set precision percentage value - if ( function_exists( 'bcmul' ) ) { - $result = bcmul( (string) $div, '100', $precision ); + if (function_exists('bcmul')) { + $result = bcmul((string) $div, '100', $precision); } else { - $result = round( $div * 100, $precision ); + $result = round($div * 100, $precision); } // Trim unnecessary zeros and decimals - $result = trim( (string) $result, '0' ); - $result = rtrim( $result, '.' ); + $result = trim((string) $result, '0'); + $result = rtrim($result, '.'); } return $result . '%'; } -function csscrush_fn__hsla_adjust ( $input ) { - list( $color, $h, $s, $l, $a ) = array_pad( CssCrush_Function::parseArgs( $input, true ), 5, 0 ); - return CssCrush_Color::colorAdjust( $color, array( $h, $s, $l, $a ) ); +function csscrush_fn__hsla_adjust ($input) { + list($color, $h, $s, $l, $a) = array_pad(CssCrush_Function::parseArgs($input, true), 5, 0); + return CssCrush_Color::colorAdjust($color, array($h, $s, $l, $a)); } -function csscrush_fn__hsl_adjust ( $input ) { - list( $color, $h, $s, $l ) = array_pad( CssCrush_Function::parseArgs( $input, true ), 4, 0 ); - return CssCrush_Color::colorAdjust( $color, array( $h, $s, $l, 0 ) ); +function csscrush_fn__hsl_adjust ($input) { + list($color, $h, $s, $l) = array_pad(CssCrush_Function::parseArgs($input, true), 4, 0); + return CssCrush_Color::colorAdjust($color, array($h, $s, $l, 0)); } -function csscrush_fn__h_adjust ( $input ) { - list( $color, $h ) = array_pad( CssCrush_Function::parseArgs( $input, true ), 2, 0 ); - return CssCrush_Color::colorAdjust( $color, array( $h, 0, 0, 0 ) ); +function csscrush_fn__h_adjust ($input) { + list($color, $h) = array_pad(CssCrush_Function::parseArgs($input, true), 2, 0); + return CssCrush_Color::colorAdjust($color, array($h, 0, 0, 0)); } -function csscrush_fn__s_adjust ( $input ) { - list( $color, $s ) = array_pad( CssCrush_Function::parseArgs( $input, true ), 2, 0 ); - return CssCrush_Color::colorAdjust( $color, array( 0, $s, 0, 0 ) ); +function csscrush_fn__s_adjust ($input) { + list($color, $s) = array_pad(CssCrush_Function::parseArgs($input, true), 2, 0); + return CssCrush_Color::colorAdjust($color, array(0, $s, 0, 0)); } -function csscrush_fn__l_adjust ( $input ) { - list( $color, $l ) = array_pad( CssCrush_Function::parseArgs( $input, true ), 2, 0 ); - return CssCrush_Color::colorAdjust( $color, array( 0, 0, $l, 0 ) ); +function csscrush_fn__l_adjust ($input) { + list($color, $l) = array_pad(CssCrush_Function::parseArgs($input, true), 2, 0); + return CssCrush_Color::colorAdjust($color, array(0, 0, $l, 0)); } -function csscrush_fn__a_adjust ( $input ) { - list( $color, $a ) = array_pad( CssCrush_Function::parseArgs( $input, true ), 2, 0 ); - return CssCrush_Color::colorAdjust( $color, array( 0, 0, 0, $a ) ); +function csscrush_fn__a_adjust ($input) { + list($color, $a) = array_pad(CssCrush_Function::parseArgs($input, true), 2, 0); + return CssCrush_Color::colorAdjust($color, array(0, 0, 0, $a)); } function csscrush_fn__this ($input, $extra) { - $args = CssCrush_Function::parseArgsSimple( $input ); + $args = CssCrush_Function::parseArgsSimple($input); $property = $args[0]; // Function relies on a context rule, bail if none. @@ -279,7 +279,7 @@ function csscrush_fn__query ($input, $extra) { // If a rule reference is found, query its data. $result = ''; if (isset($references[$name])) { - $query_rule = $references[ $name ]; + $query_rule = $references[$name]; $query_rule->processDeclarations(); $query_rule->expandDataSet('queryData', $property); diff --git a/lib/CssCrush/Hook.php b/lib/CssCrush/Hook.php index 2c6c018..e6ad28e 100644 --- a/lib/CssCrush/Hook.php +++ b/lib/CssCrush/Hook.php @@ -9,34 +9,31 @@ class CssCrush_Hook // Table of hooks and the functions attached to them. static public $register = array(); - static public function add ( $hook, $fn_name ) + static public function add ($hook, $fn_name) { // Bail early is the named hook and callback combination is already loaded. - if ( isset( self::$register[ $hook ][ $fn_name ] ) ) { - return; - } - - // Register the hook and callback. - // Store in associative array so no duplicates. - if ( function_exists( $fn_name ) ) { + if (! isset(self::$register[$hook][$fn_name])) { - self::$register[ $hook ][ $fn_name ] = true; + // Register the hook and callback. + // Store in associative array so no duplicates. + if (function_exists($fn_name)) { + self::$register[$hook][$fn_name] = true; + } } } - static public function remove ( $hook, $fn_name ) + static public function remove ($hook, $fn_name) { - unset( self::$register[ $hook ][ $fn_name ] ); + unset(self::$register[$hook][$fn_name]); } - static public function run ( $hook, $arg_obj = null ) + static public function run ($hook, $arg_obj = null) { // Run all callbacks attached to the hook. - if ( ! isset( self::$register[ $hook ] ) ) { - return; - } - foreach ( array_keys( self::$register[ $hook ] ) as $fn_name ) { - call_user_func( $fn_name, $arg_obj ); + if (isset(self::$register[$hook])) { + foreach (array_keys(self::$register[$hook]) as $fn_name) { + call_user_func($fn_name, $arg_obj); + } } } } diff --git a/plugins/ease.php b/plugins/ease.php index 0e11530..ef2a47d 100644 --- a/plugins/ease.php +++ b/plugins/ease.php @@ -25,7 +25,7 @@ function csscrush__disable_ease () { CssCrush_Hook::remove( 'rule_prealias', 'csscrush__ease' ); } -function csscrush__ease ( CssCrush_Rule $rule ) { +function csscrush__ease (CssCrush_Rule $rule) { static $find, $replace, $easing_properties; if (! $find) { diff --git a/plugins/px2rem.php b/plugins/px2rem.php new file mode 100644 index 0000000..12e0b5b --- /dev/null +++ b/plugins/px2rem.php @@ -0,0 +1,49 @@ + 'csscrush__enable_px2rem', + 'disable' => 'csscrush__disable_px2rem', +)); + +function csscrush__enable_px2rem () { + CssCrush_Function::register('px2rem', 'csscrush_fn__px2rem'); + CssCrush_Function::register('px2em', 'csscrush_fn__px2em'); +} + +function csscrush__disable_px2rem () { + CssCrush_Function::deRegister('px2rem'); + CssCrush_Function::deRegister('px2em'); +} + +function csscrush_fn__px2rem ($input) { + return csscrush__px2rem($input, 'rem'); +} + +function csscrush_fn__px2em ($input) { + return csscrush__px2rem($input, 'em'); +} + +function csscrush__px2rem ($input, $unit) { + + list($px, $base) = CssCrush_Function::parseArgsSimple($input) + array( + 16, + 16, + ); + + return round($px / $base, 5) . $unit; +} diff --git a/plugins/rem2px.php b/plugins/rem2px.php new file mode 100644 index 0000000..37f34fc --- /dev/null +++ b/plugins/rem2px.php @@ -0,0 +1,68 @@ + 'csscrush__enable_rem2px', + 'disable' => 'csscrush__disable_rem2px', +)); + +function csscrush__enable_rem2px () { + CssCrush_Hook::add( 'rule_prealias', 'csscrush__rem2px' ); +} + +function csscrush__disable_rem2px () { + CssCrush_Hook::remove( 'rule_prealias', 'csscrush__rem2px' ); +} + +function csscrush__rem2px (CssCrush_Rule $rule) { + + static $fontsize_properties, $rem_patt; + if (! $fontsize_properties) { + $fontsize_properties = array( + 'font' => true, + 'font-size' => true, + ); + $rem_patt = CssCrush_Regex::create('()rem', 'iS'); + } + + if (! array_intersect_key($rule->canonicalProperties, $fontsize_properties)) { + return; + } + + $new_set = array(); + $rule_updated = false; + foreach ($rule->declarations as $declaration) { + if ( + ! $declaration->skip && + isset($fontsize_properties[$declaration->canonicalProperty]) && + preg_match_all($rem_patt, $declaration->value, $m) + ) { + // Value has rem, create new declaration with rem value converted to pixel. + $find = $m[0]; + $replace = array(); + foreach ($m[1] as $num) { + $replace[] = round(floatval($num) * 16, 5) . 'px'; + } + $new_set[] = new CssCrush_Declaration( + $declaration->property, str_replace($find, $replace, $declaration->value)); + $rule_updated = true; + } + $new_set[] = $declaration; + } + + if ($rule_updated) { + $rule->setDeclarations($new_set); + } +} From 7074138648731c2f70b7c5f6691d9bfe25deae58 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 28 Mar 2013 08:59:21 +0000 Subject: [PATCH 089/421] Some refactoring for command line util. Reorganised options, and added ability to pass trailing input/output filepaths. Renamed rem2px plugin to rem-fallback. --- cli.php | 199 +++++++++++++++-------- lib/CssCrush/IO.php | 6 +- plugins/px2rem.php | 24 ++- plugins/{rem2px.php => rem-fallback.php} | 20 +-- plugins/svg.php | 2 +- 5 files changed, 165 insertions(+), 86 deletions(-) rename plugins/{rem2px.php => rem-fallback.php} (73%) diff --git a/cli.php b/cli.php index 3048c37..4c455a2 100755 --- a/cli.php +++ b/cli.php @@ -34,56 +34,57 @@ ################################################################## ## Resolve options. -$short_opts = array( - - // Required value arguments. - 'f:', // Input file. Defaults to SDTIN. - 'i:', // Input file alias. - 'o:', // Output file. Defaults to STDOUT. - - // Optional value arguments. - 'b::', // Output boilerplate (optional filepath). - - // Flags. - 'p', // Pretty (un-minified) output. - 'w', // Enable file watching mode. +$required_value_opts = array( + 'i|input|f|file', // Input file. Defaults to STDIN. + 'o|output', // Output file. Defaults to STDOUT. + 'E|enable' , // List of plugins to enable. + 'D|disable', // List of plugins to disable. + 'vars|variables', // Map of variable names in an http query string format. + 'formatter', // Formatter name for formatted output. + 'vendor-target', // Vendor target. + 'context', // Context for resolving URLs. + 'newlines', // Newline style. +); - // Deprecated (removed in 2.x). - 'h', // Display help. (deprecated) +$optional_value_opts = array( + 'b|boilerplate', // Boilerplate. ); -$long_opts = array( - - // Required value arguments. - 'formatter:', // Formatter name for formatted output. - 'vendor-target:', // Vendor target. - 'vars:', // Map of variable names in an http query string format. - 'enable:', // List of plugins to enable. - 'disable:', // List of plugins to disable. - 'context:', // Context for resolving URLs. - 'newlines:', // Newline style. - - // Optional value arguments. - 'boilerplate::', // Boilerplate alias. - - // Flags. - 'watch', // Watch mode alias. - 'pretty', // Pretty output alias. - 'help', // Display help. - 'version', // Display version. - 'trace', // Output sass tracing stubs. - - // Deprecated (removed in 2.x). - 'output:', // Output file alias. - 'file:', // Input file alias. - 'variables:', // Vars alias. +$flag_opts = array( + 'p|pretty', // Pretty output. + 'w|watch', // Watch mode. + 'help', // Display help. + 'version', // Display version. + 'trace', // Output sass tracing stubs. ); +// Create option strings for getopt(). +$short_opts = array(); +$long_opts = array(); +$join_opts = function ($opts_list, $modifier) use (&$short_opts, &$long_opts) { + foreach ($opts_list as $opt) { + foreach (explode('|', $opt) as $arg) { + if (strlen($arg) === 1) { + $short_opts[] = "$arg$modifier"; + } + else { + $long_opts[] = "$arg$modifier"; + } + } + } +}; +$join_opts($required_value_opts, ':'); +$join_opts($optional_value_opts, '::'); +$join_opts($flag_opts, ''); + + +// Parse opts. $opts = getopt(implode($short_opts), $long_opts); + $args = new stdClass(); // File arguments. -$args->input_file = pick($opts, 'f', 'i', 'file'); +$args->input_file = pick($opts, 'i', 'input', 'f', 'file'); $args->output_file = pick($opts, 'o', 'output'); $args->context = pick($opts, 'context'); @@ -104,8 +105,19 @@ $args->newlines = pick($opts, 'newlines'); // Arguments that require a value but accept multiple values. -$args->enable_plugins = pick($opts, 'enable'); -$args->disable_plugins = pick($opts, 'disable'); +$args->enable_plugins = pick($opts, 'E', 'enable'); +$args->disable_plugins = pick($opts, 'D', 'disable'); + +// Detect trailing IO files from raw script arguments. +list($trailing_input_file, $trailing_output_file) = get_trailing_io_args(); + +// If detected apply, not overriding explicit IO file options. +if (! $args->input_file && $trailing_input_file) { + $args->input_file = $trailing_input_file; +} +if (! $args->output_file && $trailing_output_file) { + $args->output_file = $trailing_output_file; +} ################################################################## @@ -113,8 +125,9 @@ // Validate filepath arguments. if ($args->input_file) { + $input_file = $args->input_file; if (! ($args->input_file = realpath($args->input_file))) { - stderr('Input file does not exist.'); + stderr("Input file '$input_file' does not exist."); exit(STATUS_ERROR); } @@ -300,7 +313,7 @@ if ($args->output_file) { - if (! @file_put_contents($args->output_file, $output)) { + if (! @file_put_contents($args->output_file, $output, LOCK_EX)) { $message[] = "Could not write to path '{$args->output_file}'."; stderr($message); @@ -401,46 +414,93 @@ function colorize ($str) { return str_replace($find, $replace, $str); } +function get_trailing_io_args () { + + $trailing_input_file = null; + $trailing_output_file = null; + + // Get raw script args, shift off calling scriptname and reduce to last three. + $trailing_args = $GLOBALS['argv']; + array_shift($trailing_args); + $trailing_args = array_slice($trailing_args, -3); + + // Create patterns to detecting options. + $required_values = implode('|', $GLOBALS['required_value_opts']); + $value_opt_patt = "~^-{1,2}($required_values)$~"; + $other_opt_patt = "~^-{1,2}([a-z0-9\-]+)?(=|$)~ix"; + + // Step through the args. + $filtered = array(); + for ($i = 0; $i < count($trailing_args); $i++) { + + $current = $trailing_args[$i]; + + // If tests as a required value option, reset and skip next. + if (preg_match($value_opt_patt, $current)) { + $filtered = array(); + $i++; + } + // If it looks like any other kind of flag, or optional value option, reset. + elseif (preg_match($other_opt_patt, $current)) { + $filtered = array(); + } + else { + $filtered[] = $current; + } + } + + // We're only interested in the last two values. + $filtered = array_slice($filtered, -2); + + switch (count($filtered)) { + case 1: + $trailing_input_file = $filtered[0]; + break; + case 2: + $trailing_input_file = $filtered[0]; + $trailing_output_file = $filtered[1]; + break; + } + + return array($trailing_input_file, $trailing_output_file); +} + function manpage () { $manpage = <<USAGE: - csscrush [-f|-i] [-o] [-p|--pretty] [-w|--watch] [-b|--boilerplate] - [--help] [--formatter] [--vars] [--vendor-target] - [--version] [--newlines] + csscrush [OPTIONS] [input-file] [output-file] OPTIONS: - -f, -i: - The input file. If omitted takes input from STDIN. + -i, --input: + Input file. If omitted takes input from STDIN. - -o: - The output file. If omitted prints to STDOUT. + -o, --output: + Output file. If omitted prints to STDOUT. - -p, --pretty: + -p, --pretty: Formatted, un-minified output. - -w, --watch: + -w, --watch: Watch input file for changes. Writes to file specified with -o option or to the input file directory with a '.crush.css' file extension. - -b, --boilerplate: - Whether or not to output a boilerplate. Optionally accepts filepath - to custom boilerplate template. - - --help: - Display this help mesasge. - - --context: - Filepath context for resolving URLs. - - --disable: + -D, --disable: List of plugins to disable. Pass 'all' to disable all. - --enable: + -E, --enable: List of plugins to enable. Overrides --disable. + --boilerplate: + Whether or not to output a boilerplate. Optionally accepts filepath + to a custom boilerplate template. + + --context: + Filepath context for resolving relative URLs. Only meaningful when + taking raw input from STDIN. + --formatter: Formatter to use for formatted (--pretty) output. Available formatters: @@ -452,6 +512,9 @@ function manpage () { 'padded' - Rules are printed in single lines with right padded selectors. + --help: + Display this help mesasge. + --newlines: Force newline style on output css. Defaults to the current platform newline. Possible values: 'windows' (or 'win'), 'unix', 'use-platform'. @@ -478,13 +541,13 @@ function manpage () { cat styles.css | csscrush --vars 'foo=black&bar=white' > alt-styles.css # Linting. - csscrush --pretty --enable property-sorter -i styles.css -o linted.css + csscrush --pretty --E property-sorter -i styles.css -o linted.css # Watch mode. csscrush --watch -i styles.css -o compiled/styles.css # Using custom boilerplate template. - csscrush --boilerplate=css/boilerplate.txt -i css/styles.css + csscrush --boilerplate=css/boilerplate.txt css/styles.css TPL; diff --git a/lib/CssCrush/IO.php b/lib/CssCrush/IO.php index f9fd321..f91fdfb 100644 --- a/lib/CssCrush/IO.php +++ b/lib/CssCrush/IO.php @@ -232,7 +232,7 @@ static public function getCacheData () // Create config file. CssCrush::log( 'Creating cache data file.' ); } - file_put_contents( $process->cacheFile, json_encode( array() ) ); + file_put_contents($process->cacheFile, json_encode(array()), LOCK_EX); } return $cache_data; @@ -243,14 +243,14 @@ static public function saveCacheData () $process = CssCrush::$process; CssCrush::log( 'Saving config.' ); - file_put_contents( $process->cacheFile, json_encode( $process->cacheData ) ); + file_put_contents( $process->cacheFile, json_encode( $process->cacheData ), LOCK_EX); } static public function write ( CssCrush_Stream $stream ) { $process = CssCrush::$process; $target = "{$process->output->dir}/{$process->output->filename}"; - if ( @file_put_contents( $target, $stream ) ) { + if ( @file_put_contents($target, $stream, LOCK_EX) ) { return "{$process->output->dirUrl}/{$process->output->filename}"; } else { diff --git a/plugins/px2rem.php b/plugins/px2rem.php index 12e0b5b..4dc066b 100644 --- a/plugins/px2rem.php +++ b/plugins/px2rem.php @@ -31,18 +31,34 @@ function csscrush__disable_px2rem () { } function csscrush_fn__px2rem ($input) { - return csscrush__px2rem($input, 'rem'); + + $base = 16; + + // Override default base if variable is set. + if (isset(CssCrush::$process->variables['px2rem__base'])) { + $base = CssCrush::$process->variables['px2rem__base']; + } + + return csscrush__px2rem($input, 'rem', $base); } function csscrush_fn__px2em ($input) { - return csscrush__px2rem($input, 'em'); + + $base = 16; + + // Override default base if variable is set. + if (isset(CssCrush::$process->variables['px2em__base'])) { + $base = CssCrush::$process->variables['px2em__base']; + } + + return csscrush__px2rem($input, 'em', $base); } -function csscrush__px2rem ($input, $unit) { +function csscrush__px2rem ($input, $unit, $default_base) { list($px, $base) = CssCrush_Function::parseArgsSimple($input) + array( 16, - 16, + $default_base, ); return round($px / $base, 5) . $unit; diff --git a/plugins/rem2px.php b/plugins/rem-fallback.php similarity index 73% rename from plugins/rem2px.php rename to plugins/rem-fallback.php index 37f34fc..85cb0bc 100644 --- a/plugins/rem2px.php +++ b/plugins/rem-fallback.php @@ -1,8 +1,8 @@ 'csscrush__enable_rem2px', - 'disable' => 'csscrush__disable_rem2px', +CssCrush_Plugin::register( 'rem-fallback', array( + 'enable' => 'csscrush__enable_rem_fallback', + 'disable' => 'csscrush__disable_rem_fallback', )); -function csscrush__enable_rem2px () { - CssCrush_Hook::add( 'rule_prealias', 'csscrush__rem2px' ); +function csscrush__enable_rem_fallback () { + CssCrush_Hook::add( 'rule_prealias', 'csscrush__rem_fallback' ); } -function csscrush__disable_rem2px () { - CssCrush_Hook::remove( 'rule_prealias', 'csscrush__rem2px' ); +function csscrush__disable_rem_fallback () { + CssCrush_Hook::remove( 'rule_prealias', 'csscrush__rem_fallback' ); } -function csscrush__rem2px (CssCrush_Rule $rule) { +function csscrush__rem_fallback (CssCrush_Rule $rule) { static $fontsize_properties, $rem_patt; if (! $fontsize_properties) { diff --git a/plugins/svg.php b/plugins/svg.php index eb0cec0..fab37f4 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -266,7 +266,7 @@ function csscrush__svg_generator ($input, $fn_name) { $generated_filename = substr(md5($flattened_svg), 0, 7) . ".$name.crush.svg"; $generated_path = $process->output->dir . '/' . $generated_filename; - file_put_contents($generated_path, $flattened_svg); + file_put_contents($generated_path, $flattened_svg, LOCK_EX); // Write to the same directory as the output css. $generated_url = $generated_filename; From 789fd7814387256a30360da7de7f459eacdd6db3 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 29 Mar 2013 14:11:28 +0000 Subject: [PATCH 090/421] Added option to cli for listing available plugins. Some refactoring of rule referencing. --- cli.php | 53 +++-- lib/CssCrush/Plugin.php | 33 ++- lib/CssCrush/Process.php | 480 +++++++++++++++++++-------------------- lib/CssCrush/Regex.php | 3 +- lib/CssCrush/Rule.php | 358 +++++++++++++++-------------- plugins/rem-fallback.php | 5 +- 6 files changed, 500 insertions(+), 432 deletions(-) diff --git a/cli.php b/cli.php index 4c455a2..6ff2ac2 100755 --- a/cli.php +++ b/cli.php @@ -53,6 +53,7 @@ $flag_opts = array( 'p|pretty', // Pretty output. 'w|watch', // Watch mode. + 'list', // List plugins. 'help', // Display help. 'version', // Display version. 'trace', // Output sass tracing stubs. @@ -91,6 +92,7 @@ // Flags. $args->pretty = isset($opts['p']) ?: isset($opts['pretty']); $args->watch = isset($opts['w']) ?: isset($opts['watch']); +$args->list = isset($opts['l']) ?: isset($opts['list']); $args->help = isset($opts['h']) ?: isset($opts['help']); $args->version = isset($opts['version']); $args->trace = isset($opts['trace']); @@ -120,6 +122,36 @@ } +################################################################## +## Information options. + +if ($args->version) { + + stdout('CSS-Crush ' . CssCrush::$config->version); + + exit(STATUS_OK); +} +elseif ($args->help) { + + stdout(manpage()); + + exit(STATUS_OK); +} +elseif ($args->list) { + + $plugins = array(); + + foreach (CssCrush_Plugin::info() as $name => $docs) { + // Use first line of plugin doc for description. + $headline = isset($docs[0]) ? $docs[0] : false; + $plugins[] = colorize("$name" . ($headline ? " - $headline" : '')); + } + stdout($plugins); + + exit(STATUS_OK); +} + + ################################################################## ## Filter option values. @@ -169,24 +201,6 @@ } -################################################################## -## Help and version info. - -if ($args->version) { - - stdout('CSS-Crush ' . CssCrush::$config->version); - - exit(STATUS_OK); -} - -elseif ($args->help) { - - stdout(manpage()); - - exit(STATUS_OK); -} - - ################################################################## ## Input. @@ -515,6 +529,9 @@ function manpage () { --help: Display this help mesasge. + --list: + Show plugins. + --newlines: Force newline style on output css. Defaults to the current platform newline. Possible values: 'windows' (or 'win'), 'unix', 'use-platform'. diff --git a/lib/CssCrush/Plugin.php b/lib/CssCrush/Plugin.php index 1230005..23ea874 100644 --- a/lib/CssCrush/Plugin.php +++ b/lib/CssCrush/Plugin.php @@ -8,9 +8,38 @@ class CssCrush_Plugin { static protected $plugins = array(); - static public function show () + static public function info () { - return self::$plugins; + $plugin_dirs = CssCrush::$config->plugin_dirs; + $plugin_data = array(); + + foreach ($plugin_dirs as $plugin_dir) { + + foreach (glob("$plugin_dir/*.php") as $path) { + $name = basename($path, '.php'); + $plugin_data += array($name => CssCrush_Plugin::parseDoc($path)); + } + } + + return $plugin_data; + } + + static public function parseDoc ($plugin_path) + { + $contents = file_get_contents($plugin_path); + if (preg_match('~/\*\*(.*?)\*/~s', $contents, $m)) { + + $lines = preg_split(CssCrush_Regex::$patt->newline, $m[1]); + foreach ($lines as &$line) { + $line = trim(ltrim($line, "* \t")); + } + // Remove empty strings and reset indexes. + $lines = array_values(array_filter($lines, 'strlen')); + + return $lines; + } + + return false; } static public function register ($plugin_name, $callbacks) diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 8ef6e23..aa70f83 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -6,7 +6,7 @@ */ class CssCrush_Process { - public function __construct ( $options ) + public function __construct ($options) { $config = CssCrush::$config; @@ -14,10 +14,10 @@ public function __construct ( $options ) CssCrush::loadAssets(); // Create options instance for this process. - $this->options = new CssCrush_Options( $options ); + $this->options = new CssCrush_Options($options); // Populate option defaults. - $this->options->merge( $config->options ); + $this->options->merge($config->options); // Keep track of global vars to maintain cache integrity. $this->options->global_vars = $config->vars; @@ -38,7 +38,7 @@ public function __construct ( $options ) 'p' => array(), // Parens 'u' => array(), // URLs 't' => array(), // Traces - ); + ); $this->variables = array(); $this->misc = new stdclass(); $this->input = new stdclass(); @@ -52,15 +52,15 @@ public function __construct ( $options ) // Shortcut commonly used options to avoid overhead with __get calls. $this->minifyOutput = $this->options->minify; - $this->addTracingStubs = in_array( 'stubs', $this->options->trace ); + $this->addTracingStubs = in_array('stubs', $this->options->trace); $this->ruleFormatter = $this->options->formatter; // Pick a doc root. - $this->docRoot = isset( $this->options->doc_root ) ? + $this->docRoot = isset($this->options->doc_root) ? $this->options->doc_root : $config->docRoot; // Shortcut the newline option and attach it to the process. - switch ( $this->options->newlines ) { + switch ($this->options->newlines) { case 'windows': case 'win': $this->newline = "\r\n"; @@ -76,7 +76,7 @@ public function __construct ( $options ) } // Run process_init hook. - CssCrush_Hook::run( 'process_init' ); + CssCrush_Hook::run('process_init'); } public function release () @@ -90,59 +90,59 @@ public function release () $this->plugins, $this->aliases, $this->selectorAliases - ); + ); } // Establish the input and output directories and optionally test output dir. - public function setContext ( $input_dir, $test_output_dir = true ) + public function setContext ($input_dir, $test_output_dir = true) { $doc_root = $this->docRoot; - if ( strpos( $input_dir, $doc_root ) !== 0 ) { + if (strpos($input_dir, $doc_root) !== 0) { // Not a system path. - $input_dir = realpath( "$doc_root/$input_dir" ); + $input_dir = realpath("$doc_root/$input_dir"); } // Initialise input object and store input directory. $this->input->path = null; $this->input->filename = null; $this->input->dir = $input_dir; - $this->input->dirUrl = substr( $this->input->dir, strlen( $doc_root ) ); + $this->input->dirUrl = substr($this->input->dir, strlen($doc_root)); // Store reference to the output dir. - $this->output->dir = $this->ioCall( 'getOutputDir' ); - $this->output->dirUrl = substr( $this->output->dir, strlen( $doc_root ) ); + $this->output->dir = $this->ioCall('getOutputDir'); + $this->output->dirUrl = substr($this->output->dir, strlen($doc_root)); // Test the output directory to see it exists and is writable. $output_dir_ok = false; - if ( $test_output_dir ) { - $output_dir_ok = $this->ioCall( 'testOutputDir' ); + if ($test_output_dir) { + $output_dir_ok = $this->ioCall('testOutputDir'); } // Setup the IO handler. - $this->ioCall( 'init' ); + $this->ioCall('init'); return $output_dir_ok; } - public function ioCall ( $method ) + public function ioCall ($method) { // Fetch the argument list, shift off the first item $args = func_get_args(); - array_shift( $args ); + array_shift($args); // The method address - $the_method = array( CssCrush::$config->io, $method ); + $the_method = array(CssCrush::$config->io, $method); // Return the call result - return call_user_func_array( $the_method, $args ); + return call_user_func_array($the_method, $args); } ############################# # Tokens. - public function createTokenLabel ( $type ) + public function createTokenLabel ($type) { $counter = ++$this->uid; return "?$type$counter?"; @@ -172,16 +172,16 @@ public function fetchToken ($token) return null; } - public function popToken ( $token ) + public function popToken ($token) { - $val = $this->fetchToken( $token ); - $this->releaseToken( $token ); + $val = $this->fetchToken($token); + $this->releaseToken($token); return $val; } - public function releaseToken ( $token ) + public function releaseToken ($token) { - unset( $this->tokens->{ $token[1] }[ $token ] ); + unset($this->tokens->{ $token[1] }[$token]); } public function restoreTokens ($str, $type = 'p', $release = false) @@ -209,25 +209,25 @@ public function restoreTokens ($str, $type = 'p', $release = false) ############################# # Parens. - public function captureParens ( &$str ) + public function captureParens (&$str) { static $callback; - if ( ! $callback ) { - $callback = create_function( '$m', 'return CssCrush::$process->addToken( $m[0], \'p\' );' ); + if (! $callback) { + $callback = create_function('$m', 'return CssCrush::$process->addToken($m[0], \'p\');'); } - $str = preg_replace_callback( CssCrush_Regex::$patt->balancedParens, $callback, $str ); + $str = preg_replace_callback(CssCrush_Regex::$patt->balancedParens, $callback, $str); } - public function restoreParens ( &$str, $release = true ) + public function restoreParens (&$str, $release = true) { $token_table =& $this->tokens->p; - foreach ( CssCrush_Regex::matchAll( CssCrush_Regex::$patt->p_token, $str ) as $m ) { + foreach (CssCrush_Regex::matchAll(CssCrush_Regex::$patt->p_token, $str) as $m) { $token = $m[0][0]; - if ( isset( $token_table[ $token ] ) ) { - $str = str_replace( $token, $token_table[ $token ], $str ); - if ( $release ) { - unset( $token_table[ $token ] ); + if (isset($token_table[$token])) { + $str = str_replace($token, $token_table[$token], $str); + if ($release) { + unset($token_table[$token]); } } } @@ -242,34 +242,34 @@ protected function getBoilerplate () $file = false; $boilerplate_option = $this->options->boilerplate; - if ( $boilerplate_option === true ) { + if ($boilerplate_option === true) { $file = CssCrush_Util::find( - 'CssCrush-local.boilerplate', 'CssCrush.boilerplate' ); + 'CssCrush-local.boilerplate', 'CssCrush.boilerplate'); } - elseif ( is_string( $boilerplate_option ) ) { - if ( file_exists( $boilerplate_option ) ) { + elseif (is_string($boilerplate_option)) { + if (file_exists($boilerplate_option)) { $file = $boilerplate_option; } } // Return an empty string if no file is found. - if ( ! $file ) { + if (! $file) { return ''; } // Load the file - $boilerplate = file_get_contents( $file ); + $boilerplate = file_get_contents($file); // Substitute any tags - if ( preg_match_all( '~\{\{([^}]+)\}\}~', $boilerplate, $boilerplate_matches ) ) { + if (preg_match_all('~\{\{([^}]+)\}\}~', $boilerplate, $boilerplate_matches)) { $tags = array( - 'datetime' => @date( 'Y-m-d H:i:s O' ), - 'year' => @date( 'Y' ), + 'datetime' => @date('Y-m-d H:i:s O'), + 'year' => @date('Y'), 'version' => 'v' . CssCrush::$config->version, - ); + ); - foreach ( $boilerplate_matches[0] as $index => $tag ) { + foreach ($boilerplate_matches[0] as $index => $tag) { $tag_name = $boilerplate_matches[1][$index]; $replacement = '?'; if (isset($tags[$tag_name])) { @@ -277,14 +277,14 @@ protected function getBoilerplate () } $replacements[] = $replacement; } - $boilerplate = str_replace( $boilerplate_matches[0], $replacements, $boilerplate ); + $boilerplate = str_replace($boilerplate_matches[0], $replacements, $boilerplate); } // Pretty print. $EOL = $this->newline; - $boilerplate = preg_split( '~[\t ]*(\r\n?|\n)[\t ]*~', $boilerplate ); - $boilerplate = array_map( 'trim', $boilerplate ); - $boilerplate = "$EOL * " . implode( "$EOL * ", $boilerplate ); + $boilerplate = preg_split('~[\t]*(\r\n?|\n)[\t]*~', $boilerplate); + $boilerplate = array_map('trim', $boilerplate); + $boilerplate = "$EOL * " . implode("$EOL * ", $boilerplate); return "/*{$boilerplate}$EOL */$EOL"; } @@ -295,17 +295,17 @@ protected function getBoilerplate () protected function resolveSelectorAliases () { static $callback; - if ( ! $callback ) { - $callback = create_function( '$m', 'CssCrush::$process->selectorAliases[ $m[1] ] = $m[2];' ); + if (! $callback) { + $callback = create_function('$m', 'CssCrush::$process->selectorAliases[$m[1]] = $m[2];'); } - $this->stream->pregReplaceCallback( CssCrush_Regex::$patt->selectorAlias, $callback ); + $this->stream->pregReplaceCallback(CssCrush_Regex::$patt->selectorAlias, $callback); // Merge in global selector aliases. $this->selectorAliases += CssCrush::$config->selectorAliases; // Create the selector aliases pattern and store it. - if ( $this->selectorAliases ) { - $names = implode( '|', array_keys( $this->selectorAliases ) ); + if ($this->selectorAliases) { + $names = implode('|', array_keys($this->selectorAliases)); $this->selectorAliasesPatt = '~ \:(' . $names . ')\b(?!-) (\()? @@ -313,7 +313,7 @@ protected function resolveSelectorAliases () } } - static public function applySelectorAliases ( &$str ) + static public function applySelectorAliases (&$str) { $process = CssCrush::$process; @@ -389,43 +389,43 @@ protected function filterAliases () $vendor = $this->options->vendor_target; // Default vendor argument, so use all aliases as normal. - if ( 'all' === $vendor ) { + if ('all' === $vendor) { return; } // For expicit 'none' argument turn off aliases. - if ( 'none' === $vendor ) { + if ('none' === $vendor) { $this->aliases = CssCrush::$config->bareAliasGroups; return; } // Normalize vendor_target argument. - $vendor = '-' . str_replace( '-', '', $vendor ) . '-'; + $vendor = '-' . str_replace('-', '', $vendor) . '-'; // Loop the aliases array, filter down to the target vendor. - foreach ( $this->aliases as $section => $group_array ) { + foreach ($this->aliases as $section => $group_array) { // Declarations aliases. - if ( $section === 'declarations' ) { - - foreach ( $group_array as $property => $values ) { - foreach ( $values as $value => $prefix_values ) { - foreach ( $prefix_values as $index => $declaration ) { - - list( $prop, $val ) = $declaration; - if ( !( - strpos( $prop, $vendor ) === 0 || - strpos( $val, $vendor ) === 0 - ) - ) { + if ($section === 'declarations') { + + foreach ($group_array as $property => $values) { + foreach ($values as $value => $prefix_values) { + foreach ($prefix_values as $index => $declaration) { + + list($prop, $val) = $declaration; + if (!( + strpos($prop, $vendor) === 0 || + strpos($val, $vendor) === 0 + ) + ) { // Unset uneeded aliases. - unset( $this->aliases[$section][$property][$value][$index] ); + unset($this->aliases[$section][$property][$value][$index]); - if ( empty( $this->aliases[$section][$property][$value] ) ) { - unset( $this->aliases[$section][$property][$value] ); + if (empty($this->aliases[$section][$property][$value])) { + unset($this->aliases[$section][$property][$value]); } - if ( empty( $this->aliases[$section][$property] ) ) { - unset( $this->aliases[$section][$property] ); + if (empty($this->aliases[$section][$property])) { + unset($this->aliases[$section][$property]); } } } @@ -434,13 +434,13 @@ protected function filterAliases () } // Function group aliases. - elseif ( $section === 'function_groups' ) { + elseif ($section === 'function_groups') { - foreach ( $group_array as $func_group => $vendors ) { - foreach ( $vendors as $fn_vendor => $replacements ) { + foreach ($group_array as $func_group => $vendors) { + foreach ($vendors as $fn_vendor => $replacements) { - if ( "-$fn_vendor-" !== $vendor ) { - unset( $this->aliases[ 'function_groups' ][ $func_group ][ $fn_vendor ] ); + if ("-$fn_vendor-" !== $vendor) { + unset($this->aliases['function_groups'][$func_group][$fn_vendor]); } } } @@ -448,26 +448,26 @@ protected function filterAliases () // Everything else. else { - foreach ( $group_array as $alias_keyword => $prefix_array ) { + foreach ($group_array as $alias_keyword => $prefix_array) { // Skip over pointers to function groups. - if ( $prefix_array[0] === ':' ) { + if ($prefix_array[0] === ':') { continue; } $result = array(); - foreach ( $prefix_array as $prefix ) { - if ( strpos( $prefix, $vendor ) === 0 ) { + foreach ($prefix_array as $prefix) { + if (strpos($prefix, $vendor) === 0) { $result[] = $prefix; } } // Prune the whole alias keyword if there is no result. - if ( empty( $result ) ) { - unset( $this->aliases[ $section ][ $alias_keyword ] ); + if (empty($result)) { + unset($this->aliases[$section][$alias_keyword]); } else { - $this->aliases[ $section ][ $alias_keyword ] = $result; + $this->aliases[$section][$alias_keyword] = $result; } } } @@ -484,32 +484,32 @@ protected function filterPlugins () $config = CssCrush::$config; // Checking for table keys is more convenient than array searching. - $disable = array_flip( $options->disable ); - $enable = array_flip( $options->enable ); + $disable = array_flip($options->disable); + $enable = array_flip($options->enable); // Disable has the special 'all' option. - if ( isset( $disable[ 'all' ] ) ) { + if (isset($disable['all'])) { $disable = $config->plugins; } // Remove option disabled plugins from the list, and disable them. - if ( $disable ) { - foreach ( $disable as $plugin_name => $index ) { - CssCrush_Plugin::disable( $plugin_name ); - unset( $this->plugins[ $plugin_name ] ); + if ($disable) { + foreach ($disable as $plugin_name => $index) { + CssCrush_Plugin::disable($plugin_name); + unset($this->plugins[$plugin_name]); } } // Secondly add option enabled plugins to the list. - if ( $enable ) { - foreach ( $enable as $plugin_name => $index ) { - $this->plugins[ $plugin_name ] = true; + if ($enable) { + foreach ($enable as $plugin_name => $index) { + $this->plugins[$plugin_name] = true; } } // Enable all plugins in the remaining list. - foreach ( $this->plugins as $plugin_name => $bool ) { - CssCrush_Plugin::enable( $plugin_name ); + foreach ($this->plugins as $plugin_name => $bool) { + CssCrush_Plugin::enable($plugin_name); } } @@ -523,32 +523,32 @@ protected function calculateVariables () $regex = CssCrush_Regex::$patt; $option_vars = $this->options->vars; - $this->stream->pregReplaceCallback( $regex->variables, - array( 'CssCrush_Process', 'cb_extractVariables' ) ); + $this->stream->pregReplaceCallback($regex->variables, + array('CssCrush_Process', 'cb_extractVariables')); // In-file variables override global variables. - $this->variables = array_merge( $config->vars, $this->variables ); + $this->variables = array_merge($config->vars, $this->variables); // Runtime variables override in-file variables. - if ( ! empty( $option_vars ) ) { - $this->variables = array_merge( $this->variables, $option_vars ); + if (! empty($option_vars)) { + $this->variables = array_merge($this->variables, $option_vars); } // Finally add state variables. // $this->variables = array( // 'input-path' => $this->input->dirUrl, // 'output-path' => $this->output->dirUrl, - // ) + $this->variables; + //) + $this->variables; // Place variables referenced inside variables. Excecute custom functions. - foreach ( $this->variables as $name => &$value ) { + foreach ($this->variables as $name => &$value) { // Referenced variables. - $value = preg_replace_callback( $regex->varFunction, array( 'self', 'cb_placeVariables' ), $value ); + $value = preg_replace_callback($regex->varFunction, array('self', 'cb_placeVariables'), $value); // Variable values can be escaped from function parsing with a tilde prefix. - if ( strpos( $value, '~' ) !== 0 ) { - CssCrush_Function::executeOnString( $value ); + if (strpos($value, '~') !== 0) { + CssCrush_Function::executeOnString($value); } } } @@ -556,15 +556,15 @@ protected function calculateVariables () protected function placeAllVariables () { // Place variables in main stream. - self::placeVariables( $this->stream->raw ); + self::placeVariables($this->stream->raw); // Repeat above steps for variables embedded in string tokens. - foreach ( $this->tokens->s as $label => &$value ) { - self::placeVariables( $value ); + foreach ($this->tokens->s as $label => &$value) { + self::placeVariables($value); } // Repeat above steps for variables embedded in URL tokens. - foreach ( $this->tokens->u as $label => $url ) { + foreach ($this->tokens->u as $label => $url) { if (! $url->isData && self::placeVariables($url->value)) { // Re-evaluate $url->value if anything has been interpolated. $url->evaluate(); @@ -572,19 +572,19 @@ protected function placeAllVariables () } } - static protected function placeVariables ( &$value ) + static protected function placeVariables (&$value) { $regex = CssCrush_Regex::$patt; // Variables with no default value. - $value = preg_replace_callback( $regex->varFunction, - array( 'CssCrush_Process', 'cb_placeVariables' ), $value, -1, $count ); + $value = preg_replace_callback($regex->varFunction, + array('CssCrush_Process', 'cb_placeVariables'), $value, -1, $count); - if ( strpos( $value, '$(' ) !== false ) { + if (strpos($value, '$(') !== false) { // Variables with default value. - CssCrush_Function::executeOnString( $value, '~(\$)\(~', - array( '$' => array( 'CssCrush_Process', 'cb_placeVariablesWithDefault' ) ) ); + CssCrush_Function::executeOnString($value, '~(\$)\(~', + array('$' => array('CssCrush_Process', 'cb_placeVariablesWithDefault'))); // Assume at least 1 replace. $count = 1; @@ -594,7 +594,7 @@ static protected function placeVariables ( &$value ) return $count; } - static public function cb_extractVariables ( $m ) + static public function cb_extractVariables ($m) { $regex = CssCrush_Regex::$patt; @@ -602,23 +602,23 @@ static public function cb_extractVariables ( $m ) $block = trim(CssCrush_Util::stripCommentTokens($m[1])); CssCrush::$process->variables = - array_merge( CssCrush::$process->variables, CssCrush_Util::parseBlock( $block, true ) ); + array_merge(CssCrush::$process->variables, CssCrush_Util::parseBlock($block, true)); } - static protected function cb_placeVariables ( $m ) + static protected function cb_placeVariables ($m) { $variable_name = $m[1]; - if ( isset( CssCrush::$process->variables[ $variable_name ] ) ) { - return CssCrush::$process->variables[ $variable_name ]; + if (isset(CssCrush::$process->variables[$variable_name])) { + return CssCrush::$process->variables[$variable_name]; } } - static public function cb_placeVariablesWithDefault ( $raw_arg ) + static public function cb_placeVariablesWithDefault ($raw_arg) { - list( $name, $default_value ) = CssCrush_Function::parseArgsSimple( $raw_arg ); + list($name, $default_value) = CssCrush_Function::parseArgsSimple($raw_arg); - if ( isset( CssCrush::$process->variables[ $name ] ) ) { - return CssCrush::$process->variables[ $name ]; + if (isset(CssCrush::$process->variables[$name])) { + return CssCrush::$process->variables[$name]; } else { return $default_value; @@ -631,29 +631,29 @@ static public function cb_placeVariablesWithDefault ( $raw_arg ) protected function resolveIfDefines () { - $matches = $this->stream->matchAll( CssCrush_Regex::$patt->ifDefine ); + $matches = $this->stream->matchAll(CssCrush_Regex::$patt->ifDefine); // Move through the matches last to first. - while ( $match = array_pop( $matches ) ) { + while ($match = array_pop($matches)) { - $curly_match = new CssCrush_BalancedMatch( $this->stream, $match[0][1] ); + $curly_match = new CssCrush_BalancedMatch($this->stream, $match[0][1]); - if ( ! $curly_match->match ) { + if (! $curly_match->match) { // Couldn't match the block. continue; } $negate = $match[1][1] != -1; $name = $match[2][0]; - $name_defined = isset( $this->variables[ $name ] ); + $name_defined = isset($this->variables[$name]); - if ( ! $negate && $name_defined || $negate && ! $name_defined ) { + if (! $negate && $name_defined || $negate && ! $name_defined) { // Test resolved true so include the innards. $curly_match->unWrap(); } else { // Recontruct the stream without the innards. - $curly_match->replace( '' ); + $curly_match->replace(''); } } } @@ -665,17 +665,17 @@ protected function resolveIfDefines () protected function extractMixins () { static $callback; - if ( ! $callback ) { - $callback = create_function( '$m', ' - $name = trim( $m[1] ); - $block = trim( $m[2] ); - if ( ! empty( $name ) && ! empty( $block ) ) { - CssCrush::$process->mixins[ $name ] = new CssCrush_Mixin( $block ); + if (! $callback) { + $callback = create_function('$m', ' + $name = trim($m[1]); + $block = trim($m[2]); + if (! empty($name) && ! empty($block)) { + CssCrush::$process->mixins[$name] = new CssCrush_Mixin($block); } - ' ); + '); } - $this->stream->pregReplaceCallback( CssCrush_Regex::$patt->mixin, $callback ); + $this->stream->pregReplaceCallback(CssCrush_Regex::$patt->mixin, $callback); } @@ -770,55 +770,55 @@ protected function resolveFragments () public function extractRules () { - $this->stream->pregReplaceCallback( CssCrush_Regex::$patt->rule, array( 'CssCrush_Process', 'cb_extractRules' ) ); + $this->stream->pregReplaceCallback(CssCrush_Regex::$patt->rule, array('CssCrush_Process', 'cb_extractRules')); } protected function processRules () { $aliases =& $this->aliases; - foreach ( $this->tokens->r as $rule ) { + foreach ($this->tokens->r as $rule) { $rule->processDeclarations(); - CssCrush_Hook::run( 'rule_prealias', $rule ); + CssCrush_Hook::run('rule_prealias', $rule); - if ( $aliases[ 'properties' ] ) { + if ($aliases['properties']) { $rule->addPropertyAliases(); } - if ( $aliases[ 'functions' ] ) { + if ($aliases['functions']) { $rule->addFunctionAliases(); } - if ( $aliases[ 'declarations' ] ) { + if ($aliases['declarations']) { $rule->addDeclarationAliases(); } - CssCrush_Hook::run( 'rule_postalias', $rule ); + CssCrush_Hook::run('rule_postalias', $rule); $rule->expandSelectors(); // Find previous selectors and apply them. $rule->applyExtendables(); - CssCrush_Hook::run( 'rule_postprocess', $rule ); + CssCrush_Hook::run('rule_postprocess', $rule); } } - static public function cb_extractRules ( $m ) + static public function cb_extractRules ($m) { $rule = (object) array(); - $rule->selector_raw = trim( $m[1] ); - $rule->declaration_raw = trim( $m[2] ); + $rule->selector_raw = trim($m[1]); + $rule->declaration_raw = trim($m[2]); // Run rule_preprocess hook. - CssCrush_Hook::run( 'rule_preprocess', $rule ); + CssCrush_Hook::run('rule_preprocess', $rule); - $rule = new CssCrush_Rule( $rule->selector_raw, $rule->declaration_raw ); + $rule = new CssCrush_Rule($rule->selector_raw, $rule->declaration_raw); // Store rules if they have declarations or extend arguments. - if ( ! empty( $rule->declarations ) || $rule->extendArgs ) { + if (! empty($rule->declarations) || $rule->extendArgs) { - CssCrush::$process->tokens->r[ $rule->label ] = $rule; + CssCrush::$process->tokens->r[$rule->label] = $rule; // If only using extend still return a label. return $rule->label; @@ -831,62 +831,62 @@ static public function cb_extractRules ( $m ) protected function prefixSelectors () { - $matches = $this->stream->matchAll( '~@in\s+([^{]+)\{~iS' ); + $matches = $this->stream->matchAll('~@in\s+([^{]+)\{~iS'); // Move through the matches in reverse order. - while ( $match = array_pop( $matches ) ) { + while ($match = array_pop($matches)) { $match_start_pos = $match[0][1]; - $raw_argument = trim( $match[1][0] ); + $raw_argument = trim($match[1][0]); - CssCrush_Process::applySelectorAliases( $raw_argument ); + CssCrush_Process::applySelectorAliases($raw_argument); - $this->captureParens( $raw_argument ); - $arguments = CssCrush_Util::splitDelimList( $raw_argument ); + $this->captureParens($raw_argument); + $arguments = CssCrush_Util::splitDelimList($raw_argument); - $curly_match = new CssCrush_BalancedMatch( $this->stream, $match_start_pos ); + $curly_match = new CssCrush_BalancedMatch($this->stream, $match_start_pos); - if ( ! $curly_match->match || empty( $raw_argument ) ) { + if (! $curly_match->match || empty($raw_argument)) { // Couldn't match the block. continue; } // Match all the rule tokens. $rule_matches = CssCrush_Regex::matchAll( - CssCrush_Regex::$patt->r_token, $curly_match->inside() ); + CssCrush_Regex::$patt->r_token, $curly_match->inside()); - foreach ( $rule_matches as $rule_match ) { + foreach ($rule_matches as $rule_match) { // Get the rule instance. - $rule = CssCrush_Rule::get( $rule_match[0][0] ); + $rule = CssCrush_Rule::get($rule_match[0][0]); // Using arguments create new selector list for the rule. $new_selector_list = array(); - foreach ( $arguments as $arg_selector ) { + foreach ($arguments as $arg_selector) { - foreach ( $rule->selectors as $rule_selector ) { + foreach ($rule->selectors as $rule_selector) { - $use_parent_symbol = strpos( $rule_selector->value, '&' ) !== false; + $use_parent_symbol = strpos($rule_selector->value, '&') !== false; // Skipping the prefix. - if ( ! $rule_selector->allowPrefix && ! $use_parent_symbol ) { + if (! $rule_selector->allowPrefix && ! $use_parent_symbol) { - $new_selector_list[ $rule_selector->readableValue ] = $rule_selector; + $new_selector_list[$rule_selector->readableValue] = $rule_selector; } // Positioning the prefix with parent symbol "&". - elseif ( $use_parent_symbol ) { + elseif ($use_parent_symbol) { // Find and replace the ampersand (only once). $new_value = preg_replace( '~&~', $arg_selector, $rule_selector->value, - 1 ); + 1); // Not storing the selector as named. - $new_selector_list[] = new CssCrush_Selector( $new_value ); + $new_selector_list[] = new CssCrush_Selector($new_value); } // Prepending the prefix. @@ -894,7 +894,7 @@ protected function prefixSelectors () // Not storing the selector as named. $new_selector_list[] - = new CssCrush_Selector( "$arg_selector {$rule_selector->value}" ); + = new CssCrush_Selector("$arg_selector {$rule_selector->value}"); } } } @@ -911,23 +911,23 @@ protected function prefixSelectors () protected function aliasAtRules () { - if ( empty( $this->aliases[ 'at-rules' ] ) ) { + if (empty($this->aliases['at-rules'])) { return; } - $aliases = $this->aliases[ 'at-rules' ]; + $aliases = $this->aliases['at-rules']; $regex = CssCrush_Regex::$patt; - foreach ( $aliases as $at_rule => $at_rule_aliases ) { + foreach ($aliases as $at_rule => $at_rule_aliases) { - $matches = $this->stream->matchAll( "~@$at_rule" . '[\s{]~i' ); + $matches = $this->stream->matchAll("~@$at_rule" . '[\s{]~i'); // Find at-rules that we want to alias. - while ( $match = array_pop( $matches ) ) { + while ($match = array_pop($matches)) { - $curly_match = new CssCrush_BalancedMatch( $this->stream, $match[0][1] ); + $curly_match = new CssCrush_BalancedMatch($this->stream, $match[0][1]); - if ( ! $curly_match->match ) { + if (! $curly_match->match) { // Couldn't match the block. continue; } @@ -936,46 +936,46 @@ protected function aliasAtRules () $original_block = $curly_match->whole(); $new_blocks = array(); - foreach ( $at_rule_aliases as $alias ) { + foreach ($at_rule_aliases as $alias) { // Copy original block, replacing at-rule with alias name. - $copy_block = str_replace( "@$at_rule", "@$alias", $original_block ); + $copy_block = str_replace("@$at_rule", "@$alias", $original_block); // Aliases are nearly always prefixed, capture the current vendor name. - preg_match( $regex->vendorPrefix, $alias, $vendor ); + preg_match($regex->vendorPrefix, $alias, $vendor); $vendor = $vendor ? $vendor[1] : null; // Duplicate rules. - if ( preg_match_all( $regex->r_token, $copy_block, $copy_matches ) ) { + if (preg_match_all($regex->r_token, $copy_block, $copy_matches)) { $originals = array(); $replacements = array(); - foreach ( $copy_matches[0] as $copy_match ) { + foreach ($copy_matches[0] as $copy_match) { // Clone the matched rule. $originals[] = $rule_label = $copy_match; - $cloneRule = clone $this->tokens->r[ $rule_label ]; + $cloneRule = clone $this->tokens->r[$rule_label]; // Set the vendor context. $cloneRule->vendorContext = $vendor; // Filter out declarations that have different vendor context. $new_set = array(); - foreach ( $cloneRule as $declaration ) { - if ( ! $declaration->vendor || $declaration->vendor === $vendor ) { + foreach ($cloneRule as $declaration) { + if (! $declaration->vendor || $declaration->vendor === $vendor) { $new_set[] = $declaration; } } - $cloneRule->setDeclarations( $new_set ); + $cloneRule->setDeclarations($new_set); // Store the clone. - $replacements[] = $this->addToken( $cloneRule, 'r' ); + $replacements[] = $this->addToken($cloneRule, 'r'); } // Finally replace the original labels with the cloned rule labels. - $copy_block = str_replace( $originals, $replacements, $copy_block ); + $copy_block = str_replace($originals, $replacements, $copy_block); } // Add the copied block to the stack. @@ -986,7 +986,7 @@ protected function aliasAtRules () $new_blocks[] = $original_block; // Splice in the blocks. - $curly_match->replace( implode( "\n", $new_blocks ) ); + $curly_match->replace(implode("\n", $new_blocks)); } } } @@ -1004,34 +1004,34 @@ protected function collate () $EOL = $this->newline; // Strip newlines added during processing. - $regex_replacements[ '~\n+~' ] = ''; + $regex_replacements['~\n+~'] = ''; - if ( $minify ) { + if ($minify) { // Strip whitespace around colons used in @-rule arguments. - $regex_replacements[ '~ ?\: ?~' ] = ':'; + $regex_replacements['~ ?\: ?~'] = ':'; } else { // Pretty printing. - $regex_replacements[ '~}~' ] = "$0$EOL$EOL"; - $regex_replacements[ '~([^\s])\{~' ] = "$1 {"; - $regex_replacements[ '~ ?(@[^{]+\{)~' ] = "$1$EOL"; - $regex_replacements[ '~ ?(@[^;]+\;)~' ] = "$1$EOL"; + $regex_replacements['~}~'] = "$0$EOL$EOL"; + $regex_replacements['~([^\s])\{~'] = "$1 {"; + $regex_replacements['~ ?(@[^{]+\{)~'] = "$1$EOL"; + $regex_replacements['~ ?(@[^;]+\;)~'] = "$1$EOL"; } // Apply all replacements. - $this->stream->pregReplaceHash( $regex_replacements )->lTrim(); + $this->stream->pregReplaceHash($regex_replacements)->lTrim(); // Print out rules. - $this->stream->replaceHash( $this->tokens->r ); - CssCrush::runStat( 'selector_count' ); - CssCrush::runStat( 'rule_count' ); + $this->stream->replaceHash($this->tokens->r); + CssCrush::runStat('selector_count'); + CssCrush::runStat('rule_count'); // Insert parens. - $this->stream->replaceHash( $this->tokens->p ); + $this->stream->replaceHash($this->tokens->p); // Advanced minification parameters. - if ( is_array( $minify ) ) { - if ( in_array( 'colors', $minify ) ) { + if (is_array($minify)) { + if (in_array('colors', $minify)) { $this->minifyColors(); } } @@ -1039,22 +1039,22 @@ protected function collate () // Compress hex-codes, collapse TRBL lists etc. $this->decruft(); - if ( $minify ) { + if ($minify) { // Trim whitespace around selector combinators. - $this->stream->pregReplace( '~ ?([>\~+]) ?~S', '$1' ); + $this->stream->pregReplace('~ ?([>\~+]) ?~S', '$1'); } else { // Add newlines after comments. - foreach ( $this->tokens->c as $token => &$comment ) { + foreach ($this->tokens->c as $token => &$comment) { $comment .= "$EOL$EOL"; } // Insert comments and do final whitespace cleanup. $this->stream - ->replaceHash( $this->tokens->c ) + ->replaceHash($this->tokens->c) ->trim() - ->append( $EOL ); + ->append($EOL); } // Insert URLs. @@ -1079,23 +1079,23 @@ protected function collate () } // Insert string literals. - $this->stream->replaceHash( $this->tokens->s ); + $this->stream->replaceHash($this->tokens->s); // Add in boilerplate. - if ( $options->boilerplate ) { - $this->stream->prepend( $this->getBoilerplate() ); + if ($options->boilerplate) { + $this->stream->prepend($this->getBoilerplate()); } // Add @charset at top if set. - if ( $this->charset ) { - $this->stream->prepend( "@charset \"$this->charset\";$EOL" ); + if ($this->charset) { + $this->stream->prepend("@charset \"$this->charset\";$EOL"); } } public function compile () { // Always store start time. - $this->stat[ 'compile_start_time' ] = microtime( true ); + $this->stat['compile_start_time'] = microtime(true); // Resolve active aliases and plugins. $this->filterPlugins(); @@ -1105,7 +1105,7 @@ public function compile () CssCrush_Function::setMatchPatt(); // Collate hostfile and imports. - $this->stream = new CssCrush_Stream( CssCrush_Importer::hostfile( $this->input ) ); + $this->stream = new CssCrush_Stream(CssCrush_Importer::hostfile($this->input)); // Extract and calculate variables. $this->calculateVariables(); @@ -1126,19 +1126,19 @@ public function compile () $this->resolveFragments(); // Run extract phase hooks. - CssCrush_Hook::run( 'process_extract', $this ); + CssCrush_Hook::run('process_extract', $this); // Adjust meta characters so we can extract the rules cleanly. - $this->stream->replaceHash( array( + $this->stream->replaceHash(array( '@' => "\n@", '}' => "}\n", '{' => "{\n", ';' => ";\n", - ))->prepend( "\n" ); + ))->prepend("\n"); // Parse rules. $this->extractRules(); - // csscrush::log(array_keys( $this->references) ); + // csscrush::log($this->references); // Process @in blocks. $this->prefixSelectors(); @@ -1155,7 +1155,7 @@ public function compile () // Release memory. $this->release(); - CssCrush::runStat( 'compile_time' ); + CssCrush::runStat('compile_time'); return $this->stream; } @@ -1169,7 +1169,7 @@ protected function decruft () $patt =& CssCrush_Regex::$patt; $classes =& CssCrush_Regex::$classes; - return $this->stream->pregReplaceHash( array( + return $this->stream->pregReplaceHash(array( // Strip leading zeros on floats. '~([: \(,])(-?)0(\.\d+)~S' => '$1$2$3', @@ -1189,7 +1189,7 @@ protected function decruft () // Compress hex codes. $patt->cruftyHex => '#$1$2$3', - )); + )); } @@ -1206,15 +1206,15 @@ protected function minifyColors () $keywords_patt = '~(?(rgb|hsl)\(([^\)]{5,})\)', 'iS'); $functions_callback = create_function('$m', ' - $args = CssCrush_Function::parseArgs( trim( $m[2] ) ); - if ( stripos( $m[1], \'hsl\' ) === 0 ) { - $args = CssCrush_Color::cssHslToRgb( $args ); + $args = CssCrush_Function::parseArgs(trim($m[2])); + if (stripos($m[1], \'hsl\') === 0) { + $args = CssCrush_Color::cssHslToRgb($args); } - return CssCrush_Color::rgbToHex( $args ); + return CssCrush_Color::rgbToHex($args); '); } diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php index 165c4de..85e6256 100644 --- a/lib/CssCrush/Regex.php +++ b/lib/CssCrush/Regex.php @@ -43,6 +43,7 @@ static public function init () // Misc. $classes->vendor = '-[a-zA-Z]+-'; + $classes->newline = '(?:\r\n?|\n)'; // Create standalone class patterns, add classes as class swaps. foreach ($classes as $name => $class) { @@ -97,7 +98,7 @@ static public function init () // Misc. $patt->vendorPrefix = '~^-([a-z]+)-([a-z-]+)~iS'; - $patt->ruleDirective = '~^(?:(@include|mixin)|(@?extends?)|(@name))[\s\:]+~iS'; + $patt->ruleDirective = '~^(?:(@include)|(@extends?)|(@name))[\s]+~iS'; $patt->argListSplit = '~\s*[,\s]\s*~S'; $patt->mathBlacklist = '~[^\.0-9\*\/\+\-\(\)]~S'; $patt->cruftyHex = '~\#([[:xdigit:]])\1([[:xdigit:]])\2([[:xdigit:]])\3~S'; diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php index aa76128..236c70a 100644 --- a/lib/CssCrush/Rule.php +++ b/lib/CssCrush/Rule.php @@ -11,6 +11,7 @@ class CssCrush_Rule implements IteratorAggregate public $tracingStub; public $selectors = array(); + public $extendSelectors = array(); public $declarations = array(); // The comments associated with the rule. @@ -29,87 +30,87 @@ class CssCrush_Rule implements IteratorAggregate // Declarations hash table for external query() referencing. public $queryData = array(); - public function __construct ( $selector_string = null, $declarations_string ) + public function __construct ($selector_string = null, $declarations_string) { $regex = CssCrush_Regex::$patt; $process = CssCrush::$process; - $this->label = $process->createTokenLabel( 'r' ); + $this->label = $process->createTokenLabel('r'); // If tracing store the last tracing stub, then strip all. if ( $process->addTracingStubs && - preg_match_all( $regex->t_token, $selector_string, $trace_tokens ) - ) { - $trace_token = array_pop( $trace_tokens ); - $this->tracingStub = $process->fetchToken( $trace_token[0] ); - foreach ( $trace_tokens as $trace_token ) { - $process->releaseToken( $trace_token[0] ); + preg_match_all($regex->t_token, $selector_string, $trace_tokens) + ) { + $trace_token = array_pop($trace_tokens); + $this->tracingStub = $process->fetchToken($trace_token[0]); + foreach ($trace_tokens as $trace_token) { + $process->releaseToken($trace_token[0]); } - $selector_string = preg_replace( $regex->t_token, '', $selector_string ); + $selector_string = preg_replace($regex->t_token, '', $selector_string); } // Parse the selectors chunk - if ( ! empty( $selector_string ) ) { + if (! empty($selector_string)) { - $selectors = CssCrush_Util::splitDelimList( $selector_string ); + $selectors = CssCrush_Util::splitDelimList($selector_string); // Remove and store comments that sit above the first selector // remove all comments between the other selectors - if ( strpos( $selectors[0], '?c' ) !== false ) { - preg_match_all( $regex->c_token, $selectors[0], $m ); + if (strpos($selectors[0], '?c') !== false) { + preg_match_all($regex->c_token, $selectors[0], $m); $this->comments = $m[0]; } // Strip any other comments then create selector instances - foreach ( $selectors as $selector ) { + foreach ($selectors as $selector) { - $selector = trim( CssCrush_Util::stripCommentTokens( $selector ) ); + $selector = trim(CssCrush_Util::stripCommentTokens($selector)); // If the selector matches an absract directive - if ( preg_match( $regex->abstract, $selector, $m ) ) { + if (preg_match($regex->abstract, $selector, $m)) { $abstract_name = $m[1]; // Link the rule to the abstract name and skip forward to declaration parsing. - $process->references[ $abstract_name ] = $this; + $process->references[$abstract_name] = $this; } else { - $this->addSelector( new CssCrush_Selector( $selector ) ); + $this->addSelector(new CssCrush_Selector($selector)); } } } // Parse the declarations chunk. - $declarations_string = trim( CssCrush_Util::stripCommentTokens( $declarations_string ) ); - $declarations = preg_split( '~\s*;\s*~', $declarations_string, null, PREG_SPLIT_NO_EMPTY ); + $declarations_string = trim(CssCrush_Util::stripCommentTokens($declarations_string)); + $declarations = preg_split('~\s*;\s*~', $declarations_string, null, PREG_SPLIT_NO_EMPTY); // First create a simple array of all properties and value pairs in raw state $pairs = array(); // Split declarations in to property/value pairs - foreach ( $declarations as $declaration ) { + foreach ($declarations as $declaration) { // Rule directives. Accept several different syntaxes for mixin and extends. - if ( preg_match( $regex->ruleDirective, $declaration, $m ) ) { + if (preg_match($regex->ruleDirective, $declaration, $m)) { - if ( ! empty( $m[1] ) ) { + if (! empty($m[1])) { $prop = 'mixin'; } - elseif ( ! empty( $m[2] ) ) { + elseif (! empty($m[2])) { $prop = 'extends'; } else { $prop = 'name'; } - $value = trim( substr( $declaration, strlen( $m[0] ) ) ); + $value = trim(substr($declaration, strlen($m[0]))); } - elseif ( ( $colonPos = strpos( $declaration, ':' ) ) !== false ) { + elseif (($colonPos = strpos($declaration, ':')) !== false) { - $prop = trim( substr( $declaration, 0, $colonPos ) ); + $prop = trim(substr($declaration, 0, $colonPos)); // Extract the value part of the declaration. - $value = trim( substr( $declaration, $colonPos + 1 ) ); + $value = trim(substr($declaration, $colonPos + 1)); } else { // Must be malformed. @@ -117,53 +118,53 @@ public function __construct ( $selector_string = null, $declarations_string ) } // Reject empty values. - if ( empty( $prop ) || ! strlen( $value ) ) { + if (empty($prop) || ! strlen($value)) { continue; } - if ( $prop === 'mixin' ) { + if ($prop === 'mixin') { // Mixins are a special case. - if ( $mixin_declarations = CssCrush_Mixin::parseValue( $value ) ) { + if ($mixin_declarations = CssCrush_Mixin::parseValue($value)) { // Add mixin declarations to the stack. - while ( $mixin_declaration = array_shift( $mixin_declarations ) ) { + while ($mixin_declaration = array_shift($mixin_declarations)) { - $pairs[] = array( $mixin_declaration['property'], $mixin_declaration['value'] ); + $pairs[] = array($mixin_declaration['property'], $mixin_declaration['value']); } } } - elseif ( $prop === 'extends' ) { + elseif ($prop === 'extends') { // Extends are also a special case. - $this->setExtendSelectors( $value ); + $this->setExtendSelectors($value); } - elseif ( $prop === 'name' ) { + elseif ($prop === 'name') { // Link the rule as a reference. - $process->references[ $value ] = $this; + $process->references[$value] = $this; } else { - $pairs[] = array( $prop, $value ); + $pairs[] = array($prop, $value); } } // Bind declaration objects on the rule. - foreach ( $pairs as $index => &$pair ) { + foreach ($pairs as $index => &$pair) { - list( $prop, $value ) = $pair; + list($prop, $value) = $pair; - if ( trim( $value ) !== '' ) { + if (trim($value) !== '') { // Only store to $this->selfData if the value does not itself make a // this() call to avoid circular references. - if ( ! preg_match( CssCrush_Regex::$patt->thisFunction, $value ) ) { - $this->selfData[ strtolower( $prop ) ] = $value; + if (! preg_match(CssCrush_Regex::$patt->thisFunction, $value)) { + $this->selfData[strtolower($prop)] = $value; } // Add declaration. - $this->addDeclaration( $prop, $value, $index ); + $this->addDeclaration($prop, $value, $index); } } } @@ -172,47 +173,50 @@ public function __toString () { $process = CssCrush::$process; + // Merge the extend selectors. + $this->selectors += $this->extendSelectors; + // If there are no selectors or declarations associated with the rule return empty string. - if ( empty( $this->selectors ) || empty( $this->declarations ) ) { + if (empty($this->selectors) || empty($this->declarations)) { // De-reference this instance. - unset( $process->tokens->r[ $this->label ] ); + unset($process->tokens->r[$this->label]); return ''; } // Concat and return. - if ( $process->minifyOutput ) { - $selectors = implode( ',', $this->selectors ); - $block = implode( ';', $this->declarations ); + if ($process->minifyOutput) { + $selectors = implode(',', $this->selectors); + $block = implode(';', $this->declarations); return "$this->tracingStub$selectors{{$block}}"; } else { - if ( $formatter = $process->ruleFormatter ) { - return $formatter( $this ); + if ($formatter = $process->ruleFormatter) { + return $formatter($this); } // Default block formatter. - return csscrush__fmtr_block( $this ); + return csscrush__fmtr_block($this); } } public $declarationsProcessed = false; public function processDeclarations () { - if ( $this->declarationsProcessed ) { + if ($this->declarationsProcessed) { return; } - foreach ( $this->declarations as $index => $declaration ) { + foreach ($this->declarations as $index => $declaration) { // Execute functions, store as data etc. - $declaration->process( $this ); + $declaration->process($this); // Drop declaration if value is now empty. - if ( ! empty( $declaration->inValid ) ) { - unset( $this->declarations[ $index ] ); + if (! empty($declaration->inValid)) { + unset($this->declarations[$index]); } } @@ -247,7 +251,7 @@ public function expandDataSet ($dataset, $property) 'border-right-color' => 'border-color', 'border-bottom-color' => 'border-color', 'border-left-color' => 'border-color', - ); + ); $dataset =& $this->{$dataset}; $property_group = isset($expandables[$property]) ? $expandables[$property] : null; @@ -307,7 +311,7 @@ public function expandDataSet ($dataset, $property) 'top-right', 'bottom-right', 'bottom-left', - ); + ); } else { $positions = array( @@ -315,7 +319,7 @@ public function expandDataSet ($dataset, $property) 'right', 'bottom', 'left', - ); + ); } foreach ($positions as $index => $position) { @@ -329,69 +333,82 @@ public function expandDataSet ($dataset, $property) ############################# # Rule inheritance. - public function setExtendSelectors ( $raw_value ) + public function setExtendSelectors ($raw_value) { // Reset if called earlier, last call wins by intention. $this->extendArgs = array(); - foreach ( CssCrush_Util::splitDelimList( $raw_value ) as $arg ) { - $this->extendArgs[] = new CssCrush_ExtendArg( $arg ); + foreach (CssCrush_Util::splitDelimList($raw_value) as $arg) { + $this->extendArgs[] = new CssCrush_ExtendArg($arg); } } - public function applyExtendables () - { - if ( ! $this->extendArgs ) { - return; + public $resolvedExtendables = false; + public function resolveExtendables () { + + if (! $this->extendArgs || $this->resolvedExtendables) { + + return false; } $references =& CssCrush::$process->references; - // Filter the extendArgs list to usable references - foreach ( $this->extendArgs as $key => $extend_arg ) { + // Filter the extendArgs list to usable references. + $filtered = array(); + foreach ($this->extendArgs as $key => $extend_arg) { $name = $extend_arg->name; - if ( isset( $references[ $name ] ) ) { + if (isset($references[$name])) { - $parent_rule = $references[ $name ]; + $parent_rule = $references[$name]; + $parent_rule->resolveExtendables(); $extend_arg->pointer = $parent_rule; + $filtered[$parent_rule->label] = $extend_arg; } - else { + } - // Unusable, so unset it - unset( $this->extendArgs[ $key ] ); - } + $this->resolvedExtendables = true; + $this->extendArgs = $filtered; + + return true; + } + + public function applyExtendables () + { + if (! $this->resolveExtendables()) { + + return; } - // Create a stack of all parent rule args + // Create a stack of all parent rule args. $parent_extend_args = array(); - foreach ( $this->extendArgs as $extend_arg ) { - $parent_extend_args = array_merge( $parent_extend_args, $extend_arg->pointer->extendArgs ); + foreach ($this->extendArgs as $extend_arg) { + $parent_extend_args += $extend_arg->pointer->extendArgs; } - // Merge this rule's extendArgs with parent extendArgs - $this->extendArgs = array_merge( $this->extendArgs, $parent_extend_args ); + // Merge this rule's extendArgs with parent extendArgs. + $this->extendArgs += $parent_extend_args; - // Add this rule's selectors to all extendArgs - foreach ( $this->extendArgs as $extend_arg ) { + // Add this rule's selectors to all extendArgs. + foreach ($this->extendArgs as $extend_arg) { $ancestor = $extend_arg->pointer; $extend_selectors = $this->selectors; - // If there is a pseudo class extension create a new set accordingly - if ( $extend_arg->pseudo ) { + // If there is a pseudo class extension create a new set accordingly. + if ($extend_arg->pseudo) { $extend_selectors = array(); - foreach ( $this->selectors as $readable => $selector ) { + foreach ($this->selectors as $readable => $selector) { $new_selector = clone $selector; - $new_readable = $new_selector->appendPseudo( $extend_arg->pseudo ); - $extend_selectors[ $new_readable ] = $new_selector; + $new_readable = $new_selector->appendPseudo($extend_arg->pseudo); + $extend_selectors[$new_readable] = $new_selector; } } - $ancestor->addSelectors( $extend_selectors ); + $ancestor->addExtendSelectors($extend_selectors); } } @@ -409,17 +426,17 @@ public function expandSelectors () $reg_comma = '~\s*,\s*~'; } - foreach ( $this->selectors as $readableValue => $selector ) { + foreach ($this->selectors as $readableValue => $selector) { - $pos = stripos( $selector->value, ':any?' ); + $pos = stripos($selector->value, ':any?'); - if ( $pos !== false ) { + if ($pos !== false) { // Contains an :any statement so we expand $chain = array(''); do { - if ( $pos === 0 ) { - preg_match( $any_patt, $selector->value, $m ); + if ($pos === 0) { + preg_match($any_patt, $selector->value, $m); // Parse the arguments $expression = CssCrush::$process->tokens->p[$m[1]]; @@ -430,11 +447,11 @@ public function expandSelectors () // Test for nested :any() expressions. $has_nesting = stripos($expression, ':any(') !== false; - $parts = preg_split( $reg_comma, $expression, null, PREG_SPLIT_NO_EMPTY ); + $parts = preg_split($reg_comma, $expression, null, PREG_SPLIT_NO_EMPTY); $tmp = array(); - foreach ( $chain as $rowCopy ) { - foreach ( $parts as $part ) { + foreach ($chain as $rowCopy) { + foreach ($parts as $part) { // Flatten nested :any() expressions in a hacky kind of way. if ($has_nesting) { @@ -450,27 +467,27 @@ public function expandSelectors () } } $chain = $tmp; - $selector->value = substr( $selector->value, strlen( $m[0] ) ); + $selector->value = substr($selector->value, strlen($m[0])); } else { - foreach ( $chain as &$row ) { - $row .= substr( $selector->value, 0, $pos ); + foreach ($chain as &$row) { + $row .= substr($selector->value, 0, $pos); } - $selector->value = substr( $selector->value, $pos ); + $selector->value = substr($selector->value, $pos); } - } while ( ( $pos = stripos( $selector->value, ':any?' ) ) !== false ); + } while (($pos = stripos($selector->value, ':any?')) !== false); // Finish off - foreach ( $chain as &$row ) { + foreach ($chain as &$row) { // Not creating a named rule association with this expanded selector - $new_set[] = new CssCrush_Selector( $row . $selector->value ); + $new_set[] = new CssCrush_Selector($row . $selector->value); } } else { // Nothing to expand - $new_set[ $readableValue ] = $selector; + $new_set[$readableValue] = $selector; } } // foreach @@ -478,17 +495,22 @@ public function expandSelectors () $this->selectors = $new_set; } - public function addSelector ( $selector ) + public function addSelector ($selector) { - $this->selectors[ $selector->readableValue ] = $selector; + $this->selectors[$selector->readableValue] = $selector; // Link reference. - CssCrush::$process->references[ $selector->readableValue ] = $this; + CssCrush::$process->references[$selector->readableValue] = $this; } - public function addSelectors ( $list ) + public function addSelectors ($list, $extend_selectors = false) { - $this->selectors = array_merge( $this->selectors, $list ); + $this->selectors += $list; + } + + public function addExtendSelectors ($list) + { + $this->extendSelectors += $list; } @@ -497,30 +519,30 @@ public function addSelectors ( $list ) public function addPropertyAliases () { - $aliased_properties =& CssCrush::$process->aliases[ 'properties' ]; + $aliased_properties =& CssCrush::$process->aliases['properties']; // Bail early if nothing doing. - if ( ! array_intersect_key( $aliased_properties, $this->properties ) ) { + if (! array_intersect_key($aliased_properties, $this->properties)) { return; } $stack = array(); $rule_updated = false; - foreach ( $this->declarations as $declaration ) { + foreach ($this->declarations as $declaration) { - if ( $declaration->skip ) { + if ($declaration->skip) { $stack[] = $declaration; continue; } // Shim in aliased properties. - if ( isset( $aliased_properties[ $declaration->property ] ) ) { + if (isset($aliased_properties[$declaration->property])) { - foreach ( $aliased_properties[ $declaration->property ] as $prop_alias ) { + foreach ($aliased_properties[$declaration->property] as $prop_alias) { // If an aliased version already exists to not create one. - if ( $this->propertyCount( $prop_alias ) ) { + if ($this->propertyCount($prop_alias)) { continue; } @@ -530,7 +552,7 @@ public function addPropertyAliases () // Set the aliased declaration vendor property. $copy->vendor = null; - if ( preg_match( CssCrush_Regex::$patt->vendorPrefix, $prop_alias, $vendor ) ) { + if (preg_match(CssCrush_Regex::$patt->vendorPrefix, $prop_alias, $vendor)) { $copy->vendor = $vendor[1]; } @@ -544,32 +566,32 @@ public function addPropertyAliases () } // Re-assign if any updates have been made. - if ( $rule_updated ) { - $this->setDeclarations( $stack ); + if ($rule_updated) { + $this->setDeclarations($stack); } } public function addFunctionAliases () { - $function_aliases =& CssCrush::$process->aliases[ 'functions' ]; - $function_alias_groups =& CssCrush::$process->aliases[ 'function_groups' ]; + $function_aliases =& CssCrush::$process->aliases['functions']; + $function_alias_groups =& CssCrush::$process->aliases['function_groups']; // The new modified set of declarations. $new_set = array(); $rule_updated = false; // Shim in aliased functions. - foreach ( $this->declarations as $declaration ) { + foreach ($this->declarations as $declaration) { // No functions, bail. - if ( ! $declaration->functions || $declaration->skip ) { + if (! $declaration->functions || $declaration->skip) { $new_set[] = $declaration; continue; } // Get list of functions used in declaration that are alias-able, bail if none. - $intersect = array_intersect_key( $declaration->functions, $function_aliases ); - if ( ! $intersect ) { + $intersect = array_intersect_key($declaration->functions, $function_aliases); + if (! $intersect) { $new_set[] = $declaration; continue; } @@ -577,30 +599,30 @@ public function addFunctionAliases () // Keep record of which groups have been applied. $processed_groups = array(); - foreach ( array_keys( $intersect ) as $fn_name ) { + foreach (array_keys($intersect) as $fn_name) { // Store for all the duplicated declarations. $prefixed_copies = array(); // Grouped function aliases. - if ( $function_aliases[ $fn_name ][0] === ':' ) { + if ($function_aliases[$fn_name][0] === ':') { - $group_id = $function_aliases[ $fn_name ]; + $group_id = $function_aliases[$fn_name]; // If this group has been applied we can skip over. - if ( isset( $processed_groups[ $group_id ] ) ) { + if (isset($processed_groups[$group_id])) { continue; } // Mark group as applied. - $processed_groups[ $group_id ] = true; + $processed_groups[$group_id] = true; - $groups =& $function_alias_groups[ $group_id ]; + $groups =& $function_alias_groups[$group_id]; - foreach ( $groups as $group_key => $replacements ) { + foreach ($groups as $group_key => $replacements) { // If the declaration is vendor specific only create aliases for the same vendor. - if ( $declaration->vendor && $group_key !== $declaration->vendor ) { + if ($declaration->vendor && $group_key !== $declaration->vendor) { continue; } @@ -611,26 +633,26 @@ public function addFunctionAliases () $replacements['find'], $replacements['replace'], $copy->value - ); + ); $prefixed_copies[] = $copy; $rule_updated = true; } // Post fixes. - if ( isset( CssCrush_PostAliasFix::$functions[ $group_id ] ) ) { - call_user_func( CssCrush_PostAliasFix::$functions[ $group_id ], $prefixed_copies, $group_id ); + if (isset(CssCrush_PostAliasFix::$functions[$group_id])) { + call_user_func(CssCrush_PostAliasFix::$functions[$group_id], $prefixed_copies, $group_id); } } // Single function aliases. else { - foreach ( $function_aliases[ $fn_name ] as $fn_alias ) { + foreach ($function_aliases[$fn_name] as $fn_alias) { // If the declaration is vendor specific only create aliases for the same vendor. - if ( $declaration->vendor ) { - preg_match( CssCrush_Regex::$patt->vendorPrefix, $fn_alias, $m ); - if ( $m[1] !== $declaration->vendor ) { + if ($declaration->vendor) { + preg_match(CssCrush_Regex::$patt->vendorPrefix, $fn_alias, $m); + if ($m[1] !== $declaration->vendor) { continue; } } @@ -642,62 +664,62 @@ public function addFunctionAliases () '~(?value - ); + ); $prefixed_copies[] = $copy; $rule_updated = true; } // Post fixes. - if ( isset( CssCrush_PostAliasFix::$functions[ $fn_name ] ) ) { - call_user_func( CssCrush_PostAliasFix::$functions[ $fn_name ], $prefixed_copies, $fn_name ); + if (isset(CssCrush_PostAliasFix::$functions[$fn_name])) { + call_user_func(CssCrush_PostAliasFix::$functions[$fn_name], $prefixed_copies, $fn_name); } } - $new_set = array_merge( $new_set, $prefixed_copies ); + $new_set = array_merge($new_set, $prefixed_copies); } $new_set[] = $declaration; } // Re-assign if any updates have been made. - if ( $rule_updated ) { - $this->setDeclarations( $new_set ); + if ($rule_updated) { + $this->setDeclarations($new_set); } } public function addDeclarationAliases () { - $declaration_aliases =& CssCrush::$process->aliases[ 'declarations' ]; + $declaration_aliases =& CssCrush::$process->aliases['declarations']; // First test for the existence of any aliased properties. - if ( ! ( $intersect = array_intersect_key( $declaration_aliases, $this->properties ) ) ) { + if (! ($intersect = array_intersect_key($declaration_aliases, $this->properties))) { return; } // Table lookups are faster. - $intersect = array_flip( array_keys( $intersect ) ); + $intersect = array_flip(array_keys($intersect)); $new_set = array(); $rule_updated = false; - foreach ( $this->declarations as $declaration ) { + foreach ($this->declarations as $declaration) { // Check the current declaration property is actually aliased. - if ( isset( $intersect[ $declaration->property ] ) && ! $declaration->skip ) { + if (isset($intersect[$declaration->property]) && ! $declaration->skip) { // Iterate on the current declaration property for value matches. - foreach ( $declaration_aliases[ $declaration->property ] as $value_match => $replacements ) { + foreach ($declaration_aliases[$declaration->property] as $value_match => $replacements) { // Create new alias declaration if the property and value match. - if ( $declaration->value === $value_match ) { + if ($declaration->value === $value_match) { - foreach ( $replacements as $pair ) { + foreach ($replacements as $pair) { // If the replacement property is null use the original declaration property. $new_set[] = new CssCrush_Declaration( - ! empty( $pair[0] ) ? $pair[0] : $declaration->property, + ! empty($pair[0]) ? $pair[0] : $declaration->property, $pair[1] - ); + ); $rule_updated = true; } } @@ -707,8 +729,8 @@ public function addDeclarationAliases () } // Re-assign if any updates have been made. - if ( $rule_updated ) { - $this->setDeclarations( $new_set ); + if ($rule_updated) { + $this->setDeclarations($new_set); } } @@ -718,7 +740,7 @@ public function addDeclarationAliases () public function getIterator () { - return new ArrayIterator( $this->declarations ); + return new ArrayIterator($this->declarations); } @@ -753,17 +775,17 @@ public function updatePropertyIndex () ############################# # Rule API. - public function propertyCount ( $prop ) + public function propertyCount ($prop) { - return isset( $this->properties[ $prop ] ) ? $this->properties[ $prop ] : 0; + return isset($this->properties[$prop]) ? $this->properties[$prop] : 0; } - public function addDeclaration ( $prop, $value, $contextIndex = 0 ) + public function addDeclaration ($prop, $value, $contextIndex = 0) { // Create declaration, add to the stack if it's valid - $declaration = new CssCrush_Declaration( $prop, $value, $contextIndex ); + $declaration = new CssCrush_Declaration($prop, $value, $contextIndex); - if ( empty( $declaration->inValid ) ) { + if (empty($declaration->inValid)) { $this->indexProperty($declaration); $this->declarations[] = $declaration; @@ -773,7 +795,7 @@ public function addDeclaration ( $prop, $value, $contextIndex = 0 ) return false; } - public function setDeclarations ( array $declaration_stack ) + public function setDeclarations (array $declaration_stack) { $this->declarations = $declaration_stack; @@ -781,10 +803,10 @@ public function setDeclarations ( array $declaration_stack ) $this->updatePropertyIndex(); } - static public function get ( $token ) + static public function get ($token) { - if ( isset( CssCrush::$process->tokens->r[ $token ] ) ) { - return CssCrush::$process->tokens->r[ $token ]; + if (isset(CssCrush::$process->tokens->r[$token])) { + return CssCrush::$process->tokens->r[$token]; } return null; } diff --git a/plugins/rem-fallback.php b/plugins/rem-fallback.php index 85cb0bc..c15ea9c 100644 --- a/plugins/rem-fallback.php +++ b/plugins/rem-fallback.php @@ -1,9 +1,8 @@ Date: Sat, 30 Mar 2013 11:56:23 +0000 Subject: [PATCH 091/421] Adopting parts of PSR-2 coding style guide. --- lib/CssCrush/Core.php | 4 +- lib/CssCrush/Declaration.php | 6 +- lib/CssCrush/Function.php | 2 +- lib/CssCrush/IO.php | 146 ++++++++++++------------- lib/CssCrush/Importer.php | 200 +++++++++++++++++----------------- lib/CssCrush/Options.php | 46 ++++---- lib/CssCrush/PostAliasFix.php | 52 ++++----- lib/CssCrush/Process.php | 14 +-- lib/CssCrush/Regex.php | 38 +++---- lib/CssCrush/Rule.php | 13 +-- lib/CssCrush/Selector.php | 46 ++++---- lib/CssCrush/Stream.php | 60 +++++----- lib/CssCrush/Url.php | 8 +- lib/CssCrush/Util.php | 130 +++++++++++----------- lib/functions.php | 28 ++--- misc/formatters.php | 40 +++---- plugins/ease.php | 8 +- plugins/hocus-pocus.php | 13 +-- plugins/hsl-to-hex.php | 27 +++-- plugins/ie-clip.php | 20 ++-- plugins/ie-filter.php | 34 +++--- plugins/ie-inline-block.php | 22 ++-- plugins/ie-min-height.php | 22 ++-- plugins/ie-opacity.php | 32 +++--- plugins/initial.php | 20 ++-- plugins/legacy-flexbox.php | 150 ++++++++++++------------- plugins/noise.php | 117 ++++++++++---------- plugins/property-sorter.php | 81 +++++++------- plugins/px2rem.php | 3 +- plugins/rem-fallback.php | 2 +- plugins/rgba-fallback.php | 33 +++--- plugins/spiffing.php | 17 +-- plugins/svg-gradients.php | 127 +++++++++++---------- plugins/svg.php | 45 ++++---- 34 files changed, 797 insertions(+), 809 deletions(-) diff --git a/lib/CssCrush/Core.php b/lib/CssCrush/Core.php index 1c26380..4be42bd 100644 --- a/lib/CssCrush/Core.php +++ b/lib/CssCrush/Core.php @@ -89,7 +89,7 @@ static public function init () // Force newline type on output files. Defaults to the current platform newline. // Options: 'windows' (or 'win'), 'unix', 'use-platform' 'newlines' => 'use-platform', - )); + )); // Include and register stock formatters. require_once self::$config->location . '/misc/formatters.php'; @@ -206,7 +206,7 @@ static public function loadAssets () 'function_groups' => array(), 'declarations' => array(), 'at-rules' => array(), - ); + ); self::$config->aliases += self::$config->bareAliasGroups; } else { diff --git a/lib/CssCrush/Declaration.php b/lib/CssCrush/Declaration.php index 5098961..9076a62 100644 --- a/lib/CssCrush/Declaration.php +++ b/lib/CssCrush/Declaration.php @@ -95,13 +95,13 @@ public function process ($parent_rule) // it's self referencing. $extra = array( 'rule' => $parent_rule, - ); + ); CssCrush_Function::executeOnString( $this->value, CssCrush_Regex::$patt->thisFunction, array( 'this' => 'csscrush_fn__this', - ), + ), $extra); // Add result to $rule->selfData. @@ -110,7 +110,7 @@ public function process ($parent_rule) $extra = array( 'rule' => $parent_rule, 'property' => $this->property - ); + ); CssCrush_Function::executeOnString( $this->value, null, diff --git a/lib/CssCrush/Function.php b/lib/CssCrush/Function.php index ee3a363..d29fe7b 100644 --- a/lib/CssCrush/Function.php +++ b/lib/CssCrush/Function.php @@ -28,7 +28,7 @@ class CssCrush_Function 's-adjust' => 'csscrush_fn__s_adjust', 'l-adjust' => 'csscrush_fn__l_adjust', 'a-adjust' => 'csscrush_fn__a_adjust', - ); + ); static public function setMatchPatt () { diff --git a/lib/CssCrush/IO.php b/lib/CssCrush/IO.php index f91fdfb..a770613 100644 --- a/lib/CssCrush/IO.php +++ b/lib/CssCrush/IO.php @@ -26,28 +26,28 @@ static public function testOutputDir () $pathtest = true; $error = false; - if ( ! file_exists( $output_dir ) ) { + if (! file_exists($output_dir)) { $error = "Output directory '$output_dir' doesn't exist."; $pathtest = false; } - else if ( ! is_writable( $output_dir ) ) { + else if (! is_writable($output_dir)) { - CssCrush::log( 'Attempting to change permissions.' ); + CssCrush::log('Attempting to change permissions.'); - if ( ! @chmod( $output_dir, 0755 ) ) { + if (! @chmod($output_dir, 0755)) { $error = "Output directory '$output_dir' is unwritable."; $pathtest = false; } else { - CssCrush::log( 'Permissions updated.' ); + CssCrush::log('Permissions updated.'); } } - if ( $error ) { - CssCrush::logError( $error ); - trigger_error( __METHOD__ . ": $error\n", E_USER_WARNING ); + if ($error) { + CssCrush::logError($error); + trigger_error(__METHOD__ . ": $error\n", E_USER_WARNING); } return $pathtest; @@ -58,10 +58,10 @@ static public function getOutputFileName () $process = CssCrush::$process; $options = $process->options; - $output_basename = basename( $process->input->filename, '.css' ); + $output_basename = basename($process->input->filename, '.css'); - if ( ! empty( $options->output_file ) ) { - $output_basename = basename( $options->output_file, '.css' ); + if (! empty($options->output_file)) { + $output_basename = basename($options->output_file, '.css'); } return "$output_basename.crush.css"; @@ -75,14 +75,14 @@ static public function validateExistingOutput () $input = $process->input; // Search base directory for an existing compiled file. - foreach ( scandir( $process->output->dir ) as $filename ) { + foreach (scandir($process->output->dir) as $filename) { - if ( $process->output->filename != $filename ) { + if ($process->output->filename != $filename) { continue; } // Cached file exists. - CssCrush::log( 'Cached file exists.' ); + CssCrush::log('Cached file exists.'); $existingfile = (object) array(); $existingfile->filename = $filename; @@ -90,73 +90,73 @@ static public function validateExistingOutput () $existingfile->URL = "{$process->output->dirUrl}/$existingfile->filename"; // Start off with the input file then add imported files - $all_files = array( $input->mtime ); + $all_files = array($input->mtime); - if ( file_exists( $existingfile->path ) && isset( $process->cacheData[ $process->output->filename ] ) ) { + if (file_exists($existingfile->path) && isset($process->cacheData[$process->output->filename])) { // File exists and has config - CssCrush::log( 'Cached file is registered.' ); + CssCrush::log('Cached file is registered.'); - foreach ( $process->cacheData[ $existingfile->filename ][ 'imports' ] as $import_file ) { + foreach ($process->cacheData[$existingfile->filename]['imports'] as $import_file) { // Check if this is docroot relative or input dir relative. - $root = strpos( $import_file, '/' ) === 0 ? $process->docRoot : $process->input->dir; - $import_filepath = realpath( $root ) . "/$import_file"; + $root = strpos($import_file, '/') === 0 ? $process->docRoot : $process->input->dir; + $import_filepath = realpath($root) . "/$import_file"; - if ( file_exists( $import_filepath ) ) { - $all_files[] = filemtime( $import_filepath ); + if (file_exists($import_filepath)) { + $all_files[] = filemtime($import_filepath); } else { // File has been moved, remove old file and skip to compile. - CssCrush::log( 'Import file has been moved, removing existing file.' ); - unlink( $existingfile->path ); + CssCrush::log('Import file has been moved, removing existing file.'); + unlink($existingfile->path); return false; } } // Cast because the cached options may be a stdClass if an IO adapter has been used. - $cached_options = (array) $process->cacheData[ $existingfile->filename ][ 'options' ]; + $cached_options = (array) $process->cacheData[$existingfile->filename]['options']; $active_options = $options->get(); // Compare runtime options and cached options for differences. $options_changed = false; - foreach ( $cached_options as $key => &$value ) { - if ( isset( $active_options[ $key ] ) && $active_options[ $key ] !== $value ) { + foreach ($cached_options as $key => &$value) { + if (isset($active_options[$key]) && $active_options[$key] !== $value) { $options_changed = true; break; } } // Check if any of the files have changed. - $existing_datesum = $process->cacheData[ $existingfile->filename ][ 'datem_sum' ]; - $files_changed = $existing_datesum != array_sum( $all_files ); + $existing_datesum = $process->cacheData[$existingfile->filename]['datem_sum']; + $files_changed = $existing_datesum != array_sum($all_files); - if ( ! $options_changed && ! $files_changed ) { + if (! $options_changed && ! $files_changed) { // Files have not been modified and config is the same: return the old file. CssCrush::log( - "Files and options have not been modified, returning existing file '$existingfile->URL'." ); - return $existingfile->URL . ( $options->versioning !== false ? "?$existing_datesum" : '' ); + "Files and options have not been modified, returning existing file '$existingfile->URL'."); + return $existingfile->URL . ($options->versioning !== false ? "?$existing_datesum" : ''); } else { - if ( $options_changed ) { - CssCrush::log( 'Options have been modified.' ); + if ($options_changed) { + CssCrush::log('Options have been modified.'); } - if ( $files_changed ) { - CssCrush::log( 'Files have been modified.' ); + if ($files_changed) { + CssCrush::log('Files have been modified.'); } - CssCrush::log( 'Removing existing file.' ); + CssCrush::log('Removing existing file.'); // Remove old file and continue making a new one... - unlink( $existingfile->path ); + unlink($existingfile->path); } } - else if ( file_exists( $existingfile->path ) ) { + else if (file_exists($existingfile->path)) { // File exists but has no config. - CssCrush::log( 'File exists but no config, removing existing file.' ); - unlink( $existingfile->path ); + CssCrush::log('File exists but no config, removing existing file.'); + unlink($existingfile->path); } return false; @@ -166,29 +166,29 @@ static public function validateExistingOutput () return false; } - static public function clearCache ( $dir ) + static public function clearCache ($dir) { - if ( empty( $dir ) ) { - $dir = dirname( __FILE__ ); + if (empty($dir)) { + $dir = dirname(__FILE__); } - else if ( ! file_exists( $dir ) ) { + else if (! file_exists($dir)) { return; } $configPath = $dir . '/' . CssCrush::$process->cacheFile; - if ( file_exists( $configPath ) ) { - unlink( $configPath ); + if (file_exists($configPath)) { + unlink($configPath); } // Remove any compiled files $suffix = '.crush.css'; - $suffixLength = strlen( $suffix ); + $suffixLength = strlen($suffix); - foreach ( scandir( $dir ) as $file ) { + foreach (scandir($dir) as $file) { if ( - strpos( $file, $suffix ) === strlen( $file ) - $suffixLength + strpos($file, $suffix) === strlen($file) - $suffixLength ) { - unlink( $dir . "/{$file}" ); + unlink($dir . "/{$file}"); } } } @@ -199,38 +199,38 @@ static public function getCacheData () $process = CssCrush::$process; if ( - file_exists( $process->cacheFile ) && + file_exists($process->cacheFile) && $process->cacheData ) { // Already loaded and config file exists in the current directory return; } - $cache_data_exists = file_exists( $process->cacheFile ); - $cache_data_file_is_writable = $cache_data_exists ? is_writable( $process->cacheFile ) : false; + $cache_data_exists = file_exists($process->cacheFile); + $cache_data_file_is_writable = $cache_data_exists ? is_writable($process->cacheFile) : false; $cache_data = array(); if ( $cache_data_exists && $cache_data_file_is_writable && - $cache_data = json_decode( file_get_contents( $process->cacheFile ), true ) + $cache_data = json_decode(file_get_contents($process->cacheFile), true) ) { // Successfully loaded config file. - CssCrush::log( 'Cache data loaded.' ); + CssCrush::log('Cache data loaded.'); } else { // Config file may exist but not be writable (may not be visible in some ftp situations?) - if ( $cache_data_exists ) { - if ( ! @unlink( $process->cacheFile ) ) { + if ($cache_data_exists) { + if (! @unlink($process->cacheFile)) { $error = "Could not delete config data file."; - CssCrush::logError( $error ); - trigger_error( __METHOD__ . ": $error\n", E_USER_NOTICE ); + CssCrush::logError($error); + trigger_error(__METHOD__ . ": $error\n", E_USER_NOTICE); } } else { // Create config file. - CssCrush::log( 'Creating cache data file.' ); + CssCrush::log('Creating cache data file.'); } file_put_contents($process->cacheFile, json_encode(array()), LOCK_EX); } @@ -242,43 +242,43 @@ static public function saveCacheData () { $process = CssCrush::$process; - CssCrush::log( 'Saving config.' ); - file_put_contents( $process->cacheFile, json_encode( $process->cacheData ), LOCK_EX); + CssCrush::log('Saving config.'); + file_put_contents($process->cacheFile, json_encode($process->cacheData), LOCK_EX); } - static public function write ( CssCrush_Stream $stream ) + static public function write (CssCrush_Stream $stream) { $process = CssCrush::$process; $target = "{$process->output->dir}/{$process->output->filename}"; - if ( @file_put_contents($target, $stream, LOCK_EX) ) { + if (@file_put_contents($target, $stream, LOCK_EX)) { return "{$process->output->dirUrl}/{$process->output->filename}"; } else { $error = "Could not write file '$target'."; - CssCrush::logError( $error ); - trigger_error( __METHOD__ . ": $error\n", E_USER_WARNING ); + CssCrush::logError($error); + trigger_error(__METHOD__ . ": $error\n", E_USER_WARNING); } return false; } - static final function registerInputFile ( $file ) + static final function registerInputFile ($file) { $input = CssCrush::$process->input; - $input->filename = basename( $file ); + $input->filename = basename($file); $input->path = "$input->dir/$input->filename"; - if ( ! file_exists( $input->path ) ) { + if (! file_exists($input->path)) { // On failure return false. $error = "Input file '$input->filename' not found."; - CssCrush::logError( $error ); - trigger_error( __METHOD__ . ": $error\n", E_USER_WARNING ); + CssCrush::logError($error); + trigger_error(__METHOD__ . ": $error\n", E_USER_WARNING); return false; } else { // Capture the modified time. - $input->mtime = filemtime( $input->path ); + $input->mtime = filemtime($input->path); return true; } } diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index a90b8d4..187ed5d 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -22,29 +22,29 @@ static public function hostfile () $prepend_file_contents = ''; // The prepend file. - if ( $prepend_file = CssCrush_Util::find( 'Prepend-local.css', 'Prepend.css' ) ) { + if ($prepend_file = CssCrush_Util::find('Prepend-local.css', 'Prepend.css')) { $prepend_file_contents = file_get_contents($prepend_file); $process->currentFile = 'file://' . $prepend_file; // If there's a parsing error inside the prepend file, wipe $prepend_file_contents. - if ( ! self::prepareForStream( $prepend_file_contents ) ) { + if (! self::prepareForStream($prepend_file_contents)) { $prepend_file_contents = ''; } } // Resolve main input; a string of css or a file. - if ( isset( $input->string ) ) { + if (isset($input->string)) { $str .= $input->string; $process->currentFile = 'inline css'; } else { - $str .= file_get_contents( $input->path ); + $str .= file_get_contents($input->path); $process->currentFile = 'file://' . $input->path; } // If there's a parsing error go no further. - if ( ! self::prepareForStream( $str ) ) { + if (! self::prepareForStream($str)) { return $str; } @@ -56,20 +56,20 @@ static public function hostfile () $search_offset = 0; // Recurses until the nesting heirarchy is flattened and all import files are inlined. - while ( preg_match( $regex->import, $str, $match, PREG_OFFSET_CAPTURE, $search_offset ) ) { + while (preg_match($regex->import, $str, $match, PREG_OFFSET_CAPTURE, $search_offset)) { - $match_len = strlen( $match[0][0] ); + $match_len = strlen($match[0][0]); $match_start = $match[0][1]; $match_end = $match_start + $match_len; // If just stripping the import statements. - if ( isset( $input->importIgnore ) ) { - $str = substr_replace( $str, '', $match_start, $match_len ); + if (isset($input->importIgnore)) { + $str = substr_replace($str, '', $match_start, $match_len); continue; } // Fetch the URL object. - $url = CssCrush_Url::get( $match[1][0] ); + $url = CssCrush_Url::get($match[1][0]); // Pass over protocoled import urls. if ($url->protocol) { @@ -78,7 +78,7 @@ static public function hostfile () } // The media context (if specified). - $media_context = trim( $match[2][0] ); + $media_context = trim($match[2][0]); // Create import object. $import = (object) array(); @@ -86,19 +86,19 @@ static public function hostfile () $import->mediaContext = $media_context; // Resolve import realpath. - if ( $url->isRooted ) { - $import->path = realpath( $process->docRoot . $import->url->value ); + if ($url->isRooted) { + $import->path = realpath($process->docRoot . $import->url->value); } else { - $import->path = realpath( "$input->dir/{$import->url->value}" ); + $import->path = realpath("$input->dir/{$import->url->value}"); } // Get the import contents, if unsuccessful just continue with the import line removed. - $import->content = @file_get_contents( $import->path ); - if ( $import->content === false ) { + $import->content = @file_get_contents($import->path); + if ($import->content === false) { - CssCrush::log( "Import file '{$import->url->value}' not found" ); - $str = substr_replace( $str, '', $match_start, $match_len ); + CssCrush::log("Import file '{$import->url->value}' not found"); + $str = substr_replace($str, '', $match_start, $match_len); continue; } @@ -108,149 +108,149 @@ static public function hostfile () $process->currentFile = 'file://' . $import->path; // If there are unmatched brackets inside the import, strip it. - if ( ! self::prepareForStream( $import->content ) ) { + if (! self::prepareForStream($import->content)) { - $str = substr_replace( $str, '', $match_start, $match_len ); + $str = substr_replace($str, '', $match_start, $match_len); continue; } - $import->dir = dirname( $import->url->value ); + $import->dir = dirname($import->url->value); // Store import file info for cache validation. - $mtimes[] = filemtime( $import->path ); + $mtimes[] = filemtime($import->path); $filenames[] = $import->url->value; // Alter all the @import urls to be paths relative to the hostfile. - foreach ( CssCrush_Regex::matchAll( $regex->import, $import->content ) as $m ) { + foreach (CssCrush_Regex::matchAll($regex->import, $import->content) as $m) { // Fetch the matched URL. - $url2 = CssCrush_Url::get( $m[1][0] ); + $url2 = CssCrush_Url::get($m[1][0]); // Try to resolve absolute paths. // On failure strip the @import statement. - if ( $url2->isRooted ) { + if ($url2->isRooted) { $url2->resolveRootedPath(); } else { - $url2->prepend( "$import->dir/" ); + $url2->prepend("$import->dir/"); } } // Optionally rewrite relative url and custom function data-uri references. - if ( $options->rewrite_import_urls ) { - self::rewriteImportedUrls( $import ); + if ($options->rewrite_import_urls) { + self::rewriteImportedUrls($import); } // Add media context if it exists. - if ( $import->mediaContext ) { + if ($import->mediaContext) { $import->content = "@media $import->mediaContext {{$import->content}}"; } - $str = substr_replace( $str, $import->content, $match_start, $match_len ); + $str = substr_replace($str, $import->content, $match_start, $match_len); } // Save only if caching is on and the hostfile object is associated with a real file. - if ( $input->path && $options->cache ) { + if ($input->path && $options->cache) { - $process->cacheData[ $process->output->filename ] = array( + $process->cacheData[$process->output->filename] = array( 'imports' => $filenames, - 'datem_sum' => array_sum( $mtimes ) + $input->mtime, + 'datem_sum' => array_sum($mtimes) + $input->mtime, 'options' => $options->get(), ); // Save config changes. - $process->ioCall( 'saveCacheData' ); + $process->ioCall('saveCacheData'); } return $str; } - static protected function rewriteImportedUrls ( $import ) + static protected function rewriteImportedUrls ($import) { static $non_import_urls_patt; if (! $non_import_urls_patt) { - $non_import_urls_patt = CssCrush_Regex::create( '(?', 'iS' ); + $non_import_urls_patt = CssCrush_Regex::create('(?', 'iS'); } $link = CssCrush_Util::getLinkBetweenDirs( - CssCrush::$process->input->dir, dirname( $import->path ) ); + CssCrush::$process->input->dir, dirname($import->path)); - if ( empty( $link ) ) { + if (empty($link)) { return; } // Match all urls that are not imports. preg_match_all($non_import_urls_patt, $import->content, $matches); - foreach ( $matches[0] as $token ) { + foreach ($matches[0] as $token) { // Fetch the matched URL. - $url = CssCrush_Url::get( $token ); + $url = CssCrush_Url::get($token); - if ( $url->isRelative ) { + if ($url->isRelative) { // Prepend the relative url prefix. - $url->prepend( $link ); + $url->prepend($link); } } } - static protected function prepareForStream ( &$str ) + static protected function prepareForStream (&$str) { $regex = CssCrush_Regex::$patt; $process = CssCrush::$process; // Convert all end-of-lines to unix style. - $str = preg_replace( '~\r\n?~', "\n", $str ); + $str = preg_replace('~\r\n?~', "\n", $str); - $str = preg_replace_callback( $regex->commentAndString, - array( 'self', 'cb_extractCommentAndString' ), $str ); + $str = preg_replace_callback($regex->commentAndString, + array('self', 'cb_extractCommentAndString'), $str); // If @charset is set store it. - if ( preg_match( $regex->charset, $str, $m ) ) { + if (preg_match($regex->charset, $str, $m)) { $replace = ''; - if ( ! $process->charset ) { + if (! $process->charset) { // Keep track of newlines for line tracing. - $replace = str_repeat( "\n", substr_count( $m[0], "\n" ) ); - $process->charset = trim( $process->fetchToken( $m[1] ), '"\'' ); + $replace = str_repeat("\n", substr_count($m[0], "\n")); + $process->charset = trim($process->fetchToken($m[1]), '"\''); } - $str = preg_replace( $regex->charset, $replace, $str ); + $str = preg_replace($regex->charset, $replace, $str); } // Catch obvious typing errors. $parse_errors = array(); $current_file = $process->currentFile; - $balanced_parens = substr_count( $str, "(" ) === substr_count( $str, ")" ); - $balanced_curlies = substr_count( $str, "{" ) === substr_count( $str, "}" ); + $balanced_parens = substr_count($str, "(") === substr_count($str, ")"); + $balanced_curlies = substr_count($str, "{") === substr_count($str, "}"); - if ( ! $balanced_parens ) { + if (! $balanced_parens) { $parse_errors[] = "Unmatched '(' in $current_file."; } - if ( ! $balanced_curlies ) { + if (! $balanced_curlies) { $parse_errors[] = "Unmatched '{' in $current_file."; } - if ( $parse_errors ) { - foreach ( $parse_errors as $error_msg ) { - CssCrush::logError( $error_msg ); - trigger_error( "$error_msg\n", E_USER_WARNING ); + if ($parse_errors) { + foreach ($parse_errors as $error_msg) { + CssCrush::logError($error_msg); + trigger_error("$error_msg\n", E_USER_WARNING); } return false; } // Optionally add tracing stubs. - if ( $process->addTracingStubs ) { - self::addTracingStubs( $str ); + if ($process->addTracingStubs) { + self::addTracingStubs($str); } // Strip unneeded whitespace. - $str = CssCrush_Util::normalizeWhiteSpace( $str ); + $str = CssCrush_Util::normalizeWhiteSpace($str); - self::captureUrls( $str ); + self::captureUrls($str); return true; } - static protected function captureUrls ( &$str ) + static protected function captureUrls (&$str) { static $url_patt; if (! $url_patt) { @@ -261,116 +261,116 @@ static protected function captureUrls ( &$str ) while (preg_match($url_patt, $str, $outer_m, PREG_OFFSET_CAPTURE, $offset)) { $outer_offset = $outer_m[0][1]; - $is_import_url = ! isset( $outer_m[2] ); + $is_import_url = ! isset($outer_m[2]); - if ( $is_import_url ) { - $url = new CssCrush_Url( $outer_m[1][0] ); - $str = str_replace( $outer_m[1][0], $url->label, $str ); + if ($is_import_url) { + $url = new CssCrush_Url($outer_m[1][0]); + $str = str_replace($outer_m[1][0], $url->label, $str); } // Match parenthesis if not a string token. elseif ( - preg_match( CssCrush_Regex::$patt->balancedParens, $str, $inner_m, PREG_OFFSET_CAPTURE, $outer_offset ) + preg_match(CssCrush_Regex::$patt->balancedParens, $str, $inner_m, PREG_OFFSET_CAPTURE, $outer_offset) ) { - $url = new CssCrush_Url( $inner_m[1][0] ); - $func_name = strtolower( $outer_m[2][0] ); + $url = new CssCrush_Url($inner_m[1][0]); + $func_name = strtolower($outer_m[2][0]); $url->convertToData = 'data-uri' === $func_name; - $str = substr_replace( $str, $url->label, $outer_offset, - strlen( $func_name ) + strlen( $inner_m[0][0] ) ); + $str = substr_replace($str, $url->label, $outer_offset, + strlen($func_name) + strlen($inner_m[0][0])); } // If brackets cannot be matched, skip over the original match. else { - $offset += strlen( $outer_m[0][0] ); + $offset += strlen($outer_m[0][0]); } } } - static protected function cb_extractCommentAndString ( $match ) + static protected function cb_extractCommentAndString ($match) { $full_match = $match[0]; $process = CssCrush::$process; // We return the newlines to maintain line numbering when tracing. - $newlines = str_repeat( "\n", substr_count( $full_match, "\n" ) ); + $newlines = str_repeat("\n", substr_count($full_match, "\n")); - if ( strpos( $full_match, '/*' ) === 0 ) { + if (strpos($full_match, '/*') === 0) { // Bail without storing comment if output is minified or a private comment. if ( $process->minifyOutput || - strpos( $full_match, '/*$' ) === 0 + strpos($full_match, '/*$') === 0 ) { return $newlines; } // Fix broken comments as they will break any subsquent // imported files that are inlined. - if ( ! preg_match( '~\*/$~', $full_match ) ) { + if (! preg_match('~\*/$~', $full_match)) { $full_match .= '*/'; } - $label = $process->addToken( $full_match, 'c' ); + $label = $process->addToken($full_match, 'c'); } else { // Fix broken strings as they will break any subsquent // imported files that are inlined. - if ( $full_match[0] !== $full_match[ strlen( $full_match )-1 ] ) { + if ($full_match[0] !== $full_match[strlen($full_match)-1]) { $full_match .= $full_match[0]; } - $label = $process->addToken( $full_match, 's' ); + $label = $process->addToken($full_match, 's'); } return $newlines . $label; } - static protected function addTracingStubs ( &$str ) + static protected function addTracingStubs (&$str) { $selector_patt = '~ (^|;|\})+ ([^;{}]+) (\{) ~xmS'; $token_or_whitespace = '~(\s*\?c\d+\?\s*|\s+)~S'; - $matches = CssCrush_Regex::matchAll( $selector_patt, $str ); + $matches = CssCrush_Regex::matchAll($selector_patt, $str); // Start from last match and move backwards. - while ( $m = array_pop( $matches ) ) { + while ($m = array_pop($matches)) { // Shortcuts for readability. - list( $full_match, $before, $content, $after ) = $m; + list($full_match, $before, $content, $after) = $m; $full_match_text = $full_match[0]; $full_match_start = $full_match[1]; // The correct before string. - $before = substr( $full_match_text, 0, $content[1] - $full_match_start ); + $before = substr($full_match_text, 0, $content[1] - $full_match_start); // Split the matched selector part. - $content_parts = preg_split( $token_or_whitespace, $content[0], null, - PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY ); + $content_parts = preg_split($token_or_whitespace, $content[0], null, + PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); - foreach ( $content_parts as $part ) { + foreach ($content_parts as $part) { - if ( ! preg_match( $token_or_whitespace, $part ) ) { + if (! preg_match($token_or_whitespace, $part)) { // Match to a valid selector. - if ( preg_match( '~^([^@]|@(?:abstract|page))~iS', $part ) ) { + if (preg_match('~^([^@]|@(?:abstract|page))~iS', $part)) { // Count line breaks between the start of stream and // the matched selector to get the line number. - $selector_index = $full_match_start + strlen( $before ); + $selector_index = $full_match_start + strlen($before); $line_num = 1; $str_before = ""; - if ( $selector_index ) { - $str_before = substr( $str, 0, $selector_index ); - $line_num = substr_count( $str_before, "\n" ) + 1; + if ($selector_index) { + $str_before = substr($str, 0, $selector_index); + $line_num = substr_count($str_before, "\n") + 1; } // Get the currently processed file path, and escape it. - $current_file = str_replace( ' ', '%20', CssCrush::$process->currentFile ); - $current_file = preg_replace( '~[^\w-]~', '\\\\$0', $current_file ); + $current_file = str_replace(' ', '%20', CssCrush::$process->currentFile); + $current_file = preg_replace('~[^\w-]~', '\\\\$0', $current_file); // Splice in tracing stub. - $label = CssCrush::$process->addToken( "@media -sass-debug-info{filename{font-family:$current_file}line{font-family:\\00003$line_num}}", 't' ); + $label = CssCrush::$process->addToken("@media -sass-debug-info{filename{font-family:$current_file}line{font-family:\\00003$line_num}}", 't'); - $str = $str_before . $label . substr( $str, $selector_index ); + $str = $str_before . $label . substr($str, $selector_index); } else { // Not matched as a valid selector, move on. diff --git a/lib/CssCrush/Options.php b/lib/CssCrush/Options.php index 72ee3e0..4d37d55 100644 --- a/lib/CssCrush/Options.php +++ b/lib/CssCrush/Options.php @@ -8,23 +8,23 @@ class CssCrush_Options { public $data = array(); - public function __construct ( $properties ) + public function __construct ($properties) { - if ( $properties ) { - foreach ( $properties as $key => $value ) { - $this->__set( $key, $value ); + if ($properties) { + foreach ($properties as $key => $value) { + $this->__set($key, $value); } } } - public function __set ( $name, $value ) + public function __set ($name, $value) { - switch ( $name ) { + switch ($name) { // For legacy debug option, check minify has not been set then // flip the value and change property to minify. case 'debug': - if ( ! array_key_exists( 'minify', $this->data ) ) { + if (! array_key_exists('minify', $this->data)) { $name = 'minify'; $value = ! $value; } @@ -32,17 +32,17 @@ public function __set ( $name, $value ) // If trace value is truthy set to stubs. case 'trace': - if ( ! is_array( $value ) ) { - $value = $value ? array( 'stubs' ) : array(); + if (! is_array($value)) { + $value = $value ? array('stubs') : array(); } break; // Resolve a formatter callback name and check it's callable. case 'formatter': - if ( isset( CssCrush::$config->formatters[ $value ] ) ) { - $value = CssCrush::$config->formatters[ $value ]; + if (isset(CssCrush::$config->formatters[$value])) { + $value = CssCrush::$config->formatters[$value]; } - if ( ! is_callable( $value ) ) { + if (! is_callable($value)) { $value = null; } break; @@ -50,8 +50,8 @@ public function __set ( $name, $value ) // Sanitize path options. case 'context': case 'doc_root': - if ( is_string( $value ) ) { - $value = CssCrush_Util::normalizePath( $value ); + if (is_string($value)) { + $value = CssCrush_Util::normalizePath($value); } break; @@ -63,24 +63,24 @@ public function __set ( $name, $value ) break; } - $this->data[ $name ] = $value; + $this->data[$name] = $value; } - public function __get ( $name ) + public function __get ($name) { - return isset( $this->data[ $name ] ) ? $this->data[ $name ] : null; + return isset($this->data[$name]) ? $this->data[$name] : null; } - public function __isset ( $name ) + public function __isset ($name) { - return isset( $this->data[ $name ] ); + return isset($this->data[$name]); } - public function merge ( CssCrush_Options $options_instance ) + public function merge (CssCrush_Options $options_instance) { - foreach ( $options_instance->data as $key => $value ) { - if ( ! array_key_exists( $key, $this->data ) ) { - $this->__set( $key, $value ); + foreach ($options_instance->data as $key => $value) { + if (! array_key_exists($key, $this->data)) { + $this->__set($key, $value); } } } diff --git a/lib/CssCrush/PostAliasFix.php b/lib/CssCrush/PostAliasFix.php index a718927..609f783 100644 --- a/lib/CssCrush/PostAliasFix.php +++ b/lib/CssCrush/PostAliasFix.php @@ -11,19 +11,19 @@ class CssCrush_PostAliasFix ':gradients' => 'csscrush__post_alias_fix_gradients', ); - static public function add ( $alias_type, $key, $callback ) + static public function add ($alias_type, $key, $callback) { - if ( $alias_type === 'function' ) { + if ($alias_type === 'function') { // $key is the aliased css function name. - self::$functions[ $key ] = $callback; + self::$functions[$key] = $callback; } } - static public function remove ( $alias_type, $key ) + static public function remove ($alias_type, $key) { - if ( $type === 'function' ) { + if ($type === 'function') { // $key is the aliased css function name. - unset( self::$functions[ $key ] ); + unset(self::$functions[$key]); } } } @@ -31,20 +31,20 @@ static public function remove ( $alias_type, $key ) /** * Post alias fix callback for all gradients. */ -function csscrush__post_alias_fix_gradients ( $declaration_copies ) { +function csscrush__post_alias_fix_gradients ($declaration_copies) { - csscrush__post_alias_fix_lineargradients( $declaration_copies ); - csscrush__post_alias_fix_radialgradients( $declaration_copies ); + csscrush__post_alias_fix_lineargradients($declaration_copies); + csscrush__post_alias_fix_radialgradients($declaration_copies); } /** * Convert the new angle syntax (keyword and degree) on -x-linear-gradient() functions * to legacy equivalents. */ -function csscrush__post_alias_fix_lineargradients ( $declaration_copies ) { +function csscrush__post_alias_fix_lineargradients ($declaration_copies) { static $angles_new, $angles_old; - if ( ! $angles_new ) { + if (! $angles_new) { $angles = array( 'to top' => 'bottom', 'to right' => 'left', @@ -60,18 +60,18 @@ function csscrush__post_alias_fix_lineargradients ( $declaration_copies ) { 'to bottom right' => 'top left', 'to right bottom' => 'top left', ); - $angles_new = array_keys( $angles ); - $angles_old = array_values( $angles ); + $angles_new = array_keys($angles); + $angles_old = array_values($angles); } static $deg_patt, $deg_convert_callback, $fn_patt; - if ( ! $deg_patt ) { + if (! $deg_patt) { $deg_patt = CssCrush_Regex::create('(?<=[\( ])()deg', 'i'); // Legacy angles move anti-clockwise and start from East, not North. - $deg_convert_callback = create_function( '$m', ' - $angle = floatval( $m[1] ); - $angle = ( $angle + 90 ) - ( $angle * 2 ); - return ( $angle < 0 ? $angle + 360 : $angle ) . \'deg\'; + $deg_convert_callback = create_function('$m', ' + $angle = floatval($m[1]); + $angle = ($angle + 90) - ($angle * 2); + return ($angle < 0 ? $angle + 360 : $angle) . \'deg\'; '); $fn_patt = CssCrush_Regex::create('(?:(?:repeating-)?linear-gradient)()', 'iS'); } @@ -80,10 +80,10 @@ function csscrush__post_alias_fix_lineargradients ( $declaration_copies ) { // Replace the new syntax with the legacy syntax. $original_parens = array(); $replacement_parens = array(); - foreach ( CssCrush_Regex::matchAll( $fn_patt, $declaration_copies[0]->value ) as $m ) { + foreach (CssCrush_Regex::matchAll($fn_patt, $declaration_copies[0]->value) as $m) { $original_parens[] = $m[1][0]; - $original_paren_value = CssCrush::$process->fetchToken( $m[1][0] ); + $original_paren_value = CssCrush::$process->fetchToken($m[1][0]); // Convert keyword angle values. $updated_paren_value = str_ireplace( @@ -99,11 +99,11 @@ function csscrush__post_alias_fix_lineargradients ( $declaration_copies ) { $updated_paren_value ); - $replacement_parens[] = CssCrush::$process->addToken( $updated_paren_value, 'p' ); + $replacement_parens[] = CssCrush::$process->addToken($updated_paren_value, 'p'); } // Swap in the new tokens on all the prefixed declarations. - foreach ( $declaration_copies as $prefixed_copy ) { + foreach ($declaration_copies as $prefixed_copy) { $prefixed_copy->value = str_replace( $original_parens, $replacement_parens, @@ -115,7 +115,7 @@ function csscrush__post_alias_fix_lineargradients ( $declaration_copies ) { /** * Remove the 'at' keyword from -x-radial-gradient() for legacy implementations. */ -function csscrush__post_alias_fix_radialgradients ( $declaration_copies ) { +function csscrush__post_alias_fix_radialgradients ($declaration_copies) { // Create new paren tokens based on the first prefixed declaration. // Replace the new syntax with the legacy syntax. @@ -132,12 +132,12 @@ function csscrush__post_alias_fix_radialgradients ( $declaration_copies ) { preg_replace( '~\bat +(top|left|bottom|right|center)\b~i', '$1', - CssCrush::$process->fetchToken( $m[1][0] ) - ), 'p' ); + CssCrush::$process->fetchToken($m[1][0]) + ), 'p'); } // Swap in the new tokens on all the prefixed declarations. - foreach ( $declaration_copies as $prefixed_copy ) { + foreach ($declaration_copies as $prefixed_copy) { $prefixed_copy->value = str_replace( $original_parens, $replacement_parens, diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index aa70f83..e70d94e 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -38,7 +38,7 @@ public function __construct ($options) 'p' => array(), // Parens 'u' => array(), // URLs 't' => array(), // Traces - ); + ); $this->variables = array(); $this->misc = new stdclass(); $this->input = new stdclass(); @@ -90,7 +90,7 @@ public function release () $this->plugins, $this->aliases, $this->selectorAliases - ); + ); } // Establish the input and output directories and optionally test output dir. @@ -267,7 +267,7 @@ protected function getBoilerplate () 'datetime' => @date('Y-m-d H:i:s O'), 'year' => @date('Y'), 'version' => 'v' . CssCrush::$config->version, - ); + ); foreach ($boilerplate_matches[0] as $index => $tag) { $tag_name = $boilerplate_matches[1][$index]; @@ -416,8 +416,8 @@ protected function filterAliases () if (!( strpos($prop, $vendor) === 0 || strpos($val, $vendor) === 0 - ) - ) { + ) + ) { // Unset uneeded aliases. unset($this->aliases[$section][$property][$value][$index]); @@ -1134,7 +1134,7 @@ public function compile () '}' => "}\n", '{' => "{\n", ';' => ";\n", - ))->prepend("\n"); + ))->prepend("\n"); // Parse rules. $this->extractRules(); @@ -1189,7 +1189,7 @@ protected function decruft () // Compress hex codes. $patt->cruftyHex => '#$1$2$3', - )); + )); } diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php index 85e6256..36cb341 100644 --- a/lib/CssCrush/Regex.php +++ b/lib/CssCrush/Regex.php @@ -56,21 +56,21 @@ static public function init () $patt->rooted_number = '~^' . $classes->number . '$~'; // @-rules. - $patt->import = CssCrush_Regex::create( '@import\s+()\s?([^;]*);', 'iS' ); + $patt->import = CssCrush_Regex::create('@import\s+()\s?([^;]*);', 'iS'); $patt->charset = CssCrush_Regex::create('@charset\s+()\s*;', 'iS'); - $patt->variables = CssCrush_Regex::create( '@(?:define|variables) *\{ *(.*?) *\};?', 'iS' ); - $patt->mixin = CssCrush_Regex::create( '@mixin +() *\{ *(.*?) *\};?', 'iS' ); - $patt->abstract = CssCrush_Regex::create( '^@abstract +()', 'i' ); - $patt->selectorAlias = CssCrush_Regex::create( '@selector-alias +\:() +([^;]+) *;', 'iS' ); - $patt->ifDefine = CssCrush_Regex::create( '@ifdefine +(not +)?() *\{', 'iS' ); - $patt->fragmentDef = CssCrush_Regex::create( '@fragment +() *\{', 'iS' ); - $patt->fragmentCall = CssCrush_Regex::create( '@fragment +() *(\(|;)', 'iS' ); + $patt->variables = CssCrush_Regex::create('@(?:define|variables) *\{ *(.*?) *\};?', 'iS'); + $patt->mixin = CssCrush_Regex::create('@mixin +() *\{ *(.*?) *\};?', 'iS'); + $patt->abstract = CssCrush_Regex::create('^@abstract +()', 'i'); + $patt->selectorAlias = CssCrush_Regex::create('@selector-alias +\:() +([^;]+) *;', 'iS'); + $patt->ifDefine = CssCrush_Regex::create('@ifdefine +(not +)?() *\{', 'iS'); + $patt->fragmentDef = CssCrush_Regex::create('@fragment +() *\{', 'iS'); + $patt->fragmentCall = CssCrush_Regex::create('@fragment +() *(\(|;)', 'iS'); // Functions. - $patt->function = CssCrush_Regex::create( '()()', 'S' ); - $patt->varFunction = CssCrush_Regex::create( '\$\( *() *\)', 'S' ); - $patt->argFunction = CssCrush_Regex::createFunctionPatt( array( 'arg' ) ); - $patt->thisFunction = CssCrush_Regex::createFunctionPatt( array( 'this' ) ); + $patt->function = CssCrush_Regex::create('()()', 'S'); + $patt->varFunction = CssCrush_Regex::create('\$\( *() *\)', 'S'); + $patt->argFunction = CssCrush_Regex::createFunctionPatt(array('arg')); + $patt->thisFunction = CssCrush_Regex::createFunctionPatt(array('this')); $patt->commentAndString = '~ # Quoted string (to EOF if unmatched). @@ -104,7 +104,7 @@ static public function init () $patt->cruftyHex = '~\#([[:xdigit:]])\1([[:xdigit:]])\2([[:xdigit:]])\3~S'; } - static public function create ( $pattern_template, $flags = '', $delim = '~' ) + static public function create ($pattern_template, $flags = '', $delim = '~') { static $find, $replace; if (! $find) { @@ -117,7 +117,7 @@ static public function create ( $pattern_template, $flags = '', $delim = '~' ) return "$delim{$pattern}$delim{$flags}"; } - static public function matchAll ( $patt, $subject, $preprocess_patt = false, $offset = 0 ) + static public function matchAll ($patt, $subject, $preprocess_patt = false, $offset = 0) { if ($preprocess_patt) { // Assume case-insensitive. @@ -129,20 +129,20 @@ static public function matchAll ( $patt, $subject, $preprocess_patt = false, $of return $count ? $matches : array(); } - static public function createFunctionPatt ( $list, $include_math_function = false ) + static public function createFunctionPatt ($list, $include_math_function = false) { $question = ''; - if ( $include_math_function ) { + if ($include_math_function) { $question = '?'; // Signing on math bare parens. $list[] = '-'; } - foreach ( $list as &$fn_name ) { - $fn_name = preg_quote( $fn_name ); + foreach ($list as &$fn_name) { + $fn_name = preg_quote($fn_name); } - return CssCrush_Regex::create( '(' . implode( '|', $list ) . ')' . $question . '\(', 'iS' ); + return CssCrush_Regex::create('(' . implode('|', $list) . ')' . $question . '\(', 'iS'); } } diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php index 236c70a..100d42d 100644 --- a/lib/CssCrush/Rule.php +++ b/lib/CssCrush/Rule.php @@ -40,7 +40,7 @@ public function __construct ($selector_string = null, $declarations_string) if ( $process->addTracingStubs && preg_match_all($regex->t_token, $selector_string, $trace_tokens) - ) { + ) { $trace_token = array_pop($trace_tokens); $this->tracingStub = $process->fetchToken($trace_token[0]); foreach ($trace_tokens as $trace_token) { @@ -251,7 +251,7 @@ public function expandDataSet ($dataset, $property) 'border-right-color' => 'border-color', 'border-bottom-color' => 'border-color', 'border-left-color' => 'border-color', - ); + ); $dataset =& $this->{$dataset}; $property_group = isset($expandables[$property]) ? $expandables[$property] : null; @@ -311,7 +311,7 @@ public function expandDataSet ($dataset, $property) 'top-right', 'bottom-right', 'bottom-left', - ); + ); } else { $positions = array( @@ -407,7 +407,6 @@ public function applyExtendables () $extend_selectors[$new_readable] = $new_selector; } } - $ancestor->addExtendSelectors($extend_selectors); } } @@ -633,7 +632,7 @@ public function addFunctionAliases () $replacements['find'], $replacements['replace'], $copy->value - ); + ); $prefixed_copies[] = $copy; $rule_updated = true; } @@ -664,7 +663,7 @@ public function addFunctionAliases () '~(?value - ); + ); $prefixed_copies[] = $copy; $rule_updated = true; } @@ -719,7 +718,7 @@ public function addDeclarationAliases () $new_set[] = new CssCrush_Declaration( ! empty($pair[0]) ? $pair[0] : $declaration->property, $pair[1] - ); + ); $rule_updated = true; } } diff --git a/lib/CssCrush/Selector.php b/lib/CssCrush/Selector.php index 79c9112..6a2c802 100644 --- a/lib/CssCrush/Selector.php +++ b/lib/CssCrush/Selector.php @@ -10,37 +10,37 @@ class CssCrush_Selector public $readableValue; public $allowPrefix = true; - public function __construct ( $raw_selector, $associated_rule = null ) + public function __construct ($raw_selector, $associated_rule = null) { // Look for rooting prefix. - if ( strpos( $raw_selector, '^' ) === 0 ) { - $raw_selector = ltrim( $raw_selector, "^ \n\r\t" ); + if (strpos($raw_selector, '^') === 0) { + $raw_selector = ltrim($raw_selector, "^ \n\r\t"); $this->allowPrefix = false; } // Take readable value from original un-altered state. - $this->readableValue = CssCrush_Selector::makeReadable( $raw_selector ); + $this->readableValue = CssCrush_Selector::makeReadable($raw_selector); - CssCrush_Process::applySelectorAliases( $raw_selector ); + CssCrush_Process::applySelectorAliases($raw_selector); // Capture top-level paren groups. - CssCrush::$process->captureParens( $raw_selector ); + CssCrush::$process->captureParens($raw_selector); $this->value = $raw_selector; } public function __toString () { - if ( ! CssCrush::$process->minifyOutput ) { - $this->value = CssCrush_Selector::normalizeWhiteSpace( $this->value ); + if (! CssCrush::$process->minifyOutput) { + $this->value = CssCrush_Selector::normalizeWhiteSpace($this->value); } - return CssCrush_Selector::compatFilter( $this->value ); + return CssCrush_Selector::compatFilter($this->value); } - public function appendPseudo ( $pseudo ) + public function appendPseudo ($pseudo) { // Check to avoid doubling-up - if ( ! CssCrush_Stream::endsWith( $this->readableValue, $pseudo ) ) { + if (! CssCrush_Stream::endsWith($this->readableValue, $pseudo)) { $this->readableValue .= $pseudo; $this->value .= $pseudo; @@ -48,35 +48,35 @@ public function appendPseudo ( $pseudo ) return $this->readableValue; } - static public function compatFilter ( $str ) + static public function compatFilter ($str) { // Replace double-colons for backwards compatability. - if ( strpos( $str, '::' ) !== false ) { + if (strpos($str, '::') !== false) { $str = preg_replace( - '~::(after|before|first-(?:letter|line))~iS', ':$1', $str ); + '~::(after|before|first-(?:letter|line))~iS', ':$1', $str); } return $str; } - static public function normalizeWhiteSpace ( $str ) + static public function normalizeWhiteSpace ($str) { // Create space around combinators, then normalize whitespace. - $str = preg_replace( '~([>+]|\~(?!=))~S', ' $1 ', $str ); - return CssCrush_Util::normalizeWhiteSpace( $str ); + $str = preg_replace('~([>+]|\~(?!=))~S', ' $1 ', $str); + return CssCrush_Util::normalizeWhiteSpace($str); } - static function makeReadable ( $str ) + static function makeReadable ($str) { // Quick test for paren tokens. - if ( strpos( $str, '?p' ) !== false ) { - $str = CssCrush::$process->restoreTokens( $str, 'p' ); + if (strpos($str, '?p') !== false) { + $str = CssCrush::$process->restoreTokens($str, 'p'); } - $str = CssCrush_Selector::normalizeWhiteSpace( $str ); + $str = CssCrush_Selector::normalizeWhiteSpace($str); // Quick test for string tokens. - if ( strpos( $str, '?s' ) !== false ) { - $str = CssCrush::$process->restoreTokens( $str, 's' ); + if (strpos($str, '?s') !== false) { + $str = CssCrush::$process->restoreTokens($str, 's'); } return $str; diff --git a/lib/CssCrush/Stream.php b/lib/CssCrush/Stream.php index 018fd6a..7e02f9e 100644 --- a/lib/CssCrush/Stream.php +++ b/lib/CssCrush/Stream.php @@ -6,7 +6,7 @@ */ class CssCrush_Stream { - public function __construct ( $str ) + public function __construct ($str) { $this->raw = $str; } @@ -16,19 +16,19 @@ public function __toString () return $this->raw; } - static public function endsWith ( $haystack, $needle ) + static public function endsWith ($haystack, $needle) { - return substr( $haystack, -strlen( $needle ) ) === $needle; + return substr($haystack, -strlen($needle)) === $needle; } - public function update ( $str ) + public function update ($str) { $this->raw = $str; return $this; } - public function substr ( $start, $length = null ) + public function substr ($start, $length = null) { if (! isset($length)) { @@ -40,85 +40,85 @@ public function substr ( $start, $length = null ) } } - public function matchAll ( $patt, $preprocess_patt = false ) + public function matchAll ($patt, $preprocess_patt = false) { - return CssCrush_Regex::matchAll( $patt, $this->raw, $preprocess_patt ); + return CssCrush_Regex::matchAll($patt, $this->raw, $preprocess_patt); } - public function replace ( $find, $replacement ) + public function replace ($find, $replacement) { - $this->raw = str_replace( $find, $replacement, $this->raw ); + $this->raw = str_replace($find, $replacement, $this->raw); return $this; } - public function replaceHash ( $replacements ) + public function replaceHash ($replacements) { - if ( $replacements ) { + if ($replacements) { $this->raw = str_replace( - array_keys( $replacements ), - array_values( $replacements ), - $this->raw ); + array_keys($replacements), + array_values($replacements), + $this->raw); } return $this; } - public function pregReplace ( $patt, $replacement ) + public function pregReplace ($patt, $replacement) { - $this->raw = preg_replace( $patt, $replacement, $this->raw ); + $this->raw = preg_replace($patt, $replacement, $this->raw); return $this; } - public function pregReplaceCallback ( $patt, $callback ) + public function pregReplaceCallback ($patt, $callback) { - $this->raw = preg_replace_callback( $patt, $callback, $this->raw ); + $this->raw = preg_replace_callback($patt, $callback, $this->raw); return $this; } - public function pregReplaceHash ( $replacements ) + public function pregReplaceHash ($replacements) { - if ( $replacements ) { + if ($replacements) { $this->raw = preg_replace( - array_keys( $replacements ), - array_values( $replacements ), - $this->raw ); + array_keys($replacements), + array_values($replacements), + $this->raw); } return $this; } - public function append ( $append ) + public function append ($append) { $this->raw .= $append; return $this; } - public function prepend ( $prepend ) + public function prepend ($prepend) { $this->raw = $prepend . $this->raw; return $this; } - public function splice ( $replacement, $offset, $length = null ) + public function splice ($replacement, $offset, $length = null) { - $this->raw = substr_replace( $this->raw, $replacement, $offset, $length ); + $this->raw = substr_replace($this->raw, $replacement, $offset, $length); return $this; } public function trim () { - $this->raw = trim( $this->raw ); + $this->raw = trim($this->raw); return $this; } public function rTrim () { - $this->raw = rtrim( $this->raw ); + $this->raw = rtrim($this->raw); return $this; } public function lTrim () { - $this->raw = ltrim( $this->raw ); + $this->raw = ltrim($this->raw); return $this; } } diff --git a/lib/CssCrush/Url.php b/lib/CssCrush/Url.php index 7f1b5bb..7f1fb31 100644 --- a/lib/CssCrush/Url.php +++ b/lib/CssCrush/Url.php @@ -81,14 +81,14 @@ public function evaluate () $leading_variable = strpos($this->value, '$(') === 0; // Normalize './' led paths. - $this->value = preg_replace( '~^\.\/+~i', '', $this->value ); + $this->value = preg_replace('~^\.\/+~i', '', $this->value); if ($leading_variable || ($this->value !== '' && $this->value[0] === '/')) { $type = 'rooted'; } // Normalize slashes. - $this->value = rtrim( preg_replace( '~[\\\\/]+~', '/', $this->value ), '/' ); + $this->value = rtrim(preg_replace('~[\\\\/]+~', '/', $this->value), '/'); } $this->setType($type); @@ -160,8 +160,8 @@ public function toData () return $this; } - $mime_type = $allowed_file_extensions[ $file_ext ]; - $base64 = base64_encode( file_get_contents( $file ) ); + $mime_type = $allowed_file_extensions[$file_ext]; + $base64 = base64_encode(file_get_contents($file)); $this->value = "data:$mime_type;base64,$base64"; $this->setType('data')->protocol = 'data'; diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php index 257532c..a28ca98 100644 --- a/lib/CssCrush/Util.php +++ b/lib/CssCrush/Util.php @@ -7,64 +7,64 @@ class CssCrush_Util { // Create html attribute string from array. - static public function htmlAttributes ( array $attributes ) + static public function htmlAttributes (array $attributes) { $attr_string = ''; - foreach ( $attributes as $name => $value ) { - $value = htmlspecialchars( $value, ENT_COMPAT, 'UTF-8', false ); + foreach ($attributes as $name => $value) { + $value = htmlspecialchars($value, ENT_COMPAT, 'UTF-8', false); $attr_string .= " $name=\"$value\""; } return $attr_string; } - static public function normalizePath ( $path, $strip_drive_letter = false ) + static public function normalizePath ($path, $strip_drive_letter = false) { - if ( $strip_drive_letter ) { - $path = preg_replace( '~^[a-z]\:~i', '', $path ); + if ($strip_drive_letter) { + $path = preg_replace('~^[a-z]\:~i', '', $path); } // Backslashes and repeat slashes to a single forward slash. - $path = rtrim( preg_replace( '~[\\\\/]+~', '/', $path ), '/' ); + $path = rtrim(preg_replace('~[\\\\/]+~', '/', $path), '/'); // Removing redundant './'. - $path = str_replace( '/./', '/', $path ); - if ( strpos( $path, './' ) === 0 ) { - $path = substr( $path, 2 ); + $path = str_replace('/./', '/', $path); + if (strpos($path, './') === 0) { + $path = substr($path, 2); } - return CssCrush_Util::simplifyPath( $path ); + return CssCrush_Util::simplifyPath($path); } - static public function simplifyPath ( $path ) + static public function simplifyPath ($path) { // Reduce redundant path segments (issue #32): // e.g 'foo/../bar' => 'bar' $patt = '~[^/.]+/\.\./~S'; - while ( preg_match( $patt, $path ) ) { - $path = preg_replace( $patt, '', $path ); + while (preg_match($patt, $path)) { + $path = preg_replace($patt, '', $path); } return $path; } static public function find () { - foreach ( func_get_args() as $file ) { + foreach (func_get_args() as $file) { $file_path = CssCrush::$config->location . '/' . $file; - if ( file_exists( $file_path ) ) { + if (file_exists($file_path)) { return $file_path; } } return false; } - static public function stripCommentTokens ( $str ) + static public function stripCommentTokens ($str) { - return preg_replace( CssCrush_Regex::$patt->c_token, '', $str ); + return preg_replace(CssCrush_Regex::$patt->c_token, '', $str); } - static public function normalizeWhiteSpace ( $str ) + static public function normalizeWhiteSpace ($str) { static $find, $replace; - if ( ! $find ) { + if (! $find) { $replacements = array( // Convert all whitespace sequences to a single space. '~\s+~S' => ' ', @@ -73,118 +73,118 @@ static public function normalizeWhiteSpace ( $str ) // Trim whitespace around delimiters and special characters. '~ ?([;,]) ?~S' => '$1', ); - $find = array_keys( $replacements ); - $replace = array_values( $replacements ); + $find = array_keys($replacements); + $replace = array_values($replacements); } - return preg_replace( $find, $replace, $str ); + return preg_replace($find, $replace, $str); } - static public function splitDelimList ( $str, $delim = ',', $trim = true ) + static public function splitDelimList ($str, $delim = ',', $trim = true) { - $do_preg_split = strlen( $delim ) > 1 ? true : false; + $do_preg_split = strlen($delim) > 1 ? true : false; - if ( ! $do_preg_split && strpos( $str, $delim ) === false ) { - if ( $trim ) { - $str = trim( $str ); + if (! $do_preg_split && strpos($str, $delim) === false) { + if ($trim) { + $str = trim($str); } - return array( $str ); + return array($str); } - if ( strpos( $str, '(' ) !== false ) { + if (strpos($str, '(') !== false) { $match_count - = preg_match_all( CssCrush_Regex::$patt->balancedParens, $str, $matches ); + = preg_match_all(CssCrush_Regex::$patt->balancedParens, $str, $matches); } else { $match_count = 0; } - if ( $match_count ) { + if ($match_count) { $keys = array(); - foreach ( $matches[0] as $index => &$value ) { + foreach ($matches[0] as $index => &$value) { $keys[] = "?$index?"; } - $str = str_replace( $matches[0], $keys, $str ); + $str = str_replace($matches[0], $keys, $str); } - if ( $do_preg_split ) { - $list = preg_split( '~' . $delim . '~', $str ); + if ($do_preg_split) { + $list = preg_split('~' . $delim . '~', $str); } else { - $list = explode( $delim, $str ); + $list = explode($delim, $str); } - if ( $match_count ) { - foreach ( $list as &$value ) { - $value = str_replace( $keys, $matches[0], $value ); + if ($match_count) { + foreach ($list as &$value) { + $value = str_replace($keys, $matches[0], $value); } } - if ( $trim ) { + if ($trim) { // Trim items and remove empty strings. - $list = array_filter( array_map( 'trim', $list ), 'strlen' ); + $list = array_filter(array_map('trim', $list), 'strlen'); } return $list; } - static public function parseBlock ( $str, $keyed = false, $strip_comment_tokens = true ) + static public function parseBlock ($str, $keyed = false, $strip_comment_tokens = true) { - if ( $strip_comment_tokens ) { - $str = CssCrush_Util::stripCommentTokens( $str ); + if ($strip_comment_tokens) { + $str = CssCrush_Util::stripCommentTokens($str); } - $declarations = preg_split( '~\s*;\s*~', $str, null, PREG_SPLIT_NO_EMPTY ); + $declarations = preg_split('~\s*;\s*~', $str, null, PREG_SPLIT_NO_EMPTY); $out = array(); - foreach ( $declarations as $declaration ) { - $colon_pos = strpos( $declaration, ':' ); - if ( $colon_pos === -1 ) { + foreach ($declarations as $declaration) { + $colon_pos = strpos($declaration, ':'); + if ($colon_pos === -1) { continue; } - $property = trim( substr( $declaration, 0, $colon_pos ) ); - $value = trim( substr( $declaration, $colon_pos + 1 ) ); + $property = trim(substr($declaration, 0, $colon_pos)); + $value = trim(substr($declaration, $colon_pos + 1)); // Empty strings are ignored. if (! isset($property[0]) || ! isset($value[0])) { continue; } - if ( $keyed ) { - $out[ $property ] = $value; + if ($keyed) { + $out[$property] = $value; } else { - $out[] = array( $property, $value ); + $out[] = array($property, $value); } } return $out; } - static public function getLinkBetweenDirs ( $dir1, $dir2 ) + static public function getLinkBetweenDirs ($dir1, $dir2) { // Normalise the paths. - $dir1 = trim( CssCrush_Util::normalizePath( $dir1, true ), '/' ); - $dir2 = trim( CssCrush_Util::normalizePath( $dir2, true ), '/' ); + $dir1 = trim(CssCrush_Util::normalizePath($dir1, true), '/'); + $dir2 = trim(CssCrush_Util::normalizePath($dir2, true), '/'); // The link between. $link = ''; - if ( $dir1 != $dir2 ) { + if ($dir1 != $dir2) { // Split the directory paths into arrays so we can compare segment by segment. - $dir1_segs = explode( '/', $dir1 ); - $dir2_segs = explode( '/', $dir2 ); + $dir1_segs = explode('/', $dir1); + $dir2_segs = explode('/', $dir2); // Shift the segments until they are on different branches. - while ( isset( $dir1_segs[0] ) && isset( $dir2_segs[0] ) && ( $dir1_segs[0] === $dir2_segs[0] ) ) { - array_shift( $dir1_segs ); - array_shift( $dir2_segs ); + while (isset($dir1_segs[0]) && isset($dir2_segs[0]) && ($dir1_segs[0] === $dir2_segs[0])) { + array_shift($dir1_segs); + array_shift($dir2_segs); } - $link = str_repeat( '../', count( $dir1_segs ) ) . implode( '/', $dir2_segs ); + $link = str_repeat('../', count($dir1_segs)) . implode('/', $dir2_segs); } // Add closing slash. - return $link !== '' ? rtrim( $link, '/' ) . '/' : ''; + return $link !== '' ? rtrim($link, '/') . '/' : ''; } } diff --git a/lib/functions.php b/lib/functions.php index b45c540..4ceb035 100644 --- a/lib/functions.php +++ b/lib/functions.php @@ -4,32 +4,32 @@ * Functions for procedural style API. * */ -function csscrush_file ( $file, $options = null ) { - return CssCrush::file( $file, $options ); +function csscrush_file ($file, $options = null) { + return CssCrush::file($file, $options); } -function csscrush_tag ( $file, $options = null, $attributes = array() ) { - return CssCrush::tag( $file, $options, $attributes ); +function csscrush_tag ($file, $options = null, $attributes = array()) { + return CssCrush::tag($file, $options, $attributes); } -function csscrush_inline ( $file, $options = null, $attributes = array() ) { - return CssCrush::inline( $file, $options, $attributes ); +function csscrush_inline ($file, $options = null, $attributes = array()) { + return CssCrush::inline($file, $options, $attributes); } -function csscrush_string ( $string, $options = null ) { - return CssCrush::string( $string, $options ); +function csscrush_string ($string, $options = null) { + return CssCrush::string($string, $options); } -function csscrush_globalvars ( $vars ) { - return CssCrush::globalVars( $vars ); +function csscrush_globalvars ($vars) { + return CssCrush::globalVars($vars); } -function csscrush_clearcache ( $dir = '' ) { - return CssCrush::clearcache( $dir ); +function csscrush_clearcache ($dir = '') { + return CssCrush::clearcache($dir); } -function csscrush_stat ( $name = null ) { - return CssCrush::stat( $name ); +function csscrush_stat ($name = null) { + return CssCrush::stat($name); } function csscrush_version () { diff --git a/misc/formatters.php b/misc/formatters.php index 209d1c0..21cbb1b 100644 --- a/misc/formatters.php +++ b/misc/formatters.php @@ -10,56 +10,56 @@ 'block' => 'csscrush__fmtr_block', ); -function csscrush__fmtr_single ( $rule ) { +function csscrush__fmtr_single ($rule) { $EOL = CssCrush::$process->newline; - if ( $stub = $rule->tracingStub ) { + if ($stub = $rule->tracingStub) { $stub .= $EOL; } - $comments = implode( '', $rule->comments ); - if ( $comments ) { + $comments = implode('', $rule->comments); + if ($comments) { $comments = "$EOL$comments"; } - $selectors = implode( ", ", $rule->selectors ); - $block = implode( "; ", $rule->declarations ); + $selectors = implode(", ", $rule->selectors); + $block = implode("; ", $rule->declarations); return "$comments$stub$selectors { $block; }$EOL"; } -function csscrush__fmtr_padded ( $rule, $padding = 40 ) { +function csscrush__fmtr_padded ($rule, $padding = 40) { $EOL = CssCrush::$process->newline; - if ( $stub = $rule->tracingStub ) { + if ($stub = $rule->tracingStub) { $stub .= $EOL; } - $comments = implode( '', $rule->comments ); - if ( $comments ) { + $comments = implode('', $rule->comments); + if ($comments) { $comments = "$EOL$comments"; } - $selectors = implode( ", ", $rule->selectors ); - $block = implode( "; ", $rule->declarations ); + $selectors = implode(", ", $rule->selectors); + $block = implode("; ", $rule->declarations); - if ( strlen( $selectors ) > $padding ) { - $padding = str_repeat( ' ', $padding ); + if (strlen($selectors) > $padding) { + $padding = str_repeat(' ', $padding); return "$comments$stub$selectors$EOL$padding { $block; }$EOL"; } else { - $selectors = str_pad( $selectors, $padding ); + $selectors = str_pad($selectors, $padding); return "$comments$stub$selectors { $block; }$EOL"; } } -function csscrush__fmtr_block ( $rule, $indent = ' ' ) { +function csscrush__fmtr_block ($rule, $indent = ' ') { $EOL = CssCrush::$process->newline; - if ( $stub = $rule->tracingStub ) { + if ($stub = $rule->tracingStub) { $stub .= $EOL; } - $comments = implode( '', $rule->comments ); - $selectors = implode( ",$EOL", $rule->selectors ); - $block = implode( ";$EOL$indent", $rule->declarations ); + $comments = implode('', $rule->comments); + $selectors = implode(",$EOL", $rule->selectors); + $block = implode(";$EOL$indent", $rule->declarations); return "$comments$stub$selectors {{$EOL}$indent$block;$EOL$indent}$EOL$EOL"; } diff --git a/plugins/ease.php b/plugins/ease.php index ef2a47d..82079c5 100644 --- a/plugins/ease.php +++ b/plugins/ease.php @@ -1,6 +1,6 @@ 'csscrush__enable_ease', 'disable' => 'csscrush__disable_ease', )); function csscrush__enable_ease () { - CssCrush_Hook::add( 'rule_prealias', 'csscrush__ease' ); + CssCrush_Hook::add('rule_prealias', 'csscrush__ease'); } function csscrush__disable_ease () { - CssCrush_Hook::remove( 'rule_prealias', 'csscrush__ease' ); + CssCrush_Hook::remove('rule_prealias', 'csscrush__ease'); } function csscrush__ease (CssCrush_Rule $rule) { diff --git a/plugins/hocus-pocus.php b/plugins/hocus-pocus.php index 85fd9ab..afab817 100644 --- a/plugins/hocus-pocus.php +++ b/plugins/hocus-pocus.php @@ -1,7 +1,6 @@ 'csscrush__enable_hocus_pocus', 'disable' => 'csscrush__disable_hocus_pocus', )); function csscrush__enable_hocus_pocus () { - CssCrush::$config->selectorAliases[ 'hocus' ] = ':any(:hover,:focus)'; - CssCrush::$config->selectorAliases[ 'pocus' ] = ':any(:hover,:focus,:active)'; + CssCrush::$config->selectorAliases['hocus'] = ':any(:hover,:focus)'; + CssCrush::$config->selectorAliases['pocus'] = ':any(:hover,:focus,:active)'; } function csscrush__disable_hocus_pocus () { - unset( CssCrush::$config->selectorAliases[ 'hocus' ] ); - unset( CssCrush::$config->selectorAliases[ 'pocus' ] ); + unset( CssCrush::$config->selectorAliases['hocus']); + unset( CssCrush::$config->selectorAliases['pocus']); } diff --git a/plugins/hsl-to-hex.php b/plugins/hsl-to-hex.php index df3511e..7bd38a1 100644 --- a/plugins/hsl-to-hex.php +++ b/plugins/hsl-to-hex.php @@ -1,43 +1,42 @@ 'csscrush__enable_hsl_to_hex', 'disable' => 'csscrush__disable_hsl_to_hex', )); function csscrush__enable_hsl_to_hex () { - CssCrush_Hook::add( 'rule_postalias', 'csscrush__hsl_to_hex' ); + CssCrush_Hook::add('rule_postalias', 'csscrush__hsl_to_hex'); } function csscrush__disable_hsl_to_hex () { - CssCrush_Hook::remove( 'rule_postalias', 'csscrush__hsl_to_hex' ); + CssCrush_Hook::remove('rule_postalias', 'csscrush__hsl_to_hex'); } -function csscrush__hsl_to_hex ( CssCrush_Rule $rule ) { +function csscrush__hsl_to_hex (CssCrush_Rule $rule) { static $hsl_patt; if (! $hsl_patt) { $hsl_patt = CssCrush_Regex::create('hsl()', 'i'); } - foreach ( $rule as &$declaration ) { + foreach ($rule as &$declaration) { - if ( ! $declaration->skip && isset( $declaration->functions[ 'hsl' ] ) ) { - while ( preg_match( $hsl_patt, $declaration->value, $m ) ) { + if (! $declaration->skip && isset($declaration->functions['hsl'])) { + while (preg_match($hsl_patt, $declaration->value, $m)) { $token = $m[1]; - $color = new CssCrush_Color( 'hsl' . CssCrush::$process->fetchToken( $token ) ); - CssCrush::$process->releaseToken( $token ); - $declaration->value = str_replace( $m[0], $color->getHex(), $declaration->value ); + $color = new CssCrush_Color('hsl' . CssCrush::$process->fetchToken($token)); + CssCrush::$process->releaseToken($token); + $declaration->value = str_replace($m[0], $color->getHex(), $declaration->value); } } } diff --git a/plugins/ie-clip.php b/plugins/ie-clip.php index 74bbda7..8ee853f 100644 --- a/plugins/ie-clip.php +++ b/plugins/ie-clip.php @@ -1,7 +1,7 @@ propertyCount( 'clip' ) !== 1 ) { + if ($rule->propertyCount('clip') !== 1) { return; } $new_set = array(); - foreach ( $rule as $declaration ) { + foreach ($rule as $declaration) { $new_set[] = $declaration; if ( $declaration->skip || - $declaration->property !== 'clip' + $declaration->property !== 'clip' ) { continue; } - $new_set[] = new CssCrush_Declaration( '*clip', str_replace( ',', ' ', $declaration->getFullValue() ) ); + $new_set[] = new CssCrush_Declaration('*clip', str_replace( ',', ' ', $declaration->getFullValue())); } - $rule->setDeclarations( $new_set ); + $rule->setDeclarations($new_set); } diff --git a/plugins/ie-filter.php b/plugins/ie-filter.php index e01befa..6521b09 100644 --- a/plugins/ie-filter.php +++ b/plugins/ie-filter.php @@ -16,27 +16,27 @@ * zoom: 1; */ -CssCrush_Plugin::register( 'ie-filter', array( +CssCrush_Plugin::register('ie-filter', array( 'enable' => 'csscrush__enable_ie_filter', 'disable' => 'csscrush__disable_ie_filter', )); function csscrush__enable_ie_filter () { - CssCrush_Hook::add( 'rule_postalias', 'csscrush__ie_filter' ); + CssCrush_Hook::add('rule_postalias', 'csscrush__ie_filter'); } function csscrush__disable_ie_filter () { - CssCrush_Hook::remove( 'rule_postalias', 'csscrush__ie_filter' ); + CssCrush_Hook::remove('rule_postalias', 'csscrush__ie_filter'); } -function csscrush__ie_filter ( CssCrush_Rule $rule ) { +function csscrush__ie_filter (CssCrush_Rule $rule) { - if ( $rule->propertyCount( '-ms-filter' ) < 1 ) { + if ($rule->propertyCount('-ms-filter') < 1) { return; } $filter_prefix = 'progid:DXImageTransform.Microsoft.'; $new_set = array(); - foreach ( $rule as $declaration ) { + foreach ($rule as $declaration) { if ( $declaration->skip || $declaration->property !== '-ms-filter' @@ -44,24 +44,24 @@ function csscrush__ie_filter ( CssCrush_Rule $rule ) { $new_set[] = $declaration; continue; } - $list = array_map( 'trim', explode( ',', $declaration->value ) ); - foreach ( $list as &$item ) { + $list = array_map('trim', explode(',', $declaration->value)); + foreach ($list as &$item) { if ( - strpos( $item, $filter_prefix ) !== 0 && - strpos( $item, 'alpha' ) !== 0 // Shortcut syntax permissable on alpha + strpos($item, $filter_prefix) !== 0 && + strpos($item, 'alpha') !== 0 // Shortcut syntax permissable on alpha ) { - $item = $filter_prefix . ucfirst( $item ); + $item = $filter_prefix . ucfirst($item); } } - $declaration->value = implode( ',', $list ); - if ( ! $rule->propertyCount( 'zoom' ) ) { + $declaration->value = implode(',', $list); + if (! $rule->propertyCount('zoom')) { // Filters need hasLayout - $new_set[] = new CssCrush_Declaration( 'zoom', 1 ); + $new_set[] = new CssCrush_Declaration('zoom', 1); } // Quoted version for -ms-filter IE >= 8 - $new_set[] = new CssCrush_Declaration( '-ms-filter', "\"$declaration->value\"" ); + $new_set[] = new CssCrush_Declaration('-ms-filter', "\"$declaration->value\""); // Star escaped property for IE < 8 - $new_set[] = new CssCrush_Declaration( '*filter', $declaration->value ); + $new_set[] = new CssCrush_Declaration('*filter', $declaration->value); } - $rule->setDeclarations( $new_set ); + $rule->setDeclarations($new_set); } diff --git a/plugins/ie-inline-block.php b/plugins/ie-inline-block.php index e0ba25d..1fcb372 100644 --- a/plugins/ie-inline-block.php +++ b/plugins/ie-inline-block.php @@ -1,6 +1,6 @@ 'csscrush__enable_ie_inline_block', 'disable' => 'csscrush__disable_ie_inline_block', )); function csscrush__enable_ie_inline_block () { - CssCrush_Hook::add( 'rule_postalias', 'csscrush__ie_inline_block' ); + CssCrush_Hook::add('rule_postalias', 'csscrush__ie_inline_block'); } function csscrush__disable_ie_inline_block () { - CssCrush_Hook::remove( 'rule_postalias', 'csscrush__ie_inline_block' ); + CssCrush_Hook::remove('rule_postalias', 'csscrush__ie_inline_block'); } -function csscrush__ie_inline_block ( CssCrush_Rule $rule ) { +function csscrush__ie_inline_block (CssCrush_Rule $rule) { - if ( $rule->propertyCount( 'display' ) < 1 ) { + if ($rule->propertyCount('display') < 1) { return; } $stack = array(); - foreach ( $rule as $declaration ) { + foreach ($rule as $declaration) { $stack[] = $declaration; $is_display = $declaration->property === 'display'; if ( $declaration->skip || ! $is_display || - $is_display && $declaration->value !== 'inline-block' ) { + $is_display && $declaration->value !== 'inline-block') { continue; } - $stack[] = new CssCrush_Declaration( '*display', 'inline' ); - $stack[] = new CssCrush_Declaration( '*zoom', 1 ); + $stack[] = new CssCrush_Declaration('*display', 'inline'); + $stack[] = new CssCrush_Declaration('*zoom', 1); } - $rule->setDeclarations( $stack ); + $rule->setDeclarations($stack); } diff --git a/plugins/ie-min-height.php b/plugins/ie-min-height.php index 146a6e1..03d8ea0 100644 --- a/plugins/ie-min-height.php +++ b/plugins/ie-min-height.php @@ -1,6 +1,6 @@ 'csscrush__enable_ie_min_height', 'disable' => 'csscrush__disable_ie_min_height', )); function csscrush__enable_ie_min_height () { - CssCrush_Hook::add( 'rule_postalias', 'csscrush__ie_min_height' ); + CssCrush_Hook::add('rule_postalias', 'csscrush__ie_min_height'); } function csscrush__disable_ie_min_height () { - CssCrush_Hook::remove( 'rule_postalias', 'csscrush__ie_min_height' ); + CssCrush_Hook::remove('rule_postalias', 'csscrush__ie_min_height'); } -function csscrush__ie_min_height ( CssCrush_Rule $rule ) { +function csscrush__ie_min_height (CssCrush_Rule $rule) { - if ( $rule->propertyCount( 'min-height' ) < 1 ) { + if ($rule->propertyCount('min-height') < 1) { return; } $new_set = array(); - foreach ( $rule as $declaration ) { + foreach ($rule as $declaration) { $new_set[] = $declaration; - if ( + if ( $declaration->skip || - $declaration->property !== 'min-height' ) { + $declaration->property !== 'min-height') { continue; } - $new_set[] = new CssCrush_Declaration( '_height', $declaration->value ); + $new_set[] = new CssCrush_Declaration('_height', $declaration->value); } - $rule->setDeclarations( $new_set ); + $rule->setDeclarations($new_set); } diff --git a/plugins/ie-opacity.php b/plugins/ie-opacity.php index 10a8f91..be09ba9 100755 --- a/plugins/ie-opacity.php +++ b/plugins/ie-opacity.php @@ -1,10 +1,10 @@ 'csscrush__enable_ie_opacity', 'disable' => 'csscrush__disable_ie_opacity', )); function csscrush__enable_ie_opacity () { - CssCrush_Hook::add( 'rule_postalias', 'csscrush__ie_opacity' ); + CssCrush_Hook::add('rule_postalias', 'csscrush__ie_opacity'); } function csscrush__disable_ie_opacity () { - CssCrush_Hook::remove( 'rule_postalias', 'csscrush__ie_opacity' ); + CssCrush_Hook::remove('rule_postalias', 'csscrush__ie_opacity'); } -function csscrush__ie_opacity ( CssCrush_Rule $rule ) { +function csscrush__ie_opacity (CssCrush_Rule $rule) { - if ( $rule->propertyCount( 'opacity' ) < 1 ) { + if ($rule->propertyCount('opacity') < 1) { return; } $new_set = array(); - foreach ( $rule as $declaration ) { + foreach ($rule as $declaration) { $new_set[] = $declaration; if ( $declaration->skip || @@ -41,15 +41,15 @@ function csscrush__ie_opacity ( CssCrush_Rule $rule ) { } $opacity = (float) $declaration->value; - $opacity = round( $opacity * 100 ); + $opacity = round($opacity * 100); - if ( ! $rule->propertyCount( 'zoom' ) ) { + if (! $rule->propertyCount('zoom')) { // Filters need hasLayout - $new_set[] = new CssCrush_Declaration( 'zoom', 1 ); + $new_set[] = new CssCrush_Declaration('zoom', 1); } $value = "alpha(opacity=$opacity)"; - $new_set[] = new CssCrush_Declaration( '-ms-filter', "\"$value\"" ); - $new_set[] = new CssCrush_Declaration( '*filter', $value ); + $new_set[] = new CssCrush_Declaration('-ms-filter', "\"$value\""); + $new_set[] = new CssCrush_Declaration('*filter', $value); } - $rule->setDeclarations( $new_set ); + $rule->setDeclarations($new_set); } diff --git a/plugins/initial.php b/plugins/initial.php index fae630e..c462bf9 100644 --- a/plugins/initial.php +++ b/plugins/initial.php @@ -15,32 +15,32 @@ * */ -CssCrush_Plugin::register( 'initial', array( +CssCrush_Plugin::register('initial', array( 'enable' => 'csscrush__enable_initial', 'disable' => 'csscrush__disable_initial', )); function csscrush__enable_initial () { - CssCrush_Hook::add( 'rule_prealias', 'csscrush__initial' ); + CssCrush_Hook::add('rule_prealias', 'csscrush__initial'); } function csscrush__disable_initial () { - CssCrush_Hook::remove( 'rule_prealias', 'csscrush__initial' ); + CssCrush_Hook::remove('rule_prealias', 'csscrush__initial'); } -function csscrush__initial ( CssCrush_Rule $rule ) { +function csscrush__initial (CssCrush_Rule $rule) { static $initial_values; - if ( ! $initial_values ) { - if ( ! ( $initial_values = @parse_ini_file( CssCrush::$config->location . '/misc/initial-values.ini' ) ) ) { - trigger_error( __METHOD__ . ": Initial keywords file could not be parsed.\n", E_USER_NOTICE ); + if (! $initial_values) { + if (! ($initial_values = @parse_ini_file(CssCrush::$config->location . '/misc/initial-values.ini'))) { + trigger_error(__METHOD__ . ": Initial keywords file could not be parsed.\n", E_USER_NOTICE); return; } } - foreach ( $rule as &$declaration ) { - if ( ! $declaration->skip && 'initial' === $declaration->value ) { - if ( isset( $initial_values[ $declaration->property ] ) ) { + foreach ($rule as &$declaration) { + if (! $declaration->skip && 'initial' === $declaration->value) { + if (isset($initial_values[$declaration->property])) { $declaration->value = $initial_values[ $declaration->property ]; } else { diff --git a/plugins/legacy-flexbox.php b/plugins/legacy-flexbox.php index e5f8d6b..de97371 100644 --- a/plugins/legacy-flexbox.php +++ b/plugins/legacy-flexbox.php @@ -1,6 +1,6 @@ 'csscrush__enable_legacy_flexbox', 'disable' => 'csscrush__disable_legacy_flexbox', )); function csscrush__enable_legacy_flexbox () { - CssCrush_Hook::add( 'rule_prealias', 'csscrush__legacy_flexbox' ); + CssCrush_Hook::add('rule_prealias', 'csscrush__legacy_flexbox'); } function csscrush__disable_legacy_flexbox () { - CssCrush_Hook::remove( 'rule_prealias', 'csscrush__legacy_flexbox' ); + CssCrush_Hook::remove('rule_prealias', 'csscrush__legacy_flexbox'); } -function csscrush__legacy_flexbox ( CssCrush_Rule $rule ) { +function csscrush__legacy_flexbox (CssCrush_Rule $rule) { static $flex_related_props = array( 'align-items' => true, @@ -77,70 +77,70 @@ function csscrush__legacy_flexbox ( CssCrush_Rule $rule ) { ); $properties =& $rule->properties; - $intersect_props = array_intersect_key( $properties, $flex_related_props ); + $intersect_props = array_intersect_key($properties, $flex_related_props); // Checking for flex related properties or 'display:flex'. // First checking the display property as it's pretty common. - if ( isset( $properties[ 'display' ] ) ) { - foreach ( $rule->declarations as $declaration ) { - if ( $declaration->property === 'display' && - ( $declaration->value === 'flex' || $declaration->value === 'inline-flex' ) ) { + if (isset($properties['display'])) { + foreach ($rule->declarations as $declaration) { + if ($declaration->property === 'display' && + ($declaration->value === 'flex' || $declaration->value === 'inline-flex')) { // Add 'display' to the intersected properties. - $intersect_props[ 'display' ] = true; + $intersect_props['display'] = true; break; } } } // Bail early if the rule has no flex related properties. - if ( ! $intersect_props ) { + if (! $intersect_props) { return; } - $declaration_aliases =& CssCrush::$process->aliases[ 'declarations' ]; + $declaration_aliases =& CssCrush::$process->aliases['declarations']; $stack = array(); $rule_updated = false; - foreach ( $rule as $declaration ) { + foreach ($rule as $declaration) { $prop = $declaration->property; $value = $declaration->value; - if ( ! isset( $intersect_props[ $prop ] ) ) { + if (! isset($intersect_props[$prop])) { $stack[] = $declaration; continue; } - switch ( $prop ) { + switch ($prop) { // display:flex => display:-*-box. case 'display': if ( // Treat flex and inline-flex the same in this case. - ( $value === 'flex' || $value === 'inline-flex' ) && - isset( $declaration_aliases[ 'display' ][ 'box' ] ) ) { - foreach ( $declaration_aliases[ 'display' ][ 'box' ] as $pair ) { - $stack[] = new CssCrush_Declaration( $pair[0], $pair[1] ); + ($value === 'flex' || $value === 'inline-flex') && + isset($declaration_aliases['display']['box'])) { + foreach ($declaration_aliases['display']['box'] as $pair) { + $stack[] = new CssCrush_Declaration($pair[0], $pair[1]); $rule_updated = true; } } break; case 'align-items': - $rule_updated = csscrush__flex_align_items( $value, $stack ); + $rule_updated = csscrush__flex_align_items($value, $stack); break; case 'flex': - $rule_updated = csscrush__flex( $value, $stack ); + $rule_updated = csscrush__flex($value, $stack); break; case 'flex-direction': - $rule_updated = csscrush__flex_direction( $value, $stack ); + $rule_updated = csscrush__flex_direction($value, $stack); break; case 'flex-grow': - $rule_updated = csscrush__flex_grow( $value, $stack ); + $rule_updated = csscrush__flex_grow($value, $stack); break; case 'flex-wrap': @@ -150,15 +150,15 @@ function csscrush__legacy_flexbox ( CssCrush_Rule $rule ) { // - http://stackoverflow.com/questions/5010083/\ // css3-flex-box-specifying-multiple-box-lines-doesnt-work - // $rule_updated = csscrush__flex_wrap( $value, $stack ); + // $rule_updated = csscrush__flex_wrap($value, $stack); break; case 'justify-content': - $rule_updated = csscrush__flex_justify_content( $value, $stack ); + $rule_updated = csscrush__flex_justify_content($value, $stack); break; case 'order': - $rule_updated = csscrush__flex_order( $value, $stack ); + $rule_updated = csscrush__flex_order($value, $stack); break; // Shorthand values. @@ -166,12 +166,12 @@ function csscrush__legacy_flexbox ( CssCrush_Rule $rule ) { // <‘flex-direction’> || <‘flex-wrap’> - $args = explode( ' ', $value ); - $direction = isset( $args[0] ) ? $args[0] : 'initial'; - $wrap = isset( $args[1] ) ? $args[1] : 'initial'; + $args = explode(' ', $value); + $direction = isset($args[0]) ? $args[0] : 'initial'; + $wrap = isset($args[1]) ? $args[1] : 'initial'; - $rule_updated = csscrush__flex_direction( $direction, $stack ); - // $rule_updated = csscrush__flex_wrap( $wrap, $stack ); + $rule_updated = csscrush__flex_direction($direction, $stack); + // $rule_updated = csscrush__flex_wrap($wrap, $stack); break; } @@ -180,19 +180,19 @@ function csscrush__legacy_flexbox ( CssCrush_Rule $rule ) { } // Re-assign if any updates have been made. - if ( $rule_updated ) { - $rule->setDeclarations( $stack ); + if ($rule_updated) { + $rule->setDeclarations($stack); } } -function csscrush__flex_direction ( $value, &$stack ) { +function csscrush__flex_direction ($value, &$stack) { // flex-direction: row | row-reverse | column | column-reverse // box-orient: horizontal | vertical | inline-axis | block-axis | inherit // box-direction: normal | reverse | inherit - $prop_aliases =& CssCrush::$process->aliases[ 'properties' ]; + $prop_aliases =& CssCrush::$process->aliases['properties']; static $directions = array( 'row' => 'normal', @@ -211,15 +211,15 @@ function csscrush__flex_direction ( $value, &$stack ) { ); $rule_updated = false; - if ( isset( $prop_aliases[ 'box-direction' ] ) ) { - foreach ( $prop_aliases[ 'box-direction' ] as $prop_alias ) { - $stack[] = new CssCrush_Declaration( $prop_alias, $directions[ $value ] ); + if (isset($prop_aliases['box-direction'])) { + foreach ($prop_aliases['box-direction'] as $prop_alias) { + $stack[] = new CssCrush_Declaration($prop_alias, $directions[$value]); $rule_updated = true; } } - if ( isset( $prop_aliases[ 'box-orient' ] ) ) { - foreach ( $prop_aliases[ 'box-orient' ] as $prop_alias ) { - $stack[] = new CssCrush_Declaration( $prop_alias, $orientations[ $value ] ); + if (isset($prop_aliases['box-orient'])) { + foreach ($prop_aliases['box-orient'] as $prop_alias) { + $stack[] = new CssCrush_Declaration($prop_alias, $orientations[$value]); $rule_updated = true; } } @@ -227,12 +227,12 @@ function csscrush__flex_direction ( $value, &$stack ) { } -function csscrush__flex_justify_content ( $value, &$stack ) { +function csscrush__flex_justify_content ($value, &$stack) { // justify-content: flex-start | flex-end | center | space-between | space-around // box-pack: start | end | center | justify - $prop_aliases =& CssCrush::$process->aliases[ 'properties' ]; + $prop_aliases =& CssCrush::$process->aliases['properties']; static $positions = array( 'flex-start' => 'start', @@ -243,9 +243,9 @@ function csscrush__flex_justify_content ( $value, &$stack ) { ); $rule_updated = false; - if ( isset( $prop_aliases[ 'box-pack' ] ) && isset( $positions[ $value ] ) ) { - foreach ( $prop_aliases[ 'box-pack' ] as $prop_alias ) { - $stack[] = new CssCrush_Declaration( $prop_alias, $positions[ $value ] ); + if (isset($prop_aliases['box-pack']) && isset($positions[$value])) { + foreach ($prop_aliases['box-pack'] as $prop_alias) { + $stack[] = new CssCrush_Declaration($prop_alias, $positions[$value]); $rule_updated = true; } } @@ -253,12 +253,12 @@ function csscrush__flex_justify_content ( $value, &$stack ) { } -function csscrush__flex_align_items ( $value, &$stack ) { +function csscrush__flex_align_items ($value, &$stack) { // align-items: flex-start | flex-end | center | baseline | stretch // box-align: start | end | center | baseline | stretch - $prop_aliases =& CssCrush::$process->aliases[ 'properties' ]; + $prop_aliases =& CssCrush::$process->aliases['properties']; static $positions = array( 'flex-start' => 'start', @@ -269,9 +269,9 @@ function csscrush__flex_align_items ( $value, &$stack ) { ); $rule_updated = false; - if ( isset( $prop_aliases[ 'box-align' ] ) && isset( $positions[ $value ] ) ) { - foreach ( $prop_aliases[ 'box-align' ] as $prop_alias ) { - $stack[] = new CssCrush_Declaration( $prop_alias, $positions[ $value ] ); + if (isset($prop_aliases['box-align']) && isset($positions[$value])) { + foreach ($prop_aliases['box-align'] as $prop_alias) { + $stack[] = new CssCrush_Declaration($prop_alias, $positions[$value]); $rule_updated = true; } } @@ -279,12 +279,12 @@ function csscrush__flex_align_items ( $value, &$stack ) { } -function csscrush__flex_order ( $value, &$stack ) { +function csscrush__flex_order ($value, &$stack) { // order: // box-ordinal-group: - $prop_aliases =& CssCrush::$process->aliases[ 'properties' ]; + $prop_aliases =& CssCrush::$process->aliases['properties']; // Bump value as box-ordinal-group requires a positive integer: // http://www.w3.org/TR/2009/WD-css3-flexbox-20090723/#displayorder @@ -295,9 +295,9 @@ function csscrush__flex_order ( $value, &$stack ) { $value = $value < 1 ? 1 : $value + 1; $rule_updated = false; - if ( isset( $prop_aliases[ 'box-ordinal-group' ] ) ) { - foreach ( $prop_aliases[ 'box-ordinal-group' ] as $prop_alias ) { - $stack[] = new CssCrush_Declaration( $prop_alias, $value ); + if (isset($prop_aliases['box-ordinal-group'])) { + foreach ($prop_aliases['box-ordinal-group'] as $prop_alias) { + $stack[] = new CssCrush_Declaration($prop_alias, $value); $rule_updated = true; } } @@ -305,12 +305,12 @@ function csscrush__flex_order ( $value, &$stack ) { } -function csscrush__flex_wrap ( $value, &$stack ) { +function csscrush__flex_wrap ($value, &$stack) { // flex-wrap: nowrap | wrap | wrap-reverse // box-lines: single | multiple - $prop_aliases =& CssCrush::$process->aliases[ 'properties' ]; + $prop_aliases =& CssCrush::$process->aliases['properties']; static $wrap_behaviours = array( 'nowrap' => 'single', @@ -320,9 +320,9 @@ function csscrush__flex_wrap ( $value, &$stack ) { ); $rule_updated = false; - if ( isset( $prop_aliases[ 'box-lines' ] ) && isset( $wrap_behaviours[ $value ] ) ) { - foreach ( $prop_aliases[ 'box-lines' ] as $prop_alias ) { - $stack[] = new CssCrush_Declaration( $prop_alias, $wrap_behaviours[ $value ] ); + if (isset($prop_aliases['box-lines']) && isset($wrap_behaviours[$value])) { + foreach ($prop_aliases['box-lines'] as $prop_alias) { + $stack[] = new CssCrush_Declaration($prop_alias, $wrap_behaviours[$value]); $rule_updated = true; } } @@ -330,12 +330,12 @@ function csscrush__flex_wrap ( $value, &$stack ) { } -function csscrush__flex ( $value, &$stack ) { +function csscrush__flex ($value, &$stack) { // flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ] // box-flex: - $prop_aliases =& CssCrush::$process->aliases[ 'properties' ]; + $prop_aliases =& CssCrush::$process->aliases['properties']; // Normalize keyword arguments. static $keywords = array( @@ -343,21 +343,21 @@ function csscrush__flex ( $value, &$stack ) { 'auto' => '1 1 auto', 'initial' => '0 1 auto', ); - if ( isset( $keywords[ $value ] ) ) { - $value = $keywords[ $value ]; + if (isset($keywords[$value])) { + $value = $keywords[$value]; } // Grow (first value) not have a unit to avoid being interpreted as a basis: // https://developer.mozilla.org/en-US/docs/CSS/flex $grow = 1; - if ( preg_match( '~^(\d*\.?\d+) ~', $value, $m ) ) { + if (preg_match('~^(\d*\.?\d+) ~', $value, $m)) { $grow = $m[1]; } $rule_updated = false; - if ( isset( $prop_aliases[ 'box-flex' ] ) ) { - foreach ( $prop_aliases[ 'box-flex' ] as $prop_alias ) { - $stack[] = new CssCrush_Declaration( $prop_alias, $grow ); + if (isset($prop_aliases['box-flex'])) { + foreach ($prop_aliases['box-flex'] as $prop_alias) { + $stack[] = new CssCrush_Declaration($prop_alias, $grow); $rule_updated = true; } } @@ -365,17 +365,17 @@ function csscrush__flex ( $value, &$stack ) { } -function csscrush__flex_grow ( $value, &$stack ) { +function csscrush__flex_grow ($value, &$stack) { // flex-grow: // box-flex: - $prop_aliases =& CssCrush::$process->aliases[ 'properties' ]; + $prop_aliases =& CssCrush::$process->aliases['properties']; $rule_updated = false; - if ( isset( $prop_aliases[ 'box-flex' ] ) ) { - foreach ( $prop_aliases[ 'box-flex' ] as $prop_alias ) { - $stack[] = new CssCrush_Declaration( $prop_alias, $value ); + if (isset($prop_aliases['box-flex'])) { + foreach ($prop_aliases['box-flex'] as $prop_alias) { + $stack[] = new CssCrush_Declaration($prop_alias, $value); $rule_updated = true; } } diff --git a/plugins/noise.php b/plugins/noise.php index a0e1dc1..2c9674d 100644 --- a/plugins/noise.php +++ b/plugins/noise.php @@ -1,10 +1,9 @@ || ]? * [ , ]? * ) - * + * * * Any valid CSS color value. * @@ -37,90 +36,90 @@ * "hueRotate", "saturate" * * Mixed. For "hueRotate" a degree as number. For "saturate" a ranged number (0-1). - * + * * Returns: * A base64 encoded svg data-uri. - * + * * References: * http://www.w3.org/TR/SVG/filters.html * http://srufaculty.sru.edu/david.dailey/svg/SVGOpen2010/Filters2.htm#S11 - * + * * Examples: * // Grainy noise with 50% opacity and de-saturated. * // Demonstrates the "default" keyword for skipping arguments. * background-image: noise( slategray, default, .5, saturate 0 ); - * + * * // Cloud effect. * background: noise( 700x700 skyblue, .01 4 normal, screen, saturate 0 ); - * + * * turbulence() * ------------ * As noise() except uses default turbulance type 'turbulance' and not 'fractalNoise' - * + * * Syntax: * See noise(). * * Returns: * See noise(). - * + * * References: * See noise(). * * Examples: * // Typical turbulence effect. * background: turbulence(); - * + * * // Sand effect. * background: turbulence( wheat 400x400, .35:.2 4 sharpen, normal, saturate .4 ); */ -CssCrush_Plugin::register( 'noise', array( +CssCrush_Plugin::register('noise', array( 'enable' => 'csscrush__enable_noise', 'disable' => 'csscrush__disable_noise', )); function csscrush__enable_noise () { - CssCrush_Function::register( 'noise', 'csscrush_fn__noise' ); - CssCrush_Function::register( 'turbulence', 'csscrush_fn__turbulence' ); + CssCrush_Function::register('noise', 'csscrush_fn__noise'); + CssCrush_Function::register('turbulence', 'csscrush_fn__turbulence'); } function csscrush__disable_noise () { - CssCrush_Function::deRegister( 'noise' ); - CssCrush_Function::deRegister( 'turbulence' ); + CssCrush_Function::deRegister('noise'); + CssCrush_Function::deRegister('turbulence'); } -function csscrush_fn__noise ( $input ) { - return csscrush__noise_generator( $input, array( +function csscrush_fn__noise ($input) { + return csscrush__noise_generator($input, array( 'type' => 'fractalNoise', 'frequency' => .7, 'sharpen' => 'sharpen', - 'dimensions' => array( 150, 150 ), + 'dimensions' => array(150, 150), )); } -function csscrush_fn__turbulence ( $input ) { - return csscrush__noise_generator( $input, array( +function csscrush_fn__turbulence ($input) { + return csscrush__noise_generator($input, array( 'type' => 'turbulence', 'frequency' => .01, 'sharpen' => 'normal', - 'dimensions' => array( 200, 200 ), + 'dimensions' => array(200, 200), )); } -function csscrush__noise_generator ( $input, $defaults ) { +function csscrush__noise_generator ($input, $defaults) { - $args = array_pad( CssCrush_Function::parseArgs( $input ), 4, 'default' ); + $args = array_pad(CssCrush_Function::parseArgs($input), 4, 'default'); - $type = $defaults[ 'type' ]; + $type = $defaults['type']; // Color-fill and dimensions. $fill_color = 'transparent'; - $dimensions = $defaults[ 'dimensions' ]; - if ( ( $arg = array_shift( $args ) ) !== 'default' ) { + $dimensions = $defaults['dimensions']; + if (($arg = array_shift($args)) !== 'default') { // May be a color function so explode(' ', $value) is not sufficient. - foreach ( CssCrush_Function::parseArgs( $arg, true ) as $part ) { - if ( preg_match( '~^(\d+)x(\d+)$~i', $part, $m ) ) { - $dimensions = array_slice( $m, 1 ); + foreach (CssCrush_Function::parseArgs($arg, true) as $part) { + if (preg_match('~^(\d+)x(\d+)$~i', $part, $m)) { + $dimensions = array_slice($m, 1); } else { $fill_color = $part; @@ -129,24 +128,24 @@ function csscrush__noise_generator ( $input, $defaults ) { } // Frequency, octaves and sharpening. - static $sharpen_modes = array( 'normal', 'sharpen' ); - $frequency = $defaults[ 'frequency' ]; + static $sharpen_modes = array('normal', 'sharpen'); + $frequency = $defaults['frequency']; $octaves = 1; - $sharpen = $defaults[ 'sharpen' ]; + $sharpen = $defaults['sharpen']; - if ( ( $arg = array_shift( $args ) ) !== 'default' ) { - foreach ( explode( ' ', $arg ) as $index => $value ) { - switch ( $index ) { + if (($arg = array_shift($args)) !== 'default') { + foreach (explode(' ', $arg) as $index => $value) { + switch ($index) { case 0: // x and y frequency values can be specified by joining with a colon. - $frequency = str_replace( ':', ',', $value ); + $frequency = str_replace(':', ',', $value); break; case 1: case 2: - if ( preg_match( CssCrush_Regex::$patt->rooted_number, $value ) ) { + if (preg_match(CssCrush_Regex::$patt->rooted_number, $value)) { $octaves = $value; } - elseif ( in_array( $value, $sharpen_modes ) ) { + elseif (in_array($value, $sharpen_modes)) { $sharpen = $value; } } @@ -154,13 +153,13 @@ function csscrush__noise_generator ( $input, $defaults ) { } // Blend-mode and fade. - static $blend_modes = array( 'normal', 'multiply', 'screen', 'darken', 'lighten' ); + static $blend_modes = array('normal', 'multiply', 'screen', 'darken', 'lighten'); $blend_mode = 'normal'; $opacity = 1; - if ( ( $arg = array_shift( $args ) ) !== 'default' ) { - foreach ( explode( ' ', $arg ) as $part ) { - if ( ctype_alpha( $part ) ) { - if ( in_array( $part, $blend_modes ) ) { + if (($arg = array_shift($args)) !== 'default') { + foreach (explode(' ', $arg) as $part) { + if (ctype_alpha($part)) { + if (in_array($part, $blend_modes)) { $blend_mode = $part; } } @@ -171,14 +170,14 @@ function csscrush__noise_generator ( $input, $defaults ) { } // Color filter. - static $color_filters = array( 'saturate', 'hueRotate', 'luminanceToAlpha' ); + static $color_filters = array('saturate', 'hueRotate', 'luminanceToAlpha'); $color_filter = null; - if ( ( $arg = array_shift( $args ) ) !== 'default' ) { + if (($arg = array_shift($args)) !== 'default') { // Saturate by default. - $color_filter = array( 'saturate', 1 ); - foreach ( explode( ' ', $arg ) as $part ) { - if ( ctype_alpha( $part ) ) { - if ( in_array( $part, $color_filters ) ) { + $color_filter = array('saturate', 1); + foreach (explode(' ', $arg) as $part) { + if (ctype_alpha($part)) { + if (in_array($part, $color_filters)) { $color_filter[0] = $part; } } @@ -196,26 +195,26 @@ function csscrush__noise_generator ( $input, $defaults ) { $svg .= ""; $component_adjustments = array(); - if ( $sharpen === 'sharpen' ) { + if ($sharpen === 'sharpen') { // It's more posterizing than sharpening, but it hits a sweet spot. $component_adjustments[] = ""; $component_adjustments[] = ""; // Some unpredictable results with this: // $component_adjustments[] = ""; } - if ( $opacity != '1' ) { + if ($opacity != '1') { $component_adjustments[] = ""; } - if ( $component_adjustments ) { + if ($component_adjustments) { $svg .= ""; - $svg .= implode( '', $component_adjustments ); + $svg .= implode('', $component_adjustments); $svg .= ""; } - if ( $color_filter ) { + if ($color_filter) { $svg .= ""; } - if ( $blend_mode !== 'normal' ) { + if ($blend_mode !== 'normal') { $svg .= ""; } $svg .= ''; @@ -225,7 +224,7 @@ function csscrush__noise_generator ( $input, $defaults ) { $svg .= ''; // Create data-uri url and return token label. - $url = new CssCrush_Url( 'data:image/svg+xml;base64,' . base64_encode( $svg ) ); + $url = new CssCrush_Url('data:image/svg+xml;base64,' . base64_encode($svg)); return $url->label; } diff --git a/plugins/property-sorter.php b/plugins/property-sorter.php index bb167f0..088c826 100644 --- a/plugins/property-sorter.php +++ b/plugins/property-sorter.php @@ -1,7 +1,6 @@ 'csscrush__enable_property_sorter', 'disable' => 'csscrush__disable_property_sorter', )); function csscrush__enable_property_sorter () { - CssCrush_Hook::add( 'rule_prealias', 'csscrush__property_sorter' ); + CssCrush_Hook::add('rule_prealias', 'csscrush__property_sorter'); } function csscrush__disable_property_sorter () { - CssCrush_Hook::remove( 'rule_prealias', 'csscrush__property_sorter' ); + CssCrush_Hook::remove('rule_prealias', 'csscrush__property_sorter'); } -function csscrush__property_sorter ( CssCrush_Rule $rule ) { +function csscrush__property_sorter (CssCrush_Rule $rule) { $new_set = array(); // Create plain array of rule declarations. - foreach ( $rule as $declaration ) { + foreach ($rule as $declaration) { $new_set[] = $declaration; } - usort( $new_set, '_csscrush__property_sorter_callback' ); + usort($new_set, '_csscrush__property_sorter_callback'); - $rule->setDeclarations( $new_set ); + $rule->setDeclarations($new_set); } /* Callback for sorting. */ -function _csscrush__property_sorter_callback ( $a, $b ) { +function _csscrush__property_sorter_callback ($a, $b) { $map =& _csscrush__property_sorter_get_table(); $a_prop =& $a->canonicalProperty; $b_prop =& $b->canonicalProperty; - $a_listed = isset( $map[ $a_prop ] ); - $b_listed = isset( $map[ $b_prop ] ); + $a_listed = isset($map[$a_prop]); + $b_listed = isset($map[$b_prop]); // If the properties are identical we need to flag for an index comparison. $compare_indexes = false; @@ -70,10 +69,10 @@ function _csscrush__property_sorter_callback ( $a, $b ) { $compare_vendor = false; // If both properties are listed. - if ( $a_listed && $b_listed ) { + if ($a_listed && $b_listed) { - if ( $a_prop === $b_prop ) { - if ( $a->vendor || $b->vendor ) { + if ($a_prop === $b_prop) { + if ($a->vendor || $b->vendor) { $compare_vendor = true; } else { @@ -82,23 +81,23 @@ function _csscrush__property_sorter_callback ( $a, $b ) { } else { // Table comparison. - return $map[ $a_prop ] > $map[ $b_prop ] ? 1 : -1; + return $map[$a_prop] > $map[$b_prop] ? 1 : -1; } } // If one property is listed it always takes higher priority. - elseif ( $a_listed && ! $b_listed ) { + elseif ($a_listed && ! $b_listed) { return -1; } - elseif ( $b_listed && ! $a_listed ) { + elseif ($b_listed && ! $a_listed) { return 1; } // If neither property is listed. else { - if ( $a_prop === $b_prop ) { - if ( $a->vendor || $b->vendor ) { + if ($a_prop === $b_prop) { + if ($a->vendor || $b->vendor) { $compare_vendor = true; } else { @@ -112,21 +111,21 @@ function _csscrush__property_sorter_callback ( $a, $b ) { } // Comparing by index. - if ( $compare_indexes ) { + if ($compare_indexes ) { return $a->index > $b->index ? 1 : -1; } // Comparing by vendor mark. - if ( $compare_vendor ) { - if ( ! $a->vendor && $b->vendor ) { + if ($compare_vendor) { + if (! $a->vendor && $b->vendor) { return 1; } - elseif ( $a->vendor && ! $b->vendor ) { + elseif ($a->vendor && ! $b->vendor) { return -1; } else { // If both have a vendor mark compare vendor name length. - return strlen( $b->vendor ) > strlen( $a->vendor ) ? 1 : -1; + return strlen($b->vendor) > strlen($a->vendor) ? 1 : -1; } } } @@ -138,15 +137,15 @@ function _csscrush__property_sorter_callback ( $a, $b ) { function &_csscrush__property_sorter_get_table () { // Check for cached table. - if ( isset( $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER_CACHE' ] ) ) { - return $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER_CACHE' ]; + if (isset($GLOBALS['CSSCRUSH_PROPERTY_SORT_ORDER_CACHE'])) { + return $GLOBALS['CSSCRUSH_PROPERTY_SORT_ORDER_CACHE']; } $table = array(); // Nothing cached, check for a user-defined table. - if ( isset( $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER' ] ) ) { - $table = (array) $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER' ]; + if (isset($GLOBALS['CSSCRUSH_PROPERTY_SORT_ORDER'])) { + $table = (array) $GLOBALS['CSSCRUSH_PROPERTY_SORT_ORDER']; } // No user-defined table, use pre-defined. @@ -154,24 +153,24 @@ function &_csscrush__property_sorter_get_table () { // Load from property-sorting.ini. $sorting_file_contents = - file_get_contents( CssCrush::$config->location . '/misc/property-sorting.ini' ); - if ( $sorting_file_contents !== false ) { + file_get_contents(CssCrush::$config->location . '/misc/property-sorting.ini'); + if ($sorting_file_contents !== false) { - $sorting_file_contents = preg_replace( '~;[^\r\n]*~', '', $sorting_file_contents ); - $table = preg_split( '~\s+~', trim( $sorting_file_contents ) ); + $sorting_file_contents = preg_replace('~;[^\r\n]*~', '', $sorting_file_contents); + $table = preg_split('~\s+~', trim($sorting_file_contents)); } else { - trigger_error( __METHOD__ . ": Property sorting file not found.\n", E_USER_NOTICE ); + trigger_error(__METHOD__ . ": Property sorting file not found.\n", E_USER_NOTICE); } // Store to the global variable. - $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER' ] = $table; + $GLOBALS['CSSCRUSH_PROPERTY_SORT_ORDER'] = $table; } // Cache the table (and flip it). - $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER_CACHE' ] = array_flip( $table ); + $GLOBALS['CSSCRUSH_PROPERTY_SORT_ORDER_CACHE'] = array_flip($table); - return $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER_CACHE' ]; + return $GLOBALS['CSSCRUSH_PROPERTY_SORT_ORDER_CACHE']; } @@ -180,14 +179,14 @@ function &_csscrush__property_sorter_get_table () { */ function csscrush_get_property_sort_order () { _csscrush__property_sorter_get_table(); - return $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER' ]; + return $GLOBALS['CSSCRUSH_PROPERTY_SORT_ORDER']; } /* Set a custom sorting table. */ -function csscrush_set_property_sort_order ( array $new_order ) { - unset( $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER_CACHE' ] ); - $GLOBALS[ 'CSSCRUSH_PROPERTY_SORT_ORDER' ] = $new_order; +function csscrush_set_property_sort_order (array $new_order) { + unset($GLOBALS['CSSCRUSH_PROPERTY_SORT_ORDER_CACHE']); + $GLOBALS['CSSCRUSH_PROPERTY_SORT_ORDER'] = $new_order; } diff --git a/plugins/px2rem.php b/plugins/px2rem.php index 4dc066b..17477dd 100644 --- a/plugins/px2rem.php +++ b/plugins/px2rem.php @@ -1,8 +1,7 @@ 'csscrush__enable_rgba_fallback', 'disable' => 'csscrush__disable_rgba_fallback', )); function csscrush__enable_rgba_fallback () { - CssCrush_Hook::add( 'rule_postalias', 'csscrush__rgba_fallback' ); + CssCrush_Hook::add('rule_postalias', 'csscrush__rgba_fallback'); } function csscrush__disable_rgba_fallback () { - CssCrush_Hook::remove( 'rule_postalias', 'csscrush__rgba_fallback' ); + CssCrush_Hook::remove('rule_postalias', 'csscrush__rgba_fallback'); } -function csscrush__rgba_fallback ( CssCrush_Rule $rule ) { +function csscrush__rgba_fallback (CssCrush_Rule $rule) { - $props = array_keys( $rule->properties ); + $props = array_keys($rule->properties); // Determine which properties apply $rgba_props = array(); - foreach ( $props as $prop ) { - if ( $prop === 'background' || strpos( $prop, 'color' ) !== false ) { + foreach ($props as $prop) { + if ($prop === 'background' || strpos($prop, 'color') !== false) { $rgba_props[] = $prop; } } - if ( empty( $rgba_props ) ) { + if (empty($rgba_props)) { return; } @@ -46,13 +47,13 @@ function csscrush__rgba_fallback ( CssCrush_Rule $rule ) { } $new_set = array(); - foreach ( $rule as $declaration ) { + foreach ($rule as $declaration) { - $is_viable = in_array( $declaration->property, $rgba_props ); + $is_viable = in_array($declaration->property, $rgba_props); if ( $declaration->skip || ! $is_viable || - $is_viable && ! preg_match( $rgb_patt, $declaration->value ) + $is_viable && ! preg_match($rgb_patt, $declaration->value) ) { $new_set[] = $declaration; continue; @@ -60,12 +61,12 @@ function csscrush__rgba_fallback ( CssCrush_Rule $rule ) { // Create rgb value from rgba. $raw_value = $declaration->getFullValue(); - $raw_value = substr( $raw_value, 5, strlen( $raw_value ) - 1 ); - list( $r, $g, $b, $a ) = explode( ',', $raw_value ); + $raw_value = substr($raw_value, 5, strlen($raw_value) - 1); + list($r, $g, $b, $a) = explode(',', $raw_value); // Add rgb value to the stack, followed by rgba. - $new_set[] = new CssCrush_Declaration( $declaration->property, "rgb($r,$g,$b)" ); + $new_set[] = new CssCrush_Declaration($declaration->property, "rgb($r,$g,$b)"); $new_set[] = $declaration; } - $rule->setDeclarations( $new_set ); + $rule->setDeclarations($new_set); } diff --git a/plugins/spiffing.php b/plugins/spiffing.php index 6436e92..f97db65 100644 --- a/plugins/spiffing.php +++ b/plugins/spiffing.php @@ -1,7 +1,8 @@ 'csscrush__enable_spiffing', 'disable' => 'csscrush__disable_spiffing', )); function csscrush__enable_spiffing () { - CssCrush_Hook::add( 'rule_preprocess', 'csscrush__spiffing' ); + CssCrush_Hook::add('rule_preprocess', 'csscrush__spiffing'); } function csscrush__disable_spiffing () { - CssCrush_Hook::add( 'rule_preprocess', 'csscrush__spiffing' ); + CssCrush_Hook::add('rule_preprocess', 'csscrush__spiffing'); } -function csscrush__spiffing ( $rule ) { +function csscrush__spiffing ($rule) { - $find = array( 'colour', 'grey', '!please', 'transparency', 'centre', 'plump', 'photograph', 'capitalise' ); - $replace = array( 'color', 'gray', '!important', 'opacity', 'center', 'bold', 'image', 'capitalize' ); + $find = array('colour', 'grey', '!please', 'transparency', 'centre', 'plump', 'photograph', 'capitalise'); + $replace = array('color', 'gray', '!important', 'opacity', 'center', 'bold', 'image', 'capitalize'); - $rule->declaration_raw = str_ireplace( $find, $replace, $rule->declaration_raw ); + $rule->declaration_raw = str_ireplace($find, $replace, $rule->declaration_raw); } diff --git a/plugins/svg-gradients.php b/plugins/svg-gradients.php index 41f9ea0..dc833b2 100644 --- a/plugins/svg-gradients.php +++ b/plugins/svg-gradients.php @@ -1,47 +1,46 @@ | to ,]? [, ]+ ) - * + * * Returns: * A base64 encoded svg data-uri. - * + * * Known issues: * Color stops can only take percentage value offsets. - * + * * Examples: * background-image: svg-linear-gradient( to top left, #fff, rgba(255,255,255,0) 80% ); * background-image: svg-linear-gradient( 35deg, red, gold 20%, powderblue ); - * + * * svg-radial-gradent() * -------------------- * Syntax is similar to but more limited than CR radial-gradient(): http://dev.w3.org/csswg/css3-images/#radial-gradient - * + * * svg-radial-gradent( [ | at ,]? [, ]+ ) - * + * * Returns: * A base64 encoded svg data-uri. - * + * * Known issues: * Color stops can only take percentage value offsets. * No control over shape - only circular gradients - however, the generated image can be stretched * with background-size. - * + * * Examples: * background-image: svg-radial-gradient( at center, red, blue 50%, yellow ); * background-image: svg-radial-gradient( 100% 50%, rgba(255,255,255,.5), rgba(255,255,255,0) ); * */ -CssCrush_Plugin::register( 'svg-gradients', array( +CssCrush_Plugin::register('svg-gradients', array( 'enable' => 'csscrush__enable_svg_gradients', 'disable' => 'csscrush__disable_svg_gradients', )); @@ -108,20 +107,20 @@ function csscrush__create_svg_linear_gradient ($input) { 'to bottom' => 0, 'to left' => 90, // Not very magic corners. - 'to top right' => array( array(0, 100), array(100, 0) ), - 'to top left' => array( array(100, 100), array(0, 0) ), - 'to bottom right' => array( array(0, 0), array(100, 100) ), - 'to bottom left' => array( array(100, 0), array(0, 100) ), + 'to top right' => array(array(0, 100), array(100, 0)), + 'to top left' => array(array(100, 100), array(0, 0)), + 'to bottom right' => array(array(0, 0), array(100, 100)), + 'to bottom left' => array(array(100, 0), array(0, 100)), ); - $angle_keywords[ 'to right top' ] = $angle_keywords[ 'to top right' ]; - $angle_keywords[ 'to left top' ] = $angle_keywords[ 'to top left' ]; - $angle_keywords[ 'to right bottom' ] = $angle_keywords[ 'to bottom right' ]; - $angle_keywords[ 'to left bottom' ] = $angle_keywords[ 'to bottom left' ]; + $angle_keywords['to right top'] = $angle_keywords['to top right']; + $angle_keywords['to left top'] = $angle_keywords['to top left']; + $angle_keywords['to right bottom'] = $angle_keywords['to bottom right']; + $angle_keywords['to left bottom'] = $angle_keywords['to bottom left']; $deg_patt = CssCrush_Regex::create('^deg$', 'i'); } - $args = CssCrush_Function::parseArgs( $input ); + $args = CssCrush_Function::parseArgs($input); // If no angle argument is passed the default. $angle = 0; @@ -132,26 +131,26 @@ function csscrush__create_svg_linear_gradient ($input) { $first_arg_is_angle = false; // Try to parse an angle value. - if ( preg_match( $deg_patt, $first_arg ) ) { - $angle = floatval( $first_arg ); + if (preg_match($deg_patt, $first_arg)) { + $angle = floatval($first_arg); // Quick fix to match standard linear-gradient() angle. $angle += 180; $first_arg_is_angle = true; } - elseif ( isset( $angle_keywords[ $first_arg ] ) ) { - if ( is_array( $angle_keywords[ $first_arg ] ) ) { - $coords = $angle_keywords[ $first_arg ]; + elseif (isset($angle_keywords[$first_arg])) { + if (is_array($angle_keywords[$first_arg])) { + $coords = $angle_keywords[$first_arg]; } else { - $angle = $angle_keywords[ $first_arg ]; + $angle = $angle_keywords[$first_arg]; } $first_arg_is_angle = true; } // Shift off the first argument if it has been recognised as an angle. - if ( $first_arg_is_angle ) { - array_shift( $args ); + if ($first_arg_is_angle) { + array_shift($args); } // If not using a magic corner, create start/end coordinates from the angle. @@ -169,43 +168,43 @@ function csscrush__create_svg_linear_gradient ($input) { $start_y = 0; $end_y = 100; - if ( $angle >= 0 && $angle <= 45 ) { - $start_x = ( ( $angle / 45 ) * 50 ) + 50; + if ($angle >= 0 && $angle <= 45) { + $start_x = (($angle / 45) * 50) + 50; $end_x = 100 - $start_x; $start_y = 0; $end_y = 100; } - elseif ( $angle > 45 && $angle <= 135 ) { + elseif ($angle > 45 && $angle <= 135) { $angle_delta = $angle - 45; $start_x = 100; $end_x = 0; - $start_y = ( $angle_delta / 90 ) * 100; + $start_y = ($angle_delta / 90) * 100; $end_y = 100 - $start_y; } - elseif ( $angle > 135 && $angle <= 225 ) { + elseif ($angle > 135 && $angle <= 225) { $angle_delta = $angle - 135; - $start_x = 100 - ( ( $angle_delta / 90 ) * 100 ); + $start_x = 100 - (($angle_delta / 90) * 100); $end_x = 100 - $start_x; $start_y = 100; $end_y = 0; } - elseif ( $angle > 225 && $angle <= 315 ) { + elseif ($angle > 225 && $angle <= 315) { $angle_delta = $angle - 225; $start_x = 0; $end_x = 100; - $start_y = 100 - ( ( $angle_delta / 90 ) * 100 ); + $start_y = 100 - (($angle_delta / 90) * 100); $end_y = 100 - $start_y; } - elseif ( $angle > 315 && $angle <= 360 ) { + elseif ($angle > 315 && $angle <= 360) { $angle_delta = $angle - 315; - $start_x = ( $angle_delta / 90 ) * 100; + $start_x = ($angle_delta / 90) * 100; $end_x = 100 - $start_x; $start_y = 0; $end_y = 100; } $coords = array( - array( round( $start_x, 1 ), round( $start_y, 1 ) ), - array( round( $end_x, 1 ), round( $end_y, 1 ) ), + array(round($start_x, 1), round($start_y, 1)), + array(round($end_x, 1), round($end_y, 1)), ); } @@ -213,7 +212,7 @@ function csscrush__create_svg_linear_gradient ($input) { // - Capture their color values and if specified color offset percentages. // - Only percentages are supported as SVG gradients to accept other length values // for color stop offsets. - $color_stops = csscrush__parse_gradient_color_stops( $args ); + $color_stops = csscrush__parse_gradient_color_stops($args); // Create the gradient markup with a unique id. static $uid = 0; @@ -244,43 +243,43 @@ function csscrush__create_svg_radial_gradient ($input) { 'at bottom right' => array('100%', '100%'), 'at bottom left' => array('0%', '100%'), ); - $position_keywords[ 'at right top' ] = $position_keywords[ 'at top right' ]; - $position_keywords[ 'at left top' ] = $position_keywords[ 'at top left' ]; - $position_keywords[ 'at right bottom' ] = $position_keywords[ 'at bottom right' ]; - $position_keywords[ 'at left bottom' ] = $position_keywords[ 'at bottom left' ]; + $position_keywords['at right top'] = $position_keywords['at top right']; + $position_keywords['at left top'] = $position_keywords['at top left']; + $position_keywords['at right bottom'] = $position_keywords['at bottom right']; + $position_keywords['at left bottom'] = $position_keywords['at bottom left']; $origin_patt = CssCrush_Regex::create('^(%?) +(%?)$'); } - $args = CssCrush_Function::parseArgs( $input ); + $args = CssCrush_Function::parseArgs($input); // Default origin, - $position = $position_keywords[ 'at center' ]; + $position = $position_keywords['at center']; // Parse origin coordinates from the first argument if it's an origin. $first_arg = $args[0]; $first_arg_is_position = false; // Try to parse an origin value. - if ( preg_match( $origin_patt, $first_arg, $m ) ) { - $position = array( $m[1], $m[2] ); + if (preg_match($origin_patt, $first_arg, $m)) { + $position = array($m[1], $m[2]); $first_arg_is_position = true; } - elseif ( isset( $position_keywords[ $first_arg ] ) ) { - $position = $position_keywords[ $first_arg ]; + elseif (isset($position_keywords[$first_arg])) { + $position = $position_keywords[$first_arg]; $first_arg_is_position = true; } // Shift off the first argument if it has been recognised as an origin. - if ( $first_arg_is_position ) { - array_shift( $args ); + if ($first_arg_is_position) { + array_shift($args); } // The remaining arguments are treated as color stops. // - Capture their color values and if specified color offset percentages. // - Only percentages are supported as SVG gradients to accept other length values // for color stop offsets. - $color_stops = csscrush__parse_gradient_color_stops( $args ); + $color_stops = csscrush__parse_gradient_color_stops($args); // Create the gradient markup with a unique id. static $uid = 0; @@ -332,12 +331,12 @@ function csscrush__parse_gradient_color_stops (array $color_stop_args) { $prev_index_not_null = 0; $n = count($offsets); - foreach ( $offsets as $index => $offset ) { + foreach ($offsets as $index => $offset) { if (! isset($offset)) { // Scan for next non-null offset. - for ( $i = $index; $i < $n; $i++ ) { + for ($i = $index; $i < $n; $i++) { if (isset($offsets[$i])) { $next_index_not_null = $i; break; @@ -347,16 +346,16 @@ function csscrush__parse_gradient_color_stops (array $color_stop_args) { // Get the difference between previous 'not null' offset and the next 'not null' offset. // Divide by the number of null offsets to get a value for padding between them. $padding_increment = - ( $offsets[ $next_index_not_null ] - $offsets[ $prev_index_not_null ] ) / - ( $next_index_not_null - $index + 1 ); + ($offsets[$next_index_not_null] - $offsets[$prev_index_not_null]) / + ($next_index_not_null - $index + 1); $padding = $padding_increment; - for ( $i = $index; $i < $n; $i++ ) { + for ($i = $index; $i < $n; $i++) { if (isset($offsets[$i])) { break; } // Replace the null offset with the new padded value. - $offsets[$i] = $offsets[ $prev_index_not_null ] + $padding; + $offsets[$i] = $offsets[$prev_index_not_null] + $padding; // Bump the padding for the next null offset. $padding += $padding_increment; } @@ -367,7 +366,7 @@ function csscrush__parse_gradient_color_stops (array $color_stop_args) { } $stops = ''; - foreach ( array_combine( $offsets, $colors ) as $offset => $color ) { + foreach (array_combine($offsets, $colors) as $offset => $color) { list($color_value, $opacity) = $color; $stop_opacity = $opacity < 1 ? " stop-opacity=\"$opacity\"" : ''; $stops .= ""; diff --git a/plugins/svg.php b/plugins/svg.php index fab37f4..828b884 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -1,8 +1,6 @@ array( 'tag' => 'circle', 'attrs' => 'cx cy r', @@ -142,7 +140,7 @@ function csscrush__svg_generator ($input, $fn_name) { // Convert attributes to keyed array. // Add global attributes. - foreach ($ELEMENTS as $type => &$schema) { + foreach ($schemas as $type => &$schema) { $schema['attrs'] = array_flip(explode(' ', $schema['attrs'])) + array( 'transform' => true, @@ -151,7 +149,7 @@ function csscrush__svg_generator ($input, $fn_name) { } // Non standard attributes. - static $CUSTOM_ATTRS = array( + static $custom_attrs = array( 'type' => true, 'data' => true, 'twist' => true, @@ -191,16 +189,14 @@ function csscrush__svg_generator ($input, $fn_name) { // Resolve the type. // Bail if type not recognised. $type = isset($raw_data['type']) ? strtolower($raw_data['type']) : 'rect'; - if (! isset($ELEMENTS[$type])) { + if (! isset($schemas[$type])) { return ''; } - $ATTRIBUTES = $ELEMENTS[$type]['attrs']; - // Create element object for attaching all required rendering data. $element = (object) array( - 'tag' => $ELEMENTS[$type]['tag'], + 'tag' => $schemas[$type]['tag'], 'fills' => array( 'gradients' => array(), 'patterns' => array(), @@ -231,11 +227,11 @@ function csscrush__svg_generator ($input, $fn_name) { csscrush__svg_apply_css_funcs($element, $raw_data); // Initialize element attributes. - $element->attrs = array_intersect_key($raw_data, $ATTRIBUTES); - $element->data = array_intersect_key($raw_data, $CUSTOM_ATTRS); + $element->attrs = array_intersect_key($raw_data, $schemas[$type]['attrs']); + $element->data = array_intersect_key($raw_data, $custom_attrs); // Everything else is treated as CSS. - $element->styles = array_diff_key($raw_data, $CUSTOM_ATTRS, $ATTRIBUTES); + $element->styles = array_diff_key($raw_data, $custom_attrs, $schemas[$type]['attrs']); // Pre-populate common attributes. csscrush__svg_preprocess($element); @@ -263,7 +259,8 @@ function csscrush__svg_generator ($input, $fn_name) { $flattened_svg = implode("\n", $svg); // Create fingerprint for the created file. - $generated_filename = substr(md5($flattened_svg), 0, 7) . ".$name.crush.svg"; + $fingerprint = substr(md5($flattened_svg), 0, 7); + $generated_filename = "svg-$name-$fingerprint.svg"; $generated_path = $process->output->dir . '/' . $generated_filename; file_put_contents($generated_path, $flattened_svg, LOCK_EX); @@ -753,11 +750,9 @@ function csscrush__svg_render ($element) { */ function csscrush__svg_fn_linear_gradient ($input, $element) { - static $booted; - if (! $booted) { - // Relies on functions from svg-gradients plugin. - CssCrush_Plugin::load('svg-gradients'); - } + // Relies on functions from svg-gradients plugin. + CssCrush_Plugin::load('svg-gradients'); + $generated_gradient = csscrush__create_svg_linear_gradient($input); $element->fills['gradients'][] = reset($generated_gradient); @@ -766,11 +761,9 @@ function csscrush__svg_fn_linear_gradient ($input, $element) { function csscrush__svg_fn_radial_gradient ($input, $element) { - static $booted; - if (! $booted) { - // Relies on functions from svg-gradients plugin. - CssCrush_Plugin::load('svg-gradients'); - } + // Relies on functions from svg-gradients plugin. + CssCrush_Plugin::load('svg-gradients'); + $generated_gradient = csscrush__create_svg_radial_gradient($input); $element->fills['gradients'][] = reset($generated_gradient); From 5d58e69f9c549cecbe37600b05d18a750e29be24 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 30 Mar 2013 12:11:51 +0000 Subject: [PATCH 092/421] Updated plugin summaries. Fixed some typos. --- cli.php | 2 +- plugins/hocus-pocus.php | 2 +- plugins/hsl-to-hex.php | 2 +- plugins/ie-clip.php | 2 +- plugins/ie-filter.php | 2 +- plugins/ie-inline-block.php | 2 +- plugins/ie-min-height.php | 2 +- plugins/ie-opacity.php | 2 +- plugins/initial.php | 2 +- plugins/legacy-flexbox.php | 2 +- plugins/rem-fallback.php | 2 +- plugins/rgba-fallback.php | 2 +- plugins/svg-gradients.php | 2 +- plugins/svg.php | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/cli.php b/cli.php index 6ff2ac2..f9649ad 100755 --- a/cli.php +++ b/cli.php @@ -527,7 +527,7 @@ function manpage () { Rules are printed in single lines with right padded selectors. --help: - Display this help mesasge. + Display this help message. --list: Show plugins. diff --git a/plugins/hocus-pocus.php b/plugins/hocus-pocus.php index afab817..65fc480 100644 --- a/plugins/hocus-pocus.php +++ b/plugins/hocus-pocus.php @@ -1,6 +1,6 @@ 7 * Outputs '*' escaped filter property for IE < 8 diff --git a/plugins/ie-inline-block.php b/plugins/ie-inline-block.php index 1fcb372..35d4270 100644 --- a/plugins/ie-inline-block.php +++ b/plugins/ie-inline-block.php @@ -1,6 +1,6 @@ Date: Wed, 3 Apr 2013 10:04:59 +0100 Subject: [PATCH 093/421] Vendor target option now accepts an array of vendors. Vendor filtering and context vendor filtering now works on all aliases. --- cli.php | 5 ++- lib/CssCrush/Core.php | 24 +++++++++++-- lib/CssCrush/Process.php | 73 +++++++++++++++++++--------------------- lib/CssCrush/Rule.php | 32 +++++++++++++++--- plugins/initial.php | 9 ++--- 5 files changed, 90 insertions(+), 53 deletions(-) diff --git a/cli.php b/cli.php index f9649ad..b2989fd 100755 --- a/cli.php +++ b/cli.php @@ -194,7 +194,7 @@ // Run multiple value arguments through array cast. -foreach (array('enable_plugins', 'disable_plugins') as $arg) { +foreach (array('enable_plugins', 'disable_plugins', 'vendor_target') as $arg) { if ($args->{$arg}) { $args->{$arg} = (array) $args->{$arg}; } @@ -273,7 +273,7 @@ // Vendor target arg. if ($args->vendor_target) { - $process_opts['vendor_target'] = $args->vendor_target; + $process_opts['vendor_target'] = parse_list($args->vendor_target); } // Variables args. @@ -297,7 +297,6 @@ $process_opts['output_file'] = basename($args->output_file); } - ################################################################## ## Output. diff --git a/lib/CssCrush/Core.php b/lib/CssCrush/Core.php index 4be42bd..7ec3866 100644 --- a/lib/CssCrush/Core.php +++ b/lib/CssCrush/Core.php @@ -157,15 +157,32 @@ static public function loadAssets () $result = @parse_ini_file($aliases_file, true); if ($result !== false) { + $regex = CssCrush_Regex::$patt; + foreach ($result as $section => $items) { // Declaration aliases require a little preparation. + // Also extracting vendor context (if any). if ($section === 'declarations') { + $store = array(); foreach ($items as $prop_val => $aliases) { + list($prop, $value) = array_map('trim', explode(':', $prop_val)); + foreach ($aliases as &$alias) { - $alias = explode(':', $alias); + + list($p, $v) = explode(':', $alias); + $vendor = null; + + // Try to detect the vendor from property and value in turn. + if ( + preg_match($regex->vendorPrefix, $p, $m) || + preg_match($regex->vendorPrefix, $v, $m) + ) { + $vendor = $m[1]; + } + $alias = array($p, $v, $vendor); } $store[$prop][$value] = $aliases; } @@ -185,10 +202,11 @@ static public function loadAssets () foreach ($aliases as $alias_func) { // Only supporting vendor prefixed aliases, for now. - if (preg_match(CssCrush_Regex::$patt->vendorPrefix, $alias_func, $m)) { + if (preg_match($regex->vendorPrefix, $alias_func, $m)) { // We'll cache the function matching regex here. - $vendor_grouped_aliases[$m[1]]['find'][] = CssCrush_Regex::create('' . $func_name . '', 'i'); + $vendor_grouped_aliases[$m[1]]['find'][] = + CssCrush_Regex::create('' . $func_name . '', 'i'); $vendor_grouped_aliases[$m[1]]['replace'][] = $alias_func; } } diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index e70d94e..1ac310a 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -386,21 +386,28 @@ static public function applySelectorAliases (&$str) protected function filterAliases () { // If a vendor target is given, we prune the aliases array. - $vendor = $this->options->vendor_target; + $vendors = $this->options->vendor_target; // Default vendor argument, so use all aliases as normal. - if ('all' === $vendor) { + if ('all' === $vendors) { + return; } // For expicit 'none' argument turn off aliases. - if ('none' === $vendor) { + if ('none' === $vendors) { $this->aliases = CssCrush::$config->bareAliasGroups; + return; } - // Normalize vendor_target argument. - $vendor = '-' . str_replace('-', '', $vendor) . '-'; + // Normalize vendor names and create regex patt. + $vendor_names = (array) $vendors; + foreach ($vendor_names as &$vendor_name) { + $vendor_name = trim($vendor_name, '-'); + } + $vendor_patt = '~^\-(' . implode($vendor_names, '|') . ')\-~i'; + // Loop the aliases array, filter down to the target vendor. foreach ($this->aliases as $section => $group_array) { @@ -412,21 +419,18 @@ protected function filterAliases () foreach ($values as $value => $prefix_values) { foreach ($prefix_values as $index => $declaration) { - list($prop, $val) = $declaration; - if (!( - strpos($prop, $vendor) === 0 || - strpos($val, $vendor) === 0 - ) - ) { - // Unset uneeded aliases. - unset($this->aliases[$section][$property][$value][$index]); - - if (empty($this->aliases[$section][$property][$value])) { - unset($this->aliases[$section][$property][$value]); - } - if (empty($this->aliases[$section][$property])) { - unset($this->aliases[$section][$property]); - } + if (in_array($declaration[2], $vendor_names)) { + continue; + } + + // Unset uneeded aliases. + unset($this->aliases[$section][$property][$value][$index]); + + if (empty($this->aliases[$section][$property][$value])) { + unset($this->aliases[$section][$property][$value]); + } + if (empty($this->aliases[$section][$property])) { + unset($this->aliases[$section][$property]); } } } @@ -437,10 +441,10 @@ protected function filterAliases () elseif ($section === 'function_groups') { foreach ($group_array as $func_group => $vendors) { - foreach ($vendors as $fn_vendor => $replacements) { + foreach ($vendors as $vendor => $replacements) { - if ("-$fn_vendor-" !== $vendor) { - unset($this->aliases['function_groups'][$func_group][$fn_vendor]); + if (! in_array($vendor, $vendor_names)) { + unset($this->aliases['function_groups'][$func_group][$vendor]); } } } @@ -458,10 +462,11 @@ protected function filterAliases () $result = array(); foreach ($prefix_array as $prefix) { - if (strpos($prefix, $vendor) === 0) { + if (preg_match($vendor_patt, $prefix)) { $result[] = $prefix; } } + // Prune the whole alias keyword if there is no result. if (empty($result)) { unset($this->aliases[$section][$alias_keyword]); @@ -534,7 +539,7 @@ protected function calculateVariables () $this->variables = array_merge($this->variables, $option_vars); } - // Finally add state variables. + // Add state variables. // $this->variables = array( // 'input-path' => $this->input->dirUrl, // 'output-path' => $this->output->dirUrl, @@ -912,6 +917,7 @@ protected function prefixSelectors () protected function aliasAtRules () { if (empty($this->aliases['at-rules'])) { + return; } @@ -961,19 +967,10 @@ protected function aliasAtRules () // Set the vendor context. $cloneRule->vendorContext = $vendor; - // Filter out declarations that have different vendor context. - $new_set = array(); - foreach ($cloneRule as $declaration) { - if (! $declaration->vendor || $declaration->vendor === $vendor) { - $new_set[] = $declaration; - } - } - $cloneRule->setDeclarations($new_set); - // Store the clone. $replacements[] = $this->addToken($cloneRule, 'r'); - } + // Finally replace the original labels with the cloned rule labels. $copy_block = str_replace($originals, $replacements, $copy_block); } @@ -1143,12 +1140,12 @@ public function compile () // Process @in blocks. $this->prefixSelectors(); - // Main processing on the rule objects. - $this->processRules(); - // Alias any @-rules. $this->aliasAtRules(); + // Main processing on the rule objects. + $this->processRules(); + // Print rules, optionally minify. $this->collate(); diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php index 100d42d..c43ee43 100644 --- a/lib/CssCrush/Rule.php +++ b/lib/CssCrush/Rule.php @@ -527,9 +527,16 @@ public function addPropertyAliases () $stack = array(); $rule_updated = false; + $vendor_context = $this->vendorContext; + $regex = CssCrush_Regex::$patt; foreach ($this->declarations as $declaration) { + // Check declaration against vendor context. + if ($vendor_context && $declaration->vendor && $declaration->vendor !== $vendor_context) { + continue; + } + if ($declaration->skip) { $stack[] = $declaration; continue; @@ -540,19 +547,27 @@ public function addPropertyAliases () foreach ($aliased_properties[$declaration->property] as $prop_alias) { - // If an aliased version already exists to not create one. + // If an aliased version already exists do not create one. if ($this->propertyCount($prop_alias)) { continue; } + // Get property alias vendor. + preg_match($regex->vendorPrefix, $prop_alias, $alias_vendor); + + // Check against vendor context. + if ($vendor_context && $alias_vendor && $alias_vendor[1] !== $vendor_context) { + continue; + } + // Create the aliased declaration. $copy = clone $declaration; $copy->property = $prop_alias; // Set the aliased declaration vendor property. $copy->vendor = null; - if (preg_match(CssCrush_Regex::$patt->vendorPrefix, $prop_alias, $vendor)) { - $copy->vendor = $vendor[1]; + if ($alias_vendor) { + $copy->vendor = $alias_vendor[1]; } $stack[] = $copy; @@ -574,6 +589,7 @@ public function addFunctionAliases () { $function_aliases =& CssCrush::$process->aliases['functions']; $function_alias_groups =& CssCrush::$process->aliases['function_groups']; + $vendor_context = $this->vendorContext; // The new modified set of declarations. $new_set = array(); @@ -621,7 +637,10 @@ public function addFunctionAliases () foreach ($groups as $group_key => $replacements) { // If the declaration is vendor specific only create aliases for the same vendor. - if ($declaration->vendor && $group_key !== $declaration->vendor) { + if ( + ($declaration->vendor && $group_key !== $declaration->vendor) || + ($vendor_context && $group_key !== $vendor_context) + ) { continue; } @@ -651,7 +670,10 @@ public function addFunctionAliases () // If the declaration is vendor specific only create aliases for the same vendor. if ($declaration->vendor) { preg_match(CssCrush_Regex::$patt->vendorPrefix, $fn_alias, $m); - if ($m[1] !== $declaration->vendor) { + if ( + $m[1] !== $declaration->vendor || + ($vendor_context && $m[1] !== $vendor_context) + ) { continue; } } diff --git a/plugins/initial.php b/plugins/initial.php index 772dd98..5ea03aa 100644 --- a/plugins/initial.php +++ b/plugins/initial.php @@ -1,18 +1,19 @@ Date: Thu, 4 Apr 2013 11:15:16 +0100 Subject: [PATCH 094/421] Fixed issue with nested rules not inheriting. --- cli.php | 7 +++---- lib/CssCrush/Process.php | 11 +++++------ lib/CssCrush/Rule.php | 38 ++++++++++++++++++++------------------ 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/cli.php b/cli.php index b2989fd..c7099b3 100755 --- a/cli.php +++ b/cli.php @@ -153,9 +153,9 @@ ################################################################## -## Filter option values. +## Validate option values. -// Validate filepath arguments. +// Filepath arguments. if ($args->input_file) { $input_file = $args->input_file; if (! ($args->input_file = realpath($args->input_file))) { @@ -202,7 +202,7 @@ ################################################################## -## Input. +## Resolve input. $input = null; @@ -359,7 +359,6 @@ function stdout ($lines, $closing_newline = true) { // On OSX terminal is sometimes truncating 'visual' output to terminal // with fwrite to STDOUT. - // fwrite(STDOUT, $out); echo $out; } diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 1ac310a..dbd95f9 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -890,16 +890,15 @@ protected function prefixSelectors () $rule_selector->value, 1); - // Not storing the selector as named. - $new_selector_list[] = new CssCrush_Selector($new_value); + $new = new CssCrush_Selector($new_value); + $new_selector_list[$new->readableValue] = $new; } // Prepending the prefix. else { - // Not storing the selector as named. - $new_selector_list[] - = new CssCrush_Selector("$arg_selector {$rule_selector->value}"); + $new = new CssCrush_Selector("$arg_selector {$rule_selector->value}"); + $new_selector_list[$new->readableValue] = $new; } } } @@ -1135,7 +1134,7 @@ public function compile () // Parse rules. $this->extractRules(); - // csscrush::log($this->references); + // csscrush::log(array_keys($this->references)); // Process @in blocks. $this->prefixSelectors(); diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php index c43ee43..042f97d 100644 --- a/lib/CssCrush/Rule.php +++ b/lib/CssCrush/Rule.php @@ -176,12 +176,12 @@ public function __toString () // Merge the extend selectors. $this->selectors += $this->extendSelectors; - // If there are no selectors or declarations associated with the rule return empty string. + // If there are no selectors or declarations associated with the rule + // return empty string. if (empty($this->selectors) || empty($this->declarations)) { // De-reference this instance. unset($process->tokens->r[$this->label]); - return ''; } @@ -344,32 +344,34 @@ public function setExtendSelectors ($raw_value) } public $resolvedExtendables = false; - public function resolveExtendables () { - - if (! $this->extendArgs || $this->resolvedExtendables) { + public function resolveExtendables () + { + if (! $this->extendArgs) { return false; } + elseif (! $this->resolvedExtendables) { - $references =& CssCrush::$process->references; + $references =& CssCrush::$process->references; - // Filter the extendArgs list to usable references. - $filtered = array(); - foreach ($this->extendArgs as $key => $extend_arg) { + // Filter the extendArgs list to usable references. + $filtered = array(); + foreach ($this->extendArgs as $key => $extend_arg) { - $name = $extend_arg->name; + $name = $extend_arg->name; - if (isset($references[$name])) { + if (isset($references[$name])) { - $parent_rule = $references[$name]; - $parent_rule->resolveExtendables(); - $extend_arg->pointer = $parent_rule; - $filtered[$parent_rule->label] = $extend_arg; + $parent_rule = $references[$name]; + $parent_rule->resolveExtendables(); + $extend_arg->pointer = $parent_rule; + $filtered[$parent_rule->label] = $extend_arg; + } } - } - $this->resolvedExtendables = true; - $this->extendArgs = $filtered; + $this->resolvedExtendables = true; + $this->extendArgs = $filtered; + } return true; } From 9198db7735e7c3d59f51b585653de804e107d242 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 5 Apr 2013 14:38:28 +0100 Subject: [PATCH 095/421] Made a start on canvas plugin - bitmap generation on top of PHP GD image library. Added some new boilerplate tags for command line users. Fixed small selector overwriting issue. Slimmed Plugins.ini docs. --- Plugins.ini | 50 ++-------- lib/CssCrush/Process.php | 13 +++ lib/CssCrush/Rule.php | 26 +++-- plugins/canvas.php | 208 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 244 insertions(+), 53 deletions(-) create mode 100644 plugins/canvas.php diff --git a/Plugins.ini b/Plugins.ini index f100863..18754ce 100644 --- a/Plugins.ini +++ b/Plugins.ini @@ -1,51 +1,13 @@ ;---------------------------------------------------------------- ; -; Enable or disable plugins to suit your needs. -; Plugins can be enabled and disabled at runtime with "disable" and "enable" options. +; This file is for listing plugins you want loaded and enabled by default. ; -; See plugin docs for details: https://github.com/peteboere/css-crush/wiki/Plugins +; Plugins can also be enabled and disabled at runtime with "disable" and +; "enable" options. +; +; See: https://github.com/peteboere/css-crush/wiki/Plugins ; ;---------------------------------------------------------------- -; Property sorting -; plugins[] = property-sorter. - -; min-height shim for IE < 7. -; plugins[] = ie-min-height - -; inline-block shim for IE < 8. -; plugins[] = ie-inline-block - -; clip property shim for IE < 8. -; plugins[] = ie-clip - -; IE filter shorthand. -; plugins[] = ie-filter - -; Opacity for IE < 9 (uses filter). -; plugins[] = ie-opacity - -; HSL shim - converts HSL values to hex codes. -; plugins[] = hsl-to-hex - -; Non-standard composite pseudo classes. -; plugins[] = hocus-pocus - -; CSS3 'initial' keyword shim. -; plugins[] = initial - -; Transforms correctly-spelt Queen's English into valid CSS (http://spiffingcss.com) -; plugins[] = spiffing - -; Auto generates legacy flexbox equivilant syntax. -; plugins[] = legacy-flexbox - -; Functions for creating gradients with svg data-uris. -; plugins[] = svg-gradients - -; Functions for generating noise and textures with svg data-uris. -; plugins[] = noise - -; Define and embed SVG shapes in CSS. +; Example use: ; plugins[] = svg - diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index dbd95f9..2688326 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -263,10 +263,23 @@ protected function getBoilerplate () // Substitute any tags if (preg_match_all('~\{\{([^}]+)\}\}~', $boilerplate, $boilerplate_matches)) { + $command = 'n/a'; + if (isset($_SERVER['argv'])) { + $argv = $_SERVER['argv']; + array_shift($argv); + $command = 'csscrush ' . implode(' ', $argv); + } + $tags = array( 'datetime' => @date('Y-m-d H:i:s O'), 'year' => @date('Y'), 'version' => 'v' . CssCrush::$config->version, + + // Command line arguments (if any). + 'command' => $command, + + // Enabled plugins. + 'plugins' => implode(',', array_keys($this->plugins)), ); foreach ($boilerplate_matches[0] as $index => $tag) { diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php index 042f97d..ea234f8 100644 --- a/lib/CssCrush/Rule.php +++ b/lib/CssCrush/Rule.php @@ -433,7 +433,7 @@ public function expandSelectors () if ($pos !== false) { - // Contains an :any statement so we expand + // Contains an :any statement so expand. $chain = array(''); do { if ($pos === 0) { @@ -478,16 +478,16 @@ public function expandSelectors () } } while (($pos = stripos($selector->value, ':any?')) !== false); - // Finish off + // Finish off. foreach ($chain as &$row) { - // Not creating a named rule association with this expanded selector - $new_set[] = new CssCrush_Selector($row . $selector->value); + $new = new CssCrush_Selector($row . $selector->value); + $new_set[$new->readableValue] = $new; } } else { - // Nothing to expand + // Nothing to expand. $new_set[$readableValue] = $selector; } @@ -722,6 +722,7 @@ public function addDeclarationAliases () // Table lookups are faster. $intersect = array_flip(array_keys($intersect)); + $vendor_context = $this->vendorContext; $new_set = array(); $rule_updated = false; @@ -736,13 +737,20 @@ public function addDeclarationAliases () // Create new alias declaration if the property and value match. if ($declaration->value === $value_match) { - foreach ($replacements as $pair) { + foreach ($replacements as $values) { + + // Check the vendor against context. + if ($vendor_context && $vendor_context !== $values[2]) { + continue; + } // If the replacement property is null use the original declaration property. - $new_set[] = new CssCrush_Declaration( - ! empty($pair[0]) ? $pair[0] : $declaration->property, - $pair[1] + $new = new CssCrush_Declaration( + ! empty($values[0]) ? $values[0] : $declaration->property, + $values[1] ); + $new->important = $declaration->important; + $new_set[] = $new; $rule_updated = true; } } diff --git a/plugins/canvas.php b/plugins/canvas.php new file mode 100644 index 0000000..e811fde --- /dev/null +++ b/plugins/canvas.php @@ -0,0 +1,208 @@ + 'csscrush__enable_canvas', + 'disable' => 'csscrush__disable_canvas', +)); + +function csscrush__enable_canvas () { + CssCrush_Hook::add('process_extract', 'csscrush__canvas_extract'); + CssCrush_Function::register('canvas', 'csscrush_fn__canvas'); + CssCrush_Function::register('canvas-data', 'csscrush_fn__canvas_data'); +} + +function csscrush__disable_canvas () { + CssCrush_Hook::remove('process_extract', 'csscrush__canvas_extract'); + CssCrush_Function::deRegister('canvas'); + CssCrush_Function::deRegister('canvas-data'); +} + +function csscrush_fn__canvas ($input) { + + return csscrush__canvas_generator($input, 'canvas'); +} + +function csscrush_fn__canvas_data ($input) { + + return csscrush__canvas_generator($input, 'canvas-data'); +} + +function csscrush__canvas_extract ($process) { + + static $callback, $patt; + if (! $callback) { + $patt = CssCrush_Regex::create('@canvas +() *\{ *(.*?) *\};?', 'iS'); + $callback = create_function('$m', ' + $name = strtolower($m[1]); + $block = $m[2]; + if (! empty($name) && ! empty($block)) { + CssCrush::$process->misc->canvas_defs[$name] = + new CssCrush_Template($block); + } + '); + } + + // Extract definitions. + $process->stream->pregReplaceCallback($patt, $callback); +} + +function csscrush__canvas_generator ($input, $fn_name) { + + $process = CssCrush::$process; + + $cache_key = $fn_name . $input; + if (isset($process->misc->canvas_cache[$cache_key])) { + + return $process->misc->canvas_cache[$cache_key]; + } + + // Non standard attributes. + static $custom_attrs = array( + 'fill' => true, + 'width' => true, + 'height' => true, + ); + + // Bail if no args. + $args = CssCrush_Function::parseArgs($input); + if (! isset($args[0])) { + + return ''; + } + + $name = strtolower(array_shift($args)); + + // Bail if no SVG registered by this name. + $canvas_defs =& $process->misc->canvas_defs; + if (! isset($canvas_defs[$name])) { + + return ''; + } + + // Apply args to template. + $block = $canvas_defs[$name]->apply($args); + + // Parse the block into a keyed assoc array. + $raw_data = array_change_key_case(CssCrush_Util::parseBlock($block, true)); + + // Resolve properties, set defaults if not present. + $properties = array_intersect_key($raw_data, $custom_attrs) + array( + 'width' => 100, + 'height' => 100, + 'fill' => '#000', + ); + + // Apply functions. + $storage = new stdClass(); + csscrush__canvas_apply_css_funcs($properties, $storage); + + // Extract variables. + extract($properties); + $width = intval($width); + $height = intval($height); + + // Create image object. + $image = imagecreatetruecolor($width, $height); + + // Create transparent canvas background. + imagealphablending($image, false); + imagefill($image, 0, 0, imagecolorallocatealpha($image, 0, 0, 0, 127)); + imagesavealpha($image, true); + + // Gradient fill. + if (is_array($fill)) { + + } + // Solid color fill. + elseif ($solid = CssCrush_Color::parse($fill)) { + + list($r, $g, $b, $a) = $solid; + $fill = imagecolorallocatealpha($image, $r, $g, $b, csscrush__canvas_opacity($a)); + imagefilledrectangle($image, 0, 0, $width, $height, $fill); + } + // Failure to parse fill. + else { + + imagedestroy($image); + return ''; + } + + // Either write to a file. + if ($fn_name === 'canvas') { + + // Create fingerprint for the created file. + $fingerprint = substr(md5("{$width}x{$height}$fill"), 0, 7); + $generated_filename = "cnv-$name-$fingerprint.png"; + $generated_path = $process->output->dir . '/' . $generated_filename; + + imagepng($image, $generated_path); + + // Write to the same directory as the output css. + $url = new CssCrush_Url($generated_filename); + $url->noRewrite = true; + } + // Or create data uri. + else { + ob_start(); + imagepng($image); + $data = ob_get_clean(); + + $url = new CssCrush_Url('data:image/png;base64,' . base64_encode($data)); + } + + // Cache the output URL. + $process->misc->canvas_cache[$cache_key] = $url->label; + + imagedestroy($image); + return $url->label; +} + + + + + +function csscrush__canvas_fn_linear_gradient ($input, $extra) { + + $colors = CssCrush_Function::parseArgs($input); + $extra->fill = array(); + + return ''; +} + +function csscrush__canvas_apply_css_funcs (&$properties, $extra) { + + // Setup functions for using on values. + static $generic_functions_patt, $fill_functions, $fill_functions_patt; + if (! $generic_functions_patt) { + $fill_functions = array( + 'canvas-linear-gradient' => 'csscrush__canvas_fn_linear_gradient', + ); + $generic_functions + = array_diff_key(CssCrush_Function::$functions, $fill_functions); + $generic_functions_patt + = CssCrush_Regex::createFunctionPatt(array_keys($generic_functions), true); + $fill_functions_patt + = CssCrush_Regex::createFunctionPatt(array_keys($fill_functions)); + } + + foreach ($properties as $property => &$value) { + CssCrush_Function::executeOnString($value, $generic_functions_patt); + + // if ($property === 'fill') { + // CssCrush_Function::executeOnString( + // $value, $fill_functions_patt, $fill_functions, $extra); + // } + } +} + + +/* + Helpers. +*/ +function csscrush__canvas_opacity ($float) { + return 127 - max(min(round($float * 127), 127), 0); +} From 9fff557bf5f2d6ce228f9d6bd5c8b9cad347c2a6 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 6 Apr 2013 12:08:51 +0100 Subject: [PATCH 096/421] Math constants now available in math function expressions. Just PI for now others can be added if anyone has any use cases. Moved pseudo element double-colon normalization to an earlier phase to make life easier. --- lib/CssCrush/Function.php | 8 +++++++- lib/CssCrush/Importer.php | 4 ++++ lib/CssCrush/Rule.php | 7 +------ lib/CssCrush/Selector.php | 12 +----------- 4 files changed, 13 insertions(+), 18 deletions(-) diff --git a/lib/CssCrush/Function.php b/lib/CssCrush/Function.php index d29fe7b..7819cf7 100644 --- a/lib/CssCrush/Function.php +++ b/lib/CssCrush/Function.php @@ -138,7 +138,13 @@ static public function parseArgsSimple ($input) function csscrush_fn__math ($input) { - // Strip blacklisted characters + // Swap in math constants. + $input = preg_replace( + array('~\bpi\b~i'), + array(M_PI), + $input); + + // Strip blacklisted characters. $input = preg_replace(CssCrush_Regex::$patt->mathBlacklist, '', $input); $result = @eval("return $input;"); diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index 187ed5d..3f60098 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -202,9 +202,13 @@ static protected function prepareForStream (&$str) // Convert all end-of-lines to unix style. $str = preg_replace('~\r\n?~', "\n", $str); + // Tokenize all comments and string literals. $str = preg_replace_callback($regex->commentAndString, array('self', 'cb_extractCommentAndString'), $str); + // Normalize double-colon pseudo elements for backwards compatability. + $str = preg_replace('~::(after|before|first-(?:letter|line))~iS', ':$1', $str); + // If @charset is set store it. if (preg_match($regex->charset, $str, $m)) { $replace = ''; diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php index ea234f8..767e22c 100644 --- a/lib/CssCrush/Rule.php +++ b/lib/CssCrush/Rule.php @@ -409,7 +409,7 @@ public function applyExtendables () $extend_selectors[$new_readable] = $new_selector; } } - $ancestor->addExtendSelectors($extend_selectors); + $ancestor->extendSelectors += $extend_selectors; } } @@ -509,11 +509,6 @@ public function addSelectors ($list, $extend_selectors = false) $this->selectors += $list; } - public function addExtendSelectors ($list) - { - $this->extendSelectors += $list; - } - ############################# # Aliasing. diff --git a/lib/CssCrush/Selector.php b/lib/CssCrush/Selector.php index 6a2c802..f834cea 100644 --- a/lib/CssCrush/Selector.php +++ b/lib/CssCrush/Selector.php @@ -34,7 +34,7 @@ public function __toString () if (! CssCrush::$process->minifyOutput) { $this->value = CssCrush_Selector::normalizeWhiteSpace($this->value); } - return CssCrush_Selector::compatFilter($this->value); + return $this->value; } public function appendPseudo ($pseudo) @@ -48,16 +48,6 @@ public function appendPseudo ($pseudo) return $this->readableValue; } - static public function compatFilter ($str) - { - // Replace double-colons for backwards compatability. - if (strpos($str, '::') !== false) { - $str = preg_replace( - '~::(after|before|first-(?:letter|line))~iS', ':$1', $str); - } - return $str; - } - static public function normalizeWhiteSpace ($str) { // Create space around combinators, then normalize whitespace. From 7e913c03879c78a62ef1f19718eb1c649be77869 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 8 Apr 2013 12:28:02 +0100 Subject: [PATCH 097/421] Refactoring Templating can now interpolate values into string literals. Selector aliases now use Templating so default values are possible. Added some plugin API functions to work with the changes to selector aliases. --- lib/CssCrush/Core.php | 15 ++++++ lib/CssCrush/Function.php | 2 +- lib/CssCrush/Process.php | 71 +++++++++++++------------ lib/CssCrush/Regex.php | 19 +++++-- lib/CssCrush/Template.php | 39 +++++++++++--- lib/CssCrush/Util.php | 2 +- plugins/canvas.php | 108 ++++++++++++++++++++++++++++++++------ plugins/hocus-pocus.php | 8 +-- plugins/svg.php | 6 ++- 9 files changed, 198 insertions(+), 72 deletions(-) diff --git a/lib/CssCrush/Core.php b/lib/CssCrush/Core.php index 7ec3866..5174d4f 100644 --- a/lib/CssCrush/Core.php +++ b/lib/CssCrush/Core.php @@ -524,6 +524,21 @@ static public function stat ($name = null) } + ############################# + # Global selector aliases. + + static public function addSelectorAlias ($name, $body) + { + CssCrush::$config->selectorAliases[$name] + = new CssCrush_Template($body, array("interpolate" => true)); + } + + static public function removeSelectorAlias ($name) + { + unset(CssCrush::$config->selectorAliases[$name]); + } + + ############################# # Logging and stats. diff --git a/lib/CssCrush/Function.php b/lib/CssCrush/Function.php index 7819cf7..4077c2c 100644 --- a/lib/CssCrush/Function.php +++ b/lib/CssCrush/Function.php @@ -34,7 +34,7 @@ static public function setMatchPatt () { self::$functions = self::$builtinFunctions + self::$customFunctions; self::$functionPatt = CssCrush_Regex::createFunctionPatt( - array_keys(self::$functions), true); + array_keys(self::$functions), array('bare_paren' => true)); } static public function executeOnString (&$str, $patt = null, $process_callback = null, $extra = null) diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 2688326..44a296a 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -234,6 +234,19 @@ public function restoreParens (&$str, $release = true) } + ############################# + # Strings. + + public function captureStrings (&$str) + { + static $callback; + if (! $callback) { + $callback = create_function('$m', 'return CssCrush::$process->addToken($m[0], \'s\');'); + } + $str = preg_replace_callback(CssCrush_Regex::$patt->string, $callback, $str); + } + + ############################# # Boilerplate. @@ -307,22 +320,26 @@ protected function getBoilerplate () protected function resolveSelectorAliases () { - static $callback; - if (! $callback) { - $callback = create_function('$m', 'CssCrush::$process->selectorAliases[$m[1]] = $m[2];'); + static $alias_patt, $callback; + if (! $alias_patt) { + $alias_patt = CssCrush_Regex::create('@selector-alias +\:() +([^;]+) *;', 'iS'); + $callback = create_function('$m', ' + $name = strtolower($m[1]); + $body = CssCrush_Util::stripCommentTokens($m[2]); + $template = new CssCrush_Template($body, array("interpolate" => true)); + CssCrush::$process->selectorAliases[$name] = $template; + '); } - $this->stream->pregReplaceCallback(CssCrush_Regex::$patt->selectorAlias, $callback); + $this->stream->pregReplaceCallback($alias_patt, $callback); - // Merge in global selector aliases. + // Merge with global selector aliases. $this->selectorAliases += CssCrush::$config->selectorAliases; // Create the selector aliases pattern and store it. if ($this->selectorAliases) { $names = implode('|', array_keys($this->selectorAliases)); - $this->selectorAliasesPatt = '~ - \:(' . $names . ')\b(?!-) - (\()? - ~xiS'; + $this->selectorAliasesPatt + = CssCrush_Regex::create('\:(' . $names . ')(\()?', 'iS'); } } @@ -343,52 +360,35 @@ static public function applySelectorAliases (&$str) // Step through the matches from last to first. while ($selector_alias_call = array_pop($selector_alias_calls)) { - $selector_alias_name = $selector_alias_call[1][0]; + $selector_alias_name = strtolower($selector_alias_call[1][0]); if (! isset($table[$selector_alias_name])) { continue; } - $replacement = $table[$selector_alias_name]; + $template = $table[$selector_alias_name]; $start = $selector_alias_call[0][1]; $length = strlen($selector_alias_call[0][0]); + $args = array(); // It's a function alias if a start paren is matched. if (isset($selector_alias_call[2])) { + // Parse argument list. if (! preg_match(CssCrush_Regex::$patt->balancedParens, $str, $parens, PREG_OFFSET_CAPTURE, $start)) { continue; } + $args = CssCrush_Function::parseArgs($parens[1][0]); + + // Amend offsets. $paren_start = $parens[0][1]; $paren_len = strlen($parens[0][0]); $length = ($paren_start + $paren_len) - $start; - - // Create substitutions list and apply. - $args = CssCrush_Util::splitDelimList($parens[1][0]); - foreach ($args as $index => $arg) { - $search[] = "#($index)"; - } - $replacement = str_replace($search, $args, $replacement); - - // Apply substitutions to copies of string tokens within the replacement. - preg_match_all(CssCrush_Regex::$patt->s_token, $replacement, $s_tokens); - foreach ($s_tokens as $m) { - foreach ($m as $label) { - $old_token_value = $process->fetchToken($label); - - // Create new token based on the value. - $token_value = str_replace($search, $args, $old_token_value); - $new_label = $process->addToken($token_value, 's'); - - // Swap the old token label with new. - $replacement = str_replace($label, $new_label, $replacement); - } - } } // Splice in the result. - $str = substr_replace($str, $replacement, $start, $length); + $str = substr_replace($str, $template->apply($args), $start, $length); } } @@ -724,7 +724,8 @@ protected function resolveFragments () $curly_match->replace(''); // Create the fragment and store it. - $fragments[$fragment_name] = new CssCrush_Template($curly_match->inside()); + $fragments[$fragment_name] = new CssCrush_Template( + $curly_match->inside(), array('interpolate' => true)); } } diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php index 36cb341..e83356d 100644 --- a/lib/CssCrush/Regex.php +++ b/lib/CssCrush/Regex.php @@ -61,7 +61,6 @@ static public function init () $patt->variables = CssCrush_Regex::create('@(?:define|variables) *\{ *(.*?) *\};?', 'iS'); $patt->mixin = CssCrush_Regex::create('@mixin +() *\{ *(.*?) *\};?', 'iS'); $patt->abstract = CssCrush_Regex::create('^@abstract +()', 'i'); - $patt->selectorAlias = CssCrush_Regex::create('@selector-alias +\:() +([^;]+) *;', 'iS'); $patt->ifDefine = CssCrush_Regex::create('@ifdefine +(not +)?() *\{', 'iS'); $patt->fragmentDef = CssCrush_Regex::create('@fragment +() *\{', 'iS'); $patt->fragmentCall = CssCrush_Regex::create('@fragment +() *(\(|;)', 'iS'); @@ -69,9 +68,9 @@ static public function init () // Functions. $patt->function = CssCrush_Regex::create('()()', 'S'); $patt->varFunction = CssCrush_Regex::create('\$\( *() *\)', 'S'); - $patt->argFunction = CssCrush_Regex::createFunctionPatt(array('arg')); $patt->thisFunction = CssCrush_Regex::createFunctionPatt(array('this')); + $patt->string = '~(\'|")(?:\\\\\1|[^\1])*?\1~xS'; $patt->commentAndString = '~ # Quoted string (to EOF if unmatched). (\'|")(?:\\\\\1|[^\1])*?(?:\1|$) @@ -129,20 +128,30 @@ static public function matchAll ($patt, $subject, $preprocess_patt = false, $off return $count ? $matches : array(); } - static public function createFunctionPatt ($list, $include_math_function = false) + static public function createFunctionPatt ($list, $options = array()) { + // Bare parens. $question = ''; - if ($include_math_function) { + if (! empty($options['bare_paren'])) { $question = '?'; // Signing on math bare parens. $list[] = '-'; } + // Escape function names. foreach ($list as &$fn_name) { $fn_name = preg_quote($fn_name); } - return CssCrush_Regex::create('(' . implode('|', $list) . ')' . $question . '\(', 'iS'); + // Templating func. + $template = ''; + if (! empty($options['templating'])) { + $template = '#|'; + } + + $flat_list = implode('|', $list); + + return CssCrush_Regex::create("($template(?:$flat_list)$question)\(", 'iS'); } } diff --git a/lib/CssCrush/Template.php b/lib/CssCrush/Template.php index 1372e27..54235a9 100644 --- a/lib/CssCrush/Template.php +++ b/lib/CssCrush/Template.php @@ -1,7 +1,7 @@ true)); + } + + // Interpolation. + if (! empty($options['interpolate'])) { + $this->interpolate = true; + $str = CssCrush::$process->restoreTokens($str, 's', true); + } + // Parse all arg function calls in the passed string, // callback creates default values. - CssCrush_Function::executeOnString($str, - CssCrush_Regex::$patt->argFunction, array( - 'arg' => array($this, 'capture'), - '#' => array($this, 'capture'), - )); + CssCrush_Function::executeOnString($str, $arg_patt, array( + 'arg' => array($this, 'capture'), + '#' => array($this, 'capture'), + )); + $this->string = $str; } @@ -123,7 +138,15 @@ public function apply (array $args = null, $str = null) list($find, $replace) = $this->substitutions; } - return isset($find) ? str_replace($find, $replace, $str) : $str; + // Apply substitutions. + $str = isset($find) ? str_replace($find, $replace, $str) : $str; + + // Re-tokenize string literals on returns. + if ($this->interpolate) { + CssCrush::$process->captureStrings($str); + } + + return $str; } public function count () diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php index a28ca98..69a23be 100644 --- a/lib/CssCrush/Util.php +++ b/lib/CssCrush/Util.php @@ -88,7 +88,7 @@ static public function splitDelimList ($str, $delim = ',', $trim = true) if ($trim) { $str = trim($str); } - return array($str); + return strlen($str) ? array($str) : array(); } if (strpos($str, '(') !== false) { diff --git a/plugins/canvas.php b/plugins/canvas.php index e811fde..f67c9a8 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -56,7 +56,7 @@ function csscrush__canvas_generator ($input, $fn_name) { $cache_key = $fn_name . $input; if (isset($process->misc->canvas_cache[$cache_key])) { - + return $process->misc->canvas_cache[$cache_key]; } @@ -76,7 +76,7 @@ function csscrush__canvas_generator ($input, $fn_name) { $name = strtolower(array_shift($args)); - // Bail if no SVG registered by this name. + // Bail if name not registered. $canvas_defs =& $process->misc->canvas_defs; if (! isset($canvas_defs[$name])) { @@ -86,24 +86,27 @@ function csscrush__canvas_generator ($input, $fn_name) { // Apply args to template. $block = $canvas_defs[$name]->apply($args); - // Parse the block into a keyed assoc array. + // Parse the block into a keyed array. $raw_data = array_change_key_case(CssCrush_Util::parseBlock($block, true)); // Resolve properties, set defaults if not present. $properties = array_intersect_key($raw_data, $custom_attrs) + array( 'width' => 100, 'height' => 100, - 'fill' => '#000', + 'fill' => 'black', ); // Apply functions. - $storage = new stdClass(); - csscrush__canvas_apply_css_funcs($properties, $storage); + $context = new stdClass(); + csscrush__canvas_apply_css_funcs($properties, $context); // Extract variables. extract($properties); $width = intval($width); $height = intval($height); + if (isset($context->fill)) { + $fill = $context->fill; + } // Create image object. $image = imagecreatetruecolor($width, $height); @@ -114,9 +117,50 @@ function csscrush__canvas_generator ($input, $fn_name) { imagesavealpha($image, true); // Gradient fill. - if (is_array($fill)) { + if (is_object($fill)) { + + // Resolve drawing direction. + if ($fill->direction === 'horizontal') { + $line_numbers = imagesx($image); + $line_width = imagesy($image); + } + else { + $line_numbers = imagesy($image); + $line_width = imagesx($image); + } + + list($r1, $g1, $b1, $a1) = $fill->stops[0]; + list($r2, $g2, $b2, $a2) = $fill->stops[1]; + + $r = $g = $b = $a = -1; + + for ($line = 0; $line < $line_numbers; $line++) { + $last = "$r,$g,$b,$a"; + + $r = $r2 - $r1 ? intval($r1 + ($r2 - $r1) * ($line / $line_numbers)): $r1; + $g = $g2 - $g1 ? intval($g1 + ($g2 - $g1) * ($line / $line_numbers)): $g1; + $b = $b2 - $b1 ? intval($b1 + ($b2 - $b1) * ($line / $line_numbers)): $b1; + $a = $a2 - $a1 ? ($a1 + ($a2 - $a1) * ($line / $line_numbers)) : $a1; + $a = csscrush__canvas_opacity($a); + + if ($last != "$r,$g,$b,$a") { + $color = imagecolorallocatealpha($image, $r, $g, $b, $a); + } + + switch($fill->direction) { + case 'horizontal': + imagefilledrectangle($image, $line, 0, $line, $line_width, $color); + break; + case 'vertical': + default: + imagefilledrectangle($image, 0, $line, $line_width, $line, $color); + break; + } + imagealphablending($image, true); + } } + // Solid color fill. elseif ($solid = CssCrush_Color::parse($fill)) { @@ -124,6 +168,7 @@ function csscrush__canvas_generator ($input, $fn_name) { $fill = imagecolorallocatealpha($image, $r, $g, $b, csscrush__canvas_opacity($a)); imagefilledrectangle($image, 0, 0, $width, $height, $fill); } + // Failure to parse fill. else { @@ -161,16 +206,47 @@ function csscrush__canvas_generator ($input, $fn_name) { return $url->label; } +function csscrush__canvas_fn_linear_gradient ($input, $context) { + $args = CssCrush_Function::parseArgs($input) + array( + 'white', 'black', + ); + $first_arg = strtolower($args[0]); + static $directions = array( + 'to top' => array('vertical', true), + 'to right' => array('horizontal', false), + 'to bottom' => array('vertical', false), + 'to left' => array('horizontal', true), + ); -function csscrush__canvas_fn_linear_gradient ($input, $extra) { + if (isset($directions[$first_arg])) { + list($direction, $flip) = $directions[$first_arg]; + array_shift($args); + } + else { + list($direction, $flip) = $directions['to bottom']; + } + + // Create fill object. + $fill = new stdClass(); + $fill->stops = array(); + $fill->direction = $direction; + + // Start color. + $color = CssCrush_Color::parse($args[0]); + $fill->stops[] = $color ? $color : array(0,0,0,1); - $colors = CssCrush_Function::parseArgs($input); - $extra->fill = array(); + // End color. + $color = CssCrush_Color::parse($args[1]); + $fill->stops[] = $color ? $color : array(255,255,255,1); + + if ($flip) { + $fill->stops = array_reverse($fill->stops); + } - return ''; + $context->fill = $fill; } function csscrush__canvas_apply_css_funcs (&$properties, $extra) { @@ -184,7 +260,7 @@ function csscrush__canvas_apply_css_funcs (&$properties, $extra) { $generic_functions = array_diff_key(CssCrush_Function::$functions, $fill_functions); $generic_functions_patt - = CssCrush_Regex::createFunctionPatt(array_keys($generic_functions), true); + = CssCrush_Regex::createFunctionPatt(array_keys($generic_functions), array('bare_paren' => true)); $fill_functions_patt = CssCrush_Regex::createFunctionPatt(array_keys($fill_functions)); } @@ -192,10 +268,10 @@ function csscrush__canvas_apply_css_funcs (&$properties, $extra) { foreach ($properties as $property => &$value) { CssCrush_Function::executeOnString($value, $generic_functions_patt); - // if ($property === 'fill') { - // CssCrush_Function::executeOnString( - // $value, $fill_functions_patt, $fill_functions, $extra); - // } + if ($property === 'fill') { + CssCrush_Function::executeOnString( + $value, $fill_functions_patt, $fill_functions, $extra); + } } } diff --git a/plugins/hocus-pocus.php b/plugins/hocus-pocus.php index 65fc480..9188eac 100644 --- a/plugins/hocus-pocus.php +++ b/plugins/hocus-pocus.php @@ -18,11 +18,11 @@ )); function csscrush__enable_hocus_pocus () { - CssCrush::$config->selectorAliases['hocus'] = ':any(:hover,:focus)'; - CssCrush::$config->selectorAliases['pocus'] = ':any(:hover,:focus,:active)'; + CssCrush::addSelectorAlias('hocus', ':any(:hover,:focus)'); + CssCrush::addSelectorAlias('pocus', ':any(:hover,:focus,:active)'); } function csscrush__disable_hocus_pocus () { - unset( CssCrush::$config->selectorAliases['hocus']); - unset( CssCrush::$config->selectorAliases['pocus']); + CssCrush::removeSelectorAlias('hocus'); + CssCrush::removeSelectorAlias('pocus'); } diff --git a/plugins/svg.php b/plugins/svg.php index 19f7655..2935355 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -656,8 +656,10 @@ function csscrush__svg_apply_css_funcs ($element, &$raw_data) { ); $generic_functions = array_diff_key(CssCrush_Function::$functions, $fill_functions); - $generic_functions_patt = CssCrush_Regex::createFunctionPatt(array_keys($generic_functions), true); - $fill_functions_patt = CssCrush_Regex::createFunctionPatt(array_keys($fill_functions)); + $generic_functions_patt = CssCrush_Regex::createFunctionPatt( + array_keys($generic_functions), array('bare_paren' => true)); + $fill_functions_patt = CssCrush_Regex::createFunctionPatt( + array_keys($fill_functions)); } foreach ($raw_data as $property => &$value) { From 204c9b0a40731abafde9dd8e3b3eb46424ce9265 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 11 Apr 2013 14:49:13 +0100 Subject: [PATCH 098/421] More work on the canvas plugin. Added some of the experimental dimension values 'width:min-content' etc. aliases --- Aliases.ini | 42 +++-- Plugins.ini | 2 +- lib/CssCrush/Core.php | 10 +- plugins/canvas.php | 429 +++++++++++++++++++++++++++++++----------- 4 files changed, 355 insertions(+), 128 deletions(-) diff --git a/Aliases.ini b/Aliases.ini index 3a60091..a9a3edf 100644 --- a/Aliases.ini +++ b/Aliases.ini @@ -47,17 +47,6 @@ backface-visibility[] = -moz-backface-visibility backface-visibility[] = -ms-backface-visibility - ; Background clip. - background-clip[] = -webkit-background-clip - - ; Background origin. - background-origin[] = -webkit-background-origin - background-origin[] = -moz-background-origin - - ; Background size. - background-size[] = -webkit-background-size - background-size[] = -moz-background-size - ; Border radius. border-radius[] = -webkit-border-radius border-top-left-radius[] = -webkit-border-top-left-radius @@ -277,6 +266,37 @@ cursor:zoom-out[] = cursor:-ms-zoom-out cursor:zoom-out[] = cursor:-o-zoom-out + ; Experimental width values. + width:max-content[] = width:intrinsic + width:max-content[] = width:-webkit-max-content + width:max-content[] = width:-moz-max-content + width:min-content[] = width:-webkit-min-content + width:min-content[] = width:-moz-min-content + width:available[] = width:-webkit-available + width:available[] = width:-moz-available + width:fit-content[] = width:-webkit-fit-content + width:fit-content[] = width:-moz-fit-content + + max-width:max-content[] = max-width:intrinsic + max-width:max-content[] = max-width:-webkit-max-content + max-width:max-content[] = max-width:-moz-max-content + max-width:min-content[] = max-width:-webkit-min-content + max-width:min-content[] = max-width:-moz-min-content + max-width:available[] = max-width:-webkit-available + max-width:available[] = max-width:-moz-available + max-width:fit-content[] = max-width:-webkit-fit-content + max-width:fit-content[] = max-width:-moz-fit-content + + min-width:max-content[] = min-width:intrinsic + min-width:max-content[] = min-width:-webkit-max-content + min-width:max-content[] = min-width:-moz-max-content + min-width:min-content[] = min-width:-webkit-min-content + min-width:min-content[] = min-width:-moz-min-content + min-width:available[] = min-width:-webkit-available + min-width:available[] = min-width:-moz-available + min-width:fit-content[] = min-width:-webkit-fit-content + min-width:fit-content[] = min-width:-moz-fit-content + ;---------------------------------------------------------------- ; Function aliases. diff --git a/Plugins.ini b/Plugins.ini index 18754ce..ffd455e 100644 --- a/Plugins.ini +++ b/Plugins.ini @@ -9,5 +9,5 @@ ; ;---------------------------------------------------------------- -; Example use: +; For example... ; plugins[] = svg diff --git a/lib/CssCrush/Core.php b/lib/CssCrush/Core.php index 5174d4f..40aac7f 100644 --- a/lib/CssCrush/Core.php +++ b/lib/CssCrush/Core.php @@ -590,10 +590,14 @@ static public function clearLog () CssCrush::$log = array(); } - static public function logError ($msg) + static public function logError ($errors) { - self::$process->errors[] = $msg; - self::log($msg); + $errors = (array) $errors; + self::$process->errors = array_merge($errors, self::$process->errors); + + foreach ($errors as $error) { + self::log($error); + } } static public function runStat ($name) diff --git a/plugins/canvas.php b/plugins/canvas.php index f67c9a8..1633ba9 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -1,7 +1,42 @@ stream->pregReplaceCallback($patt, $callback); } +function csscrush_fn__canvas ($input) { + + return csscrush__canvas_generator($input, 'canvas'); +} + +function csscrush_fn__canvas_data ($input) { + + return csscrush__canvas_generator($input, 'canvas-data'); +} + function csscrush__canvas_generator ($input, $fn_name) { $process = CssCrush::$process; + // Check GD requirements are met. + static $requirements; + if (! isset($requirements)) { + $requirements = csscrush__canvas_requirements(); + } + if ($requirements === false) { + return ''; + } + + // Check process cache. $cache_key = $fn_name . $input; if (isset($process->misc->canvas_cache[$cache_key])) { - return $process->misc->canvas_cache[$cache_key]; } - // Non standard attributes. - static $custom_attrs = array( - 'fill' => true, - 'width' => true, - 'height' => true, - ); - - // Bail if no args. + // Parse args, bail if none. $args = CssCrush_Function::parseArgs($input); if (! isset($args[0])) { - return ''; } @@ -79,7 +115,6 @@ function csscrush__canvas_generator ($input, $fn_name) { // Bail if name not registered. $canvas_defs =& $process->misc->canvas_defs; if (! isset($canvas_defs[$name])) { - return ''; } @@ -87,104 +122,53 @@ function csscrush__canvas_generator ($input, $fn_name) { $block = $canvas_defs[$name]->apply($args); // Parse the block into a keyed array. - $raw_data = array_change_key_case(CssCrush_Util::parseBlock($block, true)); + $raw = array_change_key_case(CssCrush_Util::parseBlock($block, true)); - // Resolve properties, set defaults if not present. - $properties = array_intersect_key($raw_data, $custom_attrs) + array( + // Create canvas object. + $canvas = new CssCrush_Canvas(); + + // Parseable canvas attributes with default values. + static $schema = array( + 'fill' => null, + 'background-fill' => null, 'width' => 100, 'height' => 100, - 'fill' => 'black', + 'margin' => 0, ); - // Apply functions. - $context = new stdClass(); - csscrush__canvas_apply_css_funcs($properties, $context); - - // Extract variables. - extract($properties); - $width = intval($width); - $height = intval($height); - if (isset($context->fill)) { - $fill = $context->fill; - } - - // Create image object. - $image = imagecreatetruecolor($width, $height); - - // Create transparent canvas background. - imagealphablending($image, false); - imagefill($image, 0, 0, imagecolorallocatealpha($image, 0, 0, 0, 127)); - imagesavealpha($image, true); - - // Gradient fill. - if (is_object($fill)) { - - // Resolve drawing direction. - if ($fill->direction === 'horizontal') { - $line_numbers = imagesx($image); - $line_width = imagesy($image); - } - else { - $line_numbers = imagesy($image); - $line_width = imagesx($image); - } - - list($r1, $g1, $b1, $a1) = $fill->stops[0]; - list($r2, $g2, $b2, $a2) = $fill->stops[1]; - - $r = $g = $b = $a = -1; - - for ($line = 0; $line < $line_numbers; $line++) { - - $last = "$r,$g,$b,$a"; + // Resolve properties, set defaults if not present. + $canvas->raw = array_intersect_key($raw, $schema) + $schema; - $r = $r2 - $r1 ? intval($r1 + ($r2 - $r1) * ($line / $line_numbers)): $r1; - $g = $g2 - $g1 ? intval($g1 + ($g2 - $g1) * ($line / $line_numbers)): $g1; - $b = $b2 - $b1 ? intval($b1 + ($b2 - $b1) * ($line / $line_numbers)): $b1; - $a = $a2 - $a1 ? ($a1 + ($a2 - $a1) * ($line / $line_numbers)) : $a1; - $a = csscrush__canvas_opacity($a); + // Pre-populate. + csscrush__canvas_preprocess($canvas); - if ($last != "$r,$g,$b,$a") { - $color = imagecolorallocatealpha($image, $r, $g, $b, $a); - } + // Apply functions. + csscrush__canvas_apply_css_funcs($canvas); - switch($fill->direction) { - case 'horizontal': - imagefilledrectangle($image, $line, 0, $line, $line_width, $color); - break; - case 'vertical': - default: - imagefilledrectangle($image, 0, $line, $line_width, $line, $color); - break; - } - imagealphablending($image, true); - } - } + // Create fingerprint for this canvas based on canvas object. + $fingerprint = substr(md5(serialize($canvas)), 0, 7); + $generated_filename = "cnv-$name-$fingerprint.png"; + $generated_filepath = $process->output->dir . '/' . $generated_filename; + $cached_file = file_exists($generated_filepath); - // Solid color fill. - elseif ($solid = CssCrush_Color::parse($fill)) { + if (! $cached_file) { + // Create transparent image as base. + csscrush__canvas_create($canvas); - list($r, $g, $b, $a) = $solid; - $fill = imagecolorallocatealpha($image, $r, $g, $b, csscrush__canvas_opacity($a)); - imagefilledrectangle($image, 0, 0, $width, $height, $fill); + // Apply fill layers. + csscrush__canvas_fill($canvas, 'background-fill'); + csscrush__canvas_fill($canvas, 'fill'); } - - // Failure to parse fill. else { - - imagedestroy($image); - return ''; + // csscrush::log('file cached'); } // Either write to a file. if ($fn_name === 'canvas') { - // Create fingerprint for the created file. - $fingerprint = substr(md5("{$width}x{$height}$fill"), 0, 7); - $generated_filename = "cnv-$name-$fingerprint.png"; - $generated_path = $process->output->dir . '/' . $generated_filename; - - imagepng($image, $generated_path); + if (! $cached_file) { + imagepng($canvas->image, $generated_filepath); + } // Write to the same directory as the output css. $url = new CssCrush_Url($generated_filename); @@ -192,9 +176,14 @@ function csscrush__canvas_generator ($input, $fn_name) { } // Or create data uri. else { - ob_start(); - imagepng($image); - $data = ob_get_clean(); + if (! $cached_file) { + ob_start(); + imagepng($canvas->image); + $data = ob_get_clean(); + } + else { + $data = file_get_contents($generated_filepath); + } $url = new CssCrush_Url('data:image/png;base64,' . base64_encode($data)); } @@ -202,11 +191,11 @@ function csscrush__canvas_generator ($input, $fn_name) { // Cache the output URL. $process->misc->canvas_cache[$cache_key] = $url->label; - imagedestroy($image); return $url->label; } -function csscrush__canvas_fn_linear_gradient ($input, $context) { + +function csscrush__canvas_fn_linear_gradient ($input, $canvas) { $args = CssCrush_Function::parseArgs($input) + array( 'white', 'black', @@ -234,6 +223,8 @@ function csscrush__canvas_fn_linear_gradient ($input, $context) { $fill->stops = array(); $fill->direction = $direction; + csscrush__canvas_set_fill_dims($fill, $canvas); + // Start color. $color = CssCrush_Color::parse($args[0]); $fill->stops[] = $color ? $color : array(0,0,0,1); @@ -246,10 +237,10 @@ function csscrush__canvas_fn_linear_gradient ($input, $context) { $fill->stops = array_reverse($fill->stops); } - $context->fill = $fill; + $canvas->fills[$canvas->currentProperty] = $fill; } -function csscrush__canvas_apply_css_funcs (&$properties, $extra) { +function csscrush__canvas_apply_css_funcs ($canvas) { // Setup functions for using on values. static $generic_functions_patt, $fill_functions, $fill_functions_patt; @@ -265,16 +256,223 @@ function csscrush__canvas_apply_css_funcs (&$properties, $extra) { = CssCrush_Regex::createFunctionPatt(array_keys($fill_functions)); } - foreach ($properties as $property => &$value) { + foreach ($canvas->raw as $property => &$value) { + + if (! is_string($value)) { + continue; + } + CssCrush_Function::executeOnString($value, $generic_functions_patt); - if ($property === 'fill') { + if (in_array($property, array('fill', 'background-fill'))) { + $canvas->currentProperty = $property; CssCrush_Function::executeOnString( - $value, $fill_functions_patt, $fill_functions, $extra); + $value, $fill_functions_patt, $fill_functions, $canvas); + } + } +} + +function csscrush__canvas_preprocess ($canvas) { + + if (isset($canvas->raw['margin'])) { + + $parts = csscrush__canvas_parselist($canvas->raw['margin']); + $count = count($parts); + if ($count === 1) { + $margin = array($parts[0], $parts[0], $parts[0], $parts[0]); } + elseif ($count === 2) { + $margin = array($parts[0], $parts[1], $parts[0], $parts[1]); + } + elseif ($count === 3) { + $margin = array($parts[0], $parts[1], $parts[2], $parts[1]); + } + else { + $margin = $parts; + } + } + else { + $margin = array(0,0,0,0); } + + foreach (array('fill', 'background-fill') as $fill_name) { + if (isset($canvas->raw[$fill_name])) { + $canvas->fills[$fill_name] = $canvas->raw[$fill_name]; + } + } + + $canvas->margin = $margin; + $canvas->width = intval($canvas->raw['width']); + $canvas->height = intval($canvas->raw['height']); } +/* + Adapted from GD Gradient Fill by Ozh (http://planetozh.com): + http://planetozh.com/blog/my-projects/images-php-gd-gradient-fill +*/ +function csscrush__canvas_gradient ($canvas, $fill) { + + $image = $canvas->image; + + // Resolve drawing direction. + if ($fill->direction === 'horizontal') { + $line_numbers = $fill->x2 - $fill->x1; + } + else { + $line_numbers = $fill->y2 - $fill->y1; + } + + list($r1, $g1, $b1, $a1) = $fill->stops[0]; + list($r2, $g2, $b2, $a2) = $fill->stops[1]; + + $r = $g = $b = $a = -1; + + for ($line = 0; $line < $line_numbers; $line++) { + + $last = "$r,$g,$b,$a"; + + $r = $r2 - $r1 ? intval($r1 + ($r2 - $r1) * ($line / $line_numbers)): $r1; + $g = $g2 - $g1 ? intval($g1 + ($g2 - $g1) * ($line / $line_numbers)): $g1; + $b = $b2 - $b1 ? intval($b1 + ($b2 - $b1) * ($line / $line_numbers)): $b1; + $a = $a2 - $a1 ? ($a1 + ($a2 - $a1) * ($line / $line_numbers)) : $a1; + $a = csscrush__canvas_opacity($a); + + if ($last != "$r,$g,$b,$a") { + $color = imagecolorallocatealpha($image, $r, $g, $b, $a); + } + + switch($fill->direction) { + case 'horizontal': + imagefilledrectangle($image, + $fill->x1 + $line, + $fill->y1, + $fill->x1 + $line, + $fill->y2, + $color); + + break; + case 'vertical': + default: + imagefilledrectangle($image, + $fill->x1, + $fill->y1 + $line, + $fill->x2, + $fill->y1 + $line, + $color); + break; + } + imagealphablending($image, true); + } +} + +function csscrush__canvas_create ($canvas) { + + // Create image object. + list($margin_top, $margin_right, $margin_bottom, $margin_left) = $canvas->margin; + + $width = $canvas->width + $margin_right + $margin_left; + $height = $canvas->height + $margin_top + $margin_bottom; + $canvas->image = imagecreatetruecolor($width, $height); + + // Set transparent canvas background. + imagealphablending($canvas->image, false); + $fill = imagecolorallocatealpha($canvas->image, 0, 0, 0, 127); + imagefill($canvas->image, 0, 0, $fill); + + imagesavealpha($canvas->image, true); +} + +function csscrush__canvas_fill ($canvas, $property) { + + if (! isset($canvas->fills[$property])) { + return false; + } + $fill = $canvas->fills[$property]; + + // Gradient fill. + if (is_object($fill)) { + csscrush__canvas_gradient($canvas, $fill); + } + + // Solid color fill. + elseif ($solid = CssCrush_Color::parse($fill)) { + + list($r, $g, $b, $a) = $solid; + $color = imagecolorallocatealpha($canvas->image, $r, $g, $b, csscrush__canvas_opacity($a)); + + $fill = new stdClass(); + $canvas->currentProperty = $property; + csscrush__canvas_set_fill_dims($fill, $canvas); + + imagefilledrectangle($canvas->image, $fill->x1, $fill->y1, $fill->x2, $fill->y2, $color); + imagealphablending($canvas->image, true); + } + + // Can't parse. + else { + return false; + } +} + +function csscrush__canvas_set_fill_dims ($fill, $canvas) { + + // Resolve fill dimensions and coordinates. + list($margin_top, $margin_right, $margin_bottom, $margin_left) = $canvas->margin; + + $fill->x1 = 0; + $fill->y1 = 0; + $fill->x2 = $canvas->width + $margin_right + $margin_left; + $fill->y2 = $canvas->height + $margin_top + $margin_bottom; + + if ($canvas->currentProperty === 'fill') { + $fill->x1 = $margin_left; + $fill->y1 = $margin_top; + $fill->x2 = $canvas->width + $fill->x1 - 1; + $fill->y2 = $canvas->height + $fill->y1 - 1; + } +} + +function csscrush__canvas_requirements () { + + $error_messages = array(); + + if (! extension_loaded('gd')) { + $error_messages[] = 'GD extension not available.'; + } + else { + $info = array_change_key_case(gd_info()); + foreach (array('png', 'jpeg') as $key) { + if (empty($info["$key support"])) { + $error_messages[] = "GD extension has no $key support."; + } + } + } + + if ($error_messages) { + CssCrush::logError($error_messages); + $error = implode(' ' . PHP_EOL, $error_messages); + trigger_error(__METHOD__ . ": $error\n", E_USER_WARNING); + + return false; + } + return true; +} + + +/* + Canvas object. +*/ +class CssCrush_Canvas +{ + public $image, $fills = array(); + + public function __destruct () + { + if (isset($this->image)) { + imagedestroy($this->image); + } + } +} /* Helpers. @@ -282,3 +480,8 @@ function csscrush__canvas_apply_css_funcs (&$properties, $extra) { function csscrush__canvas_opacity ($float) { return 127 - max(min(round($float * 127), 127), 0); } + +function csscrush__canvas_parselist ($str, $numbers = true) { + $list = preg_split('~ +~', trim($str)); + return $numbers ? array_map('floatval', $list) : $list; +} From f9108e844a5f0eb60264cc6c233ab3c53e111074 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 4 May 2013 14:05:43 +0100 Subject: [PATCH 099/421] Adding context variable to custom function calls. --- lib/CssCrush/Declaration.php | 8 +- lib/CssCrush/Function.php | 26 ++-- lib/CssCrush/Url.php | 13 ++ plugins/canvas.php | 282 ++++++++++++++++++++++++++++------- plugins/svg.php | 17 +-- 5 files changed, 266 insertions(+), 80 deletions(-) diff --git a/lib/CssCrush/Declaration.php b/lib/CssCrush/Declaration.php index 9076a62..fa5563f 100644 --- a/lib/CssCrush/Declaration.php +++ b/lib/CssCrush/Declaration.php @@ -93,7 +93,7 @@ public function process ($parent_rule) // this() function needs to be called exclusively because // it's self referencing. - $extra = array( + $context = (object) array( 'rule' => $parent_rule, ); CssCrush_Function::executeOnString( @@ -102,12 +102,12 @@ public function process ($parent_rule) array( 'this' => 'csscrush_fn__this', ), - $extra); + $context); // Add result to $rule->selfData. $parent_rule->selfData += array($this->property => $this->value); - $extra = array( + $context = (object) array( 'rule' => $parent_rule, 'property' => $this->property ); @@ -115,7 +115,7 @@ public function process ($parent_rule) $this->value, null, null, - $extra); + $context); } // Trim whitespace that may have been introduced by functions. diff --git a/lib/CssCrush/Function.php b/lib/CssCrush/Function.php index 4077c2c..8c562bd 100644 --- a/lib/CssCrush/Function.php +++ b/lib/CssCrush/Function.php @@ -37,7 +37,7 @@ static public function setMatchPatt () array_keys(self::$functions), array('bare_paren' => true)); } - static public function executeOnString (&$str, $patt = null, $process_callback = null, $extra = null) + static public function executeOnString (&$str, $patt = null, $process_callback = null, stdClass $context = null) { // No bracketed expressions, early return. if (strpos($str, '(') === false) { @@ -59,6 +59,11 @@ static public function executeOnString (&$str, $patt = null, $process_callback = // Find custom function matches. $matches = CssCrush_Regex::matchAll($patt, $str); + // Always pass in a context object. + if (! $context) { + $context = new stdClass(); + } + // Step through the matches from last to first. while ($match = array_pop($matches)) { @@ -81,22 +86,23 @@ static public function executeOnString (&$str, $patt = null, $process_callback = $closing_paren = $opening_paren + strlen($parens[0][0]); // Get the function arguments. - $args = trim($parens[1][0]); + $raw_args = trim($parens[1][0]); // Workaround the signs. $before_operator = '-' === $raw_fn_name ? '-' : ''; $func_returns = ''; + $context->function = $fn_name; // First look for function as directly passed. if (isset($process_callback[$fn_name])) { - $func_returns = call_user_func($process_callback[$fn_name], $args, $extra); + $func_returns = call_user_func($process_callback[$fn_name], $raw_args, $context); } // Secondly look for built-in function. elseif (isset(self::$functions[$fn_name])) { - $func_returns = call_user_func(self::$functions[$fn_name], $args, $extra); + $func_returns = call_user_func(self::$functions[$fn_name], $raw_args, $context); } // Splice in the function result. @@ -224,16 +230,16 @@ function csscrush_fn__a_adjust ($input) { return CssCrush_Color::colorAdjust($color, array(0, 0, 0, $a)); } -function csscrush_fn__this ($input, $extra) { +function csscrush_fn__this ($input, $context) { $args = CssCrush_Function::parseArgsSimple($input); $property = $args[0]; // Function relies on a context rule, bail if none. - if (! isset($extra['rule'])) { + if (! isset($context->rule)) { return ''; } - $rule = $extra['rule']; + $rule = $context->rule; $rule->expandDataSet('selfData', $property); @@ -251,16 +257,16 @@ function csscrush_fn__this ($input, $extra) { return ''; } -function csscrush_fn__query ($input, $extra) { +function csscrush_fn__query ($input, $context) { $args = CssCrush_Function::parseArgs($input); // Function relies on a context property, bail if none. - if (count($args) < 1 || ! isset($extra['property'])) { + if (count($args) < 1 || ! isset($context->property)) { return ''; } - $call_property = $extra['property']; + $call_property = $context->property; $references =& CssCrush::$process->references; // Resolve arguments. diff --git a/lib/CssCrush/Url.php b/lib/CssCrush/Url.php index 7f1fb31..df5f101 100644 --- a/lib/CssCrush/Url.php +++ b/lib/CssCrush/Url.php @@ -96,6 +96,19 @@ public function evaluate () return $this; } + public function getAbsolutePath () + { + $path = false; + if ($this->protocol) { + $path = $this->value; + } + elseif ($this->isRelative || $this->isRooted) { + $path = CssCrush::$config->docRoot . + ($this->isRelative ? $this->toRoot()->simplify()->value : $this->value); + } + return $path; + } + public function resolveRootedPath () { $process = CssCrush::$process; diff --git a/plugins/canvas.php b/plugins/canvas.php index 1633ba9..315437b 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -6,7 +6,7 @@ * * @example * - * // Red semi-transparent square. + * /* Red semi-transparent square. *\/ * @canvas foo { * width: 50; * height: 50; @@ -46,8 +46,8 @@ function csscrush__enable_canvas () { CssCrush_Hook::add('process_extract', 'csscrush__canvas_extract'); - CssCrush_Function::register('canvas', 'csscrush_fn__canvas'); - CssCrush_Function::register('canvas-data', 'csscrush_fn__canvas_data'); + CssCrush_Function::register('canvas', 'csscrush__canvas_generator'); + CssCrush_Function::register('canvas-data', 'csscrush__canvas_generator'); } function csscrush__disable_canvas () { @@ -75,17 +75,7 @@ function csscrush__canvas_extract ($process) { $process->stream->pregReplaceCallback($patt, $callback); } -function csscrush_fn__canvas ($input) { - - return csscrush__canvas_generator($input, 'canvas'); -} - -function csscrush_fn__canvas_data ($input) { - - return csscrush__canvas_generator($input, 'canvas-data'); -} - -function csscrush__canvas_generator ($input, $fn_name) { +function csscrush__canvas_generator ($input, $context) { $process = CssCrush::$process; @@ -99,7 +89,7 @@ function csscrush__canvas_generator ($input, $fn_name) { } // Check process cache. - $cache_key = $fn_name . $input; + $cache_key = $context->function . $input; if (isset($process->misc->canvas_cache[$cache_key])) { return $process->misc->canvas_cache[$cache_key]; } @@ -131,8 +121,10 @@ function csscrush__canvas_generator ($input, $fn_name) { static $schema = array( 'fill' => null, 'background-fill' => null, - 'width' => 100, - 'height' => 100, + 'src' => null, + 'canvas-filter' => null, + 'width' => null, + 'height' => null, 'margin' => 0, ); @@ -144,34 +136,92 @@ function csscrush__canvas_generator ($input, $fn_name) { // Apply functions. csscrush__canvas_apply_css_funcs($canvas); - +csscrush::log($canvas); // Create fingerprint for this canvas based on canvas object. $fingerprint = substr(md5(serialize($canvas)), 0, 7); $generated_filename = "cnv-$name-$fingerprint.png"; $generated_filepath = $process->output->dir . '/' . $generated_filename; $cached_file = file_exists($generated_filepath); + $cached_file = false; if (! $cached_file) { - // Create transparent image as base. - csscrush__canvas_create($canvas); - // Apply fill layers. - csscrush__canvas_fill($canvas, 'background-fill'); - csscrush__canvas_fill($canvas, 'fill'); + // Source arguments take priority. + if ($src = csscrush__canvas_fetch_src($canvas->raw['src'])) { + + // Resolve the src image dimensions and positioning. + $dst_w = $src->width; + $dst_h = $src->height; + if (isset($canvas->width) && isset($canvas->height)) { + $dst_w = $canvas->width; + $dst_h = $canvas->height; + } + elseif (isset($canvas->width)) { + $dst_w = $canvas->width; + $dst_h = ($src->height/$src->width) * $canvas->width; + } + elseif (isset($canvas->height)) { + $dst_w = ($src->width/$src->height) * $canvas->height; + $dst_h = $canvas->height; + } + + // Update the canvas height and width based on the src. + $canvas->width = $dst_w; + $canvas->height = $dst_h; + + // Create base. + csscrush__canvas_create($canvas); + + // Apply background layer. + csscrush__canvas_fill($canvas, 'background-fill'); + + // Filters. + csscrush__canvas_apply_filters($canvas, $src->image); + + // Place the src image on the base canvas image. + imagecopyresized( + $canvas->image, // dest_img + $src->image, // src_img + $canvas->margin->left, // dst_x + $canvas->margin->top, // dst_y + 0, // src_x + 0, // src_y + $dst_w, // dst_w + $dst_h, // dst_h + $src->width, // src_w + $src->height // src_h + ); + imagedestroy($src->image); + } + else { + + // Set defaults. + $canvas->width = isset($canvas->width) ? intval($canvas->width) : 100; + $canvas->height = isset($canvas->height) ? intval($canvas->height) : 100; + $canvas->fills += array('fill' => 'black'); + + // Create base. + csscrush__canvas_create($canvas); + + // Apply background layer. + csscrush__canvas_fill($canvas, 'background-fill'); + csscrush__canvas_fill($canvas, 'fill'); + } } else { // csscrush::log('file cached'); } + // Either write to a file. - if ($fn_name === 'canvas') { + if ($context->function === 'canvas') { if (! $cached_file) { imagepng($canvas->image, $generated_filepath); } // Write to the same directory as the output css. - $url = new CssCrush_Url($generated_filename); + $url = new CssCrush_Url("$generated_filename?" . time()); $url->noRewrite = true; } // Or create data uri. @@ -195,7 +245,7 @@ function csscrush__canvas_generator ($input, $fn_name) { } -function csscrush__canvas_fn_linear_gradient ($input, $canvas) { +function csscrush__canvas_fn_linear_gradient ($input, $context) { $args = CssCrush_Function::parseArgs($input) + array( 'white', 'black', @@ -223,7 +273,7 @@ function csscrush__canvas_fn_linear_gradient ($input, $canvas) { $fill->stops = array(); $fill->direction = $direction; - csscrush__canvas_set_fill_dims($fill, $canvas); + csscrush__canvas_set_fill_dims($fill, $context->canvas); // Start color. $color = CssCrush_Color::parse($args[0]); @@ -237,37 +287,118 @@ function csscrush__canvas_fn_linear_gradient ($input, $canvas) { $fill->stops = array_reverse($fill->stops); } - $canvas->fills[$canvas->currentProperty] = $fill; + $context->canvas->fills[$context->currentProperty] = $fill; +} + +function csscrush__canvas_fn_filter ($input, $context) { + + $args = CssCrush_Function::parseArgs($input); + + array_unshift($context->canvas->filters, array($context->function, $args)); +} + + +function csscrush__canvas_apply_filters ($canvas, $image) { + + foreach ($canvas->filters as $filter) { + list($name, $args) = $filter; + + switch ($name) { + case 'greyscale': + case 'grayscale': + imagefilter($image, IMG_FILTER_GRAYSCALE); + break; + case 'invert': + imagefilter($image, IMG_FILTER_NEGATE); + break; + case 'blur': + $level = 1; + if (isset($args[0])) { + // Allow multiple blurs for a stronger effect. + // Set hard limit. + $level = min(max(intval($args[0]), 1), 20); + } + while ($level--) { + imagefilter($image, IMG_FILTER_GAUSSIAN_BLUR); + } + break; + case 'contrast': + if (isset($args[0])) { + $level = intval($args[0]); + } + imagefilter($image, IMG_FILTER_CONTRAST, $level); + break; + case 'brightness': + // -255 <- 0 -> +255 + if (isset($args[0])) { + $level = intval($args[0]); + } + imagefilter($image, IMG_FILTER_BRIGHTNESS, $level); + break; + } + } } function csscrush__canvas_apply_css_funcs ($canvas) { // Setup functions for using on values. - static $generic_functions_patt, $fill_functions, $fill_functions_patt; - if (! $generic_functions_patt) { + static $map; + if (! $map) { + $fill_functions = array( 'canvas-linear-gradient' => 'csscrush__canvas_fn_linear_gradient', ); - $generic_functions - = array_diff_key(CssCrush_Function::$functions, $fill_functions); - $generic_functions_patt - = CssCrush_Regex::createFunctionPatt(array_keys($generic_functions), array('bare_paren' => true)); - $fill_functions_patt - = CssCrush_Regex::createFunctionPatt(array_keys($fill_functions)); + $map['fill'] = array( + 'patt' => CssCrush_Regex::createFunctionPatt(array_keys($fill_functions)), + 'functions' => $fill_functions, + ); + + $filter_functions = array( + 'contrast' => 'csscrush__canvas_fn_filter', + 'grayscale' => 'csscrush__canvas_fn_filter', + 'greyscale' => 'csscrush__canvas_fn_filter', + 'brightness' => 'csscrush__canvas_fn_filter', + 'invert' => 'csscrush__canvas_fn_filter', + 'blur' => 'csscrush__canvas_fn_filter', + ); + $map['filter'] = array( + 'patt' => CssCrush_Regex::createFunctionPatt(array_keys($filter_functions)), + 'functions' => $filter_functions, + ); + + $generic_functions = array_diff_key( + CssCrush_Function::$functions, $map['fill']['functions']); + $map['generic'] = array( + 'patt' => CssCrush_Regex::createFunctionPatt( + array_keys($generic_functions), array('bare_paren' => true)), + 'functions' => $generic_functions, + ); } + // Function context object. + $context = new stdClass(); + foreach ($canvas->raw as $property => &$value) { if (! is_string($value)) { continue; } - CssCrush_Function::executeOnString($value, $generic_functions_patt); + // Generic functions. + CssCrush_Function::executeOnString( + $value, $map['generic']['patt'], $map['generic']['functions']); + // Fill functions. if (in_array($property, array('fill', 'background-fill'))) { - $canvas->currentProperty = $property; + $context->currentProperty = $property; + $context->canvas = $canvas; + CssCrush_Function::executeOnString( + $value, $map['fill']['patt'], $map['fill']['functions'], $context); + } + elseif ($property === 'canvas-filter') { + $context->canvas = $canvas; CssCrush_Function::executeOnString( - $value, $fill_functions_patt, $fill_functions, $canvas); + $value, $map['filter']['patt'], $map['filter']['functions'], $context); } } } @@ -301,11 +432,58 @@ function csscrush__canvas_preprocess ($canvas) { } } - $canvas->margin = $margin; - $canvas->width = intval($canvas->raw['width']); - $canvas->height = intval($canvas->raw['height']); + $canvas->margin = (object) array( + 'top' => $margin[0], + 'right' => $margin[1], + 'bottom' => $margin[2], + 'left' => $margin[3], + ); + $canvas->width = $canvas->raw['width']; + $canvas->height = $canvas->raw['height']; } +function csscrush__canvas_fetch_src ($url_token) { + + if ($url_token && $url = CssCrush::$process->fetchToken($url_token)) { + + $file = $url->getAbsolutePath(); + + // Testing the image availability and getting info. + if ($info = @getimagesize($file)) { + + $image = null; + + // If image is available copy it. + switch ($info['mime']) { + case 'image/png': + $image = imagecreatefrompng($file); + break; + case 'image/jpg': + case 'image/jpeg': + $image = imagecreatefromjpeg($file); + break; + case 'image/gif': + $image = imagecreatefromgif($file); + break; + case 'image/webp': + $image = imagecreatefromwebp($file); + break; + } + if ($image) { + return (object) array( + 'file' => $file, + 'info' => $info, + 'width' => $info[0], + 'height' => $info[1], + 'image' => $image, + ); + } + } + } + return false; +} + + /* Adapted from GD Gradient Fill by Ozh (http://planetozh.com): http://planetozh.com/blog/my-projects/images-php-gd-gradient-fill @@ -367,11 +545,11 @@ function csscrush__canvas_gradient ($canvas, $fill) { function csscrush__canvas_create ($canvas) { - // Create image object. - list($margin_top, $margin_right, $margin_bottom, $margin_left) = $canvas->margin; + $margin = $canvas->margin; + $width = $canvas->width + $margin->right + $margin->left; + $height = $canvas->height + $margin->top + $margin->bottom; - $width = $canvas->width + $margin_right + $margin_left; - $height = $canvas->height + $margin_top + $margin_bottom; + // Create image object. $canvas->image = imagecreatetruecolor($width, $height); // Set transparent canvas background. @@ -417,16 +595,16 @@ function csscrush__canvas_fill ($canvas, $property) { function csscrush__canvas_set_fill_dims ($fill, $canvas) { // Resolve fill dimensions and coordinates. - list($margin_top, $margin_right, $margin_bottom, $margin_left) = $canvas->margin; + $margin = $canvas->margin; $fill->x1 = 0; $fill->y1 = 0; - $fill->x2 = $canvas->width + $margin_right + $margin_left; - $fill->y2 = $canvas->height + $margin_top + $margin_bottom; + $fill->x2 = $canvas->width + $margin->right + $margin->left; + $fill->y2 = $canvas->height + $margin->top + $margin->bottom; if ($canvas->currentProperty === 'fill') { - $fill->x1 = $margin_left; - $fill->y1 = $margin_top; + $fill->x1 = $margin->left; + $fill->y1 = $margin->top; $fill->x2 = $canvas->width + $fill->x1 - 1; $fill->y2 = $canvas->height + $fill->y1 - 1; } @@ -464,7 +642,7 @@ function csscrush__canvas_requirements () { */ class CssCrush_Canvas { - public $image, $fills = array(); + public $image, $fills = array(), $filters = array(); public function __destruct () { diff --git a/plugins/svg.php b/plugins/svg.php index 2935355..8a905dd 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -2,10 +2,6 @@ /** * Define and embed simple SVG elements, paths and effects inside CSS * - * @svg - * ---- - * @-rule for defining SVG shapes. Uses custom shortcut properites alongside, - * standard SVG properties. * * * Element types @@ -32,7 +28,7 @@ * } * * - * Issues + * @issues * ------ * Firefox does not allow linked images (or other svg) when SVG is in "svg as image" mode - * i.e. Used in an img tag or as a CSS background: @@ -782,21 +778,14 @@ function csscrush__svg_fn_pattern ($input, $element) { CssCrush_Function::parseArgs($input) + array('', '', 0, 0, 0, 0); - $url = CssCrush::$process->popToken($url); + $url = CssCrush::$process->fetchToken($url); if (! $url) { - return ''; } // If $width or $height is not specified get image dimensions the slow way. if (! $width || ! $height) { - if (in_array($url->protocol, array('http', 'https', 'data'))) { - $file = $url->value; - } - elseif ($url->isRelative || $url->isRooted) { - $file = CssCrush::$config->docRoot . - ($url->isRelative ? $url->toRoot()->simplify()->value : $url->value); - } + $file = $url->getAbsolutePath(); list($width, $height) = getimagesize($file); } From 1166481ef674d2c74523781852be9ccbc0f8bc61 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 4 May 2013 20:21:51 +0100 Subject: [PATCH 100/421] Renamed `rem-fallback` plugin to `rem` and expanded it to work in different modes and optionally affect all properties not just font related. Renamed `px2rem` plugin to `px2em`. --- plugins/{px2rem.php => px2em.php} | 40 ++++---- plugins/rem-fallback.php | 67 ------------- plugins/rem.php | 150 ++++++++++++++++++++++++++++++ 3 files changed, 170 insertions(+), 87 deletions(-) rename plugins/{px2rem.php => px2em.php} (71%) delete mode 100644 plugins/rem-fallback.php create mode 100644 plugins/rem.php diff --git a/plugins/px2rem.php b/plugins/px2em.php similarity index 71% rename from plugins/px2rem.php rename to plugins/px2em.php index 17477dd..3ec1547 100644 --- a/plugins/px2rem.php +++ b/plugins/px2em.php @@ -1,59 +1,59 @@ 'csscrush__enable_px2rem', - 'disable' => 'csscrush__disable_px2rem', +CssCrush_Plugin::register( 'px2em', array( + 'enable' => 'csscrush__enable_px2em', + 'disable' => 'csscrush__disable_px2em', )); -function csscrush__enable_px2rem () { - CssCrush_Function::register('px2rem', 'csscrush_fn__px2rem'); +function csscrush__enable_px2em () { CssCrush_Function::register('px2em', 'csscrush_fn__px2em'); + CssCrush_Function::register('px2rem', 'csscrush_fn__px2rem'); } -function csscrush__disable_px2rem () { - CssCrush_Function::deRegister('px2rem'); +function csscrush__disable_px2em () { CssCrush_Function::deRegister('px2em'); + CssCrush_Function::deRegister('px2rem'); } -function csscrush_fn__px2rem ($input) { +function csscrush_fn__px2em ($input) { $base = 16; // Override default base if variable is set. - if (isset(CssCrush::$process->variables['px2rem__base'])) { - $base = CssCrush::$process->variables['px2rem__base']; + if (isset(CssCrush::$process->variables['px2em__base'])) { + $base = CssCrush::$process->variables['px2em__base']; } - return csscrush__px2rem($input, 'rem', $base); + return csscrush__px2em($input, 'em', $base); } -function csscrush_fn__px2em ($input) { +function csscrush_fn__px2rem ($input) { $base = 16; // Override default base if variable is set. - if (isset(CssCrush::$process->variables['px2em__base'])) { - $base = CssCrush::$process->variables['px2em__base']; + if (isset(CssCrush::$process->variables['px2rem__base'])) { + $base = CssCrush::$process->variables['px2rem__base']; } - return csscrush__px2rem($input, 'em', $base); + return csscrush__px2em($input, 'rem', $base); } -function csscrush__px2rem ($input, $unit, $default_base) { +function csscrush__px2em ($input, $unit, $default_base) { list($px, $base) = CssCrush_Function::parseArgsSimple($input) + array( 16, diff --git a/plugins/rem-fallback.php b/plugins/rem-fallback.php deleted file mode 100644 index 58c3211..0000000 --- a/plugins/rem-fallback.php +++ /dev/null @@ -1,67 +0,0 @@ - 'csscrush__enable_rem_fallback', - 'disable' => 'csscrush__disable_rem_fallback', -)); - -function csscrush__enable_rem_fallback () { - CssCrush_Hook::add( 'rule_prealias', 'csscrush__rem_fallback' ); -} - -function csscrush__disable_rem_fallback () { - CssCrush_Hook::remove( 'rule_prealias', 'csscrush__rem_fallback' ); -} - -function csscrush__rem_fallback (CssCrush_Rule $rule) { - - static $fontsize_properties, $rem_patt; - if (! $fontsize_properties) { - $fontsize_properties = array( - 'font' => true, - 'font-size' => true, - ); - $rem_patt = CssCrush_Regex::create('()rem', 'iS'); - } - - if (! array_intersect_key($rule->canonicalProperties, $fontsize_properties)) { - return; - } - - $new_set = array(); - $rule_updated = false; - foreach ($rule->declarations as $declaration) { - if ( - ! $declaration->skip && - isset($fontsize_properties[$declaration->canonicalProperty]) && - preg_match_all($rem_patt, $declaration->value, $m) - ) { - // Value has rem, create new declaration with rem value converted to pixel. - $find = $m[0]; - $replace = array(); - foreach ($m[1] as $num) { - $replace[] = round(floatval($num) * 16, 5) . 'px'; - } - $new_set[] = new CssCrush_Declaration( - $declaration->property, str_replace($find, $replace, $declaration->value)); - $rule_updated = true; - } - $new_set[] = $declaration; - } - - if ($rule_updated) { - $rule->setDeclarations($new_set); - } -} diff --git a/plugins/rem.php b/plugins/rem.php new file mode 100644 index 0000000..ee4da38 --- /dev/null +++ b/plugins/rem.php @@ -0,0 +1,150 @@ + 8 supports rem units which will resize. See http://caniuse.com/#feat=rem + * + * Three conversion modes: + * + * rem-fallback (rem to px, with converted value as fallback) + * ============ + * font-size: 1rem; + * + * font-size: 16px; + * font-size: 1rem; + * + * px-fallback (px to rem, with original pixel value as fallback) + * =========== + * font-size: 16px; + * + * font-size: 16px; + * font-size: 1rem; + * + * convert (in-place px to rem conversion) + * ======= + * font-size: 16px; + * + * font-size: 1rem; + * + * `rem-fallback` is the default mode. To change the conversion mode set a + * variable named `rem__mode` with the mode name you want as its value. + * + * To convert all values, not just values of the font related properties, + * set a variable named `rem__all` with a value of `yes`. + */ + +CssCrush_Plugin::register('rem', array( + 'enable' => 'csscrush__enable_rem', + 'disable' => 'csscrush__disable_rem', +)); + +function csscrush__enable_rem () { + CssCrush_Hook::add('rule_prealias', 'csscrush__rem'); +} + +function csscrush__disable_rem () { + CssCrush_Hook::remove('rule_prealias', 'csscrush__rem'); +} + +function csscrush__rem (CssCrush_Rule $rule) { + + static $rem_patt, $px_patt, $font_props, $modes; + if (! $modes) { + $rem_patt = CssCrush_Regex::create('()rem', 'iS'); + $px_patt = CssCrush_Regex::create('()px', 'iS'); + $font_props = array( + 'font' => true, + 'font-size' => true, + 'line-height' => true, + ); + $modes = array('rem-fallback', 'px-fallback', 'convert'); + } + + $vars =& CssCrush::$process->variables; + + // Determine which properties are touched; all, or just font related. + $just_font_props = ! isset($vars['rem__all']); + + if ($just_font_props && ! array_intersect_key($rule->canonicalProperties, $font_props)) { + return; + } + + // Determine what conversion mode we're using. + $mode = $modes[0]; + if (isset($vars['rem__mode'])) { + $_mode = $vars['rem__mode']; + if (in_array($_mode, $modes)) { + $mode = $_mode; + } + } + + // Determine the default base em-size, to my knowledge always 16px. + $base = isset($vars['rem__base']) ? $vars['rem__base'] : 16; + + // Select the length match pattern depending on mode. + $length_patt = $mode === 'rem-fallback' ? $rem_patt : $px_patt; + + $new_set = array(); + $rule_updated = false; + foreach ($rule->declarations as $declaration) { + if ( + $declaration->skip || + ($just_font_props && ! isset($font_props[$declaration->canonicalProperty])) || + ! preg_match_all($length_patt, $declaration->value, $m) + ) { + // csscrush::log($font_props, $declaration->canonicalProperty); + csscrush::log($m, $declaration->canonicalProperty); + + $new_set[] = $declaration; + continue; + } + + // Value has matching length components. + $find = $m[0]; + $replace = array(); + $numbers = $m[1]; + + switch ($mode) { + // Converting a rem value to px. + case 'rem-fallback': + foreach ($numbers as $num) { + $replace[] = round(floatval($num) * $base, 5) . 'px'; + } + break; + + // Converting a px value to rem. + case 'convert': + case 'px-fallback': + foreach ($numbers as $num) { + $replace[] = round(floatval($num) / $base, 5) . 'rem'; + } + break; + } + + $converted_value = str_replace($find, $replace, $declaration->value); + + if ($mode === 'convert') { + $declaration->value = $converted_value; + $new_set[] = $declaration; + } + else { + $clone = clone $declaration; + $clone->value = $converted_value; + $rule_updated = true; + + if ($mode === 'px-fallback') { + $new_set[] = $declaration; + $new_set[] = $clone; + } + else { + $new_set[] = $clone; + $new_set[] = $declaration; + } + } + } + + if ($rule_updated) { + $rule->setDeclarations($new_set); + } +} From b2e2479efbf03d3c1d8ecbdfcacc7b9131656ff2 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 7 May 2013 09:20:27 +0100 Subject: [PATCH 101/421] Added a context argument to the compile method. The context is either `file` or `filter`, gives plugins a way to negotiate situations where a file would be written but is not desired; e.g. when using Crush as a filter. --- lib/CssCrush/Core.php | 5 ++--- lib/CssCrush/Process.php | 4 +++- plugins/canvas.php | 5 +++-- plugins/rem.php | 3 --- plugins/svg.php | 5 +++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/CssCrush/Core.php b/lib/CssCrush/Core.php index 40aac7f..651b7cf 100644 --- a/lib/CssCrush/Core.php +++ b/lib/CssCrush/Core.php @@ -328,7 +328,6 @@ static public function file ($file, $options = null) } } - // Compile. $stream = $process->compile(); // Create file and return url. Return empty string on failure. @@ -456,8 +455,8 @@ static public function string ($string, $options = null) $process->input->importIgnore = true; } - // Compile and return. - return $process->compile(); + // Note we're passing the alternative ioContext. + return $process->compile('filter'); } /** diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 44a296a..d1d9e7d 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -1102,8 +1102,10 @@ protected function collate () } } - public function compile () + public function compile ($io_context = 'file') { + $this->ioContext = $io_context; + // Always store start time. $this->stat['compile_start_time'] = microtime(true); diff --git a/plugins/canvas.php b/plugins/canvas.php index 315437b..e5d65e6 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -136,7 +136,8 @@ function csscrush__canvas_generator ($input, $context) { // Apply functions. csscrush__canvas_apply_css_funcs($canvas); -csscrush::log($canvas); +// csscrush::log($canvas); + // Create fingerprint for this canvas based on canvas object. $fingerprint = substr(md5(serialize($canvas)), 0, 7); $generated_filename = "cnv-$name-$fingerprint.png"; @@ -214,7 +215,7 @@ function csscrush__canvas_generator ($input, $context) { // Either write to a file. - if ($context->function === 'canvas') { + if ($context->function === 'canvas' && $process->ioContext === 'file') { if (! $cached_file) { imagepng($canvas->image, $generated_filepath); diff --git a/plugins/rem.php b/plugins/rem.php index ee4da38..9d8ef7d 100644 --- a/plugins/rem.php +++ b/plugins/rem.php @@ -93,9 +93,6 @@ function csscrush__rem (CssCrush_Rule $rule) { ($just_font_props && ! isset($font_props[$declaration->canonicalProperty])) || ! preg_match_all($length_patt, $declaration->value, $m) ) { - // csscrush::log($font_props, $declaration->canonicalProperty); - csscrush::log($m, $declaration->canonicalProperty); - $new_set[] = $declaration; continue; } diff --git a/plugins/svg.php b/plugins/svg.php index 8a905dd..29c5799 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -22,7 +22,8 @@ * fill-opacity: .5; * } * - * // Embed SVG with svg() function (generates a data URI). + * // Embed SVG with svg() function (generates an svg file in the + * // output directory). * body { * background: beige svg(foo); * } @@ -250,7 +251,7 @@ function csscrush__svg_generator ($input, $fn_name) { // echo $test; // Either write to a file. - if ($fn_name === 'svg') { + if ($fn_name === 'svg' && $process->ioContext === 'file') { $flattened_svg = implode("\n", $svg); From a60f7610ece0d390c9737359273423af268a7c0c Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 10 May 2013 11:00:59 +0100 Subject: [PATCH 102/421] Fixed issue with fallback mixin/template arguments ignoring function calls. More work on canvas plugin. --- lib/CssCrush/Template.php | 8 +++-- plugins/canvas.php | 73 +++++++++++++++++++++++++++++++-------- 2 files changed, 63 insertions(+), 18 deletions(-) diff --git a/lib/CssCrush/Template.php b/lib/CssCrush/Template.php index 54235a9..6a60b4e 100644 --- a/lib/CssCrush/Template.php +++ b/lib/CssCrush/Template.php @@ -83,9 +83,11 @@ public function getArgValue ($index, &$args) $default = isset($this->defaults[$index]) ? $this->defaults[$index] : ''; // Recurse for nested arg() calls. - if (preg_match(CssCrush_Regex::$patt->a_token, $default, $m)) { - - $default = $this->getArgValue((int) $m[1], $args); + while (preg_match(CssCrush_Regex::$patt->a_token, $default, $m)) { + $default = str_replace( + $m[0], + $this->getArgValue((int) $m[1], $args), + $default); } return $default; diff --git a/plugins/canvas.php b/plugins/canvas.php index e5d65e6..cb1d2d1 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -136,7 +136,7 @@ function csscrush__canvas_generator ($input, $context) { // Apply functions. csscrush__canvas_apply_css_funcs($canvas); -// csscrush::log($canvas); + // csscrush::log($canvas); // Create fingerprint for this canvas based on canvas object. $fingerprint = substr(md5(serialize($canvas)), 0, 7); @@ -144,7 +144,7 @@ function csscrush__canvas_generator ($input, $context) { $generated_filepath = $process->output->dir . '/' . $generated_filename; $cached_file = file_exists($generated_filepath); - $cached_file = false; + // $cached_file = false; if (! $cached_file) { // Source arguments take priority. @@ -177,7 +177,7 @@ function csscrush__canvas_generator ($input, $context) { csscrush__canvas_fill($canvas, 'background-fill'); // Filters. - csscrush__canvas_apply_filters($canvas, $src->image); + csscrush__canvas_apply_filters($canvas, $src); // Place the src image on the base canvas image. imagecopyresized( @@ -299,7 +299,7 @@ function csscrush__canvas_fn_filter ($input, $context) { } -function csscrush__canvas_apply_filters ($canvas, $image) { +function csscrush__canvas_apply_filters ($canvas, $src) { foreach ($canvas->filters as $filter) { list($name, $args) = $filter; @@ -307,11 +307,24 @@ function csscrush__canvas_apply_filters ($canvas, $image) { switch ($name) { case 'greyscale': case 'grayscale': - imagefilter($image, IMG_FILTER_GRAYSCALE); + imagefilter($src->image, IMG_FILTER_GRAYSCALE); break; + case 'invert': - imagefilter($image, IMG_FILTER_NEGATE); + imagefilter($src->image, IMG_FILTER_NEGATE); + break; + + case 'opacity': + csscrush__canvas_fade($src, floatval($args[0])); + break; + + case 'colorize': + $args += array(0,0,0); + array_unshift($args, IMG_FILTER_COLORIZE); + array_unshift($args, $src->image); + call_user_func_array('imagefilter', $args); break; + case 'blur': $level = 1; if (isset($args[0])) { @@ -320,21 +333,23 @@ function csscrush__canvas_apply_filters ($canvas, $image) { $level = min(max(intval($args[0]), 1), 20); } while ($level--) { - imagefilter($image, IMG_FILTER_GAUSSIAN_BLUR); + imagefilter($src->image, IMG_FILTER_GAUSSIAN_BLUR); } break; + case 'contrast': if (isset($args[0])) { $level = intval($args[0]); } - imagefilter($image, IMG_FILTER_CONTRAST, $level); + imagefilter($src->image, IMG_FILTER_CONTRAST, $level); break; + case 'brightness': // -255 <- 0 -> +255 if (isset($args[0])) { $level = intval($args[0]); } - imagefilter($image, IMG_FILTER_BRIGHTNESS, $level); + imagefilter($src->image, IMG_FILTER_BRIGHTNESS, $level); break; } } @@ -356,6 +371,8 @@ function csscrush__canvas_apply_css_funcs ($canvas) { $filter_functions = array( 'contrast' => 'csscrush__canvas_fn_filter', + 'opacity' => 'csscrush__canvas_fn_filter', + 'colorize' => 'csscrush__canvas_fn_filter', 'grayscale' => 'csscrush__canvas_fn_filter', 'greyscale' => 'csscrush__canvas_fn_filter', 'brightness' => 'csscrush__canvas_fn_filter', @@ -551,16 +568,42 @@ function csscrush__canvas_create ($canvas) { $height = $canvas->height + $margin->top + $margin->bottom; // Create image object. - $canvas->image = imagecreatetruecolor($width, $height); + $canvas->image = csscrush__canvas_create_transparent($width, $height); +} + +function csscrush__canvas_create_transparent ($width, $height) { + + $image = imagecreatetruecolor($width, $height); // Set transparent canvas background. - imagealphablending($canvas->image, false); - $fill = imagecolorallocatealpha($canvas->image, 0, 0, 0, 127); - imagefill($canvas->image, 0, 0, $fill); + imagealphablending($image, false); + imagesavealpha($image, true); + imagefill($image, 0, 0, imagecolorallocatealpha($image, 0, 0, 0, 127)); - imagesavealpha($canvas->image, true); + return $image; } +function csscrush__canvas_fade ($src, $opacity) { + + $width = imagesx($src->image); + $height = imagesy($src->image); + $new_image = csscrush__canvas_create_transparent($width, $height); + $opacity = csscrush__canvas_opacity($opacity); + + // Perform pixel-based alpha map application + for ($x = 0; $x < $width; $x++) { + for ($y = 0; $y < $height; $y++) { + $colors = imagecolorsforindex($src->image, imagecolorat($src->image, $x, $y)); + imagesetpixel($new_image, $x, $y, imagecolorallocatealpha( + $new_image, $colors['red'], $colors['green'], $colors['blue'], $opacity)); + } + } + + imagedestroy($src->image); + $src->image = $new_image; +} + + function csscrush__canvas_fill ($canvas, $property) { if (! isset($canvas->fills[$property])) { @@ -603,7 +646,7 @@ function csscrush__canvas_set_fill_dims ($fill, $canvas) { $fill->x2 = $canvas->width + $margin->right + $margin->left; $fill->y2 = $canvas->height + $margin->top + $margin->bottom; - if ($canvas->currentProperty === 'fill') { + if (isset($canvas->currentProperty) && $canvas->currentProperty === 'fill') { $fill->x1 = $margin->left; $fill->y1 = $margin->top; $fill->x2 = $canvas->width + $fill->x1 - 1; From e9c6b779887352272d331ce32647df8a1bb38237 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 16 May 2013 10:01:58 +0100 Subject: [PATCH 103/421] Added CSS color value parsing to the colorize() canvas filter. Flipped the polarity of the contrast() canvas filter to be more predictable. --- plugins/canvas.php | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/plugins/canvas.php b/plugins/canvas.php index cb1d2d1..d409d10 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -319,10 +319,15 @@ function csscrush__canvas_apply_filters ($canvas, $src) { break; case 'colorize': - $args += array(0,0,0); - array_unshift($args, IMG_FILTER_COLORIZE); - array_unshift($args, $src->image); - call_user_func_array('imagefilter', $args); + $rgb = $args + array('black'); + if (count($rgb) === 1) { + // If only one argument parse it as a CSS color value. + $rgb = CssCrush_Color::parse($rgb[0]); + if (! $rgb) { + $rgb = array(0,0,0); + } + } + imagefilter($src->image, IMG_FILTER_COLORIZE, $rgb[0], $rgb[1], $rgb[2]); break; case 'blur': @@ -339,14 +344,18 @@ function csscrush__canvas_apply_filters ($canvas, $src) { case 'contrast': if (isset($args[0])) { - $level = intval($args[0]); + // By default it works like this: + // (max) -100 <- 0 -> +100 (min) + // But we're flipping the polarity to be more predictable: + // (min) -100 <- 0 -> +100 (max) + $level = intval($args[0]) * -1; } imagefilter($src->image, IMG_FILTER_CONTRAST, $level); break; case 'brightness': - // -255 <- 0 -> +255 if (isset($args[0])) { + // -255 <- 0 -> +255 $level = intval($args[0]); } imagefilter($src->image, IMG_FILTER_BRIGHTNESS, $level); From e4ccf9674cd71b6d279fe36d2c1e28d2edd753b2 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 16 May 2013 19:37:08 +0100 Subject: [PATCH 104/421] Added some docs for the new plugins. Updated changelog. --- CHANGELOG.md | 12 +++++++++--- plugins/canvas.php | 11 ++++++++++- plugins/svg.php | 41 +++++++++++++++++++++++++++++++---------- 3 files changed, 50 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 712c6f7..08f9c2e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,12 @@ -1.10 (?) +1.10 (18th May 2013) ---- -* Added SVG plugin (define and embed SVG elements in CSS). -* Added `@name` in-rule directive for more flexible and robust rule referencing. +* Added SVG plugin for defining and generating SVG files/data URIs in CSS. +* Added Canvas plugin for image generation and manipulation (requires GD extension). +* Added rem and px2em plugins. +* Added ease plugin for expanded easing keywords. +* Command line utility now has a `--watch` option for automatic compiling when a file is updated. +* `vendor_target` option now accepts an array of targets. +* Added `@name` in-rule directive for more robust rule referencing. * Added grouping for function aliases so multiple related functions (e.g. gradients) can now be applied to one value. * Rule references previously looked for the closest previous match. This behaviour has been changed @@ -11,6 +16,7 @@ * Removed data-* properties. * Nested rules that use the parent symbol (&) can now work in conjunction with the rooting symbol (^). * Fixed issue with empty imported files not registering. +* Various bug fixes. 1.9.1 (31th January 2013) ----- diff --git a/plugins/canvas.php b/plugins/canvas.php index d409d10..f50dacc 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -6,7 +6,7 @@ * * @example * - * /* Red semi-transparent square. *\/ + * // Red semi-transparent square. * @canvas foo { * width: 50; * height: 50; @@ -37,6 +37,15 @@ * .bar { * background: canvas-data(foo) repeat-x; * } + * + * @example + * + * // Google logo resized to 400px width and given a sepia effect. + * @canvas foo { + * src: url("https://www.google.co.uk/images/srpr/logo4w.png"); + * width: 400; + * canvas-filter: greyscale() colorize(45, 45, 0); + * } */ CssCrush_Plugin::register('canvas', array( diff --git a/plugins/svg.php b/plugins/svg.php index 29c5799..96e2a2d 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -2,13 +2,6 @@ /** * Define and embed simple SVG elements, paths and effects inside CSS * - * - * - * Element types - * ------------- - * circle, ellipse, rect, polygon, path, line, polyline, star. - * - * * @example * * // Define SVG. @@ -27,11 +20,39 @@ * body { * background: beige svg(foo); * } + * // As above but creates a data URI instead of an svg file. + * body { + * background: beige svg-data(foo); + * } + * + * @example + * + * // Skewed circle with radial gradient fill and drop shadow. + * @svg circle { + * type: circle; + * transform: skewX(30); + * diameter: 60; + * margin: 20; + * fill: svg-radial-gradient(at top right, gold 50%, red); + * drop-shadow: 2 2 0 rgba(0,0,0,1); + * } + * + * @example + * + * // 8-sided polygon with an image fill. + * // Note: images usually have to be converted to data URIs, see known issues below. + * @svg pattern { + * type: polygon; + * sides: 8; + * diameter: 180; + * margin: 20; + * fill: pattern(data-uri(kitten.jpg), scale(1) translate(-100 0)); + * fill-opacity: .8; + * } * + * @known-issues * - * @issues - * ------ - * Firefox does not allow linked images (or other svg) when SVG is in "svg as image" mode - + * Firefox does not allow linked images (or other svg) when svg is in "svg as image" mode - * i.e. Used in an img tag or as a CSS background: * https://bugzilla.mozilla.org/show_bug.cgi?id=628747#c0 * From b9fd28258a45b9932fa790edf1d6b6431f280ab8 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 18 May 2013 21:05:20 +0100 Subject: [PATCH 105/421] Suppressing colour output for terminals that don't support it --- cli.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/cli.php b/cli.php index c7099b3..ba3be7e 100755 --- a/cli.php +++ b/cli.php @@ -398,6 +398,7 @@ function pick (array &$arr) { function colorize ($str) { + static $color_support; static $tags = array( '' => "\033[0;30m", '' => "\033[0;31m", @@ -420,8 +421,15 @@ function colorize ($str) { '' => "\033[m", ); + if (! isset($color_support)) { + $color_support = true; + if (DIRECTORY_SEPARATOR == '\\') { + $color_support = false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI'); + } + } + $find = array_keys($tags); - $replace = array_values($tags); + $replace = $color_support ? array_values($tags) : ''; return str_replace($find, $replace, $str); } From 2d1c9d2bf6939273c0cbdada8871eea5e740a358 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 28 May 2013 13:49:52 +0100 Subject: [PATCH 106/421] Removed unnecessary file un-linking prior to recompilation. --- lib/CssCrush/IO.php | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/lib/CssCrush/IO.php b/lib/CssCrush/IO.php index a770613..612ba25 100644 --- a/lib/CssCrush/IO.php +++ b/lib/CssCrush/IO.php @@ -108,8 +108,8 @@ static public function validateExistingOutput () } else { // File has been moved, remove old file and skip to compile. - CssCrush::log('Import file has been moved, removing existing file.'); - unlink($existingfile->path); + CssCrush::log('Recompiling - an import file has been moved.'); + return false; } } @@ -141,22 +141,16 @@ static public function validateExistingOutput () else { if ($options_changed) { - CssCrush::log('Options have been modified.'); + CssCrush::log('Recompiling - options have been modified.'); } if ($files_changed) { - CssCrush::log('Files have been modified.'); + CssCrush::log('Recompiling - files have been modified.'); } - CssCrush::log('Removing existing file.'); - - // Remove old file and continue making a new one... - unlink($existingfile->path); } } else if (file_exists($existingfile->path)) { - // File exists but has no config. - CssCrush::log('File exists but no config, removing existing file.'); - unlink($existingfile->path); + CssCrush::log('Recompiling - file exists but no cache data.'); } return false; From be8168eb4030dc548656d420b346d3f2a80a3564 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 3 Jun 2013 20:11:03 +0100 Subject: [PATCH 107/421] Wrapping path based options in realpath. Fixing some casing. --- lib/CssCrush/Core.php | 2 +- lib/CssCrush/Options.php | 3 ++- lib/CssCrush/Process.php | 6 +++--- lib/CssCrush/Regex.php | 4 ++-- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/CssCrush/Core.php b/lib/CssCrush/Core.php index 651b7cf..7c09f35 100644 --- a/lib/CssCrush/Core.php +++ b/lib/CssCrush/Core.php @@ -6,7 +6,7 @@ */ class CssCrush { - const VERSION = '1.10'; + const VERSION = '1.10.1'; // Global settings. static public $config; diff --git a/lib/CssCrush/Options.php b/lib/CssCrush/Options.php index 4d37d55..b6059f5 100644 --- a/lib/CssCrush/Options.php +++ b/lib/CssCrush/Options.php @@ -49,9 +49,10 @@ public function __set ($name, $value) // Sanitize path options. case 'context': + case 'output_dir': case 'doc_root': if (is_string($value)) { - $value = CssCrush_Util::normalizePath($value); + $value = CssCrush_Util::normalizePath(realpath($value)); } break; diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index d1d9e7d..6fbb389 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -40,9 +40,9 @@ public function __construct ($options) 't' => array(), // Traces ); $this->variables = array(); - $this->misc = new stdclass(); - $this->input = new stdclass(); - $this->output = new stdclass(); + $this->misc = new stdClass(); + $this->input = new stdClass(); + $this->output = new stdClass(); // Copy config values. $this->plugins = $config->plugins; diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php index e83356d..9675c7b 100644 --- a/lib/CssCrush/Regex.php +++ b/lib/CssCrush/Regex.php @@ -16,8 +16,8 @@ class CssCrush_Regex static public function init () { - self::$patt = $patt = new stdclass(); - self::$classes = $classes = new stdclass(); + self::$patt = $patt = new stdClass(); + self::$classes = $classes = new stdClass(); // CSS type classes. $classes->ident = '[a-zA-Z0-9_-]+'; From 4d0f2d00b3d6eebb37d37cf4e37ff46950676f28 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 4 Jun 2013 12:40:59 +0100 Subject: [PATCH 108/421] Moving contents of Prepend.css to misc/ folder. Deprecating and removing Prepend.css. Removing Plugins.ini, it`s purpose is better served by using the `csscrush_set()` function to define configuration defaults. --- CHANGELOG.md | 102 ++++++++++++------------ Plugins.ini | 13 --- README.md | 15 ++-- lib/CssCrush/Core.php | 23 ------ lib/CssCrush/Importer.php | 20 +---- Prepend.css => misc/css/font-stacks.css | 15 ++-- 6 files changed, 64 insertions(+), 124 deletions(-) delete mode 100644 Plugins.ini rename Prepend.css => misc/css/font-stacks.css (79%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08f9c2e..3825990 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,5 @@ -1.10 (18th May 2013) ----- +### 1.10 (2013-5-18) + * Added SVG plugin for defining and generating SVG files/data URIs in CSS. * Added Canvas plugin for image generation and manipulation (requires GD extension). * Added rem and px2em plugins. @@ -18,16 +18,16 @@ * Fixed issue with empty imported files not registering. * Various bug fixes. -1.9.1 (31th January 2013) ------ +### 1.9.1 (2013-1-31) + * Added noise plugin (noise/texture generating functions). * Resolved issues #42 and #43. * Fixed command line context option. * Fixed error notice with no enabled plugins in Plugins.ini file. * Updated aliases file. -1.9 (12th January 2013) ---- +### 1.9 (2013-1-12) + * Added flexbox aliases for both 2009 and 2012 edition specs. * Added a legacy-flexbox plugin for auto-generating the flexbox 2009 spec equivilant properties. * Updated selector aliases to take arguments at runtime. @@ -50,8 +50,8 @@ * Property/value aliases expanded and renamed as declaration aliases. * Classes now loaded via an autoloader, also some other refactoring for moving towards PSR-0 compliance. -1.8 (13th November 2012) ---- +### 1.8 (2012-11-13) + * Added selector aliasing with the `@selector-alias` directive. * Added `output_dir` option for specifying the destination of compiled files. * Added `doc_root` option for working around problems with server aliases or path rewrites. @@ -69,8 +69,8 @@ * Improved minification. * Major refactoring. -1.7 (28th September 2012) ---- +### 1.7 (2012-9-28) + * Added `trace` option to output SASS compatible debug-info stubs for use with tools like FireSass. * Added `@ifdefine` directive for dynamically including/excluding parts of a CSS file based on the existence of variables. @@ -84,12 +84,12 @@ * Updated aliases and initial value files. * Fixed parsing issue introduced in 1.6.1. -1.6.1 (22nd August 2012) ------ +### 1.6.1 (2012-8-22) + * Resolved issues #34 and #35. -1.6 (1st August 2012) ---- +### 1.6 (2012-8-1) + * Inheritance model improved to support adoption of pseudo classes and elements (see wiki). * Added rule self-referencing function `this()` and complimentary data-* properties. * Added rule referencing function `query()`. @@ -100,24 +100,24 @@ * Double-colon plugin moved to core. * Option `rewrite_import_urls` now defaults to true. -1.5.3 (13th June 2012) ------ +### 1.5.3 (2012-6-13) + * Refactoring. * Fixed some test cases. -1.5.2 (8th June 2012) ------ +### 1.5.2 (2012-6-8) + * Resolved issue #32. * `CssCrush::inline` method now defaults to not printing a boilerplate. * Updated aliases file. -1.5.1 (1st June 2012) ------ +### 1.5.1 (2012-6-1) + * Extended mixins to work with abstract rules and regular rules. * Fixed issue with selector grouping and inheritance in combination. -1.5 (21st May 2012) ---- +### 1.5 (2012-5-21) + * New feature: Rule inheritance / abstract rules. * New feature: Block nesting. * New feature: Mixins. @@ -130,13 +130,13 @@ * Internal refactoring. * Resolved issues #23, #24, #27, #28 and #29. -1.4.2 (14th March 2012) ------ +### 1.4.2 (2012-3-14) + * Fixed bug with @import statement parsing. * Some minor under the hood changes. -1.4.1 (10th February 2012) ------ +### 1.4.1 (2012-2-10) + * Added command line application. * Added `rewrite_import_urls` option - Ability to rewrite relative url references inside imported css files. * Added Prepend.css - Optionally prepend css to every input. @@ -145,8 +145,8 @@ * Initial-values updated. * Updated `CssCrush::string` method to correctly handle import statements. -1.4 (24th January 2012) ---- +### 1.4 (2012-1-24) + * Added initial-keyword plugin (shim for the CSS3 keyword). * Added inline method (Issue #18). * Added ability to escape declarations from aliasing or plugins by prefixing with tilde. @@ -157,15 +157,15 @@ * Some internal cleaning up. * Disabled IE6 min-height plugin by default. -1.3.6 (9th November 2011) ------ +### 1.3.6 (2011-11-9) + * Improved color functions. * Added `a-adjust()` function for altering a color's opacity. * Deprecated hsl-adjust function (you can use nested color functions instead). * Added the ability to use local versions of alias and plugin files so pull updates don't clobber local settings. -1.3.5 (8th November 2011) ------ +### 1.3.5 (2011-11-8) + * Added hook system for plugins. * Plugins split into seperate files. * Aliases and Plugins files renamed with '.ini' file extensions to be editor friendly. @@ -173,41 +173,41 @@ * Updated filter plugin. * Fixed nested custom function parsing (issue #14). -1.3.4 (29th October 2011) ------ +### 1.3.4 (2011-10-29) + * Added output_filename option. * Added vendor_target option. * Renamed 'macros' to the more general 'plugins' and split them into their own files. * Removed superfluous outer containing directory (update your include paths). -1.3.3 (28th October 2011) ------ +### 1.3.3 (2011-10-28) + * Fixed regression with absolute URL file imports (issue #12). * Fixed minification bug (issue #13). -1.3.2 (18th October 2011) ------ +### 1.3.2 (2011-10-18) + * Updated variable syntax. * Fixed minification bug. -1.3.1 (9th October 2011) ------ +### 1.3.1 (2011-10-9) + * Added support for svg and svgz data uris. * Added animation shorthand alias. * Added user-select alias. -1.3 (20th September 2011) ---- +### 1.3 (2011-10-20) + * Added the public function `CssCrush::string` for processing raw strings of CSS. * Added color functions. * Added aliases for IE10. -1.2 (8th September 2011) ---- -* Rewritten the file importer. +### 1.2 (2011-9-8) + +* File importer rewritten. + +### 1.1 (2011-9-2) -1.1 (2nd September 2011) ---- * Added support for global variables. * Added support for variable interpolation within string literals. * Added `CssCrush::tag` method for outputting an html link tag instead of returning a filename. @@ -218,8 +218,8 @@ * Minor correction to WAMP support. * Minor fix to rule API. -1.0 (14th July 2011) ---- +### 1.0 (2011-7-14) + * Major refactoring. * Custom functions. * Optional boilerplate. @@ -227,6 +227,6 @@ * Resolved document root issues. * Minification improvements. -0.9 (20th September 2010) ---- +### 0.9 (2010-9-20) + * Initial release. diff --git a/Plugins.ini b/Plugins.ini deleted file mode 100644 index ffd455e..0000000 --- a/Plugins.ini +++ /dev/null @@ -1,13 +0,0 @@ -;---------------------------------------------------------------- -; -; This file is for listing plugins you want loaded and enabled by default. -; -; Plugins can also be enabled and disabled at runtime with "disable" and -; "enable" options. -; -; See: https://github.com/peteboere/css-crush/wiki/Plugins -; -;---------------------------------------------------------------- - -; For example... -; plugins[] = svg diff --git a/README.md b/README.md index 0105016..63b417d 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,14 @@ CSS-Crush -=================================== +--------- -CSS-Crush is an extensible PHP based CSS preprocessor that aims to alleviate many of the hacks and workarounds necessary in modern CSS development. - - -Overview -=================================== +**CSS without the mess** — CSS-Crush is a CSS preprocessor designed to create a modern, uncluttered and standards based CSS authoring environment. +See the following overview for code examples and description of main features: http://the-echoplex.net/csscrush Quick start -=================================== +----------- ```php plugins[$plugin_name] = true; - } - } - } - } - else { - trigger_error(__METHOD__ . ": Plugin file could not be parsed.\n", E_USER_NOTICE); - } - } - $called = true; } diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index 3f60098..69e6307 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -14,25 +14,12 @@ static public function hostfile () $regex = CssCrush_Regex::$patt; $input = $process->input; + $str = ''; + // Keep track of all import file info for cache data. $mtimes = array(); $filenames = array(); - $str = ''; - $prepend_file_contents = ''; - - // The prepend file. - if ($prepend_file = CssCrush_Util::find('Prepend-local.css', 'Prepend.css')) { - - $prepend_file_contents = file_get_contents($prepend_file); - $process->currentFile = 'file://' . $prepend_file; - - // If there's a parsing error inside the prepend file, wipe $prepend_file_contents. - if (! self::prepareForStream($prepend_file_contents)) { - $prepend_file_contents = ''; - } - } - // Resolve main input; a string of css or a file. if (isset($input->string)) { $str .= $input->string; @@ -49,9 +36,6 @@ static public function hostfile () return $str; } - // Prepend any prepend file contents here. - $str = $prepend_file_contents . $str; - // This may be set non-zero during the script if an absolute @import URL is encountered. $search_offset = 0; diff --git a/Prepend.css b/misc/css/font-stacks.css similarity index 79% rename from Prepend.css rename to misc/css/font-stacks.css index 45a6775..1688bf4 100644 --- a/Prepend.css +++ b/misc/css/font-stacks.css @@ -1,19 +1,14 @@ /*$ -Prepend.css contains library variables by default, but it could also contain -reset styles such as reset.css or normalize.css that you would want prepended -to every output file. +Font stacks. + +Sources: + http://cssfontstack.com + http://www.codestyle.org */ @define { - /* Font stacks - - Sources: - http://cssfontstack.com - http://www.codestyle.org - ---------------------------------------- */ - /* Serif */ baskerville: Baskerville, "Baskerville Old Face", "Hoefler Text", Garamond, "Times New Roman", serif; georgia: Georgia, Times, "Times New Roman", serif; From 0ab959e6e66948ae9a5b2440a01de2abba4787a7 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 4 Jun 2013 13:12:34 +0100 Subject: [PATCH 109/421] Updated readme. Made changelog headings more discreet. --- CHANGELOG.md | 102 +++++++++++++++++++++++++-------------------------- README.md | 16 +++----- 2 files changed, 57 insertions(+), 61 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08f9c2e..3825990 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,5 @@ -1.10 (18th May 2013) ----- +### 1.10 (2013-5-18) + * Added SVG plugin for defining and generating SVG files/data URIs in CSS. * Added Canvas plugin for image generation and manipulation (requires GD extension). * Added rem and px2em plugins. @@ -18,16 +18,16 @@ * Fixed issue with empty imported files not registering. * Various bug fixes. -1.9.1 (31th January 2013) ------ +### 1.9.1 (2013-1-31) + * Added noise plugin (noise/texture generating functions). * Resolved issues #42 and #43. * Fixed command line context option. * Fixed error notice with no enabled plugins in Plugins.ini file. * Updated aliases file. -1.9 (12th January 2013) ---- +### 1.9 (2013-1-12) + * Added flexbox aliases for both 2009 and 2012 edition specs. * Added a legacy-flexbox plugin for auto-generating the flexbox 2009 spec equivilant properties. * Updated selector aliases to take arguments at runtime. @@ -50,8 +50,8 @@ * Property/value aliases expanded and renamed as declaration aliases. * Classes now loaded via an autoloader, also some other refactoring for moving towards PSR-0 compliance. -1.8 (13th November 2012) ---- +### 1.8 (2012-11-13) + * Added selector aliasing with the `@selector-alias` directive. * Added `output_dir` option for specifying the destination of compiled files. * Added `doc_root` option for working around problems with server aliases or path rewrites. @@ -69,8 +69,8 @@ * Improved minification. * Major refactoring. -1.7 (28th September 2012) ---- +### 1.7 (2012-9-28) + * Added `trace` option to output SASS compatible debug-info stubs for use with tools like FireSass. * Added `@ifdefine` directive for dynamically including/excluding parts of a CSS file based on the existence of variables. @@ -84,12 +84,12 @@ * Updated aliases and initial value files. * Fixed parsing issue introduced in 1.6.1. -1.6.1 (22nd August 2012) ------ +### 1.6.1 (2012-8-22) + * Resolved issues #34 and #35. -1.6 (1st August 2012) ---- +### 1.6 (2012-8-1) + * Inheritance model improved to support adoption of pseudo classes and elements (see wiki). * Added rule self-referencing function `this()` and complimentary data-* properties. * Added rule referencing function `query()`. @@ -100,24 +100,24 @@ * Double-colon plugin moved to core. * Option `rewrite_import_urls` now defaults to true. -1.5.3 (13th June 2012) ------ +### 1.5.3 (2012-6-13) + * Refactoring. * Fixed some test cases. -1.5.2 (8th June 2012) ------ +### 1.5.2 (2012-6-8) + * Resolved issue #32. * `CssCrush::inline` method now defaults to not printing a boilerplate. * Updated aliases file. -1.5.1 (1st June 2012) ------ +### 1.5.1 (2012-6-1) + * Extended mixins to work with abstract rules and regular rules. * Fixed issue with selector grouping and inheritance in combination. -1.5 (21st May 2012) ---- +### 1.5 (2012-5-21) + * New feature: Rule inheritance / abstract rules. * New feature: Block nesting. * New feature: Mixins. @@ -130,13 +130,13 @@ * Internal refactoring. * Resolved issues #23, #24, #27, #28 and #29. -1.4.2 (14th March 2012) ------ +### 1.4.2 (2012-3-14) + * Fixed bug with @import statement parsing. * Some minor under the hood changes. -1.4.1 (10th February 2012) ------ +### 1.4.1 (2012-2-10) + * Added command line application. * Added `rewrite_import_urls` option - Ability to rewrite relative url references inside imported css files. * Added Prepend.css - Optionally prepend css to every input. @@ -145,8 +145,8 @@ * Initial-values updated. * Updated `CssCrush::string` method to correctly handle import statements. -1.4 (24th January 2012) ---- +### 1.4 (2012-1-24) + * Added initial-keyword plugin (shim for the CSS3 keyword). * Added inline method (Issue #18). * Added ability to escape declarations from aliasing or plugins by prefixing with tilde. @@ -157,15 +157,15 @@ * Some internal cleaning up. * Disabled IE6 min-height plugin by default. -1.3.6 (9th November 2011) ------ +### 1.3.6 (2011-11-9) + * Improved color functions. * Added `a-adjust()` function for altering a color's opacity. * Deprecated hsl-adjust function (you can use nested color functions instead). * Added the ability to use local versions of alias and plugin files so pull updates don't clobber local settings. -1.3.5 (8th November 2011) ------ +### 1.3.5 (2011-11-8) + * Added hook system for plugins. * Plugins split into seperate files. * Aliases and Plugins files renamed with '.ini' file extensions to be editor friendly. @@ -173,41 +173,41 @@ * Updated filter plugin. * Fixed nested custom function parsing (issue #14). -1.3.4 (29th October 2011) ------ +### 1.3.4 (2011-10-29) + * Added output_filename option. * Added vendor_target option. * Renamed 'macros' to the more general 'plugins' and split them into their own files. * Removed superfluous outer containing directory (update your include paths). -1.3.3 (28th October 2011) ------ +### 1.3.3 (2011-10-28) + * Fixed regression with absolute URL file imports (issue #12). * Fixed minification bug (issue #13). -1.3.2 (18th October 2011) ------ +### 1.3.2 (2011-10-18) + * Updated variable syntax. * Fixed minification bug. -1.3.1 (9th October 2011) ------ +### 1.3.1 (2011-10-9) + * Added support for svg and svgz data uris. * Added animation shorthand alias. * Added user-select alias. -1.3 (20th September 2011) ---- +### 1.3 (2011-10-20) + * Added the public function `CssCrush::string` for processing raw strings of CSS. * Added color functions. * Added aliases for IE10. -1.2 (8th September 2011) ---- -* Rewritten the file importer. +### 1.2 (2011-9-8) + +* File importer rewritten. + +### 1.1 (2011-9-2) -1.1 (2nd September 2011) ---- * Added support for global variables. * Added support for variable interpolation within string literals. * Added `CssCrush::tag` method for outputting an html link tag instead of returning a filename. @@ -218,8 +218,8 @@ * Minor correction to WAMP support. * Minor fix to rule API. -1.0 (14th July 2011) ---- +### 1.0 (2011-7-14) + * Major refactoring. * Custom functions. * Optional boilerplate. @@ -227,6 +227,6 @@ * Resolved document root issues. * Minification improvements. -0.9 (20th September 2010) ---- +### 0.9 (2010-9-20) + * Initial release. diff --git a/README.md b/README.md index 0105016..0d223df 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,13 @@ -CSS-Crush -=================================== +CSS-Crush -CSS-Crush is an extensible PHP based CSS preprocessor that aims to alleviate many of the hacks and workarounds necessary in modern CSS development. - - -Overview -=================================== +**CSS without the mess** — CSS-Crush is a CSS preprocessor designed to create a modern, uncluttered and standards based CSS authoring environment. +See the following overview for code examples and description of main features: http://the-echoplex.net/csscrush Quick start -=================================== +----------- ```php Date: Wed, 5 Jun 2013 19:58:19 +0100 Subject: [PATCH 110/421] Moving Util::parseBlock method to Rule::parseBlock and giving it options to parse or ignore rule directives. Some refactoring for the updated method. --- lib/CssCrush/Core.php | 2 +- lib/CssCrush/Mixin.php | 2 +- lib/CssCrush/Process.php | 4 +- lib/CssCrush/Rule.php | 132 ++++++++++++++++++++++----------------- lib/CssCrush/Util.php | 32 ++-------- plugins/canvas.php | 2 +- plugins/svg.php | 2 +- 7 files changed, 87 insertions(+), 89 deletions(-) diff --git a/lib/CssCrush/Core.php b/lib/CssCrush/Core.php index 69e74a1..39e5228 100644 --- a/lib/CssCrush/Core.php +++ b/lib/CssCrush/Core.php @@ -6,7 +6,7 @@ */ class CssCrush { - const VERSION = '1.10.1'; + const VERSION = '1.11'; // Global settings. static public $config; diff --git a/lib/CssCrush/Mixin.php b/lib/CssCrush/Mixin.php index 5aab9df..92e82d0 100644 --- a/lib/CssCrush/Mixin.php +++ b/lib/CssCrush/Mixin.php @@ -15,7 +15,7 @@ public function __construct ($block) $this->template = new CssCrush_Template($block); // Parse into mixin template. - foreach (CssCrush_Util::parseBlock($this->template->string) as $pair) { + foreach (CssCrush_Rule::parseBlock($this->template->string) as $pair) { list($property, $value) = $pair; $property = strtolower($property); diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 6fbb389..5a7397f 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -620,7 +620,9 @@ static public function cb_extractVariables ($m) $block = trim(CssCrush_Util::stripCommentTokens($m[1])); CssCrush::$process->variables = - array_merge(CssCrush::$process->variables, CssCrush_Util::parseBlock($block, true)); + array_merge( + CssCrush::$process->variables, + CssCrush_Rule::parseBlock($block, array('keyed' => true, 'ignore_directives' => false))); } static protected function cb_placeVariables ($m) diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php index 767e22c..abedca5 100644 --- a/lib/CssCrush/Rule.php +++ b/lib/CssCrush/Rule.php @@ -81,72 +81,24 @@ public function __construct ($selector_string = null, $declarations_string) } } - // Parse the declarations chunk. - $declarations_string = trim(CssCrush_Util::stripCommentTokens($declarations_string)); - $declarations = preg_split('~\s*;\s*~', $declarations_string, null, PREG_SPLIT_NO_EMPTY); + // Parse rule block. + $pairs = CssCrush_Rule::parseBlock($declarations_string); - // First create a simple array of all properties and value pairs in raw state - $pairs = array(); + foreach ($pairs as $index => $pair) { - // Split declarations in to property/value pairs - foreach ($declarations as $declaration) { - - // Rule directives. Accept several different syntaxes for mixin and extends. - if (preg_match($regex->ruleDirective, $declaration, $m)) { - - if (! empty($m[1])) { - $prop = 'mixin'; - } - elseif (! empty($m[2])) { - $prop = 'extends'; - } - else { - $prop = 'name'; - } - $value = trim(substr($declaration, strlen($m[0]))); - } - elseif (($colonPos = strpos($declaration, ':')) !== false) { - - $prop = trim(substr($declaration, 0, $colonPos)); - - // Extract the value part of the declaration. - $value = trim(substr($declaration, $colonPos + 1)); - } - else { - // Must be malformed. - continue; - } - - // Reject empty values. - if (empty($prop) || ! strlen($value)) { - continue; - } - - if ($prop === 'mixin') { - - // Mixins are a special case. - if ($mixin_declarations = CssCrush_Mixin::parseValue($value)) { - - // Add mixin declarations to the stack. - while ($mixin_declaration = array_shift($mixin_declarations)) { + list($prop, $value) = $pair; - $pairs[] = array($mixin_declaration['property'], $mixin_declaration['value']); - } - } - } - elseif ($prop === 'extends') { + if ($prop === 'extends') { // Extends are also a special case. $this->setExtendSelectors($value); + unset($pairs[$index]); } elseif ($prop === 'name') { // Link the rule as a reference. $process->references[$value] = $this; - } - else { - - $pairs[] = array($prop, $value); + unset($pairs[$index]); } } @@ -159,7 +111,7 @@ public function __construct ($selector_string = null, $declarations_string) // Only store to $this->selfData if the value does not itself make a // this() call to avoid circular references. - if (! preg_match(CssCrush_Regex::$patt->thisFunction, $value)) { + if (! preg_match($regex->thisFunction, $value)) { $this->selfData[strtolower($prop)] = $value; } @@ -836,4 +788,72 @@ static public function get ($token) } return null; } + + static public function parseBlock ($str, $options = array()) + { + $regex = CssCrush_Regex::$patt; + $str = CssCrush_Util::stripCommentTokens($str); + + $lines = preg_split('~\s*;\s*~', $str, null, PREG_SPLIT_NO_EMPTY); + $keyed = isset($options['keyed']); + $directives = ! isset($options['ignore_directives']); + $out = array(); + + foreach ($lines as $line) { + + if ($directives && preg_match($regex->ruleDirective, $line, $m)) { + + if (! empty($m[1])) { + $property = 'mixin'; + } + elseif (! empty($m[2])) { + $property = 'extends'; + } + else { + $property = 'name'; + } + $value = trim(substr($line, strlen($m[0]))); + } + elseif (($colon_pos = strpos($line, ':')) !== false) { + + $property = trim(substr($line, 0, $colon_pos)); + + // Extract the value part of the declaration. + $value = trim(substr($line, $colon_pos + 1)); + } + else { + continue; + } + + // Empty strings are ignored. + if (! isset($property[0]) || ! isset($value[0])) { + continue; + } + + // Add any mixins. + if ($property === 'mixin') { + + if ($mixables = CssCrush_Mixin::parseValue($value)) { + + // Add mixin declarations to the stack. + while ($mixable = array_shift($mixables)) { + if ($keyed) { + $out[$mixable['property']] = $mixable['value']; + } + else { + $out[] = array($mixable['property'], $mixable['value']); + } + } + } + } + elseif ($keyed) { + $out[$property] = $value; + } + else { + $out[] = array($property, $value); + } + } + + return $out; + } } diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php index 69a23be..1a4aa74 100644 --- a/lib/CssCrush/Util.php +++ b/lib/CssCrush/Util.php @@ -128,36 +128,12 @@ static public function splitDelimList ($str, $delim = ',', $trim = true) return $list; } - static public function parseBlock ($str, $keyed = false, $strip_comment_tokens = true) + static public function unTokenize ($str) { - if ($strip_comment_tokens) { - $str = CssCrush_Util::stripCommentTokens($str); - } - - $declarations = preg_split('~\s*;\s*~', $str, null, PREG_SPLIT_NO_EMPTY); - $out = array(); - - foreach ($declarations as $declaration) { - $colon_pos = strpos($declaration, ':'); - if ($colon_pos === -1) { - continue; - } - $property = trim(substr($declaration, 0, $colon_pos)); - $value = trim(substr($declaration, $colon_pos + 1)); - - // Empty strings are ignored. - if (! isset($property[0]) || ! isset($value[0])) { - continue; - } + } - if ($keyed) { - $out[$property] = $value; - } - else { - $out[] = array($property, $value); - } - } - return $out; + static public function tokenize ($str) + { } static public function getLinkBetweenDirs ($dir1, $dir2) diff --git a/plugins/canvas.php b/plugins/canvas.php index f50dacc..888bccd 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -121,7 +121,7 @@ function csscrush__canvas_generator ($input, $context) { $block = $canvas_defs[$name]->apply($args); // Parse the block into a keyed array. - $raw = array_change_key_case(CssCrush_Util::parseBlock($block, true)); + $raw = array_change_key_case(CssCrush_Rule::parseBlock($block, array('keyed' => true))); // Create canvas object. $canvas = new CssCrush_Canvas(); diff --git a/plugins/svg.php b/plugins/svg.php index 96e2a2d..747e2b4 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -202,7 +202,7 @@ function csscrush__svg_generator ($input, $fn_name) { $block = $svg_defs[$name]->apply($args); // Parse the block into a keyed assoc array. - $raw_data = array_change_key_case(CssCrush_Util::parseBlock($block, true)); + $raw_data = array_change_key_case(CssCrush_Rule::parseBlock($block, array('keyed' => true))); // Resolve the type. // Bail if type not recognised. From 0a19a367657218e848be019a5bd01d19b8bbda1c Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 6 Jun 2013 13:12:11 +0100 Subject: [PATCH 111/421] Added url substitutions to templating. Removed some redundant token functions. --- lib/CssCrush/Core.php | 3 +- lib/CssCrush/Declaration.php | 2 +- lib/CssCrush/Importer.php | 44 +++---------------------- lib/CssCrush/Mixin.php | 58 ++++----------------------------- lib/CssCrush/Process.php | 63 +++++++++++++++++++++++++++++------- lib/CssCrush/Regex.php | 2 +- lib/CssCrush/Rule.php | 13 ++------ lib/CssCrush/Selector.php | 4 +-- lib/CssCrush/Template.php | 37 +++++++++++++-------- lib/CssCrush/Url.php | 22 ++++++++++--- lib/CssCrush/Util.php | 8 ----- lib/functions.php | 26 +++++++++++++++ 12 files changed, 134 insertions(+), 148 deletions(-) diff --git a/lib/CssCrush/Core.php b/lib/CssCrush/Core.php index 39e5228..0e44211 100644 --- a/lib/CssCrush/Core.php +++ b/lib/CssCrush/Core.php @@ -505,8 +505,7 @@ static public function stat ($name = null) static public function addSelectorAlias ($name, $body) { - CssCrush::$config->selectorAliases[$name] - = new CssCrush_Template($body, array("interpolate" => true)); + CssCrush::$config->selectorAliases[$name] = new CssCrush_Template($body); } static public function removeSelectorAlias ($name) diff --git a/lib/CssCrush/Declaration.php b/lib/CssCrush/Declaration.php index fa5563f..36c8803 100644 --- a/lib/CssCrush/Declaration.php +++ b/lib/CssCrush/Declaration.php @@ -132,7 +132,7 @@ public function process ($parent_rule) $parent_rule->queryData[$this->property] = $this->value; // Capture top-level paren pairs. - CssCrush::$process->captureParens($this->value); + $this->value = CssCrush::$process->captureParens($this->value); $this->indexFunctions(); } diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index 69e6307..e38a4e2 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -53,7 +53,7 @@ static public function hostfile () } // Fetch the URL object. - $url = CssCrush_Url::get($match[1][0]); + $url = $process->fetchToken($match[1][0]); // Pass over protocoled import urls. if ($url->protocol) { @@ -108,7 +108,7 @@ static public function hostfile () foreach (CssCrush_Regex::matchAll($regex->import, $import->content) as $m) { // Fetch the matched URL. - $url2 = CssCrush_Url::get($m[1][0]); + $url2 = $process->fetchToken($m[1][0]); // Try to resolve absolute paths. // On failure strip the @import statement. @@ -169,7 +169,7 @@ static protected function rewriteImportedUrls ($import) foreach ($matches[0] as $token) { // Fetch the matched URL. - $url = CssCrush_Url::get($token); + $url = CssCrush::$process->fetchToken($token); if ($url->isRelative) { // Prepend the relative url prefix. @@ -233,47 +233,11 @@ static protected function prepareForStream (&$str) // Strip unneeded whitespace. $str = CssCrush_Util::normalizeWhiteSpace($str); - self::captureUrls($str); + $str = $process->captureUrls($str); return true; } - static protected function captureUrls (&$str) - { - static $url_patt; - if (! $url_patt) { - $url_patt = CssCrush_Regex::create('@import +()|(url|data-uri)\(', 'iS'); - } - - $offset = 0; - while (preg_match($url_patt, $str, $outer_m, PREG_OFFSET_CAPTURE, $offset)) { - - $outer_offset = $outer_m[0][1]; - $is_import_url = ! isset($outer_m[2]); - - if ($is_import_url) { - $url = new CssCrush_Url($outer_m[1][0]); - $str = str_replace($outer_m[1][0], $url->label, $str); - } - - // Match parenthesis if not a string token. - elseif ( - preg_match(CssCrush_Regex::$patt->balancedParens, $str, $inner_m, PREG_OFFSET_CAPTURE, $outer_offset) - ) { - $url = new CssCrush_Url($inner_m[1][0]); - $func_name = strtolower($outer_m[2][0]); - $url->convertToData = 'data-uri' === $func_name; - $str = substr_replace($str, $url->label, $outer_offset, - strlen($func_name) + strlen($inner_m[0][0])); - } - - // If brackets cannot be matched, skip over the original match. - else { - $offset += strlen($outer_m[0][0]); - } - } - } - static protected function cb_extractCommentAndString ($match) { $full_match = $match[0]; diff --git a/lib/CssCrush/Mixin.php b/lib/CssCrush/Mixin.php index 92e82d0..e6f7a9a 100644 --- a/lib/CssCrush/Mixin.php +++ b/lib/CssCrush/Mixin.php @@ -6,59 +6,16 @@ */ class CssCrush_Mixin { - public $declarationsTemplate = array(); - public $template; public function __construct ($block) { $this->template = new CssCrush_Template($block); - - // Parse into mixin template. - foreach (CssCrush_Rule::parseBlock($this->template->string) as $pair) { - - list($property, $value) = $pair; - $property = strtolower($property); - - if ($property === 'mixin') { - - // Mixin can contain other mixins if they are available. - if ($mixin_declarations = CssCrush_Mixin::parseValue($value)) { - - // Add mixin result to the stack. - $this->declarationsTemplate = array_merge( - $this->declarationsTemplate, $mixin_declarations); - } - } - elseif ($value !== '') { - - // Store template declarations as arrays as they are copied by - // value not reference. - $this->declarationsTemplate[] = array( - 'property' => $property, - 'value' => $value, - ); - } - } } public function call ( array $args ) { - // Copy the template. - $declarations = $this->declarationsTemplate; - - if (count($this->template)) { - - $this->template->prepare($args); - - // Place the arguments. - foreach ($declarations as &$declaration) { - $declaration['value'] = $this->template->apply(null, $declaration['value']); - } - } - - // Return mixin declarations. - return $declarations; + return CssCrush_Rule::parseBlock($this->template->apply($args)); } static public function parseSingleValue ($message) @@ -68,9 +25,9 @@ static public function parseSingleValue ($message) $non_mixin = null; // e.g. - // - mymixin( 50px, rgba(0,0,0,0), left 100% ) + // - named-mixin( 50px, rgba(0,0,0,0), left 100% ) // - abstract-rule - // - #selector + // - #foo // Test for leading name if (preg_match('~^[\w-]+~', $message, $name_match)) { @@ -108,8 +65,8 @@ static public function parseSingleValue ($message) $result = array(); foreach ($non_mixin as $declaration) { $result[] = array( - 'property' => $declaration->property, - 'value' => $declaration->value, + $declaration->property, + $declaration->value, ); } @@ -117,10 +74,7 @@ static public function parseSingleValue ($message) } // Nothing matches - else { - - return false; - } + return false; } // We have a valid mixin. diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 5a7397f..95ac290 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -166,10 +166,7 @@ public function isToken ($str, $of_type = null) public function fetchToken ($token) { $path =& $this->tokens->{$token[1]}; - if (isset($path[$token])) { - return $path[$token]; - } - return null; + return isset($path[$token]) ? $path[$token] : null; } public function popToken ($token) @@ -215,7 +212,7 @@ public function captureParens (&$str) if (! $callback) { $callback = create_function('$m', 'return CssCrush::$process->addToken($m[0], \'p\');'); } - $str = preg_replace_callback(CssCrush_Regex::$patt->balancedParens, $callback, $str); + return preg_replace_callback(CssCrush_Regex::$patt->balancedParens, $callback, $str); } public function restoreParens (&$str, $release = true) @@ -237,13 +234,55 @@ public function restoreParens (&$str, $release = true) ############################# # Strings. - public function captureStrings (&$str) + public function captureStrings ($str) { static $callback; if (! $callback) { $callback = create_function('$m', 'return CssCrush::$process->addToken($m[0], \'s\');'); } - $str = preg_replace_callback(CssCrush_Regex::$patt->string, $callback, $str); + return preg_replace_callback(CssCrush_Regex::$patt->string, $callback, $str); + } + + + ############################# + # Urls. + + public function captureUrls ($str) + { + static $url_patt; + if (! $url_patt) { + $url_patt = CssCrush_Regex::create('@import +()|(url|data-uri)\(', 'iS'); + } + + $offset = 0; + while (preg_match($url_patt, $str, $outer_m, PREG_OFFSET_CAPTURE, $offset)) { + + $outer_offset = $outer_m[0][1]; + $is_import_url = ! isset($outer_m[2]); + + if ($is_import_url) { + $url = new CssCrush_Url($outer_m[1][0]); + $str = str_replace($outer_m[1][0], $url->label, $str); + } + + // Match parenthesis if not a string token. + elseif ( + preg_match(CssCrush_Regex::$patt->balancedParens, $str, $inner_m, PREG_OFFSET_CAPTURE, $outer_offset) + ) { + $url = new CssCrush_Url($inner_m[1][0]); + $func_name = strtolower($outer_m[2][0]); + $url->convertToData = 'data-uri' === $func_name; + $str = substr_replace($str, $url->label, $outer_offset, + strlen($func_name) + strlen($inner_m[0][0])); + } + + // If brackets cannot be matched, skip over the original match. + else { + $offset += strlen($outer_m[0][0]); + } + } + + return $str; } @@ -311,6 +350,7 @@ protected function getBoilerplate () $boilerplate = preg_split('~[\t]*(\r\n?|\n)[\t]*~', $boilerplate); $boilerplate = array_map('trim', $boilerplate); $boilerplate = "$EOL * " . implode("$EOL * ", $boilerplate); + return "/*{$boilerplate}$EOL */$EOL"; } @@ -326,7 +366,7 @@ protected function resolveSelectorAliases () $callback = create_function('$m', ' $name = strtolower($m[1]); $body = CssCrush_Util::stripCommentTokens($m[2]); - $template = new CssCrush_Template($body, array("interpolate" => true)); + $template = new CssCrush_Template($body); CssCrush::$process->selectorAliases[$name] = $template; '); } @@ -726,8 +766,7 @@ protected function resolveFragments () $curly_match->replace(''); // Create the fragment and store it. - $fragments[$fragment_name] = new CssCrush_Template( - $curly_match->inside(), array('interpolate' => true)); + $fragments[$fragment_name] = new CssCrush_Template($curly_match->inside()); } } @@ -862,7 +901,7 @@ protected function prefixSelectors () CssCrush_Process::applySelectorAliases($raw_argument); - $this->captureParens($raw_argument); + $raw_argument = $this->captureParens($raw_argument); $arguments = CssCrush_Util::splitDelimList($raw_argument); $curly_match = new CssCrush_BalancedMatch($this->stream, $match_start_pos); @@ -879,7 +918,7 @@ protected function prefixSelectors () foreach ($rule_matches as $rule_match) { // Get the rule instance. - $rule = CssCrush_Rule::get($rule_match[0][0]); + $rule = CssCrush::$process->fetchToken($rule_match[0][0]); // Using arguments create new selector list for the rule. $new_selector_list = array(); diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php index 9675c7b..430ea3b 100644 --- a/lib/CssCrush/Regex.php +++ b/lib/CssCrush/Regex.php @@ -48,7 +48,7 @@ static public function init () // Create standalone class patterns, add classes as class swaps. foreach ($classes as $name => $class) { self::$classSwaps['<' . str_replace('_', '-', $name) . '>'] = $class; - $patt->{$name} = '~' . $class . '~'; + $patt->{$name} = '~' . $class . '~S'; } // Rooted classes. diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php index abedca5..2066c74 100644 --- a/lib/CssCrush/Rule.php +++ b/lib/CssCrush/Rule.php @@ -781,14 +781,6 @@ public function setDeclarations (array $declaration_stack) $this->updatePropertyIndex(); } - static public function get ($token) - { - if (isset(CssCrush::$process->tokens->r[$token])) { - return CssCrush::$process->tokens->r[$token]; - } - return null; - } - static public function parseBlock ($str, $options = array()) { $regex = CssCrush_Regex::$patt; @@ -837,11 +829,12 @@ static public function parseBlock ($str, $options = array()) // Add mixin declarations to the stack. while ($mixable = array_shift($mixables)) { + list($mix_prop, $mix_val) = $mixable; if ($keyed) { - $out[$mixable['property']] = $mixable['value']; + $out[$mix_prop] = $mix_val; } else { - $out[] = array($mixable['property'], $mixable['value']); + $out[] = array($mix_prop, $mix_val); } } } diff --git a/lib/CssCrush/Selector.php b/lib/CssCrush/Selector.php index f834cea..3b3fda0 100644 --- a/lib/CssCrush/Selector.php +++ b/lib/CssCrush/Selector.php @@ -24,9 +24,7 @@ public function __construct ($raw_selector, $associated_rule = null) CssCrush_Process::applySelectorAliases($raw_selector); // Capture top-level paren groups. - CssCrush::$process->captureParens($raw_selector); - - $this->value = $raw_selector; + $this->value = CssCrush::$process->captureParens($raw_selector); } public function __toString () diff --git a/lib/CssCrush/Template.php b/lib/CssCrush/Template.php index 6a60b4e..c94144d 100644 --- a/lib/CssCrush/Template.php +++ b/lib/CssCrush/Template.php @@ -17,9 +17,6 @@ class CssCrush_Template implements Countable // The string passed in with arg calls replaced by tokens. public $string; - // Whether to substitute on string tokens. - public $interpolate = false; - public function __construct ($str, $options = array()) { static $arg_patt; @@ -28,11 +25,7 @@ public function __construct ($str, $options = array()) array('arg'), array('templating' => true)); } - // Interpolation. - if (! empty($options['interpolate'])) { - $this->interpolate = true; - $str = CssCrush::$process->restoreTokens($str, 's', true); - } + $str = CssCrush_Template::unTokenize($str); // Parse all arg function calls in the passed string, // callback creates default values. @@ -143,16 +136,32 @@ public function apply (array $args = null, $str = null) // Apply substitutions. $str = isset($find) ? str_replace($find, $replace, $str) : $str; - // Re-tokenize string literals on returns. - if ($this->interpolate) { - CssCrush::$process->captureStrings($str); - } - - return $str; + // Re-tokenize string on return. + return CssCrush_Template::tokenize($str); } public function count () { return $this->argCount; } + + static public function tokenize ($str) + { + $str = CssCrush::$process->captureStrings($str); + $str = CssCrush::$process->captureUrls($str); + + return $str; + } + + static public function unTokenize ($str) + { + $str = preg_replace_callback(CssCrush_Regex::$patt->u_token, function ($m) { + $url = CssCrush::$process->popToken($m[0]); + return $url ? $url->getOriginalValue() : ''; + }, $str); + + $str = CssCrush::$process->restoreTokens($str, 's', true); + + return $str; + } } diff --git a/lib/CssCrush/Url.php b/lib/CssCrush/Url.php index df5f101..c064132 100644 --- a/lib/CssCrush/Url.php +++ b/lib/CssCrush/Url.php @@ -54,11 +54,6 @@ public function __toString () return "url(http://github.com/$quote$this-%3Evalue$quote)"; } - static public function get ($token) - { - return CssCrush::$process->tokens->u[$token]; - } - public function evaluate () { // Protocol based url. @@ -109,6 +104,23 @@ public function getAbsolutePath () return $path; } + public function getOriginalValue () + { + // If a data URI we assume nothing useful can be achieved + // by returning the original value so we just return the token label. + if ($this->isData) { + + return $this->label; + } + + $function = 'url'; + if ($this->convertToData) { + $function = 'data-uri'; + } + + return "$function($this->value)"; + } + public function resolveRootedPath () { $process = CssCrush::$process; diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php index 1a4aa74..284f92d 100644 --- a/lib/CssCrush/Util.php +++ b/lib/CssCrush/Util.php @@ -128,14 +128,6 @@ static public function splitDelimList ($str, $delim = ',', $trim = true) return $list; } - static public function unTokenize ($str) - { - } - - static public function tokenize ($str) - { - } - static public function getLinkBetweenDirs ($dir1, $dir2) { // Normalise the paths. diff --git a/lib/functions.php b/lib/functions.php index 4ceb035..a0bcb90 100644 --- a/lib/functions.php +++ b/lib/functions.php @@ -59,3 +59,29 @@ function csscrush_set ($object_name, $modifier) { } } } + +/** + * Get default options and config settings. + * + * @param string $object_name Name of object you want to modify: 'config' or 'options'. + * @param mixed $property The property name to retrieve. + */ +function csscrush_get ($object_name, $property = null) { + + if (in_array($object_name, array('options', 'config'))) { + + $pointer = $object_name === 'options' ? + CssCrush::$config->options : CssCrush::$config; + + if (! isset($property)) { + + return $pointer; + } + else { + + return isset($pointer->{$property}) ? $pointer->{$property} : null; + } + } + + return null; +} From 2d45d788a5fe792a8cf41eaf48e3da96bd6b0df5 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 6 Jun 2013 19:58:36 +0100 Subject: [PATCH 112/421] Expanded and centralised the Token API --- lib/CssCrush/Core.php | 6 +- lib/CssCrush/Declaration.php | 4 +- lib/CssCrush/Importer.php | 16 +-- lib/CssCrush/PostAliasFix.php | 8 +- lib/CssCrush/Process.php | 207 ++++++---------------------------- lib/CssCrush/Rule.php | 10 +- lib/CssCrush/Selector.php | 6 +- lib/CssCrush/Template.php | 12 +- lib/CssCrush/Tokens.php | 178 +++++++++++++++++++++++++++++ lib/CssCrush/Url.php | 8 +- plugins/canvas.php | 2 +- plugins/hsl-to-hex.php | 4 +- plugins/svg.php | 12 +- 13 files changed, 253 insertions(+), 220 deletions(-) create mode 100644 lib/CssCrush/Tokens.php diff --git a/lib/CssCrush/Core.php b/lib/CssCrush/Core.php index 0e44211..3637c1b 100644 --- a/lib/CssCrush/Core.php +++ b/lib/CssCrush/Core.php @@ -584,17 +584,19 @@ static public function runStat ($name) return; } + $all_rules =& $process->tokens->store->r; + switch ($name) { case 'selector_count': $process->stat['selector_count'] = 0; - foreach ($process->tokens->r as $rule) { + foreach ($all_rules as $rule) { $process->stat['selector_count'] += count($rule->selectors); } break; case 'rule_count': - $process->stat['rule_count'] = count($process->tokens->r); + $process->stat['rule_count'] = count($all_rules); break; case 'compile_time': diff --git a/lib/CssCrush/Declaration.php b/lib/CssCrush/Declaration.php index 36c8803..c52c9f7 100644 --- a/lib/CssCrush/Declaration.php +++ b/lib/CssCrush/Declaration.php @@ -132,7 +132,7 @@ public function process ($parent_rule) $parent_rule->queryData[$this->property] = $this->value; // Capture top-level paren pairs. - $this->value = CssCrush::$process->captureParens($this->value); + $this->value = CssCrush::$process->tokens->captureParens($this->value); $this->indexFunctions(); } @@ -151,6 +151,6 @@ public function indexFunctions () public function getFullValue () { - return CssCrush::$process->restoreTokens($this->value, 'p'); + return CssCrush::$process->tokens->restore($this->value, 'p'); } } diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index e38a4e2..e5e6f27 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -53,7 +53,7 @@ static public function hostfile () } // Fetch the URL object. - $url = $process->fetchToken($match[1][0]); + $url = $process->tokens->get($match[1][0]); // Pass over protocoled import urls. if ($url->protocol) { @@ -108,7 +108,7 @@ static public function hostfile () foreach (CssCrush_Regex::matchAll($regex->import, $import->content) as $m) { // Fetch the matched URL. - $url2 = $process->fetchToken($m[1][0]); + $url2 = $process->tokens->get($m[1][0]); // Try to resolve absolute paths. // On failure strip the @import statement. @@ -169,7 +169,7 @@ static protected function rewriteImportedUrls ($import) foreach ($matches[0] as $token) { // Fetch the matched URL. - $url = CssCrush::$process->fetchToken($token); + $url = CssCrush::$process->tokens->get($token); if ($url->isRelative) { // Prepend the relative url prefix. @@ -199,7 +199,7 @@ static protected function prepareForStream (&$str) if (! $process->charset) { // Keep track of newlines for line tracing. $replace = str_repeat("\n", substr_count($m[0], "\n")); - $process->charset = trim($process->fetchToken($m[1]), '"\''); + $process->charset = trim($process->tokens->get($m[1]), '"\''); } $str = preg_replace($regex->charset, $replace, $str); } @@ -233,7 +233,7 @@ static protected function prepareForStream (&$str) // Strip unneeded whitespace. $str = CssCrush_Util::normalizeWhiteSpace($str); - $str = $process->captureUrls($str); + $str = $process->tokens->captureUrls($str); return true; } @@ -261,7 +261,7 @@ static protected function cb_extractCommentAndString ($match) if (! preg_match('~\*/$~', $full_match)) { $full_match .= '*/'; } - $label = $process->addToken($full_match, 'c'); + $label = $process->tokens->add($full_match, 'c'); } else { @@ -270,7 +270,7 @@ static protected function cb_extractCommentAndString ($match) if ($full_match[0] !== $full_match[strlen($full_match)-1]) { $full_match .= $full_match[0]; } - $label = $process->addToken($full_match, 's'); + $label = $process->tokens->add($full_match, 's'); } return $newlines . $label; @@ -320,7 +320,7 @@ static protected function addTracingStubs (&$str) $current_file = preg_replace('~[^\w-]~', '\\\\$0', $current_file); // Splice in tracing stub. - $label = CssCrush::$process->addToken("@media -sass-debug-info{filename{font-family:$current_file}line{font-family:\\00003$line_num}}", 't'); + $label = CssCrush::$process->tokens->add("@media -sass-debug-info{filename{font-family:$current_file}line{font-family:\\00003$line_num}}", 't'); $str = $str_before . $label . substr($str, $selector_index); } diff --git a/lib/CssCrush/PostAliasFix.php b/lib/CssCrush/PostAliasFix.php index 609f783..6368de2 100644 --- a/lib/CssCrush/PostAliasFix.php +++ b/lib/CssCrush/PostAliasFix.php @@ -83,7 +83,7 @@ function csscrush__post_alias_fix_lineargradients ($declaration_copies) { foreach (CssCrush_Regex::matchAll($fn_patt, $declaration_copies[0]->value) as $m) { $original_parens[] = $m[1][0]; - $original_paren_value = CssCrush::$process->fetchToken($m[1][0]); + $original_paren_value = CssCrush::$process->tokens->get($m[1][0]); // Convert keyword angle values. $updated_paren_value = str_ireplace( @@ -99,7 +99,7 @@ function csscrush__post_alias_fix_lineargradients ($declaration_copies) { $updated_paren_value ); - $replacement_parens[] = CssCrush::$process->addToken($updated_paren_value, 'p'); + $replacement_parens[] = CssCrush::$process->tokens->add($updated_paren_value, 'p'); } // Swap in the new tokens on all the prefixed declarations. @@ -128,11 +128,11 @@ function csscrush__post_alias_fix_radialgradients ($declaration_copies) { foreach (CssCrush_Regex::matchAll($fn_patt, $declaration_copies[0]->value) as $m) { $original_parens[] = $m[1][0]; - $replacement_parens[] = CssCrush::$process->addToken( + $replacement_parens[] = CssCrush::$process->tokens->add( preg_replace( '~\bat +(top|left|bottom|right|center)\b~i', '$1', - CssCrush::$process->fetchToken($m[1][0]) + CssCrush::$process->tokens->get($m[1][0]) ), 'p'); } diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 95ac290..6c82ec9 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -23,7 +23,6 @@ public function __construct ($options) $this->options->global_vars = $config->vars; // Initialize properties. - $this->uid = 0; $this->cacheData = array(); $this->mixins = array(); $this->references = array(); @@ -31,18 +30,11 @@ public function __construct ($options) $this->stat = array(); $this->charset = null; $this->currentFile = null; - $this->tokens = (object) array( - 's' => array(), // Strings - 'c' => array(), // Comments - 'r' => array(), // Rules - 'p' => array(), // Parens - 'u' => array(), // URLs - 't' => array(), // Traces - ); $this->variables = array(); $this->misc = new stdClass(); $this->input = new stdClass(); $this->output = new stdClass(); + $this->tokens = new CssCrush_Tokens(); // Copy config values. $this->plugins = $config->plugins; @@ -139,153 +131,6 @@ public function ioCall ($method) } - ############################# - # Tokens. - - public function createTokenLabel ($type) - { - $counter = ++$this->uid; - return "?$type$counter?"; - } - - public function addToken ($value, $type) - { - $label = $this->createTokenLabel($type); - $this->tokens->{$type}[$label] = $value; - return $label; - } - - public function isToken ($str, $of_type = null) - { - if (preg_match('~^\?([a-z])\d+\?$~S', $str, $m)) { - return $of_type ? ($of_type === $m[1]) : true; - } - return false; - } - - public function fetchToken ($token) - { - $path =& $this->tokens->{$token[1]}; - return isset($path[$token]) ? $path[$token] : null; - } - - public function popToken ($token) - { - $val = $this->fetchToken($token); - $this->releaseToken($token); - return $val; - } - - public function releaseToken ($token) - { - unset($this->tokens->{ $token[1] }[$token]); - } - - public function restoreTokens ($str, $type = 'p', $release = false) - { - // Reference the token table. - $token_table =& $this->tokens->{$type}; - - // Find matching tokens. - $matches = CssCrush_Regex::matchAll(CssCrush_Regex::$patt->{"{$type}_token"}, $str); - - foreach ($matches as $m) { - $token = $m[0][0]; - if (isset($token_table[$token])) { - $str = str_replace($token, $token_table[$token], $str); - if ($release) { - unset($token_table[$token]); - } - } - } - - return $str; - } - - - ############################# - # Parens. - - public function captureParens (&$str) - { - static $callback; - if (! $callback) { - $callback = create_function('$m', 'return CssCrush::$process->addToken($m[0], \'p\');'); - } - return preg_replace_callback(CssCrush_Regex::$patt->balancedParens, $callback, $str); - } - - public function restoreParens (&$str, $release = true) - { - $token_table =& $this->tokens->p; - - foreach (CssCrush_Regex::matchAll(CssCrush_Regex::$patt->p_token, $str) as $m) { - $token = $m[0][0]; - if (isset($token_table[$token])) { - $str = str_replace($token, $token_table[$token], $str); - if ($release) { - unset($token_table[$token]); - } - } - } - } - - - ############################# - # Strings. - - public function captureStrings ($str) - { - static $callback; - if (! $callback) { - $callback = create_function('$m', 'return CssCrush::$process->addToken($m[0], \'s\');'); - } - return preg_replace_callback(CssCrush_Regex::$patt->string, $callback, $str); - } - - - ############################# - # Urls. - - public function captureUrls ($str) - { - static $url_patt; - if (! $url_patt) { - $url_patt = CssCrush_Regex::create('@import +()|(url|data-uri)\(', 'iS'); - } - - $offset = 0; - while (preg_match($url_patt, $str, $outer_m, PREG_OFFSET_CAPTURE, $offset)) { - - $outer_offset = $outer_m[0][1]; - $is_import_url = ! isset($outer_m[2]); - - if ($is_import_url) { - $url = new CssCrush_Url($outer_m[1][0]); - $str = str_replace($outer_m[1][0], $url->label, $str); - } - - // Match parenthesis if not a string token. - elseif ( - preg_match(CssCrush_Regex::$patt->balancedParens, $str, $inner_m, PREG_OFFSET_CAPTURE, $outer_offset) - ) { - $url = new CssCrush_Url($inner_m[1][0]); - $func_name = strtolower($outer_m[2][0]); - $url->convertToData = 'data-uri' === $func_name; - $str = substr_replace($str, $url->label, $outer_offset, - strlen($func_name) + strlen($inner_m[0][0])); - } - - // If brackets cannot be matched, skip over the original match. - else { - $offset += strlen($outer_m[0][0]); - } - } - - return $str; - } - - ############################# # Boilerplate. @@ -616,13 +461,15 @@ protected function placeAllVariables () // Place variables in main stream. self::placeVariables($this->stream->raw); + $raw_tokens =& $this->tokens->store; + // Repeat above steps for variables embedded in string tokens. - foreach ($this->tokens->s as $label => &$value) { + foreach ($raw_tokens->s as $label => &$value) { self::placeVariables($value); } // Repeat above steps for variables embedded in URL tokens. - foreach ($this->tokens->u as $label => $url) { + foreach ($raw_tokens->u as $label => $url) { if (! $url->isData && self::placeVariables($url->value)) { // Re-evaluate $url->value if anything has been interpolated. $url->evaluate(); @@ -837,7 +684,7 @@ protected function processRules () { $aliases =& $this->aliases; - foreach ($this->tokens->r as $rule) { + foreach ($this->tokens->getOfType('r') as $rule) { $rule->processDeclarations(); @@ -878,7 +725,7 @@ static public function cb_extractRules ($m) // Store rules if they have declarations or extend arguments. if (! empty($rule->declarations) || $rule->extendArgs) { - CssCrush::$process->tokens->r[$rule->label] = $rule; + CssCrush::$process->tokens->add($rule, 'r', $rule->label); // If only using extend still return a label. return $rule->label; @@ -892,6 +739,7 @@ static public function cb_extractRules ($m) protected function prefixSelectors () { $matches = $this->stream->matchAll('~@in\s+([^{]+)\{~iS'); + $tokens = CssCrush::$process->tokens; // Move through the matches in reverse order. while ($match = array_pop($matches)) { @@ -901,7 +749,7 @@ protected function prefixSelectors () CssCrush_Process::applySelectorAliases($raw_argument); - $raw_argument = $this->captureParens($raw_argument); + $raw_argument = $tokens->captureParens($raw_argument); $arguments = CssCrush_Util::splitDelimList($raw_argument); $curly_match = new CssCrush_BalancedMatch($this->stream, $match_start_pos); @@ -918,7 +766,7 @@ protected function prefixSelectors () foreach ($rule_matches as $rule_match) { // Get the rule instance. - $rule = CssCrush::$process->fetchToken($rule_match[0][0]); + $rule = $tokens->get($rule_match[0][0]); // Using arguments create new selector list for the rule. $new_selector_list = array(); @@ -1012,17 +860,17 @@ protected function aliasAtRules () $originals = array(); $replacements = array(); - foreach ($copy_matches[0] as $copy_match) { + foreach ($copy_matches[0] as $rule_label) { // Clone the matched rule. - $originals[] = $rule_label = $copy_match; - $cloneRule = clone $this->tokens->r[$rule_label]; + $originals[] = $rule_label; + $clone_rule = clone $this->tokens->get($rule_label); // Set the vendor context. - $cloneRule->vendorContext = $vendor; + $clone_rule->vendorContext = $vendor; // Store the clone. - $replacements[] = $this->addToken($cloneRule, 'r'); + $replacements[] = $this->tokens->add($clone_rule, 'r'); } // Finally replace the original labels with the cloned rule labels. @@ -1073,12 +921,14 @@ protected function collate () $this->stream->pregReplaceHash($regex_replacements)->lTrim(); // Print out rules. - $this->stream->replaceHash($this->tokens->r); + $this->stream->replaceHash($this->tokens->getOfType('r')); CssCrush::runStat('selector_count'); CssCrush::runStat('rule_count'); + $this->tokens->releaseOfType('r'); // Insert parens. - $this->stream->replaceHash($this->tokens->p); + $this->stream->replaceHash($this->tokens->getOfType('p')); + $this->tokens->releaseOfType('p'); // Advanced minification parameters. if (is_array($minify)) { @@ -1096,25 +946,29 @@ protected function collate () } else { + $comments = $this->tokens->getOfType('c'); + $this->tokens->releaseOfType('c'); + // Add newlines after comments. - foreach ($this->tokens->c as $token => &$comment) { + foreach ($comments as $token => &$comment) { $comment .= "$EOL$EOL"; } // Insert comments and do final whitespace cleanup. $this->stream - ->replaceHash($this->tokens->c) + ->replaceHash($comments) ->trim() ->append($EOL); } // Insert URLs. - if ($this->tokens->u) { + $urls = $this->tokens->getOfType('u'); + if ($urls) { $link = CssCrush_Util::getLinkBetweenDirs($this->output->dir, $this->input->dir); $make_urls_absolute = $options->rewrite_import_urls === 'absolute'; - foreach ($this->tokens->u as $token => $url) { + foreach ($urls as $token => $url) { if ($url->isRelative && ! $url->noRewrite) { if ($make_urls_absolute) { @@ -1126,11 +980,14 @@ protected function collate () } } } - $this->stream->replaceHash($this->tokens->u); + $this->stream->replaceHash($urls); + unset($urls); + $this->tokens->releaseOfType('u'); } // Insert string literals. - $this->stream->replaceHash($this->tokens->s); + $this->stream->replaceHash($this->tokens->getOfType('s')); + $this->tokens->releaseOfType('s'); // Add in boilerplate. if ($options->boilerplate) { diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php index 2066c74..2935ac9 100644 --- a/lib/CssCrush/Rule.php +++ b/lib/CssCrush/Rule.php @@ -34,7 +34,7 @@ public function __construct ($selector_string = null, $declarations_string) { $regex = CssCrush_Regex::$patt; $process = CssCrush::$process; - $this->label = $process->createTokenLabel('r'); + $this->label = $process->tokens->createLabel('r'); // If tracing store the last tracing stub, then strip all. if ( @@ -42,9 +42,9 @@ public function __construct ($selector_string = null, $declarations_string) preg_match_all($regex->t_token, $selector_string, $trace_tokens) ) { $trace_token = array_pop($trace_tokens); - $this->tracingStub = $process->fetchToken($trace_token[0]); + $this->tracingStub = $process->tokens->get($trace_token[0]); foreach ($trace_tokens as $trace_token) { - $process->releaseToken($trace_token[0]); + $process->tokens->release($trace_token[0]); } $selector_string = preg_replace($regex->t_token, '', $selector_string); @@ -133,7 +133,7 @@ public function __toString () if (empty($this->selectors) || empty($this->declarations)) { // De-reference this instance. - unset($process->tokens->r[$this->label]); + $process->tokens->release($this->label); return ''; } @@ -392,7 +392,7 @@ public function expandSelectors () preg_match($any_patt, $selector->value, $m); // Parse the arguments - $expression = CssCrush::$process->tokens->p[$m[1]]; + $expression = CssCrush::$process->tokens->get($m[1]); // Remove outer parens. $expression = substr($expression, 1, strlen($expression) - 2); diff --git a/lib/CssCrush/Selector.php b/lib/CssCrush/Selector.php index 3b3fda0..9d68bbc 100644 --- a/lib/CssCrush/Selector.php +++ b/lib/CssCrush/Selector.php @@ -24,7 +24,7 @@ public function __construct ($raw_selector, $associated_rule = null) CssCrush_Process::applySelectorAliases($raw_selector); // Capture top-level paren groups. - $this->value = CssCrush::$process->captureParens($raw_selector); + $this->value = CssCrush::$process->tokens->captureParens($raw_selector); } public function __toString () @@ -57,14 +57,14 @@ static function makeReadable ($str) { // Quick test for paren tokens. if (strpos($str, '?p') !== false) { - $str = CssCrush::$process->restoreTokens($str, 'p'); + $str = CssCrush::$process->tokens->restore($str, 'p'); } $str = CssCrush_Selector::normalizeWhiteSpace($str); // Quick test for string tokens. if (strpos($str, '?s') !== false) { - $str = CssCrush::$process->restoreTokens($str, 's'); + $str = CssCrush::$process->tokens->restore($str, 's'); } return $str; diff --git a/lib/CssCrush/Template.php b/lib/CssCrush/Template.php index c94144d..ff7593b 100644 --- a/lib/CssCrush/Template.php +++ b/lib/CssCrush/Template.php @@ -147,20 +147,16 @@ public function count () static public function tokenize ($str) { - $str = CssCrush::$process->captureStrings($str); - $str = CssCrush::$process->captureUrls($str); + $str = CssCrush::$process->tokens->capture($str, 's'); + $str = CssCrush::$process->tokens->capture($str, 'u'); return $str; } static public function unTokenize ($str) { - $str = preg_replace_callback(CssCrush_Regex::$patt->u_token, function ($m) { - $url = CssCrush::$process->popToken($m[0]); - return $url ? $url->getOriginalValue() : ''; - }, $str); - - $str = CssCrush::$process->restoreTokens($str, 's', true); + $str = CssCrush::$process->tokens->restore($str, 'u', true); + $str = CssCrush::$process->tokens->restore($str, 's', true); return $str; } diff --git a/lib/CssCrush/Tokens.php b/lib/CssCrush/Tokens.php new file mode 100644 index 0000000..f5596e4 --- /dev/null +++ b/lib/CssCrush/Tokens.php @@ -0,0 +1,178 @@ +store = (object) array( + 's' => array(), // Strings + 'c' => array(), // Comments + 'r' => array(), // Rules + 'p' => array(), // Parens + 'u' => array(), // URLs + 't' => array(), // Traces + ); + } + + public function get ($label) + { + $path =& $this->store->{$label[1]}; + return isset($path[$label]) ? $path[$label] : null; + } + + public function getOfType ($type) + { + return $this->store->{$type}; + } + + public function releaseOfType ($type) + { + $this->store->{$type} = array(); + } + + public function pop ($label) + { + $value = $this->get($label); + $this->release($label); + return $value; + } + + public function release ($label) + { + unset($this->store->{$label[1]}[$label]); + } + + public function add ($value, $type, $existing_label = null) + { + $label = $existing_label ? $existing_label : $this->createLabel($type); + $this->store->{$type}[$label] = $value; + return $label; + } + + public function createLabel ($type) + { + $counter = ++$this->uid; + return "?$type$counter?"; + } + + public function restore ($str, $type, $release = false) + { + switch ($type) { + case 'u': + // Currently this always releases URLs + // may need to refactor later. + static $url_restore_callback; + if (! $url_restore_callback) { + $url_restore_callback = create_function('$m', ' + $url = CssCrush::$process->tokens->pop($m[0]); + return $url ? $url->getOriginalValue() : \'\'; + '); + } + + $str = preg_replace_callback(CssCrush_Regex::$patt->u_token, $url_revert_callback, $str); + break; + default: + $token_table =& $this->store->{$type}; + + // Find matching tokens. + foreach (CssCrush_Regex::matchAll(CssCrush_Regex::$patt->{"{$type}_token"}, $str) as $m) { + $label = $m[0][0]; + if (isset($token_table[$label])) { + $str = str_replace($label, $token_table[$label], $str); + if ($release) { + unset($token_table[$label]); + } + } + } + break; + } + + return $str; + } + + public function capture ($str, $type) + { + switch ($type) { + case 'u': + return $this->captureUrls($str); + break; + case 's': + return $this->captureStrings($str); + break; + case 'p': + return $this->captureParens($str); + break; + } + } + + public function captureParens ($str) + { + static $callback; + if (! $callback) { + $callback = create_function('$m', 'return CssCrush::$process->tokens->add($m[0], \'p\');'); + } + return preg_replace_callback(CssCrush_Regex::$patt->balancedParens, $callback, $str); + } + + public function captureStrings ($str) + { + static $callback; + if (! $callback) { + $callback = create_function('$m', 'return CssCrush::$process->tokens->add($m[0], \'s\');'); + } + return preg_replace_callback(CssCrush_Regex::$patt->string, $callback, $str); + } + + public function captureUrls ($str) + { + static $url_patt; + if (! $url_patt) { + $url_patt = CssCrush_Regex::create('@import +()|(url|data-uri)\(', 'iS'); + } + + $offset = 0; + while (preg_match($url_patt, $str, $outer_m, PREG_OFFSET_CAPTURE, $offset)) { + + $outer_offset = $outer_m[0][1]; + $is_import_url = ! isset($outer_m[2]); + + if ($is_import_url) { + $url = new CssCrush_Url($outer_m[1][0]); + $str = str_replace($outer_m[1][0], $url->label, $str); + } + + // Match parenthesis if not a string token. + elseif ( + preg_match(CssCrush_Regex::$patt->balancedParens, $str, $inner_m, PREG_OFFSET_CAPTURE, $outer_offset) + ) { + $url = new CssCrush_Url($inner_m[1][0]); + $func_name = strtolower($outer_m[2][0]); + $url->convertToData = 'data-uri' === $func_name; + $str = substr_replace($str, $url->label, $outer_offset, + strlen($func_name) + strlen($inner_m[0][0])); + } + + // If brackets cannot be matched, skip over the original match. + else { + $offset += strlen($outer_m[0][0]); + } + } + + return $str; + } + + static public function is ($label, $of_type) + { + if (preg_match('~^\?([a-z])\d+\?$~S', $label, $m)) { + return $of_type ? ($of_type === $m[1]) : true; + } + return false; + } +} diff --git a/lib/CssCrush/Url.php b/lib/CssCrush/Url.php index c064132..81f9269 100644 --- a/lib/CssCrush/Url.php +++ b/lib/CssCrush/Url.php @@ -21,18 +21,18 @@ class CssCrush_Url public function __construct ($raw_value, $convert_to_data = false) { $regex = CssCrush_Regex::$patt; - $process = CssCrush::$process; + $tokens = CssCrush::$process->tokens; if (preg_match($regex->s_token, $raw_value)) { - $this->value = trim($process->fetchToken($raw_value), '\'"'); - $process->releaseToken($raw_value); + $this->value = trim($tokens->get($raw_value), '\'"'); + $tokens->release($raw_value); } else { $this->value = $raw_value; } $this->evaluate(); - $this->label = $process->addToken($this, 'u'); + $this->label = $tokens->add($this, 'u'); } public function __toString () diff --git a/plugins/canvas.php b/plugins/canvas.php index 888bccd..e2c1c1b 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -480,7 +480,7 @@ function csscrush__canvas_preprocess ($canvas) { function csscrush__canvas_fetch_src ($url_token) { - if ($url_token && $url = CssCrush::$process->fetchToken($url_token)) { + if ($url_token && $url = CssCrush::$process->tokens->get($url_token)) { $file = $url->getAbsolutePath(); diff --git a/plugins/hsl-to-hex.php b/plugins/hsl-to-hex.php index 7d62b41..c2515cd 100644 --- a/plugins/hsl-to-hex.php +++ b/plugins/hsl-to-hex.php @@ -34,8 +34,8 @@ function csscrush__hsl_to_hex (CssCrush_Rule $rule) { if (! $declaration->skip && isset($declaration->functions['hsl'])) { while (preg_match($hsl_patt, $declaration->value, $m)) { $token = $m[1]; - $color = new CssCrush_Color('hsl' . CssCrush::$process->fetchToken($token)); - CssCrush::$process->releaseToken($token); + $color = new CssCrush_Color('hsl' . CssCrush::$process->tokens->get($token)); + CssCrush::$process->tokens->release($token); $declaration->value = str_replace($m[0], $color->getHex(), $declaration->value); } } diff --git a/plugins/svg.php b/plugins/svg.php index 747e2b4..b37a3b5 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -504,7 +504,7 @@ function csscrush__svg_text ($element) { 'text' => '', ); - $text = CssCrush::$process->restoreTokens($element->data['text'], 's'); + $text = CssCrush::$process->tokens->restore($element->data['text'], 's'); // Remove open and close quotes. $text = substr($text, 1, strlen($text) - 2); @@ -646,9 +646,9 @@ function csscrush__svg_preprocess ($element) { $value = $element->attrs[$point_data_attr]; - if (CssCrush::$process->isToken($value, 's')) { + if (CssCrush_Tokens::is($value, 's')) { $element->attrs[$point_data_attr] = - trim(CssCrush::$process->fetchToken($value), '"\'');; + trim(CssCrush::$process->tokens->get($value), '"\'');; } } } @@ -732,8 +732,8 @@ function csscrush__svg_render ($element) { $styles .= $selector . '{' . implode(';', $out) . '}'; } } - $styles = CssCrush::$process->restoreTokens($styles, 'u', true); - $styles = CssCrush::$process->restoreTokens($styles, 's'); + $styles = CssCrush::$process->tokens->restore($styles, 'u', true); + $styles = CssCrush::$process->tokens->restore($styles, 's'); $attrs = CssCrush_Util::htmlAttributes($element->attrs); $svg_attrs = CssCrush_Util::htmlAttributes($element->svg_attrs); @@ -800,7 +800,7 @@ function csscrush__svg_fn_pattern ($input, $element) { CssCrush_Function::parseArgs($input) + array('', '', 0, 0, 0, 0); - $url = CssCrush::$process->fetchToken($url); + $url = CssCrush::$process->tokens->get($url); if (! $url) { return ''; } From 4999944ad86257088846bf96cb6c83bfd43f345c Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 7 Jun 2013 19:02:12 +0100 Subject: [PATCH 113/421] Removing misc/css. --- misc/css/font-stacks.css | 36 ------------------------------------ 1 file changed, 36 deletions(-) delete mode 100644 misc/css/font-stacks.css diff --git a/misc/css/font-stacks.css b/misc/css/font-stacks.css deleted file mode 100644 index 1688bf4..0000000 --- a/misc/css/font-stacks.css +++ /dev/null @@ -1,36 +0,0 @@ -/*$ - -Font stacks. - -Sources: - http://cssfontstack.com - http://www.codestyle.org - -*/ -@define { - - /* Serif */ - baskerville: Baskerville, "Baskerville Old Face", "Hoefler Text", Garamond, "Times New Roman", serif; - georgia: Georgia, Times, "Times New Roman", serif; - palatino: Palatino, "Palatino Linotype", "Palatino LT STD", "Book Antiqua", Georgia, serif; - times: TimesNewRoman, "Times New Roman", Times, serif; - - /* Sans-serif */ - arial: Arial, Helvetica, sans-serif; - arial-narrow: "Arial Narrow", Arial, sans-serif; - gill-sans: "Gill Sans", "Gill Sans MT", Calibri, sans-serif; - helvetica: "Helvetica Neue", Helvetica, Arial, sans-serif; - lucida: "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Verdana, sans-serif; - trebuchet-ms: "Trebuchet MS", "Lucida Sans Unicode", "Lucida Grande", "Lucida Sans", Arial, sans-serif; - verdana: Verdana, Geneva, sans-serif; - - /* Monospace */ - consolas: Consolas, Monaco, monospace; - courier: "Courier New", Courier, monospace; - monaco: Monaco, Consolas, "Lucida Console", monospace; - - /* Generic defaults */ - serif: $( times ); - sans-serif: $( arial ); - monospace: $( courier ); -} From d73eded8bdfd2715435a0740e843584e534276e2 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 8 Jun 2013 19:31:32 +0100 Subject: [PATCH 114/421] Docs formatting. --- README.md | 3 ++- plugins/hsl-to-hex.php | 3 +-- plugins/svg.php | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 63b417d..3efd682 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ CSS-Crush --------- +Logo -**CSS without the mess** — CSS-Crush is a CSS preprocessor designed to create a modern, uncluttered and standards based CSS authoring environment. +CSS without the mess — CSS-Crush is a CSS preprocessor designed to create a modern, uncluttered and standards based CSS authoring environment. See the following overview for code examples and description of main features: http://the-echoplex.net/csscrush diff --git a/plugins/hsl-to-hex.php b/plugins/hsl-to-hex.php index c2515cd..8d918bc 100644 --- a/plugins/hsl-to-hex.php +++ b/plugins/hsl-to-hex.php @@ -34,8 +34,7 @@ function csscrush__hsl_to_hex (CssCrush_Rule $rule) { if (! $declaration->skip && isset($declaration->functions['hsl'])) { while (preg_match($hsl_patt, $declaration->value, $m)) { $token = $m[1]; - $color = new CssCrush_Color('hsl' . CssCrush::$process->tokens->get($token)); - CssCrush::$process->tokens->release($token); + $color = new CssCrush_Color('hsl' . CssCrush::$process->tokens->pop($token)); $declaration->value = str_replace($m[0], $color->getHex(), $declaration->value); } } diff --git a/plugins/svg.php b/plugins/svg.php index b37a3b5..4c31d8f 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -35,7 +35,7 @@ * margin: 20; * fill: svg-radial-gradient(at top right, gold 50%, red); * drop-shadow: 2 2 0 rgba(0,0,0,1); - * } + * } * * @example * @@ -48,7 +48,7 @@ * margin: 20; * fill: pattern(data-uri(kitten.jpg), scale(1) translate(-100 0)); * fill-opacity: .8; - * } + * } * * @known-issues * From a6bf4d90a8658bc83797cfd4fe26600ca16ef4ad Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 8 Jun 2013 20:15:49 +0100 Subject: [PATCH 115/421] Refactored to move more token related code into Tokens.php. Some internal renaming for consistancy. --- lib/CssCrush/Importer.php | 48 ++------------------- lib/CssCrush/Process.php | 90 +++++++++++++++++++-------------------- lib/CssCrush/Regex.php | 2 +- lib/CssCrush/Tokens.php | 44 +++++++++++++++++++ plugins/canvas.php | 6 +-- plugins/px2em.php | 8 ++-- plugins/rem.php | 2 +- plugins/svg.php | 6 +-- 8 files changed, 104 insertions(+), 102 deletions(-) diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index e5e6f27..a6300da 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -182,13 +182,11 @@ static protected function prepareForStream (&$str) { $regex = CssCrush_Regex::$patt; $process = CssCrush::$process; + $tokens = $process->tokens; // Convert all end-of-lines to unix style. - $str = preg_replace('~\r\n?~', "\n", $str); - // Tokenize all comments and string literals. - $str = preg_replace_callback($regex->commentAndString, - array('self', 'cb_extractCommentAndString'), $str); + $str = $tokens->captureCommentAndString(preg_replace('~\r\n?~', "\n", $str)); // Normalize double-colon pseudo elements for backwards compatability. $str = preg_replace('~::(after|before|first-(?:letter|line))~iS', ':$1', $str); @@ -199,7 +197,7 @@ static protected function prepareForStream (&$str) if (! $process->charset) { // Keep track of newlines for line tracing. $replace = str_repeat("\n", substr_count($m[0], "\n")); - $process->charset = trim($process->tokens->get($m[1]), '"\''); + $process->charset = trim($tokens->get($m[1]), '"\''); } $str = preg_replace($regex->charset, $replace, $str); } @@ -233,49 +231,11 @@ static protected function prepareForStream (&$str) // Strip unneeded whitespace. $str = CssCrush_Util::normalizeWhiteSpace($str); - $str = $process->tokens->captureUrls($str); + $str = $tokens->captureUrls($str); return true; } - static protected function cb_extractCommentAndString ($match) - { - $full_match = $match[0]; - $process = CssCrush::$process; - - // We return the newlines to maintain line numbering when tracing. - $newlines = str_repeat("\n", substr_count($full_match, "\n")); - - if (strpos($full_match, '/*') === 0) { - - // Bail without storing comment if output is minified or a private comment. - if ( - $process->minifyOutput || - strpos($full_match, '/*$') === 0 - ) { - return $newlines; - } - - // Fix broken comments as they will break any subsquent - // imported files that are inlined. - if (! preg_match('~\*/$~', $full_match)) { - $full_match .= '*/'; - } - $label = $process->tokens->add($full_match, 'c'); - } - else { - - // Fix broken strings as they will break any subsquent - // imported files that are inlined. - if ($full_match[0] !== $full_match[strlen($full_match)-1]) { - $full_match .= $full_match[0]; - } - $label = $process->tokens->add($full_match, 's'); - } - - return $newlines . $label; - } - static protected function addTracingStubs (&$str) { $selector_patt = '~ (^|;|\})+ ([^;{}]+) (\{) ~xmS'; diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 6c82ec9..3b37f9f 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -30,7 +30,7 @@ public function __construct ($options) $this->stat = array(); $this->charset = null; $this->currentFile = null; - $this->variables = array(); + $this->vars = array(); $this->misc = new stdClass(); $this->input = new stdClass(); $this->output = new stdClass(); @@ -75,7 +75,7 @@ public function release () { unset( $this->tokens, - $this->variables, + $this->vars, $this->mixins, $this->references, $this->misc, @@ -420,34 +420,34 @@ protected function filterPlugins () ############################# # Variables. - protected function calculateVariables () + protected function calculateVars () { $config = CssCrush::$config; $regex = CssCrush_Regex::$patt; $option_vars = $this->options->vars; - $this->stream->pregReplaceCallback($regex->variables, - array('CssCrush_Process', 'cb_extractVariables')); + $this->stream->pregReplaceCallback($regex->vars, + array('CssCrush_Process', 'cb_captureVars')); // In-file variables override global variables. - $this->variables = array_merge($config->vars, $this->variables); + $this->vars = array_merge($config->vars, $this->vars); // Runtime variables override in-file variables. if (! empty($option_vars)) { - $this->variables = array_merge($this->variables, $option_vars); + $this->vars = array_merge($this->vars, $option_vars); } // Add state variables. - // $this->variables = array( + // $this->vars = array( // 'input-path' => $this->input->dirUrl, // 'output-path' => $this->output->dirUrl, - //) + $this->variables; + //) + $this->vars; // Place variables referenced inside variables. Excecute custom functions. - foreach ($this->variables as $name => &$value) { + foreach ($this->vars as $name => &$value) { // Referenced variables. - $value = preg_replace_callback($regex->varFunction, array('self', 'cb_placeVariables'), $value); + $value = preg_replace_callback($regex->varFunction, array('self', 'cb_placeVars'), $value); // Variable values can be escaped from function parsing with a tilde prefix. if (strpos($value, '~') !== 0) { @@ -456,40 +456,40 @@ protected function calculateVariables () } } - protected function placeAllVariables () + protected function placeAllVars () { // Place variables in main stream. - self::placeVariables($this->stream->raw); + self::placeVars($this->stream->raw); $raw_tokens =& $this->tokens->store; // Repeat above steps for variables embedded in string tokens. foreach ($raw_tokens->s as $label => &$value) { - self::placeVariables($value); + self::placeVars($value); } // Repeat above steps for variables embedded in URL tokens. foreach ($raw_tokens->u as $label => $url) { - if (! $url->isData && self::placeVariables($url->value)) { + if (! $url->isData && self::placeVars($url->value)) { // Re-evaluate $url->value if anything has been interpolated. $url->evaluate(); } } } - static protected function placeVariables (&$value) + static protected function placeVars (&$value) { $regex = CssCrush_Regex::$patt; // Variables with no default value. $value = preg_replace_callback($regex->varFunction, - array('CssCrush_Process', 'cb_placeVariables'), $value, -1, $count); + array('CssCrush_Process', 'cb_placeVars'), $value, -1, $count); if (strpos($value, '$(') !== false) { // Variables with default value. CssCrush_Function::executeOnString($value, '~(\$)\(~', - array('$' => array('CssCrush_Process', 'cb_placeVariablesWithDefault'))); + array('$' => array('CssCrush_Process', 'cb_placeVarsWithDefault'))); // Assume at least 1 replace. $count = 1; @@ -499,33 +499,28 @@ static protected function placeVariables (&$value) return $count; } - static public function cb_extractVariables ($m) + static public function cb_captureVars ($m) { - $regex = CssCrush_Regex::$patt; - - // Strip comment markers. - $block = trim(CssCrush_Util::stripCommentTokens($m[1])); - - CssCrush::$process->variables = + CssCrush::$process->vars = array_merge( - CssCrush::$process->variables, - CssCrush_Rule::parseBlock($block, array('keyed' => true, 'ignore_directives' => false))); + CssCrush::$process->vars, + CssCrush_Rule::parseBlock($m[1], array('keyed' => true, 'ignore_directives' => true))); } - static protected function cb_placeVariables ($m) + static protected function cb_placeVars ($m) { - $variable_name = $m[1]; - if (isset(CssCrush::$process->variables[$variable_name])) { - return CssCrush::$process->variables[$variable_name]; + $var_name = $m[1]; + if (isset(CssCrush::$process->vars[$var_name])) { + return CssCrush::$process->vars[$var_name]; } } - static public function cb_placeVariablesWithDefault ($raw_arg) + static public function cb_placeVarsWithDefault ($raw_arg) { list($name, $default_value) = CssCrush_Function::parseArgsSimple($raw_arg); - if (isset(CssCrush::$process->variables[$name])) { - return CssCrush::$process->variables[$name]; + if (isset(CssCrush::$process->vars[$name])) { + return CssCrush::$process->vars[$name]; } else { return $default_value; @@ -552,7 +547,7 @@ protected function resolveIfDefines () $negate = $match[1][1] != -1; $name = $match[2][0]; - $name_defined = isset($this->variables[$name]); + $name_defined = isset($this->vars[$name]); if (! $negate && $name_defined || $negate && ! $name_defined) { // Test resolved true so include the innards. @@ -569,7 +564,7 @@ protected function resolveIfDefines () ############################# # Mixins. - protected function extractMixins () + protected function captureMixins () { static $callback; if (! $callback) { @@ -675,9 +670,9 @@ protected function resolveFragments () ############################# # Rules. - public function extractRules () + public function captureRules () { - $this->stream->pregReplaceCallback(CssCrush_Regex::$patt->rule, array('CssCrush_Process', 'cb_extractRules')); + $this->stream->pregReplaceCallback(CssCrush_Regex::$patt->rule, array('CssCrush_Process', 'cb_captureRules')); } protected function processRules () @@ -711,7 +706,7 @@ protected function processRules () } } - static public function cb_extractRules ($m) + static public function cb_captureRules ($m) { $rule = (object) array(); $rule->selector_raw = trim($m[1]); @@ -1018,27 +1013,30 @@ public function compile ($io_context = 'file') $this->stream = new CssCrush_Stream(CssCrush_Importer::hostfile($this->input)); // Extract and calculate variables. - $this->calculateVariables(); + $this->calculateVars(); // Place variables. - $this->placeAllVariables(); + $this->placeAllVars(); // Resolve @ifdefine blocks. $this->resolveIfDefines(); + // Capture phase 1 hook: After all vars have resolved. + CssCrush_Hook::run('capture_phase1', $this); + // Get selector aliases. $this->resolveSelectorAliases(); // Pull out @mixin definitions. - $this->extractMixins(); + $this->captureMixins(); // Pull out @fragment blocks, and invoke. $this->resolveFragments(); - // Run extract phase hooks. - CssCrush_Hook::run('process_extract', $this); + // Capture phase 2 hook: After most built-in directives have resolved. + CssCrush_Hook::run('capture_phase2', $this); - // Adjust meta characters so we can extract the rules cleanly. + // Adjust meta characters so we can capture the rules cleanly. $this->stream->replaceHash(array( '@' => "\n@", '}' => "}\n", @@ -1047,7 +1045,7 @@ public function compile ($io_context = 'file') ))->prepend("\n"); // Parse rules. - $this->extractRules(); + $this->captureRules(); // csscrush::log(array_keys($this->references)); // Process @in blocks. diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php index 430ea3b..052d446 100644 --- a/lib/CssCrush/Regex.php +++ b/lib/CssCrush/Regex.php @@ -58,7 +58,7 @@ static public function init () // @-rules. $patt->import = CssCrush_Regex::create('@import\s+()\s?([^;]*);', 'iS'); $patt->charset = CssCrush_Regex::create('@charset\s+()\s*;', 'iS'); - $patt->variables = CssCrush_Regex::create('@(?:define|variables) *\{ *(.*?) *\};?', 'iS'); + $patt->vars = CssCrush_Regex::create('@define *\{ *(.*?) *\};?', 'iS'); $patt->mixin = CssCrush_Regex::create('@mixin +() *\{ *(.*?) *\};?', 'iS'); $patt->abstract = CssCrush_Regex::create('^@abstract +()', 'i'); $patt->ifDefine = CssCrush_Regex::create('@ifdefine +(not +)?() *\{', 'iS'); diff --git a/lib/CssCrush/Tokens.php b/lib/CssCrush/Tokens.php index f5596e4..7370ab8 100644 --- a/lib/CssCrush/Tokens.php +++ b/lib/CssCrush/Tokens.php @@ -168,6 +168,50 @@ public function captureUrls ($str) return $str; } + public function captureCommentAndString ($str) + { + return preg_replace_callback(CssCrush_Regex::$patt->commentAndString, + array('self', 'cb_captureCommentAndString'), $str); + } + + protected function cb_captureCommentAndString ($match) + { + $full_match = $match[0]; + $process = CssCrush::$process; + + // We return the newlines to maintain line numbering when tracing. + $newlines = str_repeat("\n", substr_count($full_match, "\n")); + + if (strpos($full_match, '/*') === 0) { + + // Bail without storing comment if output is minified or a private comment. + if ( + $process->minifyOutput || + strpos($full_match, '/*$') === 0 + ) { + return $newlines; + } + + // Fix broken comments as they will break any subsquent + // imported files that are inlined. + if (! preg_match('~\*/$~', $full_match)) { + $full_match .= '*/'; + } + $label = $process->tokens->add($full_match, 'c'); + } + else { + + // Fix broken strings as they will break any subsquent + // imported files that are inlined. + if ($full_match[0] !== $full_match[strlen($full_match)-1]) { + $full_match .= $full_match[0]; + } + $label = $process->tokens->add($full_match, 's'); + } + + return $newlines . $label; + } + static public function is ($label, $of_type) { if (preg_match('~^\?([a-z])\d+\?$~S', $label, $m)) { diff --git a/plugins/canvas.php b/plugins/canvas.php index e2c1c1b..3f61987 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -54,18 +54,18 @@ )); function csscrush__enable_canvas () { - CssCrush_Hook::add('process_extract', 'csscrush__canvas_extract'); + CssCrush_Hook::add('capture_phase2', 'csscrush__canvas_capture'); CssCrush_Function::register('canvas', 'csscrush__canvas_generator'); CssCrush_Function::register('canvas-data', 'csscrush__canvas_generator'); } function csscrush__disable_canvas () { - CssCrush_Hook::remove('process_extract', 'csscrush__canvas_extract'); + CssCrush_Hook::remove('capture_phase2', 'csscrush__canvas_capture'); CssCrush_Function::deRegister('canvas'); CssCrush_Function::deRegister('canvas-data'); } -function csscrush__canvas_extract ($process) { +function csscrush__canvas_capture ($process) { static $callback, $patt; if (! $callback) { diff --git a/plugins/px2em.php b/plugins/px2em.php index 3ec1547..8c88d71 100644 --- a/plugins/px2em.php +++ b/plugins/px2em.php @@ -34,8 +34,8 @@ function csscrush_fn__px2em ($input) { $base = 16; // Override default base if variable is set. - if (isset(CssCrush::$process->variables['px2em__base'])) { - $base = CssCrush::$process->variables['px2em__base']; + if (isset(CssCrush::$process->vars['px2em__base'])) { + $base = CssCrush::$process->vars['px2em__base']; } return csscrush__px2em($input, 'em', $base); @@ -46,8 +46,8 @@ function csscrush_fn__px2rem ($input) { $base = 16; // Override default base if variable is set. - if (isset(CssCrush::$process->variables['px2rem__base'])) { - $base = CssCrush::$process->variables['px2rem__base']; + if (isset(CssCrush::$process->vars['px2rem__base'])) { + $base = CssCrush::$process->vars['px2rem__base']; } return csscrush__px2em($input, 'rem', $base); diff --git a/plugins/rem.php b/plugins/rem.php index 9d8ef7d..e728f9d 100644 --- a/plugins/rem.php +++ b/plugins/rem.php @@ -61,7 +61,7 @@ function csscrush__rem (CssCrush_Rule $rule) { $modes = array('rem-fallback', 'px-fallback', 'convert'); } - $vars =& CssCrush::$process->variables; + $vars =& CssCrush::$process->vars; // Determine which properties are touched; all, or just font related. $just_font_props = ! isset($vars['rem__all']); diff --git a/plugins/svg.php b/plugins/svg.php index 4c31d8f..35010ae 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -64,13 +64,13 @@ )); function csscrush__enable_svg () { - CssCrush_Hook::add('process_extract', 'csscrush__svg_extract'); + CssCrush_Hook::add('capture_phase2', 'csscrush__svg_capture'); CssCrush_Function::register('svg', 'csscrush_fn__svg'); CssCrush_Function::register('svg-data', 'csscrush_fn__svg_data'); } function csscrush__disable_svg () { - CssCrush_Hook::remove('process_extract', 'csscrush__svg_extract'); + CssCrush_Hook::remove('capture_phase2', 'csscrush__svg_capture'); CssCrush_Function::deRegister('svg'); CssCrush_Function::deRegister('svg-data'); } @@ -85,7 +85,7 @@ function csscrush_fn__svg_data ($input) { return csscrush__svg_generator($input, 'svg-data'); } -function csscrush__svg_extract ($process) { +function csscrush__svg_capture ($process) { static $callback, $patt; if (! $callback) { From a7d956fb2dbf4cecf460c79121548a6be2560394 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 10 Jun 2013 13:12:09 +0100 Subject: [PATCH 116/421] Removed unnecessary querystring timestamping on canvas generated images. Fixed typo. --- lib/CssCrush/Tokens.php | 6 +++--- plugins/canvas.php | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/CssCrush/Tokens.php b/lib/CssCrush/Tokens.php index 7370ab8..2c8d3c2 100644 --- a/lib/CssCrush/Tokens.php +++ b/lib/CssCrush/Tokens.php @@ -68,9 +68,9 @@ public function restore ($str, $type, $release = false) case 'u': // Currently this always releases URLs // may need to refactor later. - static $url_restore_callback; - if (! $url_restore_callback) { - $url_restore_callback = create_function('$m', ' + static $url_revert_callback; + if (! $url_revert_callback) { + $url_revert_callback = create_function('$m', ' $url = CssCrush::$process->tokens->pop($m[0]); return $url ? $url->getOriginalValue() : \'\'; '); diff --git a/plugins/canvas.php b/plugins/canvas.php index 3f61987..16909bb 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -231,7 +231,7 @@ function csscrush__canvas_generator ($input, $context) { } // Write to the same directory as the output css. - $url = new CssCrush_Url("$generated_filename?" . time()); + $url = new CssCrush_Url($generated_filename); $url->noRewrite = true; } // Or create data uri. From 0c878644f18f63381e63d2cd7144af12837c3cb2 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 19 Jun 2013 09:34:59 +0100 Subject: [PATCH 117/421] Command utility now has proper support for the trace option. Simplified the `csscrush_stat()` function signature; `compile_time` stat is now always available regardless of set options. Replaced instances of core static methods (`CssCrush::*`) with their higher level function equivalents where possible. --- cli.php | 75 ++++++++++++++++++++++++++----------------- lib/CssCrush/Core.php | 32 ++++++++---------- lib/CssCrush/Util.php | 33 ++++--------------- lib/functions.php | 4 +-- 4 files changed, 69 insertions(+), 75 deletions(-) diff --git a/cli.php b/cli.php index ba3be7e..261f604 100755 --- a/cli.php +++ b/cli.php @@ -48,6 +48,7 @@ $optional_value_opts = array( 'b|boilerplate', // Boilerplate. + 'trace', // Debug info. ); $flag_opts = array( @@ -56,7 +57,6 @@ 'list', // List plugins. 'help', // Display help. 'version', // Display version. - 'trace', // Output sass tracing stubs. ); // Create option strings for getopt(). @@ -95,10 +95,10 @@ $args->list = isset($opts['l']) ?: isset($opts['list']); $args->help = isset($opts['h']) ?: isset($opts['help']); $args->version = isset($opts['version']); -$args->trace = isset($opts['trace']); // Arguments that optionally accept a single value. $args->boilerplate = pick($opts, 'b', 'boilerplate'); +$args->trace = pick($opts, 'trace'); // Arguments that require a single value. $args->formatter = pick($opts, 'formatter'); @@ -127,7 +127,7 @@ if ($args->version) { - stdout('CSS-Crush ' . CssCrush::$config->version); + stdout('CSS-Crush ' . csscrush_version()); exit(STATUS_OK); } @@ -235,7 +235,6 @@ exit(STATUS_ERROR); } - ################################################################## ## Set process options. @@ -247,36 +246,29 @@ $process_opts['formatter'] = $args->formatter; } -if ($args->formatter) { - $process_opts['formatter'] = $args->formatter; -} - -// Newlines arg. if ($args->newlines) { $process_opts['newlines'] = $args->newlines; } -// Enable plugin args. if ($args->enable_plugins) { $process_opts['enable'] = parse_list($args->enable_plugins); } -// Disable plugin args. if ($args->disable_plugins) { $process_opts['disable'] = parse_list($args->disable_plugins); } -// Tracing arg. if ($args->trace) { - $process_opts['trace'] = true; + if (is_string($args->trace)) { + $args->trace = (array) $args->trace; + } + $process_opts['trace'] = is_array($args->trace) ? parse_list($args->trace) : true; } -// Vendor target arg. if ($args->vendor_target) { $process_opts['vendor_target'] = parse_list($args->vendor_target); } -// Variables args. if ($args->vars) { parse_str($args->vars, $in_vars); $process_opts['vars'] = $in_vars; @@ -309,20 +301,30 @@ while (true) { - $created_file = CssCrush::file($args->input_file, $process_opts); + $created_file = csscrush_file($args->input_file, $process_opts); + $stats = csscrush_stat(); - if (CssCrush::$process->errors) { - stderr(CssCrush::$process->errors); + if ($stats['errors']) { + stderr($stats['errors']); exit(STATUS_ERROR); } + if (is_array($args->trace) && $stats['compile_time'] > 0) { + stdout(format_stats($stats)); + } + sleep(1); } } else { - $output = CssCrush::string($input, $process_opts); + $output = csscrush_string($input, $process_opts); + $stats = csscrush_stat(); + + if ($stats['errors']) { + stderr($stats['errors']); + } if ($args->output_file) { @@ -334,15 +336,15 @@ exit(STATUS_ERROR); } } - else { - if (CssCrush::$process->errors) { - stderr(CssCrush::$process->errors); - } - stdout($output); - - exit(STATUS_OK); + if (is_array($args->trace)) { + // Use stderror for stats to preserve stdout. + stderr(format_stats($stats) . PHP_EOL); } + + stdout($output); + + exit(STATUS_OK); } @@ -372,16 +374,31 @@ function get_stdin_contents () { } function parse_list (array $option) { + $out = array(); foreach ($option as $arg) { - foreach (preg_split('~\s*,\s*~', $arg) as $item) { - $out[] = $item; + if (is_string($arg)) { + foreach (preg_split('~\s*,\s*~', $arg) as $item) { + $out[] = $item; + } + } + else { + $out[] = $arg; } } - return $out; } +function format_stats ($stats) { + $out = array(); + foreach ($stats as $name => $value) { + if (is_scalar($value)) { + $out[] = "$name: $value"; + } + } + return implode(PHP_EOL, $out); +} + function pick (array &$arr) { $args = func_get_args(); diff --git a/lib/CssCrush/Core.php b/lib/CssCrush/Core.php index 3637c1b..1c444fe 100644 --- a/lib/CssCrush/Core.php +++ b/lib/CssCrush/Core.php @@ -476,27 +476,22 @@ static public function clearCache ($dir = '') /** * Get debug info. * Depends on arguments passed to the trace option. - * - * @param string $name Name of stat to retrieve. Leave blank to retrieve all. */ - static public function stat ($name = null) + static public function stat () { $process = CssCrush::$process; - $stat = $process->stat; + $stats = $process->stat; // Get logged errors as late as possible. - if (in_array('errors', $process->options->trace) && (! $name || 'errors' === $name)) { - $stat['errors'] = $process->errors; - } - - if ($name && array_key_exists($name, $stat)) { - return array($name => $stat[$name]); - } + $stats['errors'] = $process->errors; + $stats += array( + 'compile_time' => 0 + ); // Lose stats that are only useful internally. - unset($stat['compile_start_time']); + unset($stats['compile_start_time']); - return $stat; + return $stats; } @@ -580,6 +575,12 @@ static public function runStat ($name) $process = CssCrush::$process; $trace = $process->options->trace; + if ($name == 'compile_time') { + $time = microtime(true); + $process->stat['compile_time'] = $time - $process->stat['compile_start_time']; + return; + } + if (! $trace || ! in_array($name, $trace)) { return; } @@ -598,11 +599,6 @@ static public function runStat ($name) case 'rule_count': $process->stat['rule_count'] = count($all_rules); break; - - case 'compile_time': - $time = microtime(true); - $process->stat['compile_time'] = $time - $process->stat['compile_start_time']; - break; } } } diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php index 284f92d..a503c2b 100644 --- a/lib/CssCrush/Util.php +++ b/lib/CssCrush/Util.php @@ -80,26 +80,16 @@ static public function normalizeWhiteSpace ($str) return preg_replace($find, $replace, $str); } - static public function splitDelimList ($str, $delim = ',', $trim = true) + static public function splitDelimList ($str, $delim = ',') { - $do_preg_split = strlen($delim) > 1 ? true : false; + $do_preg_split = strlen($delim) > 1; + $str = trim($str); if (! $do_preg_split && strpos($str, $delim) === false) { - if ($trim) { - $str = trim($str); - } return strlen($str) ? array($str) : array(); } - if (strpos($str, '(') !== false) { - $match_count - = preg_match_all(CssCrush_Regex::$patt->balancedParens, $str, $matches); - } - else { - $match_count = 0; - } - - if ($match_count) { + if ($match_count = preg_match_all(CssCrush_Regex::$patt->balancedParens, $str, $matches)) { $keys = array(); foreach ($matches[0] as $index => &$value) { $keys[] = "?$index?"; @@ -107,12 +97,7 @@ static public function splitDelimList ($str, $delim = ',', $trim = true) $str = str_replace($matches[0], $keys, $str); } - if ($do_preg_split) { - $list = preg_split('~' . $delim . '~', $str); - } - else { - $list = explode($delim, $str); - } + $list = $do_preg_split ? preg_split('~' . $delim . '~', $str) : explode($delim, $str); if ($match_count) { foreach ($list as &$value) { @@ -120,12 +105,8 @@ static public function splitDelimList ($str, $delim = ',', $trim = true) } } - if ($trim) { - // Trim items and remove empty strings. - $list = array_filter(array_map('trim', $list), 'strlen'); - } - - return $list; + // Trim items and remove empty strings before returning. + return array_filter(array_map('trim', $list), 'strlen'); } static public function getLinkBetweenDirs ($dir1, $dir2) diff --git a/lib/functions.php b/lib/functions.php index a0bcb90..19548f6 100644 --- a/lib/functions.php +++ b/lib/functions.php @@ -28,8 +28,8 @@ function csscrush_clearcache ($dir = '') { return CssCrush::clearcache($dir); } -function csscrush_stat ($name = null) { - return CssCrush::stat($name); +function csscrush_stat () { + return CssCrush::stat(); } function csscrush_version () { From a3e963008c32c734fe4ac6e2c731ea55ae2c7b51 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 4 Jun 2013 13:12:34 +0100 Subject: [PATCH 118/421] Updated readme. Made changelog headings more discreet. --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 3efd682..9724ff9 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,3 @@ -CSS-Crush ---------- Logo CSS without the mess — CSS-Crush is a CSS preprocessor designed to create a modern, uncluttered and standards based CSS authoring environment. From d94ba5f63c42bb6f0dad6c6c0073ba2ed0b8d153 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 26 Jun 2013 14:55:50 +0100 Subject: [PATCH 119/421] Refactoring for later source mapping integration. --- lib/CssCrush/Importer.php | 20 +++++++++------ lib/CssCrush/Tokens.php | 51 ++++++++++++++++++++++++--------------- 2 files changed, 45 insertions(+), 26 deletions(-) diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index a6300da..c9d3e7b 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -185,8 +185,10 @@ static protected function prepareForStream (&$str) $tokens = $process->tokens; // Convert all end-of-lines to unix style. - // Tokenize all comments and string literals. - $str = $tokens->captureCommentAndString(preg_replace('~\r\n?~', "\n", $str)); + $str = preg_replace('~\r\n?~', "\n", $str); + + // Capture all comments and string literals. + $str = $tokens->captureCommentAndString($str); // Normalize double-colon pseudo elements for backwards compatability. $str = preg_replace('~::(after|before|first-(?:letter|line))~iS', ':$1', $str); @@ -195,7 +197,7 @@ static protected function prepareForStream (&$str) if (preg_match($regex->charset, $str, $m)) { $replace = ''; if (! $process->charset) { - // Keep track of newlines for line tracing. + // Keep track of newlines for line numbering. $replace = str_repeat("\n", substr_count($m[0], "\n")); $process->charset = trim($tokens->get($m[1]), '"\''); } @@ -223,6 +225,8 @@ static protected function prepareForStream (&$str) return false; } + $str = $tokens->captureUrls($str); + // Optionally add tracing stubs. if ($process->addTracingStubs) { self::addTracingStubs($str); @@ -231,15 +235,17 @@ static protected function prepareForStream (&$str) // Strip unneeded whitespace. $str = CssCrush_Util::normalizeWhiteSpace($str); - $str = $tokens->captureUrls($str); - return true; } static protected function addTracingStubs (&$str) { + static $token_or_whitespace; + if (! $token_or_whitespace) { + $token_or_whitespace = CssCrush_Regex::create('(\s*\s*|\s+)', 'S'); + } + $selector_patt = '~ (^|;|\})+ ([^;{}]+) (\{) ~xmS'; - $token_or_whitespace = '~(\s*\?c\d+\?\s*|\s+)~S'; $matches = CssCrush_Regex::matchAll($selector_patt, $str); @@ -267,9 +273,9 @@ static protected function addTracingStubs (&$str) // Count line breaks between the start of stream and // the matched selector to get the line number. - $selector_index = $full_match_start + strlen($before); $line_num = 1; $str_before = ""; + $selector_index = $full_match_start + strlen($before); if ($selector_index) { $str_before = substr($str, 0, $selector_index); $line_num = substr_count($str_before, "\n") + 1; diff --git a/lib/CssCrush/Tokens.php b/lib/CssCrush/Tokens.php index 2c8d3c2..73095eb 100644 --- a/lib/CssCrush/Tokens.php +++ b/lib/CssCrush/Tokens.php @@ -134,34 +134,47 @@ public function captureUrls ($str) { static $url_patt; if (! $url_patt) { - $url_patt = CssCrush_Regex::create('@import +()|(url|data-uri)\(', 'iS'); + $url_patt = CssCrush_Regex::create('@import\s+()|(url|data-uri)\(', 'iS'); } - $offset = 0; - while (preg_match($url_patt, $str, $outer_m, PREG_OFFSET_CAPTURE, $offset)) { + $count = preg_match_all($url_patt, $str, $m, PREG_OFFSET_CAPTURE); + while ($count--) { - $outer_offset = $outer_m[0][1]; - $is_import_url = ! isset($outer_m[2]); + // Full match. + $outer0 = $m[0][$count]; - if ($is_import_url) { - $url = new CssCrush_Url($outer_m[1][0]); - $str = str_replace($outer_m[1][0], $url->label, $str); + // @import directive position. + $outer1 = $m[1][$count]; + + // URL function position. + $outer2 = is_array($m[2][$count]) ? $m[2][$count] : null; + + list($outer_text, $outer_offset) = $outer0; + $newlines = ''; + + // An @import directive. + if (! $outer2) { + + if (strpos($outer_text, "\n") !== false) { + $newlines = str_repeat("\n", substr_count($outer_text, "\n")); + } + $url = new CssCrush_Url(trim($outer1[0])); + $str = str_replace($outer1[0], $url->label . $newlines, $str); } - // Match parenthesis if not a string token. + // A URL function - match closing parens. elseif ( preg_match(CssCrush_Regex::$patt->balancedParens, $str, $inner_m, PREG_OFFSET_CAPTURE, $outer_offset) ) { - $url = new CssCrush_Url($inner_m[1][0]); - $func_name = strtolower($outer_m[2][0]); - $url->convertToData = 'data-uri' === $func_name; - $str = substr_replace($str, $url->label, $outer_offset, - strlen($func_name) + strlen($inner_m[0][0])); - } - // If brackets cannot be matched, skip over the original match. - else { - $offset += strlen($outer_m[0][0]); + $inner_text = $inner_m[0][0]; + if (strpos($inner_text, "\n") !== false) { + $newlines = str_repeat("\n", substr_count($inner_text, "\n")); + } + $url = new CssCrush_Url(trim($inner_m[1][0])); + $func_name = strtolower($outer2[0]); + $url->convertToData = 'data-uri' === $func_name; + $str = substr_replace($str, $url->label . $newlines, $outer0[1], strlen($func_name) + strlen($inner_text)); } } @@ -179,7 +192,7 @@ protected function cb_captureCommentAndString ($match) $full_match = $match[0]; $process = CssCrush::$process; - // We return the newlines to maintain line numbering when tracing. + // We return the newline count to keep track of line numbering. $newlines = str_repeat("\n", substr_count($full_match, "\n")); if (strpos($full_match, '/*') === 0) { From 650759822e3a4cb7316db0a0221cfb29064c2644 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 27 Jun 2013 09:30:47 +0100 Subject: [PATCH 120/421] Speed test using base 36 encoded token ids. --- lib/CssCrush/Regex.php | 14 +++++++------- lib/CssCrush/Tokens.php | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php index 052d446..effc618 100644 --- a/lib/CssCrush/Regex.php +++ b/lib/CssCrush/Regex.php @@ -28,13 +28,13 @@ static public function init () $classes->color_hex = '#[[:xdigit:]]{3}(?:[[:xdigit:]]{3})?'; // Tokens. - $classes->c_token = '\?c\d+\?'; // Comments. - $classes->s_token = '\?s\d+\?'; // Strings. - $classes->r_token = '\?r\d+\?'; // Rules. - $classes->p_token = '\?p\d+\?'; // Parens. - $classes->u_token = '\?u\d+\?'; // URLs. - $classes->t_token = '\?t\d+\?'; // Traces. - $classes->a_token = '\?a(\d+)\?'; // Args. + $classes->c_token = '\?c[0-9a-z]+\?'; // Comments. + $classes->s_token = '\?s[0-9a-z]+\?'; // Strings. + $classes->r_token = '\?r[0-9a-z]+\?'; // Rules. + $classes->p_token = '\?p[0-9a-z]+\?'; // Parens. + $classes->u_token = '\?u[0-9a-z]+\?'; // URLs. + $classes->t_token = '\?t[0-9a-z]+\?'; // Traces. + $classes->a_token = '\?a([0-9a-z]+)\?'; // Args. // Boundries. $classes->LB = '(?uid; + $counter = base_convert(++$this->uid, 10, 36); return "?$type$counter?"; } @@ -227,7 +227,7 @@ protected function cb_captureCommentAndString ($match) static public function is ($label, $of_type) { - if (preg_match('~^\?([a-z])\d+\?$~S', $label, $m)) { + if (preg_match('~^\?([a-z])[0-9a-z]+\?$~S', $label, $m)) { return $of_type ? ($of_type === $m[1]) : true; } return false; From 4767128115e9355ccf8c9d5653f0998ac1dd5040 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 27 Jun 2013 20:20:17 +0100 Subject: [PATCH 121/421] Formatter callbacks simplified. --- lib/CssCrush/Rule.php | 15 +++++++++++---- misc/formatters.php | 27 ++++----------------------- 2 files changed, 15 insertions(+), 27 deletions(-) diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php index 2935ac9..0896bea 100644 --- a/lib/CssCrush/Rule.php +++ b/lib/CssCrush/Rule.php @@ -145,12 +145,19 @@ public function __toString () } else { - if ($formatter = $process->ruleFormatter) { - return $formatter($this); + $EOL = CssCrush::$process->newline; + $formatter = $process->ruleFormatter ? + $process->ruleFormatter : 'csscrush__fmtr_block'; + + if ($comments = implode('', $this->comments)) { + $comments = "$EOL$comments"; + } + + if ($stub = $this->tracingStub) { + $stub .= $EOL; } - // Default block formatter. - return csscrush__fmtr_block($this); + return "$comments$stub{$formatter($this)}"; } } diff --git a/misc/formatters.php b/misc/formatters.php index 21cbb1b..bc5c714 100644 --- a/misc/formatters.php +++ b/misc/formatters.php @@ -13,53 +13,34 @@ function csscrush__fmtr_single ($rule) { $EOL = CssCrush::$process->newline; - if ($stub = $rule->tracingStub) { - $stub .= $EOL; - } - $comments = implode('', $rule->comments); - if ($comments) { - $comments = "$EOL$comments"; - } $selectors = implode(", ", $rule->selectors); $block = implode("; ", $rule->declarations); - return "$comments$stub$selectors { $block; }$EOL"; + return "$selectors { $block; }$EOL"; } function csscrush__fmtr_padded ($rule, $padding = 40) { $EOL = CssCrush::$process->newline; - if ($stub = $rule->tracingStub) { - $stub .= $EOL; - } - - $comments = implode('', $rule->comments); - if ($comments) { - $comments = "$EOL$comments"; - } $selectors = implode(", ", $rule->selectors); $block = implode("; ", $rule->declarations); if (strlen($selectors) > $padding) { $padding = str_repeat(' ', $padding); - return "$comments$stub$selectors$EOL$padding { $block; }$EOL"; + return "$selectors$EOL$padding { $block; }$EOL"; } else { $selectors = str_pad($selectors, $padding); - return "$comments$stub$selectors { $block; }$EOL"; + return "$selectors { $block; }$EOL"; } } function csscrush__fmtr_block ($rule, $indent = ' ') { $EOL = CssCrush::$process->newline; - if ($stub = $rule->tracingStub) { - $stub .= $EOL; - } - $comments = implode('', $rule->comments); $selectors = implode(",$EOL", $rule->selectors); $block = implode(";$EOL$indent", $rule->declarations); - return "$comments$stub$selectors {{$EOL}$indent$block;$EOL$indent}$EOL$EOL"; + return "$selectors {{$EOL}$indent$block;$EOL$indent}$EOL$EOL"; } From 88364bfeaa86ed91417a803290cd3fdf59c89cdc Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 2 Jul 2013 09:35:07 +0100 Subject: [PATCH 122/421] Big performance win: replaced some instances of str_replace for preg_replace and compile time on large files has almost halved(!). Refactored the tracing stubs with more fine tuned regex. Token types have their own incrementing ids now, should help with making source mapping columns more accurate and keeping the main stream small as possible. --- lib/CssCrush/Importer.php | 72 +++++++++------------------------------ lib/CssCrush/Process.php | 39 +++++++++------------ lib/CssCrush/Regex.php | 19 +++++++++++ lib/CssCrush/Rule.php | 3 +- lib/CssCrush/Stream.php | 15 ++++++++ lib/CssCrush/Tokens.php | 26 +++++++++----- 6 files changed, 86 insertions(+), 88 deletions(-) diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index c9d3e7b..63d4752 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -240,66 +240,28 @@ static protected function prepareForStream (&$str) static protected function addTracingStubs (&$str) { - static $token_or_whitespace; - if (! $token_or_whitespace) { - $token_or_whitespace = CssCrush_Regex::create('(\s*\s*|\s+)', 'S'); - } - - $selector_patt = '~ (^|;|\})+ ([^;{}]+) (\{) ~xmS'; - - $matches = CssCrush_Regex::matchAll($selector_patt, $str); - - // Start from last match and move backwards. - while ($m = array_pop($matches)) { - - // Shortcuts for readability. - list($full_match, $before, $content, $after) = $m; - $full_match_text = $full_match[0]; - $full_match_start = $full_match[1]; - - // The correct before string. - $before = substr($full_match_text, 0, $content[1] - $full_match_start); - - // Split the matched selector part. - $content_parts = preg_split($token_or_whitespace, $content[0], null, - PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); + $count = preg_match_all(CssCrush_Regex::$patt->rule2, $str, $matches, PREG_OFFSET_CAPTURE); - foreach ($content_parts as $part) { + // Get the currently processed file path, and escape it. + $current_file = str_replace(' ', '%20', CssCrush::$process->currentFile); + $current_file = preg_replace('~[^\w-]~', '\\\\$0', $current_file); + $debug_info = '@media -sass-debug-info{filename{font-family:%s}line{font-family:\\00003%d}}'; - if (! preg_match($token_or_whitespace, $part)) { + while ($count--) { - // Match to a valid selector. - if (preg_match('~^([^@]|@(?:abstract|page))~iS', $part)) { + $selector_offset = $matches['selector'][$count][1]; - // Count line breaks between the start of stream and - // the matched selector to get the line number. - $line_num = 1; - $str_before = ""; - $selector_index = $full_match_start + strlen($before); - if ($selector_index) { - $str_before = substr($str, 0, $selector_index); - $line_num = substr_count($str_before, "\n") + 1; - } - - // Get the currently processed file path, and escape it. - $current_file = str_replace(' ', '%20', CssCrush::$process->currentFile); - $current_file = preg_replace('~[^\w-]~', '\\\\$0', $current_file); - - // Splice in tracing stub. - $label = CssCrush::$process->tokens->add("@media -sass-debug-info{filename{font-family:$current_file}line{font-family:\\00003$line_num}}", 't'); - - $str = $str_before . $label . substr($str, $selector_index); - } - else { - // Not matched as a valid selector, move on. - continue 2; - } - break; - } - - // Append split segment to $before. - $before .= $part; + $line = 1; + if ($selector_offset) { + $line = substr_count(substr($str, 0, $selector_offset), "\n") + 1; } + + // Splice in tracing stub. + $str = substr_replace( + $str, + CssCrush::$process->tokens->add(sprintf($debug_info, $current_file, $line), 't'), + $selector_offset, + 0); } } } diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 3b37f9f..705bf21 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -728,6 +728,7 @@ static public function cb_captureRules ($m) } + ############################# # @in blocks. @@ -894,10 +895,11 @@ protected function collate () $options = $this->options; $minify = $options->minify; $regex = CssCrush_Regex::$patt; - $regex_replacements = array(); $EOL = $this->newline; + // Formatting replacements. // Strip newlines added during processing. + $regex_replacements = array(); $regex_replacements['~\n+~'] = ''; if ($minify) { @@ -912,20 +914,19 @@ protected function collate () $regex_replacements['~ ?(@[^;]+\;)~'] = "$1$EOL"; } - // Apply all replacements. + // Apply all formatting replacements. $this->stream->pregReplaceHash($regex_replacements)->lTrim(); // Print out rules. - $this->stream->replaceHash($this->tokens->getOfType('r')); + $this->stream->replaceTokens('r'); CssCrush::runStat('selector_count'); CssCrush::runStat('rule_count'); $this->tokens->releaseOfType('r'); - // Insert parens. - $this->stream->replaceHash($this->tokens->getOfType('p')); - $this->tokens->releaseOfType('p'); + // Restore parens. + $this->stream->replaceTokens('p'); - // Advanced minification parameters. + // If specified, apply advanced minification. if (is_array($minify)) { if (in_array('colors', $minify)) { $this->minifyColors(); @@ -936,28 +937,26 @@ protected function collate () $this->decruft(); if ($minify) { + // Trim whitespace around selector combinators. $this->stream->pregReplace('~ ?([>\~+]) ?~S', '$1'); } else { - $comments = $this->tokens->getOfType('c'); - $this->tokens->releaseOfType('c'); - // Add newlines after comments. - foreach ($comments as $token => &$comment) { + foreach ($this->tokens->store->c as $token => &$comment) { $comment .= "$EOL$EOL"; } // Insert comments and do final whitespace cleanup. $this->stream - ->replaceHash($comments) + ->replaceTokens('c') ->trim() ->append($EOL); } // Insert URLs. - $urls = $this->tokens->getOfType('u'); + $urls = $this->tokens->store->u; if ($urls) { $link = CssCrush_Util::getLinkBetweenDirs($this->output->dir, $this->input->dir); @@ -975,24 +974,20 @@ protected function collate () } } } - $this->stream->replaceHash($urls); - unset($urls); - $this->tokens->releaseOfType('u'); } - // Insert string literals. - $this->stream->replaceHash($this->tokens->getOfType('s')); - $this->tokens->releaseOfType('s'); - - // Add in boilerplate. if ($options->boilerplate) { $this->stream->prepend($this->getBoilerplate()); } - // Add @charset at top if set. if ($this->charset) { $this->stream->prepend("@charset \"$this->charset\";$EOL"); } + + // Restore remaining tokens. + $this->stream->replaceTokens('u'); + $this->stream->replaceTokens('s'); + $this->stream->replaceTokens('t'); } public function compile ($io_context = 'file') diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php index effc618..ea97e00 100644 --- a/lib/CssCrush/Regex.php +++ b/lib/CssCrush/Regex.php @@ -79,6 +79,25 @@ static public function init () /\*(?:.*?)(?:\*/|$) ~xsS'; + $patt->rule2 = CssCrush_Regex::create(' + (?:^|(?<=[;{}])) + (?P + (?:\s|)* + ) + (?P + (?: + @(?: (?i)page|abstract|font-face(?-i) ) [^{]* + | + [^@;{}]+ + ) + ) + (?P + \{\s* + ( (?: (?>[^{}]+) | (?P>block) )* ) + \s*\} + ) + ', 'xS'); + // As an exception we treat some @-rules like standard rule blocks. $patt->rule = '~ # The selector. diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php index 0896bea..eba5866 100644 --- a/lib/CssCrush/Rule.php +++ b/lib/CssCrush/Rule.php @@ -42,7 +42,7 @@ public function __construct ($selector_string = null, $declarations_string) preg_match_all($regex->t_token, $selector_string, $trace_tokens) ) { $trace_token = array_pop($trace_tokens); - $this->tracingStub = $process->tokens->get($trace_token[0]); + $this->tracingStub = $trace_token[0]; foreach ($trace_tokens as $trace_token) { $process->tokens->release($trace_token[0]); } @@ -152,7 +152,6 @@ public function __toString () if ($comments = implode('', $this->comments)) { $comments = "$EOL$comments"; } - if ($stub = $this->tracingStub) { $stub .= $EOL; } diff --git a/lib/CssCrush/Stream.php b/lib/CssCrush/Stream.php index 7e02f9e..5ae6aea 100644 --- a/lib/CssCrush/Stream.php +++ b/lib/CssCrush/Stream.php @@ -63,6 +63,21 @@ public function replaceHash ($replacements) return $this; } + public function replaceTokens ($type) + { + static $types = array(); + if (! isset($types[$type])) { + $types[$type]['callback'] = create_function('$m', ' + $tokens =& CssCrush::$process->tokens->store->' . $type . '; + return isset($tokens[$m[0]]) ? $tokens[$m[0]] : \'\'; + '); + $types[$type]['patt'] = '~\?' . $type . '[a-zA-Z0-9]+\?~S'; + } + + $this->raw = preg_replace_callback($types[$type]['patt'], $types[$type]['callback'], $this->raw); + return $this; + } + public function pregReplace ($patt, $replacement) { $this->raw = preg_replace($patt, $replacement, $this->raw); diff --git a/lib/CssCrush/Tokens.php b/lib/CssCrush/Tokens.php index deb3abc..faae9f5 100644 --- a/lib/CssCrush/Tokens.php +++ b/lib/CssCrush/Tokens.php @@ -7,18 +7,26 @@ class CssCrush_Tokens { public $store; - protected $uid = 0; + protected $ids; public function __construct () { - $this->store = (object) array( - 's' => array(), // Strings - 'c' => array(), // Comments - 'r' => array(), // Rules - 'p' => array(), // Parens - 'u' => array(), // URLs - 't' => array(), // Traces + $types = array( + 's', // Strings + 'c', // Comments + 'r', // Rules + 'p', // Parens + 'u', // URLs + 't', // Traces ); + + $this->store = new stdClass; + $this->ids = new stdClass; + + foreach ($types as $type) { + $this->store->{$type} = array(); + $this->ids->{$type} = 0; + } } public function get ($label) @@ -58,7 +66,7 @@ public function add ($value, $type, $existing_label = null) public function createLabel ($type) { - $counter = base_convert(++$this->uid, 10, 36); + $counter = base_convert(++$this->ids->{$type}, 10, 36); return "?$type$counter?"; } From 363b7b0e51ef4efb164f0ce442d7eb2a1cb5c44f Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 3 Jul 2013 13:13:03 +0100 Subject: [PATCH 123/421] Removing `Tokens::getOfType/releaseOfType` methods. --- lib/CssCrush/Process.php | 6 ++++-- lib/CssCrush/Regex.php | 2 ++ lib/CssCrush/Tokens.php | 10 ---------- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 705bf21..151c2df 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -679,7 +679,7 @@ protected function processRules () { $aliases =& $this->aliases; - foreach ($this->tokens->getOfType('r') as $rule) { + foreach ($this->tokens->store->r as $rule) { $rule->processDeclarations(); @@ -919,9 +919,11 @@ protected function collate () // Print out rules. $this->stream->replaceTokens('r'); + + // Run rule related stats then reclaim memory. CssCrush::runStat('selector_count'); CssCrush::runStat('rule_count'); - $this->tokens->releaseOfType('r'); + $this->tokens->store->r = array(); // Restore parens. $this->stream->replaceTokens('p'); diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php index ea97e00..3a5db31 100644 --- a/lib/CssCrush/Regex.php +++ b/lib/CssCrush/Regex.php @@ -42,6 +42,7 @@ static public function init () $classes->RTB = '(?=\?[a-z])'; // Right token boundry. // Misc. + $classes->curly_block = '(?P\{\s*(?P(?:(?>[^{}]+)|(?P>block))*)\s*\})'; $classes->vendor = '-[a-zA-Z]+-'; $classes->newline = '(?:\r\n?|\n)'; @@ -98,6 +99,7 @@ static public function init () ) ', 'xS'); + // As an exception we treat some @-rules like standard rule blocks. $patt->rule = '~ # The selector. diff --git a/lib/CssCrush/Tokens.php b/lib/CssCrush/Tokens.php index faae9f5..4df1fdb 100644 --- a/lib/CssCrush/Tokens.php +++ b/lib/CssCrush/Tokens.php @@ -35,16 +35,6 @@ public function get ($label) return isset($path[$label]) ? $path[$label] : null; } - public function getOfType ($type) - { - return $this->store->{$type}; - } - - public function releaseOfType ($type) - { - $this->store->{$type} = array(); - } - public function pop ($label) { $value = $this->get($label); From 409e21c61605508fe413633d3327867327b052fa Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 4 Jul 2013 10:06:15 +0100 Subject: [PATCH 124/421] Changed the format of custom regex tokens ("" => "{{foo}}") as the old format clashes with named regex matching which we`re now moving towards. --- lib/CssCrush/Color.php | 19 +++++++++++------ lib/CssCrush/Core.php | 2 +- lib/CssCrush/Importer.php | 2 +- lib/CssCrush/PostAliasFix.php | 6 +++--- lib/CssCrush/Process.php | 14 +++++------- lib/CssCrush/Regex.php | 40 ++++++++++++++++------------------- lib/CssCrush/Rule.php | 2 +- lib/CssCrush/Tokens.php | 2 +- plugins/canvas.php | 2 +- plugins/ease.php | 2 +- plugins/hsl-to-hex.php | 2 +- plugins/rem.php | 4 ++-- plugins/rgba-fallback.php | 2 +- plugins/svg-gradients.php | 4 ++-- plugins/svg.php | 2 +- 15 files changed, 51 insertions(+), 54 deletions(-) diff --git a/lib/CssCrush/Color.php b/lib/CssCrush/Color.php index 74110d6..8253c37 100644 --- a/lib/CssCrush/Color.php +++ b/lib/CssCrush/Color.php @@ -111,16 +111,21 @@ static public function parse ($str) static public function test ($str) { + static $color_patt; + if (! $color_patt) { + $color_patt = CssCrush_Regex::create('^( + \#(?={{hex}}{3}) | + \#(?={{hex}}{6}) | + rgba?(?=[?(]) | + hsla?(?=[?(]) + )', 'ixS'); + } + $color_test = array(); $str = strtolower(trim($str)); // First match a hex value or the start of a function. - if (preg_match('~^( - \#(?=[[:xdigit:]]{3}) | - \#(?=[[:xdigit:]]{6}) | - rgba?(?=[?(]) | - hsla?(?=[?(]) - )~xS', $str, $m)) { + if (preg_match($color_patt, $str, $m)) { $type_match = $m[1]; @@ -347,7 +352,7 @@ static public function colorSplit ($str) static $alpha_color_patt; if (! $alpha_color_patt) { $alpha_color_patt = CssCrush_Regex::create( - '^(rgb|hsl)a\((%?,%?,%?),()\)$'); + '^(rgb|hsl)a\(({{number}}%?,{{number}}%?,{{number}}%?),({{number}})\)$'); } // Strip all whitespace. diff --git a/lib/CssCrush/Core.php b/lib/CssCrush/Core.php index 1c444fe..063f3a7 100644 --- a/lib/CssCrush/Core.php +++ b/lib/CssCrush/Core.php @@ -206,7 +206,7 @@ static public function loadAssets () // We'll cache the function matching regex here. $vendor_grouped_aliases[$m[1]]['find'][] = - CssCrush_Regex::create('' . $func_name . '', 'i'); + CssCrush_Regex::create('{{LB}}' . $func_name . '{{RTB}}', 'i'); $vendor_grouped_aliases[$m[1]]['replace'][] = $alias_func; } } diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index 63d4752..0c87d93 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -153,7 +153,7 @@ static protected function rewriteImportedUrls ($import) { static $non_import_urls_patt; if (! $non_import_urls_patt) { - $non_import_urls_patt = CssCrush_Regex::create('(?', 'iS'); + $non_import_urls_patt = CssCrush_Regex::create('(?)deg', 'i'); + $deg_patt = CssCrush_Regex::create('(?<=[\( ])({{number}})deg', 'i'); // Legacy angles move anti-clockwise and start from East, not North. $deg_convert_callback = create_function('$m', ' $angle = floatval($m[1]); $angle = ($angle + 90) - ($angle * 2); return ($angle < 0 ? $angle + 360 : $angle) . \'deg\'; '); - $fn_patt = CssCrush_Regex::create('(?:(?:repeating-)?linear-gradient)()', 'iS'); + $fn_patt = CssCrush_Regex::create('{{LB}}{{vendor}}(?:(?:repeating-)?linear-gradient)({{p-token}})', 'iS'); } // Create new paren tokens based on the first prefixed declaration. @@ -121,7 +121,7 @@ function csscrush__post_alias_fix_radialgradients ($declaration_copies) { // Replace the new syntax with the legacy syntax. static $fn_patt; if (! $fn_patt) { - $fn_patt = CssCrush_Regex::create('(?:(?:repeating-)?radial-gradient)()', 'iS'); + $fn_patt = CssCrush_Regex::create('{{LB}}{{vendor}}(?:(?:repeating-)?radial-gradient)({{p-token}})', 'iS'); } $original_parens = array(); $replacement_parens = array(); diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 151c2df..8842da6 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -207,7 +207,7 @@ protected function resolveSelectorAliases () { static $alias_patt, $callback; if (! $alias_patt) { - $alias_patt = CssCrush_Regex::create('@selector-alias +\:() +([^;]+) *;', 'iS'); + $alias_patt = CssCrush_Regex::create('@selector-alias +\:({{ident}}) +([^;]+) *;', 'iS'); $callback = create_function('$m', ' $name = strtolower($m[1]); $body = CssCrush_Util::stripCommentTokens($m[2]); @@ -224,7 +224,7 @@ protected function resolveSelectorAliases () if ($this->selectorAliases) { $names = implode('|', array_keys($this->selectorAliases)); $this->selectorAliasesPatt - = CssCrush_Regex::create('\:(' . $names . ')(\()?', 'iS'); + = CssCrush_Regex::create('\:(' . $names . '){{RB}}(\()?', 'iS'); } } @@ -504,7 +504,7 @@ static public function cb_captureVars ($m) CssCrush::$process->vars = array_merge( CssCrush::$process->vars, - CssCrush_Rule::parseBlock($m[1], array('keyed' => true, 'ignore_directives' => true))); + CssCrush_Rule::parseBlock($m['block_content'], array('keyed' => true, 'ignore_directives' => true))); } static protected function cb_placeVars ($m) @@ -569,11 +569,7 @@ protected function captureMixins () static $callback; if (! $callback) { $callback = create_function('$m', ' - $name = trim($m[1]); - $block = trim($m[2]); - if (! empty($name) && ! empty($block)) { - CssCrush::$process->mixins[$name] = new CssCrush_Mixin($block); - } + CssCrush::$process->mixins[$m[\'name\']] = new CssCrush_Mixin($m[\'block_content\']); '); } @@ -1113,7 +1109,7 @@ protected function minifyColors () $keywords_callback = create_function('$m', 'return CssCrush_Color::$minifyableKeywords[strtolower($m[0])];'); - $functions_patt = CssCrush_Regex::create('(rgb|hsl)\(([^\)]{5,})\)', 'iS'); + $functions_patt = CssCrush_Regex::create('{{LB}}(rgb|hsl)\(([^\)]{5,})\)', 'iS'); $functions_callback = create_function('$m', ' $args = CssCrush_Function::parseArgs(trim($m[2])); if (stripos($m[1], \'hsl\') === 0) { diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php index 3a5db31..d9cb718 100644 --- a/lib/CssCrush/Regex.php +++ b/lib/CssCrush/Regex.php @@ -42,13 +42,14 @@ static public function init () $classes->RTB = '(?=\?[a-z])'; // Right token boundry. // Misc. - $classes->curly_block = '(?P\{\s*(?P(?:(?>[^{}]+)|(?P>block))*)\s*\})'; + $classes->block = '(?P\{\s*(?P(?:(?>[^{}]+)|(?P>block))*)\s*\})'; $classes->vendor = '-[a-zA-Z]+-'; $classes->newline = '(?:\r\n?|\n)'; + $classes->hex = '[[:xdigit:]]'; // Create standalone class patterns, add classes as class swaps. foreach ($classes as $name => $class) { - self::$classSwaps['<' . str_replace('_', '-', $name) . '>'] = $class; + self::$classSwaps['{{' . str_replace('_', '-', $name) . '}}'] = $class; $patt->{$name} = '~' . $class . '~S'; } @@ -57,18 +58,18 @@ static public function init () $patt->rooted_number = '~^' . $classes->number . '$~'; // @-rules. - $patt->import = CssCrush_Regex::create('@import\s+()\s?([^;]*);', 'iS'); - $patt->charset = CssCrush_Regex::create('@charset\s+()\s*;', 'iS'); - $patt->vars = CssCrush_Regex::create('@define *\{ *(.*?) *\};?', 'iS'); - $patt->mixin = CssCrush_Regex::create('@mixin +() *\{ *(.*?) *\};?', 'iS'); - $patt->abstract = CssCrush_Regex::create('^@abstract +()', 'i'); - $patt->ifDefine = CssCrush_Regex::create('@ifdefine +(not +)?() *\{', 'iS'); - $patt->fragmentDef = CssCrush_Regex::create('@fragment +() *\{', 'iS'); - $patt->fragmentCall = CssCrush_Regex::create('@fragment +() *(\(|;)', 'iS'); + $patt->import = CssCrush_Regex::create('@import\s+({{u-token}})\s?([^;]*);', 'iS'); + $patt->charset = CssCrush_Regex::create('@charset\s+({{s-token}})\s*;', 'iS'); + $patt->vars = CssCrush_Regex::create('@define *{{block}}', 'iS'); + $patt->mixin = CssCrush_Regex::create('@mixin +(?P{{ident}}) *{{block}}', 'iS'); + $patt->abstract = CssCrush_Regex::create('^@abstract +({{ident}})', 'i'); + $patt->ifDefine = CssCrush_Regex::create('@ifdefine +(not +)?({{ident}}) *\{', 'iS'); + $patt->fragmentDef = CssCrush_Regex::create('@fragment +({{ident}}) *\{', 'iS'); + $patt->fragmentCall = CssCrush_Regex::create('@fragment +({{ident}}) *(\(|;)', 'iS'); // Functions. - $patt->function = CssCrush_Regex::create('()()', 'S'); - $patt->varFunction = CssCrush_Regex::create('\$\( *() *\)', 'S'); + $patt->function = CssCrush_Regex::create('{{LB}}({{ident}})({{p-token}})', 'S'); + $patt->varFunction = CssCrush_Regex::create('\$\( *({{ident}}) *\)', 'S'); $patt->thisFunction = CssCrush_Regex::createFunctionPatt(array('this')); $patt->string = '~(\'|")(?:\\\\\1|[^\1])*?\1~xS'; @@ -83,21 +84,16 @@ static public function init () $patt->rule2 = CssCrush_Regex::create(' (?:^|(?<=[;{}])) (?P - (?:\s|)* + (?: \s | {{c-token}} )* ) (?P (?: - @(?: (?i)page|abstract|font-face(?-i) ) [^{]* + @(?: (?i)page|abstract|font-face(?-i) ) {{RB}} [^{]* | [^@;{}]+ ) ) - (?P - \{\s* - ( (?: (?>[^{}]+) | (?P>block) )* ) - \s*\} - ) - ', 'xS'); + {{block}}', 'xS'); // As an exception we treat some @-rules like standard rule blocks. @@ -121,7 +117,7 @@ static public function init () $patt->ruleDirective = '~^(?:(@include)|(@extends?)|(@name))[\s]+~iS'; $patt->argListSplit = '~\s*[,\s]\s*~S'; $patt->mathBlacklist = '~[^\.0-9\*\/\+\-\(\)]~S'; - $patt->cruftyHex = '~\#([[:xdigit:]])\1([[:xdigit:]])\2([[:xdigit:]])\3~S'; + $patt->cruftyHex = CssCrush_Regex::create('\#({{hex}})\1({{hex}})\2({{hex}})\3', 'S'); } static public function create ($pattern_template, $flags = '', $delim = '~') @@ -172,7 +168,7 @@ static public function createFunctionPatt ($list, $options = array()) $flat_list = implode('|', $list); - return CssCrush_Regex::create("($template(?:$flat_list)$question)\(", 'iS'); + return CssCrush_Regex::create("($template{{LB}}(?:$flat_list)$question)\(", 'iS'); } } diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php index eba5866..d7a493b 100644 --- a/lib/CssCrush/Rule.php +++ b/lib/CssCrush/Rule.php @@ -381,7 +381,7 @@ public function expandSelectors () static $any_patt, $reg_comma; if (! $any_patt) { - $any_patt = CssCrush_Regex::create(':any()', 'i'); + $any_patt = CssCrush_Regex::create(':any({{p-token}})', 'i'); $reg_comma = '~\s*,\s*~'; } diff --git a/lib/CssCrush/Tokens.php b/lib/CssCrush/Tokens.php index 4df1fdb..4c683a1 100644 --- a/lib/CssCrush/Tokens.php +++ b/lib/CssCrush/Tokens.php @@ -132,7 +132,7 @@ public function captureUrls ($str) { static $url_patt; if (! $url_patt) { - $url_patt = CssCrush_Regex::create('@import\s+()|(url|data-uri)\(', 'iS'); + $url_patt = CssCrush_Regex::create('@import\s+({{s-token}})|{{LB}}(url|data-uri)\(', 'iS'); } $count = preg_match_all($url_patt, $str, $m, PREG_OFFSET_CAPTURE); diff --git a/plugins/canvas.php b/plugins/canvas.php index 16909bb..9a01fac 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -69,7 +69,7 @@ function csscrush__canvas_capture ($process) { static $callback, $patt; if (! $callback) { - $patt = CssCrush_Regex::create('@canvas +() *\{ *(.*?) *\};?', 'iS'); + $patt = CssCrush_Regex::create('@canvas +({{ident}}) *\{ *(.*?) *\};?', 'iS'); $callback = create_function('$m', ' $name = strtolower($m[1]); $block = $m[2]; diff --git a/plugins/ease.php b/plugins/ease.php index 82079c5..c2dd0a2 100644 --- a/plugins/ease.php +++ b/plugins/ease.php @@ -62,7 +62,7 @@ function csscrush__ease (CssCrush_Rule $rule) { ); foreach ($easings as $property => $value) { - $patt = CssCrush_Regex::create('' . $property . '', 'i'); + $patt = CssCrush_Regex::create('{{LB}}' . $property . '{{RB}}', 'i'); $find[] = $patt; $replace[] = $value; } diff --git a/plugins/hsl-to-hex.php b/plugins/hsl-to-hex.php index 8d918bc..55da51f 100644 --- a/plugins/hsl-to-hex.php +++ b/plugins/hsl-to-hex.php @@ -26,7 +26,7 @@ function csscrush__hsl_to_hex (CssCrush_Rule $rule) { static $hsl_patt; if (! $hsl_patt) { - $hsl_patt = CssCrush_Regex::create('hsl()', 'i'); + $hsl_patt = CssCrush_Regex::create('{{LB}}hsl({{p-token}})', 'i'); } foreach ($rule as &$declaration) { diff --git a/plugins/rem.php b/plugins/rem.php index e728f9d..bfa13d1 100644 --- a/plugins/rem.php +++ b/plugins/rem.php @@ -51,8 +51,8 @@ function csscrush__rem (CssCrush_Rule $rule) { static $rem_patt, $px_patt, $font_props, $modes; if (! $modes) { - $rem_patt = CssCrush_Regex::create('()rem', 'iS'); - $px_patt = CssCrush_Regex::create('()px', 'iS'); + $rem_patt = CssCrush_Regex::create('{{LB}}({{number}})rem{{RB}}', 'iS'); + $px_patt = CssCrush_Regex::create('{{LB}}({{number}})px{{RB}}', 'iS'); $font_props = array( 'font' => true, 'font-size' => true, diff --git a/plugins/rgba-fallback.php b/plugins/rgba-fallback.php index f54190e..7742ff4 100644 --- a/plugins/rgba-fallback.php +++ b/plugins/rgba-fallback.php @@ -43,7 +43,7 @@ function csscrush__rgba_fallback (CssCrush_Rule $rule) { static $rgb_patt; if (! $rgb_patt) { - $rgb_patt = CssCrush_Regex::create('^rgba$', 'i'); + $rgb_patt = CssCrush_Regex::create('^rgba{{p-token}}$', 'i'); } $new_set = array(); diff --git a/plugins/svg-gradients.php b/plugins/svg-gradients.php index 1cee2d7..681de0b 100644 --- a/plugins/svg-gradients.php +++ b/plugins/svg-gradients.php @@ -117,7 +117,7 @@ function csscrush__create_svg_linear_gradient ($input) { $angle_keywords['to right bottom'] = $angle_keywords['to bottom right']; $angle_keywords['to left bottom'] = $angle_keywords['to bottom left']; - $deg_patt = CssCrush_Regex::create('^deg$', 'i'); + $deg_patt = CssCrush_Regex::create('^{{number}}deg$', 'i'); } $args = CssCrush_Function::parseArgs($input); @@ -248,7 +248,7 @@ function csscrush__create_svg_radial_gradient ($input) { $position_keywords['at right bottom'] = $position_keywords['at bottom right']; $position_keywords['at left bottom'] = $position_keywords['at bottom left']; - $origin_patt = CssCrush_Regex::create('^(%?) +(%?)$'); + $origin_patt = CssCrush_Regex::create('^({{number}}%?) +({{number}}%?)$'); } $args = CssCrush_Function::parseArgs($input); diff --git a/plugins/svg.php b/plugins/svg.php index 35010ae..510a482 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -89,7 +89,7 @@ function csscrush__svg_capture ($process) { static $callback, $patt; if (! $callback) { - $patt = CssCrush_Regex::create('@svg +() *\{ *(.*?) *\};?', 'iS'); + $patt = CssCrush_Regex::create('@svg +({{ident}}) *\{ *(.*?) *\};?', 'iS'); $callback = create_function('$m', ' $name = strtolower($m[1]); $block = $m[2]; From 77e7c1a4ff85f653d020435bfb025360f0a42e02 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 4 Jul 2013 21:54:10 +0100 Subject: [PATCH 125/421] Adding command line utility alias for composer`s vendor/bin directory --- composer.json | 3 +++ misc/csscrush | 8 ++++++++ 2 files changed, 11 insertions(+) create mode 100755 misc/csscrush diff --git a/composer.json b/composer.json index df29807..47f36c4 100644 --- a/composer.json +++ b/composer.json @@ -18,6 +18,9 @@ "require": { "php": ">=5.2.4" }, + "bin": [ + "misc/csscrush" + ], "autoload": { "files": ["CssCrush.php"] } diff --git a/misc/csscrush b/misc/csscrush new file mode 100755 index 0000000..665fdd6 --- /dev/null +++ b/misc/csscrush @@ -0,0 +1,8 @@ +#!/usr/bin/env php + Date: Fri, 5 Jul 2013 09:40:47 +0100 Subject: [PATCH 126/421] Switching to cleaner "" named subpattern syntax, supported PHP >= 5.2.2 --- lib/CssCrush/Process.php | 11 ++++++++--- lib/CssCrush/Regex.php | 11 ++++------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 8842da6..e2dfab9 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -582,9 +582,14 @@ protected function captureMixins () protected function resolveFragments () { - $regex = CssCrush_Regex::$patt; - $matches = $this->stream->matchAll($regex->fragmentDef); + static $define_patt, $call_patt; + if (! $define_patt) { + $define_patt = CssCrush_Regex::create('@fragment +({{ident}}) *\{', 'iS'); + $call_patt = CssCrush_Regex::create('@fragment +({{ident}}) *(\(|;)', 'iS'); + } + $fragments = array(); + $matches = $this->stream->matchAll($define_patt); // Move through the matches last to first. while ($match = array_pop($matches)) { @@ -609,7 +614,7 @@ protected function resolveFragments () } // Now find all the fragment calls. - $matches = $this->stream->matchAll($regex->fragmentCall); + $matches = $this->stream->matchAll($call_patt); // Move through the matches last to first. while ($match = array_pop($matches)) { diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php index d9cb718..fa2a561 100644 --- a/lib/CssCrush/Regex.php +++ b/lib/CssCrush/Regex.php @@ -42,9 +42,8 @@ static public function init () $classes->RTB = '(?=\?[a-z])'; // Right token boundry. // Misc. - $classes->block = '(?P\{\s*(?P(?:(?>[^{}]+)|(?P>block))*)\s*\})'; + $classes->block = '(?\{\s*(?(?:(?>[^{}]+)|(?>block))*)\s*\})'; $classes->vendor = '-[a-zA-Z]+-'; - $classes->newline = '(?:\r\n?|\n)'; $classes->hex = '[[:xdigit:]]'; // Create standalone class patterns, add classes as class swaps. @@ -61,11 +60,9 @@ static public function init () $patt->import = CssCrush_Regex::create('@import\s+({{u-token}})\s?([^;]*);', 'iS'); $patt->charset = CssCrush_Regex::create('@charset\s+({{s-token}})\s*;', 'iS'); $patt->vars = CssCrush_Regex::create('@define *{{block}}', 'iS'); - $patt->mixin = CssCrush_Regex::create('@mixin +(?P{{ident}}) *{{block}}', 'iS'); + $patt->mixin = CssCrush_Regex::create('@mixin +(?{{ident}}) *{{block}}', 'iS'); $patt->abstract = CssCrush_Regex::create('^@abstract +({{ident}})', 'i'); $patt->ifDefine = CssCrush_Regex::create('@ifdefine +(not +)?({{ident}}) *\{', 'iS'); - $patt->fragmentDef = CssCrush_Regex::create('@fragment +({{ident}}) *\{', 'iS'); - $patt->fragmentCall = CssCrush_Regex::create('@fragment +({{ident}}) *(\(|;)', 'iS'); // Functions. $patt->function = CssCrush_Regex::create('{{LB}}({{ident}})({{p-token}})', 'S'); @@ -83,10 +80,10 @@ static public function init () $patt->rule2 = CssCrush_Regex::create(' (?:^|(?<=[;{}])) - (?P + (? (?: \s | {{c-token}} )* ) - (?P + (? (?: @(?: (?i)page|abstract|font-face(?-i) ) {{RB}} [^{]* | From 5e0f5782b339a145186cbb8fdb0b1bf4e24a4e08 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 8 Jul 2013 09:06:08 +0100 Subject: [PATCH 127/421] Added VLQ encoding function. --- lib/CssCrush/Util.php | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php index a503c2b..82c71ad 100644 --- a/lib/CssCrush/Util.php +++ b/lib/CssCrush/Util.php @@ -136,4 +136,38 @@ static public function getLinkBetweenDirs ($dir1, $dir2) // Add closing slash. return $link !== '' ? rtrim($link, '/') . '/' : ''; } + + /* + * Encode integer to Base64 VLQ. + */ + static public function vlqEncode ($value) + { + static $SHIFT, $MASK, $CONTINUATION_BIT, $BASE64_MAP; + if (! $SHIFT) { + $SHIFT = 5; + $MASK = 0x1F; + $CONTINUATION_BIT = 0x20; + $BASE64_MAP = array_merge(range('A', 'Z'), range('a', 'z'), array('+', '/')); + } + + if ($value < 0) { + $value = ((-$value) << 1) | 1; + } + else { + $value <<= 1; + } + + $encoded = ""; + do { + $digit = $value & $MASK; + $value >>= $SHIFT; + if ($value > 0) { + $digit |= $CONTINUATION_BIT; + } + $encoded .= $BASE64_MAP[$digit]; + } + while ($value > 0); + + return $encoded; + } } From d7f1063c7dd02c3e942ac50dab9666eb8d7b781f Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 8 Jul 2013 20:14:59 +0100 Subject: [PATCH 128/421] Issue #48 - Added support for fragment calls within fragment definitions. --- lib/CssCrush/Fragment.php | 44 ++++++++++++++++ lib/CssCrush/Process.php | 103 ++++++++------------------------------ lib/CssCrush/Regex.php | 7 ++- 3 files changed, 72 insertions(+), 82 deletions(-) create mode 100644 lib/CssCrush/Fragment.php diff --git a/lib/CssCrush/Fragment.php b/lib/CssCrush/Fragment.php new file mode 100644 index 0000000..ee1d641 --- /dev/null +++ b/lib/CssCrush/Fragment.php @@ -0,0 +1,44 @@ +name = $options['name']; + } + + public function apply (array $args = null, $str = null) + { + $str = parent::apply($args); + + // Flatten all fragment calls within the template string. + while (preg_match(CssCrush_Regex::$patt->fragmentInvoke, $str, $m, PREG_OFFSET_CAPTURE)) { + + $name = strtolower($m['name'][0]); + $fragment = isset(CssCrush::$process->fragments[$name]) ? CssCrush::$process->fragments[$name] : null; + + $replacement = ''; + $start = $m[0][1]; + $length = strlen($m[0][0]); + + // Skip over same named fragments to avoid infinite recursion. + if ($fragment && $name !== $this->name) { + $args = array(); + if (isset($m['parens_content'][0])) { + $args = CssCrush_Function::parseArgs($m['parens_content'][0]); + } + $replacement = $fragment->apply($args); + } + $str = substr_replace($str, $replacement, $start, $length); + } + + return $str; + } +} diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index e2dfab9..7956077 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -25,6 +25,7 @@ public function __construct ($options) // Initialize properties. $this->cacheData = array(); $this->mixins = array(); + $this->fragments = array(); $this->references = array(); $this->errors = array(); $this->stat = array(); @@ -582,89 +583,29 @@ protected function captureMixins () protected function resolveFragments () { - static $define_patt, $call_patt; - if (! $define_patt) { - $define_patt = CssCrush_Regex::create('@fragment +({{ident}}) *\{', 'iS'); - $call_patt = CssCrush_Regex::create('@fragment +({{ident}}) *(\(|;)', 'iS'); - } - - $fragments = array(); - $matches = $this->stream->matchAll($define_patt); - - // Move through the matches last to first. - while ($match = array_pop($matches)) { - - $match_start_pos = $match[0][1]; - $fragment_name = $match[1][0]; - - $curly_match = new CssCrush_BalancedMatch($this->stream, $match_start_pos); - - if (! $curly_match->match) { - - // Couldn't match the block. - continue; - } - else { - // Reconstruct the stream without the fragment. - $curly_match->replace(''); - - // Create the fragment and store it. - $fragments[$fragment_name] = new CssCrush_Template($curly_match->inside()); - } - } - - // Now find all the fragment calls. - $matches = $this->stream->matchAll($call_patt); - - // Move through the matches last to first. - while ($match = array_pop($matches)) { - - list($match_string, $match_start_pos) = $match[0]; - - // The matched fragment name. - $fragment_name = $match[1][0]; - - // The fragment object, or null if name not present. - $fragment = isset($fragments[$fragment_name]) ? $fragments[$fragment_name] : null; - - // Fragment may be called without any argument list. - $with_arguments = $match[2][0] === '('; - - // Resolve end of the match. - if ($with_arguments) { - $paren_match = new CssCrush_BalancedMatch($this->stream, $match_start_pos, '()'); - // Get offset of statement terminating semi-colon. - $match_end = $paren_match->nextIndexOf(';') + 1; - $match_length = $match_end - $match_start_pos; - } - else { - $match_length = strlen($match_string); - } - - // If invalid fragment or malformed argument list. - if (! $fragment || ($with_arguments && ! $paren_match->match)) { - - $this->stream->splice('', $match_start_pos, $match_length); - - continue; - } - - // Ok. - else { - - $args = array(); - if ($with_arguments) { - // Get the argument array to pass to the fragment. - $args = CssCrush_Function::parseArgs($paren_match->inside()); + static $capture_callback, $invoke_callback; + if (! $capture_callback) { + + $capture_callback = create_function('$m', ' + CssCrush::$process->fragments[$m[\'name\']] = new CssCrush_Fragment( + $m[\'block_content\'], + array(\'name\' => strtolower($m[\'name\']))); + return \'\';'); + + $invoke_callback = create_function('$m', ' + $fragment = isset(CssCrush::$process->fragments[$m[\'name\']]) ? CssCrush::$process->fragments[$m[\'name\']] : null; + if ($fragment) { + $args = array(); + if (isset($m[\'parens_content\'])) { + $args = CssCrush_Function::parseArgs($m[\'parens_content\']); + } + return $fragment->apply($args); } - - // Execute the fragment and get the return value. - $fragment_return = $fragment->apply($args); - - // Recontruct the stream with the fragment return value. - $this->stream->splice($fragment_return, $match_start_pos, $match_length); - } + return \'\';'); } + + $this->stream->pregReplaceCallback(CssCrush_Regex::$patt->fragmentCapture, $capture_callback); + $this->stream->pregReplaceCallback(CssCrush_Regex::$patt->fragmentInvoke, $invoke_callback); } diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php index fa2a561..a9ad581 100644 --- a/lib/CssCrush/Regex.php +++ b/lib/CssCrush/Regex.php @@ -41,8 +41,11 @@ static public function init () $classes->RB = '(?![\w-])'; // Right ident boundry. $classes->RTB = '(?=\?[a-z])'; // Right token boundry. + // Recursive block matching. + $classes->block = '(?\{\s*(?(?:(?>[^{}]+)|(?&block))*)\s*\})'; + $classes->parens = '(?\(\s*(?(?:(?>[^)(]+)|(?&parens))*)\s*\))'; + // Misc. - $classes->block = '(?\{\s*(?(?:(?>[^{}]+)|(?>block))*)\s*\})'; $classes->vendor = '-[a-zA-Z]+-'; $classes->hex = '[[:xdigit:]]'; @@ -63,6 +66,8 @@ static public function init () $patt->mixin = CssCrush_Regex::create('@mixin +(?{{ident}}) *{{block}}', 'iS'); $patt->abstract = CssCrush_Regex::create('^@abstract +({{ident}})', 'i'); $patt->ifDefine = CssCrush_Regex::create('@ifdefine +(not +)?({{ident}}) *\{', 'iS'); + $patt->fragmentCapture = CssCrush_Regex::create('@fragment \s+ (?{{ident}}) \s* {{block}}', 'ixS'); + $patt->fragmentInvoke = CssCrush_Regex::create('@fragment \s+ (?{{ident}}) {{parens}}? \s* ;', 'ixS'); // Functions. $patt->function = CssCrush_Regex::create('{{LB}}({{ident}})({{p-token}})', 'S'); From 3ef6d75316ee32b86dc18f835c8e626cad4590aa Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 9 Jul 2013 13:18:45 +0100 Subject: [PATCH 129/421] Some cleanup and refactoring utilizing the new recursive regexes. --- lib/CssCrush/Fragment.php | 2 +- lib/CssCrush/Process.php | 2 +- lib/CssCrush/Regex.php | 20 ++++++++--------- lib/CssCrush/Rule.php | 4 +--- lib/CssCrush/Template.php | 7 +----- lib/CssCrush/Tokens.php | 46 +++++++++++++++------------------------ plugins/canvas.php | 12 +++++----- plugins/svg.php | 12 +++++----- 8 files changed, 44 insertions(+), 61 deletions(-) diff --git a/lib/CssCrush/Fragment.php b/lib/CssCrush/Fragment.php index ee1d641..71cd677 100644 --- a/lib/CssCrush/Fragment.php +++ b/lib/CssCrush/Fragment.php @@ -31,7 +31,7 @@ public function apply (array $args = null, $str = null) // Skip over same named fragments to avoid infinite recursion. if ($fragment && $name !== $this->name) { $args = array(); - if (isset($m['parens_content'][0])) { + if ($m['parens'][1] !== -1) { $args = CssCrush_Function::parseArgs($m['parens_content'][0]); } $replacement = $fragment->apply($args); diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 7956077..5140611 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -596,7 +596,7 @@ protected function resolveFragments () $fragment = isset(CssCrush::$process->fragments[$m[\'name\']]) ? CssCrush::$process->fragments[$m[\'name\']] : null; if ($fragment) { $args = array(); - if (isset($m[\'parens_content\'])) { + if (isset($m[\'parens\'])) { $args = CssCrush_Function::parseArgs($m[\'parens_content\']); } return $fragment->apply($args); diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php index a9ad581..1285d1e 100644 --- a/lib/CssCrush/Regex.php +++ b/lib/CssCrush/Regex.php @@ -42,8 +42,8 @@ static public function init () $classes->RTB = '(?=\?[a-z])'; // Right token boundry. // Recursive block matching. - $classes->block = '(?\{\s*(?(?:(?>[^{}]+)|(?&block))*)\s*\})'; - $classes->parens = '(?\(\s*(?(?:(?>[^)(]+)|(?&parens))*)\s*\))'; + $classes->block = '(?\{\s*(?(?:(?>[^{}]+)|(?&block))*)\})'; + $classes->parens = '(?\(\s*(?(?:(?>[^()]+)|(?&parens))*)\))'; // Misc. $classes->vendor = '-[a-zA-Z]+-'; @@ -60,18 +60,18 @@ static public function init () $patt->rooted_number = '~^' . $classes->number . '$~'; // @-rules. - $patt->import = CssCrush_Regex::create('@import\s+({{u-token}})\s?([^;]*);', 'iS'); - $patt->charset = CssCrush_Regex::create('@charset\s+({{s-token}})\s*;', 'iS'); - $patt->vars = CssCrush_Regex::create('@define *{{block}}', 'iS'); - $patt->mixin = CssCrush_Regex::create('@mixin +(?{{ident}}) *{{block}}', 'iS'); - $patt->abstract = CssCrush_Regex::create('^@abstract +({{ident}})', 'i'); - $patt->ifDefine = CssCrush_Regex::create('@ifdefine +(not +)?({{ident}}) *\{', 'iS'); + $patt->import = CssCrush_Regex::create('@import \s+ ({{u-token}}) \s? ([^;]*);', 'ixS'); + $patt->charset = CssCrush_Regex::create('@charset \s+ ({{s-token}}) \s*;', 'ixS'); + $patt->vars = CssCrush_Regex::create('@define \s* {{block}}', 'ixS'); + $patt->mixin = CssCrush_Regex::create('@mixin \s+ (?{{ident}}) \s* {{block}}', 'ixS'); + $patt->ifDefine = CssCrush_Regex::create('@ifdefine \s+ (not \s+)? ({{ident}}) \s* \{', 'ixS'); $patt->fragmentCapture = CssCrush_Regex::create('@fragment \s+ (?{{ident}}) \s* {{block}}', 'ixS'); $patt->fragmentInvoke = CssCrush_Regex::create('@fragment \s+ (?{{ident}}) {{parens}}? \s* ;', 'ixS'); + $patt->abstract = CssCrush_Regex::create('^@abstract \s+ (?{{ident}})', 'ixS'); // Functions. - $patt->function = CssCrush_Regex::create('{{LB}}({{ident}})({{p-token}})', 'S'); - $patt->varFunction = CssCrush_Regex::create('\$\( *({{ident}}) *\)', 'S'); + $patt->function = CssCrush_Regex::create('{{LB}} ({{ident}}) ({{p-token}})', 'xS'); + $patt->varFunction = CssCrush_Regex::create('\$\( \s* ({{ident}}) \s* \)', 'xS'); $patt->thisFunction = CssCrush_Regex::createFunctionPatt(array('this')); $patt->string = '~(\'|")(?:\\\\\1|[^\1])*?\1~xS'; diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php index d7a493b..f167e95 100644 --- a/lib/CssCrush/Rule.php +++ b/lib/CssCrush/Rule.php @@ -70,10 +70,8 @@ public function __construct ($selector_string = null, $declarations_string) // If the selector matches an absract directive if (preg_match($regex->abstract, $selector, $m)) { - $abstract_name = $m[1]; - // Link the rule to the abstract name and skip forward to declaration parsing. - $process->references[$abstract_name] = $this; + $process->references[strtolower($m['name'])] = $this; } else { $this->addSelector(new CssCrush_Selector($selector)); diff --git a/lib/CssCrush/Template.php b/lib/CssCrush/Template.php index ff7593b..303cd93 100644 --- a/lib/CssCrush/Template.php +++ b/lib/CssCrush/Template.php @@ -4,7 +4,7 @@ * Generalized 'in CSS' templating. * */ -class CssCrush_Template implements Countable +class CssCrush_Template { // Positional argument default values. public $defaults = array(); @@ -140,11 +140,6 @@ public function apply (array $args = null, $str = null) return CssCrush_Template::tokenize($str); } - public function count () - { - return $this->argCount; - } - static public function tokenize ($str) { $str = CssCrush::$process->tokens->capture($str, 's'); diff --git a/lib/CssCrush/Tokens.php b/lib/CssCrush/Tokens.php index 4c683a1..06c3f91 100644 --- a/lib/CssCrush/Tokens.php +++ b/lib/CssCrush/Tokens.php @@ -132,47 +132,37 @@ public function captureUrls ($str) { static $url_patt; if (! $url_patt) { - $url_patt = CssCrush_Regex::create('@import\s+({{s-token}})|{{LB}}(url|data-uri)\(', 'iS'); + $url_patt = CssCrush_Regex::create( + '@import \s+ (?{{s-token}}) | {{LB}} (?url|data-uri) {{parens}}', 'ixS'); } $count = preg_match_all($url_patt, $str, $m, PREG_OFFSET_CAPTURE); - while ($count--) { - // Full match. - $outer0 = $m[0][$count]; + while ($count--) { - // @import directive position. - $outer1 = $m[1][$count]; + list($full_text, $full_offset) = $m[0][$count]; + list($import_text, $import_offset) = $m['import'][$count]; - // URL function position. - $outer2 = is_array($m[2][$count]) ? $m[2][$count] : null; + // @import directive. + if ($import_offset !== -1) { - list($outer_text, $outer_offset) = $outer0; - $newlines = ''; + $url = new CssCrush_Url($import_text); + $str = str_replace($import_text, $url->label, $str); + } - // An @import directive. - if (! $outer2) { + // A URL function. + else { - if (strpos($outer_text, "\n") !== false) { - $newlines = str_repeat("\n", substr_count($outer_text, "\n")); + $newlines = ''; + if (strpos($full_text, "\n") !== false) { + $newlines = str_repeat("\n", substr_count($full_text, "\n")); } - $url = new CssCrush_Url(trim($outer1[0])); - $str = str_replace($outer1[0], $url->label . $newlines, $str); - } - // A URL function - match closing parens. - elseif ( - preg_match(CssCrush_Regex::$patt->balancedParens, $str, $inner_m, PREG_OFFSET_CAPTURE, $outer_offset) - ) { + $func_name = strtolower($m['func'][$count][0]); - $inner_text = $inner_m[0][0]; - if (strpos($inner_text, "\n") !== false) { - $newlines = str_repeat("\n", substr_count($inner_text, "\n")); - } - $url = new CssCrush_Url(trim($inner_m[1][0])); - $func_name = strtolower($outer2[0]); + $url = new CssCrush_Url(trim($m['parens_content'][$count][0])); $url->convertToData = 'data-uri' === $func_name; - $str = substr_replace($str, $url->label . $newlines, $outer0[1], strlen($func_name) + strlen($inner_text)); + $str = substr_replace($str, $url->label . $newlines, $full_offset, strlen($full_text)); } } diff --git a/plugins/canvas.php b/plugins/canvas.php index 9a01fac..883f77d 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -69,14 +69,14 @@ function csscrush__canvas_capture ($process) { static $callback, $patt; if (! $callback) { - $patt = CssCrush_Regex::create('@canvas +({{ident}}) *\{ *(.*?) *\};?', 'iS'); + $patt = CssCrush_Regex::create('@canvas \s+ (?{{ident}}) \s* {{block}}', 'ixS'); $callback = create_function('$m', ' - $name = strtolower($m[1]); - $block = $m[2]; - if (! empty($name) && ! empty($block)) { - CssCrush::$process->misc->canvas_defs[$name] = - new CssCrush_Template($block); + $name = strtolower($m[\'name\']); + $block = $m[\'block_content\']; + if (! empty($block)) { + CssCrush::$process->misc->canvas_defs[$name] = new CssCrush_Template($block); } + return \'\'; '); } diff --git a/plugins/svg.php b/plugins/svg.php index 510a482..2d556e9 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -89,14 +89,14 @@ function csscrush__svg_capture ($process) { static $callback, $patt; if (! $callback) { - $patt = CssCrush_Regex::create('@svg +({{ident}}) *\{ *(.*?) *\};?', 'iS'); + $patt = CssCrush_Regex::create('@svg \s+ (?{{ident}}) \s* {{block}}', 'ixS'); $callback = create_function('$m', ' - $name = strtolower($m[1]); - $block = $m[2]; - if (! empty($name) && ! empty($block)) { - CssCrush::$process->misc->svg_defs[$name] = - new CssCrush_Template($block); + $name = strtolower($m[\'name\']); + $block = $m[\'block_content\']; + if (! empty($block)) { + CssCrush::$process->misc->svg_defs[$name] = new CssCrush_Template($block); } + return \'\'; '); } From 1c7ffa83136a43f8d1969c992790b3ea496c0a8f Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 10 Jul 2013 09:46:21 +0100 Subject: [PATCH 130/421] Moving syntax checking into its own method. --- lib/CssCrush/Importer.php | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index 0c87d93..d51b2ef 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -187,9 +187,14 @@ static protected function prepareForStream (&$str) // Convert all end-of-lines to unix style. $str = preg_replace('~\r\n?~', "\n", $str); - // Capture all comments and string literals. $str = $tokens->captureCommentAndString($str); + // Validate syntax as early as possible. + if (! self::checkSyntax($str)) { + + return false; + } + // Normalize double-colon pseudo elements for backwards compatability. $str = preg_replace('~::(after|before|first-(?:letter|line))~iS', ':$1', $str); @@ -204,9 +209,24 @@ static protected function prepareForStream (&$str) $str = preg_replace($regex->charset, $replace, $str); } + $str = $tokens->captureUrls($str); + + if ($process->addTracingStubs) { + self::addTracingStubs($str); + } + + $str = CssCrush_Util::normalizeWhiteSpace($str); + + return true; + } + + static protected function checkSyntax (&$str) + { + // TODO: add more sophisticated error detection such as line/column of unmatched bracket. + // Catch obvious typing errors. $parse_errors = array(); - $current_file = $process->currentFile; + $current_file = CssCrush::$process->currentFile; $balanced_parens = substr_count($str, "(") === substr_count($str, ")"); $balanced_curlies = substr_count($str, "{") === substr_count($str, "}"); @@ -222,20 +242,9 @@ static protected function prepareForStream (&$str) CssCrush::logError($error_msg); trigger_error("$error_msg\n", E_USER_WARNING); } - return false; } - $str = $tokens->captureUrls($str); - - // Optionally add tracing stubs. - if ($process->addTracingStubs) { - self::addTracingStubs($str); - } - - // Strip unneeded whitespace. - $str = CssCrush_Util::normalizeWhiteSpace($str); - - return true; + return empty($parse_errors) ? true : false; } static protected function addTracingStubs (&$str) From 7bac5528310eeee297a918c41a61dff241b031dc Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 11 Jul 2013 10:02:26 +0100 Subject: [PATCH 131/421] Some improvements and simplification of rule parsing. --- lib/CssCrush/Importer.php | 4 +-- lib/CssCrush/Process.php | 29 +++++++++++--------- lib/CssCrush/Regex.php | 28 ++++++++++++------- lib/CssCrush/Rule.php | 58 ++++++++++----------------------------- 4 files changed, 50 insertions(+), 69 deletions(-) diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index d51b2ef..4404932 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -211,9 +211,9 @@ static protected function prepareForStream (&$str) $str = $tokens->captureUrls($str); - if ($process->addTracingStubs) { + // if ($process->addTracingStubs) { self::addTracingStubs($str); - } + // } $str = CssCrush_Util::normalizeWhiteSpace($str); diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 5140611..4435c41 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -650,14 +650,22 @@ protected function processRules () static public function cb_captureRules ($m) { - $rule = (object) array(); - $rule->selector_raw = trim($m[1]); - $rule->declaration_raw = trim($m[2]); + // $rule = (object) array(); + // $rule->selector_raw = trim($m[1]); + // $rule->declaration_raw = trim($m[2]); // Run rule_preprocess hook. - CssCrush_Hook::run('rule_preprocess', $rule); + // CssCrush_Hook::run('rule_preprocess', $rule); - $rule = new CssCrush_Rule($rule->selector_raw, $rule->declaration_raw); + $selector = trim($m['selector']); + $block = trim($m['block_content']); + + // Ignore and remove empty rules. + if (empty($block) || empty($selector)) { + return ''; + } + + $rule = new CssCrush_Rule($selector, $block, $m['trace_token']); // Store rules if they have declarations or extend arguments. if (! empty($rule->declarations) || $rule->extendArgs) { @@ -854,6 +862,9 @@ protected function collate () $regex_replacements['~([^\s])\{~'] = "$1 {"; $regex_replacements['~ ?(@[^{]+\{)~'] = "$1$EOL"; $regex_replacements['~ ?(@[^;]+\;)~'] = "$1$EOL"; + + // Trim leading spaces on rule and comment tokens + $regex_replacements[CssCrush_Regex::create(' +({{r-token}}|{{c-token}})', 'S')] = "$1"; } // Apply all formatting replacements. @@ -975,14 +986,6 @@ public function compile ($io_context = 'file') // Capture phase 2 hook: After most built-in directives have resolved. CssCrush_Hook::run('capture_phase2', $this); - // Adjust meta characters so we can capture the rules cleanly. - $this->stream->replaceHash(array( - '@' => "\n@", - '}' => "}\n", - '{' => "{\n", - ';' => ";\n", - ))->prepend("\n"); - // Parse rules. $this->captureRules(); // csscrush::log(array_keys($this->references)); diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php index 1285d1e..9ded6d5 100644 --- a/lib/CssCrush/Regex.php +++ b/lib/CssCrush/Regex.php @@ -97,18 +97,26 @@ static public function init () ) {{block}}', 'xS'); + $patt->rule = CssCrush_Regex::create(' + (? {{t-token}} ) + \s* + (? [^{]+ ) + \s* + {{block}}', 'xiS'); // As an exception we treat some @-rules like standard rule blocks. - $patt->rule = '~ - # The selector. - \n( - [^@{}]+ - | - (?: [^@{}]+ )? @(?: font-face|abstract|page ) (?!-)\b [^{]* - ) - # The declaration block. - \{ ([^{}]*) \} - ~xiS'; + // $patt->rule = '~ + // # The selector. + // \n( + // [^@{}]+ + // | + // (?: [^@{}]+ )? @(?: font-face|abstract|page ) (?!-)\b [^{]* + // ) + // # The declaration block. + // \{ ([^{}]*) \} + // ~xiS'; + + // Balanced bracket matching. $patt->balancedParens = '~\(\s* ( (?: (?>[^()]+) | (?R) )* ) \s*\)~xS'; diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php index f167e95..d8065d9 100644 --- a/lib/CssCrush/Rule.php +++ b/lib/CssCrush/Rule.php @@ -14,9 +14,6 @@ class CssCrush_Rule implements IteratorAggregate public $extendSelectors = array(); public $declarations = array(); - // The comments associated with the rule. - public $comments = array(); - // Index of properties used in the rule for fast lookup. public $properties = array(); public $canonicalProperties = array(); @@ -30,52 +27,27 @@ class CssCrush_Rule implements IteratorAggregate // Declarations hash table for external query() referencing. public $queryData = array(); - public function __construct ($selector_string = null, $declarations_string) + public function __construct ($selector_string, $declarations_string, $trace_token = null) { $regex = CssCrush_Regex::$patt; $process = CssCrush::$process; $this->label = $process->tokens->createLabel('r'); + $this->tracingStub = $process->addTracingStubs ? $trace_token : null; - // If tracing store the last tracing stub, then strip all. - if ( - $process->addTracingStubs && - preg_match_all($regex->t_token, $selector_string, $trace_tokens) - ) { - $trace_token = array_pop($trace_tokens); - $this->tracingStub = $trace_token[0]; - foreach ($trace_tokens as $trace_token) { - $process->tokens->release($trace_token[0]); - } - - $selector_string = preg_replace($regex->t_token, '', $selector_string); - } + // Parse selectors. + // Strip any other comments then create selector instances. + $selector_string = trim(CssCrush_Util::stripCommentTokens($selector_string)); - // Parse the selectors chunk - if (! empty($selector_string)) { + foreach (CssCrush_Util::splitDelimList($selector_string) as $selector) { - $selectors = CssCrush_Util::splitDelimList($selector_string); + // If the selector matches an absract directive + if (preg_match($regex->abstract, $selector, $m)) { - // Remove and store comments that sit above the first selector - // remove all comments between the other selectors - if (strpos($selectors[0], '?c') !== false) { - preg_match_all($regex->c_token, $selectors[0], $m); - $this->comments = $m[0]; + // Link the rule to the abstract name and skip forward to declaration parsing. + $process->references[strtolower($m['name'])] = $this; } - - // Strip any other comments then create selector instances - foreach ($selectors as $selector) { - - $selector = trim(CssCrush_Util::stripCommentTokens($selector)); - - // If the selector matches an absract directive - if (preg_match($regex->abstract, $selector, $m)) { - - // Link the rule to the abstract name and skip forward to declaration parsing. - $process->references[strtolower($m['name'])] = $this; - } - else { - $this->addSelector(new CssCrush_Selector($selector)); - } + else { + $this->addSelector(new CssCrush_Selector($selector)); } } @@ -100,6 +72,7 @@ public function __construct ($selector_string = null, $declarations_string) } } + // Bind declaration objects on the rule. foreach ($pairs as $index => &$pair) { @@ -147,14 +120,11 @@ public function __toString () $formatter = $process->ruleFormatter ? $process->ruleFormatter : 'csscrush__fmtr_block'; - if ($comments = implode('', $this->comments)) { - $comments = "$EOL$comments"; - } if ($stub = $this->tracingStub) { $stub .= $EOL; } - return "$comments$stub{$formatter($this)}"; + return "$stub{$formatter($this)}"; } } From 8e226f58a6a3ff468832253e394364aebb857b72 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 12 Jul 2013 10:01:36 +0100 Subject: [PATCH 132/421] Refactoring for source mapping. Cleaned up some regexes. --- lib/CssCrush/Importer.php | 24 +++++++++--------------- lib/CssCrush/Process.php | 26 ++++++++++++++++++++++++-- lib/CssCrush/Regex.php | 34 ++++++++++++---------------------- lib/CssCrush/Stream.php | 6 +++--- lib/CssCrush/Tokens.php | 8 ++++++-- 5 files changed, 54 insertions(+), 44 deletions(-) diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index 4404932..f6d62d3 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -23,11 +23,11 @@ static public function hostfile () // Resolve main input; a string of css or a file. if (isset($input->string)) { $str .= $input->string; - $process->currentFile = 'inline css'; + $process->sources[] = 'Inline CSS'; } else { $str .= file_get_contents($input->path); - $process->currentFile = 'file://' . $input->path; + $process->sources[] = $input->path; } // If there's a parsing error go no further. @@ -89,7 +89,7 @@ static public function hostfile () // Import file opened successfully so we process it: // - We need to resolve import statement urls in all imported files since // they will be brought inline with the hostfile - $process->currentFile = 'file://' . $import->path; + $process->sources[] = $import->path; // If there are unmatched brackets inside the import, strip it. if (! self::prepareForStream($import->content)) { @@ -211,9 +211,7 @@ static protected function prepareForStream (&$str) $str = $tokens->captureUrls($str); - // if ($process->addTracingStubs) { - self::addTracingStubs($str); - // } + self::addTracingStubs($str); $str = CssCrush_Util::normalizeWhiteSpace($str); @@ -222,11 +220,11 @@ static protected function prepareForStream (&$str) static protected function checkSyntax (&$str) { - // TODO: add more sophisticated error detection such as line/column of unmatched bracket. + // TODO: add more sophisticated error detection such as line/column of an unmatched bracket. // Catch obvious typing errors. $parse_errors = array(); - $current_file = CssCrush::$process->currentFile; + $current_file = 'file://' . end(CssCrush::$process->sources); $balanced_parens = substr_count($str, "(") === substr_count($str, ")"); $balanced_curlies = substr_count($str, "{") === substr_count($str, "}"); @@ -249,13 +247,9 @@ static protected function checkSyntax (&$str) static protected function addTracingStubs (&$str) { - $count = preg_match_all(CssCrush_Regex::$patt->rule2, $str, $matches, PREG_OFFSET_CAPTURE); - - // Get the currently processed file path, and escape it. - $current_file = str_replace(' ', '%20', CssCrush::$process->currentFile); - $current_file = preg_replace('~[^\w-]~', '\\\\$0', $current_file); - $debug_info = '@media -sass-debug-info{filename{font-family:%s}line{font-family:\\00003%d}}'; + $current_file_index = count(CssCrush::$process->sources) -1; + $count = preg_match_all(CssCrush_Regex::$patt->ruleFirstPass, $str, $matches, PREG_OFFSET_CAPTURE); while ($count--) { $selector_offset = $matches['selector'][$count][1]; @@ -268,7 +262,7 @@ static protected function addTracingStubs (&$str) // Splice in tracing stub. $str = substr_replace( $str, - CssCrush::$process->tokens->add(sprintf($debug_info, $current_file, $line), 't'), + CssCrush::$process->tokens->add(array($current_file_index, $line), 't'), $selector_offset, 0); } diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 4435c41..564cfd1 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -30,7 +30,7 @@ public function __construct ($options) $this->errors = array(); $this->stat = array(); $this->charset = null; - $this->currentFile = null; + $this->sources = array(); $this->vars = array(); $this->misc = new stdClass(); $this->input = new stdClass(); @@ -942,7 +942,29 @@ protected function collate () // Restore remaining tokens. $this->stream->replaceTokens('u'); $this->stream->replaceTokens('s'); - $this->stream->replaceTokens('t'); + + + static $tracing_callback; + if ($this->addTracingStubs) { + + if (! $tracing_callback) { + $tracing_callback = function ($m) { + $process = CssCrush::$process; + $tokens =& $process->tokens->store->t; + if (! isset($tokens[$m[0]])) { + return ''; + } + list($source_index, $line) = $tokens[$m[0]]; + // Get the currently processed file path, and escape it. + $current_file = 'file://' . str_replace(' ', '%20', $process->sources[$source_index]); + $current_file = preg_replace('~[^\w-]~', '\\\\$0', $current_file); + + return "@media -sass-debug-info{filename{font-family:$current_file}line{font-family:\\00003$line}}"; + }; + } + + $this->stream->replaceTokens('t', $tracing_callback); + } } public function compile ($io_context = 'file') diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php index 9ded6d5..9651101 100644 --- a/lib/CssCrush/Regex.php +++ b/lib/CssCrush/Regex.php @@ -28,13 +28,14 @@ static public function init () $classes->color_hex = '#[[:xdigit:]]{3}(?:[[:xdigit:]]{3})?'; // Tokens. - $classes->c_token = '\?c[0-9a-z]+\?'; // Comments. - $classes->s_token = '\?s[0-9a-z]+\?'; // Strings. - $classes->r_token = '\?r[0-9a-z]+\?'; // Rules. - $classes->p_token = '\?p[0-9a-z]+\?'; // Parens. - $classes->u_token = '\?u[0-9a-z]+\?'; // URLs. - $classes->t_token = '\?t[0-9a-z]+\?'; // Traces. - $classes->a_token = '\?a([0-9a-z]+)\?'; // Args. + $classes->token_id = '[0-9a-z]+'; + $classes->c_token = '\?c' . $classes->token_id . '\?'; // Comments. + $classes->s_token = '\?s' . $classes->token_id . '\?'; // Strings. + $classes->r_token = '\?r' . $classes->token_id . '\?'; // Rules. + $classes->p_token = '\?p' . $classes->token_id . '\?'; // Parens. + $classes->u_token = '\?u' . $classes->token_id . '\?'; // URLs. + $classes->t_token = '\?t' . $classes->token_id . '\?'; // Traces. + $classes->a_token = '\?a(' . $classes->token_id . ')\?'; // Args. // Boundries. $classes->LB = '(?varFunction = CssCrush_Regex::create('\$\( \s* ({{ident}}) \s* \)', 'xS'); $patt->thisFunction = CssCrush_Regex::createFunctionPatt(array('this')); + // Strings and comments. $patt->string = '~(\'|")(?:\\\\\1|[^\1])*?\1~xS'; $patt->commentAndString = '~ # Quoted string (to EOF if unmatched). @@ -83,13 +85,15 @@ static public function init () /\*(?:.*?)(?:\*/|$) ~xsS'; - $patt->rule2 = CssCrush_Regex::create(' + // Rules. + $patt->ruleFirstPass = CssCrush_Regex::create(' (?:^|(?<=[;{}])) (? (?: \s | {{c-token}} )* ) (? (?: + # Some @-rules are treated like standard rule blocks. @(?: (?i)page|abstract|font-face(?-i) ) {{RB}} [^{]* | [^@;{}]+ @@ -104,20 +108,6 @@ static public function init () \s* {{block}}', 'xiS'); - // As an exception we treat some @-rules like standard rule blocks. - // $patt->rule = '~ - // # The selector. - // \n( - // [^@{}]+ - // | - // (?: [^@{}]+ )? @(?: font-face|abstract|page ) (?!-)\b [^{]* - // ) - // # The declaration block. - // \{ ([^{}]*) \} - // ~xiS'; - - - // Balanced bracket matching. $patt->balancedParens = '~\(\s* ( (?: (?>[^()]+) | (?R) )* ) \s*\)~xS'; $patt->balancedCurlies = '~\{\s* ( (?: (?>[^{}]+) | (?R) )* ) \s*\}~xS'; diff --git a/lib/CssCrush/Stream.php b/lib/CssCrush/Stream.php index 5ae6aea..2e7a10d 100644 --- a/lib/CssCrush/Stream.php +++ b/lib/CssCrush/Stream.php @@ -63,7 +63,7 @@ public function replaceHash ($replacements) return $this; } - public function replaceTokens ($type) + public function replaceTokens ($type, $callback = null) { static $types = array(); if (! isset($types[$type])) { @@ -71,10 +71,10 @@ public function replaceTokens ($type) $tokens =& CssCrush::$process->tokens->store->' . $type . '; return isset($tokens[$m[0]]) ? $tokens[$m[0]] : \'\'; '); - $types[$type]['patt'] = '~\?' . $type . '[a-zA-Z0-9]+\?~S'; + $types[$type]['patt'] = CssCrush_Regex::create("\? $type {{token-id}} \?", 'xS'); } - $this->raw = preg_replace_callback($types[$type]['patt'], $types[$type]['callback'], $this->raw); + $this->raw = preg_replace_callback($types[$type]['patt'], $callback ? $callback : $types[$type]['callback'], $this->raw); return $this; } diff --git a/lib/CssCrush/Tokens.php b/lib/CssCrush/Tokens.php index 06c3f91..3f7014d 100644 --- a/lib/CssCrush/Tokens.php +++ b/lib/CssCrush/Tokens.php @@ -215,8 +215,12 @@ protected function cb_captureCommentAndString ($match) static public function is ($label, $of_type) { - if (preg_match('~^\?([a-z])[0-9a-z]+\?$~S', $label, $m)) { - return $of_type ? ($of_type === $m[1]) : true; + static $type_patt; + if (! $type_patt) { + $type_patt = CssCrush_Regex::create('^ \? (?[a-z]) {{token-id}} \? $', 'xS'); + } + if (preg_match($type_patt, $label, $m)) { + return $of_type ? ($of_type === $m['type']) : true; } return false; } From 7648e6bdf13ec4ee4ae641f96da4325fcf4be423 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 15 Jul 2013 09:38:05 +0100 Subject: [PATCH 133/421] Removing some redundant comments. --- CssCrush.php | 3 ++- lib/CssCrush/Importer.php | 3 +-- lib/CssCrush/Process.php | 19 ++----------------- lib/functions.php | 2 +- 4 files changed, 6 insertions(+), 21 deletions(-) diff --git a/CssCrush.php b/CssCrush.php index b140f4c..b97a811 100644 --- a/CssCrush.php +++ b/CssCrush.php @@ -6,7 +6,7 @@ */ function csscrush_autoload ($class) { - // Only autoload classes with the library prefix. + // We're only autoloading this library. if (stripos($class, 'csscrush') !== 0) { return; } @@ -23,4 +23,5 @@ function csscrush_autoload ($class) { // Core.php will also be autoloaded with API changes in v2.x. require_once 'lib/CssCrush/Core.php'; + require_once 'lib/functions.php'; diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index f6d62d3..87a6329 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -168,7 +168,6 @@ static protected function rewriteImportedUrls ($import) foreach ($matches[0] as $token) { - // Fetch the matched URL. $url = CssCrush::$process->tokens->get($token); if ($url->isRelative) { @@ -198,7 +197,7 @@ static protected function prepareForStream (&$str) // Normalize double-colon pseudo elements for backwards compatability. $str = preg_replace('~::(after|before|first-(?:letter|line))~iS', ':$1', $str); - // If @charset is set store it. + // Store @charset if set. if (preg_match($regex->charset, $str, $m)) { $replace = ''; if (! $process->charset) { diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 564cfd1..387692c 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -68,7 +68,6 @@ public function __construct ($options) break; } - // Run process_init hook. CssCrush_Hook::run('process_init'); } @@ -127,7 +126,6 @@ public function ioCall ($method) // The method address $the_method = array(CssCrush::$config->io, $method); - // Return the call result return call_user_func_array($the_method, $args); } @@ -155,7 +153,6 @@ protected function getBoilerplate () return ''; } - // Load the file $boilerplate = file_get_contents($file); // Substitute any tags @@ -812,7 +809,6 @@ protected function aliasAtRules () $originals[] = $rule_label; $clone_rule = clone $this->tokens->get($rule_label); - // Set the vendor context. $clone_rule->vendorContext = $vendor; // Store the clone. @@ -870,7 +866,6 @@ protected function collate () // Apply all formatting replacements. $this->stream->pregReplaceHash($regex_replacements)->lTrim(); - // Print out rules. $this->stream->replaceTokens('r'); // Run rule related stats then reclaim memory. @@ -878,7 +873,6 @@ protected function collate () CssCrush::runStat('rule_count'); $this->tokens->store->r = array(); - // Restore parens. $this->stream->replaceTokens('p'); // If specified, apply advanced minification. @@ -888,7 +882,6 @@ protected function collate () } } - // Compress hex-codes, collapse TRBL lists etc. $this->decruft(); if ($minify) { @@ -978,7 +971,7 @@ public function compile ($io_context = 'file') $this->filterPlugins(); $this->filterAliases(); - // Create function matching regex. + // Function matching regex. CssCrush_Function::setMatchPatt(); // Collate hostfile and imports. @@ -987,41 +980,33 @@ public function compile ($io_context = 'file') // Extract and calculate variables. $this->calculateVars(); - // Place variables. $this->placeAllVars(); - // Resolve @ifdefine blocks. $this->resolveIfDefines(); // Capture phase 1 hook: After all vars have resolved. CssCrush_Hook::run('capture_phase1', $this); - // Get selector aliases. + // Selector aliases. $this->resolveSelectorAliases(); - // Pull out @mixin definitions. $this->captureMixins(); - // Pull out @fragment blocks, and invoke. $this->resolveFragments(); // Capture phase 2 hook: After most built-in directives have resolved. CssCrush_Hook::run('capture_phase2', $this); - // Parse rules. $this->captureRules(); // csscrush::log(array_keys($this->references)); - // Process @in blocks. $this->prefixSelectors(); - // Alias any @-rules. $this->aliasAtRules(); // Main processing on the rule objects. $this->processRules(); - // Print rules, optionally minify. $this->collate(); // Release memory. diff --git a/lib/functions.php b/lib/functions.php index 19548f6..8a1ee15 100644 --- a/lib/functions.php +++ b/lib/functions.php @@ -1,7 +1,7 @@ Date: Mon, 15 Jul 2013 15:28:06 +0100 Subject: [PATCH 134/421] Fixing rule_preprocess hook. --- lib/CssCrush/Process.php | 7 ------- lib/CssCrush/Rule.php | 12 +++++++++++- plugins/spiffing.php | 2 +- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 387692c..97e6d4d 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -647,13 +647,6 @@ protected function processRules () static public function cb_captureRules ($m) { - // $rule = (object) array(); - // $rule->selector_raw = trim($m[1]); - // $rule->declaration_raw = trim($m[2]); - - // Run rule_preprocess hook. - // CssCrush_Hook::run('rule_preprocess', $rule); - $selector = trim($m['selector']); $block = trim($m['block_content']); diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php index d8065d9..5207fb9 100644 --- a/lib/CssCrush/Rule.php +++ b/lib/CssCrush/Rule.php @@ -34,6 +34,17 @@ public function __construct ($selector_string, $declarations_string, $trace_toke $this->label = $process->tokens->createLabel('r'); $this->tracingStub = $process->addTracingStubs ? $trace_token : null; + if (! empty(CssCrush_Hook::$register['rule_preprocess'])) { + // Juggling to maintain the old API. + // TODO: rework this for 2.x? + $rule = new stdClass(); + $rule->selector_raw = $selector_string; + $rule->declaration_raw = $declarations_string; + CssCrush_Hook::run('rule_preprocess', $rule); + $selector_string = $rule->selector_raw; + $declarations_string = $rule->declaration_raw; + } + // Parse selectors. // Strip any other comments then create selector instances. $selector_string = trim(CssCrush_Util::stripCommentTokens($selector_string)); @@ -72,7 +83,6 @@ public function __construct ($selector_string, $declarations_string, $trace_toke } } - // Bind declaration objects on the rule. foreach ($pairs as $index => &$pair) { diff --git a/plugins/spiffing.php b/plugins/spiffing.php index f97db65..a8b9106 100644 --- a/plugins/spiffing.php +++ b/plugins/spiffing.php @@ -24,7 +24,7 @@ function csscrush__enable_spiffing () { } function csscrush__disable_spiffing () { - CssCrush_Hook::add('rule_preprocess', 'csscrush__spiffing'); + CssCrush_Hook::remove('rule_preprocess'); } function csscrush__spiffing ($rule) { From 3afdff40cae31fd3555e2bbd69e500ab48314326 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 16 Jul 2013 09:46:56 +0100 Subject: [PATCH 135/421] Fixed issue with Util::vlqEncode(). --- lib/CssCrush/Util.php | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php index 82c71ad..19832f4 100644 --- a/lib/CssCrush/Util.php +++ b/lib/CssCrush/Util.php @@ -6,7 +6,9 @@ */ class CssCrush_Util { - // Create html attribute string from array. + /* + * Create html attribute string from array. + */ static public function htmlAttributes (array $attributes) { $attr_string = ''; @@ -142,31 +144,27 @@ static public function getLinkBetweenDirs ($dir1, $dir2) */ static public function vlqEncode ($value) { - static $SHIFT, $MASK, $CONTINUATION_BIT, $BASE64_MAP; - if (! $SHIFT) { - $SHIFT = 5; - $MASK = 0x1F; - $CONTINUATION_BIT = 0x20; - $BASE64_MAP = array_merge(range('A', 'Z'), range('a', 'z'), array('+', '/')); + static $VLQ_BASE_SHIFT, $VLQ_BASE, $VLQ_BASE_MASK, $VLQ_CONTINUATION_BIT, $BASE64_MAP; + if (! $VLQ_BASE_SHIFT) { + $VLQ_BASE_SHIFT = 5; + $VLQ_BASE = 1 << $VLQ_BASE_SHIFT; + $VLQ_BASE_MASK = $VLQ_BASE - 1; + $VLQ_CONTINUATION_BIT = $VLQ_BASE; + $BASE64_MAP = str_split('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'); } - if ($value < 0) { - $value = ((-$value) << 1) | 1; - } - else { - $value <<= 1; - } + $vlq = $value < 0 ? ((-$value) << 1) + 1 : ($value << 1) + 0; $encoded = ""; do { - $digit = $value & $MASK; - $value >>= $SHIFT; - if ($value > 0) { - $digit |= $CONTINUATION_BIT; - } - $encoded .= $BASE64_MAP[$digit]; - } - while ($value > 0); + $digit = $vlq & $VLQ_BASE_MASK; + $vlq >>= $VLQ_BASE_SHIFT; + if ($vlq > 0) { + $digit |= $VLQ_CONTINUATION_BIT; + } + $encoded .= $BASE64_MAP[$digit]; + + } while ($vlq > 0); return $encoded; } From b8cfb805bbb58fed28c274bd8571a061694c9971 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 17 Jul 2013 11:20:13 +0100 Subject: [PATCH 136/421] Added padding method for more accurate source mapping. --- lib/CssCrush/Importer.php | 2 +- lib/CssCrush/Process.php | 4 ++-- lib/CssCrush/Tokens.php | 38 +++++++++++++++++++++++--------------- 3 files changed, 26 insertions(+), 18 deletions(-) diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index 87a6329..6c601bf 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -208,7 +208,7 @@ static protected function prepareForStream (&$str) $str = preg_replace($regex->charset, $replace, $str); } - $str = $tokens->captureUrls($str); + $str = $tokens->captureUrls($str, true); self::addTracingStubs($str); diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 97e6d4d..75bb135 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -852,8 +852,8 @@ protected function collate () $regex_replacements['~ ?(@[^{]+\{)~'] = "$1$EOL"; $regex_replacements['~ ?(@[^;]+\;)~'] = "$1$EOL"; - // Trim leading spaces on rule and comment tokens - $regex_replacements[CssCrush_Regex::create(' +({{r-token}}|{{c-token}})', 'S')] = "$1"; + // Trim leading spaces on @-rules and some tokens. + $regex_replacements[CssCrush_Regex::create(' +([@}]|\?[rc]{{token-id}}\?)', 'S')] = "$1"; } // Apply all formatting replacements. diff --git a/lib/CssCrush/Tokens.php b/lib/CssCrush/Tokens.php index 3f7014d..11dac44 100644 --- a/lib/CssCrush/Tokens.php +++ b/lib/CssCrush/Tokens.php @@ -119,7 +119,7 @@ public function captureParens ($str) return preg_replace_callback(CssCrush_Regex::$patt->balancedParens, $callback, $str); } - public function captureStrings ($str) + public function captureStrings ($str, $add_padding = false) { static $callback; if (! $callback) { @@ -128,7 +128,7 @@ public function captureStrings ($str) return preg_replace_callback(CssCrush_Regex::$patt->string, $callback, $str); } - public function captureUrls ($str) + public function captureUrls ($str, $add_padding = false) { static $url_patt; if (! $url_patt) { @@ -146,23 +146,18 @@ public function captureUrls ($str) // @import directive. if ($import_offset !== -1) { - $url = new CssCrush_Url($import_text); - $str = str_replace($import_text, $url->label, $str); + $url = new CssCrush_Url(trim($import_text)); + $str = str_replace($import_text, $add_padding ? str_pad($url->label, strlen($import_text)) : $url->label, $str); } // A URL function. else { - $newlines = ''; - if (strpos($full_text, "\n") !== false) { - $newlines = str_repeat("\n", substr_count($full_text, "\n")); - } - $func_name = strtolower($m['func'][$count][0]); $url = new CssCrush_Url(trim($m['parens_content'][$count][0])); $url->convertToData = 'data-uri' === $func_name; - $str = substr_replace($str, $url->label . $newlines, $full_offset, strlen($full_text)); + $str = substr_replace($str, $add_padding ? CssCrush_Tokens::pad($url->label, $full_text) : $url->label, $full_offset, strlen($full_text)); } } @@ -180,9 +175,6 @@ protected function cb_captureCommentAndString ($match) $full_match = $match[0]; $process = CssCrush::$process; - // We return the newline count to keep track of line numbering. - $newlines = str_repeat("\n", substr_count($full_match, "\n")); - if (strpos($full_match, '/*') === 0) { // Bail without storing comment if output is minified or a private comment. @@ -190,7 +182,7 @@ protected function cb_captureCommentAndString ($match) $process->minifyOutput || strpos($full_match, '/*$') === 0 ) { - return $newlines; + return CssCrush_Tokens::pad('', $full_match); } // Fix broken comments as they will break any subsquent @@ -210,7 +202,23 @@ protected function cb_captureCommentAndString ($match) $label = $process->tokens->add($full_match, 's'); } - return $newlines . $label; + return CssCrush_Tokens::pad($label, $full_match); + } + + static public function pad ($label, $replaced_text) + { + // Padding token labels to maintain whitespace and newlines. + + // Match contains newlines. + if (($last_newline_pos = strrpos($replaced_text, "\n")) !== false) { + $label .= str_repeat("\n", substr_count($replaced_text, "\n")) . str_repeat(' ', strlen(substr($replaced_text, $last_newline_pos))-1); + } + // Match contains no newlines. + else { + $label = str_pad($label, strlen($replaced_text)); + } + + return $label; } static public function is ($label, $of_type) From 9d981ea5ccf701a94a2202f94921049eb98976fe Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 17 Jul 2013 16:04:06 +0100 Subject: [PATCH 137/421] Trimming trailing comments and whitespace in all source files to avoid the regex backtracking limit. --- lib/CssCrush/Importer.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index 6c601bf..fda54eb 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -188,7 +188,13 @@ static protected function prepareForStream (&$str) $str = $tokens->captureCommentAndString($str); - // Validate syntax as early as possible. + // Avoid backtracking limit by trimming trailing WS and comments. + static $trailing_ws_and_comments; + if (! $trailing_ws_and_comments) { + $trailing_ws_and_comments = CssCrush_Regex::create('(?: \s | {{c-token}} )*$', 'xS'); + } + $str = preg_replace($trailing_ws_and_comments, '', $str); + if (! self::checkSyntax($str)) { return false; From 96bd33262036eef13b53f1520af712a6f274fca1 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 18 Jul 2013 09:39:28 +0100 Subject: [PATCH 138/421] Moving captureCommentAndString method to Importer. --- lib/CssCrush/Importer.php | 43 ++++++++++++++++++++++++++++++++++++++- lib/CssCrush/Tokens.php | 41 ------------------------------------- 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index fda54eb..0d3b722 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -186,7 +186,7 @@ static protected function prepareForStream (&$str) // Convert all end-of-lines to unix style. $str = preg_replace('~\r\n?~', "\n", $str); - $str = $tokens->captureCommentAndString($str); + $str = self::captureCommentAndString($str); // Avoid backtracking limit by trimming trailing WS and comments. static $trailing_ws_and_comments; @@ -272,4 +272,45 @@ static protected function addTracingStubs (&$str) 0); } } + + static protected function captureCommentAndString ($str) + { + return preg_replace_callback(CssCrush_Regex::$patt->commentAndString, + array('self', 'cb_captureCommentAndString'), $str); + } + + static protected function cb_captureCommentAndString ($match) + { + $full_match = $match[0]; + $process = CssCrush::$process; + + if (strpos($full_match, '/*') === 0) { + + // Bail without storing comment if output is minified or a private comment. + if ( + $process->minifyOutput || + strpos($full_match, '/*$') === 0 + ) { + return CssCrush_Tokens::pad('', $full_match); + } + + // Fix broken comments as they will break any subsquent + // imported files that are inlined. + if (! preg_match('~\*/$~', $full_match)) { + $full_match .= '*/'; + } + $label = $process->tokens->add($full_match, 'c'); + } + else { + + // Fix broken strings as they will break any subsquent + // imported files that are inlined. + if ($full_match[0] !== $full_match[strlen($full_match)-1]) { + $full_match .= $full_match[0]; + } + $label = $process->tokens->add($full_match, 's'); + } + + return CssCrush_Tokens::pad($label, $full_match); + } } diff --git a/lib/CssCrush/Tokens.php b/lib/CssCrush/Tokens.php index 11dac44..be9fe02 100644 --- a/lib/CssCrush/Tokens.php +++ b/lib/CssCrush/Tokens.php @@ -164,47 +164,6 @@ public function captureUrls ($str, $add_padding = false) return $str; } - public function captureCommentAndString ($str) - { - return preg_replace_callback(CssCrush_Regex::$patt->commentAndString, - array('self', 'cb_captureCommentAndString'), $str); - } - - protected function cb_captureCommentAndString ($match) - { - $full_match = $match[0]; - $process = CssCrush::$process; - - if (strpos($full_match, '/*') === 0) { - - // Bail without storing comment if output is minified or a private comment. - if ( - $process->minifyOutput || - strpos($full_match, '/*$') === 0 - ) { - return CssCrush_Tokens::pad('', $full_match); - } - - // Fix broken comments as they will break any subsquent - // imported files that are inlined. - if (! preg_match('~\*/$~', $full_match)) { - $full_match .= '*/'; - } - $label = $process->tokens->add($full_match, 'c'); - } - else { - - // Fix broken strings as they will break any subsquent - // imported files that are inlined. - if ($full_match[0] !== $full_match[strlen($full_match)-1]) { - $full_match .= $full_match[0]; - } - $label = $process->tokens->add($full_match, 's'); - } - - return CssCrush_Tokens::pad($label, $full_match); - } - static public function pad ($label, $replaced_text) { // Padding token labels to maintain whitespace and newlines. From 30646f0abfc4580e2ae7193af784c67194ce7471 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 19 Jul 2013 09:15:42 +0100 Subject: [PATCH 139/421] Earlier backtracking fix had issues with large files, reverting to a simpler rtrim to avoid the issue. --- lib/CssCrush/Importer.php | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index 0d3b722..6499749 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -186,14 +186,8 @@ static protected function prepareForStream (&$str) // Convert all end-of-lines to unix style. $str = preg_replace('~\r\n?~', "\n", $str); - $str = self::captureCommentAndString($str); - - // Avoid backtracking limit by trimming trailing WS and comments. - static $trailing_ws_and_comments; - if (! $trailing_ws_and_comments) { - $trailing_ws_and_comments = CssCrush_Regex::create('(?: \s | {{c-token}} )*$', 'xS'); - } - $str = preg_replace($trailing_ws_and_comments, '', $str); + // rtrim is necessary to avoid catastrophic backtracking in large files and some edge cases. + $str = rtrim(self::captureCommentAndString($str)); if (! self::checkSyntax($str)) { From 6be5b9d60fc69858131d8c2777ee0c4cdcd12162 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 19 Jul 2013 09:23:10 +0100 Subject: [PATCH 140/421] Adding newline regex. --- lib/CssCrush/Process.php | 2 +- lib/CssCrush/Regex.php | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 75bb135..58beee7 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -190,7 +190,7 @@ protected function getBoilerplate () // Pretty print. $EOL = $this->newline; - $boilerplate = preg_split('~[\t]*(\r\n?|\n)[\t]*~', $boilerplate); + $boilerplate = preg_split('~[\t]*'. CssCrush_Regex::$classes->newline . '[\t]*~', $boilerplate); $boilerplate = array_map('trim', $boilerplate); $boilerplate = "$EOL * " . implode("$EOL * ", $boilerplate); diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php index 9651101..7afec91 100644 --- a/lib/CssCrush/Regex.php +++ b/lib/CssCrush/Regex.php @@ -49,6 +49,7 @@ static public function init () // Misc. $classes->vendor = '-[a-zA-Z]+-'; $classes->hex = '[[:xdigit:]]'; + $classes->newline = '(\r\n?|\n)'; // Create standalone class patterns, add classes as class swaps. foreach ($classes as $name => $class) { From 426c7412fdcbd27e71a0b936952b6ebb77fdb0e1 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 19 Jul 2013 20:41:06 +0100 Subject: [PATCH 141/421] Parent symbol can now be used any number of times (useful for adjacent/general sibling combinations). Moved source mapping capture into it`s own method. --- lib/CssCrush/Process.php | 56 +++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 58beee7..564a13f 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -722,12 +722,10 @@ protected function prefixSelectors () // Positioning the prefix with parent symbol "&". elseif ($use_parent_symbol) { - // Find and replace the ampersand (only once). - $new_value = preg_replace( - '~&~', + $new_value = str_replace( + '&', $arg_selector, - $rule_selector->value, - 1); + $rule_selector->value); $new = new CssCrush_Selector($new_value); $new_selector_list[$new->readableValue] = $new; @@ -925,31 +923,11 @@ protected function collate () $this->stream->prepend("@charset \"$this->charset\";$EOL"); } - // Restore remaining tokens. $this->stream->replaceTokens('u'); $this->stream->replaceTokens('s'); - - static $tracing_callback; if ($this->addTracingStubs) { - - if (! $tracing_callback) { - $tracing_callback = function ($m) { - $process = CssCrush::$process; - $tokens =& $process->tokens->store->t; - if (! isset($tokens[$m[0]])) { - return ''; - } - list($source_index, $line) = $tokens[$m[0]]; - // Get the currently processed file path, and escape it. - $current_file = 'file://' . str_replace(' ', '%20', $process->sources[$source_index]); - $current_file = preg_replace('~[^\w-]~', '\\\\$0', $current_file); - - return "@media -sass-debug-info{filename{font-family:$current_file}line{font-family:\\00003$line}}"; - }; - } - - $this->stream->replaceTokens('t', $tracing_callback); + $this->captureMappings(); } } @@ -960,11 +938,9 @@ public function compile ($io_context = 'file') // Always store start time. $this->stat['compile_start_time'] = microtime(true); - // Resolve active aliases and plugins. $this->filterPlugins(); $this->filterAliases(); - // Function matching regex. CssCrush_Function::setMatchPatt(); // Collate hostfile and imports. @@ -980,7 +956,6 @@ public function compile ($io_context = 'file') // Capture phase 1 hook: After all vars have resolved. CssCrush_Hook::run('capture_phase1', $this); - // Selector aliases. $this->resolveSelectorAliases(); $this->captureMixins(); @@ -1011,6 +986,29 @@ public function compile ($io_context = 'file') } + ############################# + # Source maps. + + public function captureMappings () + { + $this->stream->replaceTokens('t', array($this, 'captureMappings_cb')); + } + + public function captureMappings_cb ($m) + { + $tokens =& $this->tokens->store->t; + if (! isset($tokens[$m[0]])) { + return ''; + } + list($source_index, $line) = $tokens[$m[0]]; + // Get the currently processed file path, and escape it. + $current_file = 'file://' . str_replace(' ', '%20', $this->sources[$source_index]); + $current_file = preg_replace('~[^\w-]~', '\\\\$0', $current_file); + + return "@media -sass-debug-info{filename{font-family:$current_file}line{font-family:\\00003$line}}"; + } + + ############################# # Decruft. From 616cc2fc3a5261beefef6e5af0c7eed60ac94216 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 19 Jul 2013 22:11:01 +0100 Subject: [PATCH 142/421] Added check for overly conservative ini settings. --- lib/CssCrush/Process.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 564a13f..2939763 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -933,6 +933,14 @@ protected function collate () public function compile ($io_context = 'file') { + // Ensure relevant ini settings aren't too conservative. + if (ini_get('pcre.backtrack_limit') < 1000000) { + ini_set('pcre.backtrack_limit', 1000000); + } + if (preg_match('~^(\d+)M$~', ini_get('memory_limit'), $m) && $m[1] < 128) { + ini_set('memory_limit', '128M'); + } + $this->ioContext = $io_context; // Always store start time. @@ -977,7 +985,6 @@ public function compile ($io_context = 'file') $this->collate(); - // Release memory. $this->release(); CssCrush::runStat('compile_time'); From 0abce0a364dc34bccd181c18ea3623cd6a1d4fc9 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 23 Jul 2013 09:57:02 +0100 Subject: [PATCH 143/421] Some refactoring, method renaming. --- lib/CssCrush/Core.php | 3 +++ lib/CssCrush/Importer.php | 23 ++++++++++++++++------- lib/CssCrush/Process.php | 38 +++++++++++++++++++++++++++----------- lib/CssCrush/Util.php | 30 ++++++++++++++++++------------ 4 files changed, 64 insertions(+), 30 deletions(-) diff --git a/lib/CssCrush/Core.php b/lib/CssCrush/Core.php index 063f3a7..991ce34 100644 --- a/lib/CssCrush/Core.php +++ b/lib/CssCrush/Core.php @@ -86,6 +86,9 @@ static public function init () // Set true to output sass debug-info stubs that work with development tools like FireSass. 'trace' => array(), + // Whether to generate a source map. + 'source_map' => false, + // Force newline type on output files. Defaults to the current platform newline. // Options: 'windows' (or 'win'), 'unix', 'use-platform' 'newlines' => 'use-platform', diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index 6499749..1f7f82b 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -156,7 +156,7 @@ static protected function rewriteImportedUrls ($import) $non_import_urls_patt = CssCrush_Regex::create('(?input->dir, dirname($import->path)); if (empty($link)) { @@ -210,7 +210,7 @@ static protected function prepareForStream (&$str) $str = $tokens->captureUrls($str, true); - self::addTracingStubs($str); + self::addMarkers($str); $str = CssCrush_Util::normalizeWhiteSpace($str); @@ -244,24 +244,33 @@ static protected function checkSyntax (&$str) return empty($parse_errors) ? true : false; } - static protected function addTracingStubs (&$str) + static protected function addMarkers (&$str) { - $current_file_index = count(CssCrush::$process->sources) -1; + $process = CssCrush::$process; + $current_file_index = count($process->sources) -1; $count = preg_match_all(CssCrush_Regex::$patt->ruleFirstPass, $str, $matches, PREG_OFFSET_CAPTURE); while ($count--) { $selector_offset = $matches['selector'][$count][1]; - $line = 1; + $line = 0; + $before = substr($str, 0, $selector_offset); if ($selector_offset) { - $line = substr_count(substr($str, 0, $selector_offset), "\n") + 1; + $line = substr_count($before, "\n"); + } + + $point_data = array($current_file_index, $line); + + // Source maps require column index too. + if ($process->generateMap) { + $point_data[] = strlen($before) - strrpos($before, "\n") - 1; } // Splice in tracing stub. $str = substr_replace( $str, - CssCrush::$process->tokens->add(array($current_file_index, $line), 't'), + $process->tokens->add($point_data, 't'), $selector_offset, 0); } diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 2939763..2bad333 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -43,11 +43,6 @@ public function __construct ($options) $this->selectorAliases = array(); $this->selectorAliasesPatt = null; - // Shortcut commonly used options to avoid overhead with __get calls. - $this->minifyOutput = $this->options->minify; - $this->addTracingStubs = in_array('stubs', $this->options->trace); - $this->ruleFormatter = $this->options->formatter; - // Pick a doc root. $this->docRoot = isset($this->options->doc_root) ? $this->options->doc_root : $config->docRoot; @@ -898,7 +893,7 @@ protected function collate () $urls = $this->tokens->store->u; if ($urls) { - $link = CssCrush_Util::getLinkBetweenDirs($this->output->dir, $this->input->dir); + $link = CssCrush_Util::getLinkBetweenPaths($this->output->dir, $this->input->dir); $make_urls_absolute = $options->rewrite_import_urls === 'absolute'; foreach ($urls as $token => $url) { @@ -926,13 +921,16 @@ protected function collate () $this->stream->replaceTokens('u'); $this->stream->replaceTokens('s'); - if ($this->addTracingStubs) { + if ($this->addTracingStubs || $this->generateMap) { $this->captureMappings(); } } public function compile ($io_context = 'file') { + // Always store start time. + $this->stat['compile_start_time'] = microtime(true); + // Ensure relevant ini settings aren't too conservative. if (ini_get('pcre.backtrack_limit') < 1000000) { ini_set('pcre.backtrack_limit', 1000000); @@ -943,8 +941,11 @@ public function compile ($io_context = 'file') $this->ioContext = $io_context; - // Always store start time. - $this->stat['compile_start_time'] = microtime(true); + // Shortcut commonly used options during compilation to avoid overhead with __get calls. + $this->minifyOutput = $this->options->minify; + $this->addTracingStubs = in_array('stubs', $this->options->trace); + $this->generateMap = $this->ioContext === 'file' && $this->options->source_map; + $this->ruleFormatter = $this->options->formatter; $this->filterPlugins(); $this->filterAliases(); @@ -998,16 +999,31 @@ public function compile ($io_context = 'file') public function captureMappings () { - $this->stream->replaceTokens('t', array($this, 'captureMappings_cb')); + if ($this->generateMap) { + + $this->map = array( + 'version' => '3', + 'file' => $this->output->filename, + 'sources' => array(), + ); + foreach ($this->sources as $source) { + $this->map['sources'][] = CssCrush_Util::getLinkBetweenPaths($this->output->dir, $source, false); + } + $str = json_encode($this->map, JSON_PRETTY_PRINT); + // csscrush::log($str); + } + $this->stream->replaceTokens('t', array($this, 'generateTracingStub')); } - public function captureMappings_cb ($m) + public function generateTracingStub ($m) { $tokens =& $this->tokens->store->t; if (! isset($tokens[$m[0]])) { return ''; } list($source_index, $line) = $tokens[$m[0]]; + $line += 1; + // Get the currently processed file path, and escape it. $current_file = 'file://' . str_replace(' ', '%20', $this->sources[$source_index]); $current_file = preg_replace('~[^\w-]~', '\\\\$0', $current_file); diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php index 19832f4..02d3fad 100644 --- a/lib/CssCrush/Util.php +++ b/lib/CssCrush/Util.php @@ -111,32 +111,38 @@ static public function splitDelimList ($str, $delim = ',') return array_filter(array_map('trim', $list), 'strlen'); } - static public function getLinkBetweenDirs ($dir1, $dir2) + static public function getLinkBetweenPaths ($path1, $path2, $directories = true) { // Normalise the paths. - $dir1 = trim(CssCrush_Util::normalizePath($dir1, true), '/'); - $dir2 = trim(CssCrush_Util::normalizePath($dir2, true), '/'); + $path1 = trim(CssCrush_Util::normalizePath($path1, true), '/'); + $path2 = trim(CssCrush_Util::normalizePath($path2, true), '/'); // The link between. $link = ''; - if ($dir1 != $dir2) { + if ($path1 != $path2) { // Split the directory paths into arrays so we can compare segment by segment. - $dir1_segs = explode('/', $dir1); - $dir2_segs = explode('/', $dir2); + $path1_segs = explode('/', $path1); + $path2_segs = explode('/', $path2); // Shift the segments until they are on different branches. - while (isset($dir1_segs[0]) && isset($dir2_segs[0]) && ($dir1_segs[0] === $dir2_segs[0])) { - array_shift($dir1_segs); - array_shift($dir2_segs); + while (isset($path1_segs[0]) && isset($path2_segs[0]) && ($path1_segs[0] === $path2_segs[0])) { + array_shift($path1_segs); + array_shift($path2_segs); } - $link = str_repeat('../', count($dir1_segs)) . implode('/', $dir2_segs); + $link = str_repeat('../', count($path1_segs)) . implode('/', $path2_segs); } - // Add closing slash. - return $link !== '' ? rtrim($link, '/') . '/' : ''; + $link = $link !== '' ? rtrim($link, '/') : ''; + + // Add end slash if getting a link between directories. + if ($link && $directories) { + $link .= '/'; + } + + return $link; } /* From a2165f98a14c431b5b4d519cc8582522ada6b774 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 24 Jul 2013 10:09:38 +0100 Subject: [PATCH 144/421] Seperated the debug-info generation from source map generation. --- lib/CssCrush/Process.php | 45 +++++++++++++++++++++++----------------- lib/CssCrush/Rule.php | 9 +++----- 2 files changed, 29 insertions(+), 25 deletions(-) diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 2bad333..e5fb31a 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -921,8 +921,11 @@ protected function collate () $this->stream->replaceTokens('u'); $this->stream->replaceTokens('s'); - if ($this->addTracingStubs || $this->generateMap) { - $this->captureMappings(); + if ($this->addTracingStubs) { + $this->stream->replaceTokens('t', array($this, 'generateTracingStub')); + } + if ($this->generateMap) { + $this->generateSourceMap(); } } @@ -997,38 +1000,42 @@ public function compile ($io_context = 'file') ############################# # Source maps. - public function captureMappings () + public function generateSourceMap () { - if ($this->generateMap) { - - $this->map = array( - 'version' => '3', - 'file' => $this->output->filename, - 'sources' => array(), - ); - foreach ($this->sources as $source) { - $this->map['sources'][] = CssCrush_Util::getLinkBetweenPaths($this->output->dir, $source, false); - } - $str = json_encode($this->map, JSON_PRETTY_PRINT); - // csscrush::log($str); + $this->map = array( + 'version' => '3', + 'file' => $this->output->filename, + 'sources' => array(), + ); + foreach ($this->sources as $source) { + $this->map['sources'][] = CssCrush_Util::getLinkBetweenPaths($this->output->dir, $source, false); } - $this->stream->replaceTokens('t', array($this, 'generateTracingStub')); + // $str = json_encode($this->map, JSON_PRETTY_PRINT); } public function generateTracingStub ($m) { + $token = $m[0]; $tokens =& $this->tokens->store->t; - if (! isset($tokens[$m[0]])) { + if (! isset($tokens[$token])) { return ''; } - list($source_index, $line) = $tokens[$m[0]]; + list($source_index, $line) = $tokens[$token]; $line += 1; // Get the currently processed file path, and escape it. $current_file = 'file://' . str_replace(' ', '%20', $this->sources[$source_index]); $current_file = preg_replace('~[^\w-]~', '\\\\$0', $current_file); + $debug_info = "@media -sass-debug-info{filename{font-family:$current_file}line{font-family:\\00003$line}}"; + + if (! $this->minifyOutput) { + $debug_info .= $this->newline; + } + if ($this->generateMap) { + $debug_info .= $token; + } - return "@media -sass-debug-info{filename{font-family:$current_file}line{font-family:\\00003$line}}"; + return $debug_info; } diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php index 5207fb9..a1adb48 100644 --- a/lib/CssCrush/Rule.php +++ b/lib/CssCrush/Rule.php @@ -118,22 +118,19 @@ public function __toString () return ''; } + $stub = $this->tracingStub; + // Concat and return. if ($process->minifyOutput) { $selectors = implode(',', $this->selectors); $block = implode(';', $this->declarations); - return "$this->tracingStub$selectors{{$block}}"; + return "$stub$selectors{{$block}}"; } else { - $EOL = CssCrush::$process->newline; $formatter = $process->ruleFormatter ? $process->ruleFormatter : 'csscrush__fmtr_block'; - if ($stub = $this->tracingStub) { - $stub .= $EOL; - } - return "$stub{$formatter($this)}"; } } From 12487657f5f9c00828190d28dba9a087a8bffcba Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 25 Jul 2013 14:25:48 +0100 Subject: [PATCH 145/421] Added file writing utility method for consistency. --- lib/CssCrush/IO.php | 13 +++++-------- lib/CssCrush/Util.php | 13 +++++++++++++ plugins/svg.php | 5 +++-- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/lib/CssCrush/IO.php b/lib/CssCrush/IO.php index 612ba25..c3f73af 100644 --- a/lib/CssCrush/IO.php +++ b/lib/CssCrush/IO.php @@ -226,7 +226,7 @@ static public function getCacheData () // Create config file. CssCrush::log('Creating cache data file.'); } - file_put_contents($process->cacheFile, json_encode(array()), LOCK_EX); + CssCrush_Util::filePutContents($process->cacheFile, json_encode(array()), __METHOD__); } return $cache_data; @@ -237,21 +237,18 @@ static public function saveCacheData () $process = CssCrush::$process; CssCrush::log('Saving config.'); - file_put_contents($process->cacheFile, json_encode($process->cacheData), LOCK_EX); + CssCrush_Util::filePutContents($process->cacheFile, json_encode($process->cacheData), __METHOD__); } static public function write (CssCrush_Stream $stream) { $process = CssCrush::$process; $target = "{$process->output->dir}/{$process->output->filename}"; - if (@file_put_contents($target, $stream, LOCK_EX)) { + + if (CssCrush_Util::filePutContents($target, $stream, __METHOD__)) { return "{$process->output->dirUrl}/{$process->output->filename}"; } - else { - $error = "Could not write file '$target'."; - CssCrush::logError($error); - trigger_error(__METHOD__ . ": $error\n", E_USER_WARNING); - } + return false; } diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php index 02d3fad..82dd0ff 100644 --- a/lib/CssCrush/Util.php +++ b/lib/CssCrush/Util.php @@ -145,6 +145,19 @@ static public function getLinkBetweenPaths ($path1, $path2, $directories = true) return $link; } + static public function filePutContents ($file, $str, $caller = null) + { + if (@file_put_contents($file, $str, LOCK_EX)) { + + return true; + } + $error = "Could not write file '$file'."; + CssCrush::logError($error); + trigger_error(($caller ? $caller : __METHOD__) . ": $error\n", E_USER_WARNING); + + return false; + } + /* * Encode integer to Base64 VLQ. */ diff --git a/plugins/svg.php b/plugins/svg.php index 2d556e9..71c2687 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -280,10 +280,11 @@ function csscrush__svg_generator ($input, $fn_name) { $fingerprint = substr(md5($flattened_svg), 0, 7); $generated_filename = "svg-$name-$fingerprint.svg"; + // Write to the same directory as the output css. $generated_path = $process->output->dir . '/' . $generated_filename; - file_put_contents($generated_path, $flattened_svg, LOCK_EX); - // Write to the same directory as the output css. + CssCrush_Util::filePutContents($generated_path, $flattened_svg, __METHOD__); + $generated_url = $generated_filename; $url = new CssCrush_Url($generated_url); $url->noRewrite = true; From 6e614dc28433567f7f341d1b352b2ecaa3fbf464 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 2 Aug 2013 11:56:28 +0100 Subject: [PATCH 146/421] Source maps implemented. --- CHANGELOG.md | 3 +++ lib/CssCrush/IO.php | 17 ++++++++++++- lib/CssCrush/Process.php | 55 +++++++++++++++++++++++++++++++++------- lib/CssCrush/Rule.php | 6 ++--- 4 files changed, 68 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3825990..bd2607c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +### 1.11 (2013-?) + + ### 1.10 (2013-5-18) * Added SVG plugin for defining and generating SVG files/data URIs in CSS. diff --git a/lib/CssCrush/IO.php b/lib/CssCrush/IO.php index c3f73af..679b862 100644 --- a/lib/CssCrush/IO.php +++ b/lib/CssCrush/IO.php @@ -223,7 +223,6 @@ static public function getCacheData () } } else { - // Create config file. CssCrush::log('Creating cache data file.'); } CssCrush_Util::filePutContents($process->cacheFile, json_encode(array()), __METHOD__); @@ -245,7 +244,23 @@ static public function write (CssCrush_Stream $stream) $process = CssCrush::$process; $target = "{$process->output->dir}/{$process->output->filename}"; + if ($process->sourceMap) { + $source_map_filename = $process->output->filename . '.map'; + $stream->append($process->newline . "/*# sourceMappingURL=$source_map_filename */"); + } + if (CssCrush_Util::filePutContents($target, $stream, __METHOD__)) { + + if ($process->sourceMap) { + if (defined('JSON_PRETTY_PRINT')) { + $data = json_encode($process->sourceMap, JSON_PRETTY_PRINT); + } + else { + $data = json_encode($process->sourceMap); + } + CssCrush_Util::filePutContents("{$process->output->dir}/$source_map_filename", $data, __METHOD__); + } + return "{$process->output->dirUrl}/{$process->output->filename}"; } diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index e5fb31a..9b668ee 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -36,6 +36,7 @@ public function __construct ($options) $this->input = new stdClass(); $this->output = new stdClass(); $this->tokens = new CssCrush_Tokens(); + $this->sourceMap = null; // Copy config values. $this->plugins = $config->plugins; @@ -430,12 +431,6 @@ protected function calculateVars () $this->vars = array_merge($this->vars, $option_vars); } - // Add state variables. - // $this->vars = array( - // 'input-path' => $this->input->dirUrl, - // 'output-path' => $this->output->dirUrl, - //) + $this->vars; - // Place variables referenced inside variables. Excecute custom functions. foreach ($this->vars as $name => &$value) { @@ -1002,15 +997,57 @@ public function compile ($io_context = 'file') public function generateSourceMap () { - $this->map = array( + $this->sourceMap = array( 'version' => '3', 'file' => $this->output->filename, 'sources' => array(), ); foreach ($this->sources as $source) { - $this->map['sources'][] = CssCrush_Util::getLinkBetweenPaths($this->output->dir, $source, false); + $this->sourceMap['sources'][] = CssCrush_Util::getLinkBetweenPaths($this->output->dir, $source, false); } - // $str = json_encode($this->map, JSON_PRETTY_PRINT); + + $patt = CssCrush_Regex::create('\?[tm]{{token-id}}\?', 'S'); + $mappings = array(); + $lines = preg_split(CssCrush_Regex::$patt->newline, $this->stream->raw); + $tokens =& $this->tokens->store; + + // All mappings are calculated as delta values. + $previous_dest_col = 0; + $previous_src_file = 0; + $previous_src_line = 0; + $previous_src_col = 0; + + foreach ($lines as $line_number => &$line_text) { + + $line_segments = array(); + + while (preg_match($patt, $line_text, $m, PREG_OFFSET_CAPTURE)) { + + list($token, $dest_col) = $m[0]; + $token_type = $token[1]; + + if (isset($tokens->{$token_type}[$token])) { + + list($src_file, $src_line, $src_col) = $tokens->{$token_type}[$token]; + $line_segments[] = + CssCrush_Util::vlqEncode($dest_col - $previous_dest_col) . + CssCrush_Util::vlqEncode($src_file - $previous_src_file) . + CssCrush_Util::vlqEncode($src_line - $previous_src_line) . + CssCrush_Util::vlqEncode($src_col - $previous_src_col); + + $previous_dest_col = $dest_col; + $previous_src_file = $src_file; + $previous_src_line = $src_line; + $previous_src_col = $src_col; + } + $line_text = substr_replace($line_text, '', $dest_col, strlen($token)); + } + + $mappings[] = implode(',', $line_segments); + } + + $this->stream->raw = implode($this->newline, $lines); + $this->sourceMap['mappings'] = implode(';', $mappings); } public function generateTracingStub ($m) diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php index a1adb48..20ff38f 100644 --- a/lib/CssCrush/Rule.php +++ b/lib/CssCrush/Rule.php @@ -8,7 +8,7 @@ class CssCrush_Rule implements IteratorAggregate { public $vendorContext; public $label; - public $tracingStub; + public $marker; public $selectors = array(); public $extendSelectors = array(); @@ -32,7 +32,7 @@ public function __construct ($selector_string, $declarations_string, $trace_toke $regex = CssCrush_Regex::$patt; $process = CssCrush::$process; $this->label = $process->tokens->createLabel('r'); - $this->tracingStub = $process->addTracingStubs ? $trace_token : null; + $this->marker = $process->addTracingStubs || $process->generateMap ? $trace_token : null; if (! empty(CssCrush_Hook::$register['rule_preprocess'])) { // Juggling to maintain the old API. @@ -118,7 +118,7 @@ public function __toString () return ''; } - $stub = $this->tracingStub; + $stub = $this->marker; // Concat and return. if ($process->minifyOutput) { From 6fcc0134aef93f087488f72c57684aa665b1e0e9 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 2 Aug 2013 13:12:38 +0100 Subject: [PATCH 147/421] Added `--source-map` flag to command line utility. Fixed out of index notice. Updated changelog. --- CHANGELOG.md | 12 ++++++++++++ cli.php | 16 +++++++++++----- lib/CssCrush/Fragment.php | 2 +- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd2607c..2c454ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ ### 1.11 (2013-?) +* Added source map support according to Google's Source Map v3 proposal (boolean option `source-map`). +* Compile times are now 20%-30% reduced. +* Added support for fragment calls within fragment definitions (Issue #48). +* Added check and recovery for overly conservative ini settings. +* The block nesting parent symbol can now be used multiple times (useful for adjacent/general sibling combinations). +* Command utility now supports for the `trace` option. +* Custom formatter callbacks have been simplified. +* Simplified the `csscrush_stat()` function signature. +* Added command line utility alias for composer's vendor/bin directory. +* Removed Plugins.ini (use `csscrush_set()` instead). +* Removed Prepend.css. +* Various refactoring for cleaner under-the-hood APIs. ### 1.10 (2013-5-18) diff --git a/cli.php b/cli.php index 261f604..92882df 100755 --- a/cli.php +++ b/cli.php @@ -52,11 +52,12 @@ ); $flag_opts = array( - 'p|pretty', // Pretty output. - 'w|watch', // Watch mode. - 'list', // List plugins. - 'help', // Display help. - 'version', // Display version. + 'p|pretty', // Pretty output. + 'w|watch', // Watch mode. + 'list', // List plugins. + 'help', // Display help. + 'version', // Display version. + 'source-map', // Display version. ); // Create option strings for getopt(). @@ -95,6 +96,7 @@ $args->list = isset($opts['l']) ?: isset($opts['list']); $args->help = isset($opts['h']) ?: isset($opts['help']); $args->version = isset($opts['version']); +$args->source_map = isset($opts['source-map']); // Arguments that optionally accept a single value. $args->boilerplate = pick($opts, 'b', 'boilerplate'); @@ -269,6 +271,10 @@ $process_opts['vendor_target'] = parse_list($args->vendor_target); } +if ($args->source_map) { + $process_opts['source_map'] = true; +} + if ($args->vars) { parse_str($args->vars, $in_vars); $process_opts['vars'] = $in_vars; diff --git a/lib/CssCrush/Fragment.php b/lib/CssCrush/Fragment.php index 71cd677..bf52cbe 100644 --- a/lib/CssCrush/Fragment.php +++ b/lib/CssCrush/Fragment.php @@ -31,7 +31,7 @@ public function apply (array $args = null, $str = null) // Skip over same named fragments to avoid infinite recursion. if ($fragment && $name !== $this->name) { $args = array(); - if ($m['parens'][1] !== -1) { + if (isset($m['parens'][1])) { $args = CssCrush_Function::parseArgs($m['parens_content'][0]); } $replacement = $fragment->apply($args); From 53a54ee983f0aaa44129fa685dcd1e31a3582771 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 3 Aug 2013 12:38:10 +0100 Subject: [PATCH 148/421] Adding source map docs to command line utility. Updated changelog. --- CHANGELOG.md | 8 ++++---- cli.php | 5 ++++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c454ff..efd551b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,11 @@ -### 1.11 (2013-?) +### 1.11 (2013-8-3) -* Added source map support according to Google's Source Map v3 proposal (boolean option `source-map`). -* Compile times are now 20%-30% reduced. +* Added source map support according to the Source Map v3 proposal (boolean option `source-map`). +* Compile times are now 20-30% reduced. * Added support for fragment calls within fragment definitions (Issue #48). * Added check and recovery for overly conservative ini settings. * The block nesting parent symbol can now be used multiple times (useful for adjacent/general sibling combinations). -* Command utility now supports for the `trace` option. +* Command utility now supports the `trace` option. * Custom formatter callbacks have been simplified. * Simplified the `csscrush_stat()` function signature. * Added command line utility alias for composer's vendor/bin directory. diff --git a/cli.php b/cli.php index 92882df..f0992eb 100755 --- a/cli.php +++ b/cli.php @@ -565,8 +565,11 @@ function manpage () { Force newline style on output css. Defaults to the current platform newline. Possible values: 'windows' (or 'win'), 'unix', 'use-platform'. + --source-map: + Output a source map (compliant with the Source Map v3 proposal). + --trace: - Output debug-info stubs compatible with client-side sass debuggers. + Output debug-info stubs compatible with client-side Sass debuggers. --vars: Map of variable names in an http query string format. From 9f48fa49665da89277a6aa66bd2bec17cfc26f68 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 8 Aug 2013 12:55:58 +0100 Subject: [PATCH 149/421] Started port to v2 in php 5.3. --- CHANGELOG.md | 27 +-- CssCrush.php | 6 +- cli.php | 6 +- composer.json | 7 +- lib/CssCrush/BalancedMatch.php | 8 +- lib/CssCrush/Color.php | 24 +-- lib/CssCrush/{Core.php => CssCrush.php} | 32 ++-- lib/CssCrush/Declaration.php | 14 +- lib/CssCrush/ExtendArg.php | 8 +- lib/CssCrush/Fragment.php | 8 +- lib/CssCrush/{Function.php => Functions.php} | 60 +++--- lib/CssCrush/Hook.php | 4 +- lib/CssCrush/IO.php | 16 +- lib/CssCrush/IOWatch.php | 4 +- lib/CssCrush/Importer.php | 24 +-- lib/CssCrush/Mixin.php | 14 +- lib/CssCrush/Options.php | 8 +- lib/CssCrush/Plugin.php | 8 +- lib/CssCrush/PostAliasFix.php | 27 +-- lib/CssCrush/Process.php | 191 +++++++++---------- lib/CssCrush/Regex.php | 40 ++-- lib/CssCrush/Rule.php | 50 ++--- lib/CssCrush/Selector.php | 16 +- lib/CssCrush/Stream.php | 8 +- lib/CssCrush/Template.php | 16 +- lib/CssCrush/Tokens.php | 38 ++-- lib/CssCrush/Url.php | 8 +- lib/CssCrush/Util.php | 14 +- lib/CssCrush/Version.php | 6 +- lib/functions.php | 2 + misc/formatters.php | 2 + plugins/canvas.php | 25 +-- plugins/hocus-pocus.php | 23 +-- plugins/initial.php | 21 +- plugins/noise.php | 67 +++---- plugins/property-sorter.php | 33 ++-- plugins/px2em.php | 36 ++-- plugins/rem.php | 26 ++- plugins/spiffing.php | 21 +- plugins/svg-gradients.php | 12 +- plugins/svg.php | 18 +- 41 files changed, 503 insertions(+), 475 deletions(-) rename lib/CssCrush/{Core.php => CssCrush.php} (95%) rename lib/CssCrush/{Function.php => Functions.php} (78%) diff --git a/CHANGELOG.md b/CHANGELOG.md index efd551b..43b6ec0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,9 @@ -### 1.11 (2013-8-3) +### 2.0.0 (?) + + +******************************************************************** + +### 1.11.0 (2013-8-3) * Added source map support according to the Source Map v3 proposal (boolean option `source-map`). * Compile times are now 20-30% reduced. @@ -13,7 +18,7 @@ * Removed Prepend.css. * Various refactoring for cleaner under-the-hood APIs. -### 1.10 (2013-5-18) +### 1.10.0 (2013-5-18) * Added SVG plugin for defining and generating SVG files/data URIs in CSS. * Added Canvas plugin for image generation and manipulation (requires GD extension). @@ -65,7 +70,7 @@ * Property/value aliases expanded and renamed as declaration aliases. * Classes now loaded via an autoloader, also some other refactoring for moving towards PSR-0 compliance. -### 1.8 (2012-11-13) +### 1.8.0 (2012-11-13) * Added selector aliasing with the `@selector-alias` directive. * Added `output_dir` option for specifying the destination of compiled files. @@ -84,7 +89,7 @@ * Improved minification. * Major refactoring. -### 1.7 (2012-9-28) +### 1.7.0 (2012-9-28) * Added `trace` option to output SASS compatible debug-info stubs for use with tools like FireSass. * Added `@ifdefine` directive for dynamically including/excluding parts of a CSS file based on the @@ -103,7 +108,7 @@ * Resolved issues #34 and #35. -### 1.6 (2012-8-1) +### 1.6.0 (2012-8-1) * Inheritance model improved to support adoption of pseudo classes and elements (see wiki). * Added rule self-referencing function `this()` and complimentary data-* properties. @@ -131,7 +136,7 @@ * Extended mixins to work with abstract rules and regular rules. * Fixed issue with selector grouping and inheritance in combination. -### 1.5 (2012-5-21) +### 1.5.0 (2012-5-21) * New feature: Rule inheritance / abstract rules. * New feature: Block nesting. @@ -160,7 +165,7 @@ * Initial-values updated. * Updated `CssCrush::string` method to correctly handle import statements. -### 1.4 (2012-1-24) +### 1.4.0 (2012-1-24) * Added initial-keyword plugin (shim for the CSS3 keyword). * Added inline method (Issue #18). @@ -217,11 +222,11 @@ * Added color functions. * Added aliases for IE10. -### 1.2 (2011-9-8) +### 1.2.0 (2011-9-8) * File importer rewritten. -### 1.1 (2011-9-2) +### 1.1.0 (2011-9-2) * Added support for global variables. * Added support for variable interpolation within string literals. @@ -233,7 +238,7 @@ * Minor correction to WAMP support. * Minor fix to rule API. -### 1.0 (2011-7-14) +### 1.0.0 (2011-7-14) * Major refactoring. * Custom functions. @@ -242,6 +247,6 @@ * Resolved document root issues. * Minification improvements. -### 0.9 (2010-9-20) +### 0.9.0 (2010-9-20) * Initial release. diff --git a/CssCrush.php b/CssCrush.php index b97a811..b3767d8 100644 --- a/CssCrush.php +++ b/CssCrush.php @@ -13,15 +13,11 @@ function csscrush_autoload ($class) { // Tolerate some cases of lowercasing. $class = str_ireplace('csscrush', 'CssCrush', $class); - $subpath = implode('/', array_map('ucfirst', explode('_', $class))); + $subpath = implode('/', array_map('ucfirst', explode('\\', $class))); require_once dirname(__FILE__) . "/lib/$subpath.php"; } spl_autoload_register('csscrush_autoload'); - -// Core.php will also be autoloaded with API changes in v2.x. -require_once 'lib/CssCrush/Core.php'; - require_once 'lib/functions.php'; diff --git a/cli.php b/cli.php index f0992eb..b576b54 100755 --- a/cli.php +++ b/cli.php @@ -5,6 +5,8 @@ * Command line utility. * */ +namespace CssCrush; + require_once 'CssCrush.php'; ################################################################## @@ -143,7 +145,7 @@ $plugins = array(); - foreach (CssCrush_Plugin::info() as $name => $docs) { + foreach (Plugin::info() as $name => $docs) { // Use first line of plugin doc for description. $headline = isset($docs[0]) ? $docs[0] : false; $plugins[] = colorize("$name" . ($headline ? " - $headline" : '')); @@ -301,7 +303,7 @@ if ($args->watch) { // Override the IO class. - csscrush_set('config', array('io' => 'CssCrush_IOWatch')); + csscrush_set('config', array('io' => 'CssCrush\IOWatch')); stdout('CONTROL-C to quit.'); diff --git a/composer.json b/composer.json index 47f36c4..a83e599 100644 --- a/composer.json +++ b/composer.json @@ -16,12 +16,13 @@ } ], "require": { - "php": ">=5.2.4" + "php": ">=5.3.4" }, "bin": [ "misc/csscrush" ], "autoload": { - "files": ["CssCrush.php"] - } + "psr-0": { "CssCrush": "lib/" }, + "files": [ "lib/functions.php" ] + }, } \ No newline at end of file diff --git a/lib/CssCrush/BalancedMatch.php b/lib/CssCrush/BalancedMatch.php index a2dea9d..a2a0722 100644 --- a/lib/CssCrush/BalancedMatch.php +++ b/lib/CssCrush/BalancedMatch.php @@ -4,9 +4,11 @@ * Balanced bracket matching on the main stream. * */ -class CssCrush_BalancedMatch +namespace CssCrush; + +class BalancedMatch { - public function __construct (CssCrush_Stream $stream, $offset, $brackets = '{}') + public function __construct (Stream $stream, $offset, $brackets = '{}') { $this->stream = $stream; $this->offset = $offset; @@ -26,7 +28,7 @@ public function __construct (CssCrush_Stream $stream, $offset, $brackets = '{}') } $patt = $opener === '{' ? - CssCrush_Regex::$patt->balancedCurlies : CssCrush_Regex::$patt->balancedParens; + Regex::$patt->balancedCurlies : Regex::$patt->balancedParens; if (preg_match($patt, $stream->raw, $m, PREG_OFFSET_CAPTURE, $this->offset)) { diff --git a/lib/CssCrush/Color.php b/lib/CssCrush/Color.php index 8253c37..162cd78 100644 --- a/lib/CssCrush/Color.php +++ b/lib/CssCrush/Color.php @@ -4,7 +4,9 @@ * Colour parsing and conversion. * */ -class CssCrush_Color +namespace CssCrush; + +class Color { // Cached color keyword tables. static public $keywords; @@ -34,7 +36,7 @@ static public function &loadMinifyableKeywords () // If color name is longer than 4 and less than 8 test to see if its hex // representation could be shortened. $table = array(); - $keywords =& CssCrush_Color::loadKeywords(); + $keywords =& Color::loadKeywords(); foreach ($keywords as $name => &$rgb) { $name_len = strlen($name); @@ -48,7 +50,7 @@ static public function &loadMinifyableKeywords () self::$minifyableKeywords[ $name ] = $hex; } else { - if (preg_match(CssCrush_Regex::$patt->cruftyHex, $hex)) { + if (preg_match(Regex::$patt->cruftyHex, $hex)) { self::$minifyableKeywords[ $name ] = $hex; } } @@ -62,7 +64,7 @@ static public function parse ($str) { $rgba = false; - if ($test = CssCrush_Color::test($str)) { + if ($test = Color::test($str)) { $color = $test['value']; $type = $test['type']; } @@ -74,7 +76,7 @@ static public function parse ($str) switch ($type) { case 'hex': - $rgba = CssCrush_Color::hexToRgb($color); + $rgba = Color::hexToRgb($color); break; case 'rgb': @@ -90,10 +92,10 @@ static public function parse ($str) $vals[3] = isset($vals[3]) ? floatval($vals[3]) : 1; if (strpos($function, 'rgb') === 0) { - $rgba = CssCrush_Color::normalizeCssRgb($vals); + $rgba = Color::normalizeCssRgb($vals); } else { - $rgba = CssCrush_Color::cssHslToRgb($vals); + $rgba = Color::cssHslToRgb($vals); } break; @@ -113,7 +115,7 @@ static public function test ($str) { static $color_patt; if (! $color_patt) { - $color_patt = CssCrush_Regex::create('^( + $color_patt = Regex::create('^( \#(?={{hex}}{3}) | \#(?={{hex}}{6}) | rgba?(?=[?(]) | @@ -326,7 +328,7 @@ static public function hexToRgb ($hex) static public function colorAdjust ($str, array $adjustments) { - $hsla = new CssCrush_Color($str, true); + $hsla = new Color($str, true); // On failure to parse return input. return $hsla->isValid ? $hsla->adjust($adjustments)->__toString() : $str; @@ -334,7 +336,7 @@ static public function colorAdjust ($str, array $adjustments) static public function colorSplit ($str) { - if ($test = CssCrush_Color::test($str)) { + if ($test = Color::test($str)) { $color = $test['value']; $type = $test['type']; } @@ -351,7 +353,7 @@ static public function colorSplit ($str) static $alpha_color_patt; if (! $alpha_color_patt) { - $alpha_color_patt = CssCrush_Regex::create( + $alpha_color_patt = Regex::create( '^(rgb|hsl)a\(({{number}}%?,{{number}}%?,{{number}}%?),({{number}})\)$'); } diff --git a/lib/CssCrush/Core.php b/lib/CssCrush/CssCrush.php similarity index 95% rename from lib/CssCrush/Core.php rename to lib/CssCrush/CssCrush.php index 991ce34..adb129f 100644 --- a/lib/CssCrush/Core.php +++ b/lib/CssCrush/CssCrush.php @@ -4,9 +4,11 @@ * Main script. Includes core public API. * */ +namespace CssCrush; + class CssCrush { - const VERSION = '1.11'; + const VERSION = '2.0.0'; // Global settings. static public $config; @@ -17,7 +19,7 @@ class CssCrush // Init called once manually post class definition. static public function init () { - self::$config = new stdClass(); + self::$config = new \stdClass(); // Path to the project root folder. self::$config->location = dirname(dirname(dirname(__FILE__))); @@ -26,13 +28,13 @@ static public function init () self::$config->plugin_dirs = array(self::$config->location . '/plugins'); // Establish version id. - self::$config->version = new CssCrush_Version(self::VERSION); + self::$config->version = new Version(self::VERSION); // Set the docRoot reference. self::setDocRoot(); // Set the default IO handler. - self::$config->io = 'CssCrush_IO'; + self::$config->io = 'CssCrush\IO'; // Shared resources. self::$config->vars = array(); @@ -41,7 +43,7 @@ static public function init () self::$config->plugins = array(); // Default options. - self::$config->options = new CssCrush_Options(array( + self::$config->options = new Options(array( // Minify. Set false for formatting and comments. 'minify' => true, @@ -139,7 +141,7 @@ static protected function setDocRoot ($doc_root = null) } } - self::$config->docRoot = CssCrush_Util::normalizePath($doc_root); + self::$config->docRoot = Util::normalizePath($doc_root); } // Aliases and macros loader. @@ -152,7 +154,7 @@ static public function loadAssets () // Find an aliases file in the root directory // a local file overrides the default - $aliases_file = CssCrush_Util::find('Aliases-local.ini', 'Aliases.ini'); + $aliases_file = Util::find('Aliases-local.ini', 'Aliases.ini'); // Load aliases file if it exists if ($aliases_file) { @@ -160,7 +162,7 @@ static public function loadAssets () $result = @parse_ini_file($aliases_file, true); if ($result !== false) { - $regex = CssCrush_Regex::$patt; + $regex = Regex::$patt; foreach ($result as $section => $items) { @@ -209,7 +211,7 @@ static public function loadAssets () // We'll cache the function matching regex here. $vendor_grouped_aliases[$m[1]]['find'][] = - CssCrush_Regex::create('{{LB}}' . $func_name . '{{RTB}}', 'i'); + Regex::create('{{LB}}' . $func_name . '{{RTB}}', 'i'); $vendor_grouped_aliases[$m[1]]['replace'][] = $alias_func; } } @@ -254,7 +256,7 @@ static public function loadAssets () */ static public function file ($file, $options = null) { - self::$process = new CssCrush_Process($options); + self::$process = new Process($options); $config = self::$config; $process = self::$process; @@ -286,7 +288,7 @@ static public function file ($file, $options = null) } // Validate file input. - if (! CssCrush_IO::registerInputFile($file)) { + if (! IO::registerInputFile($file)) { return ''; } @@ -342,7 +344,7 @@ static public function tag ($file, $options = null, $attributes = array()) if (! isset($attributes['media'])) { $attributes['media'] = 'all'; } - $attr_string = CssCrush_Util::htmlAttributes($attributes); + $attr_string = Util::htmlAttributes($attributes); return "\n"; } else { @@ -383,7 +385,7 @@ static public function inline ($file, $options = null, $attributes = array()) $tag_close = ''; if (is_array($attributes)) { - $attr_string = CssCrush_Util::htmlAttributes($attributes); + $attr_string = Util::htmlAttributes($attributes); $tag_open = ""; $tag_close = ''; } @@ -412,7 +414,7 @@ static public function string ($string, $options = null) $options['boilerplate'] = false; } - self::$process = new CssCrush_Process($options); + self::$process = new Process($options); $config = self::$config; $process = self::$process; @@ -503,7 +505,7 @@ static public function stat () static public function addSelectorAlias ($name, $body) { - CssCrush::$config->selectorAliases[$name] = new CssCrush_Template($body); + CssCrush::$config->selectorAliases[$name] = new Template($body); } static public function removeSelectorAlias ($name) diff --git a/lib/CssCrush/Declaration.php b/lib/CssCrush/Declaration.php index c52c9f7..00b2db1 100644 --- a/lib/CssCrush/Declaration.php +++ b/lib/CssCrush/Declaration.php @@ -4,7 +4,9 @@ * Declaration objects. * */ -class CssCrush_Declaration +namespace CssCrush; + +class Declaration { public $property; public $canonicalProperty; @@ -17,7 +19,7 @@ class CssCrush_Declaration public function __construct ($prop, $value, $contextIndex = 0) { - $regex = CssCrush_Regex::$patt; + $regex = Regex::$patt; // Normalize input. Lowercase the property name. $prop = strtolower(trim($prop)); @@ -96,9 +98,9 @@ public function process ($parent_rule) $context = (object) array( 'rule' => $parent_rule, ); - CssCrush_Function::executeOnString( + Functions::executeOnString( $this->value, - CssCrush_Regex::$patt->thisFunction, + Regex::$patt->thisFunction, array( 'this' => 'csscrush_fn__this', ), @@ -111,7 +113,7 @@ public function process ($parent_rule) 'rule' => $parent_rule, 'property' => $this->property ); - CssCrush_Function::executeOnString( + Functions::executeOnString( $this->value, null, null, @@ -141,7 +143,7 @@ public function indexFunctions () { // Create an index of all regular functions in the value. $functions = array(); - if (preg_match_all(CssCrush_Regex::$patt->function, $this->value, $m)) { + if (preg_match_all(Regex::$patt->function, $this->value, $m)) { foreach ($m[1] as $index => $fn_name) { $functions[strtolower($fn_name)] = true; } diff --git a/lib/CssCrush/ExtendArg.php b/lib/CssCrush/ExtendArg.php index 01c7260..b662ef8 100644 --- a/lib/CssCrush/ExtendArg.php +++ b/lib/CssCrush/ExtendArg.php @@ -4,7 +4,9 @@ * Extend argument objects. * */ -class CssCrush_ExtendArg +namespace CssCrush; + +class ExtendArg { public $pointer; public $name; @@ -14,10 +16,10 @@ public function __construct ($name) { $this->name = $name; - if (! preg_match(CssCrush_Regex::$patt->rooted_ident, $this->name)) { + if (! preg_match(Regex::$patt->rooted_ident, $this->name)) { // Not a regular name: Some kind of selector so normalize it for later comparison. - $this->name = CssCrush_Selector::makeReadable($this->name); + $this->name = Selector::makeReadable($this->name); // If applying the pseudo on output store. if (substr($this->name, -1) === '!') { diff --git a/lib/CssCrush/Fragment.php b/lib/CssCrush/Fragment.php index bf52cbe..d11feb9 100644 --- a/lib/CssCrush/Fragment.php +++ b/lib/CssCrush/Fragment.php @@ -4,7 +4,9 @@ * Fragments. * */ -class CssCrush_Fragment extends CssCrush_Template +namespace CssCrush; + +class Fragment extends Template { public $name; @@ -19,7 +21,7 @@ public function apply (array $args = null, $str = null) $str = parent::apply($args); // Flatten all fragment calls within the template string. - while (preg_match(CssCrush_Regex::$patt->fragmentInvoke, $str, $m, PREG_OFFSET_CAPTURE)) { + while (preg_match(Regex::$patt->fragmentInvoke, $str, $m, PREG_OFFSET_CAPTURE)) { $name = strtolower($m['name'][0]); $fragment = isset(CssCrush::$process->fragments[$name]) ? CssCrush::$process->fragments[$name] : null; @@ -32,7 +34,7 @@ public function apply (array $args = null, $str = null) if ($fragment && $name !== $this->name) { $args = array(); if (isset($m['parens'][1])) { - $args = CssCrush_Function::parseArgs($m['parens_content'][0]); + $args = Functions::parseArgs($m['parens_content'][0]); } $replacement = $fragment->apply($args); } diff --git a/lib/CssCrush/Function.php b/lib/CssCrush/Functions.php similarity index 78% rename from lib/CssCrush/Function.php rename to lib/CssCrush/Functions.php index 8c562bd..f4fcae4 100644 --- a/lib/CssCrush/Function.php +++ b/lib/CssCrush/Functions.php @@ -4,7 +4,9 @@ * Custom CSS functions * */ -class CssCrush_Function +namespace CssCrush; + +class Functions { // Regex pattern for finding custom functions. static public $functionPatt; @@ -33,11 +35,11 @@ class CssCrush_Function static public function setMatchPatt () { self::$functions = self::$builtinFunctions + self::$customFunctions; - self::$functionPatt = CssCrush_Regex::createFunctionPatt( + self::$functionPatt = Regex::createFunctionPatt( array_keys(self::$functions), array('bare_paren' => true)); } - static public function executeOnString (&$str, $patt = null, $process_callback = null, stdClass $context = null) + static public function executeOnString (&$str, $patt = null, $process_callback = null, \stdClass $context = null) { // No bracketed expressions, early return. if (strpos($str, '(') === false) { @@ -47,7 +49,7 @@ static public function executeOnString (&$str, $patt = null, $process_callback = // Set default pattern if not set. if (! isset($patt)) { - $patt = CssCrush_Function::$functionPatt; + $patt = Functions::$functionPatt; } // No custom functions, early return. @@ -57,11 +59,11 @@ static public function executeOnString (&$str, $patt = null, $process_callback = } // Find custom function matches. - $matches = CssCrush_Regex::matchAll($patt, $str); + $matches = Regex::matchAll($patt, $str); // Always pass in a context object. if (! $context) { - $context = new stdClass(); + $context = new \stdClass(); } // Step through the matches from last to first. @@ -69,7 +71,7 @@ static public function executeOnString (&$str, $patt = null, $process_callback = $offset = $match[0][1]; - if (! preg_match(CssCrush_Regex::$patt->balancedParens, + if (! preg_match(Regex::$patt->balancedParens, $str, $parens, PREG_OFFSET_CAPTURE, $offset)) { continue; } @@ -116,17 +118,17 @@ static public function executeOnString (&$str, $patt = null, $process_callback = static public function register ($name, $callback) { - CssCrush_Function::$customFunctions[ $name] = $callback; + Functions::$customFunctions[ $name] = $callback; } static public function deRegister ($name) { - unset(CssCrush_Function::$customFunctions[ $name]); + unset(Functions::$customFunctions[ $name]); } static public function parseArgs ($input, $allowSpaceDelim = false) { - return CssCrush_Util::splitDelimList( + return Util::splitDelimList( $input, ($allowSpaceDelim ? '\s*[,\s]\s*' : ',')); } @@ -134,7 +136,7 @@ static public function parseArgs ($input, $allowSpaceDelim = false) // with the proviso the first argument is an ident. static public function parseArgsSimple ($input) { - return preg_split(CssCrush_Regex::$patt->argListSplit, $input, 2); + return preg_split(Regex::$patt->argListSplit, $input, 2); } } @@ -151,7 +153,7 @@ function csscrush_fn__math ($input) { $input); // Strip blacklisted characters. - $input = preg_replace(CssCrush_Regex::$patt->mathBlacklist, '', $input); + $input = preg_replace(Regex::$patt->mathBlacklist, '', $input); $result = @eval("return $input;"); @@ -163,7 +165,7 @@ function csscrush_fn__percent ($input) { // Strip non-numeric and non delimiter characters $input = preg_replace('~[^\d\.\s,]~S', '', $input); - $args = preg_split(CssCrush_Regex::$patt->argListSplit, $input, -1, PREG_SPLIT_NO_EMPTY); + $args = preg_split(Regex::$patt->argListSplit, $input, -1, PREG_SPLIT_NO_EMPTY); // Use precision argument if it exists, use default otherwise $precision = isset($args[2]) ? $args[2] : 5; @@ -201,38 +203,38 @@ function csscrush_fn__percent ($input) { } function csscrush_fn__hsla_adjust ($input) { - list($color, $h, $s, $l, $a) = array_pad(CssCrush_Function::parseArgs($input, true), 5, 0); - return CssCrush_Color::colorAdjust($color, array($h, $s, $l, $a)); + list($color, $h, $s, $l, $a) = array_pad(Functions::parseArgs($input, true), 5, 0); + return Color::colorAdjust($color, array($h, $s, $l, $a)); } function csscrush_fn__hsl_adjust ($input) { - list($color, $h, $s, $l) = array_pad(CssCrush_Function::parseArgs($input, true), 4, 0); - return CssCrush_Color::colorAdjust($color, array($h, $s, $l, 0)); + list($color, $h, $s, $l) = array_pad(Functions::parseArgs($input, true), 4, 0); + return Color::colorAdjust($color, array($h, $s, $l, 0)); } function csscrush_fn__h_adjust ($input) { - list($color, $h) = array_pad(CssCrush_Function::parseArgs($input, true), 2, 0); - return CssCrush_Color::colorAdjust($color, array($h, 0, 0, 0)); + list($color, $h) = array_pad(Functions::parseArgs($input, true), 2, 0); + return Color::colorAdjust($color, array($h, 0, 0, 0)); } function csscrush_fn__s_adjust ($input) { - list($color, $s) = array_pad(CssCrush_Function::parseArgs($input, true), 2, 0); - return CssCrush_Color::colorAdjust($color, array(0, $s, 0, 0)); + list($color, $s) = array_pad(Functions::parseArgs($input, true), 2, 0); + return Color::colorAdjust($color, array(0, $s, 0, 0)); } function csscrush_fn__l_adjust ($input) { - list($color, $l) = array_pad(CssCrush_Function::parseArgs($input, true), 2, 0); - return CssCrush_Color::colorAdjust($color, array(0, 0, $l, 0)); + list($color, $l) = array_pad(Functions::parseArgs($input, true), 2, 0); + return Color::colorAdjust($color, array(0, 0, $l, 0)); } function csscrush_fn__a_adjust ($input) { - list($color, $a) = array_pad(CssCrush_Function::parseArgs($input, true), 2, 0); - return CssCrush_Color::colorAdjust($color, array(0, 0, 0, $a)); + list($color, $a) = array_pad(Functions::parseArgs($input, true), 2, 0); + return Color::colorAdjust($color, array(0, 0, 0, $a)); } function csscrush_fn__this ($input, $context) { - $args = CssCrush_Function::parseArgsSimple($input); + $args = Functions::parseArgsSimple($input); $property = $args[0]; // Function relies on a context rule, bail if none. @@ -259,7 +261,7 @@ function csscrush_fn__this ($input, $context) { function csscrush_fn__query ($input, $context) { - $args = CssCrush_Function::parseArgs($input); + $args = Functions::parseArgs($input); // Function relies on a context property, bail if none. if (count($args) < 1 || ! isset($context->property)) { @@ -284,8 +286,8 @@ function csscrush_fn__query ($input, $context) { } $default = isset($args[0]) ? $args[0] : null; - if (! preg_match(CssCrush_Regex::$patt->rooted_ident, $name)) { - $name = CssCrush_Selector::makeReadable($name); + if (! preg_match(Regex::$patt->rooted_ident, $name)) { + $name = Selector::makeReadable($name); } // If a rule reference is found, query its data. diff --git a/lib/CssCrush/Hook.php b/lib/CssCrush/Hook.php index e6ad28e..7e5006a 100644 --- a/lib/CssCrush/Hook.php +++ b/lib/CssCrush/Hook.php @@ -4,7 +4,9 @@ * Access to the execution flow. * */ -class CssCrush_Hook +namespace CssCrush; + +class Hook { // Table of hooks and the functions attached to them. static public $register = array(); diff --git a/lib/CssCrush/IO.php b/lib/CssCrush/IO.php index 679b862..d0751b1 100644 --- a/lib/CssCrush/IO.php +++ b/lib/CssCrush/IO.php @@ -4,7 +4,9 @@ * Interface for writing files, retrieving files and checking caches * */ -class CssCrush_IO +namespace CssCrush; + +class IO { // Any setup that needs to be done static public function init () @@ -114,7 +116,7 @@ static public function validateExistingOutput () } } - // Cast because the cached options may be a stdClass if an IO adapter has been used. + // Cast because the cached options may be a \stdClass if an IO adapter has been used. $cached_options = (array) $process->cacheData[$existingfile->filename]['options']; $active_options = $options->get(); @@ -225,7 +227,7 @@ static public function getCacheData () else { CssCrush::log('Creating cache data file.'); } - CssCrush_Util::filePutContents($process->cacheFile, json_encode(array()), __METHOD__); + Util::filePutContents($process->cacheFile, json_encode(array()), __METHOD__); } return $cache_data; @@ -236,10 +238,10 @@ static public function saveCacheData () $process = CssCrush::$process; CssCrush::log('Saving config.'); - CssCrush_Util::filePutContents($process->cacheFile, json_encode($process->cacheData), __METHOD__); + Util::filePutContents($process->cacheFile, json_encode($process->cacheData), __METHOD__); } - static public function write (CssCrush_Stream $stream) + static public function write (Stream $stream) { $process = CssCrush::$process; $target = "{$process->output->dir}/{$process->output->filename}"; @@ -249,7 +251,7 @@ static public function write (CssCrush_Stream $stream) $stream->append($process->newline . "/*# sourceMappingURL=$source_map_filename */"); } - if (CssCrush_Util::filePutContents($target, $stream, __METHOD__)) { + if (Util::filePutContents($target, $stream, __METHOD__)) { if ($process->sourceMap) { if (defined('JSON_PRETTY_PRINT')) { @@ -258,7 +260,7 @@ static public function write (CssCrush_Stream $stream) else { $data = json_encode($process->sourceMap); } - CssCrush_Util::filePutContents("{$process->output->dir}/$source_map_filename", $data, __METHOD__); + Util::filePutContents("{$process->output->dir}/$source_map_filename", $data, __METHOD__); } return "{$process->output->dirUrl}/{$process->output->filename}"; diff --git a/lib/CssCrush/IOWatch.php b/lib/CssCrush/IOWatch.php index 4fca9a0..ef230e8 100644 --- a/lib/CssCrush/IOWatch.php +++ b/lib/CssCrush/IOWatch.php @@ -4,7 +4,9 @@ * IO class for command line file watching. * */ -class CssCrush_IOWatch extends CssCrush_IO +namespace CssCrush; + +class IOWatch extends IO { public static $cacheData = array(); diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index 1f7f82b..6bb3d90 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -4,14 +4,16 @@ * Recursive file importing * */ -class CssCrush_Importer +namespace CssCrush; + +class Importer { static public function hostfile () { $config = CssCrush::$config; $process = CssCrush::$process; $options = $process->options; - $regex = CssCrush_Regex::$patt; + $regex = Regex::$patt; $input = $process->input; $str = ''; @@ -105,7 +107,7 @@ static public function hostfile () $filenames[] = $import->url->value; // Alter all the @import urls to be paths relative to the hostfile. - foreach (CssCrush_Regex::matchAll($regex->import, $import->content) as $m) { + foreach (Regex::matchAll($regex->import, $import->content) as $m) { // Fetch the matched URL. $url2 = $process->tokens->get($m[1][0]); @@ -153,10 +155,10 @@ static protected function rewriteImportedUrls ($import) { static $non_import_urls_patt; if (! $non_import_urls_patt) { - $non_import_urls_patt = CssCrush_Regex::create('(?input->dir, dirname($import->path)); if (empty($link)) { @@ -179,7 +181,7 @@ static protected function rewriteImportedUrls ($import) static protected function prepareForStream (&$str) { - $regex = CssCrush_Regex::$patt; + $regex = Regex::$patt; $process = CssCrush::$process; $tokens = $process->tokens; @@ -212,7 +214,7 @@ static protected function prepareForStream (&$str) self::addMarkers($str); - $str = CssCrush_Util::normalizeWhiteSpace($str); + $str = Util::normalizeWhiteSpace($str); return true; } @@ -249,7 +251,7 @@ static protected function addMarkers (&$str) $process = CssCrush::$process; $current_file_index = count($process->sources) -1; - $count = preg_match_all(CssCrush_Regex::$patt->ruleFirstPass, $str, $matches, PREG_OFFSET_CAPTURE); + $count = preg_match_all(Regex::$patt->ruleFirstPass, $str, $matches, PREG_OFFSET_CAPTURE); while ($count--) { $selector_offset = $matches['selector'][$count][1]; @@ -278,7 +280,7 @@ static protected function addMarkers (&$str) static protected function captureCommentAndString ($str) { - return preg_replace_callback(CssCrush_Regex::$patt->commentAndString, + return preg_replace_callback(Regex::$patt->commentAndString, array('self', 'cb_captureCommentAndString'), $str); } @@ -294,7 +296,7 @@ static protected function cb_captureCommentAndString ($match) $process->minifyOutput || strpos($full_match, '/*$') === 0 ) { - return CssCrush_Tokens::pad('', $full_match); + return Tokens::pad('', $full_match); } // Fix broken comments as they will break any subsquent @@ -314,6 +316,6 @@ static protected function cb_captureCommentAndString ($match) $label = $process->tokens->add($full_match, 's'); } - return CssCrush_Tokens::pad($label, $full_match); + return Tokens::pad($label, $full_match); } } diff --git a/lib/CssCrush/Mixin.php b/lib/CssCrush/Mixin.php index e6f7a9a..4368568 100644 --- a/lib/CssCrush/Mixin.php +++ b/lib/CssCrush/Mixin.php @@ -4,18 +4,20 @@ * Mixin objects. * */ -class CssCrush_Mixin +namespace CssCrush; + +class Mixin { public $template; public function __construct ($block) { - $this->template = new CssCrush_Template($block); + $this->template = new Template($block); } public function call ( array $args ) { - return CssCrush_Rule::parseBlock($this->template->apply($args)); + return Rule::parseBlock($this->template->apply($args)); } static public function parseSingleValue ($message) @@ -49,7 +51,7 @@ static public function parseSingleValue ($message) // If no mixin or abstract rule matched, look for matching selector if (! $mixin && ! $non_mixin) { - $selector_test = CssCrush_Selector::makeReadable($message); + $selector_test = Selector::makeReadable($message); if (isset(CssCrush::$process->references[$selector_test])) { $non_mixin = CssCrush::$process->references[$selector_test]; @@ -87,7 +89,7 @@ static public function parseSingleValue ($message) // Determine what raw arguments there are to pass to the mixin $args = array(); if ($message !== '') { - $args = CssCrush_Util::splitDelimList($message); + $args = Util::splitDelimList($message); } return $mixin->call($args); @@ -98,7 +100,7 @@ static public function parseValue ($message) // Call the mixin and return the list of declarations $declarations = array(); - foreach (CssCrush_Util::splitDelimList($message) as $item) { + foreach (Util::splitDelimList($message) as $item) { if ($result = self::parseSingleValue($item)) { $declarations = array_merge($declarations, $result); diff --git a/lib/CssCrush/Options.php b/lib/CssCrush/Options.php index b6059f5..25cacee 100644 --- a/lib/CssCrush/Options.php +++ b/lib/CssCrush/Options.php @@ -4,7 +4,9 @@ * Options handling. * */ -class CssCrush_Options +namespace CssCrush; + +class Options { public $data = array(); @@ -52,7 +54,7 @@ public function __set ($name, $value) case 'output_dir': case 'doc_root': if (is_string($value)) { - $value = CssCrush_Util::normalizePath(realpath($value)); + $value = Util::normalizePath(realpath($value)); } break; @@ -77,7 +79,7 @@ public function __isset ($name) return isset($this->data[$name]); } - public function merge (CssCrush_Options $options_instance) + public function merge (Options $options_instance) { foreach ($options_instance->data as $key => $value) { if (! array_key_exists($key, $this->data)) { diff --git a/lib/CssCrush/Plugin.php b/lib/CssCrush/Plugin.php index 23ea874..3cd57c1 100644 --- a/lib/CssCrush/Plugin.php +++ b/lib/CssCrush/Plugin.php @@ -4,7 +4,9 @@ * Plugin API * */ -class CssCrush_Plugin +namespace CssCrush; + +class Plugin { static protected $plugins = array(); @@ -17,7 +19,7 @@ static public function info () foreach (glob("$plugin_dir/*.php") as $path) { $name = basename($path, '.php'); - $plugin_data += array($name => CssCrush_Plugin::parseDoc($path)); + $plugin_data += array($name => Plugin::parseDoc($path)); } } @@ -29,7 +31,7 @@ static public function parseDoc ($plugin_path) $contents = file_get_contents($plugin_path); if (preg_match('~/\*\*(.*?)\*/~s', $contents, $m)) { - $lines = preg_split(CssCrush_Regex::$patt->newline, $m[1]); + $lines = preg_split(Regex::$patt->newline, $m[1]); foreach ($lines as &$line) { $line = trim(ltrim($line, "* \t")); } diff --git a/lib/CssCrush/PostAliasFix.php b/lib/CssCrush/PostAliasFix.php index fe9d16e..c835579 100644 --- a/lib/CssCrush/PostAliasFix.php +++ b/lib/CssCrush/PostAliasFix.php @@ -4,11 +4,13 @@ * Fixes for aliasing to legacy syntaxes. * */ -class CssCrush_PostAliasFix +namespace CssCrush; + +class PostAliasFix { // Currently only post fixing aliased functions. static public $functions = array( - ':gradients' => 'csscrush__post_alias_fix_gradients', + ':gradients' => 'CssCrush\postalias_fix_gradients', ); static public function add ($alias_type, $key, $callback) @@ -31,17 +33,16 @@ static public function remove ($alias_type, $key) /** * Post alias fix callback for all gradients. */ -function csscrush__post_alias_fix_gradients ($declaration_copies) { - - csscrush__post_alias_fix_lineargradients($declaration_copies); - csscrush__post_alias_fix_radialgradients($declaration_copies); +function postalias_fix_gradients ($declaration_copies) { + postalias_fix_linear_gradients($declaration_copies); + postalias_fix_radial_gradients($declaration_copies); } /** * Convert the new angle syntax (keyword and degree) on -x-linear-gradient() functions * to legacy equivalents. */ -function csscrush__post_alias_fix_lineargradients ($declaration_copies) { +function postalias_fix_linear_gradients ($declaration_copies) { static $angles_new, $angles_old; if (! $angles_new) { @@ -66,21 +67,21 @@ function csscrush__post_alias_fix_lineargradients ($declaration_copies) { static $deg_patt, $deg_convert_callback, $fn_patt; if (! $deg_patt) { - $deg_patt = CssCrush_Regex::create('(?<=[\( ])({{number}})deg', 'i'); + $deg_patt = Regex::create('(?<=[\( ])({{number}})deg', 'i'); // Legacy angles move anti-clockwise and start from East, not North. $deg_convert_callback = create_function('$m', ' $angle = floatval($m[1]); $angle = ($angle + 90) - ($angle * 2); return ($angle < 0 ? $angle + 360 : $angle) . \'deg\'; '); - $fn_patt = CssCrush_Regex::create('{{LB}}{{vendor}}(?:(?:repeating-)?linear-gradient)({{p-token}})', 'iS'); + $fn_patt = Regex::create('{{LB}}{{vendor}}(?:(?:repeating-)?linear-gradient)({{p-token}})', 'iS'); } // Create new paren tokens based on the first prefixed declaration. // Replace the new syntax with the legacy syntax. $original_parens = array(); $replacement_parens = array(); - foreach (CssCrush_Regex::matchAll($fn_patt, $declaration_copies[0]->value) as $m) { + foreach (Regex::matchAll($fn_patt, $declaration_copies[0]->value) as $m) { $original_parens[] = $m[1][0]; $original_paren_value = CssCrush::$process->tokens->get($m[1][0]); @@ -115,17 +116,17 @@ function csscrush__post_alias_fix_lineargradients ($declaration_copies) { /** * Remove the 'at' keyword from -x-radial-gradient() for legacy implementations. */ -function csscrush__post_alias_fix_radialgradients ($declaration_copies) { +function postalias_fix_radial_gradients ($declaration_copies) { // Create new paren tokens based on the first prefixed declaration. // Replace the new syntax with the legacy syntax. static $fn_patt; if (! $fn_patt) { - $fn_patt = CssCrush_Regex::create('{{LB}}{{vendor}}(?:(?:repeating-)?radial-gradient)({{p-token}})', 'iS'); + $fn_patt = Regex::create('{{LB}}{{vendor}}(?:(?:repeating-)?radial-gradient)({{p-token}})', 'iS'); } $original_parens = array(); $replacement_parens = array(); - foreach (CssCrush_Regex::matchAll($fn_patt, $declaration_copies[0]->value) as $m) { + foreach (Regex::matchAll($fn_patt, $declaration_copies[0]->value) as $m) { $original_parens[] = $m[1][0]; $replacement_parens[] = CssCrush::$process->tokens->add( diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 9b668ee..728d9af 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -4,7 +4,9 @@ * The main class for compiling. * */ -class CssCrush_Process +namespace CssCrush; + +class Process { public function __construct ($options) { @@ -14,7 +16,7 @@ public function __construct ($options) CssCrush::loadAssets(); // Create options instance for this process. - $this->options = new CssCrush_Options($options); + $this->options = new Options($options); // Populate option defaults. $this->options->merge($config->options); @@ -32,10 +34,10 @@ public function __construct ($options) $this->charset = null; $this->sources = array(); $this->vars = array(); - $this->misc = new stdClass(); - $this->input = new stdClass(); - $this->output = new stdClass(); - $this->tokens = new CssCrush_Tokens(); + $this->misc = new \stdClass(); + $this->input = new \stdClass(); + $this->output = new \stdClass(); + $this->tokens = new Tokens(); $this->sourceMap = null; // Copy config values. @@ -64,7 +66,7 @@ public function __construct ($options) break; } - CssCrush_Hook::run('process_init'); + Hook::run('process_init'); } public function release () @@ -135,7 +137,7 @@ protected function getBoilerplate () $boilerplate_option = $this->options->boilerplate; if ($boilerplate_option === true) { - $file = CssCrush_Util::find( + $file = Util::find( 'CssCrush-local.boilerplate', 'CssCrush.boilerplate'); } elseif (is_string($boilerplate_option)) { @@ -186,7 +188,7 @@ protected function getBoilerplate () // Pretty print. $EOL = $this->newline; - $boilerplate = preg_split('~[\t]*'. CssCrush_Regex::$classes->newline . '[\t]*~', $boilerplate); + $boilerplate = preg_split('~[\t]*'. Regex::$classes->newline . '[\t]*~', $boilerplate); $boilerplate = array_map('trim', $boilerplate); $boilerplate = "$EOL * " . implode("$EOL * ", $boilerplate); @@ -201,11 +203,11 @@ protected function resolveSelectorAliases () { static $alias_patt, $callback; if (! $alias_patt) { - $alias_patt = CssCrush_Regex::create('@selector-alias +\:({{ident}}) +([^;]+) *;', 'iS'); + $alias_patt = Regex::create('@selector-alias +\:({{ident}}) +([^;]+) *;', 'iS'); $callback = create_function('$m', ' $name = strtolower($m[1]); - $body = CssCrush_Util::stripCommentTokens($m[2]); - $template = new CssCrush_Template($body); + $body = Util::stripCommentTokens($m[2]); + $template = new Template($body); CssCrush::$process->selectorAliases[$name] = $template; '); } @@ -218,7 +220,7 @@ protected function resolveSelectorAliases () if ($this->selectorAliases) { $names = implode('|', array_keys($this->selectorAliases)); $this->selectorAliasesPatt - = CssCrush_Regex::create('\:(' . $names . '){{RB}}(\()?', 'iS'); + = Regex::create('\:(' . $names . '){{RB}}(\()?', 'iS'); } } @@ -234,7 +236,7 @@ static public function applySelectorAliases (&$str) $table =& $process->selectorAliases; // Find all selector-alias matches. - $selector_alias_calls = CssCrush_Regex::matchAll($process->selectorAliasesPatt, $str); + $selector_alias_calls = Regex::matchAll($process->selectorAliasesPatt, $str); // Step through the matches from last to first. while ($selector_alias_call = array_pop($selector_alias_calls)) { @@ -254,11 +256,11 @@ static public function applySelectorAliases (&$str) if (isset($selector_alias_call[2])) { // Parse argument list. - if (! preg_match(CssCrush_Regex::$patt->balancedParens, $str, + if (! preg_match(Regex::$patt->balancedParens, $str, $parens, PREG_OFFSET_CAPTURE, $start)) { continue; } - $args = CssCrush_Function::parseArgs($parens[1][0]); + $args = Functions::parseArgs($parens[1][0]); // Amend offsets. $paren_start = $parens[0][1]; @@ -392,7 +394,7 @@ protected function filterPlugins () // Remove option disabled plugins from the list, and disable them. if ($disable) { foreach ($disable as $plugin_name => $index) { - CssCrush_Plugin::disable($plugin_name); + Plugin::disable($plugin_name); unset($this->plugins[$plugin_name]); } } @@ -406,7 +408,7 @@ protected function filterPlugins () // Enable all plugins in the remaining list. foreach ($this->plugins as $plugin_name => $bool) { - CssCrush_Plugin::enable($plugin_name); + Plugin::enable($plugin_name); } } @@ -417,11 +419,10 @@ protected function filterPlugins () protected function calculateVars () { $config = CssCrush::$config; - $regex = CssCrush_Regex::$patt; + $regex = Regex::$patt; $option_vars = $this->options->vars; - $this->stream->pregReplaceCallback($regex->vars, - array('CssCrush_Process', 'cb_captureVars')); + $this->stream->pregReplaceCallback($regex->vars, '\CssCrush\Process::cb_captureVars'); // In-file variables override global variables. $this->vars = array_merge($config->vars, $this->vars); @@ -435,11 +436,11 @@ protected function calculateVars () foreach ($this->vars as $name => &$value) { // Referenced variables. - $value = preg_replace_callback($regex->varFunction, array('self', 'cb_placeVars'), $value); + $value = preg_replace_callback($regex->varFunction, '\CssCrush\Process::cb_placeVars', $value); // Variable values can be escaped from function parsing with a tilde prefix. if (strpos($value, '~') !== 0) { - CssCrush_Function::executeOnString($value); + Functions::executeOnString($value); } } } @@ -467,17 +468,17 @@ protected function placeAllVars () static protected function placeVars (&$value) { - $regex = CssCrush_Regex::$patt; + $regex = Regex::$patt; // Variables with no default value. $value = preg_replace_callback($regex->varFunction, - array('CssCrush_Process', 'cb_placeVars'), $value, -1, $count); + '\CssCrush\Process::cb_placeVars', $value, -1, $count); if (strpos($value, '$(') !== false) { // Variables with default value. - CssCrush_Function::executeOnString($value, '~(\$)\(~', - array('$' => array('CssCrush_Process', 'cb_placeVarsWithDefault'))); + Functions::executeOnString($value, '~(\$)\(~', + array('$' => '\CssCrush\Process::cb_placeVarsWithDefault')); // Assume at least 1 replace. $count = 1; @@ -492,7 +493,7 @@ static public function cb_captureVars ($m) CssCrush::$process->vars = array_merge( CssCrush::$process->vars, - CssCrush_Rule::parseBlock($m['block_content'], array('keyed' => true, 'ignore_directives' => true))); + Rule::parseBlock($m['block_content'], array('keyed' => true, 'ignore_directives' => true))); } static protected function cb_placeVars ($m) @@ -505,7 +506,7 @@ static protected function cb_placeVars ($m) static public function cb_placeVarsWithDefault ($raw_arg) { - list($name, $default_value) = CssCrush_Function::parseArgsSimple($raw_arg); + list($name, $default_value) = Functions::parseArgsSimple($raw_arg); if (isset(CssCrush::$process->vars[$name])) { return CssCrush::$process->vars[$name]; @@ -521,12 +522,12 @@ static public function cb_placeVarsWithDefault ($raw_arg) protected function resolveIfDefines () { - $matches = $this->stream->matchAll(CssCrush_Regex::$patt->ifDefine); + $matches = $this->stream->matchAll(Regex::$patt->ifDefine); // Move through the matches last to first. while ($match = array_pop($matches)) { - $curly_match = new CssCrush_BalancedMatch($this->stream, $match[0][1]); + $curly_match = new BalancedMatch($this->stream, $match[0][1]); if (! $curly_match->match) { // Couldn't match the block. @@ -557,11 +558,11 @@ protected function captureMixins () static $callback; if (! $callback) { $callback = create_function('$m', ' - CssCrush::$process->mixins[$m[\'name\']] = new CssCrush_Mixin($m[\'block_content\']); + CssCrush::$process->mixins[$m[\'name\']] = new Mixin($m[\'block_content\']); '); } - $this->stream->pregReplaceCallback(CssCrush_Regex::$patt->mixin, $callback); + $this->stream->pregReplaceCallback(Regex::$patt->mixin, $callback); } @@ -574,7 +575,7 @@ protected function resolveFragments () if (! $capture_callback) { $capture_callback = create_function('$m', ' - CssCrush::$process->fragments[$m[\'name\']] = new CssCrush_Fragment( + CssCrush::$process->fragments[$m[\'name\']] = new CssCrush\Fragment( $m[\'block_content\'], array(\'name\' => strtolower($m[\'name\']))); return \'\';'); @@ -584,15 +585,15 @@ protected function resolveFragments () if ($fragment) { $args = array(); if (isset($m[\'parens\'])) { - $args = CssCrush_Function::parseArgs($m[\'parens_content\']); + $args = Functions::parseArgs($m[\'parens_content\']); } return $fragment->apply($args); } return \'\';'); } - $this->stream->pregReplaceCallback(CssCrush_Regex::$patt->fragmentCapture, $capture_callback); - $this->stream->pregReplaceCallback(CssCrush_Regex::$patt->fragmentInvoke, $invoke_callback); + $this->stream->pregReplaceCallback(Regex::$patt->fragmentCapture, $capture_callback); + $this->stream->pregReplaceCallback(Regex::$patt->fragmentInvoke, $invoke_callback); } @@ -601,7 +602,27 @@ protected function resolveFragments () public function captureRules () { - $this->stream->pregReplaceCallback(CssCrush_Regex::$patt->rule, array('CssCrush_Process', 'cb_captureRules')); + $this->stream->pregReplaceCallback(Regex::$patt->rule, function ($m) { + + $selector = trim($m['selector']); + $block = trim($m['block_content']); + + // Ignore and remove empty rules. + if (empty($block) || empty($selector)) { + return ''; + } + + $rule = new Rule($selector, $block, $m['trace_token']); + + // Store rules if they have declarations or extend arguments. + if (! empty($rule->declarations) || $rule->extendArgs) { + + CssCrush::$process->tokens->add($rule, 'r', $rule->label); + + // If only using extend still return a label. + return $rule->label; + } + }); } protected function processRules () @@ -612,7 +633,7 @@ protected function processRules () $rule->processDeclarations(); - CssCrush_Hook::run('rule_prealias', $rule); + Hook::run('rule_prealias', $rule); if ($aliases['properties']) { $rule->addPropertyAliases(); @@ -624,41 +645,18 @@ protected function processRules () $rule->addDeclarationAliases(); } - CssCrush_Hook::run('rule_postalias', $rule); + Hook::run('rule_postalias', $rule); $rule->expandSelectors(); // Find previous selectors and apply them. $rule->applyExtendables(); - CssCrush_Hook::run('rule_postprocess', $rule); - } - } - - static public function cb_captureRules ($m) - { - $selector = trim($m['selector']); - $block = trim($m['block_content']); - - // Ignore and remove empty rules. - if (empty($block) || empty($selector)) { - return ''; - } - - $rule = new CssCrush_Rule($selector, $block, $m['trace_token']); - - // Store rules if they have declarations or extend arguments. - if (! empty($rule->declarations) || $rule->extendArgs) { - - CssCrush::$process->tokens->add($rule, 'r', $rule->label); - - // If only using extend still return a label. - return $rule->label; + Hook::run('rule_postprocess', $rule); } } - ############################# # @in blocks. @@ -673,12 +671,12 @@ protected function prefixSelectors () $match_start_pos = $match[0][1]; $raw_argument = trim($match[1][0]); - CssCrush_Process::applySelectorAliases($raw_argument); + Process::applySelectorAliases($raw_argument); $raw_argument = $tokens->captureParens($raw_argument); - $arguments = CssCrush_Util::splitDelimList($raw_argument); + $arguments = Util::splitDelimList($raw_argument); - $curly_match = new CssCrush_BalancedMatch($this->stream, $match_start_pos); + $curly_match = new BalancedMatch($this->stream, $match_start_pos); if (! $curly_match->match || empty($raw_argument)) { // Couldn't match the block. @@ -686,8 +684,8 @@ protected function prefixSelectors () } // Match all the rule tokens. - $rule_matches = CssCrush_Regex::matchAll( - CssCrush_Regex::$patt->r_token, $curly_match->inside()); + $rule_matches = Regex::matchAll( + Regex::$patt->r_token, $curly_match->inside()); foreach ($rule_matches as $rule_match) { @@ -717,14 +715,14 @@ protected function prefixSelectors () $arg_selector, $rule_selector->value); - $new = new CssCrush_Selector($new_value); + $new = new Selector($new_value); $new_selector_list[$new->readableValue] = $new; } // Prepending the prefix. else { - $new = new CssCrush_Selector("$arg_selector {$rule_selector->value}"); + $new = new Selector("$arg_selector {$rule_selector->value}"); $new_selector_list[$new->readableValue] = $new; } } @@ -748,7 +746,7 @@ protected function aliasAtRules () } $aliases = $this->aliases['at-rules']; - $regex = CssCrush_Regex::$patt; + $regex = Regex::$patt; foreach ($aliases as $at_rule => $at_rule_aliases) { @@ -757,7 +755,7 @@ protected function aliasAtRules () // Find at-rules that we want to alias. while ($match = array_pop($matches)) { - $curly_match = new CssCrush_BalancedMatch($this->stream, $match[0][1]); + $curly_match = new BalancedMatch($this->stream, $match[0][1]); if (! $curly_match->match) { // Couldn't match the block. @@ -821,7 +819,7 @@ protected function collate () { $options = $this->options; $minify = $options->minify; - $regex = CssCrush_Regex::$patt; + $regex = Regex::$patt; $EOL = $this->newline; // Formatting replacements. @@ -841,7 +839,7 @@ protected function collate () $regex_replacements['~ ?(@[^;]+\;)~'] = "$1$EOL"; // Trim leading spaces on @-rules and some tokens. - $regex_replacements[CssCrush_Regex::create(' +([@}]|\?[rc]{{token-id}}\?)', 'S')] = "$1"; + $regex_replacements[Regex::create(' +([@}]|\?[rc]{{token-id}}\?)', 'S')] = "$1"; } // Apply all formatting replacements. @@ -888,7 +886,7 @@ protected function collate () $urls = $this->tokens->store->u; if ($urls) { - $link = CssCrush_Util::getLinkBetweenPaths($this->output->dir, $this->input->dir); + $link = Util::getLinkBetweenPaths($this->output->dir, $this->input->dir); $make_urls_absolute = $options->rewrite_import_urls === 'absolute'; foreach ($urls as $token => $url) { @@ -948,10 +946,10 @@ public function compile ($io_context = 'file') $this->filterPlugins(); $this->filterAliases(); - CssCrush_Function::setMatchPatt(); + Functions::setMatchPatt(); // Collate hostfile and imports. - $this->stream = new CssCrush_Stream(CssCrush_Importer::hostfile($this->input)); + $this->stream = new Stream(Importer::hostfile($this->input)); // Extract and calculate variables. $this->calculateVars(); @@ -961,7 +959,7 @@ public function compile ($io_context = 'file') $this->resolveIfDefines(); // Capture phase 1 hook: After all vars have resolved. - CssCrush_Hook::run('capture_phase1', $this); + Hook::run('capture_phase1', $this); $this->resolveSelectorAliases(); @@ -970,7 +968,7 @@ public function compile ($io_context = 'file') $this->resolveFragments(); // Capture phase 2 hook: After most built-in directives have resolved. - CssCrush_Hook::run('capture_phase2', $this); + Hook::run('capture_phase2', $this); $this->captureRules(); // csscrush::log(array_keys($this->references)); @@ -1003,12 +1001,12 @@ public function generateSourceMap () 'sources' => array(), ); foreach ($this->sources as $source) { - $this->sourceMap['sources'][] = CssCrush_Util::getLinkBetweenPaths($this->output->dir, $source, false); + $this->sourceMap['sources'][] = Util::getLinkBetweenPaths($this->output->dir, $source, false); } - $patt = CssCrush_Regex::create('\?[tm]{{token-id}}\?', 'S'); + $patt = Regex::create('\?[tm]{{token-id}}\?', 'S'); $mappings = array(); - $lines = preg_split(CssCrush_Regex::$patt->newline, $this->stream->raw); + $lines = preg_split(Regex::$patt->newline, $this->stream->raw); $tokens =& $this->tokens->store; // All mappings are calculated as delta values. @@ -1030,10 +1028,10 @@ public function generateSourceMap () list($src_file, $src_line, $src_col) = $tokens->{$token_type}[$token]; $line_segments[] = - CssCrush_Util::vlqEncode($dest_col - $previous_dest_col) . - CssCrush_Util::vlqEncode($src_file - $previous_src_file) . - CssCrush_Util::vlqEncode($src_line - $previous_src_line) . - CssCrush_Util::vlqEncode($src_col - $previous_src_col); + Util::vlqEncode($dest_col - $previous_dest_col) . + Util::vlqEncode($src_file - $previous_src_file) . + Util::vlqEncode($src_line - $previous_src_line) . + Util::vlqEncode($src_col - $previous_src_col); $previous_dest_col = $dest_col; $previous_src_file = $src_file; @@ -1081,16 +1079,13 @@ public function generateTracingStub ($m) protected function decruft () { - $patt =& CssCrush_Regex::$patt; - $classes =& CssCrush_Regex::$classes; - return $this->stream->pregReplaceHash(array( // Strip leading zeros on floats. '~([: \(,])(-?)0(\.\d+)~S' => '$1$2$3', // Strip unnecessary units on zero values for length types. - '~([: \(,])\.?0' . $classes->length_unit . '~iS' => '${1}0', + '~([: \(,])\.?0' . Regex::$classes->length_unit . '~iS' => '${1}0', // Collapse zero lists. '~(\: *)(?:0 0 0|0 0 0 0) *([;}])~S' => '${1}0$2', @@ -1103,7 +1098,7 @@ protected function decruft () '~(\: *)0 0 (-?(?:\d+)?\.?\d+[a-z]{1,4}) 0 *([;}])~iS' => '${1}0 0 $2$3', // Compress hex codes. - $patt->cruftyHex => '#$1$2$3', + Regex::$patt->cruftyHex => '#$1$2$3', )); } @@ -1117,19 +1112,19 @@ protected function minifyColors () if (! $keywords_patt) { - $keywords =& CssCrush_Color::loadMinifyableKeywords(); + $keywords =& Color::loadMinifyableKeywords(); $keywords_patt = '~(?ident = '[a-zA-Z0-9_-]+'; @@ -62,19 +64,19 @@ static public function init () $patt->rooted_number = '~^' . $classes->number . '$~'; // @-rules. - $patt->import = CssCrush_Regex::create('@import \s+ ({{u-token}}) \s? ([^;]*);', 'ixS'); - $patt->charset = CssCrush_Regex::create('@charset \s+ ({{s-token}}) \s*;', 'ixS'); - $patt->vars = CssCrush_Regex::create('@define \s* {{block}}', 'ixS'); - $patt->mixin = CssCrush_Regex::create('@mixin \s+ (?{{ident}}) \s* {{block}}', 'ixS'); - $patt->ifDefine = CssCrush_Regex::create('@ifdefine \s+ (not \s+)? ({{ident}}) \s* \{', 'ixS'); - $patt->fragmentCapture = CssCrush_Regex::create('@fragment \s+ (?{{ident}}) \s* {{block}}', 'ixS'); - $patt->fragmentInvoke = CssCrush_Regex::create('@fragment \s+ (?{{ident}}) {{parens}}? \s* ;', 'ixS'); - $patt->abstract = CssCrush_Regex::create('^@abstract \s+ (?{{ident}})', 'ixS'); + $patt->import = Regex::create('@import \s+ ({{u-token}}) \s? ([^;]*);', 'ixS'); + $patt->charset = Regex::create('@charset \s+ ({{s-token}}) \s*;', 'ixS'); + $patt->vars = Regex::create('@define \s* {{block}}', 'ixS'); + $patt->mixin = Regex::create('@mixin \s+ (?{{ident}}) \s* {{block}}', 'ixS'); + $patt->ifDefine = Regex::create('@ifdefine \s+ (not \s+)? ({{ident}}) \s* \{', 'ixS'); + $patt->fragmentCapture = Regex::create('@fragment \s+ (?{{ident}}) \s* {{block}}', 'ixS'); + $patt->fragmentInvoke = Regex::create('@fragment \s+ (?{{ident}}) {{parens}}? \s* ;', 'ixS'); + $patt->abstract = Regex::create('^@abstract \s+ (?{{ident}})', 'ixS'); // Functions. - $patt->function = CssCrush_Regex::create('{{LB}} ({{ident}}) ({{p-token}})', 'xS'); - $patt->varFunction = CssCrush_Regex::create('\$\( \s* ({{ident}}) \s* \)', 'xS'); - $patt->thisFunction = CssCrush_Regex::createFunctionPatt(array('this')); + $patt->function = Regex::create('{{LB}} ({{ident}}) ({{p-token}})', 'xS'); + $patt->varFunction = Regex::create('\$\( \s* ({{ident}}) \s* \)', 'xS'); + $patt->thisFunction = Regex::createFunctionPatt(array('this')); // Strings and comments. $patt->string = '~(\'|")(?:\\\\\1|[^\1])*?\1~xS'; @@ -87,7 +89,7 @@ static public function init () ~xsS'; // Rules. - $patt->ruleFirstPass = CssCrush_Regex::create(' + $patt->ruleFirstPass = Regex::create(' (?:^|(?<=[;{}])) (? (?: \s | {{c-token}} )* @@ -102,7 +104,7 @@ static public function init () ) {{block}}', 'xS'); - $patt->rule = CssCrush_Regex::create(' + $patt->rule = Regex::create(' (? {{t-token}} ) \s* (? [^{]+ ) @@ -118,7 +120,7 @@ static public function init () $patt->ruleDirective = '~^(?:(@include)|(@extends?)|(@name))[\s]+~iS'; $patt->argListSplit = '~\s*[,\s]\s*~S'; $patt->mathBlacklist = '~[^\.0-9\*\/\+\-\(\)]~S'; - $patt->cruftyHex = CssCrush_Regex::create('\#({{hex}})\1({{hex}})\2({{hex}})\3', 'S'); + $patt->cruftyHex = Regex::create('\#({{hex}})\1({{hex}})\2({{hex}})\3', 'S'); } static public function create ($pattern_template, $flags = '', $delim = '~') @@ -169,8 +171,8 @@ static public function createFunctionPatt ($list, $options = array()) $flat_list = implode('|', $list); - return CssCrush_Regex::create("($template{{LB}}(?:$flat_list)$question)\(", 'iS'); + return Regex::create("($template{{LB}}(?:$flat_list)$question)\(", 'iS'); } } -CssCrush_Regex::init(); +Regex::init(); diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php index 20ff38f..14d3b9e 100644 --- a/lib/CssCrush/Rule.php +++ b/lib/CssCrush/Rule.php @@ -4,7 +4,9 @@ * CSS rule API. * */ -class CssCrush_Rule implements IteratorAggregate +namespace CssCrush; + +class Rule implements \IteratorAggregate { public $vendorContext; public $label; @@ -29,27 +31,27 @@ class CssCrush_Rule implements IteratorAggregate public function __construct ($selector_string, $declarations_string, $trace_token = null) { - $regex = CssCrush_Regex::$patt; + $regex = Regex::$patt; $process = CssCrush::$process; $this->label = $process->tokens->createLabel('r'); $this->marker = $process->addTracingStubs || $process->generateMap ? $trace_token : null; - if (! empty(CssCrush_Hook::$register['rule_preprocess'])) { + if (! empty(Hook::$register['rule_preprocess'])) { // Juggling to maintain the old API. // TODO: rework this for 2.x? - $rule = new stdClass(); + $rule = new \stdClass(); $rule->selector_raw = $selector_string; $rule->declaration_raw = $declarations_string; - CssCrush_Hook::run('rule_preprocess', $rule); + Hook::run('rule_preprocess', $rule); $selector_string = $rule->selector_raw; $declarations_string = $rule->declaration_raw; } // Parse selectors. // Strip any other comments then create selector instances. - $selector_string = trim(CssCrush_Util::stripCommentTokens($selector_string)); + $selector_string = trim(Util::stripCommentTokens($selector_string)); - foreach (CssCrush_Util::splitDelimList($selector_string) as $selector) { + foreach (Util::splitDelimList($selector_string) as $selector) { // If the selector matches an absract directive if (preg_match($regex->abstract, $selector, $m)) { @@ -58,12 +60,12 @@ public function __construct ($selector_string, $declarations_string, $trace_toke $process->references[strtolower($m['name'])] = $this; } else { - $this->addSelector(new CssCrush_Selector($selector)); + $this->addSelector(new Selector($selector)); } } // Parse rule block. - $pairs = CssCrush_Rule::parseBlock($declarations_string); + $pairs = Rule::parseBlock($declarations_string); foreach ($pairs as $index => $pair) { @@ -271,8 +273,8 @@ public function setExtendSelectors ($raw_value) // Reset if called earlier, last call wins by intention. $this->extendArgs = array(); - foreach (CssCrush_Util::splitDelimList($raw_value) as $arg) { - $this->extendArgs[] = new CssCrush_ExtendArg($arg); + foreach (Util::splitDelimList($raw_value) as $arg) { + $this->extendArgs[] = new ExtendArg($arg); } } @@ -356,7 +358,7 @@ public function expandSelectors () static $any_patt, $reg_comma; if (! $any_patt) { - $any_patt = CssCrush_Regex::create(':any({{p-token}})', 'i'); + $any_patt = Regex::create(':any({{p-token}})', 'i'); $reg_comma = '~\s*,\s*~'; } @@ -414,7 +416,7 @@ public function expandSelectors () // Finish off. foreach ($chain as &$row) { - $new = new CssCrush_Selector($row . $selector->value); + $new = new Selector($row . $selector->value); $new_set[$new->readableValue] = $new; } } @@ -458,7 +460,7 @@ public function addPropertyAliases () $stack = array(); $rule_updated = false; $vendor_context = $this->vendorContext; - $regex = CssCrush_Regex::$patt; + $regex = Regex::$patt; foreach ($this->declarations as $declaration) { @@ -587,8 +589,8 @@ public function addFunctionAliases () } // Post fixes. - if (isset(CssCrush_PostAliasFix::$functions[$group_id])) { - call_user_func(CssCrush_PostAliasFix::$functions[$group_id], $prefixed_copies, $group_id); + if (isset(PostAliasFix::$functions[$group_id])) { + call_user_func(PostAliasFix::$functions[$group_id], $prefixed_copies, $group_id); } } @@ -599,7 +601,7 @@ public function addFunctionAliases () // If the declaration is vendor specific only create aliases for the same vendor. if ($declaration->vendor) { - preg_match(CssCrush_Regex::$patt->vendorPrefix, $fn_alias, $m); + preg_match(Regex::$patt->vendorPrefix, $fn_alias, $m); if ( $m[1] !== $declaration->vendor || ($vendor_context && $m[1] !== $vendor_context) @@ -621,8 +623,8 @@ public function addFunctionAliases () } // Post fixes. - if (isset(CssCrush_PostAliasFix::$functions[$fn_name])) { - call_user_func(CssCrush_PostAliasFix::$functions[$fn_name], $prefixed_copies, $fn_name); + if (isset(PostAliasFix::$functions[$fn_name])) { + call_user_func(PostAliasFix::$functions[$fn_name], $prefixed_copies, $fn_name); } } @@ -673,7 +675,7 @@ public function addDeclarationAliases () } // If the replacement property is null use the original declaration property. - $new = new CssCrush_Declaration( + $new = new Declaration( ! empty($values[0]) ? $values[0] : $declaration->property, $values[1] ); @@ -742,7 +744,7 @@ public function propertyCount ($prop) public function addDeclaration ($prop, $value, $contextIndex = 0) { // Create declaration, add to the stack if it's valid - $declaration = new CssCrush_Declaration($prop, $value, $contextIndex); + $declaration = new Declaration($prop, $value, $contextIndex); if (empty($declaration->inValid)) { @@ -764,8 +766,8 @@ public function setDeclarations (array $declaration_stack) static public function parseBlock ($str, $options = array()) { - $regex = CssCrush_Regex::$patt; - $str = CssCrush_Util::stripCommentTokens($str); + $regex = Regex::$patt; + $str = Util::stripCommentTokens($str); $lines = preg_split('~\s*;\s*~', $str, null, PREG_SPLIT_NO_EMPTY); $keyed = isset($options['keyed']); @@ -806,7 +808,7 @@ static public function parseBlock ($str, $options = array()) // Add any mixins. if ($property === 'mixin') { - if ($mixables = CssCrush_Mixin::parseValue($value)) { + if ($mixables = Mixin::parseValue($value)) { // Add mixin declarations to the stack. while ($mixable = array_shift($mixables)) { diff --git a/lib/CssCrush/Selector.php b/lib/CssCrush/Selector.php index 9d68bbc..5af56ff 100644 --- a/lib/CssCrush/Selector.php +++ b/lib/CssCrush/Selector.php @@ -4,7 +4,9 @@ * Selector objects. * */ -class CssCrush_Selector +namespace CssCrush; + +class Selector { public $value; public $readableValue; @@ -19,9 +21,9 @@ public function __construct ($raw_selector, $associated_rule = null) } // Take readable value from original un-altered state. - $this->readableValue = CssCrush_Selector::makeReadable($raw_selector); + $this->readableValue = Selector::makeReadable($raw_selector); - CssCrush_Process::applySelectorAliases($raw_selector); + Process::applySelectorAliases($raw_selector); // Capture top-level paren groups. $this->value = CssCrush::$process->tokens->captureParens($raw_selector); @@ -30,7 +32,7 @@ public function __construct ($raw_selector, $associated_rule = null) public function __toString () { if (! CssCrush::$process->minifyOutput) { - $this->value = CssCrush_Selector::normalizeWhiteSpace($this->value); + $this->value = Selector::normalizeWhiteSpace($this->value); } return $this->value; } @@ -38,7 +40,7 @@ public function __toString () public function appendPseudo ($pseudo) { // Check to avoid doubling-up - if (! CssCrush_Stream::endsWith($this->readableValue, $pseudo)) { + if (! Stream::endsWith($this->readableValue, $pseudo)) { $this->readableValue .= $pseudo; $this->value .= $pseudo; @@ -50,7 +52,7 @@ static public function normalizeWhiteSpace ($str) { // Create space around combinators, then normalize whitespace. $str = preg_replace('~([>+]|\~(?!=))~S', ' $1 ', $str); - return CssCrush_Util::normalizeWhiteSpace($str); + return Util::normalizeWhiteSpace($str); } static function makeReadable ($str) @@ -60,7 +62,7 @@ static function makeReadable ($str) $str = CssCrush::$process->tokens->restore($str, 'p'); } - $str = CssCrush_Selector::normalizeWhiteSpace($str); + $str = Selector::normalizeWhiteSpace($str); // Quick test for string tokens. if (strpos($str, '?s') !== false) { diff --git a/lib/CssCrush/Stream.php b/lib/CssCrush/Stream.php index 2e7a10d..e6ee8a2 100644 --- a/lib/CssCrush/Stream.php +++ b/lib/CssCrush/Stream.php @@ -4,7 +4,9 @@ * Stream sugar. * */ -class CssCrush_Stream +namespace CssCrush; + +class Stream { public function __construct ($str) { @@ -42,7 +44,7 @@ public function substr ($start, $length = null) public function matchAll ($patt, $preprocess_patt = false) { - return CssCrush_Regex::matchAll($patt, $this->raw, $preprocess_patt); + return Regex::matchAll($patt, $this->raw, $preprocess_patt); } public function replace ($find, $replacement) @@ -71,7 +73,7 @@ public function replaceTokens ($type, $callback = null) $tokens =& CssCrush::$process->tokens->store->' . $type . '; return isset($tokens[$m[0]]) ? $tokens[$m[0]] : \'\'; '); - $types[$type]['patt'] = CssCrush_Regex::create("\? $type {{token-id}} \?", 'xS'); + $types[$type]['patt'] = Regex::create("\? $type {{token-id}} \?", 'xS'); } $this->raw = preg_replace_callback($types[$type]['patt'], $callback ? $callback : $types[$type]['callback'], $this->raw); diff --git a/lib/CssCrush/Template.php b/lib/CssCrush/Template.php index 303cd93..b022025 100644 --- a/lib/CssCrush/Template.php +++ b/lib/CssCrush/Template.php @@ -4,7 +4,9 @@ * Generalized 'in CSS' templating. * */ -class CssCrush_Template +namespace CssCrush; + +class Template { // Positional argument default values. public $defaults = array(); @@ -21,15 +23,15 @@ public function __construct ($str, $options = array()) { static $arg_patt; if (! $arg_patt) { - $arg_patt = CssCrush_Regex::createFunctionPatt( + $arg_patt = Regex::createFunctionPatt( array('arg'), array('templating' => true)); } - $str = CssCrush_Template::unTokenize($str); + $str = Template::unTokenize($str); // Parse all arg function calls in the passed string, // callback creates default values. - CssCrush_Function::executeOnString($str, $arg_patt, array( + Functions::executeOnString($str, $arg_patt, array( 'arg' => array($this, 'capture'), '#' => array($this, 'capture'), )); @@ -39,7 +41,7 @@ public function __construct ($str, $options = array()) public function capture ($str) { - $args = CssCrush_Function::parseArgsSimple($str); + $args = Functions::parseArgsSimple($str); $position = array_shift($args); @@ -76,7 +78,7 @@ public function getArgValue ($index, &$args) $default = isset($this->defaults[$index]) ? $this->defaults[$index] : ''; // Recurse for nested arg() calls. - while (preg_match(CssCrush_Regex::$patt->a_token, $default, $m)) { + while (preg_match(Regex::$patt->a_token, $default, $m)) { $default = str_replace( $m[0], $this->getArgValue((int) $m[1], $args), @@ -137,7 +139,7 @@ public function apply (array $args = null, $str = null) $str = isset($find) ? str_replace($find, $replace, $str) : $str; // Re-tokenize string on return. - return CssCrush_Template::tokenize($str); + return Template::tokenize($str); } static public function tokenize ($str) diff --git a/lib/CssCrush/Tokens.php b/lib/CssCrush/Tokens.php index be9fe02..9d146e4 100644 --- a/lib/CssCrush/Tokens.php +++ b/lib/CssCrush/Tokens.php @@ -4,7 +4,9 @@ * Token API. * */ -class CssCrush_Tokens +namespace CssCrush; + +class Tokens { public $store; protected $ids; @@ -20,8 +22,8 @@ public function __construct () 't', // Traces ); - $this->store = new stdClass; - $this->ids = new stdClass; + $this->store = new \stdClass; + $this->ids = new \stdClass; foreach ($types as $type) { $this->store->{$type} = array(); @@ -74,13 +76,13 @@ public function restore ($str, $type, $release = false) '); } - $str = preg_replace_callback(CssCrush_Regex::$patt->u_token, $url_revert_callback, $str); + $str = preg_replace_callback(Regex::$patt->u_token, $url_revert_callback, $str); break; default: $token_table =& $this->store->{$type}; // Find matching tokens. - foreach (CssCrush_Regex::matchAll(CssCrush_Regex::$patt->{"{$type}_token"}, $str) as $m) { + foreach (Regex::matchAll(Regex::$patt->{"{$type}_token"}, $str) as $m) { $label = $m[0][0]; if (isset($token_table[$label])) { $str = str_replace($label, $token_table[$label], $str); @@ -112,27 +114,23 @@ public function capture ($str, $type) public function captureParens ($str) { - static $callback; - if (! $callback) { - $callback = create_function('$m', 'return CssCrush::$process->tokens->add($m[0], \'p\');'); - } - return preg_replace_callback(CssCrush_Regex::$patt->balancedParens, $callback, $str); + return preg_replace_callback(Regex::$patt->balancedParens, function ($m) { + return CssCrush::$process->tokens->add($m[0], 'p'); + }, $str); } public function captureStrings ($str, $add_padding = false) { - static $callback; - if (! $callback) { - $callback = create_function('$m', 'return CssCrush::$process->tokens->add($m[0], \'s\');'); - } - return preg_replace_callback(CssCrush_Regex::$patt->string, $callback, $str); + return preg_replace_callback(Regex::$patt->string, function ($m) { + return CssCrush::$process->tokens->add($m[0], 's'); + }, $str); } public function captureUrls ($str, $add_padding = false) { static $url_patt; if (! $url_patt) { - $url_patt = CssCrush_Regex::create( + $url_patt = Regex::create( '@import \s+ (?{{s-token}}) | {{LB}} (?url|data-uri) {{parens}}', 'ixS'); } @@ -146,7 +144,7 @@ public function captureUrls ($str, $add_padding = false) // @import directive. if ($import_offset !== -1) { - $url = new CssCrush_Url(trim($import_text)); + $url = new Url(trim($import_text)); $str = str_replace($import_text, $add_padding ? str_pad($url->label, strlen($import_text)) : $url->label, $str); } @@ -155,9 +153,9 @@ public function captureUrls ($str, $add_padding = false) $func_name = strtolower($m['func'][$count][0]); - $url = new CssCrush_Url(trim($m['parens_content'][$count][0])); + $url = new Url(trim($m['parens_content'][$count][0])); $url->convertToData = 'data-uri' === $func_name; - $str = substr_replace($str, $add_padding ? CssCrush_Tokens::pad($url->label, $full_text) : $url->label, $full_offset, strlen($full_text)); + $str = substr_replace($str, $add_padding ? Tokens::pad($url->label, $full_text) : $url->label, $full_offset, strlen($full_text)); } } @@ -184,7 +182,7 @@ static public function is ($label, $of_type) { static $type_patt; if (! $type_patt) { - $type_patt = CssCrush_Regex::create('^ \? (?[a-z]) {{token-id}} \? $', 'xS'); + $type_patt = Regex::create('^ \? (?[a-z]) {{token-id}} \? $', 'xS'); } if (preg_match($type_patt, $label, $m)) { return $of_type ? ($of_type === $m['type']) : true; diff --git a/lib/CssCrush/Url.php b/lib/CssCrush/Url.php index 81f9269..8bd601f 100644 --- a/lib/CssCrush/Url.php +++ b/lib/CssCrush/Url.php @@ -4,7 +4,9 @@ * URL tokens. * */ -class CssCrush_Url +namespace CssCrush; + +class Url { public $protocol; @@ -20,7 +22,7 @@ class CssCrush_Url public function __construct ($raw_value, $convert_to_data = false) { - $regex = CssCrush_Regex::$patt; + $regex = Regex::$patt; $tokens = CssCrush::$process->tokens; if (preg_match($regex->s_token, $raw_value)) { @@ -223,7 +225,7 @@ public function setType ($type = 'absolute') public function simplify () { if (! $this->isData) { - $this->value = CssCrush_Util::simplifyPath($this->value); + $this->value = Util::simplifyPath($this->value); } return $this; } diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php index 82dd0ff..f11ba85 100644 --- a/lib/CssCrush/Util.php +++ b/lib/CssCrush/Util.php @@ -4,7 +4,9 @@ * General utilities. * */ -class CssCrush_Util +namespace CssCrush; + +class Util { /* * Create html attribute string from array. @@ -33,7 +35,7 @@ static public function normalizePath ($path, $strip_drive_letter = false) $path = substr($path, 2); } - return CssCrush_Util::simplifyPath($path); + return Util::simplifyPath($path); } static public function simplifyPath ($path) @@ -60,7 +62,7 @@ static public function find () static public function stripCommentTokens ($str) { - return preg_replace(CssCrush_Regex::$patt->c_token, '', $str); + return preg_replace(Regex::$patt->c_token, '', $str); } static public function normalizeWhiteSpace ($str) @@ -91,7 +93,7 @@ static public function splitDelimList ($str, $delim = ',') return strlen($str) ? array($str) : array(); } - if ($match_count = preg_match_all(CssCrush_Regex::$patt->balancedParens, $str, $matches)) { + if ($match_count = preg_match_all(Regex::$patt->balancedParens, $str, $matches)) { $keys = array(); foreach ($matches[0] as $index => &$value) { $keys[] = "?$index?"; @@ -114,8 +116,8 @@ static public function splitDelimList ($str, $delim = ',') static public function getLinkBetweenPaths ($path1, $path2, $directories = true) { // Normalise the paths. - $path1 = trim(CssCrush_Util::normalizePath($path1, true), '/'); - $path2 = trim(CssCrush_Util::normalizePath($path2, true), '/'); + $path1 = trim(Util::normalizePath($path1, true), '/'); + $path2 = trim(Util::normalizePath($path2, true), '/'); // The link between. $link = ''; diff --git a/lib/CssCrush/Version.php b/lib/CssCrush/Version.php index f4c5089..6d48167 100644 --- a/lib/CssCrush/Version.php +++ b/lib/CssCrush/Version.php @@ -4,7 +4,9 @@ * Version string. * */ -class CssCrush_Version +namespace CssCrush; + +class Version { public $major = 0; public $minor = 0; @@ -54,7 +56,7 @@ public function compare ($version_string) $MORE = 1; $EQUAL = 0; - $test = new CssCrush_Version($version_string); + $test = new Version($version_string); foreach (array('major', 'minor', 'revision') as $level) { diff --git a/lib/functions.php b/lib/functions.php index 8a1ee15..cc3540f 100644 --- a/lib/functions.php +++ b/lib/functions.php @@ -4,6 +4,8 @@ * High level API. * */ +use CssCrush\CssCrush; + function csscrush_file ($file, $options = null) { return CssCrush::file($file, $options); } diff --git a/misc/formatters.php b/misc/formatters.php index bc5c714..7000eb2 100644 --- a/misc/formatters.php +++ b/misc/formatters.php @@ -4,6 +4,8 @@ * Formatter callbacks. * */ +use CssCrush\CssCrush; + CssCrush::$config->formatters = array( 'single-line' => 'csscrush__fmtr_single', 'padded' => 'csscrush__fmtr_padded', diff --git a/plugins/canvas.php b/plugins/canvas.php index 883f77d..677b423 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -47,6 +47,9 @@ * canvas-filter: greyscale() colorize(45, 45, 0); * } */ +namespace CssCrush\Plugins\Canvas; + +use CssCrush\Plugin, CssCrush\Hook, CssCrush\Function, CssCrush\Regex; CssCrush_Plugin::register('canvas', array( 'enable' => 'csscrush__enable_canvas', @@ -55,14 +58,14 @@ function csscrush__enable_canvas () { CssCrush_Hook::add('capture_phase2', 'csscrush__canvas_capture'); - CssCrush_Function::register('canvas', 'csscrush__canvas_generator'); - CssCrush_Function::register('canvas-data', 'csscrush__canvas_generator'); + CssCrush\Functions::register('canvas', 'csscrush__canvas_generator'); + CssCrush\Functions::register('canvas-data', 'csscrush__canvas_generator'); } function csscrush__disable_canvas () { CssCrush_Hook::remove('capture_phase2', 'csscrush__canvas_capture'); - CssCrush_Function::deRegister('canvas'); - CssCrush_Function::deRegister('canvas-data'); + CssCrush\Functions::deRegister('canvas'); + CssCrush\Functions::deRegister('canvas-data'); } function csscrush__canvas_capture ($process) { @@ -104,7 +107,7 @@ function csscrush__canvas_generator ($input, $context) { } // Parse args, bail if none. - $args = CssCrush_Function::parseArgs($input); + $args = CssCrush\Functions::parseArgs($input); if (! isset($args[0])) { return ''; } @@ -257,7 +260,7 @@ function csscrush__canvas_generator ($input, $context) { function csscrush__canvas_fn_linear_gradient ($input, $context) { - $args = CssCrush_Function::parseArgs($input) + array( + $args = CssCrush\Functions::parseArgs($input) + array( 'white', 'black', ); @@ -302,7 +305,7 @@ function csscrush__canvas_fn_linear_gradient ($input, $context) { function csscrush__canvas_fn_filter ($input, $context) { - $args = CssCrush_Function::parseArgs($input); + $args = CssCrush\Functions::parseArgs($input); array_unshift($context->canvas->filters, array($context->function, $args)); } @@ -403,7 +406,7 @@ function csscrush__canvas_apply_css_funcs ($canvas) { ); $generic_functions = array_diff_key( - CssCrush_Function::$functions, $map['fill']['functions']); + CssCrush\Functions::$functions, $map['fill']['functions']); $map['generic'] = array( 'patt' => CssCrush_Regex::createFunctionPatt( array_keys($generic_functions), array('bare_paren' => true)), @@ -421,19 +424,19 @@ function csscrush__canvas_apply_css_funcs ($canvas) { } // Generic functions. - CssCrush_Function::executeOnString( + CssCrush\Functions::executeOnString( $value, $map['generic']['patt'], $map['generic']['functions']); // Fill functions. if (in_array($property, array('fill', 'background-fill'))) { $context->currentProperty = $property; $context->canvas = $canvas; - CssCrush_Function::executeOnString( + CssCrush\Functions::executeOnString( $value, $map['fill']['patt'], $map['fill']['functions'], $context); } elseif ($property === 'canvas-filter') { $context->canvas = $canvas; - CssCrush_Function::executeOnString( + CssCrush\Functions::executeOnString( $value, $map['filter']['patt'], $map['filter']['functions'], $context); } } diff --git a/plugins/hocus-pocus.php b/plugins/hocus-pocus.php index 9188eac..5722665 100644 --- a/plugins/hocus-pocus.php +++ b/plugins/hocus-pocus.php @@ -11,18 +11,15 @@ * a:hover, a:focus, a:active { color: red; } * */ +namespace CssCrush; -CssCrush_Plugin::register('hocus-pocus', array( - 'enable' => 'csscrush__enable_hocus_pocus', - 'disable' => 'csscrush__disable_hocus_pocus', +Plugin::register('hocus-pocus', array( + 'enable' => function () { + CssCrush::addSelectorAlias('hocus', ':any(:hover,:focus)'); + CssCrush::addSelectorAlias('pocus', ':any(:hover,:focus,:active)'); + }, + 'disable' => function () { + CssCrush::removeSelectorAlias('hocus'); + CssCrush::removeSelectorAlias('pocus'); + }, )); - -function csscrush__enable_hocus_pocus () { - CssCrush::addSelectorAlias('hocus', ':any(:hover,:focus)'); - CssCrush::addSelectorAlias('pocus', ':any(:hover,:focus,:active)'); -} - -function csscrush__disable_hocus_pocus () { - CssCrush::removeSelectorAlias('hocus'); - CssCrush::removeSelectorAlias('pocus'); -} diff --git a/plugins/initial.php b/plugins/initial.php index 5ea03aa..890157e 100644 --- a/plugins/initial.php +++ b/plugins/initial.php @@ -15,21 +15,18 @@ * max-height: auto; * */ +namespace CssCrush; -CssCrush_Plugin::register('initial', array( - 'enable' => 'csscrush__enable_initial', - 'disable' => 'csscrush__disable_initial', +Plugin::register('initial', array( + 'enable' => function () { + Hook::add('rule_prealias', 'CssCrush\initial'); + }, + 'disable' => function () { + Hook::remove('rule_prealias', 'CssCrush\initial'); + }, )); -function csscrush__enable_initial () { - CssCrush_Hook::add('rule_prealias', 'csscrush__initial'); -} - -function csscrush__disable_initial () { - CssCrush_Hook::remove('rule_prealias', 'csscrush__initial'); -} - -function csscrush__initial (CssCrush_Rule $rule) { +function initial (CssCrush_Rule $rule) { static $initial_values; if (! $initial_values) { diff --git a/plugins/noise.php b/plugins/noise.php index 2c9674d..156a1bd 100644 --- a/plugins/noise.php +++ b/plugins/noise.php @@ -72,43 +72,38 @@ * // Sand effect. * background: turbulence( wheat 400x400, .35:.2 4 sharpen, normal, saturate .4 ); */ - -CssCrush_Plugin::register('noise', array( - 'enable' => 'csscrush__enable_noise', - 'disable' => 'csscrush__disable_noise', +namespace CssCrush; + +Plugin::register('noise', array( + + 'enable' => function () { + Functions::register('noise', function ($input) { + return noise_generator($input, array( + 'type' => 'fractalNoise', + 'frequency' => .7, + 'sharpen' => 'sharpen', + 'dimensions' => array(150, 150), + )); + }); + Functions::register('turbulence', function ($m) { + return noise_generator($input, array( + 'type' => 'turbulence', + 'frequency' => .01, + 'sharpen' => 'normal', + 'dimensions' => array(200, 200), + )); + }); + }, + + 'disable' => function () { + Functions::deRegister('noise'); + Functions::deRegister('turbulence'); + }, )); -function csscrush__enable_noise () { - CssCrush_Function::register('noise', 'csscrush_fn__noise'); - CssCrush_Function::register('turbulence', 'csscrush_fn__turbulence'); -} - -function csscrush__disable_noise () { - CssCrush_Function::deRegister('noise'); - CssCrush_Function::deRegister('turbulence'); -} - -function csscrush_fn__noise ($input) { - return csscrush__noise_generator($input, array( - 'type' => 'fractalNoise', - 'frequency' => .7, - 'sharpen' => 'sharpen', - 'dimensions' => array(150, 150), - )); -} - -function csscrush_fn__turbulence ($input) { - return csscrush__noise_generator($input, array( - 'type' => 'turbulence', - 'frequency' => .01, - 'sharpen' => 'normal', - 'dimensions' => array(200, 200), - )); -} - -function csscrush__noise_generator ($input, $defaults) { +function noise_generator ($input, $defaults) { - $args = array_pad(CssCrush_Function::parseArgs($input), 4, 'default'); + $args = array_pad(Functions::parseArgs($input), 4, 'default'); $type = $defaults['type']; @@ -117,7 +112,7 @@ function csscrush__noise_generator ($input, $defaults) { $dimensions = $defaults['dimensions']; if (($arg = array_shift($args)) !== 'default') { // May be a color function so explode(' ', $value) is not sufficient. - foreach (CssCrush_Function::parseArgs($arg, true) as $part) { + foreach (Functions::parseArgs($arg, true) as $part) { if (preg_match('~^(\d+)x(\d+)$~i', $part, $m)) { $dimensions = array_slice($m, 1); } @@ -224,7 +219,7 @@ function csscrush__noise_generator ($input, $defaults) { $svg .= ''; // Create data-uri url and return token label. - $url = new CssCrush_Url('data:image/svg+xml;base64,' . base64_encode($svg)); + $url = new Url('data:image/svg+xml;base64,' . base64_encode($svg)); return $url->label; } diff --git a/plugins/property-sorter.php b/plugins/property-sorter.php index 088c826..6b6a9ea 100644 --- a/plugins/property-sorter.php +++ b/plugins/property-sorter.php @@ -22,21 +22,18 @@ * background: #000; * */ - -CssCrush_Plugin::register('property-sorter', array( - 'enable' => 'csscrush__enable_property_sorter', - 'disable' => 'csscrush__disable_property_sorter', +namespace CssCrush; + +Plugin::register('property-sorter', array( + 'enable' => function () { + Hook::add('rule_prealias', 'property_sorter'); + }, + 'disable' => function () { + Hook::remove('rule_prealias', 'property_sorter'); + }, )); -function csscrush__enable_property_sorter () { - CssCrush_Hook::add('rule_prealias', 'csscrush__property_sorter'); -} - -function csscrush__disable_property_sorter () { - CssCrush_Hook::remove('rule_prealias', 'csscrush__property_sorter'); -} - -function csscrush__property_sorter (CssCrush_Rule $rule) { +function property_sorter (Rule $rule) { $new_set = array(); @@ -45,7 +42,7 @@ function csscrush__property_sorter (CssCrush_Rule $rule) { $new_set[] = $declaration; } - usort($new_set, '_csscrush__property_sorter_callback'); + usort($new_set, 'property_sorter_callback'); $rule->setDeclarations($new_set); } @@ -54,9 +51,9 @@ function csscrush__property_sorter (CssCrush_Rule $rule) { /* Callback for sorting. */ -function _csscrush__property_sorter_callback ($a, $b) { +function property_sorter_callback ($a, $b) { - $map =& _csscrush__property_sorter_get_table(); + $map =& property_sorter_get_table(); $a_prop =& $a->canonicalProperty; $b_prop =& $b->canonicalProperty; $a_listed = isset($map[$a_prop]); @@ -134,7 +131,7 @@ function _csscrush__property_sorter_callback ($a, $b) { /* Cache for the table of values to compare against. */ -function &_csscrush__property_sorter_get_table () { +function &property_sorter_get_table () { // Check for cached table. if (isset($GLOBALS['CSSCRUSH_PROPERTY_SORT_ORDER_CACHE'])) { @@ -178,7 +175,7 @@ function &_csscrush__property_sorter_get_table () { Get the current sorting table. */ function csscrush_get_property_sort_order () { - _csscrush__property_sorter_get_table(); + property_sorter_get_table(); return $GLOBALS['CSSCRUSH_PROPERTY_SORT_ORDER']; } diff --git a/plugins/px2em.php b/plugins/px2em.php index 8c88d71..26d69c7 100644 --- a/plugins/px2em.php +++ b/plugins/px2em.php @@ -13,23 +13,21 @@ * font-size: .84615em; * font-size: 1rem; */ - -CssCrush_Plugin::register( 'px2em', array( - 'enable' => 'csscrush__enable_px2em', - 'disable' => 'csscrush__disable_px2em', +namespace CssCrush; + +Plugin::register('px2em', array( + 'enable' => function () { + Functions::register('px2em', 'CssCrush\fn__px2em'); + Functions::register('px2rem', 'CssCrush\fn__px2rem'); + }, + 'disable' => function () { + Functions::deRegister('px2em'); + Functions::deRegister('px2rem'); + }, )); -function csscrush__enable_px2em () { - CssCrush_Function::register('px2em', 'csscrush_fn__px2em'); - CssCrush_Function::register('px2rem', 'csscrush_fn__px2rem'); -} - -function csscrush__disable_px2em () { - CssCrush_Function::deRegister('px2em'); - CssCrush_Function::deRegister('px2rem'); -} -function csscrush_fn__px2em ($input) { +function fn__px2em ($input) { $base = 16; @@ -38,10 +36,10 @@ function csscrush_fn__px2em ($input) { $base = CssCrush::$process->vars['px2em__base']; } - return csscrush__px2em($input, 'em', $base); + return px2em($input, 'em', $base); } -function csscrush_fn__px2rem ($input) { +function fn__px2rem ($input) { $base = 16; @@ -50,12 +48,12 @@ function csscrush_fn__px2rem ($input) { $base = CssCrush::$process->vars['px2rem__base']; } - return csscrush__px2em($input, 'rem', $base); + return px2em($input, 'rem', $base); } -function csscrush__px2em ($input, $unit, $default_base) { +function px2em ($input, $unit, $default_base) { - list($px, $base) = CssCrush_Function::parseArgsSimple($input) + array( + list($px, $base) = Functions::parseArgsSimple($input) + array( 16, $default_base, ); diff --git a/plugins/rem.php b/plugins/rem.php index bfa13d1..cb81d9b 100644 --- a/plugins/rem.php +++ b/plugins/rem.php @@ -33,26 +33,24 @@ * To convert all values, not just values of the font related properties, * set a variable named `rem__all` with a value of `yes`. */ - -CssCrush_Plugin::register('rem', array( - 'enable' => 'csscrush__enable_rem', - 'disable' => 'csscrush__disable_rem', +namespace CssCrush; + +Plugin::register('rem', array( + 'enable' => function () { + Hook::add('rule_prealias', 'CssCrush\rem'); + }, + 'disable' => function () { + Hook::remove('rule_prealias', 'CssCrush\rem'); + }, )); -function csscrush__enable_rem () { - CssCrush_Hook::add('rule_prealias', 'csscrush__rem'); -} - -function csscrush__disable_rem () { - CssCrush_Hook::remove('rule_prealias', 'csscrush__rem'); -} -function csscrush__rem (CssCrush_Rule $rule) { +function rem (Rule $rule) { static $rem_patt, $px_patt, $font_props, $modes; if (! $modes) { - $rem_patt = CssCrush_Regex::create('{{LB}}({{number}})rem{{RB}}', 'iS'); - $px_patt = CssCrush_Regex::create('{{LB}}({{number}})px{{RB}}', 'iS'); + $rem_patt = Regex::create('{{LB}}({{number}})rem{{RB}}', 'iS'); + $px_patt = Regex::create('{{LB}}({{number}})px{{RB}}', 'iS'); $font_props = array( 'font' => true, 'font-size' => true, diff --git a/plugins/spiffing.php b/plugins/spiffing.php index a8b9106..2d72cd1 100644 --- a/plugins/spiffing.php +++ b/plugins/spiffing.php @@ -13,21 +13,18 @@ * opacity: 0.5; * */ +namespace CssCrush; -CssCrush_Plugin::register('spiffing', array( - 'enable' => 'csscrush__enable_spiffing', - 'disable' => 'csscrush__disable_spiffing', +Plugin::register('spiffing', array( + 'enable' => function () { + Hook::add('rule_preprocess', 'spiffing'); + }, + 'disable' => function () { + CssCrush_Hook::remove('rule_preprocess'); + }, )); -function csscrush__enable_spiffing () { - CssCrush_Hook::add('rule_preprocess', 'csscrush__spiffing'); -} - -function csscrush__disable_spiffing () { - CssCrush_Hook::remove('rule_preprocess'); -} - -function csscrush__spiffing ($rule) { +function spiffing ($rule) { $find = array('colour', 'grey', '!please', 'transparency', 'centre', 'plump', 'photograph', 'capitalise'); $replace = array('color', 'gray', '!important', 'opacity', 'center', 'bold', 'image', 'capitalize'); diff --git a/plugins/svg-gradients.php b/plugins/svg-gradients.php index 681de0b..3cc1fd8 100644 --- a/plugins/svg-gradients.php +++ b/plugins/svg-gradients.php @@ -46,13 +46,13 @@ )); function csscrush__enable_svg_gradients () { - CssCrush_Function::register('svg-linear-gradient', 'csscrush_fn__svg_linear_gradient'); - CssCrush_Function::register('svg-radial-gradient', 'csscrush_fn__svg_radial_gradient'); + CssCrush\Functions::register('svg-linear-gradient', 'csscrush_fn__svg_linear_gradient'); + CssCrush\Functions::register('svg-radial-gradient', 'csscrush_fn__svg_radial_gradient'); } function csscrush__disable_svg_gradients () { - CssCrush_Function::deRegister('svg-linear-gradient'); - CssCrush_Function::deRegister('svg-radial-gradient'); + CssCrush\Functions::deRegister('svg-linear-gradient'); + CssCrush\Functions::deRegister('svg-radial-gradient'); } function csscrush_fn__svg_linear_gradient ($input) { @@ -120,7 +120,7 @@ function csscrush__create_svg_linear_gradient ($input) { $deg_patt = CssCrush_Regex::create('^{{number}}deg$', 'i'); } - $args = CssCrush_Function::parseArgs($input); + $args = CssCrush\Functions::parseArgs($input); // If no angle argument is passed the default. $angle = 0; @@ -251,7 +251,7 @@ function csscrush__create_svg_radial_gradient ($input) { $origin_patt = CssCrush_Regex::create('^({{number}}%?) +({{number}}%?)$'); } - $args = CssCrush_Function::parseArgs($input); + $args = CssCrush\Functions::parseArgs($input); // Default origin, $position = $position_keywords['at center']; diff --git a/plugins/svg.php b/plugins/svg.php index 71c2687..156009e 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -65,14 +65,14 @@ function csscrush__enable_svg () { CssCrush_Hook::add('capture_phase2', 'csscrush__svg_capture'); - CssCrush_Function::register('svg', 'csscrush_fn__svg'); - CssCrush_Function::register('svg-data', 'csscrush_fn__svg_data'); + CssCrush\Functions::register('svg', 'csscrush_fn__svg'); + CssCrush\Functions::register('svg-data', 'csscrush_fn__svg_data'); } function csscrush__disable_svg () { CssCrush_Hook::remove('capture_phase2', 'csscrush__svg_capture'); - CssCrush_Function::deRegister('svg'); - CssCrush_Function::deRegister('svg-data'); + CssCrush\Functions::deRegister('svg'); + CssCrush\Functions::deRegister('svg-data'); } function csscrush_fn__svg ($input) { @@ -183,7 +183,7 @@ function csscrush__svg_generator ($input, $fn_name) { ); // Bail if no args. - $args = CssCrush_Function::parseArgs($input); + $args = CssCrush\Functions::parseArgs($input); if (! isset($args[0])) { return ''; @@ -674,7 +674,7 @@ function csscrush__svg_apply_css_funcs ($element, &$raw_data) { 'pattern' => 'csscrush__svg_fn_pattern', ); $generic_functions = - array_diff_key(CssCrush_Function::$functions, $fill_functions); + array_diff_key(CssCrush\Functions::$functions, $fill_functions); $generic_functions_patt = CssCrush_Regex::createFunctionPatt( array_keys($generic_functions), array('bare_paren' => true)); $fill_functions_patt = CssCrush_Regex::createFunctionPatt( @@ -682,11 +682,11 @@ function csscrush__svg_apply_css_funcs ($element, &$raw_data) { } foreach ($raw_data as $property => &$value) { - CssCrush_Function::executeOnString($value, $generic_functions_patt); + CssCrush\Functions::executeOnString($value, $generic_functions_patt); // Only capturing fills for fill and stoke properties. if ($property === 'fill' || $property === 'stroke') { - CssCrush_Function::executeOnString( + CssCrush\Functions::executeOnString( $value, $fill_functions_patt, $fill_functions, $element); // If the value is a color with alpha component we split the color @@ -798,7 +798,7 @@ function csscrush__svg_fn_pattern ($input, $element) { // Get args in order with defaults. list($url, $transform_list, $width, $height, $x, $y) = - CssCrush_Function::parseArgs($input) + + CssCrush\Functions::parseArgs($input) + array('', '', 0, 0, 0, 0); $url = CssCrush::$process->tokens->get($url); From 15266498541aa05416cc88958e4f231f5c5b5b68 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 10 Aug 2013 19:53:28 +0100 Subject: [PATCH 150/421] Main changes to take advantage of php 5.3 --- Aliases.ini | 6 +- CssCrush.php | 2 +- README.md | 4 +- cli.php | 6 +- composer.json | 2 +- lib/CssCrush/BalancedMatch.php | 5 +- lib/CssCrush/Declaration.php | 2 +- lib/CssCrush/Functions.php | 45 ++-- lib/CssCrush/{IOWatch.php => IO/Watch.php} | 7 +- lib/CssCrush/Importer.php | 59 +++-- lib/CssCrush/PostAliasFix.php | 16 +- lib/CssCrush/Process.php | 170 +++++++------- lib/CssCrush/Regex.php | 4 - lib/CssCrush/Rule.php | 4 +- lib/CssCrush/Stream.php | 17 +- lib/CssCrush/Tokens.php | 15 +- lib/CssCrush/Util.php | 2 +- misc/formatters.php | 14 +- plugins/canvas.php | 183 ++++++++-------- plugins/ease.php | 22 +- plugins/hsl-to-hex.php | 24 +- plugins/ie-clip.php | 22 +- plugins/ie-filter.php | 32 ++- plugins/ie-inline-block.php | 24 +- plugins/ie-min-height.php | 22 +- plugins/ie-opacity.php | 26 +-- plugins/initial.php | 2 +- plugins/legacy-flexbox.php | 71 +++--- plugins/noise.php | 5 +- plugins/property-sorter.php | 243 +++++++++++---------- plugins/rgba-fallback.php | 24 +- plugins/spiffing.php | 5 +- plugins/svg-gradients.php | 56 +++-- plugins/svg.php | 191 ++++++++-------- 34 files changed, 640 insertions(+), 692 deletions(-) rename lib/CssCrush/{IOWatch.php => IO/Watch.php} (91%) diff --git a/Aliases.ini b/Aliases.ini index a9a3edf..8fbcb37 100644 --- a/Aliases.ini +++ b/Aliases.ini @@ -256,7 +256,7 @@ display:box[] = display:-webkit-box display:box[] = display:-moz-box - ; Experimental cursor values. + ; Cursor values (non-standard). cursor:zoom-in[] = cursor:-webkit-zoom-in cursor:zoom-in[] = cursor:-moz-zoom-in cursor:zoom-in[] = cursor:-ms-zoom-in @@ -297,6 +297,10 @@ min-width:fit-content[] = min-width:-webkit-fit-content min-width:fit-content[] = min-width:-moz-fit-content + ; Appearance (non-standard). + appearance:none[] = appearance:-webkit-appearance + appearance:none[] = appearance:-moz-appearance + ;---------------------------------------------------------------- ; Function aliases. diff --git a/CssCrush.php b/CssCrush.php index b3767d8..dd00bec 100644 --- a/CssCrush.php +++ b/CssCrush.php @@ -15,7 +15,7 @@ function csscrush_autoload ($class) { $class = str_ireplace('csscrush', 'CssCrush', $class); $subpath = implode('/', array_map('ucfirst', explode('\\', $class))); - require_once dirname(__FILE__) . "/lib/$subpath.php"; + require_once __DIR__ . "/lib/$subpath.php"; } spl_autoload_register('csscrush_autoload'); diff --git a/README.md b/README.md index 9724ff9..197245f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ Logo -CSS without the mess — CSS-Crush is a CSS preprocessor designed to create a modern, uncluttered and standards based CSS authoring environment. +CSS without the Mess — CSS-Crush is a CSS preprocessor designed to create a modern, uncluttered and standards based CSS authoring environment. See the following overview for code examples and description of main features: http://the-echoplex.net/csscrush @@ -30,4 +30,4 @@ If you think you've found a bug, please visit the Issue tracker — https://gith Submitting patches ------------------ -To contribute code and bug fixes fork this project on Github, make changes to the code in your fork, and then send a "pull request" for review. +To contribute code and bug fixes fork this project on Github, make changes to the code in your fork, and then send a pull request. diff --git a/cli.php b/cli.php index b576b54..eeedfca 100755 --- a/cli.php +++ b/cli.php @@ -5,8 +5,6 @@ * Command line utility. * */ -namespace CssCrush; - require_once 'CssCrush.php'; ################################################################## @@ -145,7 +143,7 @@ $plugins = array(); - foreach (Plugin::info() as $name => $docs) { + foreach (CssCrush\Plugin::info() as $name => $docs) { // Use first line of plugin doc for description. $headline = isset($docs[0]) ? $docs[0] : false; $plugins[] = colorize("$name" . ($headline ? " - $headline" : '')); @@ -303,7 +301,7 @@ if ($args->watch) { // Override the IO class. - csscrush_set('config', array('io' => 'CssCrush\IOWatch')); + csscrush_set('config', array('io' => 'CssCrush\IO\Watch')); stdout('CONTROL-C to quit.'); diff --git a/composer.json b/composer.json index a83e599..d4c0b64 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=5.3.4" + "php": ">=5.3.1" }, "bin": [ "misc/csscrush" diff --git a/lib/CssCrush/BalancedMatch.php b/lib/CssCrush/BalancedMatch.php index a2a0722..7fd0d92 100644 --- a/lib/CssCrush/BalancedMatch.php +++ b/lib/CssCrush/BalancedMatch.php @@ -27,8 +27,7 @@ public function __construct (Stream $stream, $offset, $brackets = '{}') return; } - $patt = $opener === '{' ? - Regex::$patt->balancedCurlies : Regex::$patt->balancedParens; + $patt = ($opener === '{') ? Regex::$patt->block : Regex::$patt->parens; if (preg_match($patt, $stream->raw, $m, PREG_OFFSET_CAPTURE, $this->offset)) { @@ -45,7 +44,7 @@ public function __construct (Stream $stream, $offset, $brackets = '{}') public function inside () { - return $this->match[1][0]; + return $this->match[2][0]; } public function whole () diff --git a/lib/CssCrush/Declaration.php b/lib/CssCrush/Declaration.php index 00b2db1..6380a44 100644 --- a/lib/CssCrush/Declaration.php +++ b/lib/CssCrush/Declaration.php @@ -102,7 +102,7 @@ public function process ($parent_rule) $this->value, Regex::$patt->thisFunction, array( - 'this' => 'csscrush_fn__this', + 'this' => 'CssCrush\fn__this', ), $context); diff --git a/lib/CssCrush/Functions.php b/lib/CssCrush/Functions.php index f4fcae4..03092a0 100644 --- a/lib/CssCrush/Functions.php +++ b/lib/CssCrush/Functions.php @@ -18,18 +18,18 @@ class Functions static protected $builtinFunctions = array( // These functions must come first in this order. - 'query' => 'csscrush_fn__query', + 'query' => 'CssCrush\fn__query', // These functions can be any order. - 'math' => 'csscrush_fn__math', - 'percent' => 'csscrush_fn__percent', - 'pc' => 'csscrush_fn__percent', - 'hsla-adjust' => 'csscrush_fn__hsla_adjust', - 'hsl-adjust' => 'csscrush_fn__hsl_adjust', - 'h-adjust' => 'csscrush_fn__h_adjust', - 's-adjust' => 'csscrush_fn__s_adjust', - 'l-adjust' => 'csscrush_fn__l_adjust', - 'a-adjust' => 'csscrush_fn__a_adjust', + 'math' => 'CssCrush\fn__math', + 'percent' => 'CssCrush\fn__percent', + 'pc' => 'CssCrush\fn__percent', + 'hsla-adjust' => 'CssCrush\fn__hsla_adjust', + 'hsl-adjust' => 'CssCrush\fn__hsl_adjust', + 'h-adjust' => 'CssCrush\fn__h_adjust', + 's-adjust' => 'CssCrush\fn__s_adjust', + 'l-adjust' => 'CssCrush\fn__l_adjust', + 'a-adjust' => 'CssCrush\fn__a_adjust', ); static public function setMatchPatt () @@ -71,8 +71,7 @@ static public function executeOnString (&$str, $patt = null, $process_callback = $offset = $match[0][1]; - if (! preg_match(Regex::$patt->balancedParens, - $str, $parens, PREG_OFFSET_CAPTURE, $offset)) { + if (! preg_match(Regex::$patt->parens, $str, $parens, PREG_OFFSET_CAPTURE, $offset)) { continue; } @@ -88,7 +87,7 @@ static public function executeOnString (&$str, $patt = null, $process_callback = $closing_paren = $opening_paren + strlen($parens[0][0]); // Get the function arguments. - $raw_args = trim($parens[1][0]); + $raw_args = trim($parens['parens_content'][0]); // Workaround the signs. $before_operator = '-' === $raw_fn_name ? '-' : ''; @@ -144,7 +143,7 @@ static public function parseArgsSimple ($input) ############################# # Stock CSS functions. -function csscrush_fn__math ($input) { +function fn__math ($input) { // Swap in math constants. $input = preg_replace( @@ -160,7 +159,7 @@ function csscrush_fn__math ($input) { return $result === false ? 0 : round($result, 5); } -function csscrush_fn__percent ($input) { +function fn__percent ($input) { // Strip non-numeric and non delimiter characters $input = preg_replace('~[^\d\.\s,]~S', '', $input); @@ -202,37 +201,37 @@ function csscrush_fn__percent ($input) { return $result . '%'; } -function csscrush_fn__hsla_adjust ($input) { +function fn__hsla_adjust ($input) { list($color, $h, $s, $l, $a) = array_pad(Functions::parseArgs($input, true), 5, 0); return Color::colorAdjust($color, array($h, $s, $l, $a)); } -function csscrush_fn__hsl_adjust ($input) { +function fn__hsl_adjust ($input) { list($color, $h, $s, $l) = array_pad(Functions::parseArgs($input, true), 4, 0); return Color::colorAdjust($color, array($h, $s, $l, 0)); } -function csscrush_fn__h_adjust ($input) { +function fn__h_adjust ($input) { list($color, $h) = array_pad(Functions::parseArgs($input, true), 2, 0); return Color::colorAdjust($color, array($h, 0, 0, 0)); } -function csscrush_fn__s_adjust ($input) { +function fn__s_adjust ($input) { list($color, $s) = array_pad(Functions::parseArgs($input, true), 2, 0); return Color::colorAdjust($color, array(0, $s, 0, 0)); } -function csscrush_fn__l_adjust ($input) { +function fn__l_adjust ($input) { list($color, $l) = array_pad(Functions::parseArgs($input, true), 2, 0); return Color::colorAdjust($color, array(0, 0, $l, 0)); } -function csscrush_fn__a_adjust ($input) { +function fn__a_adjust ($input) { list($color, $a) = array_pad(Functions::parseArgs($input, true), 2, 0); return Color::colorAdjust($color, array(0, 0, 0, $a)); } -function csscrush_fn__this ($input, $context) { +function fn__this ($input, $context) { $args = Functions::parseArgsSimple($input); $property = $args[0]; @@ -259,7 +258,7 @@ function csscrush_fn__this ($input, $context) { return ''; } -function csscrush_fn__query ($input, $context) { +function fn__query ($input, $context) { $args = Functions::parseArgs($input); diff --git a/lib/CssCrush/IOWatch.php b/lib/CssCrush/IO/Watch.php similarity index 91% rename from lib/CssCrush/IOWatch.php rename to lib/CssCrush/IO/Watch.php index ef230e8..2ae0711 100644 --- a/lib/CssCrush/IOWatch.php +++ b/lib/CssCrush/IO/Watch.php @@ -4,9 +4,12 @@ * IO class for command line file watching. * */ -namespace CssCrush; +namespace CssCrush\IO; -class IOWatch extends IO +use CssCrush\CssCrush; +use CssCrush\IO; + +class Watch extends IO { public static $cacheData = array(); diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index 6bb3d90..a78e9b9 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -269,7 +269,7 @@ static protected function addMarkers (&$str) $point_data[] = strlen($before) - strrpos($before, "\n") - 1; } - // Splice in tracing stub. + // Splice in marker token. $str = substr_replace( $str, $process->tokens->add($point_data, 't'), @@ -280,42 +280,41 @@ static protected function addMarkers (&$str) static protected function captureCommentAndString ($str) { - return preg_replace_callback(Regex::$patt->commentAndString, - array('self', 'cb_captureCommentAndString'), $str); - } + $callback = function ($m) { - static protected function cb_captureCommentAndString ($match) - { - $full_match = $match[0]; - $process = CssCrush::$process; + $full_match = $m[0]; + $process = CssCrush::$process; - if (strpos($full_match, '/*') === 0) { + if (strpos($full_match, '/*') === 0) { - // Bail without storing comment if output is minified or a private comment. - if ( - $process->minifyOutput || - strpos($full_match, '/*$') === 0 - ) { - return Tokens::pad('', $full_match); - } + // Bail without storing comment if output is minified or a private comment. + if ( + $process->minifyOutput || + strpos($full_match, '/*$') === 0 + ) { + return Tokens::pad('', $full_match); + } - // Fix broken comments as they will break any subsquent - // imported files that are inlined. - if (! preg_match('~\*/$~', $full_match)) { - $full_match .= '*/'; + // Fix broken comments as they will break any subsquent + // imported files that are inlined. + if (! preg_match('~\*/$~', $full_match)) { + $full_match .= '*/'; + } + $label = $process->tokens->add($full_match, 'c'); } - $label = $process->tokens->add($full_match, 'c'); - } - else { + else { - // Fix broken strings as they will break any subsquent - // imported files that are inlined. - if ($full_match[0] !== $full_match[strlen($full_match)-1]) { - $full_match .= $full_match[0]; + // Fix broken strings as they will break any subsquent + // imported files that are inlined. + if ($full_match[0] !== $full_match[strlen($full_match)-1]) { + $full_match .= $full_match[0]; + } + $label = $process->tokens->add($full_match, 's'); } - $label = $process->tokens->add($full_match, 's'); - } - return Tokens::pad($label, $full_match); + return Tokens::pad($label, $full_match); + }; + + return preg_replace_callback(Regex::$patt->commentAndString, $callback, $str); } } diff --git a/lib/CssCrush/PostAliasFix.php b/lib/CssCrush/PostAliasFix.php index c835579..e997a7f 100644 --- a/lib/CssCrush/PostAliasFix.php +++ b/lib/CssCrush/PostAliasFix.php @@ -65,22 +65,24 @@ function postalias_fix_linear_gradients ($declaration_copies) { $angles_old = array_values($angles); } - static $deg_patt, $deg_convert_callback, $fn_patt; + static $deg_patt, $fn_patt; if (! $deg_patt) { $deg_patt = Regex::create('(?<=[\( ])({{number}})deg', 'i'); - // Legacy angles move anti-clockwise and start from East, not North. - $deg_convert_callback = create_function('$m', ' - $angle = floatval($m[1]); - $angle = ($angle + 90) - ($angle * 2); - return ($angle < 0 ? $angle + 360 : $angle) . \'deg\'; - '); $fn_patt = Regex::create('{{LB}}{{vendor}}(?:(?:repeating-)?linear-gradient)({{p-token}})', 'iS'); } + // Legacy angles move anti-clockwise and start from East, not North. + $deg_convert_callback = function ($m) { + $angle = floatval($m[1]); + $angle = ($angle + 90) - ($angle * 2); + return ($angle < 0 ? $angle + 360 : $angle) . 'deg'; + }; + // Create new paren tokens based on the first prefixed declaration. // Replace the new syntax with the legacy syntax. $original_parens = array(); $replacement_parens = array(); + foreach (Regex::matchAll($fn_patt, $declaration_copies[0]->value) as $m) { $original_parens[] = $m[1][0]; diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 728d9af..3554e12 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -201,17 +201,17 @@ protected function getBoilerplate () protected function resolveSelectorAliases () { - static $alias_patt, $callback; + static $alias_patt; if (! $alias_patt) { $alias_patt = Regex::create('@selector-alias +\:({{ident}}) +([^;]+) *;', 'iS'); - $callback = create_function('$m', ' - $name = strtolower($m[1]); - $body = Util::stripCommentTokens($m[2]); - $template = new Template($body); - CssCrush::$process->selectorAliases[$name] = $template; - '); } - $this->stream->pregReplaceCallback($alias_patt, $callback); + + $this->stream->pregReplaceCallback($alias_patt, function ($m) { + $name = strtolower($m[1]); + $body = Util::stripCommentTokens($m[2]); + $template = new Template($body); + CssCrush::$process->selectorAliases[$name] = $template; + }); // Merge with global selector aliases. $this->selectorAliases += CssCrush::$config->selectorAliases; @@ -256,11 +256,10 @@ static public function applySelectorAliases (&$str) if (isset($selector_alias_call[2])) { // Parse argument list. - if (! preg_match(Regex::$patt->balancedParens, $str, - $parens, PREG_OFFSET_CAPTURE, $start)) { + if (! preg_match(Regex::$patt->parens, $str, $parens, PREG_OFFSET_CAPTURE, $start)) { continue; } - $args = Functions::parseArgs($parens[1][0]); + $args = Functions::parseArgs($parens[2][0]); // Amend offsets. $paren_start = $parens[0][1]; @@ -416,13 +415,19 @@ protected function filterPlugins () ############################# # Variables. - protected function calculateVars () + protected function captureVars () { $config = CssCrush::$config; $regex = Regex::$patt; $option_vars = $this->options->vars; - $this->stream->pregReplaceCallback($regex->vars, '\CssCrush\Process::cb_captureVars'); + $this->stream->pregReplaceCallback($regex->vars, function ($m) { + CssCrush::$process->vars = + array_merge( + CssCrush::$process->vars, + Rule::parseBlock($m['block_content'], array('keyed' => true, 'ignore_directives' => true)) + ); + }); // In-file variables override global variables. $this->vars = array_merge($config->vars, $this->vars); @@ -432,16 +437,9 @@ protected function calculateVars () $this->vars = array_merge($this->vars, $option_vars); } - // Place variables referenced inside variables. Excecute custom functions. + // Place variables referenced inside variables. foreach ($this->vars as $name => &$value) { - - // Referenced variables. - $value = preg_replace_callback($regex->varFunction, '\CssCrush\Process::cb_placeVars', $value); - - // Variable values can be escaped from function parsing with a tilde prefix. - if (strpos($value, '~') !== 0) { - Functions::executeOnString($value); - } + $value = preg_replace_callback($regex->varFunction, 'CssCrush\Process::cb_placeVars', $value); } } @@ -472,28 +470,29 @@ static protected function placeVars (&$value) // Variables with no default value. $value = preg_replace_callback($regex->varFunction, - '\CssCrush\Process::cb_placeVars', $value, -1, $count); + 'CssCrush\Process::cb_placeVars', $value, -1, $vars_placed); if (strpos($value, '$(') !== false) { + // Assume at least one replace. + $vars_placed = 1; + // Variables with default value. - Functions::executeOnString($value, '~(\$)\(~', - array('$' => '\CssCrush\Process::cb_placeVarsWithDefault')); + $callback = function ($raw_arg) { + list($name, $default_value) = Functions::parseArgsSimple($raw_arg); + if (isset(CssCrush::$process->vars[$name])) { + return CssCrush::$process->vars[$name]; + } + else { + return $default_value; + } + }; - // Assume at least 1 replace. - $count = 1; + Functions::executeOnString($value, '~(\$)\(~', array('$' => $callback)); } // If we know replacements have been made we may want to update $value. e.g URL tokens. - return $count; - } - - static public function cb_captureVars ($m) - { - CssCrush::$process->vars = - array_merge( - CssCrush::$process->vars, - Rule::parseBlock($m['block_content'], array('keyed' => true, 'ignore_directives' => true))); + return $vars_placed; } static protected function cb_placeVars ($m) @@ -504,18 +503,6 @@ static protected function cb_placeVars ($m) } } - static public function cb_placeVarsWithDefault ($raw_arg) - { - list($name, $default_value) = Functions::parseArgsSimple($raw_arg); - - if (isset(CssCrush::$process->vars[$name])) { - return CssCrush::$process->vars[$name]; - } - else { - return $default_value; - } - } - ############################# # @ifdefine blocks. @@ -555,14 +542,9 @@ protected function resolveIfDefines () protected function captureMixins () { - static $callback; - if (! $callback) { - $callback = create_function('$m', ' - CssCrush::$process->mixins[$m[\'name\']] = new Mixin($m[\'block_content\']); - '); - } - - $this->stream->pregReplaceCallback(Regex::$patt->mixin, $callback); + $this->stream->pregReplaceCallback(Regex::$patt->mixin, function ($m) { + CssCrush::$process->mixins[$m['name']] = new Mixin($m['block_content']); + }); } @@ -571,29 +553,27 @@ protected function captureMixins () protected function resolveFragments () { - static $capture_callback, $invoke_callback; - if (! $capture_callback) { - - $capture_callback = create_function('$m', ' - CssCrush::$process->fragments[$m[\'name\']] = new CssCrush\Fragment( - $m[\'block_content\'], - array(\'name\' => strtolower($m[\'name\']))); - return \'\';'); - - $invoke_callback = create_function('$m', ' - $fragment = isset(CssCrush::$process->fragments[$m[\'name\']]) ? CssCrush::$process->fragments[$m[\'name\']] : null; - if ($fragment) { - $args = array(); - if (isset($m[\'parens\'])) { - $args = Functions::parseArgs($m[\'parens_content\']); - } - return $fragment->apply($args); - } - return \'\';'); - } + $fragments =& CssCrush::$process->fragments; + + $this->stream->pregReplaceCallback(Regex::$patt->fragmentCapture, function ($m) use (&$fragments) { + $fragments[$m['name']] = new Fragment( + $m['block_content'], + array('name' => strtolower($m['name'])) + ); + return ''; + }); - $this->stream->pregReplaceCallback(Regex::$patt->fragmentCapture, $capture_callback); - $this->stream->pregReplaceCallback(Regex::$patt->fragmentInvoke, $invoke_callback); + $this->stream->pregReplaceCallback(Regex::$patt->fragmentInvoke, function ($m) use (&$fragments) { + $fragment = isset($fragments[$m['name']]) ? $fragments[$m['name']] : null; + if ($fragment) { + $args = array(); + if (isset($m['parens'])) { + $args = Functions::parseArgs($m['parens_content']); + } + return $fragment->apply($args); + } + return ''; + }); } @@ -644,7 +624,6 @@ protected function processRules () if ($aliases['declarations']) { $rule->addDeclarationAliases(); } - Hook::run('rule_postalias', $rule); $rule->expandSelectors(); @@ -660,7 +639,7 @@ protected function processRules () ############################# # @in blocks. - protected function prefixSelectors () + protected function resolveInBlocks () { $matches = $this->stream->matchAll('~@in\s+([^{]+)\{~iS'); $tokens = CssCrush::$process->tokens; @@ -952,7 +931,7 @@ public function compile ($io_context = 'file') $this->stream = new Stream(Importer::hostfile($this->input)); // Extract and calculate variables. - $this->calculateVars(); + $this->captureVars(); $this->placeAllVars(); @@ -973,7 +952,7 @@ public function compile ($io_context = 'file') $this->captureRules(); // csscrush::log(array_keys($this->references)); - $this->prefixSelectors(); + $this->resolveInBlocks(); $this->aliasAtRules(); @@ -1108,27 +1087,24 @@ protected function decruft () protected function minifyColors () { - static $keywords_patt, $keywords_callback, $functions_patt, $functions_callback; + static $keywords_patt, $functions_patt; if (! $keywords_patt) { - $keywords =& Color::loadMinifyableKeywords(); - $keywords_patt = '~(?stream->pregReplaceCallback($keywords_patt, $keywords_callback); - $this->stream->pregReplaceCallback($functions_patt, $functions_callback); + $this->stream->pregReplaceCallback($keywords_patt, function ($m) { + return Color::$minifyableKeywords[strtolower($m[0])]; + }); + + $this->stream->pregReplaceCallback($functions_patt, function ($m) { + $args = Functions::parseArgs(trim($m[2])); + if (stripos($m[1], 'hsl') === 0) { + $args = Color::cssHslToRgb($args); + } + return Color::rgbToHex($args); + }); } } diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php index 9152cfa..299c506 100644 --- a/lib/CssCrush/Regex.php +++ b/lib/CssCrush/Regex.php @@ -111,10 +111,6 @@ static public function init () \s* {{block}}', 'xiS'); - // Balanced bracket matching. - $patt->balancedParens = '~\(\s* ( (?: (?>[^()]+) | (?R) )* ) \s*\)~xS'; - $patt->balancedCurlies = '~\{\s* ( (?: (?>[^{}]+) | (?R) )* ) \s*\}~xS'; - // Misc. $patt->vendorPrefix = '~^-([a-z]+)-([a-z-]+)~iS'; $patt->ruleDirective = '~^(?:(@include)|(@extends?)|(@name))[\s]+~iS'; diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php index 14d3b9e..ab04927 100644 --- a/lib/CssCrush/Rule.php +++ b/lib/CssCrush/Rule.php @@ -131,7 +131,7 @@ public function __toString () else { $formatter = $process->ruleFormatter ? - $process->ruleFormatter : 'csscrush__fmtr_block'; + $process->ruleFormatter : 'CssCrush\fmtr_block'; return "$stub{$formatter($this)}"; } @@ -701,7 +701,7 @@ public function addDeclarationAliases () public function getIterator () { - return new ArrayIterator($this->declarations); + return new \ArrayIterator($this->declarations); } diff --git a/lib/CssCrush/Stream.php b/lib/CssCrush/Stream.php index e6ee8a2..60f52ee 100644 --- a/lib/CssCrush/Stream.php +++ b/lib/CssCrush/Stream.php @@ -67,16 +67,17 @@ public function replaceHash ($replacements) public function replaceTokens ($type, $callback = null) { - static $types = array(); - if (! isset($types[$type])) { - $types[$type]['callback'] = create_function('$m', ' - $tokens =& CssCrush::$process->tokens->store->' . $type . '; - return isset($tokens[$m[0]]) ? $tokens[$m[0]] : \'\'; - '); - $types[$type]['patt'] = Regex::create("\? $type {{token-id}} \?", 'xS'); + static $patts = array(); + if (! isset($patts[$type])) { + $patts[$type] = Regex::create("\? $type {{token-id}} \?", 'xS'); } - $this->raw = preg_replace_callback($types[$type]['patt'], $callback ? $callback : $types[$type]['callback'], $this->raw); + $tokens =& CssCrush::$process->tokens->store->{ $type }; + $callback = $callback ?: function ($m) use (&$tokens) { + return isset($tokens[$m[0]]) ? $tokens[$m[0]] : ''; + }; + + $this->raw = preg_replace_callback($patts[$type], $callback, $this->raw); return $this; } diff --git a/lib/CssCrush/Tokens.php b/lib/CssCrush/Tokens.php index 9d146e4..361fc79 100644 --- a/lib/CssCrush/Tokens.php +++ b/lib/CssCrush/Tokens.php @@ -68,15 +68,10 @@ public function restore ($str, $type, $release = false) case 'u': // Currently this always releases URLs // may need to refactor later. - static $url_revert_callback; - if (! $url_revert_callback) { - $url_revert_callback = create_function('$m', ' - $url = CssCrush::$process->tokens->pop($m[0]); - return $url ? $url->getOriginalValue() : \'\'; - '); - } - - $str = preg_replace_callback(Regex::$patt->u_token, $url_revert_callback, $str); + $str = preg_replace_callback(Regex::$patt->u_token, function ($m) { + $url = CssCrush::$process->tokens->pop($m[0]); + return $url ? $url->getOriginalValue() : ''; + }, $str); break; default: $token_table =& $this->store->{$type}; @@ -114,7 +109,7 @@ public function capture ($str, $type) public function captureParens ($str) { - return preg_replace_callback(Regex::$patt->balancedParens, function ($m) { + return preg_replace_callback(Regex::$patt->parens, function ($m) { return CssCrush::$process->tokens->add($m[0], 'p'); }, $str); } diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php index f11ba85..6bd1896 100644 --- a/lib/CssCrush/Util.php +++ b/lib/CssCrush/Util.php @@ -93,7 +93,7 @@ static public function splitDelimList ($str, $delim = ',') return strlen($str) ? array($str) : array(); } - if ($match_count = preg_match_all(Regex::$patt->balancedParens, $str, $matches)) { + if ($match_count = preg_match_all(Regex::$patt->parens, $str, $matches)) { $keys = array(); foreach ($matches[0] as $index => &$value) { $keys[] = "?$index?"; diff --git a/misc/formatters.php b/misc/formatters.php index 7000eb2..3e87135 100644 --- a/misc/formatters.php +++ b/misc/formatters.php @@ -4,15 +4,15 @@ * Formatter callbacks. * */ -use CssCrush\CssCrush; +namespace CssCrush; CssCrush::$config->formatters = array( - 'single-line' => 'csscrush__fmtr_single', - 'padded' => 'csscrush__fmtr_padded', - 'block' => 'csscrush__fmtr_block', + 'single-line' => 'CssCrush\fmtr_single', + 'padded' => 'CssCrush\fmtr_padded', + 'block' => 'CssCrush\fmtr_block', ); -function csscrush__fmtr_single ($rule) { +function fmtr_single ($rule) { $EOL = CssCrush::$process->newline; @@ -21,7 +21,7 @@ function csscrush__fmtr_single ($rule) { return "$selectors { $block; }$EOL"; } -function csscrush__fmtr_padded ($rule, $padding = 40) { +function fmtr_padded ($rule, $padding = 40) { $EOL = CssCrush::$process->newline; @@ -38,7 +38,7 @@ function csscrush__fmtr_padded ($rule, $padding = 40) { } } -function csscrush__fmtr_block ($rule, $indent = ' ') { +function fmtr_block ($rule, $indent = ' ') { $EOL = CssCrush::$process->newline; diff --git a/plugins/canvas.php b/plugins/canvas.php index 677b423..dfcd1c1 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -47,54 +47,50 @@ * canvas-filter: greyscale() colorize(45, 45, 0); * } */ -namespace CssCrush\Plugins\Canvas; - -use CssCrush\Plugin, CssCrush\Hook, CssCrush\Function, CssCrush\Regex; - -CssCrush_Plugin::register('canvas', array( - 'enable' => 'csscrush__enable_canvas', - 'disable' => 'csscrush__disable_canvas', +namespace CssCrush; + +use stdClass; + +Plugin::register('canvas', array( + 'enable' => function () { + Hook::add('capture_phase2', 'CssCrush\canvas_capture'); + Functions::register('canvas', 'CssCrush\canvas_generator'); + Functions::register('canvas-data', 'CssCrush\canvas_generator'); + }, + 'disable' => function () { + Hook::remove('capture_phase2', 'CssCrush\canvas_capture'); + Functions::deRegister('canvas'); + Functions::deRegister('canvas-data'); + }, )); -function csscrush__enable_canvas () { - CssCrush_Hook::add('capture_phase2', 'csscrush__canvas_capture'); - CssCrush\Functions::register('canvas', 'csscrush__canvas_generator'); - CssCrush\Functions::register('canvas-data', 'csscrush__canvas_generator'); -} - -function csscrush__disable_canvas () { - CssCrush_Hook::remove('capture_phase2', 'csscrush__canvas_capture'); - CssCrush\Functions::deRegister('canvas'); - CssCrush\Functions::deRegister('canvas-data'); -} -function csscrush__canvas_capture ($process) { +function canvas_capture ($process) { static $callback, $patt; if (! $callback) { - $patt = CssCrush_Regex::create('@canvas \s+ (?{{ident}}) \s* {{block}}', 'ixS'); - $callback = create_function('$m', ' - $name = strtolower($m[\'name\']); - $block = $m[\'block_content\']; - if (! empty($block)) { - CssCrush::$process->misc->canvas_defs[$name] = new CssCrush_Template($block); - } - return \'\'; - '); + $patt = Regex::create('@canvas \s+ (?{{ident}}) \s* {{block}}', 'ixS'); } // Extract definitions. - $process->stream->pregReplaceCallback($patt, $callback); + $process->stream->pregReplaceCallback($patt, function ($m) { + $name = strtolower($m['name']); + $block = $m['block_content']; + if (! empty($block)) { + CssCrush::$process->misc->canvas_defs[$name] = new Template($block); + } + return ''; + }); } -function csscrush__canvas_generator ($input, $context) { +function canvas_generator ($input, $context) { $process = CssCrush::$process; // Check GD requirements are met. static $requirements; if (! isset($requirements)) { - $requirements = csscrush__canvas_requirements(); + $requirements = canvas_requirements(); } if ($requirements === false) { return ''; @@ -107,7 +103,7 @@ function csscrush__canvas_generator ($input, $context) { } // Parse args, bail if none. - $args = CssCrush\Functions::parseArgs($input); + $args = Functions::parseArgs($input); if (! isset($args[0])) { return ''; } @@ -124,10 +120,10 @@ function csscrush__canvas_generator ($input, $context) { $block = $canvas_defs[$name]->apply($args); // Parse the block into a keyed array. - $raw = array_change_key_case(CssCrush_Rule::parseBlock($block, array('keyed' => true))); + $raw = array_change_key_case(Rule::parseBlock($block, array('keyed' => true))); // Create canvas object. - $canvas = new CssCrush_Canvas(); + $canvas = new Canvas(); // Parseable canvas attributes with default values. static $schema = array( @@ -144,10 +140,10 @@ function csscrush__canvas_generator ($input, $context) { $canvas->raw = array_intersect_key($raw, $schema) + $schema; // Pre-populate. - csscrush__canvas_preprocess($canvas); + canvas_preprocess($canvas); // Apply functions. - csscrush__canvas_apply_css_funcs($canvas); + canvas_apply_css_funcs($canvas); // csscrush::log($canvas); // Create fingerprint for this canvas based on canvas object. @@ -160,7 +156,7 @@ function csscrush__canvas_generator ($input, $context) { if (! $cached_file) { // Source arguments take priority. - if ($src = csscrush__canvas_fetch_src($canvas->raw['src'])) { + if ($src = canvas_fetch_src($canvas->raw['src'])) { // Resolve the src image dimensions and positioning. $dst_w = $src->width; @@ -183,13 +179,13 @@ function csscrush__canvas_generator ($input, $context) { $canvas->height = $dst_h; // Create base. - csscrush__canvas_create($canvas); + canvas_create($canvas); // Apply background layer. - csscrush__canvas_fill($canvas, 'background-fill'); + canvas_fill($canvas, 'background-fill'); // Filters. - csscrush__canvas_apply_filters($canvas, $src); + canvas_apply_filters($canvas, $src); // Place the src image on the base canvas image. imagecopyresized( @@ -214,11 +210,11 @@ function csscrush__canvas_generator ($input, $context) { $canvas->fills += array('fill' => 'black'); // Create base. - csscrush__canvas_create($canvas); + canvas_create($canvas); // Apply background layer. - csscrush__canvas_fill($canvas, 'background-fill'); - csscrush__canvas_fill($canvas, 'fill'); + canvas_fill($canvas, 'background-fill'); + canvas_fill($canvas, 'fill'); } } else { @@ -234,7 +230,7 @@ function csscrush__canvas_generator ($input, $context) { } // Write to the same directory as the output css. - $url = new CssCrush_Url($generated_filename); + $url = new Url($generated_filename); $url->noRewrite = true; } // Or create data uri. @@ -248,7 +244,7 @@ function csscrush__canvas_generator ($input, $context) { $data = file_get_contents($generated_filepath); } - $url = new CssCrush_Url('data:image/png;base64,' . base64_encode($data)); + $url = new Url('data:image/png;base64,' . base64_encode($data)); } // Cache the output URL. @@ -258,9 +254,9 @@ function csscrush__canvas_generator ($input, $context) { } -function csscrush__canvas_fn_linear_gradient ($input, $context) { +function canvas_fn_linear_gradient ($input, $context) { - $args = CssCrush\Functions::parseArgs($input) + array( + $args = Functions::parseArgs($input) + array( 'white', 'black', ); @@ -286,14 +282,14 @@ function csscrush__canvas_fn_linear_gradient ($input, $context) { $fill->stops = array(); $fill->direction = $direction; - csscrush__canvas_set_fill_dims($fill, $context->canvas); + canvas_set_fill_dims($fill, $context->canvas); // Start color. - $color = CssCrush_Color::parse($args[0]); + $color = Color::parse($args[0]); $fill->stops[] = $color ? $color : array(0,0,0,1); // End color. - $color = CssCrush_Color::parse($args[1]); + $color = Color::parse($args[1]); $fill->stops[] = $color ? $color : array(255,255,255,1); if ($flip) { @@ -303,15 +299,15 @@ function csscrush__canvas_fn_linear_gradient ($input, $context) { $context->canvas->fills[$context->currentProperty] = $fill; } -function csscrush__canvas_fn_filter ($input, $context) { +function canvas_fn_filter ($input, $context) { - $args = CssCrush\Functions::parseArgs($input); + $args = Functions::parseArgs($input); array_unshift($context->canvas->filters, array($context->function, $args)); } -function csscrush__canvas_apply_filters ($canvas, $src) { +function canvas_apply_filters ($canvas, $src) { foreach ($canvas->filters as $filter) { list($name, $args) = $filter; @@ -327,14 +323,14 @@ function csscrush__canvas_apply_filters ($canvas, $src) { break; case 'opacity': - csscrush__canvas_fade($src, floatval($args[0])); + canvas_fade($src, floatval($args[0])); break; case 'colorize': $rgb = $args + array('black'); if (count($rgb) === 1) { // If only one argument parse it as a CSS color value. - $rgb = CssCrush_Color::parse($rgb[0]); + $rgb = Color::parse($rgb[0]); if (! $rgb) { $rgb = array(0,0,0); } @@ -376,39 +372,38 @@ function csscrush__canvas_apply_filters ($canvas, $src) { } } -function csscrush__canvas_apply_css_funcs ($canvas) { +function canvas_apply_css_funcs ($canvas) { // Setup functions for using on values. static $map; if (! $map) { $fill_functions = array( - 'canvas-linear-gradient' => 'csscrush__canvas_fn_linear_gradient', + 'canvas-linear-gradient' => 'CssCrush\canvas_fn_linear_gradient', ); $map['fill'] = array( - 'patt' => CssCrush_Regex::createFunctionPatt(array_keys($fill_functions)), + 'patt' => Regex::createFunctionPatt(array_keys($fill_functions)), 'functions' => $fill_functions, ); $filter_functions = array( - 'contrast' => 'csscrush__canvas_fn_filter', - 'opacity' => 'csscrush__canvas_fn_filter', - 'colorize' => 'csscrush__canvas_fn_filter', - 'grayscale' => 'csscrush__canvas_fn_filter', - 'greyscale' => 'csscrush__canvas_fn_filter', - 'brightness' => 'csscrush__canvas_fn_filter', - 'invert' => 'csscrush__canvas_fn_filter', - 'blur' => 'csscrush__canvas_fn_filter', + 'contrast' => 'CssCrush\canvas_fn_filter', + 'opacity' => 'CssCrush\canvas_fn_filter', + 'colorize' => 'CssCrush\canvas_fn_filter', + 'grayscale' => 'CssCrush\canvas_fn_filter', + 'greyscale' => 'CssCrush\canvas_fn_filter', + 'brightness' => 'CssCrush\canvas_fn_filter', + 'invert' => 'CssCrush\canvas_fn_filter', + 'blur' => 'CssCrush\canvas_fn_filter', ); $map['filter'] = array( - 'patt' => CssCrush_Regex::createFunctionPatt(array_keys($filter_functions)), + 'patt' => Regex::createFunctionPatt(array_keys($filter_functions)), 'functions' => $filter_functions, ); - $generic_functions = array_diff_key( - CssCrush\Functions::$functions, $map['fill']['functions']); + $generic_functions = array_diff_key(Functions::$functions, $map['fill']['functions']); $map['generic'] = array( - 'patt' => CssCrush_Regex::createFunctionPatt( + 'patt' => Regex::createFunctionPatt( array_keys($generic_functions), array('bare_paren' => true)), 'functions' => $generic_functions, ); @@ -424,29 +419,29 @@ function csscrush__canvas_apply_css_funcs ($canvas) { } // Generic functions. - CssCrush\Functions::executeOnString( + Functions::executeOnString( $value, $map['generic']['patt'], $map['generic']['functions']); // Fill functions. if (in_array($property, array('fill', 'background-fill'))) { $context->currentProperty = $property; $context->canvas = $canvas; - CssCrush\Functions::executeOnString( + Functions::executeOnString( $value, $map['fill']['patt'], $map['fill']['functions'], $context); } elseif ($property === 'canvas-filter') { $context->canvas = $canvas; - CssCrush\Functions::executeOnString( + Functions::executeOnString( $value, $map['filter']['patt'], $map['filter']['functions'], $context); } } } -function csscrush__canvas_preprocess ($canvas) { +function canvas_preprocess ($canvas) { if (isset($canvas->raw['margin'])) { - $parts = csscrush__canvas_parselist($canvas->raw['margin']); + $parts = canvas_parselist($canvas->raw['margin']); $count = count($parts); if ($count === 1) { $margin = array($parts[0], $parts[0], $parts[0], $parts[0]); @@ -481,7 +476,7 @@ function csscrush__canvas_preprocess ($canvas) { $canvas->height = $canvas->raw['height']; } -function csscrush__canvas_fetch_src ($url_token) { +function canvas_fetch_src ($url_token) { if ($url_token && $url = CssCrush::$process->tokens->get($url_token)) { @@ -527,7 +522,7 @@ function csscrush__canvas_fetch_src ($url_token) { Adapted from GD Gradient Fill by Ozh (http://planetozh.com): http://planetozh.com/blog/my-projects/images-php-gd-gradient-fill */ -function csscrush__canvas_gradient ($canvas, $fill) { +function canvas_gradient ($canvas, $fill) { $image = $canvas->image; @@ -552,7 +547,7 @@ function csscrush__canvas_gradient ($canvas, $fill) { $g = $g2 - $g1 ? intval($g1 + ($g2 - $g1) * ($line / $line_numbers)): $g1; $b = $b2 - $b1 ? intval($b1 + ($b2 - $b1) * ($line / $line_numbers)): $b1; $a = $a2 - $a1 ? ($a1 + ($a2 - $a1) * ($line / $line_numbers)) : $a1; - $a = csscrush__canvas_opacity($a); + $a = canvas_opacity($a); if ($last != "$r,$g,$b,$a") { $color = imagecolorallocatealpha($image, $r, $g, $b, $a); @@ -582,17 +577,17 @@ function csscrush__canvas_gradient ($canvas, $fill) { } } -function csscrush__canvas_create ($canvas) { +function canvas_create ($canvas) { $margin = $canvas->margin; $width = $canvas->width + $margin->right + $margin->left; $height = $canvas->height + $margin->top + $margin->bottom; // Create image object. - $canvas->image = csscrush__canvas_create_transparent($width, $height); + $canvas->image = canvas_create_transparent($width, $height); } -function csscrush__canvas_create_transparent ($width, $height) { +function canvas_create_transparent ($width, $height) { $image = imagecreatetruecolor($width, $height); @@ -604,12 +599,12 @@ function csscrush__canvas_create_transparent ($width, $height) { return $image; } -function csscrush__canvas_fade ($src, $opacity) { +function canvas_fade ($src, $opacity) { $width = imagesx($src->image); $height = imagesy($src->image); - $new_image = csscrush__canvas_create_transparent($width, $height); - $opacity = csscrush__canvas_opacity($opacity); + $new_image = canvas_create_transparent($width, $height); + $opacity = canvas_opacity($opacity); // Perform pixel-based alpha map application for ($x = 0; $x < $width; $x++) { @@ -625,7 +620,7 @@ function csscrush__canvas_fade ($src, $opacity) { } -function csscrush__canvas_fill ($canvas, $property) { +function canvas_fill ($canvas, $property) { if (! isset($canvas->fills[$property])) { return false; @@ -634,18 +629,18 @@ function csscrush__canvas_fill ($canvas, $property) { // Gradient fill. if (is_object($fill)) { - csscrush__canvas_gradient($canvas, $fill); + canvas_gradient($canvas, $fill); } // Solid color fill. - elseif ($solid = CssCrush_Color::parse($fill)) { + elseif ($solid = Color::parse($fill)) { list($r, $g, $b, $a) = $solid; - $color = imagecolorallocatealpha($canvas->image, $r, $g, $b, csscrush__canvas_opacity($a)); + $color = imagecolorallocatealpha($canvas->image, $r, $g, $b, canvas_opacity($a)); $fill = new stdClass(); $canvas->currentProperty = $property; - csscrush__canvas_set_fill_dims($fill, $canvas); + canvas_set_fill_dims($fill, $canvas); imagefilledrectangle($canvas->image, $fill->x1, $fill->y1, $fill->x2, $fill->y2, $color); imagealphablending($canvas->image, true); @@ -657,7 +652,7 @@ function csscrush__canvas_fill ($canvas, $property) { } } -function csscrush__canvas_set_fill_dims ($fill, $canvas) { +function canvas_set_fill_dims ($fill, $canvas) { // Resolve fill dimensions and coordinates. $margin = $canvas->margin; @@ -675,7 +670,7 @@ function csscrush__canvas_set_fill_dims ($fill, $canvas) { } } -function csscrush__canvas_requirements () { +function canvas_requirements () { $error_messages = array(); @@ -705,7 +700,7 @@ function csscrush__canvas_requirements () { /* Canvas object. */ -class CssCrush_Canvas +class Canvas { public $image, $fills = array(), $filters = array(); @@ -720,11 +715,11 @@ public function __destruct () /* Helpers. */ -function csscrush__canvas_opacity ($float) { +function canvas_opacity ($float) { return 127 - max(min(round($float * 127), 127), 0); } -function csscrush__canvas_parselist ($str, $numbers = true) { +function canvas_parselist ($str, $numbers = true) { $list = preg_split('~ +~', trim($str)); return $numbers ? array_map('floatval', $list) : $list; } diff --git a/plugins/ease.php b/plugins/ease.php index c2dd0a2..bd09e95 100644 --- a/plugins/ease.php +++ b/plugins/ease.php @@ -11,21 +11,19 @@ * @after * transition: .2s cubic-bezier(.550,.085,.680,.530) */ +namespace CssCrush; -CssCrush_Plugin::register('ease', array( - 'enable' => 'csscrush__enable_ease', - 'disable' => 'csscrush__disable_ease', +Plugin::register('ease', array( + 'enable' => function () { + Hook::add('rule_prealias', 'CssCrush\ease'); + }, + 'disable' => function () { + Hook::remove('rule_prealias', 'CssCrush\ease'); + }, )); -function csscrush__enable_ease () { - CssCrush_Hook::add('rule_prealias', 'csscrush__ease'); -} - -function csscrush__disable_ease () { - CssCrush_Hook::remove('rule_prealias', 'csscrush__ease'); -} -function csscrush__ease (CssCrush_Rule $rule) { +function ease (Rule $rule) { static $find, $replace, $easing_properties; if (! $find) { @@ -62,7 +60,7 @@ function csscrush__ease (CssCrush_Rule $rule) { ); foreach ($easings as $property => $value) { - $patt = CssCrush_Regex::create('{{LB}}' . $property . '{{RB}}', 'i'); + $patt = Regex::create('{{LB}}' . $property . '{{RB}}', 'i'); $find[] = $patt; $replace[] = $value; } diff --git a/plugins/hsl-to-hex.php b/plugins/hsl-to-hex.php index 55da51f..dc61d37 100644 --- a/plugins/hsl-to-hex.php +++ b/plugins/hsl-to-hex.php @@ -8,25 +8,23 @@ * @after * color: #6abf40 */ +namespace CssCrush; -CssCrush_Plugin::register('hsl-to-hex', array( - 'enable' => 'csscrush__enable_hsl_to_hex', - 'disable' => 'csscrush__disable_hsl_to_hex', +Plugin::register('hsl-to-hex', array( + 'enable' => function () { + Hook::add('rule_postalias', 'CssCrush\hsl_to_hex'); + }, + 'disable' => function () { + Hook::remove('rule_postalias', 'CssCrush\hsl_to_hex'); + }, )); -function csscrush__enable_hsl_to_hex () { - CssCrush_Hook::add('rule_postalias', 'csscrush__hsl_to_hex'); -} - -function csscrush__disable_hsl_to_hex () { - CssCrush_Hook::remove('rule_postalias', 'csscrush__hsl_to_hex'); -} -function csscrush__hsl_to_hex (CssCrush_Rule $rule) { +function hsl_to_hex (Rule $rule) { static $hsl_patt; if (! $hsl_patt) { - $hsl_patt = CssCrush_Regex::create('{{LB}}hsl({{p-token}})', 'i'); + $hsl_patt = Regex::create('{{LB}}hsl({{p-token}})', 'i'); } foreach ($rule as &$declaration) { @@ -34,7 +32,7 @@ function csscrush__hsl_to_hex (CssCrush_Rule $rule) { if (! $declaration->skip && isset($declaration->functions['hsl'])) { while (preg_match($hsl_patt, $declaration->value, $m)) { $token = $m[1]; - $color = new CssCrush_Color('hsl' . CssCrush::$process->tokens->pop($token)); + $color = new Color('hsl' . CssCrush::$process->tokens->pop($token)); $declaration->value = str_replace($m[0], $color->getHex(), $declaration->value); } } diff --git a/plugins/ie-clip.php b/plugins/ie-clip.php index d85dd96..e3f614b 100644 --- a/plugins/ie-clip.php +++ b/plugins/ie-clip.php @@ -9,21 +9,19 @@ * clip: rect(1px,1px,1px,1px); * *clip: rect(1px 1px 1px 1px); */ +namespace CssCrush; -CssCrush_Plugin::register( 'ie-clip', array( - 'enable' => 'csscrush__enable_ie_clip', - 'disable' => 'csscrush__disable_ie_clip', +Plugin::register( 'ie-clip', array( + 'enable' => function () { + Hook::add('rule_postalias', 'CssCrush\ie_clip'); + }, + 'disable' => function () { + Hook::remove('rule_postalias', 'CssCrush\ie_clip'); + }, )); -function csscrush__enable_ie_clip () { - CssCrush_Hook::add('rule_postalias', 'csscrush__ie_clip'); -} - -function csscrush__disable_ie_clip () { - CssCrush_Hook::remove('rule_postalias', 'csscrush__ie_clip'); -} -function csscrush__ie_clip (CssCrush_Rule $rule) { +function ie_clip (Rule $rule) { // Assume it's been dealt with if the property occurs more than once. if ($rule->propertyCount('clip') !== 1) { @@ -38,7 +36,7 @@ function csscrush__ie_clip (CssCrush_Rule $rule) { ) { continue; } - $new_set[] = new CssCrush_Declaration('*clip', str_replace( ',', ' ', $declaration->getFullValue())); + $new_set[] = new Declaration('*clip', str_replace( ',', ' ', $declaration->getFullValue())); } $rule->setDeclarations($new_set); } diff --git a/plugins/ie-filter.php b/plugins/ie-filter.php index 4e0f766..cdeadef 100644 --- a/plugins/ie-filter.php +++ b/plugins/ie-filter.php @@ -15,21 +15,19 @@ * *filter: alpha(opacity=50), progid:DXImageTransform.Microsoft.Blur(strength=10); * zoom: 1; */ +namespace CssCrush; -CssCrush_Plugin::register('ie-filter', array( - 'enable' => 'csscrush__enable_ie_filter', - 'disable' => 'csscrush__disable_ie_filter', +Plugin::register('ie-filter', array( + 'enable' => function () { + Hook::add('rule_postalias', 'CssCrush\ie_filter'); + }, + 'disable' => function () { + Hook::remove('rule_postalias', 'CssCrush\ie_filter'); + }, )); -function csscrush__enable_ie_filter () { - CssCrush_Hook::add('rule_postalias', 'csscrush__ie_filter'); -} - -function csscrush__disable_ie_filter () { - CssCrush_Hook::remove('rule_postalias', 'csscrush__ie_filter'); -} -function csscrush__ie_filter (CssCrush_Rule $rule) { +function ie_filter (Rule $rule) { if ($rule->propertyCount('-ms-filter') < 1) { return; @@ -55,13 +53,13 @@ function csscrush__ie_filter (CssCrush_Rule $rule) { } $declaration->value = implode(',', $list); if (! $rule->propertyCount('zoom')) { - // Filters need hasLayout - $new_set[] = new CssCrush_Declaration('zoom', 1); + // Filters need hasLayout. + $new_set[] = new Declaration('zoom', 1); } - // Quoted version for -ms-filter IE >= 8 - $new_set[] = new CssCrush_Declaration('-ms-filter', "\"$declaration->value\""); - // Star escaped property for IE < 8 - $new_set[] = new CssCrush_Declaration('*filter', $declaration->value); + // Quoted version for -ms-filter IE >= 8. + $new_set[] = new Declaration('-ms-filter', "\"$declaration->value\""); + // Star escaped property for IE < 8. + $new_set[] = new Declaration('*filter', $declaration->value); } $rule->setDeclarations($new_set); } diff --git a/plugins/ie-inline-block.php b/plugins/ie-inline-block.php index 35d4270..8963600 100644 --- a/plugins/ie-inline-block.php +++ b/plugins/ie-inline-block.php @@ -10,21 +10,19 @@ * *display: inline; * *zoom: 1; */ +namespace CssCrush; -CssCrush_Plugin::register('ie-inline-block', array( - 'enable' => 'csscrush__enable_ie_inline_block', - 'disable' => 'csscrush__disable_ie_inline_block', +Plugin::register('ie-inline-block', array( + 'enable' => function () { + Hook::add('rule_postalias', 'CssCrush\ie_inline_block'); + }, + 'disable' => function () { + Hook::remove('rule_postalias', 'CssCrush\ie_inline_block'); + }, )); -function csscrush__enable_ie_inline_block () { - CssCrush_Hook::add('rule_postalias', 'csscrush__ie_inline_block'); -} - -function csscrush__disable_ie_inline_block () { - CssCrush_Hook::remove('rule_postalias', 'csscrush__ie_inline_block'); -} -function csscrush__ie_inline_block (CssCrush_Rule $rule) { +function ie_inline_block (Rule $rule) { if ($rule->propertyCount('display') < 1) { return; @@ -40,8 +38,8 @@ function csscrush__ie_inline_block (CssCrush_Rule $rule) { $is_display && $declaration->value !== 'inline-block') { continue; } - $stack[] = new CssCrush_Declaration('*display', 'inline'); - $stack[] = new CssCrush_Declaration('*zoom', 1); + $stack[] = new Declaration('*display', 'inline'); + $stack[] = new Declaration('*zoom', 1); } $rule->setDeclarations($stack); } diff --git a/plugins/ie-min-height.php b/plugins/ie-min-height.php index de65c03..81386f0 100644 --- a/plugins/ie-min-height.php +++ b/plugins/ie-min-height.php @@ -9,21 +9,19 @@ * min-height: 100px; * _height: 100px; */ +namespace CssCrush; -CssCrush_Plugin::register('ie-min-height', array( - 'enable' => 'csscrush__enable_ie_min_height', - 'disable' => 'csscrush__disable_ie_min_height', +Plugin::register('ie-min-height', array( + 'enable' => function () { + Hook::add('rule_postalias', 'CssCrush\ie_min_height'); + }, + 'disable' => function () { + Hook::remove('rule_postalias', 'CssCrush\ie_min_height'); + }, )); -function csscrush__enable_ie_min_height () { - CssCrush_Hook::add('rule_postalias', 'csscrush__ie_min_height'); -} - -function csscrush__disable_ie_min_height () { - CssCrush_Hook::remove('rule_postalias', 'csscrush__ie_min_height'); -} -function csscrush__ie_min_height (CssCrush_Rule $rule) { +function ie_min_height (Rule $rule) { if ($rule->propertyCount('min-height') < 1) { return; @@ -36,7 +34,7 @@ function csscrush__ie_min_height (CssCrush_Rule $rule) { $declaration->property !== 'min-height') { continue; } - $new_set[] = new CssCrush_Declaration('_height', $declaration->value); + $new_set[] = new Declaration('_height', $declaration->value); } $rule->setDeclarations($new_set); } diff --git a/plugins/ie-opacity.php b/plugins/ie-opacity.php index 33ad0c1..e26bffc 100755 --- a/plugins/ie-opacity.php +++ b/plugins/ie-opacity.php @@ -11,21 +11,19 @@ * *filter: alpha(opacity=45); * zoom: 1; */ +namespace CssCrush; -CssCrush_Plugin::register('ie-opacity', array( - 'enable' => 'csscrush__enable_ie_opacity', - 'disable' => 'csscrush__disable_ie_opacity', +Plugin::register('ie-opacity', array( + 'enable' => function () { + Hook::add('rule_postalias', 'CssCrush\ie_opacity'); + }, + 'disable' => function () { + Hook::remove('rule_postalias', 'CssCrush\ie_opacity'); + }, )); -function csscrush__enable_ie_opacity () { - CssCrush_Hook::add('rule_postalias', 'csscrush__ie_opacity'); -} - -function csscrush__disable_ie_opacity () { - CssCrush_Hook::remove('rule_postalias', 'csscrush__ie_opacity'); -} -function csscrush__ie_opacity (CssCrush_Rule $rule) { +function ie_opacity (Rule $rule) { if ($rule->propertyCount('opacity') < 1) { return; @@ -45,11 +43,11 @@ function csscrush__ie_opacity (CssCrush_Rule $rule) { if (! $rule->propertyCount('zoom')) { // Filters need hasLayout - $new_set[] = new CssCrush_Declaration('zoom', 1); + $new_set[] = new Declaration('zoom', 1); } $value = "alpha(opacity=$opacity)"; - $new_set[] = new CssCrush_Declaration('-ms-filter', "\"$value\""); - $new_set[] = new CssCrush_Declaration('*filter', $value); + $new_set[] = new Declaration('-ms-filter', "\"$value\""); + $new_set[] = new Declaration('*filter', $value); } $rule->setDeclarations($new_set); } diff --git a/plugins/initial.php b/plugins/initial.php index 890157e..1c93d4d 100644 --- a/plugins/initial.php +++ b/plugins/initial.php @@ -26,7 +26,7 @@ }, )); -function initial (CssCrush_Rule $rule) { +function initial (Rule $rule) { static $initial_values; if (! $initial_values) { diff --git a/plugins/legacy-flexbox.php b/plugins/legacy-flexbox.php index aac7a64..14d09bf 100644 --- a/plugins/legacy-flexbox.php +++ b/plugins/legacy-flexbox.php @@ -43,21 +43,19 @@ * Firefox 20 will ship in April 2013 with an updated (and unprefixed) implementation * of flexbox: https://developer.mozilla.org/en-US/docs/Firefox_20_for_developers */ - -CssCrush_Plugin::register('legacy-flexbox', array( - 'enable' => 'csscrush__enable_legacy_flexbox', - 'disable' => 'csscrush__disable_legacy_flexbox', +namespace CssCrush; + +Plugin::register('legacy-flexbox', array( + 'enable' => function () { + Hook::add('rule_prealias', 'CssCrush\legacy_flexbox'); + }, + 'disable' => function () { + Hook::remove('rule_prealias', 'CssCrush\legacy_flexbox'); + }, )); -function csscrush__enable_legacy_flexbox () { - CssCrush_Hook::add('rule_prealias', 'csscrush__legacy_flexbox'); -} -function csscrush__disable_legacy_flexbox () { - CssCrush_Hook::remove('rule_prealias', 'csscrush__legacy_flexbox'); -} - -function csscrush__legacy_flexbox (CssCrush_Rule $rule) { +function legacy_flexbox (Rule $rule) { static $flex_related_props = array( 'align-items' => true, @@ -121,26 +119,26 @@ function csscrush__legacy_flexbox (CssCrush_Rule $rule) { ($value === 'flex' || $value === 'inline-flex') && isset($declaration_aliases['display']['box'])) { foreach ($declaration_aliases['display']['box'] as $pair) { - $stack[] = new CssCrush_Declaration($pair[0], $pair[1]); + $stack[] = new Declaration($pair[0], $pair[1]); $rule_updated = true; } } break; case 'align-items': - $rule_updated = csscrush__flex_align_items($value, $stack); + $rule_updated = flex_align_items($value, $stack); break; case 'flex': - $rule_updated = csscrush__flex($value, $stack); + $rule_updated = flex($value, $stack); break; case 'flex-direction': - $rule_updated = csscrush__flex_direction($value, $stack); + $rule_updated = flex_direction($value, $stack); break; case 'flex-grow': - $rule_updated = csscrush__flex_grow($value, $stack); + $rule_updated = flex_grow($value, $stack); break; case 'flex-wrap': @@ -150,15 +148,15 @@ function csscrush__legacy_flexbox (CssCrush_Rule $rule) { // - http://stackoverflow.com/questions/5010083/\ // css3-flex-box-specifying-multiple-box-lines-doesnt-work - // $rule_updated = csscrush__flex_wrap($value, $stack); + // $rule_updated = flex_wrap($value, $stack); break; case 'justify-content': - $rule_updated = csscrush__flex_justify_content($value, $stack); + $rule_updated = flex_justify_content($value, $stack); break; case 'order': - $rule_updated = csscrush__flex_order($value, $stack); + $rule_updated = flex_order($value, $stack); break; // Shorthand values. @@ -170,7 +168,7 @@ function csscrush__legacy_flexbox (CssCrush_Rule $rule) { $direction = isset($args[0]) ? $args[0] : 'initial'; $wrap = isset($args[1]) ? $args[1] : 'initial'; - $rule_updated = csscrush__flex_direction($direction, $stack); + $rule_updated = flex_direction($direction, $stack); // $rule_updated = csscrush__flex_wrap($wrap, $stack); break; } @@ -186,7 +184,7 @@ function csscrush__legacy_flexbox (CssCrush_Rule $rule) { } -function csscrush__flex_direction ($value, &$stack) { +function flex_direction ($value, &$stack) { // flex-direction: row | row-reverse | column | column-reverse // box-orient: horizontal | vertical | inline-axis | block-axis | inherit @@ -213,13 +211,13 @@ function csscrush__flex_direction ($value, &$stack) { if (isset($prop_aliases['box-direction'])) { foreach ($prop_aliases['box-direction'] as $prop_alias) { - $stack[] = new CssCrush_Declaration($prop_alias, $directions[$value]); + $stack[] = new Declaration($prop_alias, $directions[$value]); $rule_updated = true; } } if (isset($prop_aliases['box-orient'])) { foreach ($prop_aliases['box-orient'] as $prop_alias) { - $stack[] = new CssCrush_Declaration($prop_alias, $orientations[$value]); + $stack[] = new Declaration($prop_alias, $orientations[$value]); $rule_updated = true; } } @@ -227,7 +225,7 @@ function csscrush__flex_direction ($value, &$stack) { } -function csscrush__flex_justify_content ($value, &$stack) { +function flex_justify_content ($value, &$stack) { // justify-content: flex-start | flex-end | center | space-between | space-around // box-pack: start | end | center | justify @@ -245,7 +243,7 @@ function csscrush__flex_justify_content ($value, &$stack) { if (isset($prop_aliases['box-pack']) && isset($positions[$value])) { foreach ($prop_aliases['box-pack'] as $prop_alias) { - $stack[] = new CssCrush_Declaration($prop_alias, $positions[$value]); + $stack[] = new Declaration($prop_alias, $positions[$value]); $rule_updated = true; } } @@ -253,7 +251,7 @@ function csscrush__flex_justify_content ($value, &$stack) { } -function csscrush__flex_align_items ($value, &$stack) { +function flex_align_items ($value, &$stack) { // align-items: flex-start | flex-end | center | baseline | stretch // box-align: start | end | center | baseline | stretch @@ -271,7 +269,7 @@ function csscrush__flex_align_items ($value, &$stack) { if (isset($prop_aliases['box-align']) && isset($positions[$value])) { foreach ($prop_aliases['box-align'] as $prop_alias) { - $stack[] = new CssCrush_Declaration($prop_alias, $positions[$value]); + $stack[] = new Declaration($prop_alias, $positions[$value]); $rule_updated = true; } } @@ -279,7 +277,7 @@ function csscrush__flex_align_items ($value, &$stack) { } -function csscrush__flex_order ($value, &$stack) { +function flex_order ($value, &$stack) { // order: // box-ordinal-group: @@ -297,7 +295,7 @@ function csscrush__flex_order ($value, &$stack) { $rule_updated = false; if (isset($prop_aliases['box-ordinal-group'])) { foreach ($prop_aliases['box-ordinal-group'] as $prop_alias) { - $stack[] = new CssCrush_Declaration($prop_alias, $value); + $stack[] = new Declaration($prop_alias, $value); $rule_updated = true; } } @@ -305,7 +303,7 @@ function csscrush__flex_order ($value, &$stack) { } -function csscrush__flex_wrap ($value, &$stack) { +function flex_wrap ($value, &$stack) { // flex-wrap: nowrap | wrap | wrap-reverse // box-lines: single | multiple @@ -322,7 +320,7 @@ function csscrush__flex_wrap ($value, &$stack) { if (isset($prop_aliases['box-lines']) && isset($wrap_behaviours[$value])) { foreach ($prop_aliases['box-lines'] as $prop_alias) { - $stack[] = new CssCrush_Declaration($prop_alias, $wrap_behaviours[$value]); + $stack[] = new Declaration($prop_alias, $wrap_behaviours[$value]); $rule_updated = true; } } @@ -330,7 +328,7 @@ function csscrush__flex_wrap ($value, &$stack) { } -function csscrush__flex ($value, &$stack) { +function flex ($value, &$stack) { // flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ] // box-flex: @@ -357,7 +355,7 @@ function csscrush__flex ($value, &$stack) { $rule_updated = false; if (isset($prop_aliases['box-flex'])) { foreach ($prop_aliases['box-flex'] as $prop_alias) { - $stack[] = new CssCrush_Declaration($prop_alias, $grow); + $stack[] = new Declaration($prop_alias, $grow); $rule_updated = true; } } @@ -365,7 +363,7 @@ function csscrush__flex ($value, &$stack) { } -function csscrush__flex_grow ($value, &$stack) { +function flex_grow ($value, &$stack) { // flex-grow: // box-flex: @@ -375,10 +373,9 @@ function csscrush__flex_grow ($value, &$stack) { $rule_updated = false; if (isset($prop_aliases['box-flex'])) { foreach ($prop_aliases['box-flex'] as $prop_alias) { - $stack[] = new CssCrush_Declaration($prop_alias, $value); + $stack[] = new Declaration($prop_alias, $value); $rule_updated = true; } } return $rule_updated; } - diff --git a/plugins/noise.php b/plugins/noise.php index 156a1bd..25363f5 100644 --- a/plugins/noise.php +++ b/plugins/noise.php @@ -85,7 +85,7 @@ 'dimensions' => array(150, 150), )); }); - Functions::register('turbulence', function ($m) { + Functions::register('turbulence', function ($input) { return noise_generator($input, array( 'type' => 'turbulence', 'frequency' => .01, @@ -101,6 +101,7 @@ }, )); + function noise_generator ($input, $defaults) { $args = array_pad(Functions::parseArgs($input), 4, 'default'); @@ -137,7 +138,7 @@ function noise_generator ($input, $defaults) { break; case 1: case 2: - if (preg_match(CssCrush_Regex::$patt->rooted_number, $value)) { + if (preg_match(Regex::$patt->rooted_number, $value)) { $octaves = $value; } elseif (in_array($value, $sharpen_modes)) { diff --git a/plugins/property-sorter.php b/plugins/property-sorter.php index 6b6a9ea..a269ada 100644 --- a/plugins/property-sorter.php +++ b/plugins/property-sorter.php @@ -22,168 +22,173 @@ * background: #000; * */ -namespace CssCrush; +namespace CssCrush { -Plugin::register('property-sorter', array( - 'enable' => function () { - Hook::add('rule_prealias', 'property_sorter'); - }, - 'disable' => function () { - Hook::remove('rule_prealias', 'property_sorter'); - }, -)); + Plugin::register('property-sorter', array( + 'enable' => function () { + Hook::add('rule_prealias', 'CssCrush\property_sorter'); + }, + 'disable' => function () { + Hook::remove('rule_prealias', 'CssCrush\property_sorter'); + }, + )); -function property_sorter (Rule $rule) { - $new_set = array(); + function property_sorter (Rule $rule) { - // Create plain array of rule declarations. - foreach ($rule as $declaration) { - $new_set[] = $declaration; - } + $new_set = array(); - usort($new_set, 'property_sorter_callback'); + // Create plain array of rule declarations. + foreach ($rule as $declaration) { + $new_set[] = $declaration; + } - $rule->setDeclarations($new_set); -} + usort($new_set, 'CssCrush\property_sorter_callback'); + + $rule->setDeclarations($new_set); + } -/* - Callback for sorting. -*/ -function property_sorter_callback ($a, $b) { + /* + Callback for sorting. + */ + function property_sorter_callback ($a, $b) { - $map =& property_sorter_get_table(); - $a_prop =& $a->canonicalProperty; - $b_prop =& $b->canonicalProperty; - $a_listed = isset($map[$a_prop]); - $b_listed = isset($map[$b_prop]); + $map =& property_sorter_get_table(); + $a_prop =& $a->canonicalProperty; + $b_prop =& $b->canonicalProperty; + $a_listed = isset($map[$a_prop]); + $b_listed = isset($map[$b_prop]); - // If the properties are identical we need to flag for an index comparison. - $compare_indexes = false; + // If the properties are identical we need to flag for an index comparison. + $compare_indexes = false; - // If the 'canonical' properties are identical we need to flag for a vendor comparison. - $compare_vendor = false; + // If the 'canonical' properties are identical we need to flag for a vendor comparison. + $compare_vendor = false; - // If both properties are listed. - if ($a_listed && $b_listed) { + // If both properties are listed. + if ($a_listed && $b_listed) { - if ($a_prop === $b_prop) { - if ($a->vendor || $b->vendor) { - $compare_vendor = true; + if ($a_prop === $b_prop) { + if ($a->vendor || $b->vendor) { + $compare_vendor = true; + } + else { + $compare_indexes = true; + } } else { - $compare_indexes = true; + // Table comparison. + return $map[$a_prop] > $map[$b_prop] ? 1 : -1; } } - else { - // Table comparison. - return $map[$a_prop] > $map[$b_prop] ? 1 : -1; - } - } - // If one property is listed it always takes higher priority. - elseif ($a_listed && ! $b_listed) { - return -1; - } - elseif ($b_listed && ! $a_listed) { - return 1; - } + // If one property is listed it always takes higher priority. + elseif ($a_listed && ! $b_listed) { + return -1; + } + elseif ($b_listed && ! $a_listed) { + return 1; + } - // If neither property is listed. - else { + // If neither property is listed. + else { - if ($a_prop === $b_prop) { - if ($a->vendor || $b->vendor) { - $compare_vendor = true; + if ($a_prop === $b_prop) { + if ($a->vendor || $b->vendor) { + $compare_vendor = true; + } + else { + $compare_indexes = true; + } } else { - $compare_indexes = true; + // Regular sort. + return $a_prop > $b_prop ? 1 : -1; } } - else { - // Regular sort. - return $a_prop > $b_prop ? 1 : -1; - } - } - // Comparing by index. - if ($compare_indexes ) { - return $a->index > $b->index ? 1 : -1; - } - - // Comparing by vendor mark. - if ($compare_vendor) { - if (! $a->vendor && $b->vendor) { - return 1; + // Comparing by index. + if ($compare_indexes ) { + return $a->index > $b->index ? 1 : -1; } - elseif ($a->vendor && ! $b->vendor) { - return -1; - } - else { - // If both have a vendor mark compare vendor name length. - return strlen($b->vendor) > strlen($a->vendor) ? 1 : -1; + + // Comparing by vendor mark. + if ($compare_vendor) { + if (! $a->vendor && $b->vendor) { + return 1; + } + elseif ($a->vendor && ! $b->vendor) { + return -1; + } + else { + // If both have a vendor mark compare vendor name length. + return strlen($b->vendor) > strlen($a->vendor) ? 1 : -1; + } } } -} -/* - Cache for the table of values to compare against. -*/ -function &property_sorter_get_table () { + /* + Cache for the table of values to compare against. + */ + function &property_sorter_get_table () { - // Check for cached table. - if (isset($GLOBALS['CSSCRUSH_PROPERTY_SORT_ORDER_CACHE'])) { - return $GLOBALS['CSSCRUSH_PROPERTY_SORT_ORDER_CACHE']; - } + // Check for cached table. + if (isset($GLOBALS['CSSCRUSH_PROPERTY_SORT_ORDER_CACHE'])) { + return $GLOBALS['CSSCRUSH_PROPERTY_SORT_ORDER_CACHE']; + } - $table = array(); + $table = array(); - // Nothing cached, check for a user-defined table. - if (isset($GLOBALS['CSSCRUSH_PROPERTY_SORT_ORDER'])) { - $table = (array) $GLOBALS['CSSCRUSH_PROPERTY_SORT_ORDER']; - } + // Nothing cached, check for a user-defined table. + if (isset($GLOBALS['CSSCRUSH_PROPERTY_SORT_ORDER'])) { + $table = (array) $GLOBALS['CSSCRUSH_PROPERTY_SORT_ORDER']; + } + + // No user-defined table, use pre-defined. + else { - // No user-defined table, use pre-defined. - else { + // Load from property-sorting.ini. + $sorting_file_contents = + file_get_contents(CssCrush::$config->location . '/misc/property-sorting.ini'); + if ($sorting_file_contents !== false) { - // Load from property-sorting.ini. - $sorting_file_contents = - file_get_contents(CssCrush::$config->location . '/misc/property-sorting.ini'); - if ($sorting_file_contents !== false) { + $sorting_file_contents = preg_replace('~;[^\r\n]*~', '', $sorting_file_contents); + $table = preg_split('~\s+~', trim($sorting_file_contents)); + } + else { + trigger_error(__METHOD__ . ": Property sorting file not found.\n", E_USER_NOTICE); + } - $sorting_file_contents = preg_replace('~;[^\r\n]*~', '', $sorting_file_contents); - $table = preg_split('~\s+~', trim($sorting_file_contents)); - } - else { - trigger_error(__METHOD__ . ": Property sorting file not found.\n", E_USER_NOTICE); + // Store to the global variable. + $GLOBALS['CSSCRUSH_PROPERTY_SORT_ORDER'] = $table; } - // Store to the global variable. - $GLOBALS['CSSCRUSH_PROPERTY_SORT_ORDER'] = $table; - } + // Cache the table (and flip it). + $GLOBALS['CSSCRUSH_PROPERTY_SORT_ORDER_CACHE'] = array_flip($table); - // Cache the table (and flip it). - $GLOBALS['CSSCRUSH_PROPERTY_SORT_ORDER_CACHE'] = array_flip($table); + return $GLOBALS['CSSCRUSH_PROPERTY_SORT_ORDER_CACHE']; + } - return $GLOBALS['CSSCRUSH_PROPERTY_SORT_ORDER_CACHE']; } +namespace { -/* - Get the current sorting table. -*/ -function csscrush_get_property_sort_order () { - property_sorter_get_table(); - return $GLOBALS['CSSCRUSH_PROPERTY_SORT_ORDER']; -} + /* + Get the current sorting table. + */ + function csscrush_get_property_sort_order () { + CssCrush\property_sorter_get_table(); + return $GLOBALS['CSSCRUSH_PROPERTY_SORT_ORDER']; + } -/* - Set a custom sorting table. -*/ -function csscrush_set_property_sort_order (array $new_order) { - unset($GLOBALS['CSSCRUSH_PROPERTY_SORT_ORDER_CACHE']); - $GLOBALS['CSSCRUSH_PROPERTY_SORT_ORDER'] = $new_order; + /* + Set a custom sorting table. + */ + function csscrush_set_property_sort_order (array $new_order) { + unset($GLOBALS['CSSCRUSH_PROPERTY_SORT_ORDER_CACHE']); + $GLOBALS['CSSCRUSH_PROPERTY_SORT_ORDER'] = $new_order; + } } diff --git a/plugins/rgba-fallback.php b/plugins/rgba-fallback.php index 7742ff4..c16c17f 100644 --- a/plugins/rgba-fallback.php +++ b/plugins/rgba-fallback.php @@ -12,21 +12,19 @@ * background: rgb(0,0,0); * background: rgba(0,0,0,.5); */ +namespace CssCrush; -CssCrush_Plugin::register('rgba-fallback', array( - 'enable' => 'csscrush__enable_rgba_fallback', - 'disable' => 'csscrush__disable_rgba_fallback', +Plugin::register('rgba-fallback', array( + 'enable' => function () { + Hook::add('rule_postalias', 'CssCrush\rgba_fallback'); + }, + 'disable' => function () { + Hook::remove('rule_postalias', 'CssCrush\rgba_fallback'); + }, )); -function csscrush__enable_rgba_fallback () { - CssCrush_Hook::add('rule_postalias', 'csscrush__rgba_fallback'); -} - -function csscrush__disable_rgba_fallback () { - CssCrush_Hook::remove('rule_postalias', 'csscrush__rgba_fallback'); -} -function csscrush__rgba_fallback (CssCrush_Rule $rule) { +function rgba_fallback (Rule $rule) { $props = array_keys($rule->properties); @@ -43,7 +41,7 @@ function csscrush__rgba_fallback (CssCrush_Rule $rule) { static $rgb_patt; if (! $rgb_patt) { - $rgb_patt = CssCrush_Regex::create('^rgba{{p-token}}$', 'i'); + $rgb_patt = Regex::create('^rgba{{p-token}}$', 'i'); } $new_set = array(); @@ -65,7 +63,7 @@ function csscrush__rgba_fallback (CssCrush_Rule $rule) { list($r, $g, $b, $a) = explode(',', $raw_value); // Add rgb value to the stack, followed by rgba. - $new_set[] = new CssCrush_Declaration($declaration->property, "rgb($r,$g,$b)"); + $new_set[] = new Declaration($declaration->property, "rgb($r,$g,$b)"); $new_set[] = $declaration; } $rule->setDeclarations($new_set); diff --git a/plugins/spiffing.php b/plugins/spiffing.php index 2d72cd1..ea8202b 100644 --- a/plugins/spiffing.php +++ b/plugins/spiffing.php @@ -17,13 +17,14 @@ Plugin::register('spiffing', array( 'enable' => function () { - Hook::add('rule_preprocess', 'spiffing'); + Hook::add('rule_preprocess', 'CssCrush\spiffing'); }, 'disable' => function () { - CssCrush_Hook::remove('rule_preprocess'); + Hook::remove('rule_preprocess', 'CssCrush\spiffing'); }, )); + function spiffing ($rule) { $find = array('colour', 'grey', '!please', 'transparency', 'centre', 'plump', 'photograph', 'capitalise'); diff --git a/plugins/svg-gradients.php b/plugins/svg-gradients.php index 3cc1fd8..9ad6fba 100644 --- a/plugins/svg-gradients.php +++ b/plugins/svg-gradients.php @@ -39,25 +39,23 @@ * background-image: svg-radial-gradient( 100% 50%, rgba(255,255,255,.5), rgba(255,255,255,0) ); * */ - -CssCrush_Plugin::register('svg-gradients', array( - 'enable' => 'csscrush__enable_svg_gradients', - 'disable' => 'csscrush__disable_svg_gradients', +namespace CssCrush; + +Plugin::register('svg-gradients', array( + 'enable' => function () { + Functions::register('svg-linear-gradient', 'CssCrush\fn__svg_linear_gradient'); + Functions::register('svg-radial-gradient', 'CssCrush\fn__svg_radial_gradient'); + }, + 'disable' => function () { + Functions::deRegister('svg-linear-gradient'); + Functions::deRegister('svg-radial-gradient'); + }, )); -function csscrush__enable_svg_gradients () { - CssCrush\Functions::register('svg-linear-gradient', 'csscrush_fn__svg_linear_gradient'); - CssCrush\Functions::register('svg-radial-gradient', 'csscrush_fn__svg_radial_gradient'); -} - -function csscrush__disable_svg_gradients () { - CssCrush\Functions::deRegister('svg-linear-gradient'); - CssCrush\Functions::deRegister('svg-radial-gradient'); -} -function csscrush_fn__svg_linear_gradient ($input) { +function fn__svg_linear_gradient ($input) { - $gradient = csscrush__create_svg_linear_gradient($input); + $gradient = create_svg_linear_gradient($input); $gradient_markup = reset($gradient); $gradient_id = key($gradient); @@ -70,15 +68,15 @@ function csscrush_fn__svg_linear_gradient ($input) { $svg .= ''; // Create data-uri url and return token label. - $url = new CssCrush_Url('data:image/svg+xml;base64,' . base64_encode($svg)); + $url = new Url('data:image/svg+xml;base64,' . base64_encode($svg)); return $url->label; } -function csscrush_fn__svg_radial_gradient ($input) { +function fn__svg_radial_gradient ($input) { - $gradient = csscrush__create_svg_radial_gradient($input); + $gradient = create_svg_radial_gradient($input); $gradient_markup = reset($gradient); $gradient_id = key($gradient); @@ -91,13 +89,13 @@ function csscrush_fn__svg_radial_gradient ($input) { $svg .= ''; // Create data-uri url and return token label. - $url = new CssCrush_Url('data:image/svg+xml;base64,' . base64_encode($svg)); + $url = new Url('data:image/svg+xml;base64,' . base64_encode($svg)); return $url->label; } -function csscrush__create_svg_linear_gradient ($input) { +function create_svg_linear_gradient ($input) { static $angle_keywords, $deg_patt; if (! $angle_keywords) { @@ -117,10 +115,10 @@ function csscrush__create_svg_linear_gradient ($input) { $angle_keywords['to right bottom'] = $angle_keywords['to bottom right']; $angle_keywords['to left bottom'] = $angle_keywords['to bottom left']; - $deg_patt = CssCrush_Regex::create('^{{number}}deg$', 'i'); + $deg_patt = Regex::create('^{{number}}deg$', 'i'); } - $args = CssCrush\Functions::parseArgs($input); + $args = Functions::parseArgs($input); // If no angle argument is passed the default. $angle = 0; @@ -212,7 +210,7 @@ function csscrush__create_svg_linear_gradient ($input) { // - Capture their color values and if specified color offset percentages. // - Only percentages are supported as SVG gradients to accept other length values // for color stop offsets. - $color_stops = csscrush__parse_gradient_color_stops($args); + $color_stops = parse_gradient_color_stops($args); // Create the gradient markup with a unique id. static $uid = 0; @@ -227,7 +225,7 @@ function csscrush__create_svg_linear_gradient ($input) { } -function csscrush__create_svg_radial_gradient ($input) { +function create_svg_radial_gradient ($input) { static $position_keywords, $origin_patt; if (! $position_keywords) { @@ -248,10 +246,10 @@ function csscrush__create_svg_radial_gradient ($input) { $position_keywords['at right bottom'] = $position_keywords['at bottom right']; $position_keywords['at left bottom'] = $position_keywords['at bottom left']; - $origin_patt = CssCrush_Regex::create('^({{number}}%?) +({{number}}%?)$'); + $origin_patt = Regex::create('^({{number}}%?) +({{number}}%?)$'); } - $args = CssCrush\Functions::parseArgs($input); + $args = Functions::parseArgs($input); // Default origin, $position = $position_keywords['at center']; @@ -279,7 +277,7 @@ function csscrush__create_svg_radial_gradient ($input) { // - Capture their color values and if specified color offset percentages. // - Only percentages are supported as SVG gradients to accept other length values // for color stop offsets. - $color_stops = csscrush__parse_gradient_color_stops($args); + $color_stops = parse_gradient_color_stops($args); // Create the gradient markup with a unique id. static $uid = 0; @@ -294,7 +292,7 @@ function csscrush__create_svg_radial_gradient ($input) { } -function csscrush__parse_gradient_color_stops (array $color_stop_args) { +function parse_gradient_color_stops (array $color_stop_args) { $offsets = array(); $colors = array(); @@ -323,7 +321,7 @@ function csscrush__parse_gradient_color_stops (array $color_stop_args) { // For hsla()/rgba() extract alpha component from color values and // convert to hsl()/rgb(). // Webkit doesn't support them for SVG colors. - $colors[] = CssCrush_Color::colorSplit($color); + $colors[] = Color::colorSplit($color); } // For unspecified color offsets fill in the blanks. diff --git a/plugins/svg.php b/plugins/svg.php index 156009e..0ae3b69 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -57,54 +57,51 @@ * https://bugzilla.mozilla.org/show_bug.cgi?id=628747#c0 * */ - -CssCrush_Plugin::register('svg', array( - 'enable' => 'csscrush__enable_svg', - 'disable' => 'csscrush__disable_svg', +namespace CssCrush; + +Plugin::register('svg', array( + 'enable' => function () { + Hook::add('capture_phase2', 'CssCrush\svg_capture'); + Functions::register('svg', 'CssCrush\fn__svg'); + Functions::register('svg-data', 'CssCrush\fn__svg_data'); + }, + 'disable' => function () { + Hook::remove('capture_phase2', 'CssCrush\svg_capture'); + Functions::deRegister('svg'); + Functions::deRegister('svg-data'); + }, )); -function csscrush__enable_svg () { - CssCrush_Hook::add('capture_phase2', 'csscrush__svg_capture'); - CssCrush\Functions::register('svg', 'csscrush_fn__svg'); - CssCrush\Functions::register('svg-data', 'csscrush_fn__svg_data'); -} -function csscrush__disable_svg () { - CssCrush_Hook::remove('capture_phase2', 'csscrush__svg_capture'); - CssCrush\Functions::deRegister('svg'); - CssCrush\Functions::deRegister('svg-data'); -} +function fn__svg ($input) { -function csscrush_fn__svg ($input) { - - return csscrush__svg_generator($input, 'svg'); + return svg_generator($input, 'svg'); } -function csscrush_fn__svg_data ($input) { +function fn__svg_data ($input) { - return csscrush__svg_generator($input, 'svg-data'); + return svg_generator($input, 'svg-data'); } -function csscrush__svg_capture ($process) { +function svg_capture ($process) { - static $callback, $patt; - if (! $callback) { - $patt = CssCrush_Regex::create('@svg \s+ (?{{ident}}) \s* {{block}}', 'ixS'); - $callback = create_function('$m', ' - $name = strtolower($m[\'name\']); - $block = $m[\'block_content\']; - if (! empty($block)) { - CssCrush::$process->misc->svg_defs[$name] = new CssCrush_Template($block); - } - return \'\'; - '); + static $patt; + if (! $patt) { + $patt = Regex::create('@svg \s+ (?{{ident}}) \s* {{block}}', 'ixS'); } // Extract svg definitions. - $process->stream->pregReplaceCallback($patt, $callback); + $process->stream->pregReplaceCallback($patt, function ($m) { + $name = strtolower($m['name']); + $block = $m['block_content']; + if (! empty($block)) { + CssCrush::$process->misc->svg_defs[$name] = new Template($block); + } + return ''; + }); } -function csscrush__svg_generator ($input, $fn_name) { +function svg_generator ($input, $fn_name) { $process = CssCrush::$process; @@ -183,7 +180,7 @@ function csscrush__svg_generator ($input, $fn_name) { ); // Bail if no args. - $args = CssCrush\Functions::parseArgs($input); + $args = Functions::parseArgs($input); if (! isset($args[0])) { return ''; @@ -202,7 +199,7 @@ function csscrush__svg_generator ($input, $fn_name) { $block = $svg_defs[$name]->apply($args); // Parse the block into a keyed assoc array. - $raw_data = array_change_key_case(CssCrush_Rule::parseBlock($block, array('keyed' => true))); + $raw_data = array_change_key_case(Rule::parseBlock($block, array('keyed' => true))); // Resolve the type. // Bail if type not recognised. @@ -242,7 +239,7 @@ function csscrush__svg_generator ($input, $fn_name) { } } - csscrush__svg_apply_css_funcs($element, $raw_data); + svg_apply_css_funcs($element, $raw_data); // Initialize element attributes. $element->attrs = array_intersect_key($raw_data, $schemas[$type]['attrs']); @@ -252,19 +249,19 @@ function csscrush__svg_generator ($input, $fn_name) { $element->styles = array_diff_key($raw_data, $custom_attrs, $schemas[$type]['attrs']); // Pre-populate common attributes. - csscrush__svg_preprocess($element); + svg_preprocess($element); // Filters. - csscrush__svg_apply_filters($element); + svg_apply_filters($element); // Apply element type callback. - call_user_func("csscrush__svg_$type", $element); + call_user_func("CssCrush\svg_$type", $element); // Apply optimizations. - csscrush__svg_compress($element); + svg_compress($element); // Build markup. - $svg = csscrush__svg_render($element); + $svg = svg_render($element); // Debugging... // $code = implode("\n", $svg); @@ -283,15 +280,15 @@ function csscrush__svg_generator ($input, $fn_name) { // Write to the same directory as the output css. $generated_path = $process->output->dir . '/' . $generated_filename; - CssCrush_Util::filePutContents($generated_path, $flattened_svg, __METHOD__); + Util::filePutContents($generated_path, $flattened_svg, __METHOD__); $generated_url = $generated_filename; - $url = new CssCrush_Url($generated_url); + $url = new Url($generated_url); $url->noRewrite = true; } // Or create data uri. else { - $url = new CssCrush_Url('data:image/svg+xml;base64,' . base64_encode(implode('', $svg))); + $url = new Url('data:image/svg+xml;base64,' . base64_encode(implode('', $svg))); } // Cache the output URL. @@ -304,7 +301,7 @@ function csscrush__svg_generator ($input, $fn_name) { /* Circle callback. */ -function csscrush__svg_circle ($element) { +function svg_circle ($element) { // Ensure required attributes have defaults set. $element->data += array( @@ -314,12 +311,12 @@ function csscrush__svg_circle ($element) { list($margin_top, $margin_right, $margin_bottom, $margin_left) = $element->data['margin']; $element->attrs['r'] = - $radius = csscrush__ifset($element->attrs['r'], $element->data['diameter'] / 2); + $radius = svg_ifset($element->attrs['r'], $element->data['diameter'] / 2); $diameter = $radius * 2; - $element->attrs['cx'] = csscrush__ifset($element->attrs['cx'], $margin_left + $radius); - $element->attrs['cy'] = csscrush__ifset($element->attrs['cy'], $margin_top + $radius); + $element->attrs['cx'] = svg_ifset($element->attrs['cx'], $margin_left + $radius); + $element->attrs['cy'] = svg_ifset($element->attrs['cy'], $margin_top + $radius); $element->svg_attrs['width'] = $margin_left + $diameter + $margin_right; $element->svg_attrs['height'] = $margin_top + $diameter + $margin_bottom; @@ -328,7 +325,7 @@ function csscrush__svg_circle ($element) { /* Rect callback. */ -function csscrush__svg_rect ($element) { +function svg_rect ($element) { $element->data += array( 'width' => 50, @@ -343,7 +340,7 @@ function csscrush__svg_rect ($element) { $element->attrs['height'] = $element->data['height']; if (isset($element->data['corner-radius'])) { - $args = csscrush__svg_parselist($element->data['corner-radius']); + $args = svg_parselist($element->data['corner-radius']); $element->attrs['rx'] = isset($args[0]) ? $args[0] : 0; $element->attrs['ry'] = isset($args[1]) ? $args[1] : $args[0]; } @@ -355,14 +352,14 @@ function csscrush__svg_rect ($element) { /* Ellipse callback. */ -function csscrush__svg_ellipse ($element) { +function svg_ellipse ($element) { $element->data += array( 'diameter' => '100 50', ); if (! isset($element->attrs['rx']) && ! isset($element->attrs['ry'])) { - $diameter = csscrush__svg_parselist($element->data['diameter']); + $diameter = svg_parselist($element->data['diameter']); $element->attrs['rx'] = $diameter[0] / 2; $element->attrs['ry'] = isset($diameter[1]) ? $diameter[1] / 2 : $diameter[0] / 2; } @@ -379,7 +376,7 @@ function csscrush__svg_ellipse ($element) { /* Path callback. */ -function csscrush__svg_path ($element) { +function svg_path ($element) { // Ensure minimum required attributes have defaults set. $element->data += array( @@ -395,7 +392,7 @@ function csscrush__svg_path ($element) { /* Polyline callback. */ -function csscrush__svg_polyline ($element) { +function svg_polyline ($element) { // Ensure required attributes have defaults set. $element->data += array( @@ -411,7 +408,7 @@ function csscrush__svg_polyline ($element) { /* Line callback. */ -function csscrush__svg_line ($element) { +function svg_line ($element) { // Set a default stroke. $element->styles += array( @@ -429,7 +426,7 @@ function csscrush__svg_line ($element) { /* Polygon callback. */ -function csscrush__svg_polygon ($element) { +function svg_polygon ($element) { if (! isset($element->attrs['points'])) { @@ -443,7 +440,7 @@ function csscrush__svg_polygon ($element) { list($margin_top, $margin_right, $margin_bottom, $margin_left) = $element->data['margin']; - $diameter = csscrush__svg_parselist($element->data['diameter']); + $diameter = svg_parselist($element->data['diameter']); $diameter = $diameter[0]; $radius = $diameter / 2; @@ -451,7 +448,7 @@ function csscrush__svg_polygon ($element) { $cy = $radius + $margin_top; $sides = $element->data['sides']; - $element->attrs['d'] = csscrush__svg_starpath($cx, $cy, $sides, $radius); + $element->attrs['d'] = svg_starpath($cx, $cy, $sides, $radius); $element->svg_attrs['width'] = $diameter + $margin_left + $margin_right; $element->svg_attrs['height'] = $diameter + $margin_top + $margin_bottom; @@ -461,7 +458,7 @@ function csscrush__svg_polygon ($element) { /* Star callback. */ -function csscrush__svg_star ($element) { +function svg_star ($element) { // Minimum required attributes have defaults. $element->data += array( @@ -472,7 +469,7 @@ function csscrush__svg_star ($element) { list($margin_top, $margin_right, $margin_bottom, $margin_left) = $element->data['margin']; - $diameter = csscrush__svg_parselist($element->data['diameter']); + $diameter = svg_parselist($element->data['diameter']); if (! isset($diameter[1])) { $diameter[1] = ($diameter[0] / 2); } @@ -484,7 +481,7 @@ function csscrush__svg_star ($element) { $points = $element->data['star-points']; $twist = $element->data['twist'] * 10; - $element->attrs['d'] = csscrush__svg_starpath($cx, $cy, $points, $outer_r, $inner_r, $twist); + $element->attrs['d'] = svg_starpath($cx, $cy, $points, $outer_r, $inner_r, $twist); $element->svg_attrs['width'] = $margin_left + ($outer_r * 2) + $margin_left; $element->svg_attrs['height'] = $margin_top + ($outer_r * 2) + $margin_bottom; @@ -494,7 +491,7 @@ function csscrush__svg_star ($element) { Text callback. Warning: Very limited for svg-as-image situations. */ -function csscrush__svg_text ($element) { +function svg_text ($element) { // Minimum required attributes have defaults. $element->data += array( @@ -528,7 +525,7 @@ function csscrush__svg_text ($element) { Adapted from http://svg-whiz.com/svg/StarMaker.svg by Doug Schepers. */ -function csscrush__svg_starpath ($cx, $cy, $points, $outer_r, $inner_r = null, $twist = 0, $orient = 'point') { +function svg_starpath ($cx, $cy, $points, $outer_r, $inner_r = null, $twist = 0, $orient = 'point') { $d = array(); @@ -575,11 +572,11 @@ function csscrush__svg_starpath ($cx, $cy, $points, $outer_r, $inner_r = null, $ return 'M' . implode(' ', $d) . 'Z'; } -function csscrush__svg_apply_filters ($element) { +function svg_apply_filters ($element) { if (isset($element->data['drop-shadow'])) { - $parts = csscrush__svg_parselist($element->data['drop-shadow'], false); + $parts = svg_parselist($element->data['drop-shadow'], false); list($ds_x, $ds_y, $ds_strength, $ds_color) = $parts += array( 2, // x offset. @@ -590,7 +587,7 @@ function csscrush__svg_apply_filters ($element) { // Opacity. $drop_shadow_opacity = null; - if ($color_components = CssCrush_Color::colorSplit($ds_color)) { + if ($color_components = Color::colorSplit($ds_color)) { list($ds_color, $drop_shadow_opacity) = $color_components; } @@ -615,13 +612,13 @@ function csscrush__svg_apply_filters ($element) { } } -function csscrush__svg_preprocess ($element) { +function svg_preprocess ($element) { if (isset($element->data['margin'])) { $margin =& $element->data['margin']; - $parts = csscrush__svg_parselist($margin); + $parts = svg_parselist($margin); $count = count($parts); if ($count === 1) { $margin = array($parts[0], $parts[0], $parts[0], $parts[0]); @@ -647,7 +644,7 @@ function csscrush__svg_preprocess ($element) { $value = $element->attrs[$point_data_attr]; - if (CssCrush_Tokens::is($value, 's')) { + if (Tokens::is($value, 's')) { $element->attrs[$point_data_attr] = trim(CssCrush::$process->tokens->get($value), '"\'');; } @@ -662,37 +659,37 @@ function csscrush__svg_preprocess ($element) { } } -function csscrush__svg_apply_css_funcs ($element, &$raw_data) { +function svg_apply_css_funcs ($element, &$raw_data) { // Setup functions for using on values. // Note using custom versions of svg-*-gradient(). static $generic_functions_patt, $fill_functions, $fill_functions_patt; if (! $generic_functions_patt) { $fill_functions = array( - 'svg-linear-gradient' => 'csscrush__svg_fn_linear_gradient', - 'svg-radial-gradient' => 'csscrush__svg_fn_radial_gradient', - 'pattern' => 'csscrush__svg_fn_pattern', + 'svg-linear-gradient' => 'CssCrush\svg_fn_linear_gradient', + 'svg-radial-gradient' => 'CssCrush\svg_fn_radial_gradient', + 'pattern' => 'CssCrush\svg_fn_pattern', ); $generic_functions = - array_diff_key(CssCrush\Functions::$functions, $fill_functions); - $generic_functions_patt = CssCrush_Regex::createFunctionPatt( + array_diff_key(Functions::$functions, $fill_functions); + $generic_functions_patt = Regex::createFunctionPatt( array_keys($generic_functions), array('bare_paren' => true)); - $fill_functions_patt = CssCrush_Regex::createFunctionPatt( + $fill_functions_patt = Regex::createFunctionPatt( array_keys($fill_functions)); } foreach ($raw_data as $property => &$value) { - CssCrush\Functions::executeOnString($value, $generic_functions_patt); + Functions::executeOnString($value, $generic_functions_patt); // Only capturing fills for fill and stoke properties. if ($property === 'fill' || $property === 'stroke') { - CssCrush\Functions::executeOnString( + Functions::executeOnString( $value, $fill_functions_patt, $fill_functions, $element); // If the value is a color with alpha component we split the color // and set the corresponding *-opacity property because Webkit doesn't // support rgba()/hsla() in SVG. - if ($components = CssCrush_Color::colorSplit($value)) { + if ($components = Color::colorSplit($value)) { list($color, $opacity) = $components; $raw_data[$property] = $color; if ($opacity < 1) { @@ -703,19 +700,21 @@ function csscrush__svg_apply_css_funcs ($element, &$raw_data) { } } -function csscrush__svg_compress ($element) { +function svg_compress ($element) { foreach ($element->attrs as $key => &$value) { // Compress numbers on data attributes. if (in_array($key, array('points', 'd'))) { $value = preg_replace_callback( - CssCrush_Regex::$patt->number, 'csscrush__svg_number_compress', $value); + Regex::$patt->number, + function ($m) { return round($m[0], 2); }, + $value); } } } -function csscrush__svg_render ($element) { +function svg_render ($element) { // Flatten styles. $styles = ''; @@ -736,8 +735,8 @@ function csscrush__svg_render ($element) { $styles = CssCrush::$process->tokens->restore($styles, 'u', true); $styles = CssCrush::$process->tokens->restore($styles, 's'); - $attrs = CssCrush_Util::htmlAttributes($element->attrs); - $svg_attrs = CssCrush_Util::htmlAttributes($element->svg_attrs); + $attrs = Util::htmlAttributes($element->attrs); + $svg_attrs = Util::htmlAttributes($element->svg_attrs); // Markup. $svg[] = ""; @@ -769,36 +768,36 @@ function csscrush__svg_render ($element) { /* Custom versions of svg-*-gradient() for integrating. */ -function csscrush__svg_fn_linear_gradient ($input, $element) { +function svg_fn_linear_gradient ($input, $element) { // Relies on functions from svg-gradients plugin. - CssCrush_Plugin::load('svg-gradients'); + Plugin::load('svg-gradients'); - $generated_gradient = csscrush__create_svg_linear_gradient($input); + $generated_gradient = create_svg_linear_gradient($input); $element->fills['gradients'][] = reset($generated_gradient); return 'url(#' . key($generated_gradient) . ')'; } -function csscrush__svg_fn_radial_gradient ($input, $element) { +function svg_fn_radial_gradient ($input, $element) { // Relies on functions from svg-gradients plugin. - CssCrush_Plugin::load('svg-gradients'); + Plugin::load('svg-gradients'); - $generated_gradient = csscrush__create_svg_radial_gradient($input); + $generated_gradient = create_svg_radial_gradient($input); $element->fills['gradients'][] = reset($generated_gradient); return 'url(#' . key($generated_gradient) . ')'; } -function csscrush__svg_fn_pattern ($input, $element) { +function svg_fn_pattern ($input, $element) { static $uid = 0; $pid = 'p' . (++$uid); // Get args in order with defaults. list($url, $transform_list, $width, $height, $x, $y) = - CssCrush\Functions::parseArgs($input) + + Functions::parseArgs($input) + array('', '', 0, 0, 0, 0); $url = CssCrush::$process->tokens->get($url); @@ -832,16 +831,12 @@ function csscrush__svg_fn_pattern ($input, $element) { /* Helpers. */ -function csscrush__svg_parselist ($str, $numbers = true) { +function svg_parselist ($str, $numbers = true) { $list = preg_split('~ +~', trim($str)); return $numbers ? array_map('floatval', $list) : $list; } -function csscrush__svg_number_compress ($m) { - return round($m[0], 2); -} - -function csscrush__ifset (&$var, $fallback = null) { +function svg_ifset (&$var, $fallback = null) { if (isset($var)) { return $var; } From 98ff6d87e0d16c6d6372f555619243ff30c55bc5 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 10 Aug 2013 22:02:40 +0100 Subject: [PATCH 151/421] Trying alternative method of storing trace tokens. --- lib/CssCrush/Importer.php | 4 ++-- lib/CssCrush/Process.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index a78e9b9..baf1134 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -269,10 +269,10 @@ static protected function addMarkers (&$str) $point_data[] = strlen($before) - strrpos($before, "\n") - 1; } - // Splice in marker token. + // Splice in marker token (packing point_data into string is more memory efficient). $str = substr_replace( $str, - $process->tokens->add($point_data, 't'), + $process->tokens->add(implode(',', $point_data), 't'), $selector_offset, 0); } diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 3554e12..aa5bb16 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -1005,7 +1005,7 @@ public function generateSourceMap () if (isset($tokens->{$token_type}[$token])) { - list($src_file, $src_line, $src_col) = $tokens->{$token_type}[$token]; + list($src_file, $src_line, $src_col) = explode(',', $tokens->{$token_type}[$token]); $line_segments[] = Util::vlqEncode($dest_col - $previous_dest_col) . Util::vlqEncode($src_file - $previous_src_file) . @@ -1034,7 +1034,7 @@ public function generateTracingStub ($m) if (! isset($tokens[$token])) { return ''; } - list($source_index, $line) = $tokens[$token]; + list($source_index, $line) = explode(',', $tokens[$token]); $line += 1; // Get the currently processed file path, and escape it. From b0d419b3c64ad4c411847b224487ef1cfd834f59 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 12 Aug 2013 12:06:21 +0100 Subject: [PATCH 152/421] Typo in composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index d4c0b64..8e1b0bc 100644 --- a/composer.json +++ b/composer.json @@ -24,5 +24,5 @@ "autoload": { "psr-0": { "CssCrush": "lib/" }, "files": [ "lib/functions.php" ] - }, + } } \ No newline at end of file From 9360e1caa9b9b57055b6a4057d098ed502bd88bb Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 12 Aug 2013 16:47:37 +0100 Subject: [PATCH 153/421] Some small changes for custom formatters. Added compile_time tag to boilerplate. --- Aliases.ini | 4 ++-- composer.json | 2 +- lib/CssCrush/Options.php | 2 +- lib/CssCrush/Process.php | 28 +++++++++++++++------------- misc/formatters.php | 2 +- 5 files changed, 20 insertions(+), 18 deletions(-) diff --git a/Aliases.ini b/Aliases.ini index 8fbcb37..6a48286 100644 --- a/Aliases.ini +++ b/Aliases.ini @@ -298,8 +298,8 @@ min-width:fit-content[] = min-width:-moz-fit-content ; Appearance (non-standard). - appearance:none[] = appearance:-webkit-appearance - appearance:none[] = appearance:-moz-appearance + appearance:none[] = -webkit-appearance:none + appearance:none[] = -moz-appearance:none ;---------------------------------------------------------------- diff --git a/composer.json b/composer.json index 8e1b0bc..d2f9e32 100644 --- a/composer.json +++ b/composer.json @@ -25,4 +25,4 @@ "psr-0": { "CssCrush": "lib/" }, "files": [ "lib/functions.php" ] } -} \ No newline at end of file +} diff --git a/lib/CssCrush/Options.php b/lib/CssCrush/Options.php index 25cacee..aa12cba 100644 --- a/lib/CssCrush/Options.php +++ b/lib/CssCrush/Options.php @@ -41,7 +41,7 @@ public function __set ($name, $value) // Resolve a formatter callback name and check it's callable. case 'formatter': - if (isset(CssCrush::$config->formatters[$value])) { + if (is_string($value) && isset(CssCrush::$config->formatters[$value])) { $value = CssCrush::$config->formatters[$value]; } if (! is_callable($value)) { diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index aa5bb16..d0a995d 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -73,7 +73,6 @@ public function release () { unset( $this->tokens, - $this->vars, $this->mixins, $this->references, $this->misc, @@ -137,8 +136,7 @@ protected function getBoilerplate () $boilerplate_option = $this->options->boilerplate; if ($boilerplate_option === true) { - $file = Util::find( - 'CssCrush-local.boilerplate', 'CssCrush.boilerplate'); + $file = Util::find('CssCrush-local.boilerplate', 'CssCrush.boilerplate'); } elseif (is_string($boilerplate_option)) { if (file_exists($boilerplate_option)) { @@ -156,30 +154,31 @@ protected function getBoilerplate () // Substitute any tags if (preg_match_all('~\{\{([^}]+)\}\}~', $boilerplate, $boilerplate_matches)) { - $command = 'n/a'; + // Command line arguments (if any). + $command_args = 'n/a'; if (isset($_SERVER['argv'])) { $argv = $_SERVER['argv']; array_shift($argv); - $command = 'csscrush ' . implode(' ', $argv); + $command_args = 'csscrush ' . implode(' ', $argv); } $tags = array( 'datetime' => @date('Y-m-d H:i:s O'), 'year' => @date('Y'), - 'version' => 'v' . CssCrush::$config->version, - - // Command line arguments (if any). - 'command' => $command, - - // Enabled plugins. + 'version' => 'v' . csscrush_version(), + 'command' => $command_args, 'plugins' => implode(',', array_keys($this->plugins)), + 'compile_time' => function () { + $now = microtime(true) - CssCrush::$process->stat['compile_start_time']; + return round($now, 4) . ' seconds'; + }, ); foreach ($boilerplate_matches[0] as $index => $tag) { $tag_name = $boilerplate_matches[1][$index]; $replacement = '?'; if (isset($tags[$tag_name])) { - $replacement = $tags[$tag_name]; + $replacement = is_callable($tags[$tag_name]) ? $tags[$tag_name]() : $tags[$tag_name]; } $replacements[] = $replacement; } @@ -819,6 +818,9 @@ protected function collate () // Trim leading spaces on @-rules and some tokens. $regex_replacements[Regex::create(' +([@}]|\?[rc]{{token-id}}\?)', 'S')] = "$1"; + + // Additional newline between adjacent rules and comments. + $regex_replacements[Regex::create('({{r-token}}) (\s*) ({{c-token}})', 'xS')] = "$1$EOL$2$3"; } // Apply all formatting replacements. @@ -851,7 +853,7 @@ protected function collate () // Add newlines after comments. foreach ($this->tokens->store->c as $token => &$comment) { - $comment .= "$EOL$EOL"; + $comment .= $EOL; } // Insert comments and do final whitespace cleanup. diff --git a/misc/formatters.php b/misc/formatters.php index 3e87135..c93fc94 100644 --- a/misc/formatters.php +++ b/misc/formatters.php @@ -44,5 +44,5 @@ function fmtr_block ($rule, $indent = ' ') { $selectors = implode(",$EOL", $rule->selectors); $block = implode(";$EOL$indent", $rule->declarations); - return "$selectors {{$EOL}$indent$block;$EOL$indent}$EOL$EOL"; + return "$selectors {{$EOL}$indent$block;$EOL$indent}$EOL"; } From 19453d6d1735d5f2136fd291e5f1b18c04cfd672 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 12 Aug 2013 17:00:22 +0100 Subject: [PATCH 154/421] Swapping instances of dirname(__FILE__) for __DIR__ --- lib/CssCrush/CssCrush.php | 4 ++-- lib/CssCrush/IO.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/CssCrush/CssCrush.php b/lib/CssCrush/CssCrush.php index adb129f..01dedf5 100644 --- a/lib/CssCrush/CssCrush.php +++ b/lib/CssCrush/CssCrush.php @@ -22,7 +22,7 @@ static public function init () self::$config = new \stdClass(); // Path to the project root folder. - self::$config->location = dirname(dirname(dirname(__FILE__))); + self::$config->location = dirname(dirname(__DIR__)); // Plugin directories. self::$config->plugin_dirs = array(self::$config->location . '/plugins'); @@ -279,7 +279,7 @@ static public function file ($file, $options = null) } // Relative path. else { - $pathtest = $process->setContext(dirname(dirname(__FILE__) . '/' . $file)); + $pathtest = $process->setContext(dirname(__DIR__ . '/' . $file)); } if (! $pathtest) { diff --git a/lib/CssCrush/IO.php b/lib/CssCrush/IO.php index d0751b1..c0a6144 100644 --- a/lib/CssCrush/IO.php +++ b/lib/CssCrush/IO.php @@ -165,7 +165,7 @@ static public function validateExistingOutput () static public function clearCache ($dir) { if (empty($dir)) { - $dir = dirname(__FILE__); + $dir = __DIR__; } else if (! file_exists($dir)) { return; From bd9e7de5a21a9a5f333411ec87510b83426941c8 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 13 Aug 2013 11:50:13 +0100 Subject: [PATCH 155/421] Added ARIA plugin for working with aria roles states and properties. Plugins can now add selector aliases as callables. --- lib/CssCrush/CssCrush.php | 2 +- lib/CssCrush/Process.php | 11 +++-- plugins/aria.php | 93 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 5 deletions(-) create mode 100644 plugins/aria.php diff --git a/lib/CssCrush/CssCrush.php b/lib/CssCrush/CssCrush.php index 01dedf5..3412e65 100644 --- a/lib/CssCrush/CssCrush.php +++ b/lib/CssCrush/CssCrush.php @@ -505,7 +505,7 @@ static public function stat () static public function addSelectorAlias ($name, $body) { - CssCrush::$config->selectorAliases[$name] = new Template($body); + CssCrush::$config->selectorAliases[$name] = is_callable($body) ? $body : new Template($body); } static public function removeSelectorAlias ($name) diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index d0a995d..0df6692 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -207,9 +207,7 @@ protected function resolveSelectorAliases () $this->stream->pregReplaceCallback($alias_patt, function ($m) { $name = strtolower($m[1]); - $body = Util::stripCommentTokens($m[2]); - $template = new Template($body); - CssCrush::$process->selectorAliases[$name] = $template; + CssCrush::$process->selectorAliases[$name] = new Template(Util::stripCommentTokens($m[2])); }); // Merge with global selector aliases. @@ -246,7 +244,6 @@ static public function applySelectorAliases (&$str) continue; } - $template = $table[$selector_alias_name]; $start = $selector_alias_call[0][1]; $length = strlen($selector_alias_call[0][0]); $args = array(); @@ -266,6 +263,12 @@ static public function applySelectorAliases (&$str) $length = ($paren_start + $paren_len) - $start; } + // Resolve the selector alias value to a template instance if a callable is given. + $template = $table[$selector_alias_name]; + if (is_callable($template)) { + $template = new Template($template($args)); + } + // Splice in the result. $str = substr_replace($str, $template->apply($args), $start, $length); } diff --git a/plugins/aria.php b/plugins/aria.php new file mode 100644 index 0000000..3392e97 --- /dev/null +++ b/plugins/aria.php @@ -0,0 +1,93 @@ + function () { + foreach (aria() as $name => $value) { + CssCrush::addSelectorAlias($name, $value); + } + }, + 'disable' => function () { + foreach (aria() as $name => $value) { + CssCrush::removeSelectorAlias($name); + } + }, +)); + + +function aria () { + + static $aria, $optional_value; + if (! $aria) { + $optional_value = function ($property) { + return function ($args) use ($property) { + return $args ? "[$property=\"#(0)\"]" : "[$property]"; + }; + }; + $aria = array( + + // Roles. + 'role' => '[role="#(0)"]', + + // States and properties. + 'aria-activedescendant' => $optional_value('aria-activedescendant'), + 'aria-atomic' => '[aria-atomic="#(0 true)"]', + 'aria-autocomplete' => $optional_value('aria-autocomplete'), + 'aria-busy' => '[aria-busy="#(0 true)"]', + 'aria-checked' => '[aria-checked="#(0 true)"]', + 'aria-controls' => $optional_value('aria-controls'), + 'aria-describedby' => $optional_value('aria-describedby'), + 'aria-disabled' => '[aria-disabled="#(0 true)"]', + 'aria-dropeffect' => $optional_value('aria-dropeffect'), + 'aria-expanded' => '[aria-expanded="#(0 true)"]', + 'aria-flowto' => $optional_value('aria-flowto'), + 'aria-grabbed' => '[aria-grabbed="#(0 true)"]', + 'aria-haspopup' => '[aria-haspopup="#(0 true)"]', + 'aria-hidden' => '[aria-hidden="#(0 true)"]', + 'aria-invalid' => '[aria-invalid="#(0 true)"]', + 'aria-label' => $optional_value('aria-label'), + 'aria-labelledby' => $optional_value('aria-labelledby'), + 'aria-level' => $optional_value('aria-level'), + 'aria-live' => $optional_value('aria-live'), + 'aria-multiline' => '[aria-multiline="#(0 true)"]', + 'aria-multiselectable' => '[aria-multiselectable="#(0 true)"]', + 'aria-orientation' => $optional_value('aria-orientation'), + 'aria-owns' => $optional_value('aria-owns'), + 'aria-posinset' => $optional_value('aria-posinset'), + 'aria-pressed' => '[aria-pressed="#(0 true)"]', + 'aria-readonly' => '[aria-readonly="#(0 true)"]', + 'aria-relevant' => $optional_value('aria-relevant'), + 'aria-required' => '[aria-required="#(0 true)"]', + 'aria-selected' => '[aria-selected="#(0 true)"]', + 'aria-setsize' => $optional_value('aria-setsize'), + 'aria-sort' => $optional_value('aria-sort'), + 'aria-valuemax' => $optional_value('aria-valuemax'), + 'aria-valuemin' => $optional_value('aria-valuemin'), + 'aria-valuenow' => $optional_value('aria-valuenow'), + 'aria-valuetext' => $optional_value('aria-valuetext'), + ); + } + + return $aria; +} From f859baf1c1cde6a7eb8b2bed9e0b17750c37dc6a Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 13 Aug 2013 17:12:12 +0100 Subject: [PATCH 156/421] Added forms plugin: pseudo classes for working with forms. --- plugins/aria.php | 1 - plugins/forms.php | 48 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 plugins/forms.php diff --git a/plugins/aria.php b/plugins/aria.php index 3392e97..af251fc 100644 --- a/plugins/aria.php +++ b/plugins/aria.php @@ -18,7 +18,6 @@ * [aria-expanded="false"] {...} * [aria-label] {...} * [aria-label="foobarbaz"] {...} - * */ namespace CssCrush; diff --git a/plugins/forms.php b/plugins/forms.php new file mode 100644 index 0000000..9d1ebfc --- /dev/null +++ b/plugins/forms.php @@ -0,0 +1,48 @@ + function () { + foreach (forms() as $name => $value) { + CssCrush::addSelectorAlias($name, $value); + } + }, + 'disable' => function () { + foreach (forms() as $name => $value) { + CssCrush::removeSelectorAlias($name); + } + }, +)); + + +function forms () { + return array( + 'input' => 'input[type="#(0 text)"]', + 'button' => ':any(button, input[type="button"])', + 'checkbox' => 'input[type="checkbox"]', + 'file' => 'input[type="file"]', + 'image' => 'input[type="image"]', + 'password' => 'input[type="password"]', + 'submit' => 'input[type="submit"]', + 'radio' => 'input[type="radio"]', + 'reset' => 'input[type="reset"]', + 'text' => 'input[type="text"]', + ); +} From 3d09188cf66e0ecb452da5b7a19e3cf192897293 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 15 Aug 2013 10:02:29 +0100 Subject: [PATCH 157/421] Refactoring for more consistent mixin and referencing behaviour. Referencing and mixins are now defered to a later stage of execution where possible so clearing up some edge cases. --- lib/CssCrush/IO.php | 14 ++--- lib/CssCrush/Mixin.php | 121 ++++++++++++++++++++------------------ lib/CssCrush/Process.php | 30 +++++++--- lib/CssCrush/Regex.php | 14 +---- lib/CssCrush/Rule.php | 99 ++++++++++++++++--------------- lib/CssCrush/Selector.php | 2 +- lib/CssCrush/Stream.php | 4 +- plugins/canvas.php | 5 +- plugins/svg.php | 5 +- 9 files changed, 159 insertions(+), 135 deletions(-) diff --git a/lib/CssCrush/IO.php b/lib/CssCrush/IO.php index c0a6144..6950410 100644 --- a/lib/CssCrush/IO.php +++ b/lib/CssCrush/IO.php @@ -238,7 +238,9 @@ static public function saveCacheData () $process = CssCrush::$process; CssCrush::log('Saving config.'); - Util::filePutContents($process->cacheFile, json_encode($process->cacheData), __METHOD__); + + $flags = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 0; + Util::filePutContents($process->cacheFile, json_encode($process->cacheData, $flags), __METHOD__); } static public function write (Stream $stream) @@ -254,13 +256,9 @@ static public function write (Stream $stream) if (Util::filePutContents($target, $stream, __METHOD__)) { if ($process->sourceMap) { - if (defined('JSON_PRETTY_PRINT')) { - $data = json_encode($process->sourceMap, JSON_PRETTY_PRINT); - } - else { - $data = json_encode($process->sourceMap); - } - Util::filePutContents("{$process->output->dir}/$source_map_filename", $data, __METHOD__); + $flags = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 0; + Util::filePutContents("{$process->output->dir}/$source_map_filename", + json_encode($process->sourceMap, $flags), __METHOD__); } return "{$process->output->dirUrl}/{$process->output->filename}"; diff --git a/lib/CssCrush/Mixin.php b/lib/CssCrush/Mixin.php index 4368568..834c2b7 100644 --- a/lib/CssCrush/Mixin.php +++ b/lib/CssCrush/Mixin.php @@ -15,98 +15,107 @@ public function __construct ($block) $this->template = new Template($block); } - public function call ( array $args ) + static public function call ($message, $context = null) { - return Rule::parseBlock($this->template->apply($args)); - } - - static public function parseSingleValue ($message) - { - $message = ltrim($message); - $mixin = null; - $non_mixin = null; + static $message_patt; + if (! $message_patt) { + $message_patt = Regex::create('^(?{{ident}}) {{parens}}?', 'xS'); + } - // e.g. - // - named-mixin( 50px, rgba(0,0,0,0), left 100% ) - // - abstract-rule - // - #foo + $process = CssCrush::$process; + $mixable = null; + $message = trim($message); - // Test for leading name - if (preg_match('~^[\w-]+~', $message, $name_match)) { + // Test for mixin or abstract rule. e.g: + // named-mixin( 50px, rgba(0,0,0,0), left 100% ) + // abstract-rule + if (preg_match($message_patt, $message, $message_match)) { - $name = $name_match[0]; + $name = $message_match['name']; - if (isset(CssCrush::$process->mixins[$name])) { + if (isset($process->mixins[$name])) { - // Mixin match - $mixin = CssCrush::$process->mixins[$name]; + $mixable = $process->mixins[$name]; } - elseif (isset(CssCrush::$process->references[$name])) { + elseif (isset($process->references[$name])) { - // Abstract rule match - $non_mixin = CssCrush::$process->references[$name]; + $mixable = $process->references[$name]; } } // If no mixin or abstract rule matched, look for matching selector - if (! $mixin && ! $non_mixin) { + if (! $mixable) { $selector_test = Selector::makeReadable($message); - if (isset(CssCrush::$process->references[$selector_test])) { - $non_mixin = CssCrush::$process->references[$selector_test]; + if (isset($process->references[$selector_test])) { + $mixable = $process->references[$selector_test]; } } - // If no mixin matched, but matched alternative, use alternative - if (! $mixin) { + // Avoid infinite recursion. + if (! $mixable || $mixable === $context) { - if ($non_mixin) { + return false; + } + elseif ($mixable instanceof Mixin) { - // Return expected format - $result = array(); - foreach ($non_mixin as $declaration) { + $args = array(); + $raw_args = isset($message_match['parens_content']) ? trim($message_match['parens_content']) : null; + if ($raw_args) { + $args = Util::splitDelimList($raw_args); + } + + return Rule::parseBlock($mixable->template->apply($args), array( + 'flatten' => true, + 'context' => $mixable, + )); + } + elseif ($mixable instanceof Rule) { + + $result = array(); + foreach ($mixable as $declaration) { + if ($declaration instanceof Declaration) { $result[] = array( $declaration->property, $declaration->value, ); } - - return $result; + else { + $result[] = $declaration; + } } - // Nothing matches - return false; + return $result; } + } - // We have a valid mixin. - // Discard the name part and any wrapping parens and whitespace - $message = substr($message, strlen($name)); - $message = preg_replace('~^\s*\(?\s*|\s*\)?\s*$~', '', $message); - - // e.g. "value, rgba(0,0,0,0), left 100%" + static public function merge (array $input, $message_list, $options = array()) + { + $context = isset($options['context']) ? $options['context'] : null; - // Determine what raw arguments there are to pass to the mixin - $args = array(); - if ($message !== '') { - $args = Util::splitDelimList($message); + $mixables = array(); + foreach (Util::splitDelimList($message_list) as $message) { + if ($result = self::call($message, $context)) { + $mixables = array_merge($mixables, $result); + } } - return $mixin->call($args); - } - - static public function parseValue ($message) - { - // Call the mixin and return the list of declarations - $declarations = array(); + while ($pair = array_shift($mixables)) { - foreach (Util::splitDelimList($message) as $item) { + list($property, $value) = $pair; - if ($result = self::parseSingleValue($item)) { - $declarations = array_merge($declarations, $result); + if ($property === 'mixin') { + $input = Mixin::merge($input, $value, $options); + } + elseif (! empty($options['keyed'])) { + $input[$property] = $value; + } + else { + $input[] = array($property, $value); } } - return $declarations; + return $input; } } diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 0df6692..ae9d235 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -427,12 +427,15 @@ protected function captureVars () CssCrush::$process->vars = array_merge( CssCrush::$process->vars, - Rule::parseBlock($m['block_content'], array('keyed' => true, 'ignore_directives' => true)) + Rule::parseBlock($m['block_content'], array( + 'keyed' => true, + 'ignore_directives' => true, + )) ); }); // In-file variables override global variables. - $this->vars = array_merge($config->vars, $this->vars); + $this->vars += $config->vars; // Runtime variables override in-file variables. if (! empty($option_vars)) { @@ -609,21 +612,35 @@ public function captureRules () protected function processRules () { - $aliases =& $this->aliases; + // Create table of name/selector to rule references. + $named_references = array(); + foreach ($this->tokens->store->r as $rule) { + if ($rule->name) { + $named_references[$rule->name] = $rule; + } + foreach ($rule->selectors as $selector) { + $this->references[$selector->readableValue] = $rule; + } + } + + // Explicit named references take precedence. + $this->references = $named_references + $this->references; foreach ($this->tokens->store->r as $rule) { + $rule->flatten(); + $rule->processDeclarations(); Hook::run('rule_prealias', $rule); - if ($aliases['properties']) { + if ($this->aliases['properties']) { $rule->addPropertyAliases(); } - if ($aliases['functions']) { + if ($this->aliases['functions']) { $rule->addFunctionAliases(); } - if ($aliases['declarations']) { + if ($this->aliases['declarations']) { $rule->addDeclarationAliases(); } Hook::run('rule_postalias', $rule); @@ -961,7 +978,6 @@ public function compile ($io_context = 'file') $this->aliasAtRules(); - // Main processing on the rule objects. $this->processRules(); $this->collate(); diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php index 299c506..25440e0 100644 --- a/lib/CssCrush/Regex.php +++ b/lib/CssCrush/Regex.php @@ -132,13 +132,8 @@ static public function create ($pattern_template, $flags = '', $delim = '~') return "$delim{$pattern}$delim{$flags}"; } - static public function matchAll ($patt, $subject, $preprocess_patt = false, $offset = 0) + static public function matchAll ($patt, $subject, $offset = 0) { - if ($preprocess_patt) { - // Assume case-insensitive. - $patt = self::create($patt, 'i'); - } - $count = preg_match_all($patt, $subject, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER, $offset); return $count ? $matches : array(); @@ -154,18 +149,13 @@ static public function createFunctionPatt ($list, $options = array()) $list[] = '-'; } - // Escape function names. - foreach ($list as &$fn_name) { - $fn_name = preg_quote($fn_name); - } - // Templating func. $template = ''; if (! empty($options['templating'])) { $template = '#|'; } - $flat_list = implode('|', $list); + $flat_list = implode('|', array_map('preg_quote', $list)); return Regex::create("($template{{LB}}(?:$flat_list)$question)\(", 'iS'); } diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php index ab04927..f48278a 100644 --- a/lib/CssCrush/Rule.php +++ b/lib/CssCrush/Rule.php @@ -11,6 +11,9 @@ class Rule implements \IteratorAggregate public $vendorContext; public $label; public $marker; + public $name; + public $isAbstract; + public $isFlat = true; public $selectors = array(); public $extendSelectors = array(); @@ -31,7 +34,6 @@ class Rule implements \IteratorAggregate public function __construct ($selector_string, $declarations_string, $trace_token = null) { - $regex = Regex::$patt; $process = CssCrush::$process; $this->label = $process->tokens->createLabel('r'); $this->marker = $process->addTracingStubs || $process->generateMap ? $trace_token : null; @@ -53,11 +55,9 @@ public function __construct ($selector_string, $declarations_string, $trace_toke foreach (Util::splitDelimList($selector_string) as $selector) { - // If the selector matches an absract directive - if (preg_match($regex->abstract, $selector, $m)) { - - // Link the rule to the abstract name and skip forward to declaration parsing. - $process->references[strtolower($m['name'])] = $this; + if (preg_match(Regex::$patt->abstract, $selector, $m)) { + $this->name = strtolower($m['name']); + $this->isAbstract = true; } else { $this->addSelector(new Selector($selector)); @@ -79,8 +79,9 @@ public function __construct ($selector_string, $declarations_string, $trace_toke } elseif ($prop === 'name') { - // Link the rule as a reference. - $process->references[$value] = $this; + if (! $this->name) { + $this->name = $value; + } unset($pairs[$index]); } } @@ -92,14 +93,18 @@ public function __construct ($selector_string, $declarations_string, $trace_toke if (trim($value) !== '') { - // Only store to $this->selfData if the value does not itself make a - // this() call to avoid circular references. - if (! preg_match($regex->thisFunction, $value)) { - $this->selfData[strtolower($prop)] = $value; + if ($prop === 'mixin') { + $this->isFlat = false; + $this->declarations[] = $pair; + } + else { + // Only store to $this->selfData if the value does not itself make a + // this() call to avoid circular references. + if (! preg_match(Regex::$patt->thisFunction, $value)) { + $this->selfData[strtolower($prop)] = $value; + } + $this->addDeclaration($prop, $value, $index); } - - // Add declaration. - $this->addDeclaration($prop, $value, $index); } } } @@ -161,6 +166,30 @@ public function processDeclarations () $this->declarationsProcessed = true; } + public function flatten () + { + if ($this->isFlat) { + return; + } + + // Flatten mixins. + $new_set = array(); + foreach ($this->declarations as $declaration) { + if (is_array($declaration) && $declaration[0] === 'mixin') { + foreach (Mixin::merge(array(), $declaration[1], array('context' => $this)) as $pair) { + $new_set[] = new Declaration($pair[0], $pair[1], count($new_set)); + } + } + else { + $declaration->index = count($new_set); + $new_set[] = $declaration; + } + } + + $this->setDeclarations($new_set); + $this->isFlat = true; + } + public function expandDataSet ($dataset, $property) { // Expand shorthand properties to make them available @@ -434,14 +463,6 @@ public function expandSelectors () public function addSelector ($selector) { $this->selectors[$selector->readableValue] = $selector; - - // Link reference. - CssCrush::$process->references[$selector->readableValue] = $this; - } - - public function addSelectors ($list, $extend_selectors = false) - { - $this->selectors += $list; } @@ -766,17 +787,15 @@ public function setDeclarations (array $declaration_stack) static public function parseBlock ($str, $options = array()) { - $regex = Regex::$patt; $str = Util::stripCommentTokens($str); - $lines = preg_split('~\s*;\s*~', $str, null, PREG_SPLIT_NO_EMPTY); - $keyed = isset($options['keyed']); - $directives = ! isset($options['ignore_directives']); + $keyed = ! empty($options['keyed']); + $directives = empty($options['ignore_directives']); $out = array(); foreach ($lines as $line) { - if ($directives && preg_match($regex->ruleDirective, $line, $m)) { + if ($directives && preg_match(Regex::$patt->ruleDirective, $line, $m)) { if (! empty($m[1])) { $property = 'mixin'; @@ -792,35 +811,21 @@ static public function parseBlock ($str, $options = array()) elseif (($colon_pos = strpos($line, ':')) !== false) { $property = trim(substr($line, 0, $colon_pos)); - - // Extract the value part of the declaration. $value = trim(substr($line, $colon_pos + 1)); } else { continue; } - // Empty strings are ignored. if (! isset($property[0]) || ! isset($value[0])) { continue; } - // Add any mixins. - if ($property === 'mixin') { - - if ($mixables = Mixin::parseValue($value)) { - - // Add mixin declarations to the stack. - while ($mixable = array_shift($mixables)) { - list($mix_prop, $mix_val) = $mixable; - if ($keyed) { - $out[$mix_prop] = $mix_val; - } - else { - $out[] = array($mix_prop, $mix_val); - } - } - } + if ($property === 'mixin' && ! empty($options['flatten'])) { + $out = Mixin::merge($out, $value, array( + 'keyed' => $keyed, + 'context' => isset($options['context']) ? $options['context'] : null, + )); } elseif ($keyed) { $out[$property] = $value; diff --git a/lib/CssCrush/Selector.php b/lib/CssCrush/Selector.php index 5af56ff..89daa89 100644 --- a/lib/CssCrush/Selector.php +++ b/lib/CssCrush/Selector.php @@ -39,7 +39,7 @@ public function __toString () public function appendPseudo ($pseudo) { - // Check to avoid doubling-up + // Check to avoid doubling-up. if (! Stream::endsWith($this->readableValue, $pseudo)) { $this->readableValue .= $pseudo; diff --git a/lib/CssCrush/Stream.php b/lib/CssCrush/Stream.php index 60f52ee..52d7cc7 100644 --- a/lib/CssCrush/Stream.php +++ b/lib/CssCrush/Stream.php @@ -42,9 +42,9 @@ public function substr ($start, $length = null) } } - public function matchAll ($patt, $preprocess_patt = false) + public function matchAll ($patt, $offset = 0) { - return Regex::matchAll($patt, $this->raw, $preprocess_patt); + return Regex::matchAll($patt, $this->raw, $offset); } public function replace ($find, $replacement) diff --git a/plugins/canvas.php b/plugins/canvas.php index dfcd1c1..ebd45ae 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -120,7 +120,10 @@ function canvas_generator ($input, $context) { $block = $canvas_defs[$name]->apply($args); // Parse the block into a keyed array. - $raw = array_change_key_case(Rule::parseBlock($block, array('keyed' => true))); + $raw = array_change_key_case(Rule::parseBlock($block, array( + 'keyed' => true, + 'flatten' => true, + ))); // Create canvas object. $canvas = new Canvas(); diff --git a/plugins/svg.php b/plugins/svg.php index 0ae3b69..1de7e3c 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -199,7 +199,10 @@ function svg_generator ($input, $fn_name) { $block = $svg_defs[$name]->apply($args); // Parse the block into a keyed assoc array. - $raw_data = array_change_key_case(Rule::parseBlock($block, array('keyed' => true))); + $raw_data = array_change_key_case(Rule::parseBlock($block, array( + 'keyed' => true, + 'flatten' => true, + ))); // Resolve the type. // Bail if type not recognised. From 07dc4aa0adfe609d38e3595b62e6959b2155e1aa Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 17 Aug 2013 14:10:41 +0100 Subject: [PATCH 158/421] Renamed Regex::create to Regex::make and made it simpler so preprocessed regexes can be cached internally. --- lib/CssCrush/Color.php | 16 ++++------ lib/CssCrush/CssCrush.php | 2 +- lib/CssCrush/Functions.php | 2 +- lib/CssCrush/Importer.php | 8 ++--- lib/CssCrush/Mixin.php | 7 +--- lib/CssCrush/PostAliasFix.php | 6 ++-- lib/CssCrush/Process.php | 27 +++++++--------- lib/CssCrush/Regex.php | 60 +++++++++++++++++++---------------- lib/CssCrush/Rule.php | 2 +- lib/CssCrush/Stream.php | 7 +--- lib/CssCrush/Template.php | 2 +- lib/CssCrush/Tokens.php | 30 ++++++++++-------- plugins/canvas.php | 29 ++++++++--------- plugins/ease.php | 2 +- plugins/hsl-to-hex.php | 5 +-- plugins/rem.php | 4 +-- plugins/rgba-fallback.php | 5 +-- plugins/svg-gradients.php | 4 +-- plugins/svg.php | 27 +++++++--------- 19 files changed, 110 insertions(+), 135 deletions(-) diff --git a/lib/CssCrush/Color.php b/lib/CssCrush/Color.php index 162cd78..e3dab3d 100644 --- a/lib/CssCrush/Color.php +++ b/lib/CssCrush/Color.php @@ -115,12 +115,12 @@ static public function test ($str) { static $color_patt; if (! $color_patt) { - $color_patt = Regex::create('^( + $color_patt = Regex::make('~^( \#(?={{hex}}{3}) | \#(?={{hex}}{6}) | rgba?(?=[?(]) | hsla?(?=[?(]) - )', 'ixS'); + )~ixS'); } $color_test = array(); @@ -351,18 +351,16 @@ static public function colorSplit ($str) return array($color, 1); } - static $alpha_color_patt; - if (! $alpha_color_patt) { - $alpha_color_patt = Regex::create( - '^(rgb|hsl)a\(({{number}}%?,{{number}}%?,{{number}}%?),({{number}})\)$'); - } - // Strip all whitespace. $color = preg_replace('~\s+~', '', $color); // Extract alpha component if one is matched. $opacity = 1; - if (preg_match($alpha_color_patt, $color, $m)) { + if (preg_match( + Regex::make('~^(rgb|hsl)a\(({{number}}%?,{{number}}%?,{{number}}%?),({{number}})\)$~i'), + $color, + $m) + ) { $opacity = floatval($m[3]); $color = "$m[1]($m[2])"; } diff --git a/lib/CssCrush/CssCrush.php b/lib/CssCrush/CssCrush.php index 3412e65..f723015 100644 --- a/lib/CssCrush/CssCrush.php +++ b/lib/CssCrush/CssCrush.php @@ -211,7 +211,7 @@ static public function loadAssets () // We'll cache the function matching regex here. $vendor_grouped_aliases[$m[1]]['find'][] = - Regex::create('{{LB}}' . $func_name . '{{RTB}}', 'i'); + Regex::make('~{{LB}}' . $func_name . '{{RTB}}~i'); $vendor_grouped_aliases[$m[1]]['replace'][] = $alias_func; } } diff --git a/lib/CssCrush/Functions.php b/lib/CssCrush/Functions.php index 03092a0..6530fc7 100644 --- a/lib/CssCrush/Functions.php +++ b/lib/CssCrush/Functions.php @@ -35,7 +35,7 @@ class Functions static public function setMatchPatt () { self::$functions = self::$builtinFunctions + self::$customFunctions; - self::$functionPatt = Regex::createFunctionPatt( + self::$functionPatt = Regex::makeFunctionPatt( array_keys(self::$functions), array('bare_paren' => true)); } diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index baf1134..93d4b15 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -153,20 +153,16 @@ static public function hostfile () static protected function rewriteImportedUrls ($import) { - static $non_import_urls_patt; - if (! $non_import_urls_patt) { - $non_import_urls_patt = Regex::create('(?input->dir, dirname($import->path)); if (empty($link)) { + return; } // Match all urls that are not imports. - preg_match_all($non_import_urls_patt, $import->content, $matches); + preg_match_all(Regex::make('~(?content, $matches); foreach ($matches[0] as $token) { diff --git a/lib/CssCrush/Mixin.php b/lib/CssCrush/Mixin.php index 834c2b7..8b3fcb8 100644 --- a/lib/CssCrush/Mixin.php +++ b/lib/CssCrush/Mixin.php @@ -17,11 +17,6 @@ public function __construct ($block) static public function call ($message, $context = null) { - static $message_patt; - if (! $message_patt) { - $message_patt = Regex::create('^(?{{ident}}) {{parens}}?', 'xS'); - } - $process = CssCrush::$process; $mixable = null; $message = trim($message); @@ -29,7 +24,7 @@ static public function call ($message, $context = null) // Test for mixin or abstract rule. e.g: // named-mixin( 50px, rgba(0,0,0,0), left 100% ) // abstract-rule - if (preg_match($message_patt, $message, $message_match)) { + if (preg_match(Regex::make('~^(?{{ident}}) {{parens}}?~xS'), $message, $message_match)) { $name = $message_match['name']; diff --git a/lib/CssCrush/PostAliasFix.php b/lib/CssCrush/PostAliasFix.php index e997a7f..8660fb3 100644 --- a/lib/CssCrush/PostAliasFix.php +++ b/lib/CssCrush/PostAliasFix.php @@ -67,8 +67,8 @@ function postalias_fix_linear_gradients ($declaration_copies) { static $deg_patt, $fn_patt; if (! $deg_patt) { - $deg_patt = Regex::create('(?<=[\( ])({{number}})deg', 'i'); - $fn_patt = Regex::create('{{LB}}{{vendor}}(?:(?:repeating-)?linear-gradient)({{p-token}})', 'iS'); + $deg_patt = Regex::make('~(?<=[\( ])({{number}})deg~i'); + $fn_patt = Regex::make('~{{LB}}{{vendor}}(?:(?:repeating-)?linear-gradient)({{p-token}})~iS'); } // Legacy angles move anti-clockwise and start from East, not North. @@ -124,7 +124,7 @@ function postalias_fix_radial_gradients ($declaration_copies) { // Replace the new syntax with the legacy syntax. static $fn_patt; if (! $fn_patt) { - $fn_patt = Regex::create('{{LB}}{{vendor}}(?:(?:repeating-)?radial-gradient)({{p-token}})', 'iS'); + $fn_patt = Regex::make('~{{LB}}{{vendor}}(?:(?:repeating-)?radial-gradient)({{p-token}})~iS'); } $original_parens = array(); $replacement_parens = array(); diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index ae9d235..80d01ff 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -200,15 +200,12 @@ protected function getBoilerplate () protected function resolveSelectorAliases () { - static $alias_patt; - if (! $alias_patt) { - $alias_patt = Regex::create('@selector-alias +\:({{ident}}) +([^;]+) *;', 'iS'); - } - - $this->stream->pregReplaceCallback($alias_patt, function ($m) { - $name = strtolower($m[1]); - CssCrush::$process->selectorAliases[$name] = new Template(Util::stripCommentTokens($m[2])); - }); + $this->stream->pregReplaceCallback( + Regex::make('~@selector-alias +\:?({{ident}}) +([^;]+) *;~iS'), + function ($m) { + $name = strtolower($m[1]); + CssCrush::$process->selectorAliases[$name] = new Template(Util::stripCommentTokens($m[2])); + }); // Merge with global selector aliases. $this->selectorAliases += CssCrush::$config->selectorAliases; @@ -217,7 +214,7 @@ protected function resolveSelectorAliases () if ($this->selectorAliases) { $names = implode('|', array_keys($this->selectorAliases)); $this->selectorAliasesPatt - = Regex::create('\:(' . $names . '){{RB}}(\()?', 'iS'); + = Regex::make('~\:(' . $names . '){{RB}}(\()?~iS'); } } @@ -837,10 +834,10 @@ protected function collate () $regex_replacements['~ ?(@[^;]+\;)~'] = "$1$EOL"; // Trim leading spaces on @-rules and some tokens. - $regex_replacements[Regex::create(' +([@}]|\?[rc]{{token-id}}\?)', 'S')] = "$1"; + $regex_replacements[Regex::make('~ +([@}]|\?[rc]{{token-id}}\?)~S')] = "$1"; // Additional newline between adjacent rules and comments. - $regex_replacements[Regex::create('({{r-token}}) (\s*) ({{c-token}})', 'xS')] = "$1$EOL$2$3"; + $regex_replacements[Regex::make('~({{r-token}}) (\s*) ({{c-token}})~xS')] = "$1$EOL$2$3"; } // Apply all formatting replacements. @@ -1004,7 +1001,7 @@ public function generateSourceMap () $this->sourceMap['sources'][] = Util::getLinkBetweenPaths($this->output->dir, $source, false); } - $patt = Regex::create('\?[tm]{{token-id}}\?', 'S'); + $token_patt = Regex::make('~\?[tm]{{token-id}}\?~S'); $mappings = array(); $lines = preg_split(Regex::$patt->newline, $this->stream->raw); $tokens =& $this->tokens->store; @@ -1019,7 +1016,7 @@ public function generateSourceMap () $line_segments = array(); - while (preg_match($patt, $line_text, $m, PREG_OFFSET_CAPTURE)) { + while (preg_match($token_patt, $line_text, $m, PREG_OFFSET_CAPTURE)) { list($token, $dest_col) = $m[0]; $token_type = $token[1]; @@ -1113,7 +1110,7 @@ protected function minifyColors () if (! $keywords_patt) { $keywords =& Color::loadMinifyableKeywords(); $keywords_patt = '~(?stream->pregReplaceCallback($keywords_patt, function ($m) { diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php index 25440e0..ed2bf02 100644 --- a/lib/CssCrush/Regex.php +++ b/lib/CssCrush/Regex.php @@ -14,7 +14,7 @@ class Regex // Character classes. static public $classes; - static public $classSwaps = array(); + static public $swaps = array(); static public function init () { @@ -55,7 +55,7 @@ static public function init () // Create standalone class patterns, add classes as class swaps. foreach ($classes as $name => $class) { - self::$classSwaps['{{' . str_replace('_', '-', $name) . '}}'] = $class; + self::$swaps['{{' . str_replace('_', '-', $name) . '}}'] = $class; $patt->{$name} = '~' . $class . '~S'; } @@ -64,19 +64,19 @@ static public function init () $patt->rooted_number = '~^' . $classes->number . '$~'; // @-rules. - $patt->import = Regex::create('@import \s+ ({{u-token}}) \s? ([^;]*);', 'ixS'); - $patt->charset = Regex::create('@charset \s+ ({{s-token}}) \s*;', 'ixS'); - $patt->vars = Regex::create('@define \s* {{block}}', 'ixS'); - $patt->mixin = Regex::create('@mixin \s+ (?{{ident}}) \s* {{block}}', 'ixS'); - $patt->ifDefine = Regex::create('@ifdefine \s+ (not \s+)? ({{ident}}) \s* \{', 'ixS'); - $patt->fragmentCapture = Regex::create('@fragment \s+ (?{{ident}}) \s* {{block}}', 'ixS'); - $patt->fragmentInvoke = Regex::create('@fragment \s+ (?{{ident}}) {{parens}}? \s* ;', 'ixS'); - $patt->abstract = Regex::create('^@abstract \s+ (?{{ident}})', 'ixS'); + $patt->import = Regex::make('~@import \s+ ({{u-token}}) \s? ([^;]*);~ixS'); + $patt->charset = Regex::make('~@charset \s+ ({{s-token}}) \s*;~ixS'); + $patt->vars = Regex::make('~@define \s* {{block}}~ixS'); + $patt->mixin = Regex::make('~@mixin \s+ (?{{ident}}) \s* {{block}}~ixS'); + $patt->ifDefine = Regex::make('~@ifdefine \s+ (not \s+)? ({{ident}}) \s* \{~ixS'); + $patt->fragmentCapture = Regex::make('~@fragment \s+ (?{{ident}}) \s* {{block}}~ixS'); + $patt->fragmentInvoke = Regex::make('~@fragment \s+ (?{{ident}}) {{parens}}? \s* ;~ixS'); + $patt->abstract = Regex::make('~^@abstract \s+ (?{{ident}})~ixS'); // Functions. - $patt->function = Regex::create('{{LB}} ({{ident}}) ({{p-token}})', 'xS'); - $patt->varFunction = Regex::create('\$\( \s* ({{ident}}) \s* \)', 'xS'); - $patt->thisFunction = Regex::createFunctionPatt(array('this')); + $patt->function = Regex::make('~{{LB}} ({{ident}}) ({{p-token}})~xS'); + $patt->varFunction = Regex::make('~\$\( \s* ({{ident}}) \s* \)~xS'); + $patt->thisFunction = Regex::makeFunctionPatt(array('this')); // Strings and comments. $patt->string = '~(\'|")(?:\\\\\1|[^\1])*?\1~xS'; @@ -89,7 +89,7 @@ static public function init () ~xsS'; // Rules. - $patt->ruleFirstPass = Regex::create(' + $patt->ruleFirstPass = Regex::make('~ (?:^|(?<=[;{}])) (? (?: \s | {{c-token}} )* @@ -102,34 +102,38 @@ static public function init () [^@;{}]+ ) ) - {{block}}', 'xS'); + {{block}} + ~xS'); - $patt->rule = Regex::create(' + $patt->rule = Regex::make('~ (? {{t-token}} ) \s* (? [^{]+ ) \s* - {{block}}', 'xiS'); + {{block}} + ~xiS'); // Misc. $patt->vendorPrefix = '~^-([a-z]+)-([a-z-]+)~iS'; $patt->ruleDirective = '~^(?:(@include)|(@extends?)|(@name))[\s]+~iS'; $patt->argListSplit = '~\s*[,\s]\s*~S'; $patt->mathBlacklist = '~[^\.0-9\*\/\+\-\(\)]~S'; - $patt->cruftyHex = Regex::create('\#({{hex}})\1({{hex}})\2({{hex}})\3', 'S'); + $patt->cruftyHex = Regex::make('~\#({{hex}})\1({{hex}})\2({{hex}})\3~S'); } - static public function create ($pattern_template, $flags = '', $delim = '~') + static public function make ($pattern) { - static $find, $replace; - if (! $find) { - $find = array_keys(self::$classSwaps); - $replace = array_values(self::$classSwaps); - } + static $cache = array(), $find, $replace; + if (isset($cache[$pattern])) { - $pattern = str_replace($find, $replace, $pattern_template); + return $cache[$pattern]; + } + elseif (! $find) { + $find = array_keys(self::$swaps); + $replace = array_values(self::$swaps); + } - return "$delim{$pattern}$delim{$flags}"; + return $cache[$pattern] = str_replace($find, $replace, $pattern); } static public function matchAll ($patt, $subject, $offset = 0) @@ -139,7 +143,7 @@ static public function matchAll ($patt, $subject, $offset = 0) return $count ? $matches : array(); } - static public function createFunctionPatt ($list, $options = array()) + static public function makeFunctionPatt ($list, $options = array()) { // Bare parens. $question = ''; @@ -157,7 +161,7 @@ static public function createFunctionPatt ($list, $options = array()) $flat_list = implode('|', array_map('preg_quote', $list)); - return Regex::create("($template{{LB}}(?:$flat_list)$question)\(", 'iS'); + return Regex::make("~($template{{LB}}(?:$flat_list)$question)\(~iS"); } } diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php index f48278a..b8ab4a4 100644 --- a/lib/CssCrush/Rule.php +++ b/lib/CssCrush/Rule.php @@ -387,7 +387,7 @@ public function expandSelectors () static $any_patt, $reg_comma; if (! $any_patt) { - $any_patt = Regex::create(':any({{p-token}})', 'i'); + $any_patt = Regex::make('~:any({{p-token}})~i'); $reg_comma = '~\s*,\s*~'; } diff --git a/lib/CssCrush/Stream.php b/lib/CssCrush/Stream.php index 52d7cc7..e0c4836 100644 --- a/lib/CssCrush/Stream.php +++ b/lib/CssCrush/Stream.php @@ -67,17 +67,12 @@ public function replaceHash ($replacements) public function replaceTokens ($type, $callback = null) { - static $patts = array(); - if (! isset($patts[$type])) { - $patts[$type] = Regex::create("\? $type {{token-id}} \?", 'xS'); - } - $tokens =& CssCrush::$process->tokens->store->{ $type }; $callback = $callback ?: function ($m) use (&$tokens) { return isset($tokens[$m[0]]) ? $tokens[$m[0]] : ''; }; - $this->raw = preg_replace_callback($patts[$type], $callback, $this->raw); + $this->raw = preg_replace_callback(Regex::make("~\? $type {{token-id}} \?~xS"), $callback, $this->raw); return $this; } diff --git a/lib/CssCrush/Template.php b/lib/CssCrush/Template.php index b022025..9ba9b16 100644 --- a/lib/CssCrush/Template.php +++ b/lib/CssCrush/Template.php @@ -23,7 +23,7 @@ public function __construct ($str, $options = array()) { static $arg_patt; if (! $arg_patt) { - $arg_patt = Regex::createFunctionPatt( + $arg_patt = Regex::makeFunctionPatt( array('arg'), array('templating' => true)); } diff --git a/lib/CssCrush/Tokens.php b/lib/CssCrush/Tokens.php index 361fc79..bcfacbf 100644 --- a/lib/CssCrush/Tokens.php +++ b/lib/CssCrush/Tokens.php @@ -123,13 +123,11 @@ public function captureStrings ($str, $add_padding = false) public function captureUrls ($str, $add_padding = false) { - static $url_patt; - if (! $url_patt) { - $url_patt = Regex::create( - '@import \s+ (?{{s-token}}) | {{LB}} (?url|data-uri) {{parens}}', 'ixS'); - } - - $count = preg_match_all($url_patt, $str, $m, PREG_OFFSET_CAPTURE); + $count = preg_match_all( + Regex::make('~@import \s+ (?{{s-token}}) | {{LB}} (?url|data-uri) {{parens}}~ixS'), + $str, + $m, + PREG_OFFSET_CAPTURE); while ($count--) { @@ -140,7 +138,9 @@ public function captureUrls ($str, $add_padding = false) if ($import_offset !== -1) { $url = new Url(trim($import_text)); - $str = str_replace($import_text, $add_padding ? str_pad($url->label, strlen($import_text)) : $url->label, $str); + $str = str_replace( + $import_text, + $add_padding ? str_pad($url->label, strlen($import_text)) : $url->label, $str); } // A URL function. @@ -150,7 +150,11 @@ public function captureUrls ($str, $add_padding = false) $url = new Url(trim($m['parens_content'][$count][0])); $url->convertToData = 'data-uri' === $func_name; - $str = substr_replace($str, $add_padding ? Tokens::pad($url->label, $full_text) : $url->label, $full_offset, strlen($full_text)); + $str = substr_replace( + $str, + $add_padding ? Tokens::pad($url->label, $full_text) : $url->label, + $full_offset, + strlen($full_text)); } } @@ -175,13 +179,11 @@ static public function pad ($label, $replaced_text) static public function is ($label, $of_type) { - static $type_patt; - if (! $type_patt) { - $type_patt = Regex::create('^ \? (?[a-z]) {{token-id}} \? $', 'xS'); - } - if (preg_match($type_patt, $label, $m)) { + if (preg_match(Regex::make('~^ \? (?[a-z]) {{token-id}} \? $~xS'), $label, $m)) { + return $of_type ? ($of_type === $m['type']) : true; } + return false; } } diff --git a/plugins/canvas.php b/plugins/canvas.php index ebd45ae..d83a029 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -67,20 +67,17 @@ function canvas_capture ($process) { - static $callback, $patt; - if (! $callback) { - $patt = Regex::create('@canvas \s+ (?{{ident}}) \s* {{block}}', 'ixS'); - } - // Extract definitions. - $process->stream->pregReplaceCallback($patt, function ($m) { - $name = strtolower($m['name']); - $block = $m['block_content']; - if (! empty($block)) { - CssCrush::$process->misc->canvas_defs[$name] = new Template($block); - } - return ''; - }); + $process->stream->pregReplaceCallback( + Regex::make('~@canvas \s+ (?{{ident}}) \s* {{block}}~ixS'), + function ($m) { + $name = strtolower($m['name']); + $block = $m['block_content']; + if (! empty($block)) { + CssCrush::$process->misc->canvas_defs[$name] = new Template($block); + } + return ''; + }); } function canvas_generator ($input, $context) { @@ -385,7 +382,7 @@ function canvas_apply_css_funcs ($canvas) { 'canvas-linear-gradient' => 'CssCrush\canvas_fn_linear_gradient', ); $map['fill'] = array( - 'patt' => Regex::createFunctionPatt(array_keys($fill_functions)), + 'patt' => Regex::makeFunctionPatt(array_keys($fill_functions)), 'functions' => $fill_functions, ); @@ -400,13 +397,13 @@ function canvas_apply_css_funcs ($canvas) { 'blur' => 'CssCrush\canvas_fn_filter', ); $map['filter'] = array( - 'patt' => Regex::createFunctionPatt(array_keys($filter_functions)), + 'patt' => Regex::makeFunctionPatt(array_keys($filter_functions)), 'functions' => $filter_functions, ); $generic_functions = array_diff_key(Functions::$functions, $map['fill']['functions']); $map['generic'] = array( - 'patt' => Regex::createFunctionPatt( + 'patt' => Regex::makeFunctionPatt( array_keys($generic_functions), array('bare_paren' => true)), 'functions' => $generic_functions, ); diff --git a/plugins/ease.php b/plugins/ease.php index bd09e95..3529760 100644 --- a/plugins/ease.php +++ b/plugins/ease.php @@ -60,7 +60,7 @@ function ease (Rule $rule) { ); foreach ($easings as $property => $value) { - $patt = Regex::create('{{LB}}' . $property . '{{RB}}', 'i'); + $patt = Regex::make('~{{LB}}' . $property . '{{RB}}~i'); $find[] = $patt; $replace[] = $value; } diff --git a/plugins/hsl-to-hex.php b/plugins/hsl-to-hex.php index dc61d37..b10c1d4 100644 --- a/plugins/hsl-to-hex.php +++ b/plugins/hsl-to-hex.php @@ -22,10 +22,7 @@ function hsl_to_hex (Rule $rule) { - static $hsl_patt; - if (! $hsl_patt) { - $hsl_patt = Regex::create('{{LB}}hsl({{p-token}})', 'i'); - } + $hsl_patt = Regex::make('~{{LB}}hsl({{p-token}})~i'); foreach ($rule as &$declaration) { diff --git a/plugins/rem.php b/plugins/rem.php index cb81d9b..ff67eb6 100644 --- a/plugins/rem.php +++ b/plugins/rem.php @@ -49,8 +49,8 @@ function rem (Rule $rule) { static $rem_patt, $px_patt, $font_props, $modes; if (! $modes) { - $rem_patt = Regex::create('{{LB}}({{number}})rem{{RB}}', 'iS'); - $px_patt = Regex::create('{{LB}}({{number}})px{{RB}}', 'iS'); + $rem_patt = Regex::make('~{{LB}}({{number}})rem{{RB}}~iS'); + $px_patt = Regex::make('~{{LB}}({{number}})px{{RB}}~iS'); $font_props = array( 'font' => true, 'font-size' => true, diff --git a/plugins/rgba-fallback.php b/plugins/rgba-fallback.php index c16c17f..f04b080 100644 --- a/plugins/rgba-fallback.php +++ b/plugins/rgba-fallback.php @@ -39,10 +39,7 @@ function rgba_fallback (Rule $rule) { return; } - static $rgb_patt; - if (! $rgb_patt) { - $rgb_patt = Regex::create('^rgba{{p-token}}$', 'i'); - } + $rgb_patt = Regex::make('~^rgba{{p-token}}$~i'); $new_set = array(); foreach ($rule as $declaration) { diff --git a/plugins/svg-gradients.php b/plugins/svg-gradients.php index 9ad6fba..2e02859 100644 --- a/plugins/svg-gradients.php +++ b/plugins/svg-gradients.php @@ -115,7 +115,7 @@ function create_svg_linear_gradient ($input) { $angle_keywords['to right bottom'] = $angle_keywords['to bottom right']; $angle_keywords['to left bottom'] = $angle_keywords['to bottom left']; - $deg_patt = Regex::create('^{{number}}deg$', 'i'); + $deg_patt = Regex::make('~^{{number}}deg$~i'); } $args = Functions::parseArgs($input); @@ -246,7 +246,7 @@ function create_svg_radial_gradient ($input) { $position_keywords['at right bottom'] = $position_keywords['at bottom right']; $position_keywords['at left bottom'] = $position_keywords['at bottom left']; - $origin_patt = Regex::create('^({{number}}%?) +({{number}}%?)$'); + $origin_patt = Regex::make('~^({{number}}%?) +({{number}}%?)$~'); } $args = Functions::parseArgs($input); diff --git a/plugins/svg.php b/plugins/svg.php index 1de7e3c..2c47372 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -85,20 +85,17 @@ function fn__svg_data ($input) { function svg_capture ($process) { - static $patt; - if (! $patt) { - $patt = Regex::create('@svg \s+ (?{{ident}}) \s* {{block}}', 'ixS'); - } - // Extract svg definitions. - $process->stream->pregReplaceCallback($patt, function ($m) { - $name = strtolower($m['name']); - $block = $m['block_content']; - if (! empty($block)) { - CssCrush::$process->misc->svg_defs[$name] = new Template($block); - } - return ''; - }); + $process->stream->pregReplaceCallback( + Regex::make('~@svg \s+ (?{{ident}}) \s* {{block}}~ixS'), + function ($m) { + $name = strtolower($m['name']); + $block = $m['block_content']; + if (! empty($block)) { + CssCrush::$process->misc->svg_defs[$name] = new Template($block); + } + return ''; + }); } function svg_generator ($input, $fn_name) { @@ -675,9 +672,9 @@ function svg_apply_css_funcs ($element, &$raw_data) { ); $generic_functions = array_diff_key(Functions::$functions, $fill_functions); - $generic_functions_patt = Regex::createFunctionPatt( + $generic_functions_patt = Regex::makeFunctionPatt( array_keys($generic_functions), array('bare_paren' => true)); - $fill_functions_patt = Regex::createFunctionPatt( + $fill_functions_patt = Regex::makeFunctionPatt( array_keys($fill_functions)); } From c7475c4108318d4212d5d629f3bb9c0259db48ac Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 20 Aug 2013 15:48:59 +0100 Subject: [PATCH 159/421] Added parsing for single line variable definitions e.g. `@define col-width 30px;` --- lib/CssCrush/Process.php | 43 ++++++++++++++++++++++++---------------- lib/CssCrush/Regex.php | 1 - 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 80d01ff..d2a19aa 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -416,32 +416,41 @@ protected function filterPlugins () protected function captureVars () { - $config = CssCrush::$config; - $regex = Regex::$patt; - $option_vars = $this->options->vars; - - $this->stream->pregReplaceCallback($regex->vars, function ($m) { - CssCrush::$process->vars = - array_merge( - CssCrush::$process->vars, - Rule::parseBlock($m['block_content'], array( - 'keyed' => true, - 'ignore_directives' => true, - )) - ); + $vars_patt = Regex::make('~ + @define + (?: + \s* {{block}} | + \s+ (?{{ident}}) \s+ (?[^;]+) \s* ; + ) + ~ixS'); + + $this->stream->pregReplaceCallback($vars_patt, function ($m) { + if (isset($m['name'])) { + CssCrush::$process->vars[strtolower($m['name'])] = $m['value']; + } + else { + CssCrush::$process->vars = + array_merge( + CssCrush::$process->vars, + Rule::parseBlock($m['block_content'], array( + 'keyed' => true, + 'ignore_directives' => true, + )) + ); + } }); // In-file variables override global variables. - $this->vars += $config->vars; + $this->vars += CssCrush::$config->vars; // Runtime variables override in-file variables. - if (! empty($option_vars)) { - $this->vars = array_merge($this->vars, $option_vars); + if (! empty($this->options->vars)) { + $this->vars = array_merge($this->vars, $this->options->vars); } // Place variables referenced inside variables. foreach ($this->vars as $name => &$value) { - $value = preg_replace_callback($regex->varFunction, 'CssCrush\Process::cb_placeVars', $value); + $value = preg_replace_callback(Regex::$patt->varFunction, 'CssCrush\Process::cb_placeVars', $value); } } diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php index ed2bf02..339754c 100644 --- a/lib/CssCrush/Regex.php +++ b/lib/CssCrush/Regex.php @@ -66,7 +66,6 @@ static public function init () // @-rules. $patt->import = Regex::make('~@import \s+ ({{u-token}}) \s? ([^;]*);~ixS'); $patt->charset = Regex::make('~@charset \s+ ({{s-token}}) \s*;~ixS'); - $patt->vars = Regex::make('~@define \s* {{block}}~ixS'); $patt->mixin = Regex::make('~@mixin \s+ (?{{ident}}) \s* {{block}}~ixS'); $patt->ifDefine = Regex::make('~@ifdefine \s+ (not \s+)? ({{ident}}) \s* \{~ixS'); $patt->fragmentCapture = Regex::make('~@fragment \s+ (?{{ident}}) \s* {{block}}~ixS'); From b5b686312f2bedf4b3633311605a4a7f727772ae Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 21 Aug 2013 10:21:47 +0100 Subject: [PATCH 160/421] To be more readable `Functions::executeOnString` now returns a string instead of modifying a reference. --- lib/CssCrush/CssCrush.php | 4 ++-- lib/CssCrush/Declaration.php | 4 ++-- lib/CssCrush/Functions.php | 14 +++++++----- lib/CssCrush/Process.php | 44 ++++++++++++++++-------------------- lib/CssCrush/Template.php | 4 +--- plugins/canvas.php | 6 ++--- plugins/svg.php | 4 ++-- 7 files changed, 37 insertions(+), 43 deletions(-) diff --git a/lib/CssCrush/CssCrush.php b/lib/CssCrush/CssCrush.php index f723015..3592f97 100644 --- a/lib/CssCrush/CssCrush.php +++ b/lib/CssCrush/CssCrush.php @@ -452,13 +452,13 @@ static public function globalVars ($vars) // Merge into the stack, overrides existing variables of the same name if (is_array($vars)) { - $config->vars = array_merge($config->vars, $vars); + $config->vars = $vars + $config->vars; } // Test for a file. If it is attempt to parse it elseif (is_string($vars) && file_exists($vars)) { if ($result = @parse_ini_file($vars)) { - $config->vars = array_merge($config->vars, $result); + $config->vars = $result + $config->vars; } } diff --git a/lib/CssCrush/Declaration.php b/lib/CssCrush/Declaration.php index 6380a44..597c205 100644 --- a/lib/CssCrush/Declaration.php +++ b/lib/CssCrush/Declaration.php @@ -98,7 +98,7 @@ public function process ($parent_rule) $context = (object) array( 'rule' => $parent_rule, ); - Functions::executeOnString( + $this->value = Functions::executeOnString( $this->value, Regex::$patt->thisFunction, array( @@ -113,7 +113,7 @@ public function process ($parent_rule) 'rule' => $parent_rule, 'property' => $this->property ); - Functions::executeOnString( + $this->value = Functions::executeOnString( $this->value, null, null, diff --git a/lib/CssCrush/Functions.php b/lib/CssCrush/Functions.php index 6530fc7..6a13786 100644 --- a/lib/CssCrush/Functions.php +++ b/lib/CssCrush/Functions.php @@ -39,12 +39,12 @@ static public function setMatchPatt () array_keys(self::$functions), array('bare_paren' => true)); } - static public function executeOnString (&$str, $patt = null, $process_callback = null, \stdClass $context = null) + static public function executeOnString ($str, $patt = null, $process_callback = null, \stdClass $context = null) { // No bracketed expressions, early return. if (strpos($str, '(') === false) { - return; + return $str; } // Set default pattern if not set. @@ -55,17 +55,17 @@ static public function executeOnString (&$str, $patt = null, $process_callback = // No custom functions, early return. if (! preg_match($patt, $str)) { - return; + return $str; } - // Find custom function matches. - $matches = Regex::matchAll($patt, $str); - // Always pass in a context object. if (! $context) { $context = new \stdClass(); } + // Find custom function matches. + $matches = Regex::matchAll($patt, $str); + // Step through the matches from last to first. while ($match = array_pop($matches)) { @@ -109,6 +109,8 @@ static public function executeOnString (&$str, $patt = null, $process_callback = // Splice in the function result. $str = substr_replace($str, "$before_operator$func_returns", $offset, $closing_paren - $offset); } + + return $str; } diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index d2a19aa..02192dc 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -426,17 +426,13 @@ protected function captureVars () $this->stream->pregReplaceCallback($vars_patt, function ($m) { if (isset($m['name'])) { - CssCrush::$process->vars[strtolower($m['name'])] = $m['value']; + CssCrush::$process->vars[$m['name']] = $m['value']; } else { - CssCrush::$process->vars = - array_merge( - CssCrush::$process->vars, - Rule::parseBlock($m['block_content'], array( - 'keyed' => true, - 'ignore_directives' => true, - )) - ); + CssCrush::$process->vars = Rule::parseBlock($m['block_content'], array( + 'keyed' => true, + 'ignore_directives' => true, + )) + CssCrush::$process->vars; } }); @@ -445,7 +441,7 @@ protected function captureVars () // Runtime variables override in-file variables. if (! empty($this->options->vars)) { - $this->vars = array_merge($this->vars, $this->options->vars); + $this->vars = $this->options->vars + $this->vars; } // Place variables referenced inside variables. @@ -477,29 +473,27 @@ protected function placeAllVars () static protected function placeVars (&$value) { - $regex = Regex::$patt; - // Variables with no default value. - $value = preg_replace_callback($regex->varFunction, + $value = preg_replace_callback(Regex::$patt->varFunction, 'CssCrush\Process::cb_placeVars', $value, -1, $vars_placed); + // Variables with default value. if (strpos($value, '$(') !== false) { // Assume at least one replace. $vars_placed = 1; - // Variables with default value. - $callback = function ($raw_arg) { - list($name, $default_value) = Functions::parseArgsSimple($raw_arg); - if (isset(CssCrush::$process->vars[$name])) { - return CssCrush::$process->vars[$name]; - } - else { - return $default_value; - } - }; - - Functions::executeOnString($value, '~(\$)\(~', array('$' => $callback)); + // Variables may be nested so need to apply full function parsing. + $value = Functions::executeOnString($value, '~(\$)\(~', + array('$' => function ($raw_args) { + list($name, $default_value) = Functions::parseArgsSimple($raw_args); + if (isset(CssCrush::$process->vars[$name])) { + return CssCrush::$process->vars[$name]; + } + else { + return $default_value; + } + })); } // If we know replacements have been made we may want to update $value. e.g URL tokens. diff --git a/lib/CssCrush/Template.php b/lib/CssCrush/Template.php index 9ba9b16..e57a97d 100644 --- a/lib/CssCrush/Template.php +++ b/lib/CssCrush/Template.php @@ -31,12 +31,10 @@ public function __construct ($str, $options = array()) // Parse all arg function calls in the passed string, // callback creates default values. - Functions::executeOnString($str, $arg_patt, array( + $this->string = Functions::executeOnString($str, $arg_patt, array( 'arg' => array($this, 'capture'), '#' => array($this, 'capture'), )); - - $this->string = $str; } public function capture ($str) diff --git a/plugins/canvas.php b/plugins/canvas.php index d83a029..9a95002 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -419,19 +419,19 @@ function canvas_apply_css_funcs ($canvas) { } // Generic functions. - Functions::executeOnString( + $value = Functions::executeOnString( $value, $map['generic']['patt'], $map['generic']['functions']); // Fill functions. if (in_array($property, array('fill', 'background-fill'))) { $context->currentProperty = $property; $context->canvas = $canvas; - Functions::executeOnString( + $value = Functions::executeOnString( $value, $map['fill']['patt'], $map['fill']['functions'], $context); } elseif ($property === 'canvas-filter') { $context->canvas = $canvas; - Functions::executeOnString( + $value = Functions::executeOnString( $value, $map['filter']['patt'], $map['filter']['functions'], $context); } } diff --git a/plugins/svg.php b/plugins/svg.php index 2c47372..7805569 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -679,11 +679,11 @@ function svg_apply_css_funcs ($element, &$raw_data) { } foreach ($raw_data as $property => &$value) { - Functions::executeOnString($value, $generic_functions_patt); + $value = Functions::executeOnString($value, $generic_functions_patt); // Only capturing fills for fill and stoke properties. if ($property === 'fill' || $property === 'stroke') { - Functions::executeOnString( + $value = Functions::executeOnString( $value, $fill_functions_patt, $fill_functions, $element); // If the value is a color with alpha component we split the color From 08e9e817dfac0c57122e0198432c94bc2bd5ab25 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 22 Aug 2013 11:38:58 +0100 Subject: [PATCH 161/421] Expanded syntax validation to report line and column of unbalanced or stray brackets. --- lib/CssCrush/CssCrush.php | 6 ++--- lib/CssCrush/Importer.php | 50 ++++++++++++++++++++++++++++++++++----- 2 files changed, 47 insertions(+), 9 deletions(-) diff --git a/lib/CssCrush/CssCrush.php b/lib/CssCrush/CssCrush.php index 3592f97..0403642 100644 --- a/lib/CssCrush/CssCrush.php +++ b/lib/CssCrush/CssCrush.php @@ -450,19 +450,19 @@ static public function globalVars ($vars) { $config = self::$config; - // Merge into the stack, overrides existing variables of the same name + // Merge into the stack, overrides existing variables of the same name. if (is_array($vars)) { $config->vars = $vars + $config->vars; } - // Test for a file. If it is attempt to parse it + // Test for a file. If it is attempt to parse it. elseif (is_string($vars) && file_exists($vars)) { if ($result = @parse_ini_file($vars)) { $config->vars = $result + $config->vars; } } - // Clear the stack if the argument is explicitly null + // Clear the stack if the argument is explicitly null. elseif (is_null($vars)) { $config->vars = array(); } diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index 93d4b15..65207f4 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -189,6 +189,7 @@ static protected function prepareForStream (&$str) if (! self::checkSyntax($str)) { + $str = ''; return false; } @@ -217,19 +218,56 @@ static protected function prepareForStream (&$str) static protected function checkSyntax (&$str) { - // TODO: add more sophisticated error detection such as line/column of an unmatched bracket. - // Catch obvious typing errors. $parse_errors = array(); $current_file = 'file://' . end(CssCrush::$process->sources); $balanced_parens = substr_count($str, "(") === substr_count($str, ")"); $balanced_curlies = substr_count($str, "{") === substr_count($str, "}"); - if (! $balanced_parens) { - $parse_errors[] = "Unmatched '(' in $current_file."; - } + $validate_pairings = function ($str, $pairing) use ($current_file) { + if ($pairing === '{}') { + $opener_patt = '~\{~'; + $balancer_patt = Regex::make('~^{{block}}~'); + } + else { + $opener_patt = '~\(~'; + $balancer_patt = Regex::make('~^{{parens}}~'); + } + + // Find unbalanced opening brackets. + preg_match_all($opener_patt, $str, $matches, PREG_OFFSET_CAPTURE); + foreach ($matches[0] as $m) { + $offset = $m[1]; + if (! preg_match($balancer_patt, substr($str, $offset), $m)) { + $substr = substr($str, 0, $offset); + $line = substr_count($substr, "\n") + 1; + $column = strlen($substr) - strrpos($substr, "\n"); + return "Unbalanced '{$pairing[0]}' in $current_file, Line $line, Column $column."; + } + } + + // Reverse the stream (and brackets) to find stray closing brackets. + $str = strtr(strrev($str), $pairing, strrev($pairing)); + + preg_match_all($opener_patt, $str, $matches, PREG_OFFSET_CAPTURE); + foreach ($matches[0] as $m) { + $offset = $m[1]; + $substr = substr($str, $offset); + if (! preg_match($balancer_patt, $substr, $m)) { + $line = substr_count($substr, "\n") + 1; + $column = strpos($substr, "\n"); + return "Stray '{$pairing[1]}' in $current_file, Line $line, Column $column."; + } + } + + return false; + }; + if (! $balanced_curlies) { - $parse_errors[] = "Unmatched '{' in $current_file."; + $parse_errors[] = $validate_pairings($str, '{}') ?: "Unbalanced '{' in $current_file."; + } + if (! $balanced_parens) { + $parse_errors[] = $validate_pairings($str, '()') ?: "Unbalanced '(' in $current_file."; } if ($parse_errors) { From 0fa45245d4aa2935abafd313413825525ec99b7a Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 22 Aug 2013 15:20:01 +0100 Subject: [PATCH 162/421] Added partial support for relative input file paths (based on the current excecuting script path). --- lib/CssCrush/CssCrush.php | 4 ++-- lib/CssCrush/Process.php | 1 - lib/CssCrush/Util.php | 1 + 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/CssCrush/CssCrush.php b/lib/CssCrush/CssCrush.php index 0403642..7a3fe21 100644 --- a/lib/CssCrush/CssCrush.php +++ b/lib/CssCrush/CssCrush.php @@ -277,9 +277,9 @@ static public function file ($file, $options = null) else if (strpos($file, '/') === 0) { $pathtest = $process->setContext(dirname($doc_root . $file)); } - // Relative path. + // Relative path. Try resolving based on the directory of the executing script. else { - $pathtest = $process->setContext(dirname(__DIR__ . '/' . $file)); + $pathtest = $process->setContext(dirname($_SERVER['SCRIPT_FILENAME']) . '/' . dirname($file)); } if (! $pathtest) { diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 02192dc..d4333d0 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -82,7 +82,6 @@ public function release () ); } - // Establish the input and output directories and optionally test output dir. public function setContext ($input_dir, $test_output_dir = true) { $doc_root = $this->docRoot; diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php index 6bd1896..d279179 100644 --- a/lib/CssCrush/Util.php +++ b/lib/CssCrush/Util.php @@ -26,6 +26,7 @@ static public function normalizePath ($path, $strip_drive_letter = false) if ($strip_drive_letter) { $path = preg_replace('~^[a-z]\:~i', '', $path); } + // Backslashes and repeat slashes to a single forward slash. $path = rtrim(preg_replace('~[\\\\/]+~', '/', $path), '/'); From 749670f802a4472afebd6c409a9b1db48dfc5b3f Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 28 Aug 2013 21:06:40 +0100 Subject: [PATCH 163/421] Fixed `Color->__toString()` not rounding floats on output to rgba() notation. Edited some comments for clarity. Added `Util::resolveUserPath()` method for resolving path based user options consistently. Refactored out `IO::registerInputFile()` method. Renamed some internal static methods for clarity. Some code style amends. --- lib/CssCrush/Color.php | 22 +++++++++----- lib/CssCrush/CssCrush.php | 61 +++++++++++++++------------------------ lib/CssCrush/IO.php | 29 ++----------------- lib/CssCrush/Importer.php | 2 +- lib/CssCrush/Options.php | 11 +++++-- lib/CssCrush/Plugin.php | 7 ++--- lib/CssCrush/Process.php | 25 +++++++--------- lib/CssCrush/Util.php | 29 +++++++++++++++---- plugins/aria.php | 2 +- 9 files changed, 90 insertions(+), 98 deletions(-) diff --git a/lib/CssCrush/Color.php b/lib/CssCrush/Color.php index e3dab3d..6de5856 100644 --- a/lib/CssCrush/Color.php +++ b/lib/CssCrush/Color.php @@ -62,17 +62,17 @@ static public function &loadMinifyableKeywords () static public function parse ($str) { - $rgba = false; - if ($test = Color::test($str)) { $color = $test['value']; $type = $test['type']; } else { - return $rgba; + return false; } + $rgba = false; + switch ($type) { case 'hex': @@ -380,7 +380,7 @@ static public function colorSplit ($str) public function __construct ($color, $use_hsl_color_space = false) { $this->value = is_array($color) ? $color : self::parse($color); - $this->isValid = $this->value; + $this->isValid = ! empty($this->value); if ($use_hsl_color_space && $this->isValid) { $this->toHsl(); } @@ -388,13 +388,20 @@ public function __construct ($color, $use_hsl_color_space = false) public function __toString () { - if ($this->value[3] !== 1) { + // For opaque colors return hex notation as it's the most compact. + if ($this->value[3] === 1) { - return 'rgba(' . implode(',', $this->hslColorSpace ? $this->getRgb() : $this->value) . ')'; + return $this->getHex(); } else { - return $this->getHex(); + // R, G and B components must be integers. + $components = array(); + foreach (($this->hslColorSpace ? $this->getRgb() : $this->value) as $component_index => $component) { + $components[] = $component_index === 3 ? $component : min(round($component), 255); + } + + return 'rgba(' . implode(',', $components) . ')'; } } @@ -448,6 +455,7 @@ public function adjust (array $adjustments) $was_hsl_color_space = $this->hslColorSpace; $this->toHsl(); + // Normalize percentage adjustment parameters to floating point numbers. foreach ($adjustments as $index => $val) { diff --git a/lib/CssCrush/CssCrush.php b/lib/CssCrush/CssCrush.php index 7a3fe21..7ccdb18 100644 --- a/lib/CssCrush/CssCrush.php +++ b/lib/CssCrush/CssCrush.php @@ -25,15 +25,13 @@ static public function init () self::$config->location = dirname(dirname(__DIR__)); // Plugin directories. - self::$config->plugin_dirs = array(self::$config->location . '/plugins'); + self::$config->pluginDirs = array(self::$config->location . '/plugins'); - // Establish version id. self::$config->version = new Version(self::VERSION); + self::$config->scriptDir = dirname($_SERVER['SCRIPT_FILENAME']); + self::$config->docRoot = self::resolveDocRoot(); - // Set the docRoot reference. - self::setDocRoot(); - - // Set the default IO handler. + // Set default IO handler. self::$config->io = 'CssCrush\IO'; // Shared resources. @@ -51,7 +49,7 @@ static public function init () // Alternative formatter to use for un-minified output. 'formatter' => null, - // Append 'checksum' to output file name. + // Appends checksum query string to output file name. 'versioning' => true, // Use the template boilerplate. @@ -63,7 +61,7 @@ static public function init () // Enable/disable the cache. 'cache' => true, - // Output filename. Defaults the host-filename. + // Output filename. Defaults the hostfile filename. 'output_file' => null, // Output directory. Defaults to the same directory as the host file. @@ -100,7 +98,7 @@ static public function init () require_once self::$config->location . '/misc/formatters.php'; } - static protected function setDocRoot ($doc_root = null) + static protected function resolveDocRoot ($doc_root = null) { // Get document_root reference // $_SERVER['DOCUMENT_ROOT'] is unreliable in certain CGI/Apache/IIS setups @@ -141,7 +139,7 @@ static protected function setDocRoot ($doc_root = null) } } - self::$config->docRoot = Util::normalizePath($doc_root); + return Util::normalizePath($doc_root); } // Aliases and macros loader. @@ -263,47 +261,36 @@ static public function file ($file, $options = null) $options = $process->options; $doc_root = $process->docRoot; - // Since we're comparing strings, we need to iron out OS differences. - $file = str_replace('\\', '/', $file); - - // Finding the system path of the input file and validating it. - $pathtest = true; + if (! ($input_file = Util::resolveUserPath($file))) { + $basename = basename($file); + $error = "Input file '$basename' not found."; + CssCrush::logError($error); + trigger_error(__METHOD__ . ": $error\n", E_USER_WARNING); - // System path. - if (strpos($file, $doc_root) === 0) { - $pathtest = $process->setContext(dirname($file)); - } - // WWW root path. - else if (strpos($file, '/') === 0) { - $pathtest = $process->setContext(dirname($doc_root . $file)); - } - // Relative path. Try resolving based on the directory of the executing script. - else { - $pathtest = $process->setContext(dirname($_SERVER['SCRIPT_FILENAME']) . '/' . dirname($file)); - } - - if (! $pathtest) { - // Main directory not found or is not writable return an empty string. return ''; } - // Validate file input. - if (! IO::registerInputFile($file)) { + if ($process->setContext(dirname($input_file))) { + $process->input->path = $input_file; + $process->input->filename = basename($input_file); + $process->input->mtime = filemtime($input_file); + } + else { return ''; } // Create a filename that will be used later // Used in validateCache, and writing to filesystem - $process->output->filename = $process->ioCall('getOutputFileName'); + $process->output->filename = $process->io('getOutputFileName'); // Caching. if ($options->cache) { // Load the cache data. - $process->cacheData = $process->ioCall('getCacheData'); + $process->cacheData = $process->io('getCacheData'); // If cache is enabled check for a valid compiled file. - $valid_compliled_file = $process->ioCall('validateExistingOutput'); + $valid_compliled_file = $process->io('validateExistingOutput'); if (is_string($valid_compliled_file)) { return $valid_compliled_file; @@ -313,7 +300,7 @@ static public function file ($file, $options = null) $stream = $process->compile(); // Create file and return url. Return empty string on failure. - if ($url = $process->ioCall('write', $stream)) { + if ($url = $process->io('write', $stream)) { $timestamp = $options->versioning ? '?' . time() : ''; return "$url$timestamp"; } @@ -475,7 +462,7 @@ static public function globalVars ($vars) */ static public function clearCache ($dir = '') { - return $process->ioCall('clearCache', $dir); + return $process->io('clearCache', $dir); } /** diff --git a/lib/CssCrush/IO.php b/lib/CssCrush/IO.php index 6950410..3b639f8 100644 --- a/lib/CssCrush/IO.php +++ b/lib/CssCrush/IO.php @@ -8,7 +8,6 @@ class IO { - // Any setup that needs to be done static public function init () { $process = CssCrush::$process; @@ -33,7 +32,7 @@ static public function testOutputDir () $error = "Output directory '$output_dir' doesn't exist."; $pathtest = false; } - else if (! is_writable($output_dir)) { + elseif (! is_writable($output_dir)) { CssCrush::log('Attempting to change permissions.'); @@ -150,7 +149,7 @@ static public function validateExistingOutput () } } } - else if (file_exists($existingfile->path)) { + elseif (file_exists($existingfile->path)) { CssCrush::log('Recompiling - file exists but no cache data.'); } @@ -167,7 +166,7 @@ static public function clearCache ($dir) if (empty($dir)) { $dir = __DIR__; } - else if (! file_exists($dir)) { + elseif (! file_exists($dir)) { return; } @@ -266,26 +265,4 @@ static public function write (Stream $stream) return false; } - - static final function registerInputFile ($file) - { - $input = CssCrush::$process->input; - - $input->filename = basename($file); - $input->path = "$input->dir/$input->filename"; - - if (! file_exists($input->path)) { - - // On failure return false. - $error = "Input file '$input->filename' not found."; - CssCrush::logError($error); - trigger_error(__METHOD__ . ": $error\n", E_USER_WARNING); - return false; - } - else { - // Capture the modified time. - $input->mtime = filemtime($input->path); - return true; - } - } } diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index 65207f4..fc515a4 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -145,7 +145,7 @@ static public function hostfile () ); // Save config changes. - $process->ioCall('saveCacheData'); + $process->io('saveCacheData'); } return $str; diff --git a/lib/CssCrush/Options.php b/lib/CssCrush/Options.php index aa12cba..6196084 100644 --- a/lib/CssCrush/Options.php +++ b/lib/CssCrush/Options.php @@ -49,9 +49,16 @@ public function __set ($name, $value) } break; - // Sanitize path options. - case 'context': + // Path options. case 'output_dir': + case 'boilerplate': + if (is_string($value)) { + $value = Util::resolveUserPath($value); + } + break; + + // Path options that only accept system paths. + case 'context': case 'doc_root': if (is_string($value)) { $value = Util::normalizePath(realpath($value)); diff --git a/lib/CssCrush/Plugin.php b/lib/CssCrush/Plugin.php index 3cd57c1..e58c246 100644 --- a/lib/CssCrush/Plugin.php +++ b/lib/CssCrush/Plugin.php @@ -12,7 +12,7 @@ class Plugin static public function info () { - $plugin_dirs = CssCrush::$config->plugin_dirs; + $plugin_dirs = CssCrush::$config->pluginDirs; $plugin_data = array(); foreach ($plugin_dirs as $plugin_dir) { @@ -57,7 +57,7 @@ static public function load ($plugin_name) $found = false; // Loop plugin_dirs to find the plugin. - foreach (CssCrush::$config->plugin_dirs as $plugin_dir) { + foreach (CssCrush::$config->pluginDirs as $plugin_dir) { $path = "$plugin_dir/$plugin_name.php"; if (file_exists($path)) { @@ -68,8 +68,7 @@ static public function load ($plugin_name) } if (! $found) { - trigger_error(__METHOD__ . - ": $plugin_name plugin not found.\n", E_USER_NOTICE); + trigger_error(__METHOD__ . ": Plugin '$plugin_name' not found.\n", E_USER_NOTICE); } } diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index d4333d0..b77d7db 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -47,8 +47,7 @@ public function __construct ($options) $this->selectorAliasesPatt = null; // Pick a doc root. - $this->docRoot = isset($this->options->doc_root) ? - $this->options->doc_root : $config->docRoot; + $this->docRoot = isset($this->options->doc_root) ? $this->options->doc_root : $config->docRoot; // Shortcut the newline option and attach it to the process. switch ($this->options->newlines) { @@ -86,43 +85,39 @@ public function setContext ($input_dir, $test_output_dir = true) { $doc_root = $this->docRoot; + // If not a system path. if (strpos($input_dir, $doc_root) !== 0) { - // Not a system path. - $input_dir = realpath("$doc_root/$input_dir"); + $input_dir = Util::normalizePath(realpath("$doc_root/$input_dir")); } // Initialise input object and store input directory. $this->input->path = null; $this->input->filename = null; $this->input->dir = $input_dir; - $this->input->dirUrl = substr($this->input->dir, strlen($doc_root)); + $this->input->dirUrl = substr($input_dir, strlen($doc_root)); // Store reference to the output dir. - $this->output->dir = $this->ioCall('getOutputDir'); + $this->output->dir = $this->io('getOutputDir'); $this->output->dirUrl = substr($this->output->dir, strlen($doc_root)); // Test the output directory to see it exists and is writable. $output_dir_ok = false; if ($test_output_dir) { - $output_dir_ok = $this->ioCall('testOutputDir'); + $output_dir_ok = $this->io('testOutputDir'); } - // Setup the IO handler. - $this->ioCall('init'); + $this->io('init'); return $output_dir_ok; } - public function ioCall ($method) + public function io ($method) { - // Fetch the argument list, shift off the first item + // Get argument list (excluding the method name which comes first). $args = func_get_args(); array_shift($args); - // The method address - $the_method = array(CssCrush::$config->io, $method); - - return call_user_func_array($the_method, $args); + return call_user_func_array(array(CssCrush::$config->io, $method), $args); } diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php index d279179..f551943 100644 --- a/lib/CssCrush/Util.php +++ b/lib/CssCrush/Util.php @@ -8,9 +8,6 @@ class Util { - /* - * Create html attribute string from array. - */ static public function htmlAttributes (array $attributes) { $attr_string = ''; @@ -23,6 +20,10 @@ static public function htmlAttributes (array $attributes) static public function normalizePath ($path, $strip_drive_letter = false) { + if (! $path) { + return ''; + } + if ($strip_drive_letter) { $path = preg_replace('~^[a-z]\:~i', '', $path); } @@ -41,8 +42,7 @@ static public function normalizePath ($path, $strip_drive_letter = false) static public function simplifyPath ($path) { - // Reduce redundant path segments (issue #32): - // e.g 'foo/../bar' => 'bar' + // Reduce redundant path segments. e.g 'foo/../bar' => 'bar' $patt = '~[^/.]+/\.\./~S'; while (preg_match($patt, $path)) { $path = preg_replace($patt, '', $path); @@ -50,6 +50,25 @@ static public function simplifyPath ($path) return $path; } + static public function resolveUserPath ($path) + { + // System path. + if ($realpath = realpath($path)) { + $path = $realpath; + } + // WWW root path. + elseif (strpos($path, '/') === 0) { + $doc_root = isset(CssCrush::$process) ? CssCrush::$process->docRoot : CssCrush::$config->docRoot; + $path = realpath($doc_root . $path); + } + // Relative path. Try resolving based on the directory of the executing script. + else { + $path = realpath(CssCrush::$config->scriptDir . '/' . $path); + } + + return $path ? Util::normalizePath($path) : false; + } + static public function find () { foreach (func_get_args() as $file) { diff --git a/plugins/aria.php b/plugins/aria.php index af251fc..e81ba22 100644 --- a/plugins/aria.php +++ b/plugins/aria.php @@ -47,7 +47,7 @@ function aria () { $aria = array( // Roles. - 'role' => '[role="#(0)"]', + 'role' => $optional_value('role'), // States and properties. 'aria-activedescendant' => $optional_value('aria-activedescendant'), From ed64511b17084c49270ce121c12d8f90d368d88a Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 29 Aug 2013 09:24:20 +0100 Subject: [PATCH 164/421] Added loop plugin: For...in loops with lists and generator functions. --- plugins/loop.php | 184 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 plugins/loop.php diff --git a/plugins/loop.php b/plugins/loop.php new file mode 100644 index 0000000..fc3c156 --- /dev/null +++ b/plugins/loop.php @@ -0,0 +1,184 @@ + function () { + Hook::add('capture_phase1', 'CssCrush\loop'); + }, + 'disable' => function () { + Hook::remove('capture_phase1', 'CssCrush\loop'); + }, +)); + + +function loop () { + + $for_block_patt_base = '@for \s+ (?{{ident}}) \s+ in \s+ (?[^{]+) \s*'; + $for_block_patt = Regex::make('~' . $for_block_patt_base . '{{block}}~xiS'); + $start_for_block_patt = Regex::make('~' . $for_block_patt_base . '\{~xiS'); + $generator_func_patt = Regex::make('~(?range|color-range) {{parens}}~ix'); + $loop_var_patt = '~\#\( \s* (?[a-zA-Z][\.a-zA-Z0-9-_]*) \s* \)~x'; + + // Matching each root level loop construct then evaluating all nested loops and the enclosing loop. + CssCrush::$process->stream->pregReplaceCallback($for_block_patt, function ($top_m) use ( + $start_for_block_patt, + $for_block_patt, + $generator_func_patt, + $loop_var_patt + ) { + + $full_match = $top_m[0]; + + $count = preg_match_all($start_for_block_patt, $full_match, $loops, PREG_OFFSET_CAPTURE); + while ($count--) { + + preg_match($for_block_patt, $full_match, $loop, PREG_OFFSET_CAPTURE, $loops[0][$count][1]); + + $list_text = Functions::executeOnString($loop['list'][0]); + + // Resolve the list of items for iteration. + // Either a generator function or a plain list. + $items = array(); + if (preg_match($generator_func_patt, $list_text, $m)) { + $func = strtolower($m['func']); + $args = Functions::parseArgs($m['parens_content']); + switch ($func) { + case 'range': + $items = call_user_func_array('range', $args); + break; + default: + $func = str_replace('-', '_', $func); + if (function_exists("CssCrush\loop_$func")) { + $items = call_user_func_array("CssCrush\loop_$func", $args); + } + } + } + else { + $items = Util::splitDelimList($list_text); + } + + // Multiply the text applying each iterated value. + $source_text = Template::unTokenize($loop['block_content'][0]); + $loop_output = ''; + $loop_var = $loop['var'][0]; + + foreach ($items as $index => $item) { + $loop_output .= preg_replace_callback($loop_var_patt, + function ($m) use ($index, $item, $loop_var) { + $arg = $m['arg']; + $arg_lc = strtolower($m['arg']); + + $parent_ref_patt = '~^loop\.parent\.~i'; + if (preg_match($parent_ref_patt, $arg)) { + return '#(' . preg_replace($parent_ref_patt, 'loop.', $arg) . ')'; + } + elseif ($arg_lc === 'loop.counter') { + return $index + 1; + } + elseif ($arg_lc === 'loop.counter0') { + return $index; + } + elseif ($arg === $loop_var || preg_replace('~^loop\.~i', 'loop.', $arg) === "loop.$loop_var") { + return $item; + } + else { + // Skip over. + return $m[0]; + } + }, $source_text); + } + $loop_output = Template::tokenize($loop_output); + + $full_match = substr_replace($full_match, $loop_output, $loop[0][1], strlen($loop[0][0])); + } + + // Remove unused loop variables before returning. + return preg_replace($loop_var_patt, '', $full_match); + }); +} + + +function loop_color_range () { + + $args = func_get_args(); + + $source_colors = array(); + while ($args && $color = Color::parse($args[0])) { + $source_colors[] = $color; + array_shift($args); + } + + $steps = max(1, isset($args[0]) ? (int) $args[0] : 1); + + $generated_colors = array(); + foreach ($source_colors as $index => $source_color) { + + $generated_colors[] = new Color($source_color); + + // Generate the in-between colors. + $next_source_color = isset($source_colors[$index + 1]) ? $source_colors[$index + 1] : null; + if (! $next_source_color) { + break; + } + for ($i = 0; $i < $steps; $i++) { + $rgba = array(); + foreach ($source_color as $component_index => $component_value) { + if ($component_diff = $next_source_color[$component_index] - $component_value) { + $component_step = $component_diff / ($steps+1); + $rgba[] = min(round($component_value + ($component_step * ($i+1)), 2), 255); + } + else { + $rgba[] = $component_value; + } + } + $generated_colors[] = new Color($rgba); + } + } + + return $generated_colors; +} From 9436472ce53e55fd3f9562577e3d923dd4d02944 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 29 Aug 2013 13:47:58 +0100 Subject: [PATCH 165/421] Slightly expanded the Tokens class to be useful in plugins. Updated the property-sorting file. --- lib/CssCrush/Tokens.php | 6 ++-- misc/property-sorting.ini | 70 ++++++++++++++++++++------------------- 2 files changed, 39 insertions(+), 37 deletions(-) diff --git a/lib/CssCrush/Tokens.php b/lib/CssCrush/Tokens.php index bcfacbf..adb9c30 100644 --- a/lib/CssCrush/Tokens.php +++ b/lib/CssCrush/Tokens.php @@ -11,9 +11,9 @@ class Tokens public $store; protected $ids; - public function __construct () + public function __construct (array $types = null) { - $types = array( + $types = $types ?: array( 's', // Strings 'c', // Comments 'r', // Rules @@ -179,7 +179,7 @@ static public function pad ($label, $replaced_text) static public function is ($label, $of_type) { - if (preg_match(Regex::make('~^ \? (?[a-z]) {{token-id}} \? $~xS'), $label, $m)) { + if (preg_match(Regex::make('~^ \? (?[a-zA-Z]) {{token-id}} \? $~xS'), $label, $m)) { return $of_type ? ($of_type === $m['type']) : true; } diff --git a/misc/property-sorting.ini b/misc/property-sorting.ini index 799d262..3dfab1c 100644 --- a/misc/property-sorting.ini +++ b/misc/property-sorting.ini @@ -1,6 +1,10 @@ ; Table for property sorting. ; Vendor prefixes are added at runtime. +; Generated content +content +quotes + ; Positioning position z-index @@ -18,12 +22,6 @@ overflow-x overflow-y vertical-align -; Generated content -content -counter-increment -counter-reset -quotes - ; Floats float clear @@ -60,88 +58,92 @@ margin-left ; Box-model: borders border -border-width -border-style -border-radius -border-image border-color +border-image +border-radius +border-style +border-width border-top -border-top-width -border-top-style +border-top-color border-top-left-radius border-top-right-radius -border-top-color +border-top-style +border-top-width border-right -border-right-width -border-right-style border-right-color +border-right-style +border-right-width border-bottom -border-bottom-width +border-bottom-color border-bottom-style border-bottom-left-radius border-bottom-right-radius -border-bottom-color +border-bottom-width border-left -border-left-width -border-left-style border-left-color +border-left-style +border-left-width ; Box-model: effects box-shadow +; Counters +counter-increment +counter-reset + ; Foreground color color ; Background background -background-image +background-attachment +background-clip background-color -background-repeat +background-image +background-origin background-position background-position-x background-position-y -background-origin +background-repeat background-size -background-clip -background-attachment ; Text direction text-align text-align-last -text-indent -text-overflow -text-transform text-decoration text-decoration-color text-decoration-line text-decoration-style +text-indent +text-overflow text-shadow +text-transform ; Fonts: general font font-family font-size -line-height -font-weight font-style +font-weight font-variant +line-height ; Fonts: spacing and behaviour letter-spacing +white-space +word-break word-spacing word-wrap -word-break -white-space hyphens orphans ; Outlines outline -outline-width -outline-style outline-color outline-offset +outline-style +outline-width ; Animations animation @@ -170,5 +172,5 @@ empty-cells ; Lists specific list-style list-style-image -list-style-type list-style-position +list-style-type From cb10b7fbf0f4e228af4b8438906e141ce02440fd Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 30 Aug 2013 11:53:36 +0100 Subject: [PATCH 166/421] Loop plugin rewritten to handle nested loops properly. --- plugins/loop.php | 178 +++++++++++++++++++++++++---------------------- 1 file changed, 94 insertions(+), 84 deletions(-) diff --git a/plugins/loop.php b/plugins/loop.php index fc3c156..36587de 100644 --- a/plugins/loop.php +++ b/plugins/loop.php @@ -15,16 +15,20 @@ * ... * * @example - * @for i in range(1, 12) { - * .grid-#(i)-of-12 { - * width: (#(i) / 12 * 100)%; - * } + * @for base in range(2, 24) { + * @for i in range(1, #(base)) { + * .grid-#(i)-of-#(base) { + * width: (#(i) / #(base) * 100)%; + * } + * } * } * * // Yields: - * .grid-1-of-12 { width: 8.33333%; } + * .grid-1-of-2 { width: 50%; } + * .grid-2-of-2 { width: 100%; } * ... - * .grid-12-of-12 { width: 100%; } + * .grid-23-of-24 { width: 95.83333%; } + * .grid-24-of-24 { width: 100%; } * * @example * // The last argument to color-range() is an integer specifying how many @@ -56,92 +60,98 @@ )); +define('CssCrush\LOOP_VAR_PATT', + '~\#\( \s* (?[a-zA-Z][\.a-zA-Z0-9-_]*) \s* \)~x'); +define('CssCrush\LOOP_PATT', + Regex::make('~(? @for \s+ (?{{ident}}) \s+ in \s+ (?[^{]+) ) \s* {{block}}~xiS')); + + function loop () { - $for_block_patt_base = '@for \s+ (?{{ident}}) \s+ in \s+ (?[^{]+) \s*'; - $for_block_patt = Regex::make('~' . $for_block_patt_base . '{{block}}~xiS'); - $start_for_block_patt = Regex::make('~' . $for_block_patt_base . '\{~xiS'); - $generator_func_patt = Regex::make('~(?range|color-range) {{parens}}~ix'); - $loop_var_patt = '~\#\( \s* (?[a-zA-Z][\.a-zA-Z0-9-_]*) \s* \)~x'; - - // Matching each root level loop construct then evaluating all nested loops and the enclosing loop. - CssCrush::$process->stream->pregReplaceCallback($for_block_patt, function ($top_m) use ( - $start_for_block_patt, - $for_block_patt, - $generator_func_patt, - $loop_var_patt - ) { - - $full_match = $top_m[0]; - - $count = preg_match_all($start_for_block_patt, $full_match, $loops, PREG_OFFSET_CAPTURE); - while ($count--) { - - preg_match($for_block_patt, $full_match, $loop, PREG_OFFSET_CAPTURE, $loops[0][$count][1]); - - $list_text = Functions::executeOnString($loop['list'][0]); - - // Resolve the list of items for iteration. - // Either a generator function or a plain list. - $items = array(); - if (preg_match($generator_func_patt, $list_text, $m)) { - $func = strtolower($m['func']); - $args = Functions::parseArgs($m['parens_content']); - switch ($func) { - case 'range': - $items = call_user_func_array('range', $args); - break; - default: - $func = str_replace('-', '_', $func); - if (function_exists("CssCrush\loop_$func")) { - $items = call_user_func_array("CssCrush\loop_$func", $args); - } - } - } - else { - $items = Util::splitDelimList($list_text); - } + CssCrush::$process->stream->pregReplaceCallback(LOOP_PATT, function ($m) { - // Multiply the text applying each iterated value. - $source_text = Template::unTokenize($loop['block_content'][0]); - $loop_output = ''; - $loop_var = $loop['var'][0]; - - foreach ($items as $index => $item) { - $loop_output .= preg_replace_callback($loop_var_patt, - function ($m) use ($index, $item, $loop_var) { - $arg = $m['arg']; - $arg_lc = strtolower($m['arg']); - - $parent_ref_patt = '~^loop\.parent\.~i'; - if (preg_match($parent_ref_patt, $arg)) { - return '#(' . preg_replace($parent_ref_patt, 'loop.', $arg) . ')'; - } - elseif ($arg_lc === 'loop.counter') { - return $index + 1; - } - elseif ($arg_lc === 'loop.counter0') { - return $index; - } - elseif ($arg === $loop_var || preg_replace('~^loop\.~i', 'loop.', $arg) === "loop.$loop_var") { - return $item; - } - else { - // Skip over. - return $m[0]; - } - }, $source_text); - } - $loop_output = Template::tokenize($loop_output); + return Template::tokenize(loop_unroll(Template::unTokenize($m[0]))); + }); +} + +function loop_unroll ($str, $context = array()) { + + $str = loop_apply_scope($str, $context); + + while (preg_match(LOOP_PATT, $str, $m, PREG_OFFSET_CAPTURE)) { + + $str = substr_replace($str, '', $m[0][1], strlen($m[0][0])); - $full_match = substr_replace($full_match, $loop_output, $loop[0][1], strlen($loop[0][0])); + $context['loop.parent.counter'] = isset($context['loop.counter']) ? $context['loop.counter'] : -1; + $context['loop.parent.counter0'] = isset($context['loop.counter0']) ? $context['loop.counter0'] : -1; + + foreach (loop_resolve_list($m['list'][0]) as $index => $value) { + + $str .= loop_unroll($m['block_content'][0], array( + $m['var'][0] => $value, + 'loop.counter' => $index + 1, + 'loop.counter0' => $index, + ) + $context); } + } - // Remove unused loop variables before returning. - return preg_replace($loop_var_patt, '', $full_match); - }); + return $str; } +function loop_resolve_list ($list_text) { + + // Resolve the list of items for iteration. + // Either a generator function or a plain list. + $items = array(); + + $list_text = Functions::executeOnString($list_text); + $generator_func_patt = Regex::make('~(?range|color-range) {{parens}}~ix'); + + if (preg_match($generator_func_patt, $list_text, $m)) { + $func = strtolower($m['func']); + $args = Functions::parseArgs($m['parens_content']); + switch ($func) { + case 'range': + $items = call_user_func_array('range', $args); + break; + default: + $func = str_replace('-', '_', $func); + if (function_exists("CssCrush\loop_$func")) { + $items = call_user_func_array("CssCrush\loop_$func", $args); + } + } + } + else { + $items = Util::splitDelimList($list_text); + } + + return $items; +} + +function loop_apply_scope ($str, $context) { + + // Need to temporarily hide child block scopes. + $child_scopes = array(); + + $str = preg_replace_callback(LOOP_PATT, function ($m) use (&$child_scopes) { + $label = '?B' . count($child_scopes) . '?'; + $child_scopes[$label] = $m['block']; + return $m['expression'] . $label; + }, $str); + + $str = preg_replace_callback(LOOP_VAR_PATT, function ($m) use ($context) { + + // Normalize casing of built-in loop variables. + // User variables are case-sensitive. + $arg = preg_replace_callback('~^loop\.(parent\.)?counter0?$~i', function ($m) { + return strtolower($m[0]); + }, $m['arg']); + + return isset($context[$arg]) ? $context[$arg] : ''; + }, $str); + + return str_replace(array_keys($child_scopes), array_values($child_scopes), $str); +} function loop_color_range () { From f093c063d8142044e3a4e685b59cca784bd352d3 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 30 Aug 2013 12:23:54 +0100 Subject: [PATCH 167/421] Added support for protocol-relative (//) URLs. --- lib/CssCrush/Url.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/CssCrush/Url.php b/lib/CssCrush/Url.php index 8bd601f..e685c23 100644 --- a/lib/CssCrush/Url.php +++ b/lib/CssCrush/Url.php @@ -58,10 +58,11 @@ public function __toString () public function evaluate () { - // Protocol based url. - if (preg_match('~^([a-z]+)\:~i', $this->value, $m)) { + // Protocol or protocol-relative (//) based URL. + if (preg_match('~^(?: (?[a-z]+)\: | \/{2} )~ix', $this->value, $m)) { + + $this->protocol = ! empty($m['protocol']) ? strtolower($m['protocol']) : 'relative'; - $this->protocol = strtolower($m[1]); switch ($this->protocol) { case 'data': $type = 'data'; @@ -71,8 +72,7 @@ public function evaluate () break; } } - - // Relative and rooted urls. + // Relative and rooted URLs. else { $type = 'relative'; $leading_variable = strpos($this->value, '$(') === 0; @@ -224,7 +224,7 @@ public function setType ($type = 'absolute') public function simplify () { - if (! $this->isData) { + if ($this->isRelative || $this->isRooted) { $this->value = Util::simplifyPath($this->value); } return $this; From 4fe88ad9955fc3ea238a35bbf4f10b030ec0f41c Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 3 Sep 2013 10:27:03 +0100 Subject: [PATCH 168/421] Made some changes to better handle rooted URLs (issue #50). Also some refactoring and removal of redundant comments for readability. --- lib/CssCrush/Importer.php | 67 ++++++++++++++++++--------------------- lib/CssCrush/Url.php | 20 ++++-------- lib/CssCrush/Util.php | 4 +-- 3 files changed, 39 insertions(+), 52 deletions(-) diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index fc515a4..8dec9ee 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -54,32 +54,27 @@ static public function hostfile () continue; } - // Fetch the URL object. - $url = $process->tokens->get($match[1][0]); + // Create import object for convenience. + $import = new \stdClass(); + $import->url = $process->tokens->get($match[1][0]); + $import->media = trim($match[2][0]); - // Pass over protocoled import urls. - if ($url->protocol) { + // Skip import if the import URL is protocoled. + if ($import->url->protocol) { $search_offset = $match_end; continue; } - // The media context (if specified). - $media_context = trim($match[2][0]); - - // Create import object. - $import = (object) array(); - $import->url = $url; - $import->mediaContext = $media_context; - - // Resolve import realpath. - if ($url->isRooted) { + // Resolve import path information. + if ($import->url->isRooted) { $import->path = realpath($process->docRoot . $import->url->value); } else { $import->path = realpath("$input->dir/{$import->url->value}"); } + $import->dir = dirname($import->path); - // Get the import contents, if unsuccessful just continue with the import line removed. + // If unsuccessful getting import contents continue with the import line removed. $import->content = @file_get_contents($import->path); if ($import->content === false) { @@ -88,37 +83,38 @@ static public function hostfile () continue; } - // Import file opened successfully so we process it: - // - We need to resolve import statement urls in all imported files since - // they will be brought inline with the hostfile + // Import file exists so register it. $process->sources[] = $import->path; + $mtimes[] = filemtime($import->path); + $filenames[] = $import->url->value; - // If there are unmatched brackets inside the import, strip it. + // If the import content doesn't pass syntax validation skip to next import. if (! self::prepareForStream($import->content)) { $str = substr_replace($str, '', $match_start, $match_len); continue; } - $import->dir = dirname($import->url->value); - - // Store import file info for cache validation. - $mtimes[] = filemtime($import->path); - $filenames[] = $import->url->value; + // Resolve a relative link between the import file and the host-file. + if ($import->url->isRooted) { + $import->relativeDir = Util::getLinkBetweenPaths($import->dir, $input->dir); + } + else { + $import->relativeDir = dirname($import->url->value); + } - // Alter all the @import urls to be paths relative to the hostfile. + // Alter all embedded import URLs to be relative to the host-file. foreach (Regex::matchAll($regex->import, $import->content) as $m) { - // Fetch the matched URL. - $url2 = $process->tokens->get($m[1][0]); + $nested_url = $process->tokens->get($m[1][0]); - // Try to resolve absolute paths. - // On failure strip the @import statement. - if ($url2->isRooted) { - $url2->resolveRootedPath(); + // Resolve rooted paths. + if ($nested_url->isRooted) { + $link = Util::getLinkBetweenPaths(dirname($nested_url->getAbsolutePath()), $import->dir); + $nested_url->update($link . basename($nested_url->value)); } - else { - $url2->prepend("$import->dir/"); + elseif (strlen($import->relativeDir)) { + $nested_url->prepend("$import->relativeDir/"); } } @@ -127,9 +123,8 @@ static public function hostfile () self::rewriteImportedUrls($import); } - // Add media context if it exists. - if ($import->mediaContext) { - $import->content = "@media $import->mediaContext {{$import->content}}"; + if ($import->media) { + $import->content = "@media $import->media {{$import->content}}"; } $str = substr_replace($str, $import->content, $match_start, $match_len); diff --git a/lib/CssCrush/Url.php b/lib/CssCrush/Url.php index e685c23..ff35627 100644 --- a/lib/CssCrush/Url.php +++ b/lib/CssCrush/Url.php @@ -56,6 +56,13 @@ public function __toString () return "url(http://github.com/$quote$this-%3Evalue$quote)"; } + public function update ($new_value) + { + $this->value = $new_value; + + return $this->evaluate(); + } + public function evaluate () { // Protocol or protocol-relative (//) based URL. @@ -123,19 +130,6 @@ public function getOriginalValue () return "$function($this->value)"; } - public function resolveRootedPath () - { - $process = CssCrush::$process; - - if (! file_exists ($process->docRoot . $this->value)) { - return false; - } - - // Move upwards '..' by the number of slashes in baseURL to get a relative path. - $this->value = str_repeat('../', substr_count($process->input->dirUrl, '/')) . - substr($this->value, 1); - } - public function prepend ($path_fragment) { if ($this->isRelative) { diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php index f551943..7d6f630 100644 --- a/lib/CssCrush/Util.php +++ b/lib/CssCrush/Util.php @@ -135,11 +135,9 @@ static public function splitDelimList ($str, $delim = ',') static public function getLinkBetweenPaths ($path1, $path2, $directories = true) { - // Normalise the paths. $path1 = trim(Util::normalizePath($path1, true), '/'); $path2 = trim(Util::normalizePath($path2, true), '/'); - // The link between. $link = ''; if ($path1 != $path2) { @@ -159,7 +157,7 @@ static public function getLinkBetweenPaths ($path1, $path2, $directories = true) $link = $link !== '' ? rtrim($link, '/') : ''; - // Add end slash if getting a link between directories. + // Append end slash if getting a link between directories. if ($link && $directories) { $link .= '/'; } From c458d3bd769dffb7318c4052e84f5d6e7716858f Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 4 Sep 2013 13:47:37 +0100 Subject: [PATCH 169/421] Path specified in `output_dir` option now creates the output directory if it doesn't exist. Added recovery callback argument to `Util::resolveUserPath()` --- lib/CssCrush/Options.php | 13 ++++++++++++- lib/CssCrush/Util.php | 23 +++++++++++++++-------- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/lib/CssCrush/Options.php b/lib/CssCrush/Options.php index 6196084..3b2ae35 100644 --- a/lib/CssCrush/Options.php +++ b/lib/CssCrush/Options.php @@ -50,13 +50,24 @@ public function __set ($name, $value) break; // Path options. - case 'output_dir': case 'boilerplate': if (is_string($value)) { $value = Util::resolveUserPath($value); } break; + case 'output_dir': + if (is_string($value)) { + $value = Util::resolveUserPath($value, function ($dir) use ($name) { + if (! @mkdir($dir)) { + trigger_error(__METHOD__ . ': Could not find or create directory ' . + "specified by `$name` option.\n", E_USER_NOTICE); + } + return $dir; + }); + } + break; + // Path options that only accept system paths. case 'context': case 'doc_root': diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php index 7d6f630..1baf431 100644 --- a/lib/CssCrush/Util.php +++ b/lib/CssCrush/Util.php @@ -50,20 +50,27 @@ static public function simplifyPath ($path) return $path; } - static public function resolveUserPath ($path) + static public function resolveUserPath ($path, $recovery = null) { // System path. if ($realpath = realpath($path)) { $path = $realpath; } - // WWW root path. - elseif (strpos($path, '/') === 0) { - $doc_root = isset(CssCrush::$process) ? CssCrush::$process->docRoot : CssCrush::$config->docRoot; - $path = realpath($doc_root . $path); - } - // Relative path. Try resolving based on the directory of the executing script. else { - $path = realpath(CssCrush::$config->scriptDir . '/' . $path); + // WWW root path. + if (strpos($path, '/') === 0) { + $doc_root = isset(CssCrush::$process) ? CssCrush::$process->docRoot : CssCrush::$config->docRoot; + $path = $doc_root . $path; + } + // Relative path. Try resolving based on the directory of the executing script. + else { + $path = CssCrush::$config->scriptDir . '/' . $path; + } + + if (! file_exists($path) && is_callable($recovery)) { + $path = call_user_func($recovery, $path); + } + $path = realpath($path); } return $path ? Util::normalizePath($path) : false; From f71b83ffef005ccde06b4e67fcf96348ff9f81e7 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 6 Sep 2013 12:23:32 +0100 Subject: [PATCH 170/421] Some tweaks to SVG output to better support common svg2png convertors. Changed default SVG type from 'rect' to 'path'. --- plugins/svg.php | 57 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 18 deletions(-) diff --git a/plugins/svg.php b/plugins/svg.php index 7805569..a118caa 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -203,7 +203,7 @@ function svg_generator ($input, $fn_name) { // Resolve the type. // Bail if type not recognised. - $type = isset($raw_data['type']) ? strtolower($raw_data['type']) : 'rect'; + $type = isset($raw_data['type']) ? strtolower($raw_data['type']) : 'path'; if (! isset($schemas[$type])) { return ''; @@ -549,7 +549,7 @@ function svg_starpath ($cx, $cy, $points, $outer_r, $inner_r = null, $twist = 0, $y = ( $outer_r * sin($outer_angle) ) + $cy; if ($points != $s) { - $d[] = "$x,$y"; + $d[] = "$x $y"; } // If star shape is required need inner angles too. @@ -565,11 +565,11 @@ function svg_starpath ($cx, $cy, $points, $outer_r, $inner_r = null, $twist = 0, $ix = ( $inner_r * cos($inner_angle) ) + $cx; $iy = ( $inner_r * sin($inner_angle) ) + $cy; - $d[] = "$ix,$iy"; + $d[] = "$ix $iy"; } } - return 'M' . implode(' ', $d) . 'Z'; + return 'M' . implode('L', $d) . 'Z'; } function svg_apply_filters ($element) { @@ -721,7 +721,6 @@ function svg_render ($element) { $styles_data = array( '@font-face' => $element->face_styles, 'svg' => $element->svg_styles, - $element->tag => $element->styles, ); foreach ($styles_data as $selector => $declarations) { if ($declarations) { @@ -735,24 +734,46 @@ function svg_render ($element) { $styles = CssCrush::$process->tokens->restore($styles, 'u', true); $styles = CssCrush::$process->tokens->restore($styles, 's'); - $attrs = Util::htmlAttributes($element->attrs); + // Add element styles as attributes which tend to work better with svg2png converters. + $attrs = Util::htmlAttributes($element->attrs + $element->styles); + + // Add viewbox to help IE scale correctly. + if (isset($element->svg_attrs['width']) && isset($element->svg_attrs['height'])) { + $element->svg_attrs += array( + 'viewbox' => implode(' ', array( + 0, + 0, + $element->svg_attrs['width'], + $element->svg_attrs['height'] + )), + ); + } $svg_attrs = Util::htmlAttributes($element->svg_attrs); // Markup. $svg[] = ""; - $svg[] = ''; - $svg[] = implode($element->fills['gradients']); - $svg[] = implode($element->fills['patterns']); - $svg[] = implode($element->filters); - if ($styles) { - $cdata = preg_match('~[<>&]~', $styles); - $svg[] = ''; + + if ( + $element->fills['gradients'] || + $element->fills['patterns'] || + $element->filters || + $styles + ) { + $svg[] = ''; + $svg[] = implode($element->fills['gradients']); + $svg[] = implode($element->fills['patterns']); + $svg[] = implode($element->filters); + if ($styles) { + $cdata = preg_match('~[<>&]~', $styles); + $svg[] = ''; + } + $svg[] = ''; } - $svg[] = ''; + if ($element->tag === 'text') { $svg[] = "{$element->data['text']}"; } From c87dccadc97203b1e54d4e8cff491536f8793577 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 11 Sep 2013 09:47:12 +0100 Subject: [PATCH 171/421] Forms plugin `:input` selector alias now accepts multiple type arguments. Added `position:sticky` declaration alias. --- Aliases.ini | 2 ++ lib/CssCrush/Functions.php | 2 +- lib/CssCrush/Url.php | 2 +- plugins/forms.php | 25 +++++++++++++++---------- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/Aliases.ini b/Aliases.ini index 6a48286..12ddda6 100644 --- a/Aliases.ini +++ b/Aliases.ini @@ -301,6 +301,8 @@ appearance:none[] = -webkit-appearance:none appearance:none[] = -moz-appearance:none + position:sticky[] = position:-webkit-sticky + ;---------------------------------------------------------------- ; Function aliases. diff --git a/lib/CssCrush/Functions.php b/lib/CssCrush/Functions.php index 6a13786..b51050c 100644 --- a/lib/CssCrush/Functions.php +++ b/lib/CssCrush/Functions.php @@ -124,7 +124,7 @@ static public function register ($name, $callback) static public function deRegister ($name) { - unset(Functions::$customFunctions[ $name]); + unset(Functions::$customFunctions[$name]); } static public function parseArgs ($input, $allowSpaceDelim = false) diff --git a/lib/CssCrush/Url.php b/lib/CssCrush/Url.php index ff35627..e9dfd1b 100644 --- a/lib/CssCrush/Url.php +++ b/lib/CssCrush/Url.php @@ -20,7 +20,7 @@ class Url public $value; public $label; - public function __construct ($raw_value, $convert_to_data = false) + public function __construct ($raw_value) { $regex = Regex::$patt; $tokens = CssCrush::$process->tokens; diff --git a/plugins/forms.php b/plugins/forms.php index 9d1ebfc..363c923 100644 --- a/plugins/forms.php +++ b/plugins/forms.php @@ -3,18 +3,16 @@ * Pseudo classes for working with forms. * * @before - * :text {...} + * :input(date, search, email) {...} * :checkbox {...} * :radio {...} - * :button {...} - * :input(date) {...} + * :text {...} * * @after - * input[type="text"] {...} + * input[type="date"], input[type="search"], input[type="email"] {...} * input[type="checkbox"] {...} * input[type="radio"] {...} - * input[type="button"], button {...} - * input[type="date"] {...} + * input[type="text"] {...} */ namespace CssCrush; @@ -34,15 +32,22 @@ function forms () { return array( - 'input' => 'input[type="#(0 text)"]', - 'button' => ':any(button, input[type="button"])', + 'input' => function ($args) { + $types = array(); + foreach ($args as $type) { + $types[] = "[type=\"$type\"]"; + } + + $result = $types ? 'input:any(' . implode(',', $types) . ')' : 'input[type="text"]'; + return CssCrush::$process->tokens->captureStrings($result); + }, + 'checkbox' => 'input[type="checkbox"]', + 'radio' => 'input[type="radio"]', 'file' => 'input[type="file"]', 'image' => 'input[type="image"]', 'password' => 'input[type="password"]', 'submit' => 'input[type="submit"]', - 'radio' => 'input[type="radio"]', - 'reset' => 'input[type="reset"]', 'text' => 'input[type="text"]', ); } From 6052c8d01c08439f7c0e85e9cfcb9b48a04c8369 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 11 Sep 2013 13:44:12 +0100 Subject: [PATCH 172/421] Removed `csscrush_clearcache()` function and associated static method. It's functionality can be easily replicated in plain php since all outoutput files have a '.crush.css' file extension. --- lib/CssCrush/CssCrush.php | 12 +----------- lib/CssCrush/IO.php | 27 --------------------------- lib/functions.php | 4 ---- 3 files changed, 1 insertion(+), 42 deletions(-) diff --git a/lib/CssCrush/CssCrush.php b/lib/CssCrush/CssCrush.php index 7ccdb18..001bf33 100644 --- a/lib/CssCrush/CssCrush.php +++ b/lib/CssCrush/CssCrush.php @@ -455,16 +455,6 @@ static public function globalVars ($vars) } } - /** - * Clear config file and compiled files for the specified directory. - * - * @param string $dir System path to the directory. - */ - static public function clearCache ($dir = '') - { - return $process->io('clearCache', $dir); - } - /** * Get debug info. * Depends on arguments passed to the trace option. @@ -480,7 +470,7 @@ static public function stat () 'compile_time' => 0 ); - // Lose stats that are only useful internally. + // Lose stats that are only used internally. unset($stats['compile_start_time']); return $stats; diff --git a/lib/CssCrush/IO.php b/lib/CssCrush/IO.php index 3b639f8..6a9d6bf 100644 --- a/lib/CssCrush/IO.php +++ b/lib/CssCrush/IO.php @@ -161,33 +161,6 @@ static public function validateExistingOutput () return false; } - static public function clearCache ($dir) - { - if (empty($dir)) { - $dir = __DIR__; - } - elseif (! file_exists($dir)) { - return; - } - - $configPath = $dir . '/' . CssCrush::$process->cacheFile; - if (file_exists($configPath)) { - unlink($configPath); - } - - // Remove any compiled files - $suffix = '.crush.css'; - $suffixLength = strlen($suffix); - - foreach (scandir($dir) as $file) { - if ( - strpos($file, $suffix) === strlen($file) - $suffixLength - ) { - unlink($dir . "/{$file}"); - } - } - } - static public function getCacheData () { $config = CssCrush::$config; diff --git a/lib/functions.php b/lib/functions.php index cc3540f..78fda51 100644 --- a/lib/functions.php +++ b/lib/functions.php @@ -26,10 +26,6 @@ function csscrush_globalvars ($vars) { return CssCrush::globalVars($vars); } -function csscrush_clearcache ($dir = '') { - return CssCrush::clearcache($dir); -} - function csscrush_stat () { return CssCrush::stat(); } From 817e9a9fa48b314b4b71f6e86842ea213bba41a6 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 12 Sep 2013 09:34:06 +0100 Subject: [PATCH 173/421] Added `stat_dump` option for saving stats and variables used to a file in json format. Simplified signature of internal `CssCrush::runStat()` method. --- lib/CssCrush/CssCrush.php | 94 ++++++++++++++------------------------- lib/CssCrush/IO.php | 14 +++++- lib/CssCrush/Options.php | 15 +++++-- lib/CssCrush/Process.php | 6 +-- lib/CssCrush/Url.php | 12 ++--- 5 files changed, 67 insertions(+), 74 deletions(-) diff --git a/lib/CssCrush/CssCrush.php b/lib/CssCrush/CssCrush.php index 001bf33..85a50fe 100644 --- a/lib/CssCrush/CssCrush.php +++ b/lib/CssCrush/CssCrush.php @@ -40,57 +40,24 @@ static public function init () self::$config->selectorAliases = array(); self::$config->plugins = array(); - // Default options. + // Set option defaults, see wiki for details. self::$config->options = new Options(array( - - // Minify. Set false for formatting and comments. 'minify' => true, - - // Alternative formatter to use for un-minified output. 'formatter' => null, - - // Appends checksum query string to output file name. 'versioning' => true, - - // Use the template boilerplate. 'boilerplate' => true, - - // Variables passed in at runtime. 'vars' => array(), - - // Enable/disable the cache. 'cache' => true, - - // Output filename. Defaults the hostfile filename. 'output_file' => null, - - // Output directory. Defaults to the same directory as the host file. 'output_dir' => null, - - // Alternative document_root may be used to workaround server aliases and rewrites. 'doc_root' => null, - - // Vendor target. Only apply prefixes for a specific vendor, set to 'none' for no prefixes. 'vendor_target' => 'all', - - // Whether to rewrite the url references inside imported files. 'rewrite_import_urls' => true, - - // List of plugins to enable (as Array of names). 'enable' => null, - - // List of plugins to disable (as Array of names). 'disable' => null, - - // Debugging options. - // Set true to output sass debug-info stubs that work with development tools like FireSass. + 'stat_dump' => false, 'trace' => array(), - - // Whether to generate a source map. 'source_map' => false, - - // Force newline type on output files. Defaults to the current platform newline. - // Options: 'windows' (or 'win'), 'unix', 'use-platform' 'newlines' => 'use-platform', )); @@ -274,6 +241,7 @@ static public function file ($file, $options = null) $process->input->path = $input_file; $process->input->filename = basename($input_file); $process->input->mtime = filemtime($input_file); + CssCrush::runStat('hostfile'); } else { return ''; @@ -293,6 +261,7 @@ static public function file ($file, $options = null) $valid_compliled_file = $process->io('validateExistingOutput'); if (is_string($valid_compliled_file)) { + $process->release(); return $valid_compliled_file; } } @@ -457,7 +426,6 @@ static public function globalVars ($vars) /** * Get debug info. - * Depends on arguments passed to the trace option. */ static public function stat () { @@ -467,12 +435,9 @@ static public function stat () // Get logged errors as late as possible. $stats['errors'] = $process->errors; $stats += array( - 'compile_time' => 0 + 'compile_time' => 0, ); - // Lose stats that are only used internally. - unset($stats['compile_start_time']); - return $stats; } @@ -552,35 +517,42 @@ static public function logError ($errors) } } - static public function runStat ($name) + static public function runStat () { $process = CssCrush::$process; - $trace = $process->options->trace; + $all_rules =& $process->tokens->store->r; - if ($name == 'compile_time') { - $time = microtime(true); - $process->stat['compile_time'] = $time - $process->stat['compile_start_time']; - return; - } + foreach (func_get_args() as $stat_name) { - if (! $trace || ! in_array($name, $trace)) { - return; - } + switch ($stat_name) { + case 'hostfile': + $process->stat['hostfile'] = $process->input->filename; + break; - $all_rules =& $process->tokens->store->r; + case 'vars': + $process->stat['vars'] = $process->vars; + break; - switch ($name) { + case 'computed_vars': + $process->stat['computed_vars'] = array_map('CssCrush\Functions::executeOnString', $process->vars); + break; - case 'selector_count': - $process->stat['selector_count'] = 0; - foreach ($all_rules as $rule) { - $process->stat['selector_count'] += count($rule->selectors); - } - break; + case 'compile_time': + $process->stat['compile_time'] = microtime(true) - $process->stat['compile_start_time']; + unset($process->stat['compile_start_time']); + break; - case 'rule_count': - $process->stat['rule_count'] = count($all_rules); - break; + case 'selector_count': + $process->stat['selector_count'] = 0; + foreach ($all_rules as $rule) { + $process->stat['selector_count'] += count($rule->selectors); + } + break; + + case 'rule_count': + $process->stat['rule_count'] = count($all_rules); + break; + } } } } diff --git a/lib/CssCrush/IO.php b/lib/CssCrush/IO.php index 6a9d6bf..664247f 100644 --- a/lib/CssCrush/IO.php +++ b/lib/CssCrush/IO.php @@ -227,10 +227,20 @@ static public function write (Stream $stream) if (Util::filePutContents($target, $stream, __METHOD__)) { + $json_encode_flags = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 0; + if ($process->sourceMap) { - $flags = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 0; Util::filePutContents("{$process->output->dir}/$source_map_filename", - json_encode($process->sourceMap, $flags), __METHOD__); + json_encode($process->sourceMap, $json_encode_flags), __METHOD__); + } + + if ($process->options->stat_dump) { + $stat_file = is_string($process->options->stat_dump) ? + $process->options->stat_dump : + $process->output->dir . '/' . $process->output->filename . '.json'; + + $GLOBALS['CSSCRUSH_STAT_FILE'] = $stat_file; + Util::filePutContents($stat_file, json_encode(csscrush_stat(), $json_encode_flags), __METHOD__); } return "{$process->output->dirUrl}/{$process->output->filename}"; diff --git a/lib/CssCrush/Options.php b/lib/CssCrush/Options.php index 3b2ae35..301aa91 100644 --- a/lib/CssCrush/Options.php +++ b/lib/CssCrush/Options.php @@ -56,14 +56,23 @@ public function __set ($name, $value) } break; + case 'stat_dump': + if (is_string($value)) { + $value = Util::resolveUserPath($value, function ($path) { + touch($path); + return $path; + }); + } + break; + case 'output_dir': if (is_string($value)) { - $value = Util::resolveUserPath($value, function ($dir) use ($name) { - if (! @mkdir($dir)) { + $value = Util::resolveUserPath($value, function ($path) use ($name) { + if (! @mkdir($path)) { trigger_error(__METHOD__ . ': Could not find or create directory ' . "specified by `$name` option.\n", E_USER_NOTICE); } - return $dir; + return $path; }); } break; diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index b77d7db..0ddf0ce 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -74,6 +74,7 @@ public function release () $this->tokens, $this->mixins, $this->references, + $this->cacheData, $this->misc, $this->plugins, $this->aliases, @@ -842,9 +843,8 @@ protected function collate () $this->stream->replaceTokens('r'); - // Run rule related stats then reclaim memory. - CssCrush::runStat('selector_count'); - CssCrush::runStat('rule_count'); + // Record stats then drop rule objects to reclaim memory. + CssCrush::runStat('selector_count', 'rule_count', 'vars', 'computed_vars'); $this->tokens->store->r = array(); $this->stream->replaceTokens('p'); diff --git a/lib/CssCrush/Url.php b/lib/CssCrush/Url.php index e9dfd1b..43d553d 100644 --- a/lib/CssCrush/Url.php +++ b/lib/CssCrush/Url.php @@ -20,12 +20,15 @@ class Url public $value; public $label; - public function __construct ($raw_value) + public function __construct ($raw_value, $options = array()) { - $regex = Regex::$patt; - $tokens = CssCrush::$process->tokens; + $standalone = ! empty($options['standalone']); + if (! $standalone) { + $tokens = CssCrush::$process->tokens; + $this->label = $tokens->add($this, 'u'); + } - if (preg_match($regex->s_token, $raw_value)) { + if (! $standalone && preg_match(Regex::$patt->s_token, $raw_value)) { $this->value = trim($tokens->get($raw_value), '\'"'); $tokens->release($raw_value); } @@ -34,7 +37,6 @@ public function __construct ($raw_value) } $this->evaluate(); - $this->label = $tokens->add($this, 'u'); } public function __toString () From cf366dc383d7ea252d69db05e6a1ae1addff744d Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 12 Sep 2013 11:11:41 +0100 Subject: [PATCH 174/421] Removed `csscrush_globalvars()` function and associated static method; use `csscrush_set()` instead. --- lib/CssCrush/CssCrush.php | 29 +---------------------------- lib/functions.php | 4 ---- 2 files changed, 1 insertion(+), 32 deletions(-) diff --git a/lib/CssCrush/CssCrush.php b/lib/CssCrush/CssCrush.php index 85a50fe..7d8b3a1 100644 --- a/lib/CssCrush/CssCrush.php +++ b/lib/CssCrush/CssCrush.php @@ -210,7 +210,7 @@ static public function loadAssets () ############################# - # External API. + # Public API. /** * Process host CSS file and return a new compiled file. @@ -397,33 +397,6 @@ static public function string ($string, $options = null) return $process->compile('filter'); } - /** - * Add variables globally. - * - * @param mixed $var Assoc array of variable names and values, a php ini filename or null. - */ - static public function globalVars ($vars) - { - $config = self::$config; - - // Merge into the stack, overrides existing variables of the same name. - if (is_array($vars)) { - $config->vars = $vars + $config->vars; - } - - // Test for a file. If it is attempt to parse it. - elseif (is_string($vars) && file_exists($vars)) { - if ($result = @parse_ini_file($vars)) { - $config->vars = $result + $config->vars; - } - } - - // Clear the stack if the argument is explicitly null. - elseif (is_null($vars)) { - $config->vars = array(); - } - } - /** * Get debug info. */ diff --git a/lib/functions.php b/lib/functions.php index 78fda51..057324a 100644 --- a/lib/functions.php +++ b/lib/functions.php @@ -22,10 +22,6 @@ function csscrush_string ($string, $options = null) { return CssCrush::string($string, $options); } -function csscrush_globalvars ($vars) { - return CssCrush::globalVars($vars); -} - function csscrush_stat () { return CssCrush::stat(); } From 112e1af749271b0eae5f3a0016c5ad710c97f58f Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 14 Sep 2013 13:50:47 +0100 Subject: [PATCH 175/421] Sepreated aliases file parsing into its own method `CssCrush::parseAliasesFile()` so merging with other alias files, or using external alias file is possible (issue #51). Renamed the default boilerplate file to use a conventional file extention. Deprecated and removed the *-local.ini now there is a better ways of augmenting the default aliases. Lowercased the Aliases.ini filename. Moved the library location property `CssCrush::$config->location` to `CssCrush::$dir` --- CssCrush.boilerplate | 2 - Aliases.ini => aliases.ini | 0 boilerplate.txt | 2 + lib/CssCrush/Color.php | 2 +- lib/CssCrush/CssCrush.php | 139 ++++++++++++++++++------------------ lib/CssCrush/Process.php | 5 +- lib/CssCrush/Util.php | 11 --- plugins/initial.php | 2 +- plugins/property-sorter.php | 2 +- 9 files changed, 75 insertions(+), 90 deletions(-) delete mode 100644 CssCrush.boilerplate rename Aliases.ini => aliases.ini (100%) create mode 100644 boilerplate.txt diff --git a/CssCrush.boilerplate b/CssCrush.boilerplate deleted file mode 100644 index 3feeafb..0000000 --- a/CssCrush.boilerplate +++ /dev/null @@ -1,2 +0,0 @@ -CSS-Crush(ed) on {{datetime}} -http://github.com/peteboere/css-crush ({{version}}) \ No newline at end of file diff --git a/Aliases.ini b/aliases.ini similarity index 100% rename from Aliases.ini rename to aliases.ini diff --git a/boilerplate.txt b/boilerplate.txt new file mode 100644 index 0000000..fb1ade1 --- /dev/null +++ b/boilerplate.txt @@ -0,0 +1,2 @@ +CSS-Crush(ed) on {{datetime}} +http://github.com/peteboere/css-crush ({{version}}) diff --git a/lib/CssCrush/Color.php b/lib/CssCrush/Color.php index 6de5856..c5ecb6c 100644 --- a/lib/CssCrush/Color.php +++ b/lib/CssCrush/Color.php @@ -17,7 +17,7 @@ static public function &loadKeywords () if (! isset(self::$keywords)) { $table = array(); - $path = CssCrush::$config->location . '/misc/color-keywords.ini'; + $path = CssCrush::$dir . '/misc/color-keywords.ini'; if ($keywords = parse_ini_file($path)) { foreach ($keywords as $word => $rgb) { $rgb = array_map('intval', explode(',', $rgb)); diff --git a/lib/CssCrush/CssCrush.php b/lib/CssCrush/CssCrush.php index 7d8b3a1..aa30e14 100644 --- a/lib/CssCrush/CssCrush.php +++ b/lib/CssCrush/CssCrush.php @@ -16,16 +16,18 @@ class CssCrush // The current active process. static public $process; + // Library root directory. + static public $dir; + // Init called once manually post class definition. static public function init () { - self::$config = new \stdClass(); + self::$dir = dirname(dirname(__DIR__)); - // Path to the project root folder. - self::$config->location = dirname(dirname(__DIR__)); + self::$config = new \stdClass(); // Plugin directories. - self::$config->pluginDirs = array(self::$config->location . '/plugins'); + self::$config->pluginDirs = array(self::$dir . '/plugins'); self::$config->version = new Version(self::VERSION); self::$config->scriptDir = dirname($_SERVER['SCRIPT_FILENAME']); @@ -36,7 +38,15 @@ static public function init () // Shared resources. self::$config->vars = array(); + self::$config->aliasesFile = self::$dir . '/aliases.ini'; self::$config->aliases = array(); + self::$config->bareAliases = array( + 'properties' => array(), + 'functions' => array(), + 'function_groups' => array(), + 'declarations' => array(), + 'at-rules' => array(), + ); self::$config->selectorAliases = array(); self::$config->plugins = array(); @@ -62,7 +72,7 @@ static public function init () )); // Include and register stock formatters. - require_once self::$config->location . '/misc/formatters.php'; + require_once self::$dir . '/misc/formatters.php'; } static protected function resolveDocRoot ($doc_root = null) @@ -109,103 +119,90 @@ static protected function resolveDocRoot ($doc_root = null) return Util::normalizePath($doc_root); } - // Aliases and macros loader. static public function loadAssets () { static $called; if ($called) { return; } + $called = true; + + if (! self::$config->aliases) { + $aliases = self::parseAliasesFile(self::$config->aliasesFile); + self::$config->aliases = $aliases ?: self::$config->bareAliases; + } + } + + static public function parseAliasesFile ($file) + { + $tree = @parse_ini_file($file, true); - // Find an aliases file in the root directory - // a local file overrides the default - $aliases_file = Util::find('Aliases-local.ini', 'Aliases.ini'); + if ($tree === false) { - // Load aliases file if it exists - if ($aliases_file) { + trigger_error(__METHOD__ . ": Could not parse aliases file '$file'.\n", E_USER_NOTICE); - $result = @parse_ini_file($aliases_file, true); - if ($result !== false) { + return false; + } - $regex = Regex::$patt; + $regex = Regex::$patt; - foreach ($result as $section => $items) { + // Some alias groups need further parsing to unpack useful information into the tree. + foreach ($tree as $section => $items) { - // Declaration aliases require a little preparation. - // Also extracting vendor context (if any). - if ($section === 'declarations') { + if ($section === 'declarations') { - $store = array(); - foreach ($items as $prop_val => $aliases) { + $store = array(); + foreach ($items as $prop_val => $aliases) { - list($prop, $value) = array_map('trim', explode(':', $prop_val)); + list($prop, $value) = array_map('trim', explode(':', $prop_val)); - foreach ($aliases as &$alias) { + foreach ($aliases as &$alias) { - list($p, $v) = explode(':', $alias); - $vendor = null; + list($p, $v) = explode(':', $alias); + $vendor = null; - // Try to detect the vendor from property and value in turn. - if ( - preg_match($regex->vendorPrefix, $p, $m) || - preg_match($regex->vendorPrefix, $v, $m) - ) { - $vendor = $m[1]; - } - $alias = array($p, $v, $vendor); - } - $store[$prop][$value] = $aliases; + // Try to detect the vendor from property and value in turn. + if ( + preg_match($regex->vendorPrefix, $p, $m) || + preg_match($regex->vendorPrefix, $v, $m) + ) { + $vendor = $m[1]; } - $result['declarations'] = $store; + $alias = array($p, $v, $vendor); } + $store[$prop][$value] = $aliases; + } + $tree['declarations'] = $store; + } - // Function groups. - elseif (strpos($section, 'functions:') === 0) { - $group = substr($section, strlen('functions')); + // Function groups. + elseif (strpos($section, 'functions:') === 0) { - $vendor_grouped_aliases = array(); - foreach ($items as $func_name => $aliases) { + $group = substr($section, strlen('functions')); - // Assign group name to the aliasable function. - $result['functions'][$func_name] = $group; + $vendor_grouped_aliases = array(); + foreach ($items as $func_name => $aliases) { - foreach ($aliases as $alias_func) { + // Assign group name to the aliasable function. + $tree['functions'][$func_name] = $group; - // Only supporting vendor prefixed aliases, for now. - if (preg_match($regex->vendorPrefix, $alias_func, $m)) { + foreach ($aliases as $alias_func) { - // We'll cache the function matching regex here. - $vendor_grouped_aliases[$m[1]]['find'][] = - Regex::make('~{{LB}}' . $func_name . '{{RTB}}~i'); - $vendor_grouped_aliases[$m[1]]['replace'][] = $alias_func; - } - } + // Only supporting vendor prefixed aliases, for now. + if (preg_match($regex->vendorPrefix, $alias_func, $m)) { + + // We'll cache the function matching regex here. + $vendor_grouped_aliases[$m[1]]['find'][] = + Regex::make('~{{LB}}' . $func_name . '{{RTB}}~i'); + $vendor_grouped_aliases[$m[1]]['replace'][] = $alias_func; } - $result['function_groups'][$group] = $vendor_grouped_aliases; } } - - self::$config->aliases = $result; - - // Ensure all alias groups are at least set (issue #34) - self::$config->bareAliasGroups = array( - 'properties' => array(), - 'functions' => array(), - 'function_groups' => array(), - 'declarations' => array(), - 'at-rules' => array(), - ); - self::$config->aliases += self::$config->bareAliasGroups; - } - else { - trigger_error(__METHOD__ . ": Aliases file could not be parsed.\n", E_USER_NOTICE); + $tree['function_groups'][$group] = $vendor_grouped_aliases; } } - else { - trigger_error(__METHOD__ . ": Aliases file not found.\n", E_USER_NOTICE); - } - $called = true; + return $tree + self::$config->bareAliases; } diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 0ddf0ce..c5b5ffc 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -12,7 +12,6 @@ public function __construct ($options) { $config = CssCrush::$config; - // Load in aliases and plugins. CssCrush::loadAssets(); // Create options instance for this process. @@ -131,7 +130,7 @@ protected function getBoilerplate () $boilerplate_option = $this->options->boilerplate; if ($boilerplate_option === true) { - $file = Util::find('CssCrush-local.boilerplate', 'CssCrush.boilerplate'); + $file = CssCrush::$dir . '/boilerplate.txt'; } elseif (is_string($boilerplate_option)) { if (file_exists($boilerplate_option)) { @@ -283,7 +282,7 @@ protected function filterAliases () // For expicit 'none' argument turn off aliases. if ('none' === $vendors) { - $this->aliases = CssCrush::$config->bareAliasGroups; + $this->aliases = CssCrush::$config->bareAliases; return; } diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php index 1baf431..8a2a43c 100644 --- a/lib/CssCrush/Util.php +++ b/lib/CssCrush/Util.php @@ -76,17 +76,6 @@ static public function resolveUserPath ($path, $recovery = null) return $path ? Util::normalizePath($path) : false; } - static public function find () - { - foreach (func_get_args() as $file) { - $file_path = CssCrush::$config->location . '/' . $file; - if (file_exists($file_path)) { - return $file_path; - } - } - return false; - } - static public function stripCommentTokens ($str) { return preg_replace(Regex::$patt->c_token, '', $str); diff --git a/plugins/initial.php b/plugins/initial.php index 1c93d4d..7838baa 100644 --- a/plugins/initial.php +++ b/plugins/initial.php @@ -30,7 +30,7 @@ function initial (Rule $rule) { static $initial_values; if (! $initial_values) { - if (! ($initial_values = @parse_ini_file(CssCrush::$config->location . '/misc/initial-values.ini'))) { + if (! ($initial_values = @parse_ini_file(CssCrush::$dir . '/misc/initial-values.ini'))) { trigger_error(__METHOD__ . ": Initial keywords file could not be parsed.\n", E_USER_NOTICE); return; } diff --git a/plugins/property-sorter.php b/plugins/property-sorter.php index a269ada..b1cd469 100644 --- a/plugins/property-sorter.php +++ b/plugins/property-sorter.php @@ -151,7 +151,7 @@ function &property_sorter_get_table () { // Load from property-sorting.ini. $sorting_file_contents = - file_get_contents(CssCrush::$config->location . '/misc/property-sorting.ini'); + file_get_contents(CssCrush::$dir . '/misc/property-sorting.ini'); if ($sorting_file_contents !== false) { $sorting_file_contents = preg_replace('~;[^\r\n]*~', '', $sorting_file_contents); From 4f6fcd265dc04ff8fd7cfc685bcb62b7f5c2bcd6 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 18 Sep 2013 12:42:29 +0100 Subject: [PATCH 176/421] Added `asset_dir` option for directing generated svg and image files. --- lib/CssCrush/CssCrush.php | 1 + lib/CssCrush/Options.php | 1 + plugins/canvas.php | 14 +++++++++++--- plugins/svg.php | 14 ++++++++++---- 4 files changed, 23 insertions(+), 7 deletions(-) diff --git a/lib/CssCrush/CssCrush.php b/lib/CssCrush/CssCrush.php index aa30e14..59b9c1e 100644 --- a/lib/CssCrush/CssCrush.php +++ b/lib/CssCrush/CssCrush.php @@ -60,6 +60,7 @@ static public function init () 'cache' => true, 'output_file' => null, 'output_dir' => null, + 'asset_dir' => null, 'doc_root' => null, 'vendor_target' => 'all', 'rewrite_import_urls' => true, diff --git a/lib/CssCrush/Options.php b/lib/CssCrush/Options.php index 301aa91..4f98f13 100644 --- a/lib/CssCrush/Options.php +++ b/lib/CssCrush/Options.php @@ -66,6 +66,7 @@ public function __set ($name, $value) break; case 'output_dir': + case 'asset_dir': if (is_string($value)) { $value = Util::resolveUserPath($value, function ($path) use ($name) { if (! @mkdir($path)) { diff --git a/plugins/canvas.php b/plugins/canvas.php index 9a95002..7c23b2a 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -149,7 +149,16 @@ function canvas_generator ($input, $context) { // Create fingerprint for this canvas based on canvas object. $fingerprint = substr(md5(serialize($canvas)), 0, 7); $generated_filename = "cnv-$name-$fingerprint.png"; - $generated_filepath = $process->output->dir . '/' . $generated_filename; + + if (! empty($process->options->asset_dir)) { + $generated_filepath = $process->options->asset_dir . '/' . $generated_filename; + $generated_url = Util::getLinkBetweenPaths( + $process->output->dir, $process->options->asset_dir) . $generated_filename; + } + else { + $generated_filepath = $process->output->dir . '/' . $generated_filename; + $generated_url = $generated_filename; + } $cached_file = file_exists($generated_filepath); // $cached_file = false; @@ -229,8 +238,7 @@ function canvas_generator ($input, $context) { imagepng($canvas->image, $generated_filepath); } - // Write to the same directory as the output css. - $url = new Url($generated_filename); + $url = new Url($generated_url); $url->noRewrite = true; } // Or create data uri. diff --git a/plugins/svg.php b/plugins/svg.php index a118caa..e76e8af 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -277,12 +277,18 @@ function svg_generator ($input, $fn_name) { $fingerprint = substr(md5($flattened_svg), 0, 7); $generated_filename = "svg-$name-$fingerprint.svg"; - // Write to the same directory as the output css. - $generated_path = $process->output->dir . '/' . $generated_filename; + if (! empty($process->options->asset_dir)) { + $generated_filepath = $process->options->asset_dir . '/' . $generated_filename; + $generated_url = Util::getLinkBetweenPaths( + $process->output->dir, $process->options->asset_dir) . $generated_filename; + } + else { + $generated_filepath = $process->output->dir . '/' . $generated_filename; + $generated_url = $generated_filename; + } - Util::filePutContents($generated_path, $flattened_svg, __METHOD__); + Util::filePutContents($generated_filepath, $flattened_svg, __METHOD__); - $generated_url = $generated_filename; $url = new Url($generated_url); $url->noRewrite = true; } From 28b835e3d2a22b4dbdceda1a95baba298433be25 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 20 Sep 2013 11:57:11 +0100 Subject: [PATCH 177/421] File methods now return relative URLs is the input file path is relative. Cleaned up some of the I/O related code. --- lib/CssCrush/CssCrush.php | 77 ++++++----------- lib/CssCrush/IO.php | 169 ++++++++++++++++++++------------------ lib/CssCrush/Process.php | 39 +++++---- lib/CssCrush/Util.php | 33 +++++++- 4 files changed, 163 insertions(+), 155 deletions(-) diff --git a/lib/CssCrush/CssCrush.php b/lib/CssCrush/CssCrush.php index 59b9c1e..3e04cf4 100644 --- a/lib/CssCrush/CssCrush.php +++ b/lib/CssCrush/CssCrush.php @@ -30,7 +30,7 @@ static public function init () self::$config->pluginDirs = array(self::$dir . '/plugins'); self::$config->version = new Version(self::VERSION); - self::$config->scriptDir = dirname($_SERVER['SCRIPT_FILENAME']); + self::$config->scriptDir = dirname(realpath($_SERVER['SCRIPT_FILENAME'])); self::$config->docRoot = self::resolveDocRoot(); // Set default IO handler. @@ -226,6 +226,8 @@ static public function file ($file, $options = null) $options = $process->options; $doc_root = $process->docRoot; + $process->input->raw = $file; + if (! ($input_file = Util::resolveUserPath($file))) { $basename = basename($file); $error = "Input file '$basename' not found."; @@ -235,45 +237,25 @@ static public function file ($file, $options = null) return ''; } - if ($process->setContext(dirname($input_file))) { - $process->input->path = $input_file; - $process->input->filename = basename($input_file); - $process->input->mtime = filemtime($input_file); - CssCrush::runStat('hostfile'); - } - else { + if (! $process->resolveContext(dirname($input_file), $input_file)) { + return ''; } - // Create a filename that will be used later - // Used in validateCache, and writing to filesystem - $process->output->filename = $process->io('getOutputFileName'); + CssCrush::runStat('hostfile'); - // Caching. if ($options->cache) { - - // Load the cache data. $process->cacheData = $process->io('getCacheData'); - - // If cache is enabled check for a valid compiled file. - $valid_compliled_file = $process->io('validateExistingOutput'); - - if (is_string($valid_compliled_file)) { + if ($process->io('validateCache')) { $process->release(); - return $valid_compliled_file; + + return $process->io('getOutputUrl'); } } $stream = $process->compile(); - // Create file and return url. Return empty string on failure. - if ($url = $process->io('write', $stream)) { - $timestamp = $options->versioning ? '?' . time() : ''; - return "$url$timestamp"; - } - else { - return ''; - } + return $process->io('write', $stream) ? $process->io('getOutputUrl') : ''; } /** @@ -284,28 +266,25 @@ static public function file ($file, $options = null) * @param array $attributes An array of HTML attributes. * @return string HTML link tag or error message inside HTML comment. */ - static public function tag ($file, $options = null, $attributes = array()) + static public function tag ($file, $options = null, $tag_attributes = array()) { $file = self::file($file, $options); if (! empty($file)) { - - // On success return the tag with any custom attributes - $attributes['rel'] = 'stylesheet'; - $attributes['href'] = $file; - - // Should media type be forced to 'all'? - if (! isset($attributes['media'])) { - $attributes['media'] = 'all'; - } - $attr_string = Util::htmlAttributes($attributes); - return "\n"; + $tag_attributes['href'] = $file; + $tag_attributes += array( + 'rel' => 'stylesheet', + 'media' => 'all', + ); + $attrs = Util::htmlAttributes($tag_attributes, array('rel', 'href', 'media')); + + return "\n"; } else { - // Return an HTML comment with message on failure $class = __CLASS__; $errors = implode("\n", self::$process->errors); + return "\n"; } } @@ -318,7 +297,7 @@ static public function tag ($file, $options = null, $attributes = array()) * @param array $attributes An array of HTML attributes, set false to return CSS text without tag. * @return string HTML link tag or error message inside HTML comment. */ - static public function inline ($file, $options = null, $attributes = array()) + static public function inline ($file, $options = null, $tag_attributes = array()) { // For inline output set boilerplate to not display by default if (! is_array($options)) { @@ -333,13 +312,12 @@ static public function inline ($file, $options = null, $attributes = array()) if (! empty($file)) { // On success fetch the CSS text - $content = file_get_contents(self::$process->output->dir . '/' - . self::$process->output->filename); + $content = file_get_contents(self::$process->output->dir . '/' . self::$process->output->filename); $tag_open = ''; $tag_close = ''; - if (is_array($attributes)) { - $attr_string = Util::htmlAttributes($attributes); + if (is_array($tag_attributes)) { + $attr_string = Util::htmlAttributes($tag_attributes); $tag_open = ""; $tag_close = ''; } @@ -377,10 +355,10 @@ static public function string ($string, $options = null) // Set the path context if one is given. // Fallback to document root. if (! empty($options->context)) { - $process->setContext($options->context, false); + $process->resolveContext($options->context); } else { - $process->setContext($process->docRoot, false); + $process->resolveContext($process->docRoot); } // Set the string on the input object. @@ -391,8 +369,7 @@ static public function string ($string, $options = null) $process->input->importIgnore = true; } - // Note we're passing the alternative ioContext. - return $process->compile('filter'); + return $process->compile(); } /** diff --git a/lib/CssCrush/IO.php b/lib/CssCrush/IO.php index 664247f..9ff7568 100644 --- a/lib/CssCrush/IO.php +++ b/lib/CssCrush/IO.php @@ -18,27 +18,28 @@ static public function getOutputDir () { $process = CssCrush::$process; $output_dir = $process->options->output_dir; + return $output_dir ? $output_dir : $process->input->dir; } static public function testOutputDir () { - $output_dir = CssCrush::$process->output->dir; + $dir = CssCrush::$process->output->dir; $pathtest = true; $error = false; - if (! file_exists($output_dir)) { + if (! file_exists($dir)) { - $error = "Output directory '$output_dir' doesn't exist."; + $error = "Output directory '$dir' doesn't exist."; $pathtest = false; } - elseif (! is_writable($output_dir)) { + elseif (! is_writable($dir)) { CssCrush::log('Attempting to change permissions.'); - if (! @chmod($output_dir, 0755)) { + if (! @chmod($dir, 0755)) { - $error = "Output directory '$output_dir' is unwritable."; + $error = "Output directory '$dir' is unwritable."; $pathtest = false; } else { @@ -68,97 +69,105 @@ static public function getOutputFileName () return "$output_basename.crush.css"; } - static public function validateExistingOutput () + static public function getOutputUrl () { $process = CssCrush::$process; $options = $process->options; - $config = CssCrush::$config; - $input = $process->input; + $filename = $process->output->filename; - // Search base directory for an existing compiled file. - foreach (scandir($process->output->dir) as $filename) { + $url = $process->output->dirUrl . '/' . $filename; - if ($process->output->filename != $filename) { - continue; - } + // Make URL relative if the input path was relative. + $input_path = new Url($process->input->raw, array('standalone' => true)); + if ($input_path->isRelative) { + $url = Util::getLinkBetweenPaths(CssCrush::$config->scriptDir, $process->output->dir) . $filename; + } - // Cached file exists. - CssCrush::log('Cached file exists.'); + // Optional query-string timestamp. + if ($options->versioning !== false) { + $url .= '?'; + if (isset($process->cacheData[$filename]['datem_sum'])) { + $url .= $process->cacheData[$filename]['datem_sum']; + } + else { + $url .= time(); + } + } - $existingfile = (object) array(); - $existingfile->filename = $filename; - $existingfile->path = "{$process->output->dir}/$existingfile->filename"; - $existingfile->URL = "{$process->output->dirUrl}/$existingfile->filename"; + return $url; + } - // Start off with the input file then add imported files - $all_files = array($input->mtime); + static public function validateCache () + { + $process = CssCrush::$process; + $config = CssCrush::$config; + $options = $process->options; + $input = $process->input; + $output = $process->output; - if (file_exists($existingfile->path) && isset($process->cacheData[$process->output->filename])) { + $filename = $output->filename; - // File exists and has config - CssCrush::log('Cached file is registered.'); + if (! file_exists($output->dir . '/' . $filename)) { + CssCrush::log('No file cached.'); - foreach ($process->cacheData[$existingfile->filename]['imports'] as $import_file) { + return false; + } - // Check if this is docroot relative or input dir relative. - $root = strpos($import_file, '/') === 0 ? $process->docRoot : $process->input->dir; - $import_filepath = realpath($root) . "/$import_file"; + if (! isset($process->cacheData[$filename])) { + CssCrush::log('Cached file exists but is not registered.'); - if (file_exists($import_filepath)) { - $all_files[] = filemtime($import_filepath); - } - else { - // File has been moved, remove old file and skip to compile. - CssCrush::log('Recompiling - an import file has been moved.'); + return false; + } - return false; - } - } + $data =& $process->cacheData[$filename]; - // Cast because the cached options may be a \stdClass if an IO adapter has been used. - $cached_options = (array) $process->cacheData[$existingfile->filename]['options']; - $active_options = $options->get(); - - // Compare runtime options and cached options for differences. - $options_changed = false; - foreach ($cached_options as $key => &$value) { - if (isset($active_options[$key]) && $active_options[$key] !== $value) { - $options_changed = true; - break; - } - } + // Make stack of file mtimes starting with the input file. + $file_sums = array($input->mtime); + foreach ($data['imports'] as $import_file) { - // Check if any of the files have changed. - $existing_datesum = $process->cacheData[$existingfile->filename]['datem_sum']; - $files_changed = $existing_datesum != array_sum($all_files); + // Check if this is docroot relative or input dir relative. + $root = strpos($import_file, '/') === 0 ? $process->docRoot : $input->dir; + $import_filepath = realpath($root) . "/$import_file"; - if (! $options_changed && ! $files_changed) { + if (file_exists($import_filepath)) { + $file_sums[] = filemtime($import_filepath); + } + else { + // File has been moved, remove old file and skip to compile. + CssCrush::log('Recompiling - an import file has been moved.'); - // Files have not been modified and config is the same: return the old file. - CssCrush::log( - "Files and options have not been modified, returning existing file '$existingfile->URL'."); - return $existingfile->URL . ($options->versioning !== false ? "?$existing_datesum" : ''); - } - else { - - if ($options_changed) { - CssCrush::log('Recompiling - options have been modified.'); - } - if ($files_changed) { - CssCrush::log('Recompiling - files have been modified.'); - } - } + return false; } - elseif (file_exists($existingfile->path)) { + } - CssCrush::log('Recompiling - file exists but no cache data.'); + $files_changed = $data['datem_sum'] != array_sum($file_sums); + if ($files_changed) { + CssCrush::log('Files have been modified. Recompiling.'); + } + + // Compare runtime options and cached options for differences. + // Cast because the cached options may be a \stdClass if an IO adapter has been used. + $options_changed = false; + $cached_options = (array) $data['options']; + $active_options = $options->get(); + foreach ($cached_options as $key => &$value) { + if (isset($active_options[$key]) && $active_options[$key] !== $value) { + CssCrush::log('Options have been changed. Recompiling.'); + $options_changed = true; + break; } + } - return false; + if (! $options_changed && ! $files_changed) { + CssCrush::log("Files and options have not been modified, returning cached file."); - } // foreach + return true; + } + else { + $data['datem_sum'] = array_sum($file_sums); - return false; + return false; + } } static public function getCacheData () @@ -218,32 +227,30 @@ static public function saveCacheData () static public function write (Stream $stream) { $process = CssCrush::$process; - $target = "{$process->output->dir}/{$process->output->filename}"; + $output = $process->output; if ($process->sourceMap) { - $source_map_filename = $process->output->filename . '.map'; - $stream->append($process->newline . "/*# sourceMappingURL=$source_map_filename */"); + $stream->append($process->newline . "/*# sourceMappingURL=$source_map_filename.map */"); } - if (Util::filePutContents($target, $stream, __METHOD__)) { + if (Util::filePutContents("$output->dir/$output->filename", $stream, __METHOD__)) { $json_encode_flags = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 0; if ($process->sourceMap) { - Util::filePutContents("{$process->output->dir}/$source_map_filename", + Util::filePutContents("$output->dir/$source_map_filename", json_encode($process->sourceMap, $json_encode_flags), __METHOD__); } if ($process->options->stat_dump) { $stat_file = is_string($process->options->stat_dump) ? - $process->options->stat_dump : - $process->output->dir . '/' . $process->output->filename . '.json'; + $process->options->stat_dump : "$output->dir/$output->filename.json"; $GLOBALS['CSSCRUSH_STAT_FILE'] = $stat_file; Util::filePutContents($stat_file, json_encode(csscrush_stat(), $json_encode_flags), __METHOD__); } - return "{$process->output->dirUrl}/{$process->output->filename}"; + return true; } return false; diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index c5b5ffc..da1e598 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -81,34 +81,35 @@ public function release () ); } - public function setContext ($input_dir, $test_output_dir = true) + public function resolveContext ($input_dir, $input_file = null) { - $doc_root = $this->docRoot; - - // If not a system path. - if (strpos($input_dir, $doc_root) !== 0) { - $input_dir = Util::normalizePath(realpath("$doc_root/$input_dir")); + if ($input_file) { + $this->ioContext = 'file'; + $this->input->path = $input_file; + $this->input->filename = basename($input_file); + $this->input->mtime = filemtime($input_file); + } + else { + $this->ioContext = 'filter'; + $this->input->path = null; + $this->input->filename = null; } - // Initialise input object and store input directory. - $this->input->path = null; - $this->input->filename = null; $this->input->dir = $input_dir; - $this->input->dirUrl = substr($input_dir, strlen($doc_root)); + $this->input->dirUrl = substr($input_dir, strlen($this->docRoot)); - // Store reference to the output dir. $this->output->dir = $this->io('getOutputDir'); - $this->output->dirUrl = substr($this->output->dir, strlen($doc_root)); + $this->output->filename = $this->io('getOutputFileName'); + $this->output->dirUrl = substr($this->output->dir, strlen($this->docRoot)); - // Test the output directory to see it exists and is writable. - $output_dir_ok = false; - if ($test_output_dir) { - $output_dir_ok = $this->io('testOutputDir'); + $context_resolved = true; + if ($input_file) { + $context_resolved = $this->io('testOutputDir'); } $this->io('init'); - return $output_dir_ok; + return $context_resolved; } public function io ($method) @@ -916,7 +917,7 @@ protected function collate () } } - public function compile ($io_context = 'file') + public function compile () { // Always store start time. $this->stat['compile_start_time'] = microtime(true); @@ -929,8 +930,6 @@ public function compile ($io_context = 'file') ini_set('memory_limit', '128M'); } - $this->ioContext = $io_context; - // Shortcut commonly used options during compilation to avoid overhead with __get calls. $this->minifyOutput = $this->options->minify; $this->addTracingStubs = in_array('stubs', $this->options->trace); diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php index 8a2a43c..7fec45a 100644 --- a/lib/CssCrush/Util.php +++ b/lib/CssCrush/Util.php @@ -8,14 +8,39 @@ class Util { - static public function htmlAttributes (array $attributes) + static public function htmlAttributes (array $attributes, array $sort_order = null) { - $attr_string = ''; + // Optionally sort attributes (for better readability). + if ($sort_order) { + uksort($attributes, function ($a, $b) use ($sort_order) { + $a_index = array_search($a, $sort_order); + $b_index = array_search($b, $sort_order); + $a_found = is_int($a_index); + $b_found = is_int($b_index); + + if ($a_found && $b_found) { + if ($a_index == $b_index) { + return 0; + } + return $a_index > $b_index ? 1 : -1; + } + elseif ($a_found && ! $b_found) { + return -1; + } + elseif ($b_found && ! $a_found) { + return 1; + } + + return strcmp($a, $b); + }); + } + + $str = ''; foreach ($attributes as $name => $value) { $value = htmlspecialchars($value, ENT_COMPAT, 'UTF-8', false); - $attr_string .= " $name=\"$value\""; + $str .= " $name=\"$value\""; } - return $attr_string; + return $str; } static public function normalizePath ($path, $strip_drive_letter = false) From 6218881bdf58627321028ef325d1e87ffdb86c8a Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 20 Sep 2013 14:25:46 +0100 Subject: [PATCH 178/421] If `formatter` option is set will now override the `minify` option (setting it to false) --- cli.php | 3 +-- lib/CssCrush/CssCrush.php | 2 +- lib/CssCrush/Process.php | 5 +++++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/cli.php b/cli.php index eeedfca..694ba78 100755 --- a/cli.php +++ b/cli.php @@ -545,8 +545,7 @@ function manpage () { taking raw input from STDIN. --formatter: - Formatter to use for formatted (--pretty) output. - Available formatters: + Formatting styles. 'block' (default) - Rules are block formatted. diff --git a/lib/CssCrush/CssCrush.php b/lib/CssCrush/CssCrush.php index 3e04cf4..4a964f7 100644 --- a/lib/CssCrush/CssCrush.php +++ b/lib/CssCrush/CssCrush.php @@ -72,7 +72,7 @@ static public function init () 'newlines' => 'use-platform', )); - // Include and register stock formatters. + // Register stock formatters. require_once self::$dir . '/misc/formatters.php'; } diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index da1e598..2298cf1 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -20,6 +20,11 @@ public function __construct ($options) // Populate option defaults. $this->options->merge($config->options); + // Override the minify option if a formatter is specified. + if ($this->options->formatter) { + $this->options->minify = false; + } + // Keep track of global vars to maintain cache integrity. $this->options->global_vars = $config->vars; From 2677f4938b7aeed315f10d332471c9fa288dc34f Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 21 Sep 2013 13:40:48 +0100 Subject: [PATCH 179/421] Switched to PSR-3 compatible logging. --- lib/CssCrush/BalancedMatch.php | 6 +- lib/CssCrush/CssCrush.php | 67 ++------------- lib/CssCrush/IO.php | 40 ++++----- lib/CssCrush/Importer.php | 24 +++--- lib/CssCrush/Logger.php | 152 +++++++++++++++++++++++++++++++++ lib/CssCrush/Options.php | 4 +- lib/CssCrush/Plugin.php | 2 +- lib/CssCrush/Process.php | 15 ++-- lib/CssCrush/Util.php | 4 +- plugins/canvas.php | 21 ++--- plugins/initial.php | 3 +- plugins/property-sorter.php | 5 +- 12 files changed, 214 insertions(+), 129 deletions(-) create mode 100644 lib/CssCrush/Logger.php diff --git a/lib/CssCrush/BalancedMatch.php b/lib/CssCrush/BalancedMatch.php index 7fd0d92..f29dc84 100644 --- a/lib/CssCrush/BalancedMatch.php +++ b/lib/CssCrush/BalancedMatch.php @@ -18,12 +18,14 @@ public function __construct (Stream $stream, $offset, $brackets = '{}') list($opener, $closer) = str_split($brackets, 1); if (strpos($stream->raw, $opener, $this->offset) === false) { + return; } if (substr_count($stream->raw, $opener) !== substr_count($stream->raw, $closer)) { $sample = substr($stream->raw, $this->offset, 25); - trigger_error(__METHOD__ . ": Unmatched token near '$sample'.\n", E_USER_WARNING); + CssCrush::$process->logger->warning("[[CssCrush]] - Unmatched token near '$sample'."); + return; } @@ -38,7 +40,7 @@ public function __construct (Stream $stream, $offset, $brackets = '{}') $this->length = $this->matchEnd - $this->offset; } else { - trigger_error(__METHOD__ . ": Could not match '$opener'. Exiting.\n", E_USER_WARNING); + CssCrush::$process->logger->warning("[[CssCrush]] - Could not match '$opener'. Exiting."); } } diff --git a/lib/CssCrush/CssCrush.php b/lib/CssCrush/CssCrush.php index 4a964f7..148515c 100644 --- a/lib/CssCrush/CssCrush.php +++ b/lib/CssCrush/CssCrush.php @@ -103,17 +103,11 @@ static protected function resolveDocRoot ($doc_root = null) } if (! $doc_root) { - - // If doc_root is still falsy, fallback to DOCUMENT_ROOT $doc_root = realpath($_SERVER['DOCUMENT_ROOT']); } if (! $doc_root) { - - // If doc_root is still falsy, log an error - $error = "Could not get a document_root reference."; - CssCrush::logError($error); - trigger_error(__METHOD__ . ": $error\n", E_USER_NOTICE); + CssCrush::$process->logger->warning("[[CssCrush]] - Could not get a valid DOCUMENT_ROOT reference."); } } @@ -139,8 +133,7 @@ static public function parseAliasesFile ($file) $tree = @parse_ini_file($file, true); if ($tree === false) { - - trigger_error(__METHOD__ . ": Could not parse aliases file '$file'.\n", E_USER_NOTICE); + CssCrush::$process->logger->notice("[[CssCrush]] - Could not parse aliases file '$file'."); return false; } @@ -229,10 +222,7 @@ static public function file ($file, $options = null) $process->input->raw = $file; if (! ($input_file = Util::resolveUserPath($file))) { - $basename = basename($file); - $error = "Input file '$basename' not found."; - CssCrush::logError($error); - trigger_error(__METHOD__ . ": $error\n", E_USER_WARNING); + $process->logger->warning('[[CssCrush]] - Input file \'' . basename($file) . '\' not found.'); return ''; } @@ -407,62 +397,21 @@ static public function removeSelectorAlias ($name) ############################# # Logging and stats. - static public $logging = false; - - static public $log = array(); - - static public function log ($arg = null, $label = null, $var_dump = false) + static public function printLog () { - if (! self::$logging) { - - return; - } - - // If no arguments are passed return the log. - if (! func_num_args()) { + if (! empty(self::$process->debugLog)) { if (PHP_SAPI !== 'cli') { $out = array(); - foreach (CssCrush::$log as $item) { + foreach (self::$process->debugLog as $item) { $out[] = '
' . htmlspecialchars($item) . '
'; } - return implode('
', $out); + echo implode('
', $out); } else { - return implode(PHP_EOL, CssCrush::$log) . PHP_EOL; + echo implode(PHP_EOL, self::$process->debugLog), PHP_EOL; } } - - if ($label) { - $label = PHP_EOL . "$label" . PHP_EOL . str_repeat('=', strlen($label)) . PHP_EOL; - } - else { - $label = ''; - } - - if (is_string($arg)) { - CssCrush::$log[] = "$label$arg"; - } - else { - ob_start(); - $var_dump ? var_dump($arg) : print_r($arg); - CssCrush::$log[] = $label . ob_get_clean(); - } - } - - static public function clearLog () - { - CssCrush::$log = array(); - } - - static public function logError ($errors) - { - $errors = (array) $errors; - self::$process->errors = array_merge($errors, self::$process->errors); - - foreach ($errors as $error) { - self::log($error); - } } static public function runStat () diff --git a/lib/CssCrush/IO.php b/lib/CssCrush/IO.php index 9ff7568..5448d69 100644 --- a/lib/CssCrush/IO.php +++ b/lib/CssCrush/IO.php @@ -25,33 +25,26 @@ static public function getOutputDir () static public function testOutputDir () { $dir = CssCrush::$process->output->dir; + $logger = CssCrush::$process->logger; $pathtest = true; - $error = false; if (! file_exists($dir)) { - - $error = "Output directory '$dir' doesn't exist."; + $logger->warning("[[CssCrush]] - Output directory '$dir' doesn't exist."); $pathtest = false; } elseif (! is_writable($dir)) { - CssCrush::log('Attempting to change permissions.'); + $logger->debug('Attempting to change permissions.'); if (! @chmod($dir, 0755)) { - - $error = "Output directory '$dir' is unwritable."; + $logger->warning("[[CssCrush]] - Output directory '$dir' is unwritable."); $pathtest = false; } else { - CssCrush::log('Permissions updated.'); + $logger->debug('Permissions updated.'); } } - if ($error) { - CssCrush::logError($error); - trigger_error(__METHOD__ . ": $error\n", E_USER_WARNING); - } - return $pathtest; } @@ -108,13 +101,13 @@ static public function validateCache () $filename = $output->filename; if (! file_exists($output->dir . '/' . $filename)) { - CssCrush::log('No file cached.'); + $process->logger->debug('No file cached.'); return false; } if (! isset($process->cacheData[$filename])) { - CssCrush::log('Cached file exists but is not registered.'); + $process->logger->debug('Cached file exists but is not registered.'); return false; } @@ -134,7 +127,7 @@ static public function validateCache () } else { // File has been moved, remove old file and skip to compile. - CssCrush::log('Recompiling - an import file has been moved.'); + $process->logger->debug('Recompiling - an import file has been moved.'); return false; } @@ -142,7 +135,7 @@ static public function validateCache () $files_changed = $data['datem_sum'] != array_sum($file_sums); if ($files_changed) { - CssCrush::log('Files have been modified. Recompiling.'); + $process->logger->debug('Files have been modified. Recompiling.'); } // Compare runtime options and cached options for differences. @@ -152,14 +145,14 @@ static public function validateCache () $active_options = $options->get(); foreach ($cached_options as $key => &$value) { if (isset($active_options[$key]) && $active_options[$key] !== $value) { - CssCrush::log('Options have been changed. Recompiling.'); + $process->logger->debug('Options have been changed. Recompiling.'); $options_changed = true; break; } } if (! $options_changed && ! $files_changed) { - CssCrush::log("Files and options have not been modified, returning cached file."); + $process->logger->debug("Files and options have not been modified, returning cached file."); return true; } @@ -193,20 +186,17 @@ static public function getCacheData () $cache_data = json_decode(file_get_contents($process->cacheFile), true) ) { // Successfully loaded config file. - CssCrush::log('Cache data loaded.'); + $process->logger->debug('Cache data loaded.'); } else { // Config file may exist but not be writable (may not be visible in some ftp situations?) if ($cache_data_exists) { if (! @unlink($process->cacheFile)) { - - $error = "Could not delete config data file."; - CssCrush::logError($error); - trigger_error(__METHOD__ . ": $error\n", E_USER_NOTICE); + CssCrush::$process->logger->notice('[[CssCrush]] - Could not delete cache data file.'); } } else { - CssCrush::log('Creating cache data file.'); + $process->logger->debug('Creating cache data file.'); } Util::filePutContents($process->cacheFile, json_encode(array()), __METHOD__); } @@ -218,7 +208,7 @@ static public function saveCacheData () { $process = CssCrush::$process; - CssCrush::log('Saving config.'); + $process->logger->debug('Saving config.'); $flags = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 0; Util::filePutContents($process->cacheFile, json_encode($process->cacheData, $flags), __METHOD__); diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index 8dec9ee..9034e14 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -78,7 +78,7 @@ static public function hostfile () $import->content = @file_get_contents($import->path); if ($import->content === false) { - CssCrush::log("Import file '{$import->url->value}' not found"); + $process->logger->debug("Import file '{$import->url->value}' not found"); $str = substr_replace($str, '', $match_start, $match_len); continue; } @@ -214,12 +214,13 @@ static protected function prepareForStream (&$str) static protected function checkSyntax (&$str) { // Catch obvious typing errors. - $parse_errors = array(); + $errors = false; $current_file = 'file://' . end(CssCrush::$process->sources); $balanced_parens = substr_count($str, "(") === substr_count($str, ")"); $balanced_curlies = substr_count($str, "{") === substr_count($str, "}"); - $validate_pairings = function ($str, $pairing) use ($current_file) { + $validate_pairings = function ($str, $pairing) use ($current_file) + { if ($pairing === '{}') { $opener_patt = '~\{~'; $balancer_patt = Regex::make('~^{{block}}~'); @@ -259,20 +260,17 @@ static protected function checkSyntax (&$str) }; if (! $balanced_curlies) { - $parse_errors[] = $validate_pairings($str, '{}') ?: "Unbalanced '{' in $current_file."; + $errors = true; + CssCrush::$process->logger->warning( + '[[CssCrush]] - ' . $validate_pairings($str, '{}') ?: "Unbalanced '{' in $current_file."); } if (! $balanced_parens) { - $parse_errors[] = $validate_pairings($str, '()') ?: "Unbalanced '(' in $current_file."; + $errors = true; + CssCrush::$process->logger->warning( + '[[CssCrush]] - ' . $validate_pairings($str, '()') ?: "Unbalanced '(' in $current_file."); } - if ($parse_errors) { - foreach ($parse_errors as $error_msg) { - CssCrush::logError($error_msg); - trigger_error("$error_msg\n", E_USER_WARNING); - } - } - - return empty($parse_errors) ? true : false; + return $errors ? false : true; } static protected function addMarkers (&$str) diff --git a/lib/CssCrush/Logger.php b/lib/CssCrush/Logger.php new file mode 100644 index 0000000..fa38fd9 --- /dev/null +++ b/lib/CssCrush/Logger.php @@ -0,0 +1,152 @@ +error($message, $context); + } + + /** + * Action must be taken immediately. + * + * Example: Entire website down, database unavailable, etc. This should + * trigger the SMS alerts and wake you up. + * + * @param string $message + * @param array $context + * @return null + */ + public function alert($message, array $context = array()) + { + $this->error($message, $context); + } + + /** + * Critical conditions. + * + * Example: Application component unavailable, unexpected exception. + * + * @param string $message + * @param array $context + * @return null + */ + public function critical($message, array $context = array()) + { + $this->error($message, $context); + } + + /** + * Runtime errors that do not require immediate action but should typically + * be logged and monitored. + * + * @param string $message + * @param array $context + * @return null + */ + public function error($message, array $context = array()) + { + CssCrush::$process->errors[] = $message; + trigger_error($message, E_USER_ERROR); + } + + /** + * Exceptional occurrences that are not errors. + * + * Example: Use of deprecated APIs, poor use of an API, undesirable things + * that are not necessarily wrong. + * + * @param string $message + * @param array $context + * @return null + */ + public function warning($message, array $context = array()) + { + CssCrush::$process->errors[] = $message; + trigger_error($message, E_USER_WARNING); + } + + /** + * Normal but significant events. + * + * @param string $message + * @param array $context + * @return null + */ + public function notice($message, array $context = array()) + { + CssCrush::$process->errors[] = $message; + trigger_error($message, E_USER_NOTICE); + } + + /** + * Interesting events. + * + * Example: User logs in, SQL logs. + * + * @param string $message + * @param array $context + * @return null + */ + public function info($message, array $context = array()) + { + $this->debug($message, $context); + } + + /** + * Detailed debug information. + * + * @param string $message + * @param array $context + * @return null + */ + public function debug($message, array $context = array()) + { + if (! empty($context['label'])) { + $label = PHP_EOL . "$label" . PHP_EOL . str_repeat('=', strlen($label)) . PHP_EOL; + } + else { + $label = ''; + } + + if (is_string($message)) { + CssCrush::$process->debugLog[] = "$label$message"; + } + else { + ob_start(); + ! empty($context['var_dump']) ? var_dump($message) : print_r($message); + CssCrush::$process->debugLog[] = $label . ob_get_clean(); + } + } + + /** + * Logs with an arbitrary level. + * + * @param mixed $level + * @param string $message + * @param array $context + * @return null + */ + public function log($level, $message, array $context = array()) + { + $log_levels = array_flip(get_class_methods(__CLASS__)); + unset($log_levels['log']); + + if (isset($log_levels[$level])) { + return call_user_func(array($this, $level), $message, $context); + } + } +} diff --git a/lib/CssCrush/Options.php b/lib/CssCrush/Options.php index 4f98f13..3e16903 100644 --- a/lib/CssCrush/Options.php +++ b/lib/CssCrush/Options.php @@ -70,8 +70,8 @@ public function __set ($name, $value) if (is_string($value)) { $value = Util::resolveUserPath($value, function ($path) use ($name) { if (! @mkdir($path)) { - trigger_error(__METHOD__ . ': Could not find or create directory ' . - "specified by `$name` option.\n", E_USER_NOTICE); + CssCrush::$process->logger->notice( + "[[CssCrush]] - Could not find or create directory specified by `$name` option."); } return $path; }); diff --git a/lib/CssCrush/Plugin.php b/lib/CssCrush/Plugin.php index e58c246..c00eb8e 100644 --- a/lib/CssCrush/Plugin.php +++ b/lib/CssCrush/Plugin.php @@ -68,7 +68,7 @@ static public function load ($plugin_name) } if (! $found) { - trigger_error(__METHOD__ . ": Plugin '$plugin_name' not found.\n", E_USER_NOTICE); + CssCrush::$process->logger->notice("[[CssCrush]] - Plugin '$plugin_name' not found."); } } diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 2298cf1..2ab651f 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -33,8 +33,6 @@ public function __construct ($options) $this->mixins = array(); $this->fragments = array(); $this->references = array(); - $this->errors = array(); - $this->stat = array(); $this->charset = null; $this->sources = array(); $this->vars = array(); @@ -43,15 +41,18 @@ public function __construct ($options) $this->output = new \stdClass(); $this->tokens = new Tokens(); $this->sourceMap = null; + $this->selectorAliases = array(); + $this->selectorAliasesPatt = null; + + $this->debugLog = array(); + $this->errors = array(); + $this->stat = array(); - // Copy config values. $this->plugins = $config->plugins; $this->aliases = $config->aliases; - $this->selectorAliases = array(); - $this->selectorAliasesPatt = null; - // Pick a doc root. $this->docRoot = isset($this->options->doc_root) ? $this->options->doc_root : $config->docRoot; + $this->logger = isset($this->options->logger) ? $this->options->logger : new Logger(); // Shortcut the newline option and attach it to the process. switch ($this->options->newlines) { @@ -969,7 +970,7 @@ public function compile () Hook::run('capture_phase2', $this); $this->captureRules(); - // csscrush::log(array_keys($this->references)); + // $process->logger->debug(array_keys($this->references)); $this->resolveInBlocks(); diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php index 7fec45a..8b9d999 100644 --- a/lib/CssCrush/Util.php +++ b/lib/CssCrush/Util.php @@ -192,9 +192,7 @@ static public function filePutContents ($file, $str, $caller = null) return true; } - $error = "Could not write file '$file'."; - CssCrush::logError($error); - trigger_error(($caller ? $caller : __METHOD__) . ": $error\n", E_USER_WARNING); + CssCrush::$process->logger->warning("[[CssCrush]] - Could not write file '$file'."); return false; } diff --git a/plugins/canvas.php b/plugins/canvas.php index 7c23b2a..4c1f2bc 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -144,7 +144,7 @@ function canvas_generator ($input, $context) { // Apply functions. canvas_apply_css_funcs($canvas); - // csscrush::log($canvas); + // $process->logger->debug($canvas); // Create fingerprint for this canvas based on canvas object. $fingerprint = substr(md5(serialize($canvas)), 0, 7); @@ -227,7 +227,7 @@ function canvas_generator ($input, $context) { } } else { - // csscrush::log('file cached'); + // $process->logger->debug('file cached'); } @@ -680,28 +680,23 @@ function canvas_set_fill_dims ($fill, $canvas) { function canvas_requirements () { - $error_messages = array(); + $requirements_met = true; if (! extension_loaded('gd')) { - $error_messages[] = 'GD extension not available.'; + $requirements_met = false; + CssCrush::$process->logger->warning('[[CssCrush]] - GD extension not available.'); } else { $info = array_change_key_case(gd_info()); foreach (array('png', 'jpeg') as $key) { if (empty($info["$key support"])) { - $error_messages[] = "GD extension has no $key support."; + $requirements_met = false; + CssCrush::$process->logger->warning("[[CssCrush]] - GD extension has no $key support."); } } } - if ($error_messages) { - CssCrush::logError($error_messages); - $error = implode(' ' . PHP_EOL, $error_messages); - trigger_error(__METHOD__ . ": $error\n", E_USER_WARNING); - - return false; - } - return true; + return $requirements_met; } diff --git a/plugins/initial.php b/plugins/initial.php index 7838baa..9e9485f 100644 --- a/plugins/initial.php +++ b/plugins/initial.php @@ -31,7 +31,8 @@ function initial (Rule $rule) { static $initial_values; if (! $initial_values) { if (! ($initial_values = @parse_ini_file(CssCrush::$dir . '/misc/initial-values.ini'))) { - trigger_error(__METHOD__ . ": Initial keywords file could not be parsed.\n", E_USER_NOTICE); + CssCrush::$process->logger->notice("[[CssCrush]] - Initial keywords file could not be parsed."); + return; } } diff --git a/plugins/property-sorter.php b/plugins/property-sorter.php index b1cd469..c2548db 100644 --- a/plugins/property-sorter.php +++ b/plugins/property-sorter.php @@ -150,15 +150,14 @@ function &property_sorter_get_table () { else { // Load from property-sorting.ini. - $sorting_file_contents = - file_get_contents(CssCrush::$dir . '/misc/property-sorting.ini'); + $sorting_file_contents = file_get_contents(CssCrush::$dir . '/misc/property-sorting.ini'); if ($sorting_file_contents !== false) { $sorting_file_contents = preg_replace('~;[^\r\n]*~', '', $sorting_file_contents); $table = preg_split('~\s+~', trim($sorting_file_contents)); } else { - trigger_error(__METHOD__ . ": Property sorting file not found.\n", E_USER_NOTICE); + CssCrush::$process->logger->notice("[[CssCrush]] - Property sorting file not found."); } // Store to the global variable. From a42998ea655ebeef525225c51b2a5f8e13afb99d Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 21 Sep 2013 13:51:49 +0100 Subject: [PATCH 180/421] Swapped order of public and static keywords (PSR-2 recommendation) --- lib/CssCrush/Color.php | 30 +++++++++++++++--------------- lib/CssCrush/CssCrush.php | 31 ++++++++++++++++--------------- lib/CssCrush/Functions.php | 16 ++++++++-------- lib/CssCrush/Hook.php | 8 ++++---- lib/CssCrush/IO.php | 18 +++++++++--------- lib/CssCrush/IO/Watch.php | 2 +- lib/CssCrush/Importer.php | 2 +- lib/CssCrush/Mixin.php | 4 ++-- lib/CssCrush/Plugin.php | 12 ++++++------ lib/CssCrush/PostAliasFix.php | 6 +++--- lib/CssCrush/Process.php | 2 +- lib/CssCrush/Regex.php | 14 +++++++------- lib/CssCrush/Rule.php | 2 +- lib/CssCrush/Selector.php | 2 +- lib/CssCrush/Stream.php | 2 +- lib/CssCrush/Template.php | 4 ++-- lib/CssCrush/Tokens.php | 4 ++-- lib/CssCrush/Util.php | 20 ++++++++++---------- 18 files changed, 90 insertions(+), 89 deletions(-) diff --git a/lib/CssCrush/Color.php b/lib/CssCrush/Color.php index c5ecb6c..9f0d052 100644 --- a/lib/CssCrush/Color.php +++ b/lib/CssCrush/Color.php @@ -9,10 +9,10 @@ class Color { // Cached color keyword tables. - static public $keywords; - static public $minifyableKeywords; + public static $keywords; + public static $minifyableKeywords; - static public function &loadKeywords () + public static function &loadKeywords () { if (! isset(self::$keywords)) { @@ -29,7 +29,7 @@ static public function &loadKeywords () return self::$keywords; } - static public function &loadMinifyableKeywords () + public static function &loadMinifyableKeywords () { if (! isset(self::$minifyableKeywords)) { @@ -60,7 +60,7 @@ static public function &loadMinifyableKeywords () return self::$minifyableKeywords; } - static public function parse ($str) + public static function parse ($str) { if ($test = Color::test($str)) { $color = $test['value']; @@ -111,7 +111,7 @@ static public function parse ($str) return $rgba; } - static public function test ($str) + public static function test ($str) { static $color_patt; if (! $color_patt) { @@ -170,7 +170,7 @@ static public function test ($str) * Assumes r, g, and b are contained in the set [0, 255] and * returns h, s, and l in the set [0, 1]. */ - static public function rgbToHsl (array $rgba) + public static function rgbToHsl (array $rgba) { list($r, $g, $b, $a) = $rgba; $r /= 255; @@ -214,7 +214,7 @@ static public function rgbToHsl (array $rgba) * Assumes h, s, and l are contained in the set [0, 1] and * returns r, g, and b in the set [0, 255]. */ - static public function hslToRgb (array $hsla) + public static function hslToRgb (array $hsla) { // Populate unspecified alpha value. if (! isset($hsla[3])) { @@ -240,7 +240,7 @@ static public function hslToRgb (array $hsla) } // Convert percentages to points (0-255). - static public function normalizeCssRgb (array $rgba) + public static function normalizeCssRgb (array $rgba) { foreach ($rgba as &$val) { if (strpos($val, '%') !== false) { @@ -252,7 +252,7 @@ static public function normalizeCssRgb (array $rgba) return $rgba; } - static public function cssHslToRgb (array $hsla) + public static function cssHslToRgb (array $hsla) { // Populate unspecified alpha value. if (! isset($hsla[3])) { @@ -280,7 +280,7 @@ static public function cssHslToRgb (array $hsla) return self::hslToRgb(array($h, $s, $l, $a)); } - static public function hueToRgb ($p, $q, $t) + public static function hueToRgb ($p, $q, $t) { if ($t < 0) $t += 1; if ($t > 1) $t -= 1; @@ -290,7 +290,7 @@ static public function hueToRgb ($p, $q, $t) return $p; } - static public function rgbToHex (array $rgba) + public static function rgbToHex (array $rgba) { // Drop alpha component. array_pop($rgba); @@ -303,7 +303,7 @@ static public function rgbToHex (array $rgba) return $hex_out; } - static public function hexToRgb ($hex) + public static function hexToRgb ($hex) { $hex = substr($hex, 1); @@ -326,7 +326,7 @@ static public function hexToRgb ($hex) return $rgba; } - static public function colorAdjust ($str, array $adjustments) + public static function colorAdjust ($str, array $adjustments) { $hsla = new Color($str, true); @@ -334,7 +334,7 @@ static public function colorAdjust ($str, array $adjustments) return $hsla->isValid ? $hsla->adjust($adjustments)->__toString() : $str; } - static public function colorSplit ($str) + public static function colorSplit ($str) { if ($test = Color::test($str)) { $color = $test['value']; diff --git a/lib/CssCrush/CssCrush.php b/lib/CssCrush/CssCrush.php index 148515c..bac79b9 100644 --- a/lib/CssCrush/CssCrush.php +++ b/lib/CssCrush/CssCrush.php @@ -11,16 +11,16 @@ class CssCrush const VERSION = '2.0.0'; // Global settings. - static public $config; + public static $config; // The current active process. - static public $process; + public static $process; // Library root directory. - static public $dir; + public static $dir; // Init called once manually post class definition. - static public function init () + public static function init () { self::$dir = dirname(dirname(__DIR__)); @@ -67,6 +67,7 @@ static public function init () 'enable' => null, 'disable' => null, 'stat_dump' => false, + 'logger' => null, 'trace' => array(), 'source_map' => false, 'newlines' => 'use-platform', @@ -114,7 +115,7 @@ static protected function resolveDocRoot ($doc_root = null) return Util::normalizePath($doc_root); } - static public function loadAssets () + public static function loadAssets () { static $called; if ($called) { @@ -128,7 +129,7 @@ static public function loadAssets () } } - static public function parseAliasesFile ($file) + public static function parseAliasesFile ($file) { $tree = @parse_ini_file($file, true); @@ -210,7 +211,7 @@ static public function parseAliasesFile ($file) * @param mixed $options An array of options or null. * @return string The public path to the compiled file or an empty string. */ - static public function file ($file, $options = null) + public static function file ($file, $options = null) { self::$process = new Process($options); @@ -256,7 +257,7 @@ static public function file ($file, $options = null) * @param array $attributes An array of HTML attributes. * @return string HTML link tag or error message inside HTML comment. */ - static public function tag ($file, $options = null, $tag_attributes = array()) + public static function tag ($file, $options = null, $tag_attributes = array()) { $file = self::file($file, $options); @@ -287,7 +288,7 @@ static public function tag ($file, $options = null, $tag_attributes = array()) * @param array $attributes An array of HTML attributes, set false to return CSS text without tag. * @return string HTML link tag or error message inside HTML comment. */ - static public function inline ($file, $options = null, $tag_attributes = array()) + public static function inline ($file, $options = null, $tag_attributes = array()) { // For inline output set boilerplate to not display by default if (! is_array($options)) { @@ -329,7 +330,7 @@ static public function inline ($file, $options = null, $tag_attributes = array() * @param mixed $options An array of options or null. * @return string CSS text. */ - static public function string ($string, $options = null) + public static function string ($string, $options = null) { // For strings set boilerplate to not display by default if (! isset($options['boilerplate'])) { @@ -365,7 +366,7 @@ static public function string ($string, $options = null) /** * Get debug info. */ - static public function stat () + public static function stat () { $process = CssCrush::$process; $stats = $process->stat; @@ -383,12 +384,12 @@ static public function stat () ############################# # Global selector aliases. - static public function addSelectorAlias ($name, $body) + public static function addSelectorAlias ($name, $body) { CssCrush::$config->selectorAliases[$name] = is_callable($body) ? $body : new Template($body); } - static public function removeSelectorAlias ($name) + public static function removeSelectorAlias ($name) { unset(CssCrush::$config->selectorAliases[$name]); } @@ -397,7 +398,7 @@ static public function removeSelectorAlias ($name) ############################# # Logging and stats. - static public function printLog () + public static function printLog () { if (! empty(self::$process->debugLog)) { @@ -414,7 +415,7 @@ static public function printLog () } } - static public function runStat () + public static function runStat () { $process = CssCrush::$process; $all_rules =& $process->tokens->store->r; diff --git a/lib/CssCrush/Functions.php b/lib/CssCrush/Functions.php index b51050c..b317d5a 100644 --- a/lib/CssCrush/Functions.php +++ b/lib/CssCrush/Functions.php @@ -9,9 +9,9 @@ class Functions { // Regex pattern for finding custom functions. - static public $functionPatt; + public static $functionPatt; - static public $functions; + public static $functions; static protected $customFunctions = array(); @@ -32,14 +32,14 @@ class Functions 'a-adjust' => 'CssCrush\fn__a_adjust', ); - static public function setMatchPatt () + public static function setMatchPatt () { self::$functions = self::$builtinFunctions + self::$customFunctions; self::$functionPatt = Regex::makeFunctionPatt( array_keys(self::$functions), array('bare_paren' => true)); } - static public function executeOnString ($str, $patt = null, $process_callback = null, \stdClass $context = null) + public static function executeOnString ($str, $patt = null, $process_callback = null, \stdClass $context = null) { // No bracketed expressions, early return. if (strpos($str, '(') === false) { @@ -117,17 +117,17 @@ static public function executeOnString ($str, $patt = null, $process_callback = ############################# # API and helpers. - static public function register ($name, $callback) + public static function register ($name, $callback) { Functions::$customFunctions[ $name] = $callback; } - static public function deRegister ($name) + public static function deRegister ($name) { unset(Functions::$customFunctions[$name]); } - static public function parseArgs ($input, $allowSpaceDelim = false) + public static function parseArgs ($input, $allowSpaceDelim = false) { return Util::splitDelimList( $input, ($allowSpaceDelim ? '\s*[,\s]\s*' : ',')); @@ -135,7 +135,7 @@ static public function parseArgs ($input, $allowSpaceDelim = false) // Intended as a quick arg-list parse for function that take up-to 2 arguments // with the proviso the first argument is an ident. - static public function parseArgsSimple ($input) + public static function parseArgsSimple ($input) { return preg_split(Regex::$patt->argListSplit, $input, 2); } diff --git a/lib/CssCrush/Hook.php b/lib/CssCrush/Hook.php index 7e5006a..535f441 100644 --- a/lib/CssCrush/Hook.php +++ b/lib/CssCrush/Hook.php @@ -9,9 +9,9 @@ class Hook { // Table of hooks and the functions attached to them. - static public $register = array(); + public static $register = array(); - static public function add ($hook, $fn_name) + public static function add ($hook, $fn_name) { // Bail early is the named hook and callback combination is already loaded. if (! isset(self::$register[$hook][$fn_name])) { @@ -24,12 +24,12 @@ static public function add ($hook, $fn_name) } } - static public function remove ($hook, $fn_name) + public static function remove ($hook, $fn_name) { unset(self::$register[$hook][$fn_name]); } - static public function run ($hook, $arg_obj = null) + public static function run ($hook, $arg_obj = null) { // Run all callbacks attached to the hook. if (isset(self::$register[$hook])) { diff --git a/lib/CssCrush/IO.php b/lib/CssCrush/IO.php index 5448d69..5e151bd 100644 --- a/lib/CssCrush/IO.php +++ b/lib/CssCrush/IO.php @@ -8,13 +8,13 @@ class IO { - static public function init () + public static function init () { $process = CssCrush::$process; $process->cacheFile = "{$process->output->dir}/.csscrush"; } - static public function getOutputDir () + public static function getOutputDir () { $process = CssCrush::$process; $output_dir = $process->options->output_dir; @@ -22,7 +22,7 @@ static public function getOutputDir () return $output_dir ? $output_dir : $process->input->dir; } - static public function testOutputDir () + public static function testOutputDir () { $dir = CssCrush::$process->output->dir; $logger = CssCrush::$process->logger; @@ -48,7 +48,7 @@ static public function testOutputDir () return $pathtest; } - static public function getOutputFileName () + public static function getOutputFileName () { $process = CssCrush::$process; $options = $process->options; @@ -62,7 +62,7 @@ static public function getOutputFileName () return "$output_basename.crush.css"; } - static public function getOutputUrl () + public static function getOutputUrl () { $process = CssCrush::$process; $options = $process->options; @@ -90,7 +90,7 @@ static public function getOutputUrl () return $url; } - static public function validateCache () + public static function validateCache () { $process = CssCrush::$process; $config = CssCrush::$config; @@ -163,7 +163,7 @@ static public function validateCache () } } - static public function getCacheData () + public static function getCacheData () { $config = CssCrush::$config; $process = CssCrush::$process; @@ -204,7 +204,7 @@ static public function getCacheData () return $cache_data; } - static public function saveCacheData () + public static function saveCacheData () { $process = CssCrush::$process; @@ -214,7 +214,7 @@ static public function saveCacheData () Util::filePutContents($process->cacheFile, json_encode($process->cacheData, $flags), __METHOD__); } - static public function write (Stream $stream) + public static function write (Stream $stream) { $process = CssCrush::$process; $output = $process->output; diff --git a/lib/CssCrush/IO/Watch.php b/lib/CssCrush/IO/Watch.php index 2ae0711..380513f 100644 --- a/lib/CssCrush/IO/Watch.php +++ b/lib/CssCrush/IO/Watch.php @@ -13,7 +13,7 @@ class Watch extends IO { public static $cacheData = array(); - static public function getOutputFileName () + public static function getOutputFileName () { $process = CssCrush::$process; $options = $process->options; diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index 9034e14..4eab51a 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -8,7 +8,7 @@ class Importer { - static public function hostfile () + public static function hostfile () { $config = CssCrush::$config; $process = CssCrush::$process; diff --git a/lib/CssCrush/Mixin.php b/lib/CssCrush/Mixin.php index 8b3fcb8..e6e4245 100644 --- a/lib/CssCrush/Mixin.php +++ b/lib/CssCrush/Mixin.php @@ -15,7 +15,7 @@ public function __construct ($block) $this->template = new Template($block); } - static public function call ($message, $context = null) + public static function call ($message, $context = null) { $process = CssCrush::$process; $mixable = null; @@ -85,7 +85,7 @@ static public function call ($message, $context = null) } } - static public function merge (array $input, $message_list, $options = array()) + public static function merge (array $input, $message_list, $options = array()) { $context = isset($options['context']) ? $options['context'] : null; diff --git a/lib/CssCrush/Plugin.php b/lib/CssCrush/Plugin.php index c00eb8e..2517ea9 100644 --- a/lib/CssCrush/Plugin.php +++ b/lib/CssCrush/Plugin.php @@ -10,7 +10,7 @@ class Plugin { static protected $plugins = array(); - static public function info () + public static function info () { $plugin_dirs = CssCrush::$config->pluginDirs; $plugin_data = array(); @@ -26,7 +26,7 @@ static public function info () return $plugin_data; } - static public function parseDoc ($plugin_path) + public static function parseDoc ($plugin_path) { $contents = file_get_contents($plugin_path); if (preg_match('~/\*\*(.*?)\*/~s', $contents, $m)) { @@ -44,12 +44,12 @@ static public function parseDoc ($plugin_path) return false; } - static public function register ($plugin_name, $callbacks) + public static function register ($plugin_name, $callbacks) { self::$plugins[$plugin_name] = $callbacks; } - static public function load ($plugin_name) + public static function load ($plugin_name) { // Assume the the plugin file is not loaded if null. if (! isset(self::$plugins[$plugin_name])) { @@ -75,7 +75,7 @@ static public function load ($plugin_name) return isset(self::$plugins[$plugin_name]) ? self::$plugins[$plugin_name] : null; } - static public function enable ($plugin_name) + public static function enable ($plugin_name) { $plugin = self::load($plugin_name); @@ -86,7 +86,7 @@ static public function enable ($plugin_name) return true; } - static public function disable ($plugin_name) + public static function disable ($plugin_name) { $plugin = isset(self::$plugins[$plugin_name]) ? self::$plugins[$plugin_name] : null; diff --git a/lib/CssCrush/PostAliasFix.php b/lib/CssCrush/PostAliasFix.php index 8660fb3..a210bd1 100644 --- a/lib/CssCrush/PostAliasFix.php +++ b/lib/CssCrush/PostAliasFix.php @@ -9,11 +9,11 @@ class PostAliasFix { // Currently only post fixing aliased functions. - static public $functions = array( + public static $functions = array( ':gradients' => 'CssCrush\postalias_fix_gradients', ); - static public function add ($alias_type, $key, $callback) + public static function add ($alias_type, $key, $callback) { if ($alias_type === 'function') { // $key is the aliased css function name. @@ -21,7 +21,7 @@ static public function add ($alias_type, $key, $callback) } } - static public function remove ($alias_type, $key) + public static function remove ($alias_type, $key) { if ($type === 'function') { // $key is the aliased css function name. diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 2ab651f..2b3a969 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -219,7 +219,7 @@ function ($m) { } } - static public function applySelectorAliases (&$str) + public static function applySelectorAliases (&$str) { $process = CssCrush::$process; diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php index 339754c..5172f4e 100644 --- a/lib/CssCrush/Regex.php +++ b/lib/CssCrush/Regex.php @@ -9,14 +9,14 @@ class Regex { // Patterns. - static public $patt; + public static $patt; // Character classes. - static public $classes; + public static $classes; - static public $swaps = array(); + public static $swaps = array(); - static public function init () + public static function init () { self::$patt = $patt = new \stdClass(); self::$classes = $classes = new \stdClass(); @@ -120,7 +120,7 @@ static public function init () $patt->cruftyHex = Regex::make('~\#({{hex}})\1({{hex}})\2({{hex}})\3~S'); } - static public function make ($pattern) + public static function make ($pattern) { static $cache = array(), $find, $replace; if (isset($cache[$pattern])) { @@ -135,14 +135,14 @@ static public function make ($pattern) return $cache[$pattern] = str_replace($find, $replace, $pattern); } - static public function matchAll ($patt, $subject, $offset = 0) + public static function matchAll ($patt, $subject, $offset = 0) { $count = preg_match_all($patt, $subject, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER, $offset); return $count ? $matches : array(); } - static public function makeFunctionPatt ($list, $options = array()) + public static function makeFunctionPatt ($list, $options = array()) { // Bare parens. $question = ''; diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php index b8ab4a4..e144042 100644 --- a/lib/CssCrush/Rule.php +++ b/lib/CssCrush/Rule.php @@ -785,7 +785,7 @@ public function setDeclarations (array $declaration_stack) $this->updatePropertyIndex(); } - static public function parseBlock ($str, $options = array()) + public static function parseBlock ($str, $options = array()) { $str = Util::stripCommentTokens($str); $lines = preg_split('~\s*;\s*~', $str, null, PREG_SPLIT_NO_EMPTY); diff --git a/lib/CssCrush/Selector.php b/lib/CssCrush/Selector.php index 89daa89..e221c00 100644 --- a/lib/CssCrush/Selector.php +++ b/lib/CssCrush/Selector.php @@ -48,7 +48,7 @@ public function appendPseudo ($pseudo) return $this->readableValue; } - static public function normalizeWhiteSpace ($str) + public static function normalizeWhiteSpace ($str) { // Create space around combinators, then normalize whitespace. $str = preg_replace('~([>+]|\~(?!=))~S', ' $1 ', $str); diff --git a/lib/CssCrush/Stream.php b/lib/CssCrush/Stream.php index e0c4836..cefc130 100644 --- a/lib/CssCrush/Stream.php +++ b/lib/CssCrush/Stream.php @@ -18,7 +18,7 @@ public function __toString () return $this->raw; } - static public function endsWith ($haystack, $needle) + public static function endsWith ($haystack, $needle) { return substr($haystack, -strlen($needle)) === $needle; } diff --git a/lib/CssCrush/Template.php b/lib/CssCrush/Template.php index e57a97d..6cce96d 100644 --- a/lib/CssCrush/Template.php +++ b/lib/CssCrush/Template.php @@ -140,7 +140,7 @@ public function apply (array $args = null, $str = null) return Template::tokenize($str); } - static public function tokenize ($str) + public static function tokenize ($str) { $str = CssCrush::$process->tokens->capture($str, 's'); $str = CssCrush::$process->tokens->capture($str, 'u'); @@ -148,7 +148,7 @@ static public function tokenize ($str) return $str; } - static public function unTokenize ($str) + public static function unTokenize ($str) { $str = CssCrush::$process->tokens->restore($str, 'u', true); $str = CssCrush::$process->tokens->restore($str, 's', true); diff --git a/lib/CssCrush/Tokens.php b/lib/CssCrush/Tokens.php index adb9c30..7df2eaf 100644 --- a/lib/CssCrush/Tokens.php +++ b/lib/CssCrush/Tokens.php @@ -161,7 +161,7 @@ public function captureUrls ($str, $add_padding = false) return $str; } - static public function pad ($label, $replaced_text) + public static function pad ($label, $replaced_text) { // Padding token labels to maintain whitespace and newlines. @@ -177,7 +177,7 @@ static public function pad ($label, $replaced_text) return $label; } - static public function is ($label, $of_type) + public static function is ($label, $of_type) { if (preg_match(Regex::make('~^ \? (?[a-zA-Z]) {{token-id}} \? $~xS'), $label, $m)) { diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php index 8b9d999..bb49124 100644 --- a/lib/CssCrush/Util.php +++ b/lib/CssCrush/Util.php @@ -8,7 +8,7 @@ class Util { - static public function htmlAttributes (array $attributes, array $sort_order = null) + public static function htmlAttributes (array $attributes, array $sort_order = null) { // Optionally sort attributes (for better readability). if ($sort_order) { @@ -43,7 +43,7 @@ static public function htmlAttributes (array $attributes, array $sort_order = nu return $str; } - static public function normalizePath ($path, $strip_drive_letter = false) + public static function normalizePath ($path, $strip_drive_letter = false) { if (! $path) { return ''; @@ -65,7 +65,7 @@ static public function normalizePath ($path, $strip_drive_letter = false) return Util::simplifyPath($path); } - static public function simplifyPath ($path) + public static function simplifyPath ($path) { // Reduce redundant path segments. e.g 'foo/../bar' => 'bar' $patt = '~[^/.]+/\.\./~S'; @@ -75,7 +75,7 @@ static public function simplifyPath ($path) return $path; } - static public function resolveUserPath ($path, $recovery = null) + public static function resolveUserPath ($path, $recovery = null) { // System path. if ($realpath = realpath($path)) { @@ -101,12 +101,12 @@ static public function resolveUserPath ($path, $recovery = null) return $path ? Util::normalizePath($path) : false; } - static public function stripCommentTokens ($str) + public static function stripCommentTokens ($str) { return preg_replace(Regex::$patt->c_token, '', $str); } - static public function normalizeWhiteSpace ($str) + public static function normalizeWhiteSpace ($str) { static $find, $replace; if (! $find) { @@ -125,7 +125,7 @@ static public function normalizeWhiteSpace ($str) return preg_replace($find, $replace, $str); } - static public function splitDelimList ($str, $delim = ',') + public static function splitDelimList ($str, $delim = ',') { $do_preg_split = strlen($delim) > 1; $str = trim($str); @@ -154,7 +154,7 @@ static public function splitDelimList ($str, $delim = ',') return array_filter(array_map('trim', $list), 'strlen'); } - static public function getLinkBetweenPaths ($path1, $path2, $directories = true) + public static function getLinkBetweenPaths ($path1, $path2, $directories = true) { $path1 = trim(Util::normalizePath($path1, true), '/'); $path2 = trim(Util::normalizePath($path2, true), '/'); @@ -186,7 +186,7 @@ static public function getLinkBetweenPaths ($path1, $path2, $directories = true) return $link; } - static public function filePutContents ($file, $str, $caller = null) + public static function filePutContents ($file, $str, $caller = null) { if (@file_put_contents($file, $str, LOCK_EX)) { @@ -200,7 +200,7 @@ static public function filePutContents ($file, $str, $caller = null) /* * Encode integer to Base64 VLQ. */ - static public function vlqEncode ($value) + public static function vlqEncode ($value) { static $VLQ_BASE_SHIFT, $VLQ_BASE, $VLQ_BASE_MASK, $VLQ_CONTINUATION_BIT, $BASE64_MAP; if (! $VLQ_BASE_SHIFT) { From 1c7fc0b599758b2747507b39d1d9f7143bb7a423 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 27 Sep 2013 11:04:34 +0100 Subject: [PATCH 181/421] Removed legacy IE plugins (ie-clip, ie-filter, ie-min-height, rgba-fallback) and spiffing. Updated README. --- README.md | 64 +++++++++++++++++++++++++++++-------- plugins/ie-clip.php | 42 ------------------------ plugins/ie-filter.php | 65 ------------------------------------- plugins/ie-min-height.php | 40 ----------------------- plugins/rgba-fallback.php | 67 --------------------------------------- plugins/spiffing.php | 34 -------------------- 6 files changed, 50 insertions(+), 262 deletions(-) delete mode 100644 plugins/ie-clip.php delete mode 100644 plugins/ie-filter.php delete mode 100644 plugins/ie-min-height.php delete mode 100644 plugins/rgba-fallback.php delete mode 100644 plugins/spiffing.php diff --git a/README.md b/README.md index 197245f..e9cde09 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,69 @@ Logo -CSS without the Mess — CSS-Crush is a CSS preprocessor designed to create a modern, uncluttered and standards based CSS authoring environment. +CSS without the Mess — CSS-Crush is a CSS preprocessor designed to enable a modern, uncluttered and standards based CSS workflow. -See the following overview for code examples and description of main features: -http://the-echoplex.net/csscrush +* Automatic vendor prefixing +* Variables +* Import inlining +* Functions (color manipulation, math, data-uris etc.) +* Rule inheritance (@extends) +* Mixins +* Block nesting +* Minification +* Lightweight plugin system +See the [docs](http://the-echoplex.net/csscrush) for full details. -Quick start ------------ + +### Setup + +If you're using [Composer](http://getcomposer.org) you can use Crush in your project with the following line in your terminal: + +```shell +composer require css-crush/css-crush:dev-master +``` + +If you're not using Composer yet just download the library into a convenient location and require the bootstrap file: + +```php + +``` + +### Basic usage ```php +``` + +Compiles the CSS file and outputs the following link tag: - +```html + ``` +There are several other [functions](http://the-echoplex.net/csscrush#api) for working with files and strings of CSS: + +* `csscrush_file($file, $options)` - Returns a URL of the compiled file. +* `csscrush_string($css, $options)` - Compiles a raw string of css and returns the resulting css. +* `csscrush_inline($file, $options, $tag_attributes)` - Returns compiled css in an inline style tag. + +There are a number of [options](http://the-echoplex.net/csscrush#options) available for tailoring the output, and a collection of bundled [plugins](http://the-echoplex.net/csscrush#plugins) that cover many workflow issues in contemporary CSS development. + + +### Contibuting + +If you think you've found a bug please create an [issue](https://github.com/peteboere/css-crush/issues) explaining the problem and expected result. + +Likewise, if you'd like to request a feature please create an [issue](https://github.com/peteboere/css-crush/issues) with some explaination of the requested feature and use-cases. -Submitting bugs ---------------- +[Pull requests](https://help.github.com/articles/using-pull-requests) are welcome, though please keep coding style consistent with the project (which is based on [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)). -If you think you've found a bug, please visit the Issue tracker — https://github.com/peteboere/css-crush/issues — and create an issue explaining the problem and expected result. +### Licence -Submitting patches ------------------- +MIT -To contribute code and bug fixes fork this project on Github, make changes to the code in your fork, and then send a pull request. diff --git a/plugins/ie-clip.php b/plugins/ie-clip.php deleted file mode 100644 index e3f614b..0000000 --- a/plugins/ie-clip.php +++ /dev/null @@ -1,42 +0,0 @@ - function () { - Hook::add('rule_postalias', 'CssCrush\ie_clip'); - }, - 'disable' => function () { - Hook::remove('rule_postalias', 'CssCrush\ie_clip'); - }, -)); - - -function ie_clip (Rule $rule) { - - // Assume it's been dealt with if the property occurs more than once. - if ($rule->propertyCount('clip') !== 1) { - return; - } - $new_set = array(); - foreach ($rule as $declaration) { - $new_set[] = $declaration; - if ( - $declaration->skip || - $declaration->property !== 'clip' - ) { - continue; - } - $new_set[] = new Declaration('*clip', str_replace( ',', ' ', $declaration->getFullValue())); - } - $rule->setDeclarations($new_set); -} diff --git a/plugins/ie-filter.php b/plugins/ie-filter.php deleted file mode 100644 index cdeadef..0000000 --- a/plugins/ie-filter.php +++ /dev/null @@ -1,65 +0,0 @@ - 7 - * Outputs '*' escaped filter property for IE < 8 - * Adds hasLayout via zoom property (required by filter effects) - * - * - * @before - * -ms-filter: alpha(opacity=50), blur(strength=10); - * - * @after - * -ms-filter: "alpha(opacity=50), progid:DXImageTransform.Microsoft.Blur(strength=10)"; - * *filter: alpha(opacity=50), progid:DXImageTransform.Microsoft.Blur(strength=10); - * zoom: 1; - */ -namespace CssCrush; - -Plugin::register('ie-filter', array( - 'enable' => function () { - Hook::add('rule_postalias', 'CssCrush\ie_filter'); - }, - 'disable' => function () { - Hook::remove('rule_postalias', 'CssCrush\ie_filter'); - }, -)); - - -function ie_filter (Rule $rule) { - - if ($rule->propertyCount('-ms-filter') < 1) { - return; - } - $filter_prefix = 'progid:DXImageTransform.Microsoft.'; - $new_set = array(); - foreach ($rule as $declaration) { - if ( - $declaration->skip || - $declaration->property !== '-ms-filter' - ) { - $new_set[] = $declaration; - continue; - } - $list = array_map('trim', explode(',', $declaration->value)); - foreach ($list as &$item) { - if ( - strpos($item, $filter_prefix) !== 0 && - strpos($item, 'alpha') !== 0 // Shortcut syntax permissable on alpha - ) { - $item = $filter_prefix . ucfirst($item); - } - } - $declaration->value = implode(',', $list); - if (! $rule->propertyCount('zoom')) { - // Filters need hasLayout. - $new_set[] = new Declaration('zoom', 1); - } - // Quoted version for -ms-filter IE >= 8. - $new_set[] = new Declaration('-ms-filter', "\"$declaration->value\""); - // Star escaped property for IE < 8. - $new_set[] = new Declaration('*filter', $declaration->value); - } - $rule->setDeclarations($new_set); -} diff --git a/plugins/ie-min-height.php b/plugins/ie-min-height.php deleted file mode 100644 index 81386f0..0000000 --- a/plugins/ie-min-height.php +++ /dev/null @@ -1,40 +0,0 @@ - function () { - Hook::add('rule_postalias', 'CssCrush\ie_min_height'); - }, - 'disable' => function () { - Hook::remove('rule_postalias', 'CssCrush\ie_min_height'); - }, -)); - - -function ie_min_height (Rule $rule) { - - if ($rule->propertyCount('min-height') < 1) { - return; - } - $new_set = array(); - foreach ($rule as $declaration) { - $new_set[] = $declaration; - if ( - $declaration->skip || - $declaration->property !== 'min-height') { - continue; - } - $new_set[] = new Declaration('_height', $declaration->value); - } - $rule->setDeclarations($new_set); -} diff --git a/plugins/rgba-fallback.php b/plugins/rgba-fallback.php deleted file mode 100644 index f04b080..0000000 --- a/plugins/rgba-fallback.php +++ /dev/null @@ -1,67 +0,0 @@ - function () { - Hook::add('rule_postalias', 'CssCrush\rgba_fallback'); - }, - 'disable' => function () { - Hook::remove('rule_postalias', 'CssCrush\rgba_fallback'); - }, -)); - - -function rgba_fallback (Rule $rule) { - - $props = array_keys($rule->properties); - - // Determine which properties apply - $rgba_props = array(); - foreach ($props as $prop) { - if ($prop === 'background' || strpos($prop, 'color') !== false) { - $rgba_props[] = $prop; - } - } - if (empty($rgba_props)) { - return; - } - - $rgb_patt = Regex::make('~^rgba{{p-token}}$~i'); - - $new_set = array(); - foreach ($rule as $declaration) { - - $is_viable = in_array($declaration->property, $rgba_props); - if ( - $declaration->skip || - ! $is_viable || - $is_viable && ! preg_match($rgb_patt, $declaration->value) - ) { - $new_set[] = $declaration; - continue; - } - - // Create rgb value from rgba. - $raw_value = $declaration->getFullValue(); - $raw_value = substr($raw_value, 5, strlen($raw_value) - 1); - list($r, $g, $b, $a) = explode(',', $raw_value); - - // Add rgb value to the stack, followed by rgba. - $new_set[] = new Declaration($declaration->property, "rgb($r,$g,$b)"); - $new_set[] = $declaration; - } - $rule->setDeclarations($new_set); -} diff --git a/plugins/spiffing.php b/plugins/spiffing.php deleted file mode 100644 index ea8202b..0000000 --- a/plugins/spiffing.php +++ /dev/null @@ -1,34 +0,0 @@ - function () { - Hook::add('rule_preprocess', 'CssCrush\spiffing'); - }, - 'disable' => function () { - Hook::remove('rule_preprocess', 'CssCrush\spiffing'); - }, -)); - - -function spiffing ($rule) { - - $find = array('colour', 'grey', '!please', 'transparency', 'centre', 'plump', 'photograph', 'capitalise'); - $replace = array('color', 'gray', '!important', 'opacity', 'center', 'bold', 'image', 'capitalize'); - - $rule->declaration_raw = str_ireplace($find, $replace, $rule->declaration_raw); -} From 44128009ca7fe9052e9d3e5338ff77525084f9a6 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 12 Oct 2013 12:32:43 +0100 Subject: [PATCH 182/421] Moved logger from options to the config object. --- README.md | 1 + lib/CssCrush/BalancedMatch.php | 4 ++-- lib/CssCrush/CssCrush.php | 8 ++++---- lib/CssCrush/IO.php | 25 ++++++++++++++----------- lib/CssCrush/Importer.php | 6 +++--- lib/CssCrush/Options.php | 2 +- lib/CssCrush/Plugin.php | 2 +- lib/CssCrush/Process.php | 2 -- lib/CssCrush/Util.php | 2 +- plugins/canvas.php | 9 +++++---- plugins/initial.php | 2 +- plugins/property-sorter.php | 2 +- 12 files changed, 34 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index e9cde09..3da6f5f 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ If you're not using Composer yet just download the library into a convenient loc ``` + ### Basic usage ```php diff --git a/lib/CssCrush/BalancedMatch.php b/lib/CssCrush/BalancedMatch.php index f29dc84..7cab43b 100644 --- a/lib/CssCrush/BalancedMatch.php +++ b/lib/CssCrush/BalancedMatch.php @@ -24,7 +24,7 @@ public function __construct (Stream $stream, $offset, $brackets = '{}') if (substr_count($stream->raw, $opener) !== substr_count($stream->raw, $closer)) { $sample = substr($stream->raw, $this->offset, 25); - CssCrush::$process->logger->warning("[[CssCrush]] - Unmatched token near '$sample'."); + CssCrush::$config->logger->warning("[[CssCrush]] - Unmatched token near '$sample'."); return; } @@ -40,7 +40,7 @@ public function __construct (Stream $stream, $offset, $brackets = '{}') $this->length = $this->matchEnd - $this->offset; } else { - CssCrush::$process->logger->warning("[[CssCrush]] - Could not match '$opener'. Exiting."); + CssCrush::$config->logger->warning("[[CssCrush]] - Could not match '$opener'. Exiting."); } } diff --git a/lib/CssCrush/CssCrush.php b/lib/CssCrush/CssCrush.php index bac79b9..ceae7f1 100644 --- a/lib/CssCrush/CssCrush.php +++ b/lib/CssCrush/CssCrush.php @@ -32,6 +32,7 @@ public static function init () self::$config->version = new Version(self::VERSION); self::$config->scriptDir = dirname(realpath($_SERVER['SCRIPT_FILENAME'])); self::$config->docRoot = self::resolveDocRoot(); + self::$config->logger = new Logger(); // Set default IO handler. self::$config->io = 'CssCrush\IO'; @@ -67,7 +68,6 @@ public static function init () 'enable' => null, 'disable' => null, 'stat_dump' => false, - 'logger' => null, 'trace' => array(), 'source_map' => false, 'newlines' => 'use-platform', @@ -108,7 +108,7 @@ static protected function resolveDocRoot ($doc_root = null) } if (! $doc_root) { - CssCrush::$process->logger->warning("[[CssCrush]] - Could not get a valid DOCUMENT_ROOT reference."); + CssCrush::$config->logger->warning("[[CssCrush]] - Could not get a valid DOCUMENT_ROOT reference."); } } @@ -134,7 +134,7 @@ public static function parseAliasesFile ($file) $tree = @parse_ini_file($file, true); if ($tree === false) { - CssCrush::$process->logger->notice("[[CssCrush]] - Could not parse aliases file '$file'."); + CssCrush::$config->logger->notice("[[CssCrush]] - Could not parse aliases file '$file'."); return false; } @@ -223,7 +223,7 @@ public static function file ($file, $options = null) $process->input->raw = $file; if (! ($input_file = Util::resolveUserPath($file))) { - $process->logger->warning('[[CssCrush]] - Input file \'' . basename($file) . '\' not found.'); + $config->logger->warning('[[CssCrush]] - Input file \'' . basename($file) . '\' not found.'); return ''; } diff --git a/lib/CssCrush/IO.php b/lib/CssCrush/IO.php index 5e151bd..7fbf4af 100644 --- a/lib/CssCrush/IO.php +++ b/lib/CssCrush/IO.php @@ -25,7 +25,7 @@ public static function getOutputDir () public static function testOutputDir () { $dir = CssCrush::$process->output->dir; - $logger = CssCrush::$process->logger; + $logger = CssCrush::$config->logger; $pathtest = true; if (! file_exists($dir)) { @@ -94,6 +94,7 @@ public static function validateCache () { $process = CssCrush::$process; $config = CssCrush::$config; + $logger = $config->logger; $options = $process->options; $input = $process->input; $output = $process->output; @@ -101,13 +102,13 @@ public static function validateCache () $filename = $output->filename; if (! file_exists($output->dir . '/' . $filename)) { - $process->logger->debug('No file cached.'); + $logger->debug('No file cached.'); return false; } if (! isset($process->cacheData[$filename])) { - $process->logger->debug('Cached file exists but is not registered.'); + $logger->debug('Cached file exists but is not registered.'); return false; } @@ -127,7 +128,7 @@ public static function validateCache () } else { // File has been moved, remove old file and skip to compile. - $process->logger->debug('Recompiling - an import file has been moved.'); + $logger->debug('Recompiling - an import file has been moved.'); return false; } @@ -135,7 +136,7 @@ public static function validateCache () $files_changed = $data['datem_sum'] != array_sum($file_sums); if ($files_changed) { - $process->logger->debug('Files have been modified. Recompiling.'); + $logger->debug('Files have been modified. Recompiling.'); } // Compare runtime options and cached options for differences. @@ -145,14 +146,14 @@ public static function validateCache () $active_options = $options->get(); foreach ($cached_options as $key => &$value) { if (isset($active_options[$key]) && $active_options[$key] !== $value) { - $process->logger->debug('Options have been changed. Recompiling.'); + $logger->debug('Options have been changed. Recompiling.'); $options_changed = true; break; } } if (! $options_changed && ! $files_changed) { - $process->logger->debug("Files and options have not been modified, returning cached file."); + $logger->debug("Files and options have not been modified, returning cached file."); return true; } @@ -166,6 +167,7 @@ public static function validateCache () public static function getCacheData () { $config = CssCrush::$config; + $logger = $config->logger; $process = CssCrush::$process; if ( @@ -186,17 +188,17 @@ public static function getCacheData () $cache_data = json_decode(file_get_contents($process->cacheFile), true) ) { // Successfully loaded config file. - $process->logger->debug('Cache data loaded.'); + $logger->debug('Cache data loaded.'); } else { // Config file may exist but not be writable (may not be visible in some ftp situations?) if ($cache_data_exists) { if (! @unlink($process->cacheFile)) { - CssCrush::$process->logger->notice('[[CssCrush]] - Could not delete cache data file.'); + $logger->notice('[[CssCrush]] - Could not delete cache data file.'); } } else { - $process->logger->debug('Creating cache data file.'); + $logger->debug('Creating cache data file.'); } Util::filePutContents($process->cacheFile, json_encode(array()), __METHOD__); } @@ -207,8 +209,9 @@ public static function getCacheData () public static function saveCacheData () { $process = CssCrush::$process; + $logger = CssCrush::$config->logger; - $process->logger->debug('Saving config.'); + $logger->debug('Saving config.'); $flags = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 0; Util::filePutContents($process->cacheFile, json_encode($process->cacheData, $flags), __METHOD__); diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index 4eab51a..7db8a5b 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -78,7 +78,7 @@ public static function hostfile () $import->content = @file_get_contents($import->path); if ($import->content === false) { - $process->logger->debug("Import file '{$import->url->value}' not found"); + $config->logger->debug("Import file '{$import->url->value}' not found"); $str = substr_replace($str, '', $match_start, $match_len); continue; } @@ -261,12 +261,12 @@ static protected function checkSyntax (&$str) if (! $balanced_curlies) { $errors = true; - CssCrush::$process->logger->warning( + CssCrush::$config->logger->warning( '[[CssCrush]] - ' . $validate_pairings($str, '{}') ?: "Unbalanced '{' in $current_file."); } if (! $balanced_parens) { $errors = true; - CssCrush::$process->logger->warning( + CssCrush::$config->logger->warning( '[[CssCrush]] - ' . $validate_pairings($str, '()') ?: "Unbalanced '(' in $current_file."); } diff --git a/lib/CssCrush/Options.php b/lib/CssCrush/Options.php index 3e16903..69bf15b 100644 --- a/lib/CssCrush/Options.php +++ b/lib/CssCrush/Options.php @@ -70,7 +70,7 @@ public function __set ($name, $value) if (is_string($value)) { $value = Util::resolveUserPath($value, function ($path) use ($name) { if (! @mkdir($path)) { - CssCrush::$process->logger->notice( + CssCrush::$config->logger->notice( "[[CssCrush]] - Could not find or create directory specified by `$name` option."); } return $path; diff --git a/lib/CssCrush/Plugin.php b/lib/CssCrush/Plugin.php index 2517ea9..14082f9 100644 --- a/lib/CssCrush/Plugin.php +++ b/lib/CssCrush/Plugin.php @@ -68,7 +68,7 @@ public static function load ($plugin_name) } if (! $found) { - CssCrush::$process->logger->notice("[[CssCrush]] - Plugin '$plugin_name' not found."); + CssCrush::$config->logger->notice("[[CssCrush]] - Plugin '$plugin_name' not found."); } } diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 2b3a969..cc79b5d 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -52,7 +52,6 @@ public function __construct ($options) $this->aliases = $config->aliases; $this->docRoot = isset($this->options->doc_root) ? $this->options->doc_root : $config->docRoot; - $this->logger = isset($this->options->logger) ? $this->options->logger : new Logger(); // Shortcut the newline option and attach it to the process. switch ($this->options->newlines) { @@ -970,7 +969,6 @@ public function compile () Hook::run('capture_phase2', $this); $this->captureRules(); - // $process->logger->debug(array_keys($this->references)); $this->resolveInBlocks(); diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php index bb49124..c7bd398 100644 --- a/lib/CssCrush/Util.php +++ b/lib/CssCrush/Util.php @@ -192,7 +192,7 @@ public static function filePutContents ($file, $str, $caller = null) return true; } - CssCrush::$process->logger->warning("[[CssCrush]] - Could not write file '$file'."); + CssCrush::$config->logger->warning("[[CssCrush]] - Could not write file '$file'."); return false; } diff --git a/plugins/canvas.php b/plugins/canvas.php index 4c1f2bc..c267b27 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -83,6 +83,7 @@ function ($m) { function canvas_generator ($input, $context) { $process = CssCrush::$process; + $logger = CssCrush::$config->logger; // Check GD requirements are met. static $requirements; @@ -144,7 +145,7 @@ function canvas_generator ($input, $context) { // Apply functions. canvas_apply_css_funcs($canvas); - // $process->logger->debug($canvas); + // $logger->debug($canvas); // Create fingerprint for this canvas based on canvas object. $fingerprint = substr(md5(serialize($canvas)), 0, 7); @@ -227,7 +228,7 @@ function canvas_generator ($input, $context) { } } else { - // $process->logger->debug('file cached'); + // $logger->debug('file cached'); } @@ -684,14 +685,14 @@ function canvas_requirements () { if (! extension_loaded('gd')) { $requirements_met = false; - CssCrush::$process->logger->warning('[[CssCrush]] - GD extension not available.'); + CssCrush::$config->logger->warning('[[CssCrush]] - GD extension not available.'); } else { $info = array_change_key_case(gd_info()); foreach (array('png', 'jpeg') as $key) { if (empty($info["$key support"])) { $requirements_met = false; - CssCrush::$process->logger->warning("[[CssCrush]] - GD extension has no $key support."); + CssCrush::$config->logger->warning("[[CssCrush]] - GD extension has no $key support."); } } } diff --git a/plugins/initial.php b/plugins/initial.php index 9e9485f..5cc4d00 100644 --- a/plugins/initial.php +++ b/plugins/initial.php @@ -31,7 +31,7 @@ function initial (Rule $rule) { static $initial_values; if (! $initial_values) { if (! ($initial_values = @parse_ini_file(CssCrush::$dir . '/misc/initial-values.ini'))) { - CssCrush::$process->logger->notice("[[CssCrush]] - Initial keywords file could not be parsed."); + CssCrush::$config->logger->notice("[[CssCrush]] - Initial keywords file could not be parsed."); return; } diff --git a/plugins/property-sorter.php b/plugins/property-sorter.php index c2548db..9e49ebd 100644 --- a/plugins/property-sorter.php +++ b/plugins/property-sorter.php @@ -157,7 +157,7 @@ function &property_sorter_get_table () { $table = preg_split('~\s+~', trim($sorting_file_contents)); } else { - CssCrush::$process->logger->notice("[[CssCrush]] - Property sorting file not found."); + CssCrush::$config->logger->notice("[[CssCrush]] - Property sorting file not found."); } // Store to the global variable. From 6e671e48928b938f068678c6a9b2bd56b24c56fe Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 12 Oct 2013 13:02:27 +0100 Subject: [PATCH 183/421] Fixed issue with resolveUserPath not handling non-existing paths. Added additional logging notice when directories are created. --- lib/CssCrush/Options.php | 16 +++++++++++----- lib/CssCrush/Util.php | 10 +++++++--- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/lib/CssCrush/Options.php b/lib/CssCrush/Options.php index 69bf15b..7326288 100644 --- a/lib/CssCrush/Options.php +++ b/lib/CssCrush/Options.php @@ -21,6 +21,9 @@ public function __construct ($properties) public function __set ($name, $value) { + $config = CssCrush::$config; + $logger = $config->logger; + switch ($name) { // For legacy debug option, check minify has not been set then @@ -41,8 +44,8 @@ public function __set ($name, $value) // Resolve a formatter callback name and check it's callable. case 'formatter': - if (is_string($value) && isset(CssCrush::$config->formatters[$value])) { - $value = CssCrush::$config->formatters[$value]; + if (is_string($value) && isset($config->formatters[$value])) { + $value = $config->formatters[$value]; } if (! is_callable($value)) { $value = null; @@ -68,10 +71,13 @@ public function __set ($name, $value) case 'output_dir': case 'asset_dir': if (is_string($value)) { - $value = Util::resolveUserPath($value, function ($path) use ($name) { + $value = Util::resolveUserPath($value, function ($path) use ($name, $logger) { if (! @mkdir($path)) { - CssCrush::$config->logger->notice( - "[[CssCrush]] - Could not find or create directory specified by `$name` option."); + $logger->notice( + "[[CssCrush]] - Could not create directory $path (setting `$name` option)."); + } + else { + $logger->notice("[[CssCrush]] - Created directory $path (setting `$name` option)."); } return $path; }); diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php index c7bd398..93031c7 100644 --- a/lib/CssCrush/Util.php +++ b/lib/CssCrush/Util.php @@ -82,10 +82,14 @@ public static function resolveUserPath ($path, $recovery = null) $path = $realpath; } else { - // WWW root path. + $doc_root = isset(CssCrush::$process) ? CssCrush::$process->docRoot : CssCrush::$config->docRoot; + + // Absolute path. if (strpos($path, '/') === 0) { - $doc_root = isset(CssCrush::$process) ? CssCrush::$process->docRoot : CssCrush::$config->docRoot; - $path = $doc_root . $path; + // If $path is not doc_root based assume it's doc_root relative and prepend doc_root. + if (strpos($path, $doc_root) !== 0) { + $path = $doc_root . $path; + } } // Relative path. Try resolving based on the directory of the executing script. else { From 125845d86a4193ee11e291df682341bc6dd84821 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 21 Oct 2013 12:31:47 +0100 Subject: [PATCH 184/421] Updated changelog. --- CHANGELOG.md | 21 ++++++++++++++++++++- README.md | 2 +- cli.php | 41 +++++++++++++++++++++++------------------ 3 files changed, 44 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 43b6ec0..64a46a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,23 @@ -### 2.0.0 (?) +### 2.0.0 (2013-) + +* Raised PHP version requirement to PHP 5.3.1. +* Library code (excluding API functions) is now namespaced. +* Added loop plugin: For...in loops with lists and generator functions. +* Added ARIA plugin for working with aria roles states and properties. +* Added forms plugin: pseudo classes for working with forms. +* Removed legacy IE plugins (ie-clip, ie-filter, ie-min-height, rgba-fallback) and spiffing. +* Added parsing for single line variable definitions e.g. `@define col-width 30px;` +* Added support for relative input/output file paths (based on the current excecuting script path). +* Added support for protocol-relative (//) URLs. +* Removed `csscrush_clearcache()` function – Its functionality can be easily replicated in plain PHP since all output files have a '.crush.css' file extension. +* Removed `csscrush_globalvars()` function. Use `csscrush_set()` instead. +* Added `stat_dump` option for saving stats and variables used to a file in json format. +* Added `asset_dir` option for directing generated svg and image files. +* Deprecated and removed the *-local.ini now there is a better ways of augmenting the default aliases. +* If `formatter` option is set will now override the `minify` option (setting it to false) +* Now using a PSR-3 compatible logging interface (default implementation can be overridden). +* Better error reporting for syntax errors. +* Various Bug fixes. ******************************************************************** diff --git a/README.md b/README.md index 3da6f5f..d1e297e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ Logo -CSS without the Mess — CSS-Crush is a CSS preprocessor designed to enable a modern, uncluttered and standards based CSS workflow. +CSS without the Mess — CSS-Crush is a standards inspired preprocessor designed to enable a modern and uncluttered CSS workflow. * Automatic vendor prefixing * Variables diff --git a/cli.php b/cli.php index 694ba78..a925ae1 100755 --- a/cli.php +++ b/cli.php @@ -36,28 +36,29 @@ $required_value_opts = array( 'i|input|f|file', // Input file. Defaults to STDIN. - 'o|output', // Output file. Defaults to STDOUT. - 'E|enable' , // List of plugins to enable. - 'D|disable', // List of plugins to disable. - 'vars|variables', // Map of variable names in an http query string format. - 'formatter', // Formatter name for formatted output. - 'vendor-target', // Vendor target. - 'context', // Context for resolving URLs. - 'newlines', // Newline style. + 'o|output', // Output file. Defaults to STDOUT. + 'E|enable' , + 'D|disable', + 'vars|variables', + 'formatter', + 'vendor-target', + 'context', + 'newlines', ); $optional_value_opts = array( - 'b|boilerplate', // Boilerplate. - 'trace', // Debug info. + 'b|boilerplate', + 'stat-dump', + 'trace', ); $flag_opts = array( - 'p|pretty', // Pretty output. - 'w|watch', // Watch mode. - 'list', // List plugins. - 'help', // Display help. - 'version', // Display version. - 'source-map', // Display version. + 'p|pretty', + 'w|watch', + 'list', + 'help', + 'version', + 'source-map', ); // Create option strings for getopt(). @@ -82,7 +83,6 @@ // Parse opts. $opts = getopt(implode($short_opts), $long_opts); - $args = new stdClass(); // File arguments. @@ -100,6 +100,7 @@ // Arguments that optionally accept a single value. $args->boilerplate = pick($opts, 'b', 'boilerplate'); +$args->stat_dump = pick($opts, 'stat-dump'); $args->trace = pick($opts, 'trace'); // Arguments that require a single value. @@ -194,7 +195,6 @@ } } - // Run multiple value arguments through array cast. foreach (array('enable_plugins', 'disable_plugins', 'vendor_target') as $arg) { if ($args->{$arg}) { @@ -267,6 +267,10 @@ $process_opts['trace'] = is_array($args->trace) ? parse_list($args->trace) : true; } +if ($args->stat_dump) { + $process_opts['stat_dump'] = $args->stat_dump; +} + if ($args->vendor_target) { $process_opts['vendor_target'] = parse_list($args->vendor_target); } @@ -295,6 +299,7 @@ $process_opts['output_file'] = basename($args->output_file); } + ################################################################## ## Output. From 5d8a350bd4d1231debd6f5103e20d20a4100eee8 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 26 Oct 2013 20:35:35 +0100 Subject: [PATCH 185/421] Typo. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d1e297e..d9f57de 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ There are several other [functions](http://the-echoplex.net/csscrush#api) for wo There are a number of [options](http://the-echoplex.net/csscrush#options) available for tailoring the output, and a collection of bundled [plugins](http://the-echoplex.net/csscrush#plugins) that cover many workflow issues in contemporary CSS development. -### Contibuting +### Contributing If you think you've found a bug please create an [issue](https://github.com/peteboere/css-crush/issues) explaining the problem and expected result. From 3f9b4ec6adcc4aa41da0ce4eb52d54bd914f00c3 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 26 Oct 2013 20:40:36 +0100 Subject: [PATCH 186/421] Adding master branch alias to composer.json --- composer.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/composer.json b/composer.json index d2f9e32..f712263 100644 --- a/composer.json +++ b/composer.json @@ -24,5 +24,10 @@ "autoload": { "psr-0": { "CssCrush": "lib/" }, "files": [ "lib/functions.php" ] + }, + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } } } From ea12bb3542a9f9d48660bb959ae496f1734ee46d Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 29 Oct 2013 10:15:47 +0000 Subject: [PATCH 187/421] Removing notice on directory creation. --- lib/CssCrush/Options.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/CssCrush/Options.php b/lib/CssCrush/Options.php index 7326288..81d6f6c 100644 --- a/lib/CssCrush/Options.php +++ b/lib/CssCrush/Options.php @@ -77,7 +77,7 @@ public function __set ($name, $value) "[[CssCrush]] - Could not create directory $path (setting `$name` option)."); } else { - $logger->notice("[[CssCrush]] - Created directory $path (setting `$name` option)."); + $logger->debug("Created directory $path (setting `$name` option)."); } return $path; }); From 1dea2aae481252661565b94f5496d1cfabc8ecab Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 30 Oct 2013 09:59:15 +0000 Subject: [PATCH 188/421] Fixed issue with cacheData being dropped prematurely. --- lib/CssCrush/CssCrush.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/CssCrush/CssCrush.php b/lib/CssCrush/CssCrush.php index ceae7f1..63f79d6 100644 --- a/lib/CssCrush/CssCrush.php +++ b/lib/CssCrush/CssCrush.php @@ -238,9 +238,10 @@ public static function file ($file, $options = null) if ($options->cache) { $process->cacheData = $process->io('getCacheData'); if ($process->io('validateCache')) { + $file_url = $process->io('getOutputUrl'); $process->release(); - return $process->io('getOutputUrl'); + return $file_url; } } From cc0ffc5bfafa16487715dddaabc1b75e3a7e0f86 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 2 Nov 2013 13:14:26 +0000 Subject: [PATCH 189/421] Updated changelog. License.txt now hard wraps lines to be more terminal friendly. --- CHANGELOG.md | 2 +- LICENSE.txt | 18 +++++++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 64a46a9..bc08cbb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -### 2.0.0 (2013-) +### 2.0.0 (2013-11-2) * Raised PHP version requirement to PHP 5.3.1. * Library code (excluding API functions) is now namespaced. diff --git a/LICENSE.txt b/LICENSE.txt index 1242e00..51cfed7 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,7 +1,19 @@ Copyright (c) 2010-2013 Pete Boere -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: +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 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. +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. From 811430952a0f9114befa15b1cd39efa8203e05ba Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 7 Nov 2013 10:14:47 +0000 Subject: [PATCH 190/421] Minor code style refresh. Removed space before opening paren in function/method definitions to be more PSR-2 friendly. Minor README copy change. --- CssCrush.php | 2 +- README.md | 2 +- cli.php | 18 ++++++------ lib/CssCrush/BalancedMatch.php | 12 ++++---- lib/CssCrush/Color.php | 42 +++++++++++++-------------- lib/CssCrush/CssCrush.php | 26 ++++++++--------- lib/CssCrush/Declaration.php | 10 +++---- lib/CssCrush/ExtendArg.php | 2 +- lib/CssCrush/Fragment.php | 4 +-- lib/CssCrush/Functions.php | 32 ++++++++++----------- lib/CssCrush/Hook.php | 6 ++-- lib/CssCrush/IO.php | 18 ++++++------ lib/CssCrush/IO/Watch.php | 6 ++-- lib/CssCrush/Importer.php | 12 ++++---- lib/CssCrush/Mixin.php | 6 ++-- lib/CssCrush/Options.php | 12 ++++---- lib/CssCrush/Plugin.php | 12 ++++---- lib/CssCrush/PostAliasFix.php | 10 +++---- lib/CssCrush/Process.php | 52 +++++++++++++++++----------------- lib/CssCrush/Regex.php | 8 +++--- lib/CssCrush/Rule.php | 40 +++++++++++++------------- lib/CssCrush/Selector.php | 10 +++---- lib/CssCrush/Stream.php | 36 +++++++++++------------ lib/CssCrush/Template.php | 16 +++++------ lib/CssCrush/Tokens.php | 26 ++++++++--------- lib/CssCrush/Url.php | 22 +++++++------- lib/CssCrush/Util.php | 20 ++++++------- lib/CssCrush/Version.php | 6 ++-- lib/functions.php | 16 +++++------ misc/formatters.php | 6 ++-- plugins/aria.php | 2 +- plugins/canvas.php | 36 +++++++++++------------ plugins/ease.php | 2 +- plugins/forms.php | 2 +- plugins/hsl-to-hex.php | 2 +- plugins/ie-inline-block.php | 2 +- plugins/ie-opacity.php | 2 +- plugins/initial.php | 2 +- plugins/legacy-flexbox.php | 16 +++++------ plugins/loop.php | 10 +++---- plugins/noise.php | 2 +- plugins/property-sorter.php | 8 +++--- plugins/px2em.php | 6 ++-- plugins/rem.php | 2 +- plugins/svg-gradients.php | 10 +++---- plugins/svg.php | 48 +++++++++++++++---------------- 46 files changed, 321 insertions(+), 321 deletions(-) diff --git a/CssCrush.php b/CssCrush.php index dd00bec..c109956 100644 --- a/CssCrush.php +++ b/CssCrush.php @@ -4,7 +4,7 @@ * Bootstrap file with autoloader. * */ -function csscrush_autoload ($class) { +function csscrush_autoload($class) { // We're only autoloading this library. if (stripos($class, 'csscrush') !== 0) { diff --git a/README.md b/README.md index d9f57de..d4718e9 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ Logo -CSS without the Mess — CSS-Crush is a standards inspired preprocessor designed to enable a modern and uncluttered CSS workflow. +CSS-Crush is a standards inspired preprocessor designed to enable a modern and uncluttered CSS workflow. * Automatic vendor prefixing * Variables diff --git a/cli.php b/cli.php index a925ae1..54722bf 100755 --- a/cli.php +++ b/cli.php @@ -362,12 +362,12 @@ ################################################################## ## Helpers. -function stderr ($lines, $closing_newline = true) { +function stderr($lines, $closing_newline = true) { $out = implode(PHP_EOL, (array) $lines) . ($closing_newline ? PHP_EOL : ''); fwrite(STDERR, $out); } -function stdout ($lines, $closing_newline = true) { +function stdout($lines, $closing_newline = true) { $out = implode(PHP_EOL, (array) $lines) . ($closing_newline ? PHP_EOL : ''); // On OSX terminal is sometimes truncating 'visual' output to terminal @@ -375,7 +375,7 @@ function stdout ($lines, $closing_newline = true) { echo $out; } -function get_stdin_contents () { +function get_stdin_contents() { $stdin = fopen('php://stdin', 'r'); stream_set_blocking($stdin, false); $stdin_contents = stream_get_contents($stdin); @@ -384,7 +384,7 @@ function get_stdin_contents () { return $stdin_contents; } -function parse_list (array $option) { +function parse_list(array $option) { $out = array(); foreach ($option as $arg) { @@ -400,7 +400,7 @@ function parse_list (array $option) { return $out; } -function format_stats ($stats) { +function format_stats($stats) { $out = array(); foreach ($stats as $name => $value) { if (is_scalar($value)) { @@ -410,7 +410,7 @@ function format_stats ($stats) { return implode(PHP_EOL, $out); } -function pick (array &$arr) { +function pick(array &$arr) { $args = func_get_args(); array_shift($args); @@ -424,7 +424,7 @@ function pick (array &$arr) { return null; } -function colorize ($str) { +function colorize($str) { static $color_support; static $tags = array( @@ -462,7 +462,7 @@ function colorize ($str) { return str_replace($find, $replace, $str); } -function get_trailing_io_args () { +function get_trailing_io_args() { $trailing_input_file = null; $trailing_output_file = null; @@ -513,7 +513,7 @@ function get_trailing_io_args () { return array($trailing_input_file, $trailing_output_file); } -function manpage () { +function manpage() { $manpage = <<stream = $stream; $this->offset = $offset; @@ -44,27 +44,27 @@ public function __construct (Stream $stream, $offset, $brackets = '{}') } } - public function inside () + public function inside() { return $this->match[2][0]; } - public function whole () + public function whole() { return substr($this->stream->raw, $this->offset, $this->length); } - public function replace ($replacement) + public function replace($replacement) { $this->stream->splice($replacement, $this->offset, $this->length); } - public function unWrap () + public function unWrap() { $this->stream->splice($this->inside(), $this->offset, $this->length); } - public function nextIndexOf ($needle) + public function nextIndexOf($needle) { return strpos($this->stream->raw, $needle, $this->offset); } diff --git a/lib/CssCrush/Color.php b/lib/CssCrush/Color.php index 9f0d052..d0bdeb5 100644 --- a/lib/CssCrush/Color.php +++ b/lib/CssCrush/Color.php @@ -60,7 +60,7 @@ public static function &loadMinifyableKeywords () return self::$minifyableKeywords; } - public static function parse ($str) + public static function parse($str) { if ($test = Color::test($str)) { $color = $test['value']; @@ -111,7 +111,7 @@ public static function parse ($str) return $rgba; } - public static function test ($str) + public static function test($str) { static $color_patt; if (! $color_patt) { @@ -170,7 +170,7 @@ public static function test ($str) * Assumes r, g, and b are contained in the set [0, 255] and * returns h, s, and l in the set [0, 1]. */ - public static function rgbToHsl (array $rgba) + public static function rgbToHsl(array $rgba) { list($r, $g, $b, $a) = $rgba; $r /= 255; @@ -214,7 +214,7 @@ public static function rgbToHsl (array $rgba) * Assumes h, s, and l are contained in the set [0, 1] and * returns r, g, and b in the set [0, 255]. */ - public static function hslToRgb (array $hsla) + public static function hslToRgb(array $hsla) { // Populate unspecified alpha value. if (! isset($hsla[3])) { @@ -240,7 +240,7 @@ public static function hslToRgb (array $hsla) } // Convert percentages to points (0-255). - public static function normalizeCssRgb (array $rgba) + public static function normalizeCssRgb(array $rgba) { foreach ($rgba as &$val) { if (strpos($val, '%') !== false) { @@ -252,7 +252,7 @@ public static function normalizeCssRgb (array $rgba) return $rgba; } - public static function cssHslToRgb (array $hsla) + public static function cssHslToRgb(array $hsla) { // Populate unspecified alpha value. if (! isset($hsla[3])) { @@ -280,7 +280,7 @@ public static function cssHslToRgb (array $hsla) return self::hslToRgb(array($h, $s, $l, $a)); } - public static function hueToRgb ($p, $q, $t) + public static function hueToRgb($p, $q, $t) { if ($t < 0) $t += 1; if ($t > 1) $t -= 1; @@ -290,7 +290,7 @@ public static function hueToRgb ($p, $q, $t) return $p; } - public static function rgbToHex (array $rgba) + public static function rgbToHex(array $rgba) { // Drop alpha component. array_pop($rgba); @@ -303,7 +303,7 @@ public static function rgbToHex (array $rgba) return $hex_out; } - public static function hexToRgb ($hex) + public static function hexToRgb($hex) { $hex = substr($hex, 1); @@ -326,7 +326,7 @@ public static function hexToRgb ($hex) return $rgba; } - public static function colorAdjust ($str, array $adjustments) + public static function colorAdjust($str, array $adjustments) { $hsla = new Color($str, true); @@ -334,7 +334,7 @@ public static function colorAdjust ($str, array $adjustments) return $hsla->isValid ? $hsla->adjust($adjustments)->__toString() : $str; } - public static function colorSplit ($str) + public static function colorSplit($str) { if ($test = Color::test($str)) { $color = $test['value']; @@ -377,7 +377,7 @@ public static function colorSplit ($str) protected $hslColorSpace; public $isValid; - public function __construct ($color, $use_hsl_color_space = false) + public function __construct($color, $use_hsl_color_space = false) { $this->value = is_array($color) ? $color : self::parse($color); $this->isValid = ! empty($this->value); @@ -386,7 +386,7 @@ public function __construct ($color, $use_hsl_color_space = false) } } - public function __toString () + public function __toString() { // For opaque colors return hex notation as it's the most compact. if ($this->value[3] === 1) { @@ -405,7 +405,7 @@ public function __toString () } } - public function toRgb () + public function toRgb() { if ($this->hslColorSpace) { $this->hslColorSpace = false; @@ -415,7 +415,7 @@ public function toRgb () return $this; } - public function toHsl () + public function toHsl() { if (! $this->hslColorSpace) { $this->hslColorSpace = true; @@ -425,32 +425,32 @@ public function toHsl () return $this; } - public function getHex () + public function getHex() { return self::rgbToHex($this->getRgb()); } - public function getHsl () + public function getHsl() { return ! $this->hslColorSpace ? self::rgbToHsl($this->value) : $this->value; } - public function getRgb () + public function getRgb() { return $this->hslColorSpace ? self::hslToRgb($this->value) : $this->value; } - public function getComponent ($index) + public function getComponent($index) { return $this->value[$index]; } - public function setComponent ($index, $new_component_value) + public function setComponent($index, $new_component_value) { $this->value[$index] = $new_component_value; } - public function adjust (array $adjustments) + public function adjust(array $adjustments) { $was_hsl_color_space = $this->hslColorSpace; diff --git a/lib/CssCrush/CssCrush.php b/lib/CssCrush/CssCrush.php index 63f79d6..cbcb6fd 100644 --- a/lib/CssCrush/CssCrush.php +++ b/lib/CssCrush/CssCrush.php @@ -20,7 +20,7 @@ class CssCrush public static $dir; // Init called once manually post class definition. - public static function init () + public static function init() { self::$dir = dirname(dirname(__DIR__)); @@ -77,7 +77,7 @@ public static function init () require_once self::$dir . '/misc/formatters.php'; } - static protected function resolveDocRoot ($doc_root = null) + static protected function resolveDocRoot($doc_root = null) { // Get document_root reference // $_SERVER['DOCUMENT_ROOT'] is unreliable in certain CGI/Apache/IIS setups @@ -115,7 +115,7 @@ static protected function resolveDocRoot ($doc_root = null) return Util::normalizePath($doc_root); } - public static function loadAssets () + public static function loadAssets() { static $called; if ($called) { @@ -129,7 +129,7 @@ public static function loadAssets () } } - public static function parseAliasesFile ($file) + public static function parseAliasesFile($file) { $tree = @parse_ini_file($file, true); @@ -211,7 +211,7 @@ public static function parseAliasesFile ($file) * @param mixed $options An array of options or null. * @return string The public path to the compiled file or an empty string. */ - public static function file ($file, $options = null) + public static function file($file, $options = null) { self::$process = new Process($options); @@ -258,7 +258,7 @@ public static function file ($file, $options = null) * @param array $attributes An array of HTML attributes. * @return string HTML link tag or error message inside HTML comment. */ - public static function tag ($file, $options = null, $tag_attributes = array()) + public static function tag($file, $options = null, $tag_attributes = array()) { $file = self::file($file, $options); @@ -289,7 +289,7 @@ public static function tag ($file, $options = null, $tag_attributes = array()) * @param array $attributes An array of HTML attributes, set false to return CSS text without tag. * @return string HTML link tag or error message inside HTML comment. */ - public static function inline ($file, $options = null, $tag_attributes = array()) + public static function inline($file, $options = null, $tag_attributes = array()) { // For inline output set boilerplate to not display by default if (! is_array($options)) { @@ -331,7 +331,7 @@ public static function inline ($file, $options = null, $tag_attributes = array() * @param mixed $options An array of options or null. * @return string CSS text. */ - public static function string ($string, $options = null) + public static function string($string, $options = null) { // For strings set boilerplate to not display by default if (! isset($options['boilerplate'])) { @@ -367,7 +367,7 @@ public static function string ($string, $options = null) /** * Get debug info. */ - public static function stat () + public static function stat() { $process = CssCrush::$process; $stats = $process->stat; @@ -385,12 +385,12 @@ public static function stat () ############################# # Global selector aliases. - public static function addSelectorAlias ($name, $body) + public static function addSelectorAlias($name, $body) { CssCrush::$config->selectorAliases[$name] = is_callable($body) ? $body : new Template($body); } - public static function removeSelectorAlias ($name) + public static function removeSelectorAlias($name) { unset(CssCrush::$config->selectorAliases[$name]); } @@ -399,7 +399,7 @@ public static function removeSelectorAlias ($name) ############################# # Logging and stats. - public static function printLog () + public static function printLog() { if (! empty(self::$process->debugLog)) { @@ -416,7 +416,7 @@ public static function printLog () } } - public static function runStat () + public static function runStat() { $process = CssCrush::$process; $all_rules =& $process->tokens->store->r; diff --git a/lib/CssCrush/Declaration.php b/lib/CssCrush/Declaration.php index 597c205..622b2c2 100644 --- a/lib/CssCrush/Declaration.php +++ b/lib/CssCrush/Declaration.php @@ -17,7 +17,7 @@ class Declaration public $skip; public $important; - public function __construct ($prop, $value, $contextIndex = 0) + public function __construct($prop, $value, $contextIndex = 0) { $regex = Regex::$patt; @@ -70,7 +70,7 @@ public function __construct ($prop, $value, $contextIndex = 0) $this->important = $important; } - public function __toString () + public function __toString() { if (CssCrush::$process->minifyOutput) { $whitespace = ''; @@ -88,7 +88,7 @@ public function __toString () Capture parens. Index functions. */ - public function process ($parent_rule) + public function process($parent_rule) { // Apply custom functions. if (! $this->skip) { @@ -139,7 +139,7 @@ public function process ($parent_rule) $this->indexFunctions(); } - public function indexFunctions () + public function indexFunctions() { // Create an index of all regular functions in the value. $functions = array(); @@ -151,7 +151,7 @@ public function indexFunctions () $this->functions = $functions; } - public function getFullValue () + public function getFullValue() { return CssCrush::$process->tokens->restore($this->value, 'p'); } diff --git a/lib/CssCrush/ExtendArg.php b/lib/CssCrush/ExtendArg.php index b662ef8..8492dc0 100644 --- a/lib/CssCrush/ExtendArg.php +++ b/lib/CssCrush/ExtendArg.php @@ -12,7 +12,7 @@ class ExtendArg public $name; public $pseudo; - public function __construct ($name) + public function __construct($name) { $this->name = $name; diff --git a/lib/CssCrush/Fragment.php b/lib/CssCrush/Fragment.php index d11feb9..395fdea 100644 --- a/lib/CssCrush/Fragment.php +++ b/lib/CssCrush/Fragment.php @@ -10,13 +10,13 @@ class Fragment extends Template { public $name; - public function __construct ($str, $options = array()) + public function __construct($str, $options = array()) { parent::__construct($str, $options); $this->name = $options['name']; } - public function apply (array $args = null, $str = null) + public function apply(array $args = null, $str = null) { $str = parent::apply($args); diff --git a/lib/CssCrush/Functions.php b/lib/CssCrush/Functions.php index b317d5a..a6f3475 100644 --- a/lib/CssCrush/Functions.php +++ b/lib/CssCrush/Functions.php @@ -32,14 +32,14 @@ class Functions 'a-adjust' => 'CssCrush\fn__a_adjust', ); - public static function setMatchPatt () + public static function setMatchPatt() { self::$functions = self::$builtinFunctions + self::$customFunctions; self::$functionPatt = Regex::makeFunctionPatt( array_keys(self::$functions), array('bare_paren' => true)); } - public static function executeOnString ($str, $patt = null, $process_callback = null, \stdClass $context = null) + public static function executeOnString($str, $patt = null, $process_callback = null, \stdClass $context = null) { // No bracketed expressions, early return. if (strpos($str, '(') === false) { @@ -117,17 +117,17 @@ public static function executeOnString ($str, $patt = null, $process_callback = ############################# # API and helpers. - public static function register ($name, $callback) + public static function register($name, $callback) { Functions::$customFunctions[ $name] = $callback; } - public static function deRegister ($name) + public static function deRegister($name) { unset(Functions::$customFunctions[$name]); } - public static function parseArgs ($input, $allowSpaceDelim = false) + public static function parseArgs($input, $allowSpaceDelim = false) { return Util::splitDelimList( $input, ($allowSpaceDelim ? '\s*[,\s]\s*' : ',')); @@ -135,7 +135,7 @@ public static function parseArgs ($input, $allowSpaceDelim = false) // Intended as a quick arg-list parse for function that take up-to 2 arguments // with the proviso the first argument is an ident. - public static function parseArgsSimple ($input) + public static function parseArgsSimple($input) { return preg_split(Regex::$patt->argListSplit, $input, 2); } @@ -145,7 +145,7 @@ public static function parseArgsSimple ($input) ############################# # Stock CSS functions. -function fn__math ($input) { +function fn__math($input) { // Swap in math constants. $input = preg_replace( @@ -161,7 +161,7 @@ function fn__math ($input) { return $result === false ? 0 : round($result, 5); } -function fn__percent ($input) { +function fn__percent($input) { // Strip non-numeric and non delimiter characters $input = preg_replace('~[^\d\.\s,]~S', '', $input); @@ -203,37 +203,37 @@ function fn__percent ($input) { return $result . '%'; } -function fn__hsla_adjust ($input) { +function fn__hsla_adjust($input) { list($color, $h, $s, $l, $a) = array_pad(Functions::parseArgs($input, true), 5, 0); return Color::colorAdjust($color, array($h, $s, $l, $a)); } -function fn__hsl_adjust ($input) { +function fn__hsl_adjust($input) { list($color, $h, $s, $l) = array_pad(Functions::parseArgs($input, true), 4, 0); return Color::colorAdjust($color, array($h, $s, $l, 0)); } -function fn__h_adjust ($input) { +function fn__h_adjust($input) { list($color, $h) = array_pad(Functions::parseArgs($input, true), 2, 0); return Color::colorAdjust($color, array($h, 0, 0, 0)); } -function fn__s_adjust ($input) { +function fn__s_adjust($input) { list($color, $s) = array_pad(Functions::parseArgs($input, true), 2, 0); return Color::colorAdjust($color, array(0, $s, 0, 0)); } -function fn__l_adjust ($input) { +function fn__l_adjust($input) { list($color, $l) = array_pad(Functions::parseArgs($input, true), 2, 0); return Color::colorAdjust($color, array(0, 0, $l, 0)); } -function fn__a_adjust ($input) { +function fn__a_adjust($input) { list($color, $a) = array_pad(Functions::parseArgs($input, true), 2, 0); return Color::colorAdjust($color, array(0, 0, 0, $a)); } -function fn__this ($input, $context) { +function fn__this($input, $context) { $args = Functions::parseArgsSimple($input); $property = $args[0]; @@ -260,7 +260,7 @@ function fn__this ($input, $context) { return ''; } -function fn__query ($input, $context) { +function fn__query($input, $context) { $args = Functions::parseArgs($input); diff --git a/lib/CssCrush/Hook.php b/lib/CssCrush/Hook.php index 535f441..6ec5b13 100644 --- a/lib/CssCrush/Hook.php +++ b/lib/CssCrush/Hook.php @@ -11,7 +11,7 @@ class Hook // Table of hooks and the functions attached to them. public static $register = array(); - public static function add ($hook, $fn_name) + public static function add($hook, $fn_name) { // Bail early is the named hook and callback combination is already loaded. if (! isset(self::$register[$hook][$fn_name])) { @@ -24,12 +24,12 @@ public static function add ($hook, $fn_name) } } - public static function remove ($hook, $fn_name) + public static function remove($hook, $fn_name) { unset(self::$register[$hook][$fn_name]); } - public static function run ($hook, $arg_obj = null) + public static function run($hook, $arg_obj = null) { // Run all callbacks attached to the hook. if (isset(self::$register[$hook])) { diff --git a/lib/CssCrush/IO.php b/lib/CssCrush/IO.php index 7fbf4af..a66f3b4 100644 --- a/lib/CssCrush/IO.php +++ b/lib/CssCrush/IO.php @@ -8,13 +8,13 @@ class IO { - public static function init () + public static function init() { $process = CssCrush::$process; $process->cacheFile = "{$process->output->dir}/.csscrush"; } - public static function getOutputDir () + public static function getOutputDir() { $process = CssCrush::$process; $output_dir = $process->options->output_dir; @@ -22,7 +22,7 @@ public static function getOutputDir () return $output_dir ? $output_dir : $process->input->dir; } - public static function testOutputDir () + public static function testOutputDir() { $dir = CssCrush::$process->output->dir; $logger = CssCrush::$config->logger; @@ -48,7 +48,7 @@ public static function testOutputDir () return $pathtest; } - public static function getOutputFileName () + public static function getOutputFileName() { $process = CssCrush::$process; $options = $process->options; @@ -62,7 +62,7 @@ public static function getOutputFileName () return "$output_basename.crush.css"; } - public static function getOutputUrl () + public static function getOutputUrl() { $process = CssCrush::$process; $options = $process->options; @@ -90,7 +90,7 @@ public static function getOutputUrl () return $url; } - public static function validateCache () + public static function validateCache() { $process = CssCrush::$process; $config = CssCrush::$config; @@ -164,7 +164,7 @@ public static function validateCache () } } - public static function getCacheData () + public static function getCacheData() { $config = CssCrush::$config; $logger = $config->logger; @@ -206,7 +206,7 @@ public static function getCacheData () return $cache_data; } - public static function saveCacheData () + public static function saveCacheData() { $process = CssCrush::$process; $logger = CssCrush::$config->logger; @@ -217,7 +217,7 @@ public static function saveCacheData () Util::filePutContents($process->cacheFile, json_encode($process->cacheData, $flags), __METHOD__); } - public static function write (Stream $stream) + public static function write(Stream $stream) { $process = CssCrush::$process; $output = $process->output; diff --git a/lib/CssCrush/IO/Watch.php b/lib/CssCrush/IO/Watch.php index 380513f..292b801 100644 --- a/lib/CssCrush/IO/Watch.php +++ b/lib/CssCrush/IO/Watch.php @@ -13,7 +13,7 @@ class Watch extends IO { public static $cacheData = array(); - public static function getOutputFileName () + public static function getOutputFileName() { $process = CssCrush::$process; $options = $process->options; @@ -32,7 +32,7 @@ public static function getOutputFileName () return "$output_basename$suffix.css"; } - public static function getCacheData () + public static function getCacheData() { // Clear results from earlier processes. clearstatcache(); @@ -41,7 +41,7 @@ public static function getCacheData () return self::$cacheData; } - public static function saveCacheData () + public static function saveCacheData() { self::$cacheData = CssCrush::$process->cacheData; } diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index 7db8a5b..942fc2a 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -8,7 +8,7 @@ class Importer { - public static function hostfile () + public static function hostfile() { $config = CssCrush::$config; $process = CssCrush::$process; @@ -146,7 +146,7 @@ public static function hostfile () return $str; } - static protected function rewriteImportedUrls ($import) + static protected function rewriteImportedUrls($import) { $link = Util::getLinkBetweenPaths( CssCrush::$process->input->dir, dirname($import->path)); @@ -170,7 +170,7 @@ static protected function rewriteImportedUrls ($import) } } - static protected function prepareForStream (&$str) + static protected function prepareForStream(&$str) { $regex = Regex::$patt; $process = CssCrush::$process; @@ -211,7 +211,7 @@ static protected function prepareForStream (&$str) return true; } - static protected function checkSyntax (&$str) + static protected function checkSyntax(&$str) { // Catch obvious typing errors. $errors = false; @@ -273,7 +273,7 @@ static protected function checkSyntax (&$str) return $errors ? false : true; } - static protected function addMarkers (&$str) + static protected function addMarkers(&$str) { $process = CssCrush::$process; $current_file_index = count($process->sources) -1; @@ -305,7 +305,7 @@ static protected function addMarkers (&$str) } } - static protected function captureCommentAndString ($str) + static protected function captureCommentAndString($str) { $callback = function ($m) { diff --git a/lib/CssCrush/Mixin.php b/lib/CssCrush/Mixin.php index e6e4245..003cbd6 100644 --- a/lib/CssCrush/Mixin.php +++ b/lib/CssCrush/Mixin.php @@ -10,12 +10,12 @@ class Mixin { public $template; - public function __construct ($block) + public function __construct($block) { $this->template = new Template($block); } - public static function call ($message, $context = null) + public static function call($message, $context = null) { $process = CssCrush::$process; $mixable = null; @@ -85,7 +85,7 @@ public static function call ($message, $context = null) } } - public static function merge (array $input, $message_list, $options = array()) + public static function merge(array $input, $message_list, $options = array()) { $context = isset($options['context']) ? $options['context'] : null; diff --git a/lib/CssCrush/Options.php b/lib/CssCrush/Options.php index 81d6f6c..59e75ed 100644 --- a/lib/CssCrush/Options.php +++ b/lib/CssCrush/Options.php @@ -10,7 +10,7 @@ class Options { public $data = array(); - public function __construct ($properties) + public function __construct($properties) { if ($properties) { foreach ($properties as $key => $value) { @@ -19,7 +19,7 @@ public function __construct ($properties) } } - public function __set ($name, $value) + public function __set($name, $value) { $config = CssCrush::$config; $logger = $config->logger; @@ -103,17 +103,17 @@ public function __set ($name, $value) $this->data[$name] = $value; } - public function __get ($name) + public function __get($name) { return isset($this->data[$name]) ? $this->data[$name] : null; } - public function __isset ($name) + public function __isset($name) { return isset($this->data[$name]); } - public function merge (Options $options_instance) + public function merge(Options $options_instance) { foreach ($options_instance->data as $key => $value) { if (! array_key_exists($key, $this->data)) { @@ -122,7 +122,7 @@ public function merge (Options $options_instance) } } - public function get () + public function get() { return $this->data; } diff --git a/lib/CssCrush/Plugin.php b/lib/CssCrush/Plugin.php index 14082f9..0599798 100644 --- a/lib/CssCrush/Plugin.php +++ b/lib/CssCrush/Plugin.php @@ -10,7 +10,7 @@ class Plugin { static protected $plugins = array(); - public static function info () + public static function info() { $plugin_dirs = CssCrush::$config->pluginDirs; $plugin_data = array(); @@ -26,7 +26,7 @@ public static function info () return $plugin_data; } - public static function parseDoc ($plugin_path) + public static function parseDoc($plugin_path) { $contents = file_get_contents($plugin_path); if (preg_match('~/\*\*(.*?)\*/~s', $contents, $m)) { @@ -44,12 +44,12 @@ public static function parseDoc ($plugin_path) return false; } - public static function register ($plugin_name, $callbacks) + public static function register($plugin_name, $callbacks) { self::$plugins[$plugin_name] = $callbacks; } - public static function load ($plugin_name) + public static function load($plugin_name) { // Assume the the plugin file is not loaded if null. if (! isset(self::$plugins[$plugin_name])) { @@ -75,7 +75,7 @@ public static function load ($plugin_name) return isset(self::$plugins[$plugin_name]) ? self::$plugins[$plugin_name] : null; } - public static function enable ($plugin_name) + public static function enable($plugin_name) { $plugin = self::load($plugin_name); @@ -86,7 +86,7 @@ public static function enable ($plugin_name) return true; } - public static function disable ($plugin_name) + public static function disable($plugin_name) { $plugin = isset(self::$plugins[$plugin_name]) ? self::$plugins[$plugin_name] : null; diff --git a/lib/CssCrush/PostAliasFix.php b/lib/CssCrush/PostAliasFix.php index a210bd1..b00aca0 100644 --- a/lib/CssCrush/PostAliasFix.php +++ b/lib/CssCrush/PostAliasFix.php @@ -13,7 +13,7 @@ class PostAliasFix ':gradients' => 'CssCrush\postalias_fix_gradients', ); - public static function add ($alias_type, $key, $callback) + public static function add($alias_type, $key, $callback) { if ($alias_type === 'function') { // $key is the aliased css function name. @@ -21,7 +21,7 @@ public static function add ($alias_type, $key, $callback) } } - public static function remove ($alias_type, $key) + public static function remove($alias_type, $key) { if ($type === 'function') { // $key is the aliased css function name. @@ -33,7 +33,7 @@ public static function remove ($alias_type, $key) /** * Post alias fix callback for all gradients. */ -function postalias_fix_gradients ($declaration_copies) { +function postalias_fix_gradients($declaration_copies) { postalias_fix_linear_gradients($declaration_copies); postalias_fix_radial_gradients($declaration_copies); } @@ -42,7 +42,7 @@ function postalias_fix_gradients ($declaration_copies) { * Convert the new angle syntax (keyword and degree) on -x-linear-gradient() functions * to legacy equivalents. */ -function postalias_fix_linear_gradients ($declaration_copies) { +function postalias_fix_linear_gradients($declaration_copies) { static $angles_new, $angles_old; if (! $angles_new) { @@ -118,7 +118,7 @@ function postalias_fix_linear_gradients ($declaration_copies) { /** * Remove the 'at' keyword from -x-radial-gradient() for legacy implementations. */ -function postalias_fix_radial_gradients ($declaration_copies) { +function postalias_fix_radial_gradients($declaration_copies) { // Create new paren tokens based on the first prefixed declaration. // Replace the new syntax with the legacy syntax. diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index cc79b5d..83175d7 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -8,7 +8,7 @@ class Process { - public function __construct ($options) + public function __construct($options) { $config = CssCrush::$config; @@ -72,7 +72,7 @@ public function __construct ($options) Hook::run('process_init'); } - public function release () + public function release() { unset( $this->tokens, @@ -86,7 +86,7 @@ public function release () ); } - public function resolveContext ($input_dir, $input_file = null) + public function resolveContext($input_dir, $input_file = null) { if ($input_file) { $this->ioContext = 'file'; @@ -117,7 +117,7 @@ public function resolveContext ($input_dir, $input_file = null) return $context_resolved; } - public function io ($method) + public function io($method) { // Get argument list (excluding the method name which comes first). $args = func_get_args(); @@ -130,7 +130,7 @@ public function io ($method) ############################# # Boilerplate. - protected function getBoilerplate () + protected function getBoilerplate() { $file = false; $boilerplate_option = $this->options->boilerplate; @@ -198,7 +198,7 @@ protected function getBoilerplate () ############################# # Selector aliases. - protected function resolveSelectorAliases () + protected function resolveSelectorAliases() { $this->stream->pregReplaceCallback( Regex::make('~@selector-alias +\:?({{ident}}) +([^;]+) *;~iS'), @@ -218,7 +218,7 @@ function ($m) { } } - public static function applySelectorAliases (&$str) + public static function applySelectorAliases(&$str) { $process = CssCrush::$process; @@ -275,7 +275,7 @@ public static function applySelectorAliases (&$str) ############################# # Aliases. - protected function filterAliases () + protected function filterAliases() { // If a vendor target is given, we prune the aliases array. $vendors = $this->options->vendor_target; @@ -375,7 +375,7 @@ protected function filterAliases () ############################# # Plugins. - protected function filterPlugins () + protected function filterPlugins() { $options = $this->options; $config = CssCrush::$config; @@ -414,7 +414,7 @@ protected function filterPlugins () ############################# # Variables. - protected function captureVars () + protected function captureVars() { $vars_patt = Regex::make('~ @define @@ -450,7 +450,7 @@ protected function captureVars () } } - protected function placeAllVars () + protected function placeAllVars() { // Place variables in main stream. self::placeVars($this->stream->raw); @@ -471,7 +471,7 @@ protected function placeAllVars () } } - static protected function placeVars (&$value) + static protected function placeVars(&$value) { // Variables with no default value. $value = preg_replace_callback(Regex::$patt->varFunction, @@ -500,7 +500,7 @@ static protected function placeVars (&$value) return $vars_placed; } - static protected function cb_placeVars ($m) + static protected function cb_placeVars($m) { $var_name = $m[1]; if (isset(CssCrush::$process->vars[$var_name])) { @@ -512,7 +512,7 @@ static protected function cb_placeVars ($m) ############################# # @ifdefine blocks. - protected function resolveIfDefines () + protected function resolveIfDefines() { $matches = $this->stream->matchAll(Regex::$patt->ifDefine); @@ -545,7 +545,7 @@ protected function resolveIfDefines () ############################# # Mixins. - protected function captureMixins () + protected function captureMixins() { $this->stream->pregReplaceCallback(Regex::$patt->mixin, function ($m) { CssCrush::$process->mixins[$m['name']] = new Mixin($m['block_content']); @@ -556,7 +556,7 @@ protected function captureMixins () ############################# # Fragments. - protected function resolveFragments () + protected function resolveFragments() { $fragments =& CssCrush::$process->fragments; @@ -585,7 +585,7 @@ protected function resolveFragments () ############################# # Rules. - public function captureRules () + public function captureRules() { $this->stream->pregReplaceCallback(Regex::$patt->rule, function ($m) { @@ -610,7 +610,7 @@ public function captureRules () }); } - protected function processRules () + protected function processRules() { // Create table of name/selector to rule references. $named_references = array(); @@ -658,7 +658,7 @@ protected function processRules () ############################# # @in blocks. - protected function resolveInBlocks () + protected function resolveInBlocks() { $matches = $this->stream->matchAll('~@in\s+([^{]+)\{~iS'); $tokens = CssCrush::$process->tokens; @@ -736,7 +736,7 @@ protected function resolveInBlocks () ############################# # @-rule aliasing. - protected function aliasAtRules () + protected function aliasAtRules() { if (empty($this->aliases['at-rules'])) { @@ -813,7 +813,7 @@ protected function aliasAtRules () ############################# # Compile / collate. - protected function collate () + protected function collate() { $options = $this->options; $minify = $options->minify; @@ -922,7 +922,7 @@ protected function collate () } } - public function compile () + public function compile() { // Always store start time. $this->stat['compile_start_time'] = microtime(true); @@ -989,7 +989,7 @@ public function compile () ############################# # Source maps. - public function generateSourceMap () + public function generateSourceMap() { $this->sourceMap = array( 'version' => '3', @@ -1044,7 +1044,7 @@ public function generateSourceMap () $this->sourceMap['mappings'] = implode(';', $mappings); } - public function generateTracingStub ($m) + public function generateTracingStub($m) { $token = $m[0]; $tokens =& $this->tokens->store->t; @@ -1073,7 +1073,7 @@ public function generateTracingStub ($m) ############################# # Decruft. - protected function decruft () + protected function decruft() { return $this->stream->pregReplaceHash(array( @@ -1102,7 +1102,7 @@ protected function decruft () ############################# # Advanced minification. - protected function minifyColors () + protected function minifyColors() { static $keywords_patt, $functions_patt; diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php index 5172f4e..66ecd41 100644 --- a/lib/CssCrush/Regex.php +++ b/lib/CssCrush/Regex.php @@ -16,7 +16,7 @@ class Regex public static $swaps = array(); - public static function init () + public static function init() { self::$patt = $patt = new \stdClass(); self::$classes = $classes = new \stdClass(); @@ -120,7 +120,7 @@ public static function init () $patt->cruftyHex = Regex::make('~\#({{hex}})\1({{hex}})\2({{hex}})\3~S'); } - public static function make ($pattern) + public static function make($pattern) { static $cache = array(), $find, $replace; if (isset($cache[$pattern])) { @@ -135,14 +135,14 @@ public static function make ($pattern) return $cache[$pattern] = str_replace($find, $replace, $pattern); } - public static function matchAll ($patt, $subject, $offset = 0) + public static function matchAll($patt, $subject, $offset = 0) { $count = preg_match_all($patt, $subject, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER, $offset); return $count ? $matches : array(); } - public static function makeFunctionPatt ($list, $options = array()) + public static function makeFunctionPatt($list, $options = array()) { // Bare parens. $question = ''; diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php index e144042..6929109 100644 --- a/lib/CssCrush/Rule.php +++ b/lib/CssCrush/Rule.php @@ -32,7 +32,7 @@ class Rule implements \IteratorAggregate // Declarations hash table for external query() referencing. public $queryData = array(); - public function __construct ($selector_string, $declarations_string, $trace_token = null) + public function __construct($selector_string, $declarations_string, $trace_token = null) { $process = CssCrush::$process; $this->label = $process->tokens->createLabel('r'); @@ -109,7 +109,7 @@ public function __construct ($selector_string, $declarations_string, $trace_toke } } - public function __toString () + public function __toString() { $process = CssCrush::$process; @@ -143,7 +143,7 @@ public function __toString () } public $declarationsProcessed = false; - public function processDeclarations () + public function processDeclarations() { if ($this->declarationsProcessed) { return; @@ -166,7 +166,7 @@ public function processDeclarations () $this->declarationsProcessed = true; } - public function flatten () + public function flatten() { if ($this->isFlat) { return; @@ -190,7 +190,7 @@ public function flatten () $this->isFlat = true; } - public function expandDataSet ($dataset, $property) + public function expandDataSet($dataset, $property) { // Expand shorthand properties to make them available // as data for this() and query(). @@ -297,7 +297,7 @@ public function expandDataSet ($dataset, $property) ############################# # Rule inheritance. - public function setExtendSelectors ($raw_value) + public function setExtendSelectors($raw_value) { // Reset if called earlier, last call wins by intention. $this->extendArgs = array(); @@ -308,7 +308,7 @@ public function setExtendSelectors ($raw_value) } public $resolvedExtendables = false; - public function resolveExtendables () + public function resolveExtendables() { if (! $this->extendArgs) { @@ -340,7 +340,7 @@ public function resolveExtendables () return true; } - public function applyExtendables () + public function applyExtendables() { if (! $this->resolveExtendables()) { @@ -381,7 +381,7 @@ public function applyExtendables () ############################# # Selectors. - public function expandSelectors () + public function expandSelectors() { $new_set = array(); @@ -460,7 +460,7 @@ public function expandSelectors () $this->selectors = $new_set; } - public function addSelector ($selector) + public function addSelector($selector) { $this->selectors[$selector->readableValue] = $selector; } @@ -469,7 +469,7 @@ public function addSelector ($selector) ############################# # Aliasing. - public function addPropertyAliases () + public function addPropertyAliases() { $aliased_properties =& CssCrush::$process->aliases['properties']; @@ -538,7 +538,7 @@ public function addPropertyAliases () } } - public function addFunctionAliases () + public function addFunctionAliases() { $function_aliases =& CssCrush::$process->aliases['functions']; $function_alias_groups =& CssCrush::$process->aliases['function_groups']; @@ -660,7 +660,7 @@ public function addFunctionAliases () } } - public function addDeclarationAliases () + public function addDeclarationAliases() { $declaration_aliases =& CssCrush::$process->aliases['declarations']; @@ -720,7 +720,7 @@ public function addDeclarationAliases () ############################# # IteratorAggregate interface. - public function getIterator () + public function getIterator() { return new \ArrayIterator($this->declarations); } @@ -729,7 +729,7 @@ public function getIterator () ############################# # Property indexing. - public function indexProperty ($declaration) + public function indexProperty($declaration) { $prop = $declaration->property; @@ -742,7 +742,7 @@ public function indexProperty ($declaration) $this->canonicalProperties[$declaration->canonicalProperty] = true; } - public function updatePropertyIndex () + public function updatePropertyIndex() { // Reset tables. $this->properties = array(); @@ -757,12 +757,12 @@ public function updatePropertyIndex () ############################# # Rule API. - public function propertyCount ($prop) + public function propertyCount($prop) { return isset($this->properties[$prop]) ? $this->properties[$prop] : 0; } - public function addDeclaration ($prop, $value, $contextIndex = 0) + public function addDeclaration($prop, $value, $contextIndex = 0) { // Create declaration, add to the stack if it's valid $declaration = new Declaration($prop, $value, $contextIndex); @@ -777,7 +777,7 @@ public function addDeclaration ($prop, $value, $contextIndex = 0) return false; } - public function setDeclarations (array $declaration_stack) + public function setDeclarations(array $declaration_stack) { $this->declarations = $declaration_stack; @@ -785,7 +785,7 @@ public function setDeclarations (array $declaration_stack) $this->updatePropertyIndex(); } - public static function parseBlock ($str, $options = array()) + public static function parseBlock($str, $options = array()) { $str = Util::stripCommentTokens($str); $lines = preg_split('~\s*;\s*~', $str, null, PREG_SPLIT_NO_EMPTY); diff --git a/lib/CssCrush/Selector.php b/lib/CssCrush/Selector.php index e221c00..aef8c0b 100644 --- a/lib/CssCrush/Selector.php +++ b/lib/CssCrush/Selector.php @@ -12,7 +12,7 @@ class Selector public $readableValue; public $allowPrefix = true; - public function __construct ($raw_selector, $associated_rule = null) + public function __construct($raw_selector, $associated_rule = null) { // Look for rooting prefix. if (strpos($raw_selector, '^') === 0) { @@ -29,7 +29,7 @@ public function __construct ($raw_selector, $associated_rule = null) $this->value = CssCrush::$process->tokens->captureParens($raw_selector); } - public function __toString () + public function __toString() { if (! CssCrush::$process->minifyOutput) { $this->value = Selector::normalizeWhiteSpace($this->value); @@ -37,7 +37,7 @@ public function __toString () return $this->value; } - public function appendPseudo ($pseudo) + public function appendPseudo($pseudo) { // Check to avoid doubling-up. if (! Stream::endsWith($this->readableValue, $pseudo)) { @@ -48,14 +48,14 @@ public function appendPseudo ($pseudo) return $this->readableValue; } - public static function normalizeWhiteSpace ($str) + public static function normalizeWhiteSpace($str) { // Create space around combinators, then normalize whitespace. $str = preg_replace('~([>+]|\~(?!=))~S', ' $1 ', $str); return Util::normalizeWhiteSpace($str); } - static function makeReadable ($str) + static function makeReadable($str) { // Quick test for paren tokens. if (strpos($str, '?p') !== false) { diff --git a/lib/CssCrush/Stream.php b/lib/CssCrush/Stream.php index cefc130..fd42df8 100644 --- a/lib/CssCrush/Stream.php +++ b/lib/CssCrush/Stream.php @@ -8,29 +8,29 @@ class Stream { - public function __construct ($str) + public function __construct($str) { $this->raw = $str; } - public function __toString () + public function __toString() { return $this->raw; } - public static function endsWith ($haystack, $needle) + public static function endsWith($haystack, $needle) { return substr($haystack, -strlen($needle)) === $needle; } - public function update ($str) + public function update($str) { $this->raw = $str; return $this; } - public function substr ($start, $length = null) + public function substr($start, $length = null) { if (! isset($length)) { @@ -42,19 +42,19 @@ public function substr ($start, $length = null) } } - public function matchAll ($patt, $offset = 0) + public function matchAll($patt, $offset = 0) { return Regex::matchAll($patt, $this->raw, $offset); } - public function replace ($find, $replacement) + public function replace($find, $replacement) { $this->raw = str_replace($find, $replacement, $this->raw); return $this; } - public function replaceHash ($replacements) + public function replaceHash($replacements) { if ($replacements) { $this->raw = str_replace( @@ -65,7 +65,7 @@ public function replaceHash ($replacements) return $this; } - public function replaceTokens ($type, $callback = null) + public function replaceTokens($type, $callback = null) { $tokens =& CssCrush::$process->tokens->store->{ $type }; $callback = $callback ?: function ($m) use (&$tokens) { @@ -76,19 +76,19 @@ public function replaceTokens ($type, $callback = null) return $this; } - public function pregReplace ($patt, $replacement) + public function pregReplace($patt, $replacement) { $this->raw = preg_replace($patt, $replacement, $this->raw); return $this; } - public function pregReplaceCallback ($patt, $callback) + public function pregReplaceCallback($patt, $callback) { $this->raw = preg_replace_callback($patt, $callback, $this->raw); return $this; } - public function pregReplaceHash ($replacements) + public function pregReplaceHash($replacements) { if ($replacements) { $this->raw = preg_replace( @@ -99,37 +99,37 @@ public function pregReplaceHash ($replacements) return $this; } - public function append ($append) + public function append($append) { $this->raw .= $append; return $this; } - public function prepend ($prepend) + public function prepend($prepend) { $this->raw = $prepend . $this->raw; return $this; } - public function splice ($replacement, $offset, $length = null) + public function splice($replacement, $offset, $length = null) { $this->raw = substr_replace($this->raw, $replacement, $offset, $length); return $this; } - public function trim () + public function trim() { $this->raw = trim($this->raw); return $this; } - public function rTrim () + public function rTrim() { $this->raw = rtrim($this->raw); return $this; } - public function lTrim () + public function lTrim() { $this->raw = ltrim($this->raw); return $this; diff --git a/lib/CssCrush/Template.php b/lib/CssCrush/Template.php index 6cce96d..1ae8a1d 100644 --- a/lib/CssCrush/Template.php +++ b/lib/CssCrush/Template.php @@ -19,7 +19,7 @@ class Template // The string passed in with arg calls replaced by tokens. public $string; - public function __construct ($str, $options = array()) + public function __construct($str, $options = array()) { static $arg_patt; if (! $arg_patt) { @@ -37,7 +37,7 @@ public function __construct ($str, $options = array()) )); } - public function capture ($str) + public function capture($str) { $args = Functions::parseArgsSimple($str); @@ -64,7 +64,7 @@ public function capture ($str) return "?a$position?"; } - public function getArgValue ($index, &$args) + public function getArgValue($index, &$args) { // First lookup a passed value. if (isset($args[$index]) && $args[$index] !== 'default') { @@ -86,7 +86,7 @@ public function getArgValue ($index, &$args) return $default; } - public function prepare (array $args, $persist = true) + public function prepare(array $args, $persist = true) { // Create table of substitutions. $find = array(); @@ -112,12 +112,12 @@ public function prepare (array $args, $persist = true) return $substitutions; } - public function reset () + public function reset() { unset($this->substitutions); } - public function apply (array $args = null, $str = null) + public function apply(array $args = null, $str = null) { $str = isset($str) ? $str : $this->string; @@ -140,7 +140,7 @@ public function apply (array $args = null, $str = null) return Template::tokenize($str); } - public static function tokenize ($str) + public static function tokenize($str) { $str = CssCrush::$process->tokens->capture($str, 's'); $str = CssCrush::$process->tokens->capture($str, 'u'); @@ -148,7 +148,7 @@ public static function tokenize ($str) return $str; } - public static function unTokenize ($str) + public static function unTokenize($str) { $str = CssCrush::$process->tokens->restore($str, 'u', true); $str = CssCrush::$process->tokens->restore($str, 's', true); diff --git a/lib/CssCrush/Tokens.php b/lib/CssCrush/Tokens.php index 7df2eaf..f9e1d57 100644 --- a/lib/CssCrush/Tokens.php +++ b/lib/CssCrush/Tokens.php @@ -11,7 +11,7 @@ class Tokens public $store; protected $ids; - public function __construct (array $types = null) + public function __construct(array $types = null) { $types = $types ?: array( 's', // Strings @@ -31,38 +31,38 @@ public function __construct (array $types = null) } } - public function get ($label) + public function get($label) { $path =& $this->store->{$label[1]}; return isset($path[$label]) ? $path[$label] : null; } - public function pop ($label) + public function pop($label) { $value = $this->get($label); $this->release($label); return $value; } - public function release ($label) + public function release($label) { unset($this->store->{$label[1]}[$label]); } - public function add ($value, $type, $existing_label = null) + public function add($value, $type, $existing_label = null) { $label = $existing_label ? $existing_label : $this->createLabel($type); $this->store->{$type}[$label] = $value; return $label; } - public function createLabel ($type) + public function createLabel($type) { $counter = base_convert(++$this->ids->{$type}, 10, 36); return "?$type$counter?"; } - public function restore ($str, $type, $release = false) + public function restore($str, $type, $release = false) { switch ($type) { case 'u': @@ -92,7 +92,7 @@ public function restore ($str, $type, $release = false) return $str; } - public function capture ($str, $type) + public function capture($str, $type) { switch ($type) { case 'u': @@ -107,21 +107,21 @@ public function capture ($str, $type) } } - public function captureParens ($str) + public function captureParens($str) { return preg_replace_callback(Regex::$patt->parens, function ($m) { return CssCrush::$process->tokens->add($m[0], 'p'); }, $str); } - public function captureStrings ($str, $add_padding = false) + public function captureStrings($str, $add_padding = false) { return preg_replace_callback(Regex::$patt->string, function ($m) { return CssCrush::$process->tokens->add($m[0], 's'); }, $str); } - public function captureUrls ($str, $add_padding = false) + public function captureUrls($str, $add_padding = false) { $count = preg_match_all( Regex::make('~@import \s+ (?{{s-token}}) | {{LB}} (?url|data-uri) {{parens}}~ixS'), @@ -161,7 +161,7 @@ public function captureUrls ($str, $add_padding = false) return $str; } - public static function pad ($label, $replaced_text) + public static function pad($label, $replaced_text) { // Padding token labels to maintain whitespace and newlines. @@ -177,7 +177,7 @@ public static function pad ($label, $replaced_text) return $label; } - public static function is ($label, $of_type) + public static function is($label, $of_type) { if (preg_match(Regex::make('~^ \? (?[a-zA-Z]) {{token-id}} \? $~xS'), $label, $m)) { diff --git a/lib/CssCrush/Url.php b/lib/CssCrush/Url.php index 43d553d..76ef080 100644 --- a/lib/CssCrush/Url.php +++ b/lib/CssCrush/Url.php @@ -20,7 +20,7 @@ class Url public $value; public $label; - public function __construct ($raw_value, $options = array()) + public function __construct($raw_value, $options = array()) { $standalone = ! empty($options['standalone']); if (! $standalone) { @@ -39,7 +39,7 @@ public function __construct ($raw_value, $options = array()) $this->evaluate(); } - public function __toString () + public function __toString() { if ($this->convertToData) { $this->toData(); @@ -58,14 +58,14 @@ public function __toString () return "url(http://github.com/$quote$this-%3Evalue$quote)"; } - public function update ($new_value) + public function update($new_value) { $this->value = $new_value; return $this->evaluate(); } - public function evaluate () + public function evaluate() { // Protocol or protocol-relative (//) based URL. if (preg_match('~^(?: (?[a-z]+)\: | \/{2} )~ix', $this->value, $m)) { @@ -102,7 +102,7 @@ public function evaluate () return $this; } - public function getAbsolutePath () + public function getAbsolutePath() { $path = false; if ($this->protocol) { @@ -115,7 +115,7 @@ public function getAbsolutePath () return $path; } - public function getOriginalValue () + public function getOriginalValue() { // If a data URI we assume nothing useful can be achieved // by returning the original value so we just return the token label. @@ -132,7 +132,7 @@ public function getOriginalValue () return "$function($this->value)"; } - public function prepend ($path_fragment) + public function prepend($path_fragment) { if ($this->isRelative) { $this->value = $path_fragment . $this->value; @@ -141,7 +141,7 @@ public function prepend ($path_fragment) return $this; } - public function toRoot () + public function toRoot() { if ($this->isRelative) { $this->prepend(CssCrush::$process->input->dirUrl . '/'); @@ -151,7 +151,7 @@ public function toRoot () return $this; } - public function toData () + public function toData() { // Only make one conversion attempt. $this->convertToData = false; @@ -192,7 +192,7 @@ public function toData () return $this; } - public function setType ($type = 'absolute') + public function setType($type = 'absolute') { $this->isAbsolute = false; $this->isRooted = false; @@ -218,7 +218,7 @@ public function setType ($type = 'absolute') return $this; } - public function simplify () + public function simplify() { if ($this->isRelative || $this->isRooted) { $this->value = Util::simplifyPath($this->value); diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php index 93031c7..bebb77c 100644 --- a/lib/CssCrush/Util.php +++ b/lib/CssCrush/Util.php @@ -8,7 +8,7 @@ class Util { - public static function htmlAttributes (array $attributes, array $sort_order = null) + public static function htmlAttributes(array $attributes, array $sort_order = null) { // Optionally sort attributes (for better readability). if ($sort_order) { @@ -43,7 +43,7 @@ public static function htmlAttributes (array $attributes, array $sort_order = nu return $str; } - public static function normalizePath ($path, $strip_drive_letter = false) + public static function normalizePath($path, $strip_drive_letter = false) { if (! $path) { return ''; @@ -65,7 +65,7 @@ public static function normalizePath ($path, $strip_drive_letter = false) return Util::simplifyPath($path); } - public static function simplifyPath ($path) + public static function simplifyPath($path) { // Reduce redundant path segments. e.g 'foo/../bar' => 'bar' $patt = '~[^/.]+/\.\./~S'; @@ -75,7 +75,7 @@ public static function simplifyPath ($path) return $path; } - public static function resolveUserPath ($path, $recovery = null) + public static function resolveUserPath($path, $recovery = null) { // System path. if ($realpath = realpath($path)) { @@ -105,12 +105,12 @@ public static function resolveUserPath ($path, $recovery = null) return $path ? Util::normalizePath($path) : false; } - public static function stripCommentTokens ($str) + public static function stripCommentTokens($str) { return preg_replace(Regex::$patt->c_token, '', $str); } - public static function normalizeWhiteSpace ($str) + public static function normalizeWhiteSpace($str) { static $find, $replace; if (! $find) { @@ -129,7 +129,7 @@ public static function normalizeWhiteSpace ($str) return preg_replace($find, $replace, $str); } - public static function splitDelimList ($str, $delim = ',') + public static function splitDelimList($str, $delim = ',') { $do_preg_split = strlen($delim) > 1; $str = trim($str); @@ -158,7 +158,7 @@ public static function splitDelimList ($str, $delim = ',') return array_filter(array_map('trim', $list), 'strlen'); } - public static function getLinkBetweenPaths ($path1, $path2, $directories = true) + public static function getLinkBetweenPaths($path1, $path2, $directories = true) { $path1 = trim(Util::normalizePath($path1, true), '/'); $path2 = trim(Util::normalizePath($path2, true), '/'); @@ -190,7 +190,7 @@ public static function getLinkBetweenPaths ($path1, $path2, $directories = true) return $link; } - public static function filePutContents ($file, $str, $caller = null) + public static function filePutContents($file, $str, $caller = null) { if (@file_put_contents($file, $str, LOCK_EX)) { @@ -204,7 +204,7 @@ public static function filePutContents ($file, $str, $caller = null) /* * Encode integer to Base64 VLQ. */ - public static function vlqEncode ($value) + public static function vlqEncode($value) { static $VLQ_BASE_SHIFT, $VLQ_BASE, $VLQ_BASE_MASK, $VLQ_CONTINUATION_BIT, $BASE64_MAP; if (! $VLQ_BASE_SHIFT) { diff --git a/lib/CssCrush/Version.php b/lib/CssCrush/Version.php index 6d48167..9a709c3 100644 --- a/lib/CssCrush/Version.php +++ b/lib/CssCrush/Version.php @@ -13,7 +13,7 @@ class Version public $revision = 0; public $extra; - public function __construct ($version_string) + public function __construct($version_string) { if (($hyphen_pos = strpos($version_string, '-' )) !== false) { $this->extra = substr($version_string, $hyphen_pos + 1); @@ -33,7 +33,7 @@ public function __construct ($version_string) } } - public function __toString () + public function __toString() { $out = (string) $this->major; @@ -50,7 +50,7 @@ public function __toString () return $out; } - public function compare ($version_string) + public function compare($version_string) { $LESS = -1; $MORE = 1; diff --git a/lib/functions.php b/lib/functions.php index 057324a..61b98f3 100644 --- a/lib/functions.php +++ b/lib/functions.php @@ -6,27 +6,27 @@ */ use CssCrush\CssCrush; -function csscrush_file ($file, $options = null) { +function csscrush_file($file, $options = null) { return CssCrush::file($file, $options); } -function csscrush_tag ($file, $options = null, $attributes = array()) { +function csscrush_tag($file, $options = null, $attributes = array()) { return CssCrush::tag($file, $options, $attributes); } -function csscrush_inline ($file, $options = null, $attributes = array()) { +function csscrush_inline($file, $options = null, $attributes = array()) { return CssCrush::inline($file, $options, $attributes); } -function csscrush_string ($string, $options = null) { +function csscrush_string($string, $options = null) { return CssCrush::string($string, $options); } -function csscrush_stat () { +function csscrush_stat() { return CssCrush::stat(); } -function csscrush_version () { +function csscrush_version() { return CssCrush::$config->version; } @@ -36,7 +36,7 @@ function csscrush_version () { * @param string $object_name Name of object you want to modify: 'config' or 'options'. * @param mixed $modifier Assoc array of keys and values to set, or callable which is passed the object. */ -function csscrush_set ($object_name, $modifier) { +function csscrush_set($object_name, $modifier) { if (in_array($object_name, array('options', 'config'))) { @@ -60,7 +60,7 @@ function csscrush_set ($object_name, $modifier) { * @param string $object_name Name of object you want to modify: 'config' or 'options'. * @param mixed $property The property name to retrieve. */ -function csscrush_get ($object_name, $property = null) { +function csscrush_get($object_name, $property = null) { if (in_array($object_name, array('options', 'config'))) { diff --git a/misc/formatters.php b/misc/formatters.php index c93fc94..c20929d 100644 --- a/misc/formatters.php +++ b/misc/formatters.php @@ -12,7 +12,7 @@ 'block' => 'CssCrush\fmtr_block', ); -function fmtr_single ($rule) { +function fmtr_single($rule) { $EOL = CssCrush::$process->newline; @@ -21,7 +21,7 @@ function fmtr_single ($rule) { return "$selectors { $block; }$EOL"; } -function fmtr_padded ($rule, $padding = 40) { +function fmtr_padded($rule, $padding = 40) { $EOL = CssCrush::$process->newline; @@ -38,7 +38,7 @@ function fmtr_padded ($rule, $padding = 40) { } } -function fmtr_block ($rule, $indent = ' ') { +function fmtr_block($rule, $indent = ' ') { $EOL = CssCrush::$process->newline; diff --git a/plugins/aria.php b/plugins/aria.php index e81ba22..04d17f2 100644 --- a/plugins/aria.php +++ b/plugins/aria.php @@ -35,7 +35,7 @@ )); -function aria () { +function aria() { static $aria, $optional_value; if (! $aria) { diff --git a/plugins/canvas.php b/plugins/canvas.php index c267b27..354e843 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -65,7 +65,7 @@ )); -function canvas_capture ($process) { +function canvas_capture($process) { // Extract definitions. $process->stream->pregReplaceCallback( @@ -80,7 +80,7 @@ function ($m) { }); } -function canvas_generator ($input, $context) { +function canvas_generator($input, $context) { $process = CssCrush::$process; $logger = CssCrush::$config->logger; @@ -263,7 +263,7 @@ function canvas_generator ($input, $context) { } -function canvas_fn_linear_gradient ($input, $context) { +function canvas_fn_linear_gradient($input, $context) { $args = Functions::parseArgs($input) + array( 'white', 'black', @@ -308,7 +308,7 @@ function canvas_fn_linear_gradient ($input, $context) { $context->canvas->fills[$context->currentProperty] = $fill; } -function canvas_fn_filter ($input, $context) { +function canvas_fn_filter($input, $context) { $args = Functions::parseArgs($input); @@ -316,7 +316,7 @@ function canvas_fn_filter ($input, $context) { } -function canvas_apply_filters ($canvas, $src) { +function canvas_apply_filters($canvas, $src) { foreach ($canvas->filters as $filter) { list($name, $args) = $filter; @@ -381,7 +381,7 @@ function canvas_apply_filters ($canvas, $src) { } } -function canvas_apply_css_funcs ($canvas) { +function canvas_apply_css_funcs($canvas) { // Setup functions for using on values. static $map; @@ -446,7 +446,7 @@ function canvas_apply_css_funcs ($canvas) { } } -function canvas_preprocess ($canvas) { +function canvas_preprocess($canvas) { if (isset($canvas->raw['margin'])) { @@ -485,7 +485,7 @@ function canvas_preprocess ($canvas) { $canvas->height = $canvas->raw['height']; } -function canvas_fetch_src ($url_token) { +function canvas_fetch_src($url_token) { if ($url_token && $url = CssCrush::$process->tokens->get($url_token)) { @@ -531,7 +531,7 @@ function canvas_fetch_src ($url_token) { Adapted from GD Gradient Fill by Ozh (http://planetozh.com): http://planetozh.com/blog/my-projects/images-php-gd-gradient-fill */ -function canvas_gradient ($canvas, $fill) { +function canvas_gradient($canvas, $fill) { $image = $canvas->image; @@ -586,7 +586,7 @@ function canvas_gradient ($canvas, $fill) { } } -function canvas_create ($canvas) { +function canvas_create($canvas) { $margin = $canvas->margin; $width = $canvas->width + $margin->right + $margin->left; @@ -596,7 +596,7 @@ function canvas_create ($canvas) { $canvas->image = canvas_create_transparent($width, $height); } -function canvas_create_transparent ($width, $height) { +function canvas_create_transparent($width, $height) { $image = imagecreatetruecolor($width, $height); @@ -608,7 +608,7 @@ function canvas_create_transparent ($width, $height) { return $image; } -function canvas_fade ($src, $opacity) { +function canvas_fade($src, $opacity) { $width = imagesx($src->image); $height = imagesy($src->image); @@ -629,7 +629,7 @@ function canvas_fade ($src, $opacity) { } -function canvas_fill ($canvas, $property) { +function canvas_fill($canvas, $property) { if (! isset($canvas->fills[$property])) { return false; @@ -661,7 +661,7 @@ function canvas_fill ($canvas, $property) { } } -function canvas_set_fill_dims ($fill, $canvas) { +function canvas_set_fill_dims($fill, $canvas) { // Resolve fill dimensions and coordinates. $margin = $canvas->margin; @@ -679,7 +679,7 @@ function canvas_set_fill_dims ($fill, $canvas) { } } -function canvas_requirements () { +function canvas_requirements() { $requirements_met = true; @@ -708,7 +708,7 @@ class Canvas { public $image, $fills = array(), $filters = array(); - public function __destruct () + public function __destruct() { if (isset($this->image)) { imagedestroy($this->image); @@ -719,11 +719,11 @@ public function __destruct () /* Helpers. */ -function canvas_opacity ($float) { +function canvas_opacity($float) { return 127 - max(min(round($float * 127), 127), 0); } -function canvas_parselist ($str, $numbers = true) { +function canvas_parselist($str, $numbers = true) { $list = preg_split('~ +~', trim($str)); return $numbers ? array_map('floatval', $list) : $list; } diff --git a/plugins/ease.php b/plugins/ease.php index 3529760..5c15c73 100644 --- a/plugins/ease.php +++ b/plugins/ease.php @@ -23,7 +23,7 @@ )); -function ease (Rule $rule) { +function ease(Rule $rule) { static $find, $replace, $easing_properties; if (! $find) { diff --git a/plugins/forms.php b/plugins/forms.php index 363c923..8a89921 100644 --- a/plugins/forms.php +++ b/plugins/forms.php @@ -30,7 +30,7 @@ )); -function forms () { +function forms() { return array( 'input' => function ($args) { $types = array(); diff --git a/plugins/hsl-to-hex.php b/plugins/hsl-to-hex.php index b10c1d4..ddfff71 100644 --- a/plugins/hsl-to-hex.php +++ b/plugins/hsl-to-hex.php @@ -20,7 +20,7 @@ )); -function hsl_to_hex (Rule $rule) { +function hsl_to_hex(Rule $rule) { $hsl_patt = Regex::make('~{{LB}}hsl({{p-token}})~i'); diff --git a/plugins/ie-inline-block.php b/plugins/ie-inline-block.php index 8963600..19206f4 100644 --- a/plugins/ie-inline-block.php +++ b/plugins/ie-inline-block.php @@ -22,7 +22,7 @@ )); -function ie_inline_block (Rule $rule) { +function ie_inline_block(Rule $rule) { if ($rule->propertyCount('display') < 1) { return; diff --git a/plugins/ie-opacity.php b/plugins/ie-opacity.php index e26bffc..64e10d2 100755 --- a/plugins/ie-opacity.php +++ b/plugins/ie-opacity.php @@ -23,7 +23,7 @@ )); -function ie_opacity (Rule $rule) { +function ie_opacity(Rule $rule) { if ($rule->propertyCount('opacity') < 1) { return; diff --git a/plugins/initial.php b/plugins/initial.php index 5cc4d00..aab7b3a 100644 --- a/plugins/initial.php +++ b/plugins/initial.php @@ -26,7 +26,7 @@ }, )); -function initial (Rule $rule) { +function initial(Rule $rule) { static $initial_values; if (! $initial_values) { diff --git a/plugins/legacy-flexbox.php b/plugins/legacy-flexbox.php index 14d09bf..c7d5894 100644 --- a/plugins/legacy-flexbox.php +++ b/plugins/legacy-flexbox.php @@ -55,7 +55,7 @@ )); -function legacy_flexbox (Rule $rule) { +function legacy_flexbox(Rule $rule) { static $flex_related_props = array( 'align-items' => true, @@ -184,7 +184,7 @@ function legacy_flexbox (Rule $rule) { } -function flex_direction ($value, &$stack) { +function flex_direction($value, &$stack) { // flex-direction: row | row-reverse | column | column-reverse // box-orient: horizontal | vertical | inline-axis | block-axis | inherit @@ -225,7 +225,7 @@ function flex_direction ($value, &$stack) { } -function flex_justify_content ($value, &$stack) { +function flex_justify_content($value, &$stack) { // justify-content: flex-start | flex-end | center | space-between | space-around // box-pack: start | end | center | justify @@ -251,7 +251,7 @@ function flex_justify_content ($value, &$stack) { } -function flex_align_items ($value, &$stack) { +function flex_align_items($value, &$stack) { // align-items: flex-start | flex-end | center | baseline | stretch // box-align: start | end | center | baseline | stretch @@ -277,7 +277,7 @@ function flex_align_items ($value, &$stack) { } -function flex_order ($value, &$stack) { +function flex_order($value, &$stack) { // order: // box-ordinal-group: @@ -303,7 +303,7 @@ function flex_order ($value, &$stack) { } -function flex_wrap ($value, &$stack) { +function flex_wrap($value, &$stack) { // flex-wrap: nowrap | wrap | wrap-reverse // box-lines: single | multiple @@ -328,7 +328,7 @@ function flex_wrap ($value, &$stack) { } -function flex ($value, &$stack) { +function flex($value, &$stack) { // flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ] // box-flex: @@ -363,7 +363,7 @@ function flex ($value, &$stack) { } -function flex_grow ($value, &$stack) { +function flex_grow($value, &$stack) { // flex-grow: // box-flex: diff --git a/plugins/loop.php b/plugins/loop.php index 36587de..9f14873 100644 --- a/plugins/loop.php +++ b/plugins/loop.php @@ -66,7 +66,7 @@ Regex::make('~(? @for \s+ (?{{ident}}) \s+ in \s+ (?[^{]+) ) \s* {{block}}~xiS')); -function loop () { +function loop() { CssCrush::$process->stream->pregReplaceCallback(LOOP_PATT, function ($m) { @@ -74,7 +74,7 @@ function loop () { }); } -function loop_unroll ($str, $context = array()) { +function loop_unroll($str, $context = array()) { $str = loop_apply_scope($str, $context); @@ -98,7 +98,7 @@ function loop_unroll ($str, $context = array()) { return $str; } -function loop_resolve_list ($list_text) { +function loop_resolve_list($list_text) { // Resolve the list of items for iteration. // Either a generator function or a plain list. @@ -128,7 +128,7 @@ function loop_resolve_list ($list_text) { return $items; } -function loop_apply_scope ($str, $context) { +function loop_apply_scope($str, $context) { // Need to temporarily hide child block scopes. $child_scopes = array(); @@ -153,7 +153,7 @@ function loop_apply_scope ($str, $context) { return str_replace(array_keys($child_scopes), array_values($child_scopes), $str); } -function loop_color_range () { +function loop_color_range() { $args = func_get_args(); diff --git a/plugins/noise.php b/plugins/noise.php index 25363f5..8ca918c 100644 --- a/plugins/noise.php +++ b/plugins/noise.php @@ -102,7 +102,7 @@ )); -function noise_generator ($input, $defaults) { +function noise_generator($input, $defaults) { $args = array_pad(Functions::parseArgs($input), 4, 'default'); diff --git a/plugins/property-sorter.php b/plugins/property-sorter.php index 9e49ebd..4cc81fc 100644 --- a/plugins/property-sorter.php +++ b/plugins/property-sorter.php @@ -34,7 +34,7 @@ )); - function property_sorter (Rule $rule) { + function property_sorter(Rule $rule) { $new_set = array(); @@ -52,7 +52,7 @@ function property_sorter (Rule $rule) { /* Callback for sorting. */ - function property_sorter_callback ($a, $b) { + function property_sorter_callback($a, $b) { $map =& property_sorter_get_table(); $a_prop =& $a->canonicalProperty; @@ -177,7 +177,7 @@ function &property_sorter_get_table () { /* Get the current sorting table. */ - function csscrush_get_property_sort_order () { + function csscrush_get_property_sort_order() { CssCrush\property_sorter_get_table(); return $GLOBALS['CSSCRUSH_PROPERTY_SORT_ORDER']; } @@ -186,7 +186,7 @@ function csscrush_get_property_sort_order () { /* Set a custom sorting table. */ - function csscrush_set_property_sort_order (array $new_order) { + function csscrush_set_property_sort_order(array $new_order) { unset($GLOBALS['CSSCRUSH_PROPERTY_SORT_ORDER_CACHE']); $GLOBALS['CSSCRUSH_PROPERTY_SORT_ORDER'] = $new_order; } diff --git a/plugins/px2em.php b/plugins/px2em.php index 26d69c7..f60306c 100644 --- a/plugins/px2em.php +++ b/plugins/px2em.php @@ -27,7 +27,7 @@ )); -function fn__px2em ($input) { +function fn__px2em($input) { $base = 16; @@ -39,7 +39,7 @@ function fn__px2em ($input) { return px2em($input, 'em', $base); } -function fn__px2rem ($input) { +function fn__px2rem($input) { $base = 16; @@ -51,7 +51,7 @@ function fn__px2rem ($input) { return px2em($input, 'rem', $base); } -function px2em ($input, $unit, $default_base) { +function px2em($input, $unit, $default_base) { list($px, $base) = Functions::parseArgsSimple($input) + array( 16, diff --git a/plugins/rem.php b/plugins/rem.php index ff67eb6..55217e4 100644 --- a/plugins/rem.php +++ b/plugins/rem.php @@ -45,7 +45,7 @@ )); -function rem (Rule $rule) { +function rem(Rule $rule) { static $rem_patt, $px_patt, $font_props, $modes; if (! $modes) { diff --git a/plugins/svg-gradients.php b/plugins/svg-gradients.php index 2e02859..157ccac 100644 --- a/plugins/svg-gradients.php +++ b/plugins/svg-gradients.php @@ -53,7 +53,7 @@ )); -function fn__svg_linear_gradient ($input) { +function fn__svg_linear_gradient($input) { $gradient = create_svg_linear_gradient($input); $gradient_markup = reset($gradient); @@ -74,7 +74,7 @@ function fn__svg_linear_gradient ($input) { } -function fn__svg_radial_gradient ($input) { +function fn__svg_radial_gradient($input) { $gradient = create_svg_radial_gradient($input); $gradient_markup = reset($gradient); @@ -95,7 +95,7 @@ function fn__svg_radial_gradient ($input) { } -function create_svg_linear_gradient ($input) { +function create_svg_linear_gradient($input) { static $angle_keywords, $deg_patt; if (! $angle_keywords) { @@ -225,7 +225,7 @@ function create_svg_linear_gradient ($input) { } -function create_svg_radial_gradient ($input) { +function create_svg_radial_gradient($input) { static $position_keywords, $origin_patt; if (! $position_keywords) { @@ -292,7 +292,7 @@ function create_svg_radial_gradient ($input) { } -function parse_gradient_color_stops (array $color_stop_args) { +function parse_gradient_color_stops(array $color_stop_args) { $offsets = array(); $colors = array(); diff --git a/plugins/svg.php b/plugins/svg.php index e76e8af..14e21d5 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -73,17 +73,17 @@ )); -function fn__svg ($input) { +function fn__svg($input) { return svg_generator($input, 'svg'); } -function fn__svg_data ($input) { +function fn__svg_data($input) { return svg_generator($input, 'svg-data'); } -function svg_capture ($process) { +function svg_capture($process) { // Extract svg definitions. $process->stream->pregReplaceCallback( @@ -98,7 +98,7 @@ function ($m) { }); } -function svg_generator ($input, $fn_name) { +function svg_generator($input, $fn_name) { $process = CssCrush::$process; @@ -307,7 +307,7 @@ function svg_generator ($input, $fn_name) { /* Circle callback. */ -function svg_circle ($element) { +function svg_circle($element) { // Ensure required attributes have defaults set. $element->data += array( @@ -331,7 +331,7 @@ function svg_circle ($element) { /* Rect callback. */ -function svg_rect ($element) { +function svg_rect($element) { $element->data += array( 'width' => 50, @@ -358,7 +358,7 @@ function svg_rect ($element) { /* Ellipse callback. */ -function svg_ellipse ($element) { +function svg_ellipse($element) { $element->data += array( 'diameter' => '100 50', @@ -382,7 +382,7 @@ function svg_ellipse ($element) { /* Path callback. */ -function svg_path ($element) { +function svg_path($element) { // Ensure minimum required attributes have defaults set. $element->data += array( @@ -398,7 +398,7 @@ function svg_path ($element) { /* Polyline callback. */ -function svg_polyline ($element) { +function svg_polyline($element) { // Ensure required attributes have defaults set. $element->data += array( @@ -414,7 +414,7 @@ function svg_polyline ($element) { /* Line callback. */ -function svg_line ($element) { +function svg_line($element) { // Set a default stroke. $element->styles += array( @@ -432,7 +432,7 @@ function svg_line ($element) { /* Polygon callback. */ -function svg_polygon ($element) { +function svg_polygon($element) { if (! isset($element->attrs['points'])) { @@ -464,7 +464,7 @@ function svg_polygon ($element) { /* Star callback. */ -function svg_star ($element) { +function svg_star($element) { // Minimum required attributes have defaults. $element->data += array( @@ -497,7 +497,7 @@ function svg_star ($element) { Text callback. Warning: Very limited for svg-as-image situations. */ -function svg_text ($element) { +function svg_text($element) { // Minimum required attributes have defaults. $element->data += array( @@ -531,7 +531,7 @@ function svg_text ($element) { Adapted from http://svg-whiz.com/svg/StarMaker.svg by Doug Schepers. */ -function svg_starpath ($cx, $cy, $points, $outer_r, $inner_r = null, $twist = 0, $orient = 'point') { +function svg_starpath($cx, $cy, $points, $outer_r, $inner_r = null, $twist = 0, $orient = 'point') { $d = array(); @@ -578,7 +578,7 @@ function svg_starpath ($cx, $cy, $points, $outer_r, $inner_r = null, $twist = 0, return 'M' . implode('L', $d) . 'Z'; } -function svg_apply_filters ($element) { +function svg_apply_filters($element) { if (isset($element->data['drop-shadow'])) { @@ -618,7 +618,7 @@ function svg_apply_filters ($element) { } } -function svg_preprocess ($element) { +function svg_preprocess($element) { if (isset($element->data['margin'])) { @@ -665,7 +665,7 @@ function svg_preprocess ($element) { } } -function svg_apply_css_funcs ($element, &$raw_data) { +function svg_apply_css_funcs($element, &$raw_data) { // Setup functions for using on values. // Note using custom versions of svg-*-gradient(). @@ -706,7 +706,7 @@ function svg_apply_css_funcs ($element, &$raw_data) { } } -function svg_compress ($element) { +function svg_compress($element) { foreach ($element->attrs as $key => &$value) { @@ -720,7 +720,7 @@ function ($m) { return round($m[0], 2); }, } } -function svg_render ($element) { +function svg_render($element) { // Flatten styles. $styles = ''; @@ -795,7 +795,7 @@ function svg_render ($element) { /* Custom versions of svg-*-gradient() for integrating. */ -function svg_fn_linear_gradient ($input, $element) { +function svg_fn_linear_gradient($input, $element) { // Relies on functions from svg-gradients plugin. Plugin::load('svg-gradients'); @@ -806,7 +806,7 @@ function svg_fn_linear_gradient ($input, $element) { return 'url(#' . key($generated_gradient) . ')'; } -function svg_fn_radial_gradient ($input, $element) { +function svg_fn_radial_gradient($input, $element) { // Relies on functions from svg-gradients plugin. Plugin::load('svg-gradients'); @@ -817,7 +817,7 @@ function svg_fn_radial_gradient ($input, $element) { return 'url(#' . key($generated_gradient) . ')'; } -function svg_fn_pattern ($input, $element) { +function svg_fn_pattern($input, $element) { static $uid = 0; $pid = 'p' . (++$uid); @@ -858,12 +858,12 @@ function svg_fn_pattern ($input, $element) { /* Helpers. */ -function svg_parselist ($str, $numbers = true) { +function svg_parselist($str, $numbers = true) { $list = preg_split('~ +~', trim($str)); return $numbers ? array_map('floatval', $list) : $list; } -function svg_ifset (&$var, $fallback = null) { +function svg_ifset(&$var, $fallback = null) { if (isset($var)) { return $var; } From 2b037972abf7306e72b911dfd32376cbe317a5d3 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sun, 24 Nov 2013 22:12:03 +0000 Subject: [PATCH 191/421] Some refactoring for phpunit. Added a hook reset to fix a bug when processing files with different options. --- lib/CssCrush/CssCrush.php | 6 ++--- lib/CssCrush/Declaration.php | 1 + lib/CssCrush/Hook.php | 9 +++++-- lib/CssCrush/Process.php | 38 +++++++++++++++++---------- lib/CssCrush/Rule.php | 1 - lib/CssCrush/Template.php | 51 ++++++++++++++++-------------------- lib/CssCrush/Util.php | 2 +- 7 files changed, 59 insertions(+), 49 deletions(-) diff --git a/lib/CssCrush/CssCrush.php b/lib/CssCrush/CssCrush.php index cbcb6fd..34046d9 100644 --- a/lib/CssCrush/CssCrush.php +++ b/lib/CssCrush/CssCrush.php @@ -8,7 +8,7 @@ class CssCrush { - const VERSION = '2.0.0'; + const VERSION = '2.0.1'; // Global settings. public static $config; @@ -213,7 +213,7 @@ public static function parseAliasesFile($file) */ public static function file($file, $options = null) { - self::$process = new Process($options); + self::$process = new Process($options, array('io_context' => 'file')); $config = self::$config; $process = self::$process; @@ -338,7 +338,7 @@ public static function string($string, $options = null) $options['boilerplate'] = false; } - self::$process = new Process($options); + self::$process = new Process($options, array('io_context' => 'filter')); $config = self::$config; $process = self::$process; diff --git a/lib/CssCrush/Declaration.php b/lib/CssCrush/Declaration.php index 622b2c2..a1f9be9 100644 --- a/lib/CssCrush/Declaration.php +++ b/lib/CssCrush/Declaration.php @@ -16,6 +16,7 @@ class Declaration public $index; public $skip; public $important; + public $inValid = false; public function __construct($prop, $value, $contextIndex = 0) { diff --git a/lib/CssCrush/Hook.php b/lib/CssCrush/Hook.php index 6ec5b13..84514c8 100644 --- a/lib/CssCrush/Hook.php +++ b/lib/CssCrush/Hook.php @@ -1,8 +1,8 @@ 'filter' + ); + // Create options instance for this process. - $this->options = new Options($options); + $this->options = new Options($user_options); // Populate option defaults. $this->options->merge($config->options); @@ -48,10 +52,18 @@ public function __construct($options) $this->errors = array(); $this->stat = array(); + // Copy config values. $this->plugins = $config->plugins; $this->aliases = $config->aliases; $this->docRoot = isset($this->options->doc_root) ? $this->options->doc_root : $config->docRoot; + $this->ioContext = $dev_options['io_context']; + + // Shortcut commonly used options to avoid overhead with __get calls. + $this->minifyOutput = $this->options->minify; + $this->addTracingStubs = in_array('stubs', $this->options->trace); + $this->generateMap = $this->ioContext === 'file' && $this->options->source_map; + $this->ruleFormatter = $this->options->formatter; // Shortcut the newline option and attach it to the process. switch ($this->options->newlines) { @@ -89,13 +101,11 @@ public function release() public function resolveContext($input_dir, $input_file = null) { if ($input_file) { - $this->ioContext = 'file'; $this->input->path = $input_file; $this->input->filename = basename($input_file); $this->input->mtime = filemtime($input_file); } else { - $this->ioContext = 'filter'; $this->input->path = null; $this->input->filename = null; } @@ -922,11 +932,8 @@ protected function collate() } } - public function compile() + public function prepare() { - // Always store start time. - $this->stat['compile_start_time'] = microtime(true); - // Ensure relevant ini settings aren't too conservative. if (ini_get('pcre.backtrack_limit') < 1000000) { ini_set('pcre.backtrack_limit', 1000000); @@ -935,16 +942,19 @@ public function compile() ini_set('memory_limit', '128M'); } - // Shortcut commonly used options during compilation to avoid overhead with __get calls. - $this->minifyOutput = $this->options->minify; - $this->addTracingStubs = in_array('stubs', $this->options->trace); - $this->generateMap = $this->ioContext === 'file' && $this->options->source_map; - $this->ruleFormatter = $this->options->formatter; - + Hook::reset(); $this->filterPlugins(); $this->filterAliases(); Functions::setMatchPatt(); + } + + public function compile() + { + // Always store start time. + $this->stat['compile_start_time'] = microtime(true); + + $this->prepare(); // Collate hostfile and imports. $this->stream = new Stream(Importer::hostfile($this->input)); diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php index 6929109..0296c5c 100644 --- a/lib/CssCrush/Rule.php +++ b/lib/CssCrush/Rule.php @@ -40,7 +40,6 @@ public function __construct($selector_string, $declarations_string, $trace_token if (! empty(Hook::$register['rule_preprocess'])) { // Juggling to maintain the old API. - // TODO: rework this for 2.x? $rule = new \stdClass(); $rule->selector_raw = $selector_string; $rule->declaration_raw = $declarations_string; diff --git a/lib/CssCrush/Template.php b/lib/CssCrush/Template.php index 1ae8a1d..3a4f80a 100644 --- a/lib/CssCrush/Template.php +++ b/lib/CssCrush/Template.php @@ -31,37 +31,37 @@ public function __construct($str, $options = array()) // Parse all arg function calls in the passed string, // callback creates default values. - $this->string = Functions::executeOnString($str, $arg_patt, array( - 'arg' => array($this, 'capture'), - '#' => array($this, 'capture'), - )); - } + $capture_callback = function ($str) + { + $args = Functions::parseArgsSimple($str); - public function capture($str) - { - $args = Functions::parseArgsSimple($str); + $position = array_shift($args); - $position = array_shift($args); + // Match the argument index integer. + if (! isset($position) || ! ctype_digit($position)) { - // Match the argument index integer. - if (! isset($position) || ! ctype_digit($position)) { + // On failure to match an integer return empty string. + return ''; + } - // On failure to match an integer return empty string. - return ''; - } + // Store the default value. + $default_value = isset($args[0]) ? $args[0] : null; - // Store the default value. - $default_value = isset($args[0]) ? $args[0] : null; + if (isset($default_value)) { + $this->defaults[$position] = $default_value; + } - if (isset($default_value)) { - $this->defaults[$position] = $default_value; - } + // Update the argument count. + $argNumber = ((int) $position) + 1; + $this->argCount = max($this->argCount, $argNumber); - // Update the argument count. - $argNumber = ((int) $position) + 1; - $this->argCount = max($this->argCount, $argNumber); + return "?a$position?"; + }; - return "?a$position?"; + $this->string = Functions::executeOnString($str, $arg_patt, array( + 'arg' => $capture_callback, + '#' => $capture_callback, + )); } public function getArgValue($index, &$args) @@ -112,11 +112,6 @@ public function prepare(array $args, $persist = true) return $substitutions; } - public function reset() - { - unset($this->substitutions); - } - public function apply(array $args = null, $str = null) { $str = isset($str) ? $str : $this->string; diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php index bebb77c..56b9b18 100644 --- a/lib/CssCrush/Util.php +++ b/lib/CssCrush/Util.php @@ -190,7 +190,7 @@ public static function getLinkBetweenPaths($path1, $path2, $directories = true) return $link; } - public static function filePutContents($file, $str, $caller = null) + public static function filePutContents($file, $str) { if (@file_put_contents($file, $str, LOCK_EX)) { From 455cf9743f710272017faa91be602b6366224a98 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 25 Nov 2013 13:40:29 +0000 Subject: [PATCH 192/421] Simplified the version string reported by the command line utility. --- cli.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli.php b/cli.php index 54722bf..35e3a42 100755 --- a/cli.php +++ b/cli.php @@ -130,7 +130,7 @@ if ($args->version) { - stdout('CSS-Crush ' . csscrush_version()); + stdout('v' . csscrush_version()); exit(STATUS_OK); } From aaf23a5f70f5cc7063e2f2b2475fd9653852968f Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 25 Nov 2013 15:37:46 +0000 Subject: [PATCH 193/421] Making `csscrush_string()` return a true string instead of Stream object (with a __toString method). --- lib/CssCrush/CssCrush.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/CssCrush/CssCrush.php b/lib/CssCrush/CssCrush.php index 34046d9..4df5692 100644 --- a/lib/CssCrush/CssCrush.php +++ b/lib/CssCrush/CssCrush.php @@ -361,7 +361,7 @@ public static function string($string, $options = null) $process->input->importIgnore = true; } - return $process->compile(); + return $process->compile()->__toString(); } /** From b28cdb5592a0c77d514c2b0f20713d6cac53ac90 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 25 Nov 2013 17:14:15 +0000 Subject: [PATCH 194/421] Fixed `Url::getAbsolutePath()` not using the current process doc_root. --- lib/CssCrush/Url.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/CssCrush/Url.php b/lib/CssCrush/Url.php index 76ef080..7b20621 100644 --- a/lib/CssCrush/Url.php +++ b/lib/CssCrush/Url.php @@ -109,7 +109,7 @@ public function getAbsolutePath() $path = $this->value; } elseif ($this->isRelative || $this->isRooted) { - $path = CssCrush::$config->docRoot . + $path = CssCrush::$process->docRoot . ($this->isRelative ? $this->toRoot()->simplify()->value : $this->value); } return $path; From f5a9e83e166184c05be5cb9e0ea31367678bba71 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 29 Nov 2013 19:21:40 +0000 Subject: [PATCH 195/421] Added parsing for `git describe --long` output, and method to get this information if available. Added tests dev dependencies to composer.json. --- composer.json | 4 +++ lib/CssCrush/CssCrush.php | 2 +- lib/CssCrush/Version.php | 54 +++++++++++++++++++++++++++------------ lib/functions.php | 5 +++- 4 files changed, 46 insertions(+), 19 deletions(-) diff --git a/composer.json b/composer.json index f712263..ef4ef7e 100644 --- a/composer.json +++ b/composer.json @@ -18,6 +18,10 @@ "require": { "php": ">=5.3.1" }, + "require-dev": { + "phpunit/phpunit": "3.7.*@dev", + "twig/twig": "1.*" + }, "bin": [ "misc/csscrush" ], diff --git a/lib/CssCrush/CssCrush.php b/lib/CssCrush/CssCrush.php index 4df5692..20310cb 100644 --- a/lib/CssCrush/CssCrush.php +++ b/lib/CssCrush/CssCrush.php @@ -8,7 +8,7 @@ class CssCrush { - const VERSION = '2.0.1'; + const VERSION = '2.0.1-beta'; // Global settings. public static $config; diff --git a/lib/CssCrush/Version.php b/lib/CssCrush/Version.php index 9a709c3..9955e7b 100644 --- a/lib/CssCrush/Version.php +++ b/lib/CssCrush/Version.php @@ -8,28 +8,30 @@ class Version { - public $major = 0; - public $minor = 0; - public $revision = 0; + public $major; + public $minor; + public $revision; public $extra; public function __construct($version_string) { - if (($hyphen_pos = strpos($version_string, '-' )) !== false) { - $this->extra = substr($version_string, $hyphen_pos + 1); - $version_string = substr($version_string, 0, $hyphen_pos); - } - - $parts = explode('.', $version_string); + // Ideally expecting `git describe --long` (e.g. v2.0.0-5-gb28cdb5) + // but also accepting simpler formats. + preg_match('~^ + v? + (?\d+) + (?:\.(?\d+))? + (?:\.(?\d+))? + (?:-(?.+))? + $~ix', + $version_string, + $version); - if (! is_null($major = array_shift($parts))) { - $this->major = (int) $major; - } - if (! is_null($minor = array_shift($parts))) { - $this->minor = (int) $minor; - } - if (! is_null($revision = array_shift($parts))) { - $this->revision = (int) $revision; + if ($version) { + $this->major = (int) $version['major']; + $this->minor = isset($version['minor']) ? (int) $version['minor'] : 0; + $this->revision = isset($version['patch']) ? (int) $version['patch'] : 0; + $this->extra = isset($version['extra']) ? $version['extra'] : null; } } @@ -72,4 +74,22 @@ public function compare($version_string) return $EQUAL; } + + public static function gitDescribe() + { + static $attempted, $version; + if (! $attempted && file_exists(CssCrush::$dir . '/.git')) { + $attempted = true; + $command = 'cd ' . escapeshellarg(CssCrush::$dir) . ' && git describe --tag --long'; + @exec($command, $lines); + if ($lines) { + $version = new Version(trim($lines[0])); + if (is_null($version->major)) { + $version = null; + } + } + } + + return $version; + } } diff --git a/lib/functions.php b/lib/functions.php index 61b98f3..9d7d6e9 100644 --- a/lib/functions.php +++ b/lib/functions.php @@ -26,7 +26,10 @@ function csscrush_stat() { return CssCrush::stat(); } -function csscrush_version() { +function csscrush_version($use_git = false) { + if ($use_git && $version = \CssCrush\Version::gitDescribe()) { + return $version; + } return CssCrush::$config->version; } From adffbb0c4b9825923d6ac0321b0670f0a21313bc Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 5 Dec 2013 09:39:12 +0000 Subject: [PATCH 196/421] Some refactoring of `CssCrush::applySelectorAliases` to fix issue with nested aliases. Added logger shortcut functions. --- lib/CssCrush/CssCrush.php | 18 ++++++++++++++++++ lib/CssCrush/Process.php | 26 +++++++++----------------- lib/CssCrush/Rule.php | 2 -- lib/functions.php | 6 ++---- 4 files changed, 29 insertions(+), 23 deletions(-) diff --git a/lib/CssCrush/CssCrush.php b/lib/CssCrush/CssCrush.php index 20310cb..6497f39 100644 --- a/lib/CssCrush/CssCrush.php +++ b/lib/CssCrush/CssCrush.php @@ -456,4 +456,22 @@ public static function runStat() } } + +function log($message, $context = array(), $type = 'debug') { + CssCrush::$config->logger->{$type}($message, $context); +} + +function debug($message, $context = array()) { + CssCrush::$config->logger->debug($message, $context); +} + +function notice($message, $context = array()) { + CssCrush::$config->logger->notice($message, $context); +} + +function warning($message, $context = array()) { + CssCrush::$config->logger->warning($message, $context); +} + + CssCrush::init(); diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 9605d2b..dad9a6f 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -239,18 +239,11 @@ public static function applySelectorAliases(&$str) $table =& $process->selectorAliases; - // Find all selector-alias matches. - $selector_alias_calls = Regex::matchAll($process->selectorAliasesPatt, $str); - - // Step through the matches from last to first. - while ($selector_alias_call = array_pop($selector_alias_calls)) { + while (preg_match_all($process->selectorAliasesPatt, $str, $m, PREG_OFFSET_CAPTURE | PREG_SET_ORDER)) { + $selector_alias_call = array_pop($m); $selector_alias_name = strtolower($selector_alias_call[1][0]); - if (! isset($table[$selector_alias_name])) { - continue; - } - $start = $selector_alias_call[0][1]; $length = strlen($selector_alias_call[0][0]); $args = array(); @@ -259,15 +252,14 @@ public static function applySelectorAliases(&$str) if (isset($selector_alias_call[2])) { // Parse argument list. - if (! preg_match(Regex::$patt->parens, $str, $parens, PREG_OFFSET_CAPTURE, $start)) { - continue; - } - $args = Functions::parseArgs($parens[2][0]); + if (preg_match(Regex::$patt->parens, $str, $parens, PREG_OFFSET_CAPTURE, $start)) { + $args = Functions::parseArgs($parens[2][0]); - // Amend offsets. - $paren_start = $parens[0][1]; - $paren_len = strlen($parens[0][0]); - $length = ($paren_start + $paren_len) - $start; + // Amend offsets. + $paren_start = $parens[0][1]; + $paren_len = strlen($parens[0][0]); + $length = ($paren_start + $paren_len) - $start; + } } // Resolve the selector alias value to a template instance if a callable is given. diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php index 0296c5c..987b819 100644 --- a/lib/CssCrush/Rule.php +++ b/lib/CssCrush/Rule.php @@ -393,7 +393,6 @@ public function expandSelectors() foreach ($this->selectors as $readableValue => $selector) { $pos = stripos($selector->value, ':any?'); - if ($pos !== false) { // Contains an :any statement so expand. @@ -416,7 +415,6 @@ public function expandSelectors() $tmp = array(); foreach ($chain as $rowCopy) { foreach ($parts as $part) { - // Flatten nested :any() expressions in a hacky kind of way. if ($has_nesting) { $part = str_ireplace(':any(', '', $part); diff --git a/lib/functions.php b/lib/functions.php index 9d7d6e9..890d823 100644 --- a/lib/functions.php +++ b/lib/functions.php @@ -43,8 +43,7 @@ function csscrush_set($object_name, $modifier) { if (in_array($object_name, array('options', 'config'))) { - $pointer = $object_name === 'options' ? - CssCrush::$config->options : CssCrush::$config; + $pointer = $object_name === 'options' ? CssCrush::$config->options : CssCrush::$config; if (is_callable($modifier)) { call_user_func($modifier, $pointer); @@ -67,8 +66,7 @@ function csscrush_get($object_name, $property = null) { if (in_array($object_name, array('options', 'config'))) { - $pointer = $object_name === 'options' ? - CssCrush::$config->options : CssCrush::$config; + $pointer = $object_name === 'options' ? CssCrush::$config->options : CssCrush::$config; if (! isset($property)) { From f5b4deac3c68ad0a931cc2c8177dcaf86b1b1bf7 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 5 Dec 2013 09:49:36 +0000 Subject: [PATCH 197/421] Applying the shortcut logging functions. --- lib/CssCrush/BalancedMatch.php | 4 ++-- lib/CssCrush/CssCrush.php | 6 +++--- lib/CssCrush/IO.php | 30 ++++++++++++++---------------- lib/CssCrush/Importer.php | 8 +++----- lib/CssCrush/Options.php | 8 +++----- lib/CssCrush/Plugin.php | 2 +- lib/CssCrush/Process.php | 2 +- lib/CssCrush/Util.php | 2 +- plugins/canvas.php | 8 ++++---- plugins/initial.php | 2 +- plugins/property-sorter.php | 2 +- 11 files changed, 34 insertions(+), 40 deletions(-) diff --git a/lib/CssCrush/BalancedMatch.php b/lib/CssCrush/BalancedMatch.php index a7d457a..f455fba 100644 --- a/lib/CssCrush/BalancedMatch.php +++ b/lib/CssCrush/BalancedMatch.php @@ -24,7 +24,7 @@ public function __construct(Stream $stream, $offset, $brackets = '{}') if (substr_count($stream->raw, $opener) !== substr_count($stream->raw, $closer)) { $sample = substr($stream->raw, $this->offset, 25); - CssCrush::$config->logger->warning("[[CssCrush]] - Unmatched token near '$sample'."); + warning("[[CssCrush]] - Unmatched token near '$sample'."); return; } @@ -40,7 +40,7 @@ public function __construct(Stream $stream, $offset, $brackets = '{}') $this->length = $this->matchEnd - $this->offset; } else { - CssCrush::$config->logger->warning("[[CssCrush]] - Could not match '$opener'. Exiting."); + warning("[[CssCrush]] - Could not match '$opener'. Exiting."); } } diff --git a/lib/CssCrush/CssCrush.php b/lib/CssCrush/CssCrush.php index 6497f39..42ea6e3 100644 --- a/lib/CssCrush/CssCrush.php +++ b/lib/CssCrush/CssCrush.php @@ -108,7 +108,7 @@ static protected function resolveDocRoot($doc_root = null) } if (! $doc_root) { - CssCrush::$config->logger->warning("[[CssCrush]] - Could not get a valid DOCUMENT_ROOT reference."); + warning("[[CssCrush]] - Could not get a valid DOCUMENT_ROOT reference."); } } @@ -134,7 +134,7 @@ public static function parseAliasesFile($file) $tree = @parse_ini_file($file, true); if ($tree === false) { - CssCrush::$config->logger->notice("[[CssCrush]] - Could not parse aliases file '$file'."); + notice("[[CssCrush]] - Could not parse aliases file '$file'."); return false; } @@ -223,7 +223,7 @@ public static function file($file, $options = null) $process->input->raw = $file; if (! ($input_file = Util::resolveUserPath($file))) { - $config->logger->warning('[[CssCrush]] - Input file \'' . basename($file) . '\' not found.'); + warning('[[CssCrush]] - Input file \'' . basename($file) . '\' not found.'); return ''; } diff --git a/lib/CssCrush/IO.php b/lib/CssCrush/IO.php index a66f3b4..1c06d70 100644 --- a/lib/CssCrush/IO.php +++ b/lib/CssCrush/IO.php @@ -25,23 +25,22 @@ public static function getOutputDir() public static function testOutputDir() { $dir = CssCrush::$process->output->dir; - $logger = CssCrush::$config->logger; $pathtest = true; if (! file_exists($dir)) { - $logger->warning("[[CssCrush]] - Output directory '$dir' doesn't exist."); + warning("[[CssCrush]] - Output directory '$dir' doesn't exist."); $pathtest = false; } elseif (! is_writable($dir)) { - $logger->debug('Attempting to change permissions.'); + debug('Attempting to change permissions.'); if (! @chmod($dir, 0755)) { - $logger->warning("[[CssCrush]] - Output directory '$dir' is unwritable."); + warning("[[CssCrush]] - Output directory '$dir' is unwritable."); $pathtest = false; } else { - $logger->debug('Permissions updated.'); + debug('Permissions updated.'); } } @@ -94,7 +93,6 @@ public static function validateCache() { $process = CssCrush::$process; $config = CssCrush::$config; - $logger = $config->logger; $options = $process->options; $input = $process->input; $output = $process->output; @@ -102,13 +100,13 @@ public static function validateCache() $filename = $output->filename; if (! file_exists($output->dir . '/' . $filename)) { - $logger->debug('No file cached.'); + debug('No file cached.'); return false; } if (! isset($process->cacheData[$filename])) { - $logger->debug('Cached file exists but is not registered.'); + debug('Cached file exists but is not registered.'); return false; } @@ -128,7 +126,7 @@ public static function validateCache() } else { // File has been moved, remove old file and skip to compile. - $logger->debug('Recompiling - an import file has been moved.'); + debug('Recompiling - an import file has been moved.'); return false; } @@ -136,7 +134,7 @@ public static function validateCache() $files_changed = $data['datem_sum'] != array_sum($file_sums); if ($files_changed) { - $logger->debug('Files have been modified. Recompiling.'); + debug('Files have been modified. Recompiling.'); } // Compare runtime options and cached options for differences. @@ -146,14 +144,14 @@ public static function validateCache() $active_options = $options->get(); foreach ($cached_options as $key => &$value) { if (isset($active_options[$key]) && $active_options[$key] !== $value) { - $logger->debug('Options have been changed. Recompiling.'); + debug('Options have been changed. Recompiling.'); $options_changed = true; break; } } if (! $options_changed && ! $files_changed) { - $logger->debug("Files and options have not been modified, returning cached file."); + debug("Files and options have not been modified, returning cached file."); return true; } @@ -188,17 +186,17 @@ public static function getCacheData() $cache_data = json_decode(file_get_contents($process->cacheFile), true) ) { // Successfully loaded config file. - $logger->debug('Cache data loaded.'); + debug('Cache data loaded.'); } else { // Config file may exist but not be writable (may not be visible in some ftp situations?) if ($cache_data_exists) { if (! @unlink($process->cacheFile)) { - $logger->notice('[[CssCrush]] - Could not delete cache data file.'); + notice('[[CssCrush]] - Could not delete cache data file.'); } } else { - $logger->debug('Creating cache data file.'); + debug('Creating cache data file.'); } Util::filePutContents($process->cacheFile, json_encode(array()), __METHOD__); } @@ -211,7 +209,7 @@ public static function saveCacheData() $process = CssCrush::$process; $logger = CssCrush::$config->logger; - $logger->debug('Saving config.'); + debug('Saving config.'); $flags = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 0; Util::filePutContents($process->cacheFile, json_encode($process->cacheData, $flags), __METHOD__); diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index 942fc2a..28d5986 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -78,7 +78,7 @@ public static function hostfile() $import->content = @file_get_contents($import->path); if ($import->content === false) { - $config->logger->debug("Import file '{$import->url->value}' not found"); + debug("Import file '{$import->url->value}' not found"); $str = substr_replace($str, '', $match_start, $match_len); continue; } @@ -261,13 +261,11 @@ static protected function checkSyntax(&$str) if (! $balanced_curlies) { $errors = true; - CssCrush::$config->logger->warning( - '[[CssCrush]] - ' . $validate_pairings($str, '{}') ?: "Unbalanced '{' in $current_file."); + warning('[[CssCrush]] - ' . $validate_pairings($str, '{}') ?: "Unbalanced '{' in $current_file."); } if (! $balanced_parens) { $errors = true; - CssCrush::$config->logger->warning( - '[[CssCrush]] - ' . $validate_pairings($str, '()') ?: "Unbalanced '(' in $current_file."); + warning('[[CssCrush]] - ' . $validate_pairings($str, '()') ?: "Unbalanced '(' in $current_file."); } return $errors ? false : true; diff --git a/lib/CssCrush/Options.php b/lib/CssCrush/Options.php index 59e75ed..1069dce 100644 --- a/lib/CssCrush/Options.php +++ b/lib/CssCrush/Options.php @@ -22,7 +22,6 @@ public function __construct($properties) public function __set($name, $value) { $config = CssCrush::$config; - $logger = $config->logger; switch ($name) { @@ -71,13 +70,12 @@ public function __set($name, $value) case 'output_dir': case 'asset_dir': if (is_string($value)) { - $value = Util::resolveUserPath($value, function ($path) use ($name, $logger) { + $value = Util::resolveUserPath($value, function ($path) use ($name) { if (! @mkdir($path)) { - $logger->notice( - "[[CssCrush]] - Could not create directory $path (setting `$name` option)."); + notice("[[CssCrush]] - Could not create directory $path (setting `$name` option)."); } else { - $logger->debug("Created directory $path (setting `$name` option)."); + debug("Created directory $path (setting `$name` option)."); } return $path; }); diff --git a/lib/CssCrush/Plugin.php b/lib/CssCrush/Plugin.php index 0599798..6236648 100644 --- a/lib/CssCrush/Plugin.php +++ b/lib/CssCrush/Plugin.php @@ -68,7 +68,7 @@ public static function load($plugin_name) } if (! $found) { - CssCrush::$config->logger->notice("[[CssCrush]] - Plugin '$plugin_name' not found."); + notice("[[CssCrush]] - Plugin '$plugin_name' not found."); } } diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index dad9a6f..769a83d 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -241,7 +241,7 @@ public static function applySelectorAliases(&$str) while (preg_match_all($process->selectorAliasesPatt, $str, $m, PREG_OFFSET_CAPTURE | PREG_SET_ORDER)) { - $selector_alias_call = array_pop($m); + $selector_alias_call = end($m); $selector_alias_name = strtolower($selector_alias_call[1][0]); $start = $selector_alias_call[0][1]; diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php index 56b9b18..9ac47c8 100644 --- a/lib/CssCrush/Util.php +++ b/lib/CssCrush/Util.php @@ -196,7 +196,7 @@ public static function filePutContents($file, $str) return true; } - CssCrush::$config->logger->warning("[[CssCrush]] - Could not write file '$file'."); + warning("[[CssCrush]] - Could not write file '$file'."); return false; } diff --git a/plugins/canvas.php b/plugins/canvas.php index 354e843..6b17892 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -145,7 +145,7 @@ function canvas_generator($input, $context) { // Apply functions. canvas_apply_css_funcs($canvas); - // $logger->debug($canvas); + // debug($canvas); // Create fingerprint for this canvas based on canvas object. $fingerprint = substr(md5(serialize($canvas)), 0, 7); @@ -228,7 +228,7 @@ function canvas_generator($input, $context) { } } else { - // $logger->debug('file cached'); + // debug('file cached'); } @@ -685,14 +685,14 @@ function canvas_requirements() { if (! extension_loaded('gd')) { $requirements_met = false; - CssCrush::$config->logger->warning('[[CssCrush]] - GD extension not available.'); + warning('[[CssCrush]] - GD extension not available.'); } else { $info = array_change_key_case(gd_info()); foreach (array('png', 'jpeg') as $key) { if (empty($info["$key support"])) { $requirements_met = false; - CssCrush::$config->logger->warning("[[CssCrush]] - GD extension has no $key support."); + warning("[[CssCrush]] - GD extension has no $key support."); } } } diff --git a/plugins/initial.php b/plugins/initial.php index aab7b3a..21877a1 100644 --- a/plugins/initial.php +++ b/plugins/initial.php @@ -31,7 +31,7 @@ function initial(Rule $rule) { static $initial_values; if (! $initial_values) { if (! ($initial_values = @parse_ini_file(CssCrush::$dir . '/misc/initial-values.ini'))) { - CssCrush::$config->logger->notice("[[CssCrush]] - Initial keywords file could not be parsed."); + notice("[[CssCrush]] - Initial keywords file could not be parsed."); return; } diff --git a/plugins/property-sorter.php b/plugins/property-sorter.php index 4cc81fc..484fb12 100644 --- a/plugins/property-sorter.php +++ b/plugins/property-sorter.php @@ -157,7 +157,7 @@ function &property_sorter_get_table () { $table = preg_split('~\s+~', trim($sorting_file_contents)); } else { - CssCrush::$config->logger->notice("[[CssCrush]] - Property sorting file not found."); + notice("[[CssCrush]] - Property sorting file not found."); } // Store to the global variable. From 132ef78e4b4d6ce2dc426a60d20f63079bc0a13b Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 7 Dec 2013 13:59:32 +0000 Subject: [PATCH 198/421] Testing selector list iterator. --- lib/CssCrush/IO.php | 3 ++- lib/CssCrush/Process.php | 2 +- lib/CssCrush/Rule.php | 22 ++++++++++++---------- misc/formatters.php | 6 +++--- 4 files changed, 18 insertions(+), 15 deletions(-) diff --git a/lib/CssCrush/IO.php b/lib/CssCrush/IO.php index 1c06d70..ea93352 100644 --- a/lib/CssCrush/IO.php +++ b/lib/CssCrush/IO.php @@ -219,9 +219,10 @@ public static function write(Stream $stream) { $process = CssCrush::$process; $output = $process->output; + $source_map_filename = "$output->filename.map"; if ($process->sourceMap) { - $stream->append($process->newline . "/*# sourceMappingURL=$source_map_filename.map */"); + $stream->append($process->newline . "/*# sourceMappingURL=$source_map_filename */"); } if (Util::filePutContents("$output->dir/$output->filename", $stream, __METHOD__)) { diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 769a83d..0796eaf 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -727,7 +727,7 @@ protected function resolveInBlocks() } } } - $rule->selectors = $new_selector_list; + $rule->selectors->store = $new_selector_list; } $curly_match->unWrap(); diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php index 987b819..a3119a7 100644 --- a/lib/CssCrush/Rule.php +++ b/lib/CssCrush/Rule.php @@ -15,7 +15,7 @@ class Rule implements \IteratorAggregate public $isAbstract; public $isFlat = true; - public $selectors = array(); + public $selectors; public $extendSelectors = array(); public $declarations = array(); @@ -37,6 +37,7 @@ public function __construct($selector_string, $declarations_string, $trace_token $process = CssCrush::$process; $this->label = $process->tokens->createLabel('r'); $this->marker = $process->addTracingStubs || $process->generateMap ? $trace_token : null; + $this->selectors = new SelectorList(); if (! empty(Hook::$register['rule_preprocess'])) { // Juggling to maintain the old API. @@ -59,7 +60,8 @@ public function __construct($selector_string, $declarations_string, $trace_token $this->isAbstract = true; } else { - $this->addSelector(new Selector($selector)); + $this->selectors->push(new Selector($selector)); + // $this->addSelector(new Selector($selector)); } } @@ -113,11 +115,11 @@ public function __toString() $process = CssCrush::$process; // Merge the extend selectors. - $this->selectors += $this->extendSelectors; + $this->selectors->store += $this->extendSelectors; // If there are no selectors or declarations associated with the rule // return empty string. - if (empty($this->selectors) || empty($this->declarations)) { + if (empty($this->selectors->store) || empty($this->declarations)) { // De-reference this instance. $process->tokens->release($this->label); @@ -128,7 +130,7 @@ public function __toString() // Concat and return. if ($process->minifyOutput) { - $selectors = implode(',', $this->selectors); + $selectors = $this->selectors->join(); $block = implode(';', $this->declarations); return "$stub$selectors{{$block}}"; } @@ -360,13 +362,13 @@ public function applyExtendables() $ancestor = $extend_arg->pointer; - $extend_selectors = $this->selectors; + $extend_selectors = $this->selectors->store; // If there is a pseudo class extension create a new set accordingly. if ($extend_arg->pseudo) { $extend_selectors = array(); - foreach ($this->selectors as $readable => $selector) { + foreach ($this->selectors->store as $readable => $selector) { $new_selector = clone $selector; $new_readable = $new_selector->appendPseudo($extend_arg->pseudo); $extend_selectors[$new_readable] = $new_selector; @@ -390,7 +392,7 @@ public function expandSelectors() $reg_comma = '~\s*,\s*~'; } - foreach ($this->selectors as $readableValue => $selector) { + foreach ($this->selectors->store as $readableValue => $selector) { $pos = stripos($selector->value, ':any?'); if ($pos !== false) { @@ -454,12 +456,12 @@ public function expandSelectors() } // foreach - $this->selectors = $new_set; + $this->selectors->store = $new_set; } public function addSelector($selector) { - $this->selectors[$selector->readableValue] = $selector; + $this->selectors->store[$selector->readableValue] = $selector; } diff --git a/misc/formatters.php b/misc/formatters.php index c20929d..87e2003 100644 --- a/misc/formatters.php +++ b/misc/formatters.php @@ -16,7 +16,7 @@ function fmtr_single($rule) { $EOL = CssCrush::$process->newline; - $selectors = implode(", ", $rule->selectors); + $selectors = $rule->selectors->join(', '); $block = implode("; ", $rule->declarations); return "$selectors { $block; }$EOL"; } @@ -25,7 +25,7 @@ function fmtr_padded($rule, $padding = 40) { $EOL = CssCrush::$process->newline; - $selectors = implode(", ", $rule->selectors); + $selectors = $rule->selectors->join(', '); $block = implode("; ", $rule->declarations); if (strlen($selectors) > $padding) { @@ -42,7 +42,7 @@ function fmtr_block($rule, $indent = ' ') { $EOL = CssCrush::$process->newline; - $selectors = implode(",$EOL", $rule->selectors); + $selectors = $rule->selectors->join(",$EOL"); $block = implode(";$EOL$indent", $rule->declarations); return "$selectors {{$EOL}$indent$block;$EOL$indent}$EOL"; } From d964174fcaa0ff2f168434077229a3ba82a47bf9 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 7 Dec 2013 14:06:29 +0000 Subject: [PATCH 199/421] Missing sourcemap variable. --- lib/CssCrush/IO.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/CssCrush/IO.php b/lib/CssCrush/IO.php index 1c06d70..ea93352 100644 --- a/lib/CssCrush/IO.php +++ b/lib/CssCrush/IO.php @@ -219,9 +219,10 @@ public static function write(Stream $stream) { $process = CssCrush::$process; $output = $process->output; + $source_map_filename = "$output->filename.map"; if ($process->sourceMap) { - $stream->append($process->newline . "/*# sourceMappingURL=$source_map_filename.map */"); + $stream->append($process->newline . "/*# sourceMappingURL=$source_map_filename */"); } if (Util::filePutContents("$output->dir/$output->filename", $stream, __METHOD__)) { From 01e270a9e0b1d454421620f9f62e5db7a2398c4e Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 7 Dec 2013 21:38:03 +0000 Subject: [PATCH 200/421] Testing declaration list iterator. --- lib/CssCrush/DeclarationList.php | 72 ++++++++++++++++++ lib/CssCrush/Iterator.php | 61 ++++++++++++++++ lib/CssCrush/Process.php | 2 +- lib/CssCrush/Rule.php | 122 ++++++++----------------------- lib/CssCrush/SelectorList.php | 27 +++++++ misc/formatters.php | 6 +- plugins/ease.php | 2 +- plugins/ie-inline-block.php | 4 +- plugins/ie-opacity.php | 7 +- plugins/legacy-flexbox.php | 4 +- plugins/property-sorter.php | 2 +- plugins/rem.php | 4 +- 12 files changed, 205 insertions(+), 108 deletions(-) create mode 100644 lib/CssCrush/DeclarationList.php create mode 100644 lib/CssCrush/Iterator.php create mode 100644 lib/CssCrush/SelectorList.php diff --git a/lib/CssCrush/DeclarationList.php b/lib/CssCrush/DeclarationList.php new file mode 100644 index 0000000..3454f0d --- /dev/null +++ b/lib/CssCrush/DeclarationList.php @@ -0,0 +1,72 @@ +inValid)) { + + $this->index($declaration); + $this->store[] = $declaration; + return $declaration; + } + + return false; + } + + public function reset(array $declaration_stack) + { + $this->store = $declaration_stack; + + $this->updateIndex(); + } + + public function index($declaration) + { + $property = $declaration->property; + + if (isset($this->properties[$property])) { + $this->properties[$property]++; + } + else { + $this->properties[$property] = 1; + } + $this->canonicalProperties[$declaration->canonicalProperty] = true; + } + + public function updateIndex() + { + $this->properties = array(); + $this->canonicalProperties = array(); + + foreach ($this->store as $declaration) { + $this->index($declaration); + } + } + + public function propertyCount($property) + { + return isset($this->properties[$property]) ? $this->properties[$property] : 0; + } + + public function join($glue = ';') + { + return implode($glue, $this->store); + } +} diff --git a/lib/CssCrush/Iterator.php b/lib/CssCrush/Iterator.php new file mode 100644 index 0000000..56b5dd0 --- /dev/null +++ b/lib/CssCrush/Iterator.php @@ -0,0 +1,61 @@ +store = $items; + } + + /* + IteratorAggregate implementation. + */ + public function getIterator() + { + return new \ArrayIterator($this->store); + } + + /* + ArrayAccess implementation. + */ + public function offsetExists($index) + { + return array_key_exists($index, $this->store); + } + + public function offsetGet($index) + { + return isset($this->store[$index]) ? $this->store[$index] : null; + } + + public function offsetSet($index, $value) + { + $this->store[$index] = $value; + } + + public function offsetUnset($index) + { + unset($this->store[$index]); + } + + public function getContents() + { + return $this->store; + } + + /* + Countable implementation. + */ + public function count() + { + return count($this->store); + } +} diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 0796eaf..eeafae3 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -602,7 +602,7 @@ public function captureRules() $rule = new Rule($selector, $block, $m['trace_token']); // Store rules if they have declarations or extend arguments. - if (! empty($rule->declarations) || $rule->extendArgs) { + if (! empty($rule->declarations->store) || $rule->extendArgs) { CssCrush::$process->tokens->add($rule, 'r', $rule->label); diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php index a3119a7..b08dec5 100644 --- a/lib/CssCrush/Rule.php +++ b/lib/CssCrush/Rule.php @@ -16,15 +16,11 @@ class Rule implements \IteratorAggregate public $isFlat = true; public $selectors; - public $extendSelectors = array(); - public $declarations = array(); - - // Index of properties used in the rule for fast lookup. - public $properties = array(); - public $canonicalProperties = array(); + public $declarations; // Arugments passed via @extend. public $extendArgs = array(); + public $extendSelectors = array(); // Declarations hash table for inter-rule this() referencing. public $selfData = array(); @@ -38,6 +34,7 @@ public function __construct($selector_string, $declarations_string, $trace_token $this->label = $process->tokens->createLabel('r'); $this->marker = $process->addTracingStubs || $process->generateMap ? $trace_token : null; $this->selectors = new SelectorList(); + $this->declarations = new DeclarationList(); if (! empty(Hook::$register['rule_preprocess'])) { // Juggling to maintain the old API. @@ -60,8 +57,7 @@ public function __construct($selector_string, $declarations_string, $trace_token $this->isAbstract = true; } else { - $this->selectors->push(new Selector($selector)); - // $this->addSelector(new Selector($selector)); + $this->selectors->add(new Selector($selector)); } } @@ -96,7 +92,7 @@ public function __construct($selector_string, $declarations_string, $trace_token if ($prop === 'mixin') { $this->isFlat = false; - $this->declarations[] = $pair; + $this->declarations->store[] = $pair; } else { // Only store to $this->selfData if the value does not itself make a @@ -104,7 +100,7 @@ public function __construct($selector_string, $declarations_string, $trace_token if (! preg_match(Regex::$patt->thisFunction, $value)) { $this->selfData[strtolower($prop)] = $value; } - $this->addDeclaration($prop, $value, $index); + $this->declarations->add($prop, $value, $index); } } } @@ -119,7 +115,7 @@ public function __toString() // If there are no selectors or declarations associated with the rule // return empty string. - if (empty($this->selectors->store) || empty($this->declarations)) { + if (empty($this->selectors->store) || empty($this->declarations->store)) { // De-reference this instance. $process->tokens->release($this->label); @@ -130,19 +126,21 @@ public function __toString() // Concat and return. if ($process->minifyOutput) { - $selectors = $this->selectors->join(); - $block = implode(';', $this->declarations); - return "$stub$selectors{{$block}}"; + return "$stub{$this->selectors->join()}{{$this->declarations->join()}}"; } else { - - $formatter = $process->ruleFormatter ? - $process->ruleFormatter : 'CssCrush\fmtr_block'; + $formatter = $process->ruleFormatter ? $process->ruleFormatter : 'CssCrush\fmtr_block'; return "$stub{$formatter($this)}"; } } + public function __clone() + { + $this->selectors = clone $this->selectors; + $this->declarations = clone $this->declarations; + } + public $declarationsProcessed = false; public function processDeclarations() { @@ -150,14 +148,14 @@ public function processDeclarations() return; } - foreach ($this->declarations as $index => $declaration) { + foreach ($this->declarations->store as $index => $declaration) { // Execute functions, store as data etc. $declaration->process($this); // Drop declaration if value is now empty. if (! empty($declaration->inValid)) { - unset($this->declarations[$index]); + unset($this->declarations->store[$index]); } } @@ -175,7 +173,7 @@ public function flatten() // Flatten mixins. $new_set = array(); - foreach ($this->declarations as $declaration) { + foreach ($this->declarations->store as $declaration) { if (is_array($declaration) && $declaration[0] === 'mixin') { foreach (Mixin::merge(array(), $declaration[1], array('context' => $this)) as $pair) { $new_set[] = new Declaration($pair[0], $pair[1], count($new_set)); @@ -187,7 +185,7 @@ public function flatten() } } - $this->setDeclarations($new_set); + $this->declarations->reset($new_set); $this->isFlat = true; } @@ -473,7 +471,7 @@ public function addPropertyAliases() $aliased_properties =& CssCrush::$process->aliases['properties']; // Bail early if nothing doing. - if (! array_intersect_key($aliased_properties, $this->properties)) { + if (! array_intersect_key($aliased_properties, $this->declarations->properties)) { return; } @@ -482,7 +480,7 @@ public function addPropertyAliases() $vendor_context = $this->vendorContext; $regex = Regex::$patt; - foreach ($this->declarations as $declaration) { + foreach ($this->declarations->store as $declaration) { // Check declaration against vendor context. if ($vendor_context && $declaration->vendor && $declaration->vendor !== $vendor_context) { @@ -500,7 +498,7 @@ public function addPropertyAliases() foreach ($aliased_properties[$declaration->property] as $prop_alias) { // If an aliased version already exists do not create one. - if ($this->propertyCount($prop_alias)) { + if ($this->declarations->propertyCount($prop_alias)) { continue; } @@ -533,7 +531,7 @@ public function addPropertyAliases() // Re-assign if any updates have been made. if ($rule_updated) { - $this->setDeclarations($stack); + $this->declarations->reset($stack); } } @@ -548,7 +546,7 @@ public function addFunctionAliases() $rule_updated = false; // Shim in aliased functions. - foreach ($this->declarations as $declaration) { + foreach ($this->declarations->store as $declaration) { // No functions, bail. if (! $declaration->functions || $declaration->skip) { @@ -655,7 +653,7 @@ public function addFunctionAliases() // Re-assign if any updates have been made. if ($rule_updated) { - $this->setDeclarations($new_set); + $this->declarations->reset($new_set); } } @@ -664,19 +662,16 @@ public function addDeclarationAliases() $declaration_aliases =& CssCrush::$process->aliases['declarations']; // First test for the existence of any aliased properties. - if (! ($intersect = array_intersect_key($declaration_aliases, $this->properties))) { - + if (! ($intersect = array_intersect_key($declaration_aliases, $this->declarations->properties))) { return; } - // Table lookups are faster. $intersect = array_flip(array_keys($intersect)); - $vendor_context = $this->vendorContext; $new_set = array(); $rule_updated = false; - foreach ($this->declarations as $declaration) { + foreach ($this->declarations->store as $declaration) { // Check the current declaration property is actually aliased. if (isset($intersect[$declaration->property]) && ! $declaration->skip) { @@ -711,7 +706,7 @@ public function addDeclarationAliases() // Re-assign if any updates have been made. if ($rule_updated) { - $this->setDeclarations($new_set); + $this->declarations->reset($new_set); } } @@ -721,68 +716,9 @@ public function addDeclarationAliases() public function getIterator() { - return new \ArrayIterator($this->declarations); - } - - - ############################# - # Property indexing. - - public function indexProperty($declaration) - { - $prop = $declaration->property; - - if (isset($this->properties[$prop])) { - $this->properties[$prop]++; - } - else { - $this->properties[$prop] = 1; - } - $this->canonicalProperties[$declaration->canonicalProperty] = true; - } - - public function updatePropertyIndex() - { - // Reset tables. - $this->properties = array(); - $this->canonicalProperties = array(); - - foreach ($this->declarations as $declaration) { - $this->indexProperty($declaration); - } - } - - - ############################# - # Rule API. - - public function propertyCount($prop) - { - return isset($this->properties[$prop]) ? $this->properties[$prop] : 0; + return new \ArrayIterator($this->declarations->store); } - public function addDeclaration($prop, $value, $contextIndex = 0) - { - // Create declaration, add to the stack if it's valid - $declaration = new Declaration($prop, $value, $contextIndex); - - if (empty($declaration->inValid)) { - - $this->indexProperty($declaration); - $this->declarations[] = $declaration; - return $declaration; - } - - return false; - } - - public function setDeclarations(array $declaration_stack) - { - $this->declarations = $declaration_stack; - - // Update the property index. - $this->updatePropertyIndex(); - } public static function parseBlock($str, $options = array()) { diff --git a/lib/CssCrush/SelectorList.php b/lib/CssCrush/SelectorList.php new file mode 100644 index 0000000..9fde441 --- /dev/null +++ b/lib/CssCrush/SelectorList.php @@ -0,0 +1,27 @@ +store[$selector->readableValue] = $selector; + } + + public function join($glue = ',') + { + return implode($glue, $this->store); + } +} diff --git a/misc/formatters.php b/misc/formatters.php index 87e2003..58e9c1f 100644 --- a/misc/formatters.php +++ b/misc/formatters.php @@ -17,7 +17,7 @@ function fmtr_single($rule) { $EOL = CssCrush::$process->newline; $selectors = $rule->selectors->join(', '); - $block = implode("; ", $rule->declarations); + $block = $rule->declarations->join('; '); return "$selectors { $block; }$EOL"; } @@ -26,7 +26,7 @@ function fmtr_padded($rule, $padding = 40) { $EOL = CssCrush::$process->newline; $selectors = $rule->selectors->join(', '); - $block = implode("; ", $rule->declarations); + $block = $rule->declarations->join('; '); if (strlen($selectors) > $padding) { $padding = str_repeat(' ', $padding); @@ -43,6 +43,6 @@ function fmtr_block($rule, $indent = ' ') { $EOL = CssCrush::$process->newline; $selectors = $rule->selectors->join(",$EOL"); - $block = implode(";$EOL$indent", $rule->declarations); + $block = $rule->declarations->join(";$EOL$indent"); return "$selectors {{$EOL}$indent$block;$EOL$indent}$EOL"; } diff --git a/plugins/ease.php b/plugins/ease.php index 5c15c73..ad2e8ad 100644 --- a/plugins/ease.php +++ b/plugins/ease.php @@ -66,7 +66,7 @@ function ease(Rule $rule) { } } - if (! array_intersect_key($rule->canonicalProperties, $easing_properties)) { + if (! array_intersect_key($rule->declarations->canonicalProperties, $easing_properties)) { return; } diff --git a/plugins/ie-inline-block.php b/plugins/ie-inline-block.php index 19206f4..8029b4f 100644 --- a/plugins/ie-inline-block.php +++ b/plugins/ie-inline-block.php @@ -24,7 +24,7 @@ function ie_inline_block(Rule $rule) { - if ($rule->propertyCount('display') < 1) { + if ($rule->declarations->propertyCount('display') < 1) { return; } @@ -41,5 +41,5 @@ function ie_inline_block(Rule $rule) { $stack[] = new Declaration('*display', 'inline'); $stack[] = new Declaration('*zoom', 1); } - $rule->setDeclarations($stack); + $rule->declarations->reset($stack); } diff --git a/plugins/ie-opacity.php b/plugins/ie-opacity.php index 64e10d2..a39d050 100755 --- a/plugins/ie-opacity.php +++ b/plugins/ie-opacity.php @@ -25,9 +25,10 @@ function ie_opacity(Rule $rule) { - if ($rule->propertyCount('opacity') < 1) { + if ($rule->declarations->propertyCount('opacity') < 1) { return; } + $new_set = array(); foreach ($rule as $declaration) { $new_set[] = $declaration; @@ -41,7 +42,7 @@ function ie_opacity(Rule $rule) { $opacity = (float) $declaration->value; $opacity = round($opacity * 100); - if (! $rule->propertyCount('zoom')) { + if (! $rule->declarations->propertyCount('zoom')) { // Filters need hasLayout $new_set[] = new Declaration('zoom', 1); } @@ -49,5 +50,5 @@ function ie_opacity(Rule $rule) { $new_set[] = new Declaration('-ms-filter', "\"$value\""); $new_set[] = new Declaration('*filter', $value); } - $rule->setDeclarations($new_set); + $rule->declarations->reset($new_set); } diff --git a/plugins/legacy-flexbox.php b/plugins/legacy-flexbox.php index c7d5894..3b57e20 100644 --- a/plugins/legacy-flexbox.php +++ b/plugins/legacy-flexbox.php @@ -74,7 +74,7 @@ function legacy_flexbox(Rule $rule) { // - flex-basis ); - $properties =& $rule->properties; + $properties =& $rule->declarations->properties; $intersect_props = array_intersect_key($properties, $flex_related_props); // Checking for flex related properties or 'display:flex'. @@ -179,7 +179,7 @@ function legacy_flexbox(Rule $rule) { // Re-assign if any updates have been made. if ($rule_updated) { - $rule->setDeclarations($stack); + $rule->declarations->reset($stack); } } diff --git a/plugins/property-sorter.php b/plugins/property-sorter.php index 484fb12..189ea2d 100644 --- a/plugins/property-sorter.php +++ b/plugins/property-sorter.php @@ -45,7 +45,7 @@ function property_sorter(Rule $rule) { usort($new_set, 'CssCrush\property_sorter_callback'); - $rule->setDeclarations($new_set); + $rule->declarations->reset($new_set); } diff --git a/plugins/rem.php b/plugins/rem.php index 55217e4..f050913 100644 --- a/plugins/rem.php +++ b/plugins/rem.php @@ -64,7 +64,7 @@ function rem(Rule $rule) { // Determine which properties are touched; all, or just font related. $just_font_props = ! isset($vars['rem__all']); - if ($just_font_props && ! array_intersect_key($rule->canonicalProperties, $font_props)) { + if ($just_font_props && ! array_intersect_key($rule->declarations->canonicalProperties, $font_props)) { return; } @@ -140,6 +140,6 @@ function rem(Rule $rule) { } if ($rule_updated) { - $rule->setDeclarations($new_set); + $rule->declarations->reset($new_set); } } From 35ccb853e482e82cda3c485b72b4ab85c032c8d6 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 9 Dec 2013 09:38:35 +0000 Subject: [PATCH 201/421] Version bump. --- lib/CssCrush/CssCrush.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/CssCrush/CssCrush.php b/lib/CssCrush/CssCrush.php index 42ea6e3..5b22fd7 100644 --- a/lib/CssCrush/CssCrush.php +++ b/lib/CssCrush/CssCrush.php @@ -8,7 +8,7 @@ class CssCrush { - const VERSION = '2.0.1-beta'; + const VERSION = '2.0.1'; // Global settings. public static $config; From 8cf80f0d444efb7a1fc44610f98327822f54c6bc Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 9 Dec 2013 11:58:04 +0000 Subject: [PATCH 202/421] Renamed property `$version->revision` => `$version->patch` to be more inline with semantic versioning terminology. --- lib/CssCrush/CssCrush.php | 2 +- lib/CssCrush/Version.php | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/CssCrush/CssCrush.php b/lib/CssCrush/CssCrush.php index 42ea6e3..e81880c 100644 --- a/lib/CssCrush/CssCrush.php +++ b/lib/CssCrush/CssCrush.php @@ -8,7 +8,7 @@ class CssCrush { - const VERSION = '2.0.1-beta'; + const VERSION = '2.1.0-beta'; // Global settings. public static $config; diff --git a/lib/CssCrush/Version.php b/lib/CssCrush/Version.php index 9955e7b..74db90c 100644 --- a/lib/CssCrush/Version.php +++ b/lib/CssCrush/Version.php @@ -10,7 +10,7 @@ class Version { public $major; public $minor; - public $revision; + public $patch; public $extra; public function __construct($version_string) @@ -30,7 +30,7 @@ public function __construct($version_string) if ($version) { $this->major = (int) $version['major']; $this->minor = isset($version['minor']) ? (int) $version['minor'] : 0; - $this->revision = isset($version['patch']) ? (int) $version['patch'] : 0; + $this->patch = isset($version['patch']) ? (int) $version['patch'] : 0; $this->extra = isset($version['extra']) ? $version['extra'] : null; } } @@ -42,8 +42,8 @@ public function __toString() if (isset($this->minor)) { $out .= ".$this->minor"; } - if (isset($this->revision)) { - $out .= ".$this->revision"; + if (isset($this->patch)) { + $out .= ".$this->patch"; } if (isset($this->extra)) { $out .= "-$this->extra"; @@ -60,7 +60,7 @@ public function compare($version_string) $test = new Version($version_string); - foreach (array('major', 'minor', 'revision') as $level) { + foreach (array('major', 'minor', 'patch') as $level) { if ($this->{$level} < $test->{$level}) { From d84794ed262bd3ba3b5ac18488c8390d4809df03 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 9 Dec 2013 12:00:55 +0000 Subject: [PATCH 203/421] Removing legacy rule_preprocess hook code. --- lib/CssCrush/Rule.php | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php index b08dec5..59f8f43 100644 --- a/lib/CssCrush/Rule.php +++ b/lib/CssCrush/Rule.php @@ -36,16 +36,6 @@ public function __construct($selector_string, $declarations_string, $trace_token $this->selectors = new SelectorList(); $this->declarations = new DeclarationList(); - if (! empty(Hook::$register['rule_preprocess'])) { - // Juggling to maintain the old API. - $rule = new \stdClass(); - $rule->selector_raw = $selector_string; - $rule->declaration_raw = $declarations_string; - Hook::run('rule_preprocess', $rule); - $selector_string = $rule->selector_raw; - $declarations_string = $rule->declaration_raw; - } - // Parse selectors. // Strip any other comments then create selector instances. $selector_string = trim(Util::stripCommentTokens($selector_string)); From 0b3439ceda30b8da1c8d9186a1c8975c559e5487 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 9 Dec 2013 12:03:17 +0000 Subject: [PATCH 204/421] Moving to `git describe` style output in cli version reporting. --- cli.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli.php b/cli.php index 35e3a42..2c0c1bd 100755 --- a/cli.php +++ b/cli.php @@ -130,7 +130,7 @@ if ($args->version) { - stdout('v' . csscrush_version()); + stdout('v' . csscrush_version(true)); exit(STATUS_OK); } From 3494a33ffa4b8d674df1a77a61b0036e6fd85af7 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 10 Dec 2013 12:27:28 +0000 Subject: [PATCH 205/421] Refactoring. Moving methods from Rule to SelectorList and DeclarationList. --- lib/CssCrush/Declaration.php | 21 +- lib/CssCrush/DeclarationList.php | 457 ++++++++++++++++++++++++- lib/CssCrush/Functions.php | 14 +- lib/CssCrush/Mixin.php | 2 +- lib/CssCrush/Process.php | 24 +- lib/CssCrush/Rule.php | 557 +------------------------------ lib/CssCrush/SelectorList.php | 77 +++++ plugins/canvas.php | 2 +- plugins/svg.php | 2 +- 9 files changed, 562 insertions(+), 594 deletions(-) diff --git a/lib/CssCrush/Declaration.php b/lib/CssCrush/Declaration.php index a1f9be9..dedc37f 100644 --- a/lib/CssCrush/Declaration.php +++ b/lib/CssCrush/Declaration.php @@ -16,22 +16,14 @@ class Declaration public $index; public $skip; public $important; - public $inValid = false; + public $valid = true; public function __construct($prop, $value, $contextIndex = 0) { $regex = Regex::$patt; // Normalize input. Lowercase the property name. - $prop = strtolower(trim($prop)); - $value = trim($value); - - // Check the input. - if ($prop === '' || $value === '' || $value === null) { - $this->inValid = true; - - return; - } + $prop = strtolower($prop); // Test for escape tilde. if ($skip = strpos($prop, '~') === 0) { @@ -57,7 +49,7 @@ public function __construct($prop, $value, $contextIndex = 0) // Reject declarations with empty CSS values. if ($value === false || $value === '') { - $this->inValid = true; + $this->valid = false; return; } @@ -107,8 +99,7 @@ public function process($parent_rule) ), $context); - // Add result to $rule->selfData. - $parent_rule->selfData += array($this->property => $this->value); + $parent_rule->declarations->data += array($this->property => $this->value); $context = (object) array( 'rule' => $parent_rule, @@ -127,12 +118,12 @@ public function process($parent_rule) // After functions have applied value may be empty. if ($this->value === '') { - $this->inValid = true; + $this->valid = false; return; } // Store raw value as data on the parent rule. - $parent_rule->queryData[$this->property] = $this->value; + $parent_rule->declarations->queryData[$this->property] = $this->value; // Capture top-level paren pairs. $this->value = CssCrush::$process->tokens->captureParens($this->value); diff --git a/lib/CssCrush/DeclarationList.php b/lib/CssCrush/DeclarationList.php index 3454f0d..255b639 100644 --- a/lib/CssCrush/DeclarationList.php +++ b/lib/CssCrush/DeclarationList.php @@ -8,9 +8,18 @@ class DeclarationList extends Iterator { + public $flattened = true; + public $processed = false; + public $properties = array(); public $canonicalProperties = array(); + // Declarations hash table for inter-rule this() referencing. + public $data = array(); + + // Declarations hash table for external query() referencing. + public $queryData = array(); + public function __construct() { parent::__construct(); @@ -20,7 +29,7 @@ public function add($property, $value, $contextIndex = 0) { $declaration = new Declaration($property, $value, $contextIndex); - if (empty($declaration->inValid)) { + if ($declaration->valid) { $this->index($declaration); $this->store[] = $declaration; @@ -69,4 +78,450 @@ public function join($glue = ';') { return implode($glue, $this->store); } + + /* + Aliasing. + */ + public function aliasProperties($vendor_context = null) + { + $aliased_properties =& CssCrush::$process->aliases['properties']; + + // Bail early if nothing doing. + if (! array_intersect_key($aliased_properties, $this->properties)) { + return; + } + + $stack = array(); + $rule_updated = false; + $regex = Regex::$patt; + + foreach ($this->store as $declaration) { + + // Check declaration against vendor context. + if ($vendor_context && $declaration->vendor && $declaration->vendor !== $vendor_context) { + continue; + } + + if ($declaration->skip) { + $stack[] = $declaration; + continue; + } + + // Shim in aliased properties. + if (isset($aliased_properties[$declaration->property])) { + + foreach ($aliased_properties[$declaration->property] as $prop_alias) { + + // If an aliased version already exists do not create one. + if ($this->propertyCount($prop_alias)) { + continue; + } + + // Get property alias vendor. + preg_match($regex->vendorPrefix, $prop_alias, $alias_vendor); + + // Check against vendor context. + if ($vendor_context && $alias_vendor && $alias_vendor[1] !== $vendor_context) { + continue; + } + + // Create the aliased declaration. + $copy = clone $declaration; + $copy->property = $prop_alias; + + // Set the aliased declaration vendor property. + $copy->vendor = null; + if ($alias_vendor) { + $copy->vendor = $alias_vendor[1]; + } + + $stack[] = $copy; + $rule_updated = true; + } + } + + // Un-aliased property or a property alias that has been manually set. + $stack[] = $declaration; + } + + // Re-assign if any updates have been made. + if ($rule_updated) { + $this->reset($stack); + } + } + + public function aliasFunctions($vendor_context = null) + { + $function_aliases =& CssCrush::$process->aliases['functions']; + $function_alias_groups =& CssCrush::$process->aliases['function_groups']; + + // The new modified set of declarations. + $new_set = array(); + $rule_updated = false; + + // Shim in aliased functions. + foreach ($this->store as $declaration) { + + // No functions, bail. + if (! $declaration->functions || $declaration->skip) { + $new_set[] = $declaration; + continue; + } + + // Get list of functions used in declaration that are alias-able, bail if none. + $intersect = array_intersect_key($declaration->functions, $function_aliases); + if (! $intersect) { + $new_set[] = $declaration; + continue; + } + + // Keep record of which groups have been applied. + $processed_groups = array(); + + foreach (array_keys($intersect) as $fn_name) { + + // Store for all the duplicated declarations. + $prefixed_copies = array(); + + // Grouped function aliases. + if ($function_aliases[$fn_name][0] === ':') { + + $group_id = $function_aliases[$fn_name]; + + // If this group has been applied we can skip over. + if (isset($processed_groups[$group_id])) { + continue; + } + + // Mark group as applied. + $processed_groups[$group_id] = true; + + $groups =& $function_alias_groups[$group_id]; + + foreach ($groups as $group_key => $replacements) { + + // If the declaration is vendor specific only create aliases for the same vendor. + if ( + ($declaration->vendor && $group_key !== $declaration->vendor) || + ($vendor_context && $group_key !== $vendor_context) + ) { + continue; + } + + $copy = clone $declaration; + + // Make swaps. + $copy->value = preg_replace( + $replacements['find'], + $replacements['replace'], + $copy->value + ); + $prefixed_copies[] = $copy; + $rule_updated = true; + } + + // Post fixes. + if (isset(PostAliasFix::$functions[$group_id])) { + call_user_func(PostAliasFix::$functions[$group_id], $prefixed_copies, $group_id); + } + } + + // Single function aliases. + else { + + foreach ($function_aliases[$fn_name] as $fn_alias) { + + // If the declaration is vendor specific only create aliases for the same vendor. + if ($declaration->vendor) { + preg_match(Regex::$patt->vendorPrefix, $fn_alias, $m); + if ( + $m[1] !== $declaration->vendor || + ($vendor_context && $m[1] !== $vendor_context) + ) { + continue; + } + } + + $copy = clone $declaration; + + // Make swaps. + $copy->value = preg_replace( + '~(?value + ); + $prefixed_copies[] = $copy; + $rule_updated = true; + } + + // Post fixes. + if (isset(PostAliasFix::$functions[$fn_name])) { + call_user_func(PostAliasFix::$functions[$fn_name], $prefixed_copies, $fn_name); + } + } + + $new_set = array_merge($new_set, $prefixed_copies); + } + $new_set[] = $declaration; + } + + // Re-assign if any updates have been made. + if ($rule_updated) { + $this->reset($new_set); + } + } + + public function aliasDeclarations($vendor_context = null) + { + $declaration_aliases =& CssCrush::$process->aliases['declarations']; + + // First test for the existence of any aliased properties. + if (! ($intersect = array_intersect_key($declaration_aliases, $this->properties))) { + return; + } + + $intersect = array_flip(array_keys($intersect)); + $new_set = array(); + $rule_updated = false; + + foreach ($this->store as $declaration) { + + // Check the current declaration property is actually aliased. + if (isset($intersect[$declaration->property]) && ! $declaration->skip) { + + // Iterate on the current declaration property for value matches. + foreach ($declaration_aliases[$declaration->property] as $value_match => $replacements) { + + // Create new alias declaration if the property and value match. + if ($declaration->value === $value_match) { + + foreach ($replacements as $values) { + + // Check the vendor against context. + if ($vendor_context && $vendor_context !== $values[2]) { + continue; + } + + // If the replacement property is null use the original declaration property. + $new = new Declaration( + ! empty($values[0]) ? $values[0] : $declaration->property, + $values[1] + ); + $new->important = $declaration->important; + $new_set[] = $new; + $rule_updated = true; + } + } + } + } + $new_set[] = $declaration; + } + + // Re-assign if any updates have been made. + if ($rule_updated) { + $this->reset($new_set); + } + } + + public static function parse($str, $options = array()) + { + $str = Util::stripCommentTokens($str); + $lines = preg_split('~\s*;\s*~', $str, null, PREG_SPLIT_NO_EMPTY); + $keyed = ! empty($options['keyed']); + $directives = empty($options['ignore_directives']); + $out = array(); + + foreach ($lines as $line) { + + if ($directives && preg_match(Regex::$patt->ruleDirective, $line, $m)) { + + if (! empty($m[1])) { + $property = 'mixin'; + } + elseif (! empty($m[2])) { + $property = 'extends'; + } + else { + $property = 'name'; + } + $value = trim(substr($line, strlen($m[0]))); + } + elseif (($colon_pos = strpos($line, ':')) !== false) { + + $property = trim(substr($line, 0, $colon_pos)); + $value = trim(substr($line, $colon_pos + 1)); + } + else { + continue; + } + + if (! isset($property[0]) || ! isset($value[0])) { + continue; + } + + if ($property === 'mixin' && ! empty($options['flatten'])) { + $out = Mixin::merge($out, $value, array( + 'keyed' => $keyed, + 'context' => isset($options['context']) ? $options['context'] : null, + )); + } + elseif ($keyed) { + $out[$property] = $value; + } + else { + $out[] = array($property, $value); + } + } + + return $out; + } + + public function flatten($rule_context) + { + if ($this->flattened) { + return; + } + + $new_set = array(); + foreach ($this->store as $declaration) { + if (is_array($declaration) && $declaration[0] === 'mixin') { + foreach (Mixin::merge(array(), $declaration[1], array('context' => $rule_context)) as $pair) { + $new_set[] = new Declaration($pair[0], $pair[1], count($new_set)); + } + } + else { + $declaration->index = count($new_set); + $new_set[] = $declaration; + } + } + + $this->reset($new_set); + $this->flattened = true; + } + + public function process($rule_context) + { + if ($this->processed) { + return; + } + + foreach ($this->store as $index => $declaration) { + + // Execute functions, store as data etc. + $declaration->process($rule_context); + + // Drop declaration if value is now empty. + if (! $declaration->valid) { + unset($this->store[$index]); + } + } + + // data is done with, reclaim memory. + unset($this->data); + + $this->processed = true; + } + + public function expandData($dataset, $property) + { + // Expand shorthand properties to make them available + // as data for this() and query(). + static $expandables = array( + 'margin-top' => 'margin', + 'margin-right' => 'margin', + 'margin-bottom' => 'margin', + 'margin-left' => 'margin', + 'padding-top' => 'padding', + 'padding-right' => 'padding', + 'padding-bottom' => 'padding', + 'padding-left' => 'padding', + 'border-top-width' => 'border-width', + 'border-right-width' => 'border-width', + 'border-bottom-width' => 'border-width', + 'border-left-width' => 'border-width', + 'border-top-left-radius' => 'border-radius', + 'border-top-right-radius' => 'border-radius', + 'border-bottom-right-radius' => 'border-radius', + 'border-bottom-left-radius' => 'border-radius', + 'border-top-color' => 'border-color', + 'border-right-color' => 'border-color', + 'border-bottom-color' => 'border-color', + 'border-left-color' => 'border-color', + ); + + $dataset =& $this->{$dataset}; + $property_group = isset($expandables[$property]) ? $expandables[$property] : null; + + // Bail if property non-expandable or already set. + if (! $property_group || isset($dataset[$property]) || ! isset($dataset[$property_group])) { + return; + } + + // Get the expandable property value. + $value = $dataset[$property_group]; + + // Top-Right-Bottom-Left "trbl" expandable properties. + $trbl_fmt = null; + switch ($property_group) { + case 'margin': + $trbl_fmt = 'margin-%s'; + break; + case 'padding': + $trbl_fmt = 'padding-%s'; + break; + case 'border-width': + $trbl_fmt = 'border-%s-width'; + break; + case 'border-radius': + $trbl_fmt = 'border-%s-radius'; + break; + case 'border-color': + $trbl_fmt = 'border-%s-color'; + break; + } + if ($trbl_fmt) { + $parts = explode(' ', $value); + $placeholders = array(); + + // 4 values. + if (isset($parts[3])) { + $placeholders = $parts; + } + // 3 values. + elseif (isset($parts[2])) { + $placeholders = array($parts[0], $parts[1], $parts[2], $parts[1]); + } + // 2 values. + elseif (isset($parts[1])) { + $placeholders = array($parts[0], $parts[1], $parts[0], $parts[1]); + } + // 1 value. + else { + $placeholders = array_pad($placeholders, 4, $parts[0]); + } + + // Set positional variants. + if ($property_group === 'border-radius') { + $positions = array( + 'top-left', + 'top-right', + 'bottom-right', + 'bottom-left', + ); + } + else { + $positions = array( + 'top', + 'right', + 'bottom', + 'left', + ); + } + + foreach ($positions as $index => $position) { + $prop = sprintf($trbl_fmt, $position); + $dataset += array($prop => $placeholders[$index]); + } + } + } } diff --git a/lib/CssCrush/Functions.php b/lib/CssCrush/Functions.php index a6f3475..6b952cc 100644 --- a/lib/CssCrush/Functions.php +++ b/lib/CssCrush/Functions.php @@ -244,11 +244,11 @@ function fn__this($input, $context) { } $rule = $context->rule; - $rule->expandDataSet('selfData', $property); + $rule->declarations->expandData('data', $property); - if (isset($rule->selfData[$property])) { + if (isset($rule->declarations->data[$property])) { - return $rule->selfData[$property]; + return $rule->declarations->data[$property]; } // Fallback value. @@ -295,11 +295,11 @@ function fn__query($input, $context) { $result = ''; if (isset($references[$name])) { $query_rule = $references[$name]; - $query_rule->processDeclarations(); - $query_rule->expandDataSet('queryData', $property); + $query_rule->declarations->process($query_rule); + $query_rule->declarations->expandData('queryData', $property); - if (isset($query_rule->queryData[$property])) { - $result = $query_rule->queryData[$property]; + if (isset($query_rule->declarations->queryData[$property])) { + $result = $query_rule->declarations->queryData[$property]; } } diff --git a/lib/CssCrush/Mixin.php b/lib/CssCrush/Mixin.php index 003cbd6..d4fe9c3 100644 --- a/lib/CssCrush/Mixin.php +++ b/lib/CssCrush/Mixin.php @@ -61,7 +61,7 @@ public static function call($message, $context = null) $args = Util::splitDelimList($raw_args); } - return Rule::parseBlock($mixable->template->apply($args), array( + return DeclarationList::parse($mixable->template->apply($args), array( 'flatten' => true, 'context' => $mixable, )); diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index eeafae3..1ced7c7 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -431,7 +431,7 @@ protected function captureVars() CssCrush::$process->vars[$m['name']] = $m['value']; } else { - CssCrush::$process->vars = Rule::parseBlock($m['block_content'], array( + CssCrush::$process->vars = DeclarationList::parse($m['block_content'], array( 'keyed' => true, 'ignore_directives' => true, )) + CssCrush::$process->vars; @@ -630,26 +630,18 @@ protected function processRules() foreach ($this->tokens->store->r as $rule) { - $rule->flatten(); - - $rule->processDeclarations(); + $rule->declarations->flatten($rule); + $rule->declarations->process($rule); Hook::run('rule_prealias', $rule); - if ($this->aliases['properties']) { - $rule->addPropertyAliases(); - } - if ($this->aliases['functions']) { - $rule->addFunctionAliases(); - } - if ($this->aliases['declarations']) { - $rule->addDeclarationAliases(); - } - Hook::run('rule_postalias', $rule); + $rule->declarations->aliasProperties($rule->vendorContext); + $rule->declarations->aliasFunctions($rule->vendorContext); + $rule->declarations->aliasDeclarations($rule->vendorContext); - $rule->expandSelectors(); + Hook::run('rule_postalias', $rule); - // Find previous selectors and apply them. + $rule->selectors->expand(); $rule->applyExtendables(); Hook::run('rule_postprocess', $rule); diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php index 59f8f43..1ea472c 100644 --- a/lib/CssCrush/Rule.php +++ b/lib/CssCrush/Rule.php @@ -13,7 +13,7 @@ class Rule implements \IteratorAggregate public $marker; public $name; public $isAbstract; - public $isFlat = true; + public $resolvedExtendables; public $selectors; public $declarations; @@ -22,12 +22,6 @@ class Rule implements \IteratorAggregate public $extendArgs = array(); public $extendSelectors = array(); - // Declarations hash table for inter-rule this() referencing. - public $selfData = array(); - - // Declarations hash table for external query() referencing. - public $queryData = array(); - public function __construct($selector_string, $declarations_string, $trace_token = null) { $process = CssCrush::$process; @@ -51,8 +45,7 @@ public function __construct($selector_string, $declarations_string, $trace_token } } - // Parse rule block. - $pairs = Rule::parseBlock($declarations_string); + $pairs = DeclarationList::parse($declarations_string); foreach ($pairs as $index => $pair) { @@ -81,14 +74,14 @@ public function __construct($selector_string, $declarations_string, $trace_token if (trim($value) !== '') { if ($prop === 'mixin') { - $this->isFlat = false; + $this->declarations->flattened = false; $this->declarations->store[] = $pair; } else { - // Only store to $this->selfData if the value does not itself make a + // Only store to $this->data if the value does not itself make a // this() call to avoid circular references. if (! preg_match(Regex::$patt->thisFunction, $value)) { - $this->selfData[strtolower($prop)] = $value; + $this->declarations->data[strtolower($prop)] = $value; } $this->declarations->add($prop, $value, $index); } @@ -131,157 +124,6 @@ public function __clone() $this->declarations = clone $this->declarations; } - public $declarationsProcessed = false; - public function processDeclarations() - { - if ($this->declarationsProcessed) { - return; - } - - foreach ($this->declarations->store as $index => $declaration) { - - // Execute functions, store as data etc. - $declaration->process($this); - - // Drop declaration if value is now empty. - if (! empty($declaration->inValid)) { - unset($this->declarations->store[$index]); - } - } - - // selfData is done with, reclaim memory. - unset($this->selfData); - - $this->declarationsProcessed = true; - } - - public function flatten() - { - if ($this->isFlat) { - return; - } - - // Flatten mixins. - $new_set = array(); - foreach ($this->declarations->store as $declaration) { - if (is_array($declaration) && $declaration[0] === 'mixin') { - foreach (Mixin::merge(array(), $declaration[1], array('context' => $this)) as $pair) { - $new_set[] = new Declaration($pair[0], $pair[1], count($new_set)); - } - } - else { - $declaration->index = count($new_set); - $new_set[] = $declaration; - } - } - - $this->declarations->reset($new_set); - $this->isFlat = true; - } - - public function expandDataSet($dataset, $property) - { - // Expand shorthand properties to make them available - // as data for this() and query(). - static $expandables = array( - 'margin-top' => 'margin', - 'margin-right' => 'margin', - 'margin-bottom' => 'margin', - 'margin-left' => 'margin', - 'padding-top' => 'padding', - 'padding-right' => 'padding', - 'padding-bottom' => 'padding', - 'padding-left' => 'padding', - 'border-top-width' => 'border-width', - 'border-right-width' => 'border-width', - 'border-bottom-width' => 'border-width', - 'border-left-width' => 'border-width', - 'border-top-left-radius' => 'border-radius', - 'border-top-right-radius' => 'border-radius', - 'border-bottom-right-radius' => 'border-radius', - 'border-bottom-left-radius' => 'border-radius', - 'border-top-color' => 'border-color', - 'border-right-color' => 'border-color', - 'border-bottom-color' => 'border-color', - 'border-left-color' => 'border-color', - ); - - $dataset =& $this->{$dataset}; - $property_group = isset($expandables[$property]) ? $expandables[$property] : null; - - // Bail if property non-expandable or already set. - if (! $property_group || isset($dataset[$property]) || ! isset($dataset[$property_group])) { - return; - } - - // Get the expandable property value. - $value = $dataset[$property_group]; - - // Top-Right-Bottom-Left "trbl" expandable properties. - $trbl_fmt = null; - switch ($property_group) { - case 'margin': - $trbl_fmt = 'margin-%s'; - break; - case 'padding': - $trbl_fmt = 'padding-%s'; - break; - case 'border-width': - $trbl_fmt = 'border-%s-width'; - break; - case 'border-radius': - $trbl_fmt = 'border-%s-radius'; - break; - case 'border-color': - $trbl_fmt = 'border-%s-color'; - break; - } - if ($trbl_fmt) { - $parts = explode(' ', $value); - $placeholders = array(); - - // 4 values. - if (isset($parts[3])) { - $placeholders = $parts; - } - // 3 values. - elseif (isset($parts[2])) { - $placeholders = array($parts[0], $parts[1], $parts[2], $parts[1]); - } - // 2 values. - elseif (isset($parts[1])) { - $placeholders = array($parts[0], $parts[1], $parts[0], $parts[1]); - } - // 1 value. - else { - $placeholders = array_pad($placeholders, 4, $parts[0]); - } - - // Set positional variants. - if ($property_group === 'border-radius') { - $positions = array( - 'top-left', - 'top-right', - 'bottom-right', - 'bottom-left', - ); - } - else { - $positions = array( - 'top', - 'right', - 'bottom', - 'left', - ); - } - - foreach ($positions as $index => $position) { - $prop = sprintf($trbl_fmt, $position); - $dataset += array($prop => $placeholders[$index]); - } - } - } - ############################# # Rule inheritance. @@ -296,7 +138,6 @@ public function setExtendSelectors($raw_value) } } - public $resolvedExtendables = false; public function resolveExtendables() { if (! $this->extendArgs) { @@ -367,340 +208,6 @@ public function applyExtendables() } - ############################# - # Selectors. - - public function expandSelectors() - { - $new_set = array(); - - static $any_patt, $reg_comma; - if (! $any_patt) { - $any_patt = Regex::make('~:any({{p-token}})~i'); - $reg_comma = '~\s*,\s*~'; - } - - foreach ($this->selectors->store as $readableValue => $selector) { - - $pos = stripos($selector->value, ':any?'); - if ($pos !== false) { - - // Contains an :any statement so expand. - $chain = array(''); - do { - if ($pos === 0) { - preg_match($any_patt, $selector->value, $m); - - // Parse the arguments - $expression = CssCrush::$process->tokens->get($m[1]); - - // Remove outer parens. - $expression = substr($expression, 1, strlen($expression) - 2); - - // Test for nested :any() expressions. - $has_nesting = stripos($expression, ':any(') !== false; - - $parts = preg_split($reg_comma, $expression, null, PREG_SPLIT_NO_EMPTY); - - $tmp = array(); - foreach ($chain as $rowCopy) { - foreach ($parts as $part) { - // Flatten nested :any() expressions in a hacky kind of way. - if ($has_nesting) { - $part = str_ireplace(':any(', '', $part); - - // If $part has unbalanced parens trim closing parens to match. - $diff = substr_count($part, ')') - substr_count($part, '('); - if ($diff > 0) { - $part = preg_replace('~\){1,'. $diff .'}$~', '', $part); - } - } - $tmp[] = $rowCopy . $part; - } - } - $chain = $tmp; - $selector->value = substr($selector->value, strlen($m[0])); - } - else { - foreach ($chain as &$row) { - $row .= substr($selector->value, 0, $pos); - } - $selector->value = substr($selector->value, $pos); - } - } while (($pos = stripos($selector->value, ':any?')) !== false); - - // Finish off. - foreach ($chain as &$row) { - - $new = new Selector($row . $selector->value); - $new_set[$new->readableValue] = $new; - } - } - else { - - // Nothing to expand. - $new_set[$readableValue] = $selector; - } - - } // foreach - - $this->selectors->store = $new_set; - } - - public function addSelector($selector) - { - $this->selectors->store[$selector->readableValue] = $selector; - } - - - ############################# - # Aliasing. - - public function addPropertyAliases() - { - $aliased_properties =& CssCrush::$process->aliases['properties']; - - // Bail early if nothing doing. - if (! array_intersect_key($aliased_properties, $this->declarations->properties)) { - return; - } - - $stack = array(); - $rule_updated = false; - $vendor_context = $this->vendorContext; - $regex = Regex::$patt; - - foreach ($this->declarations->store as $declaration) { - - // Check declaration against vendor context. - if ($vendor_context && $declaration->vendor && $declaration->vendor !== $vendor_context) { - continue; - } - - if ($declaration->skip) { - $stack[] = $declaration; - continue; - } - - // Shim in aliased properties. - if (isset($aliased_properties[$declaration->property])) { - - foreach ($aliased_properties[$declaration->property] as $prop_alias) { - - // If an aliased version already exists do not create one. - if ($this->declarations->propertyCount($prop_alias)) { - continue; - } - - // Get property alias vendor. - preg_match($regex->vendorPrefix, $prop_alias, $alias_vendor); - - // Check against vendor context. - if ($vendor_context && $alias_vendor && $alias_vendor[1] !== $vendor_context) { - continue; - } - - // Create the aliased declaration. - $copy = clone $declaration; - $copy->property = $prop_alias; - - // Set the aliased declaration vendor property. - $copy->vendor = null; - if ($alias_vendor) { - $copy->vendor = $alias_vendor[1]; - } - - $stack[] = $copy; - $rule_updated = true; - } - } - - // Un-aliased property or a property alias that has been manually set. - $stack[] = $declaration; - } - - // Re-assign if any updates have been made. - if ($rule_updated) { - $this->declarations->reset($stack); - } - } - - public function addFunctionAliases() - { - $function_aliases =& CssCrush::$process->aliases['functions']; - $function_alias_groups =& CssCrush::$process->aliases['function_groups']; - $vendor_context = $this->vendorContext; - - // The new modified set of declarations. - $new_set = array(); - $rule_updated = false; - - // Shim in aliased functions. - foreach ($this->declarations->store as $declaration) { - - // No functions, bail. - if (! $declaration->functions || $declaration->skip) { - $new_set[] = $declaration; - continue; - } - - // Get list of functions used in declaration that are alias-able, bail if none. - $intersect = array_intersect_key($declaration->functions, $function_aliases); - if (! $intersect) { - $new_set[] = $declaration; - continue; - } - - // Keep record of which groups have been applied. - $processed_groups = array(); - - foreach (array_keys($intersect) as $fn_name) { - - // Store for all the duplicated declarations. - $prefixed_copies = array(); - - // Grouped function aliases. - if ($function_aliases[$fn_name][0] === ':') { - - $group_id = $function_aliases[$fn_name]; - - // If this group has been applied we can skip over. - if (isset($processed_groups[$group_id])) { - continue; - } - - // Mark group as applied. - $processed_groups[$group_id] = true; - - $groups =& $function_alias_groups[$group_id]; - - foreach ($groups as $group_key => $replacements) { - - // If the declaration is vendor specific only create aliases for the same vendor. - if ( - ($declaration->vendor && $group_key !== $declaration->vendor) || - ($vendor_context && $group_key !== $vendor_context) - ) { - continue; - } - - $copy = clone $declaration; - - // Make swaps. - $copy->value = preg_replace( - $replacements['find'], - $replacements['replace'], - $copy->value - ); - $prefixed_copies[] = $copy; - $rule_updated = true; - } - - // Post fixes. - if (isset(PostAliasFix::$functions[$group_id])) { - call_user_func(PostAliasFix::$functions[$group_id], $prefixed_copies, $group_id); - } - } - - // Single function aliases. - else { - - foreach ($function_aliases[$fn_name] as $fn_alias) { - - // If the declaration is vendor specific only create aliases for the same vendor. - if ($declaration->vendor) { - preg_match(Regex::$patt->vendorPrefix, $fn_alias, $m); - if ( - $m[1] !== $declaration->vendor || - ($vendor_context && $m[1] !== $vendor_context) - ) { - continue; - } - } - - $copy = clone $declaration; - - // Make swaps. - $copy->value = preg_replace( - '~(?value - ); - $prefixed_copies[] = $copy; - $rule_updated = true; - } - - // Post fixes. - if (isset(PostAliasFix::$functions[$fn_name])) { - call_user_func(PostAliasFix::$functions[$fn_name], $prefixed_copies, $fn_name); - } - } - - $new_set = array_merge($new_set, $prefixed_copies); - } - $new_set[] = $declaration; - } - - // Re-assign if any updates have been made. - if ($rule_updated) { - $this->declarations->reset($new_set); - } - } - - public function addDeclarationAliases() - { - $declaration_aliases =& CssCrush::$process->aliases['declarations']; - - // First test for the existence of any aliased properties. - if (! ($intersect = array_intersect_key($declaration_aliases, $this->declarations->properties))) { - return; - } - - $intersect = array_flip(array_keys($intersect)); - $vendor_context = $this->vendorContext; - $new_set = array(); - $rule_updated = false; - - foreach ($this->declarations->store as $declaration) { - - // Check the current declaration property is actually aliased. - if (isset($intersect[$declaration->property]) && ! $declaration->skip) { - - // Iterate on the current declaration property for value matches. - foreach ($declaration_aliases[$declaration->property] as $value_match => $replacements) { - - // Create new alias declaration if the property and value match. - if ($declaration->value === $value_match) { - - foreach ($replacements as $values) { - - // Check the vendor against context. - if ($vendor_context && $vendor_context !== $values[2]) { - continue; - } - - // If the replacement property is null use the original declaration property. - $new = new Declaration( - ! empty($values[0]) ? $values[0] : $declaration->property, - $values[1] - ); - $new->important = $declaration->important; - $new_set[] = $new; - $rule_updated = true; - } - } - } - } - $new_set[] = $declaration; - } - - // Re-assign if any updates have been made. - if ($rule_updated) { - $this->declarations->reset($new_set); - } - } - - ############################# # IteratorAggregate interface. @@ -708,58 +215,4 @@ public function getIterator() { return new \ArrayIterator($this->declarations->store); } - - - public static function parseBlock($str, $options = array()) - { - $str = Util::stripCommentTokens($str); - $lines = preg_split('~\s*;\s*~', $str, null, PREG_SPLIT_NO_EMPTY); - $keyed = ! empty($options['keyed']); - $directives = empty($options['ignore_directives']); - $out = array(); - - foreach ($lines as $line) { - - if ($directives && preg_match(Regex::$patt->ruleDirective, $line, $m)) { - - if (! empty($m[1])) { - $property = 'mixin'; - } - elseif (! empty($m[2])) { - $property = 'extends'; - } - else { - $property = 'name'; - } - $value = trim(substr($line, strlen($m[0]))); - } - elseif (($colon_pos = strpos($line, ':')) !== false) { - - $property = trim(substr($line, 0, $colon_pos)); - $value = trim(substr($line, $colon_pos + 1)); - } - else { - continue; - } - - if (! isset($property[0]) || ! isset($value[0])) { - continue; - } - - if ($property === 'mixin' && ! empty($options['flatten'])) { - $out = Mixin::merge($out, $value, array( - 'keyed' => $keyed, - 'context' => isset($options['context']) ? $options['context'] : null, - )); - } - elseif ($keyed) { - $out[$property] = $value; - } - else { - $out[] = array($property, $value); - } - } - - return $out; - } } diff --git a/lib/CssCrush/SelectorList.php b/lib/CssCrush/SelectorList.php index 9fde441..eb29506 100644 --- a/lib/CssCrush/SelectorList.php +++ b/lib/CssCrush/SelectorList.php @@ -24,4 +24,81 @@ public function join($glue = ',') { return implode($glue, $this->store); } + + public function expand() + { + $new_set = array(); + + static $any_patt, $reg_comma; + if (! $any_patt) { + $any_patt = Regex::make('~:any({{p-token}})~i'); + $reg_comma = '~\s*,\s*~'; + } + + foreach ($this->store as $readableValue => $selector) { + + $pos = stripos($selector->value, ':any?'); + if ($pos !== false) { + + // Contains an :any statement so expand. + $chain = array(''); + do { + if ($pos === 0) { + preg_match($any_patt, $selector->value, $m); + + // Parse the arguments + $expression = CssCrush::$process->tokens->get($m[1]); + + // Remove outer parens. + $expression = substr($expression, 1, strlen($expression) - 2); + + // Test for nested :any() expressions. + $has_nesting = stripos($expression, ':any(') !== false; + + $parts = preg_split($reg_comma, $expression, null, PREG_SPLIT_NO_EMPTY); + + $tmp = array(); + foreach ($chain as $rowCopy) { + foreach ($parts as $part) { + // Flatten nested :any() expressions in a hacky kind of way. + if ($has_nesting) { + $part = str_ireplace(':any(', '', $part); + + // If $part has unbalanced parens trim closing parens to match. + $diff = substr_count($part, ')') - substr_count($part, '('); + if ($diff > 0) { + $part = preg_replace('~\){1,'. $diff .'}$~', '', $part); + } + } + $tmp[] = $rowCopy . $part; + } + } + $chain = $tmp; + $selector->value = substr($selector->value, strlen($m[0])); + } + else { + foreach ($chain as &$row) { + $row .= substr($selector->value, 0, $pos); + } + $selector->value = substr($selector->value, $pos); + } + } while (($pos = stripos($selector->value, ':any?')) !== false); + + // Finish off. + foreach ($chain as &$row) { + + $new = new Selector($row . $selector->value); + $new_set[$new->readableValue] = $new; + } + } + else { + + // Nothing to expand. + $new_set[$readableValue] = $selector; + } + + } // foreach + + $this->store = $new_set; + } } diff --git a/plugins/canvas.php b/plugins/canvas.php index 6b17892..b5a6625 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -118,7 +118,7 @@ function canvas_generator($input, $context) { $block = $canvas_defs[$name]->apply($args); // Parse the block into a keyed array. - $raw = array_change_key_case(Rule::parseBlock($block, array( + $raw = array_change_key_case(DeclarationList::parse($block, array( 'keyed' => true, 'flatten' => true, ))); diff --git a/plugins/svg.php b/plugins/svg.php index 14e21d5..573fd11 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -196,7 +196,7 @@ function svg_generator($input, $fn_name) { $block = $svg_defs[$name]->apply($args); // Parse the block into a keyed assoc array. - $raw_data = array_change_key_case(Rule::parseBlock($block, array( + $raw_data = array_change_key_case(DeclarationList::parse($block, array( 'keyed' => true, 'flatten' => true, ))); From aa17b735ea5be247106bf9c2917548a69f4375e7 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 10 Dec 2013 13:17:44 +0000 Subject: [PATCH 206/421] Removed implied declaration iteration from Rule objects. Iterate on `$rule->declarations` instead. --- lib/CssCrush/Mixin.php | 2 +- lib/CssCrush/Rule.php | 13 ++----------- plugins/ease.php | 2 +- plugins/hsl-to-hex.php | 4 ++-- plugins/ie-inline-block.php | 2 +- plugins/ie-opacity.php | 2 +- plugins/initial.php | 2 +- plugins/legacy-flexbox.php | 4 ++-- plugins/property-sorter.php | 2 +- plugins/rem.php | 2 +- 10 files changed, 13 insertions(+), 22 deletions(-) diff --git a/lib/CssCrush/Mixin.php b/lib/CssCrush/Mixin.php index d4fe9c3..87ddb93 100644 --- a/lib/CssCrush/Mixin.php +++ b/lib/CssCrush/Mixin.php @@ -69,7 +69,7 @@ public static function call($message, $context = null) elseif ($mixable instanceof Rule) { $result = array(); - foreach ($mixable as $declaration) { + foreach ($mixable->declarations as $declaration) { if ($declaration instanceof Declaration) { $result[] = array( $declaration->property, diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php index 1ea472c..bc5f1d4 100644 --- a/lib/CssCrush/Rule.php +++ b/lib/CssCrush/Rule.php @@ -6,7 +6,7 @@ */ namespace CssCrush; -class Rule implements \IteratorAggregate +class Rule { public $vendorContext; public $label; @@ -66,7 +66,7 @@ public function __construct($selector_string, $declarations_string, $trace_token } } - // Bind declaration objects on the rule. + // Build declaration list. foreach ($pairs as $index => &$pair) { list($prop, $value) = $pair; @@ -206,13 +206,4 @@ public function applyExtendables() $ancestor->extendSelectors += $extend_selectors; } } - - - ############################# - # IteratorAggregate interface. - - public function getIterator() - { - return new \ArrayIterator($this->declarations->store); - } } diff --git a/plugins/ease.php b/plugins/ease.php index ad2e8ad..ea8df2e 100644 --- a/plugins/ease.php +++ b/plugins/ease.php @@ -70,7 +70,7 @@ function ease(Rule $rule) { return; } - foreach ($rule as $declaration) { + foreach ($rule->declarations as $declaration) { if ( ! $declaration->skip && isset($easing_properties[$declaration->canonicalProperty]) diff --git a/plugins/hsl-to-hex.php b/plugins/hsl-to-hex.php index ddfff71..38ef2ff 100644 --- a/plugins/hsl-to-hex.php +++ b/plugins/hsl-to-hex.php @@ -4,7 +4,7 @@ * * @before * color: hsl( 100, 50%, 50%) - * + * * @after * color: #6abf40 */ @@ -24,7 +24,7 @@ function hsl_to_hex(Rule $rule) { $hsl_patt = Regex::make('~{{LB}}hsl({{p-token}})~i'); - foreach ($rule as &$declaration) { + foreach ($rule->declarations as $declaration) { if (! $declaration->skip && isset($declaration->functions['hsl'])) { while (preg_match($hsl_patt, $declaration->value, $m)) { diff --git a/plugins/ie-inline-block.php b/plugins/ie-inline-block.php index 8029b4f..80d73bd 100644 --- a/plugins/ie-inline-block.php +++ b/plugins/ie-inline-block.php @@ -29,7 +29,7 @@ function ie_inline_block(Rule $rule) { } $stack = array(); - foreach ($rule as $declaration) { + foreach ($rule->declarations as $declaration) { $stack[] = $declaration; $is_display = $declaration->property === 'display'; if ( diff --git a/plugins/ie-opacity.php b/plugins/ie-opacity.php index a39d050..45accfa 100755 --- a/plugins/ie-opacity.php +++ b/plugins/ie-opacity.php @@ -30,7 +30,7 @@ function ie_opacity(Rule $rule) { } $new_set = array(); - foreach ($rule as $declaration) { + foreach ($rule->declarations as $declaration) { $new_set[] = $declaration; if ( $declaration->skip || diff --git a/plugins/initial.php b/plugins/initial.php index 21877a1..bd19c29 100644 --- a/plugins/initial.php +++ b/plugins/initial.php @@ -37,7 +37,7 @@ function initial(Rule $rule) { } } - foreach ($rule as &$declaration) { + foreach ($rule->declarations as $declaration) { if (! $declaration->skip && 'initial' === $declaration->value) { if (isset($initial_values[$declaration->property])) { $declaration->value = $initial_values[ $declaration->property ]; diff --git a/plugins/legacy-flexbox.php b/plugins/legacy-flexbox.php index 3b57e20..f5a3fbc 100644 --- a/plugins/legacy-flexbox.php +++ b/plugins/legacy-flexbox.php @@ -44,7 +44,7 @@ * of flexbox: https://developer.mozilla.org/en-US/docs/Firefox_20_for_developers */ namespace CssCrush; - + Plugin::register('legacy-flexbox', array( 'enable' => function () { Hook::add('rule_prealias', 'CssCrush\legacy_flexbox'); @@ -100,7 +100,7 @@ function legacy_flexbox(Rule $rule) { $stack = array(); $rule_updated = false; - foreach ($rule as $declaration) { + foreach ($rule->declarations as $declaration) { $prop = $declaration->property; $value = $declaration->value; diff --git a/plugins/property-sorter.php b/plugins/property-sorter.php index 189ea2d..e942f6d 100644 --- a/plugins/property-sorter.php +++ b/plugins/property-sorter.php @@ -39,7 +39,7 @@ function property_sorter(Rule $rule) { $new_set = array(); // Create plain array of rule declarations. - foreach ($rule as $declaration) { + foreach ($rule->declarations as $declaration) { $new_set[] = $declaration; } diff --git a/plugins/rem.php b/plugins/rem.php index f050913..609de3e 100644 --- a/plugins/rem.php +++ b/plugins/rem.php @@ -29,7 +29,7 @@ * * `rem-fallback` is the default mode. To change the conversion mode set a * variable named `rem__mode` with the mode name you want as its value. - * + * * To convert all values, not just values of the font related properties, * set a variable named `rem__all` with a value of `yes`. */ From f75b04cc68c80843941e4f9f729d43f68b3f21fa Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 10 Dec 2013 13:43:56 +0000 Subject: [PATCH 207/421] Changed `Regex::make` to accept spaces around substitutions. --- lib/CssCrush/Regex.php | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php index 66ecd41..3c359c7 100644 --- a/lib/CssCrush/Regex.php +++ b/lib/CssCrush/Regex.php @@ -14,8 +14,6 @@ class Regex // Character classes. public static $classes; - public static $swaps = array(); - public static function init() { self::$patt = $patt = new \stdClass(); @@ -55,7 +53,6 @@ public static function init() // Create standalone class patterns, add classes as class swaps. foreach ($classes as $name => $class) { - self::$swaps['{{' . str_replace('_', '-', $name) . '}}'] = $class; $patt->{$name} = '~' . $class . '~S'; } @@ -122,17 +119,23 @@ public static function init() public static function make($pattern) { - static $cache = array(), $find, $replace; - if (isset($cache[$pattern])) { + static $cache = array(), $pattern_map; + if (isset($cache[$pattern])) { return $cache[$pattern]; } - elseif (! $find) { - $find = array_keys(self::$swaps); - $replace = array_values(self::$swaps); + + if (! $pattern_map) { + $pattern_map = array(); + foreach (self::$classes as $name => $regex_class) { + $pattern_map[str_replace('_', '-', $name)] = $regex_class; + } } - return $cache[$pattern] = str_replace($find, $replace, $pattern); + return $cache[$pattern] = preg_replace_callback( + '~\{\{ *(?[\w-]+) *\}\}~S', function ($m) use ($pattern_map) { + return $pattern_map[$m['name']]; + }, $pattern); } public static function matchAll($patt, $subject, $offset = 0) From 8b750cdf6d2679c569f0909943f8ecbfbae748fe Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 12 Dec 2013 09:54:52 +0000 Subject: [PATCH 208/421] Rewritten selector grouping expansion to play nicer with selector aliases. --- lib/CssCrush/Process.php | 14 ++--- lib/CssCrush/Selector.php | 13 +---- lib/CssCrush/SelectorList.php | 105 ++++++++++++++++------------------ 3 files changed, 56 insertions(+), 76 deletions(-) diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 1ced7c7..78ab8be 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -228,13 +228,12 @@ function ($m) { } } - public static function applySelectorAliases(&$str) + public static function applySelectorAliases($str) { $process = CssCrush::$process; - // Early bail conditions. if (! $process->selectorAliases || ! preg_match($process->selectorAliasesPatt, $str)) { - return; + return $str; } $table =& $process->selectorAliases; @@ -268,9 +267,10 @@ public static function applySelectorAliases(&$str) $template = new Template($template($args)); } - // Splice in the result. $str = substr_replace($str, $template->apply($args), $start, $length); } + + return $str; } @@ -663,15 +663,11 @@ protected function resolveInBlocks() $match_start_pos = $match[0][1]; $raw_argument = trim($match[1][0]); - Process::applySelectorAliases($raw_argument); - - $raw_argument = $tokens->captureParens($raw_argument); - $arguments = Util::splitDelimList($raw_argument); + $arguments = Util::splitDelimList(Process::applySelectorAliases($raw_argument)); $curly_match = new BalancedMatch($this->stream, $match_start_pos); if (! $curly_match->match || empty($raw_argument)) { - // Couldn't match the block. continue; } diff --git a/lib/CssCrush/Selector.php b/lib/CssCrush/Selector.php index aef8c0b..8192952 100644 --- a/lib/CssCrush/Selector.php +++ b/lib/CssCrush/Selector.php @@ -23,10 +23,7 @@ public function __construct($raw_selector, $associated_rule = null) // Take readable value from original un-altered state. $this->readableValue = Selector::makeReadable($raw_selector); - Process::applySelectorAliases($raw_selector); - - // Capture top-level paren groups. - $this->value = CssCrush::$process->tokens->captureParens($raw_selector); + $this->value = Process::applySelectorAliases($raw_selector); } public function __toString() @@ -51,17 +48,11 @@ public function appendPseudo($pseudo) public static function normalizeWhiteSpace($str) { // Create space around combinators, then normalize whitespace. - $str = preg_replace('~([>+]|\~(?!=))~S', ' $1 ', $str); - return Util::normalizeWhiteSpace($str); + return Util::normalizeWhiteSpace(preg_replace('~([>+]|\~(?!=))~S', ' $1 ', $str)); } static function makeReadable($str) { - // Quick test for paren tokens. - if (strpos($str, '?p') !== false) { - $str = CssCrush::$process->tokens->restore($str, 'p'); - } - $str = Selector::normalizeWhiteSpace($str); // Quick test for string tokens. diff --git a/lib/CssCrush/SelectorList.php b/lib/CssCrush/SelectorList.php index eb29506..3dba28b 100644 --- a/lib/CssCrush/SelectorList.php +++ b/lib/CssCrush/SelectorList.php @@ -27,78 +27,71 @@ public function join($glue = ',') public function expand() { - $new_set = array(); + static $grouping_patt, $expand, $expandSelector; + if (! $grouping_patt) { - static $any_patt, $reg_comma; - if (! $any_patt) { - $any_patt = Regex::make('~:any({{p-token}})~i'); - $reg_comma = '~\s*,\s*~'; - } - - foreach ($this->store as $readableValue => $selector) { - - $pos = stripos($selector->value, ':any?'); - if ($pos !== false) { + $grouping_patt = Regex::make('~\:any{{ parens }}~iS'); - // Contains an :any statement so expand. - $chain = array(''); - do { - if ($pos === 0) { - preg_match($any_patt, $selector->value, $m); + $expand = function ($selector_string) use ($grouping_patt) + { + if (preg_match($grouping_patt, $selector_string, $m, PREG_OFFSET_CAPTURE)) { - // Parse the arguments - $expression = CssCrush::$process->tokens->get($m[1]); + list($full_match, $full_match_offset) = $m[0]; + $before = substr($selector_string, 0, $full_match_offset); + $after = substr($selector_string, strlen($full_match) + $full_match_offset); - // Remove outer parens. - $expression = substr($expression, 1, strlen($expression) - 2); + $selectors = array(); + foreach (Util::splitDelimList($m['parens_content'][0]) as $segment) { + $selectors["$before$segment$after"] = true; + } - // Test for nested :any() expressions. - $has_nesting = stripos($expression, ':any(') !== false; + return $selectors; + } - $parts = preg_split($reg_comma, $expression, null, PREG_SPLIT_NO_EMPTY); + return false; + }; - $tmp = array(); - foreach ($chain as $rowCopy) { - foreach ($parts as $part) { - // Flatten nested :any() expressions in a hacky kind of way. - if ($has_nesting) { - $part = str_ireplace(':any(', '', $part); + $expandSelector = function ($selector_string) use ($expand) + { + if ($running_stack = $expand($selector_string)) { - // If $part has unbalanced parens trim closing parens to match. - $diff = substr_count($part, ')') - substr_count($part, '('); - if ($diff > 0) { - $part = preg_replace('~\){1,'. $diff .'}$~', '', $part); - } - } - $tmp[] = $rowCopy . $part; + $flattened_stack = array(); + do { + $loop_stack = array(); + foreach ($running_stack as $selector => $bool) { + $selectors = $expand($selector); + if (! $selectors) { + $flattened_stack += array($selector => true); + } + else { + $loop_stack += $selectors; } } - $chain = $tmp; - $selector->value = substr($selector->value, strlen($m[0])); - } - else { - foreach ($chain as &$row) { - $row .= substr($selector->value, 0, $pos); - } - $selector->value = substr($selector->value, $pos); - } - } while (($pos = stripos($selector->value, ':any?')) !== false); + $running_stack = $loop_stack; - // Finish off. - foreach ($chain as &$row) { + } while ($loop_stack); - $new = new Selector($row . $selector->value); - $new_set[$new->readableValue] = $new; + return $flattened_stack; + } + + return array($input => true); + }; + } + + $expanded_set = array(); + + foreach ($this->store as $readable_value => $original_selector) { + if (stripos($original_selector->value, ':any(') !== false) { + foreach ($expandSelector($original_selector->value) as $selector_string => $bool) { + $new = new Selector($selector_string); + $expanded_set[$new->readableValue] = $new; } } else { - - // Nothing to expand. - $new_set[$readableValue] = $selector; + $expanded_set[$original_selector->readableValue] = $original_selector; } + } - } // foreach - - $this->store = $new_set; + $this->store = $expanded_set; } } From d9823055c36575c2ceb64cc6c75661da5c45c5c0 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 13 Dec 2013 20:10:10 +0000 Subject: [PATCH 209/421] Adding Collections. --- lib/CssCrush/Collection.php | 79 ++++++++++++++++++++++++++++ lib/CssCrush/CollectionInterface.php | 10 ++++ lib/CssCrush/Declaration.php | 18 +++---- lib/CssCrush/Iterator.php | 11 +++- lib/CssCrush/SelectorList.php | 2 - plugins/hsl-to-hex.php | 7 ++- plugins/initial.php | 15 +++--- 7 files changed, 117 insertions(+), 25 deletions(-) create mode 100644 lib/CssCrush/Collection.php create mode 100644 lib/CssCrush/CollectionInterface.php diff --git a/lib/CssCrush/Collection.php b/lib/CssCrush/Collection.php new file mode 100644 index 0000000..e4f6f1f --- /dev/null +++ b/lib/CssCrush/Collection.php @@ -0,0 +1,79 @@ +store = $store; + } + + public function get($index = null) + { + return is_int($index) ? $this->store[$index] : $this->store; + } + + static public function value($item, $property) + { + if (strpos($property, '|') !== false) { + $filters = explode('|', $property); + $property = array_shift($filters); + $value = $item->$property; + foreach ($filters as $filter) { + switch ($filter) { + case 'lower': + $value = strtolower($value); + break; + } + } + return $value; + } + return $item->$property; + } + + public function filter() + { + $args = func_get_args(); + + $assoc_array = is_array($args[0]) ? $args[0] : false; + + if ($assoc_array) { + + $ops = array( + '===' => function ($item) use ($assoc_array) { + foreach ($assoc_array as $property => $value) { + if (Collection::value($item, $property) !== $value) { + return false; + } + } + return true; + }, + '!==' => function ($item) use ($assoc_array) { + foreach ($assoc_array as $property => $value) { + if (Collection::value($item, $property) === $value) { + return false; + } + } + return true; + }, + ); + + $op = isset($args[1]) ? $args[1] : '==='; + $callback = $ops[$op]; + } + elseif (is_callable($args[0])) { + $callback = $args[0]; + } + + if (isset($callback)) { + $this->store = array_filter($this->store, $callback); + } + + return $this; + } +} diff --git a/lib/CssCrush/CollectionInterface.php b/lib/CssCrush/CollectionInterface.php new file mode 100644 index 0000000..32f6015 --- /dev/null +++ b/lib/CssCrush/CollectionInterface.php @@ -0,0 +1,10 @@ +property = $prop; + $this->property = $prop; $this->canonicalProperty = $canonical_property; - $this->vendor = $vendor; - $this->index = $contextIndex; - $this->value = $value; - $this->skip = $skip; - $this->important = $important; + $this->vendor = $vendor; + $this->index = $contextIndex; + $this->value = $value; + $this->skip = $skip; + $this->important = $important; } public function __toString() diff --git a/lib/CssCrush/Iterator.php b/lib/CssCrush/Iterator.php index 56b5dd0..94f8425 100644 --- a/lib/CssCrush/Iterator.php +++ b/lib/CssCrush/Iterator.php @@ -6,7 +6,7 @@ */ namespace CssCrush; -class Iterator implements \IteratorAggregate, \ArrayAccess, \Countable +class Iterator implements \IteratorAggregate, \ArrayAccess, \Countable, CollectionInterface { public $store; @@ -58,4 +58,13 @@ public function count() { return count($this->store); } + + /* + Collection interface. + */ + public function filter() + { + $collection = new Collection($this->store); + return call_user_func_array(array($collection, __FUNCTION__), func_get_args()); + } } diff --git a/lib/CssCrush/SelectorList.php b/lib/CssCrush/SelectorList.php index 3dba28b..f392390 100644 --- a/lib/CssCrush/SelectorList.php +++ b/lib/CssCrush/SelectorList.php @@ -8,8 +8,6 @@ class SelectorList extends Iterator { - public $store; - public function __construct() { parent::__construct(); diff --git a/plugins/hsl-to-hex.php b/plugins/hsl-to-hex.php index 38ef2ff..2bcf232 100644 --- a/plugins/hsl-to-hex.php +++ b/plugins/hsl-to-hex.php @@ -22,11 +22,10 @@ function hsl_to_hex(Rule $rule) { - $hsl_patt = Regex::make('~{{LB}}hsl({{p-token}})~i'); + $hsl_patt = Regex::make('~{{ LB }}hsl({{ p-token }})~i'); - foreach ($rule->declarations as $declaration) { - - if (! $declaration->skip && isset($declaration->functions['hsl'])) { + foreach ($rule->declarations->filter(array('skip' => false)) as $declaration) { + if (isset($declaration->functions['hsl'])) { while (preg_match($hsl_patt, $declaration->value, $m)) { $token = $m[1]; $color = new Color('hsl' . CssCrush::$process->tokens->pop($token)); diff --git a/plugins/initial.php b/plugins/initial.php index bd19c29..8944c24 100644 --- a/plugins/initial.php +++ b/plugins/initial.php @@ -37,15 +37,12 @@ function initial(Rule $rule) { } } - foreach ($rule->declarations as $declaration) { - if (! $declaration->skip && 'initial' === $declaration->value) { - if (isset($initial_values[$declaration->property])) { - $declaration->value = $initial_values[ $declaration->property ]; - } - else { - // Fallback to 'inherit'. - $declaration->value = 'inherit'; - } + foreach ($rule->declarations->filter(array('skip' => false, 'value|lower' => 'initial')) as $declaration) { + if (isset($initial_values[$declaration->property])) { + $declaration->value = $initial_values[ $declaration->property ]; + } + else { + $declaration->value = 'inherit'; } } } From 661e14ec0ac93228811274aa4610a3b2d4ffc760 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 13 Dec 2013 20:25:50 +0000 Subject: [PATCH 210/421] Added ini file parsing helper. --- lib/CssCrush/Color.php | 4 +--- lib/CssCrush/Util.php | 10 ++++++++++ plugins/initial.php | 4 +--- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/CssCrush/Color.php b/lib/CssCrush/Color.php index d0bdeb5..2aa16b7 100644 --- a/lib/CssCrush/Color.php +++ b/lib/CssCrush/Color.php @@ -16,9 +16,7 @@ public static function &loadKeywords () { if (! isset(self::$keywords)) { - $table = array(); - $path = CssCrush::$dir . '/misc/color-keywords.ini'; - if ($keywords = parse_ini_file($path)) { + if ($keywords = Util::loadIni('misc/color-keywords.ini')) { foreach ($keywords as $word => $rgb) { $rgb = array_map('intval', explode(',', $rgb)); self::$keywords[ $word ] = $rgb; diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php index 9ac47c8..a87536a 100644 --- a/lib/CssCrush/Util.php +++ b/lib/CssCrush/Util.php @@ -201,6 +201,16 @@ public static function filePutContents($file, $str) return false; } + public static function loadIni($library_relative_path, $sections = false) + { + if (! ($ini_array = @parse_ini_file(CssCrush::$dir . '/' . $library_relative_path, $sections))) { + notice("[[CssCrush]] - Ini file '$library_relative_path' could not be parsed."); + + return false; + } + return $ini_array; + } + /* * Encode integer to Base64 VLQ. */ diff --git a/plugins/initial.php b/plugins/initial.php index 8944c24..4a59a71 100644 --- a/plugins/initial.php +++ b/plugins/initial.php @@ -30,9 +30,7 @@ function initial(Rule $rule) { static $initial_values; if (! $initial_values) { - if (! ($initial_values = @parse_ini_file(CssCrush::$dir . '/misc/initial-values.ini'))) { - notice("[[CssCrush]] - Initial keywords file could not be parsed."); - + if (! ($initial_values = Util::loadIni('misc/initial-values.ini'))) { return; } } From 29a8b68a0336fb89ba3f2b44f064edb779438cd6 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 13 Dec 2013 21:37:57 +0000 Subject: [PATCH 211/421] Applying new collection method. --- plugins/ease.php | 9 +++------ plugins/property-sorter.php | 11 +---------- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/plugins/ease.php b/plugins/ease.php index ea8df2e..0195283 100644 --- a/plugins/ease.php +++ b/plugins/ease.php @@ -60,7 +60,7 @@ function ease(Rule $rule) { ); foreach ($easings as $property => $value) { - $patt = Regex::make('~{{LB}}' . $property . '{{RB}}~i'); + $patt = Regex::make("~{{ LB }}$property{{ RB }}~i"); $find[] = $patt; $replace[] = $value; } @@ -70,11 +70,8 @@ function ease(Rule $rule) { return; } - foreach ($rule->declarations as $declaration) { - if ( - ! $declaration->skip && - isset($easing_properties[$declaration->canonicalProperty]) - ) { + foreach ($rule->declarations->filter(array('skip' => false)) as $declaration) { + if (isset($easing_properties[$declaration->canonicalProperty])) { $declaration->value = preg_replace($find, $replace, $declaration->value); } } diff --git a/plugins/property-sorter.php b/plugins/property-sorter.php index e942f6d..a7b6ff8 100644 --- a/plugins/property-sorter.php +++ b/plugins/property-sorter.php @@ -36,16 +36,7 @@ function property_sorter(Rule $rule) { - $new_set = array(); - - // Create plain array of rule declarations. - foreach ($rule->declarations as $declaration) { - $new_set[] = $declaration; - } - - usort($new_set, 'CssCrush\property_sorter_callback'); - - $rule->declarations->reset($new_set); + usort($rule->declarations->store, 'CssCrush\property_sorter_callback'); } From 59f94546fa3a2294565acc298702eb6c389344f5 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 14 Dec 2013 19:07:24 +0000 Subject: [PATCH 212/421] Dropped CollectionInterface; overkill at this stage. --- lib/CssCrush/Collection.php | 21 ++++++++------------- lib/CssCrush/CollectionInterface.php | 10 ---------- lib/CssCrush/Iterator.php | 6 +++--- lib/CssCrush/Plugin.php | 6 +++--- plugins/forms.php | 2 +- 5 files changed, 15 insertions(+), 30 deletions(-) delete mode 100644 lib/CssCrush/CollectionInterface.php diff --git a/lib/CssCrush/Collection.php b/lib/CssCrush/Collection.php index e4f6f1f..dbaf92f 100644 --- a/lib/CssCrush/Collection.php +++ b/lib/CssCrush/Collection.php @@ -36,25 +36,21 @@ static public function value($item, $property) return $item->$property; } - public function filter() + public function filter($filterer, $op = '===') { - $args = func_get_args(); - - $assoc_array = is_array($args[0]) ? $args[0] : false; - - if ($assoc_array) { + if (is_array($filterer)) { $ops = array( - '===' => function ($item) use ($assoc_array) { - foreach ($assoc_array as $property => $value) { + '===' => function ($item) use ($filterer) { + foreach ($filterer as $property => $value) { if (Collection::value($item, $property) !== $value) { return false; } } return true; }, - '!==' => function ($item) use ($assoc_array) { - foreach ($assoc_array as $property => $value) { + '!==' => function ($item) use ($filterer) { + foreach ($filterer as $property => $value) { if (Collection::value($item, $property) === $value) { return false; } @@ -63,11 +59,10 @@ public function filter() }, ); - $op = isset($args[1]) ? $args[1] : '==='; $callback = $ops[$op]; } - elseif (is_callable($args[0])) { - $callback = $args[0]; + elseif (is_callable($filterer)) { + $callback = $filterer; } if (isset($callback)) { diff --git a/lib/CssCrush/CollectionInterface.php b/lib/CssCrush/CollectionInterface.php deleted file mode 100644 index 32f6015..0000000 --- a/lib/CssCrush/CollectionInterface.php +++ /dev/null @@ -1,10 +0,0 @@ -store); - return call_user_func_array(array($collection, __FUNCTION__), func_get_args()); + return $collection->filter($filterer, $op); } } diff --git a/lib/CssCrush/Plugin.php b/lib/CssCrush/Plugin.php index 6236648..a2e1f46 100644 --- a/lib/CssCrush/Plugin.php +++ b/lib/CssCrush/Plugin.php @@ -1,14 +1,14 @@ function ($args) { $types = array(); foreach ($args as $type) { - $types[] = "[type=\"$type\"]"; + $types[] = "[type=$type]"; } $result = $types ? 'input:any(' . implode(',', $types) . ')' : 'input[type="text"]'; From 64cad63b431dcf89a882ac0622078bdd0d30c980 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 18 Dec 2013 09:26:22 +0000 Subject: [PATCH 213/421] Removing ms gradient aliases --- aliases.ini | 4 ---- 1 file changed, 4 deletions(-) diff --git a/aliases.ini b/aliases.ini index 12ddda6..434141f 100644 --- a/aliases.ini +++ b/aliases.ini @@ -322,21 +322,17 @@ ; Gradients. linear-gradient[] = -webkit-linear-gradient linear-gradient[] = -moz-linear-gradient - linear-gradient[] = -ms-linear-gradient linear-gradient[] = -o-linear-gradient radial-gradient[] = -webkit-radial-gradient radial-gradient[] = -moz-radial-gradient - radial-gradient[] = -ms-radial-gradient radial-gradient[] = -o-radial-gradient ; Repeating gradients. repeating-linear-gradient[] = -webkit-repeating-linear-gradient repeating-linear-gradient[] = -moz-repeating-linear-gradient - repeating-linear-gradient[] = -ms-repeating-linear-gradient repeating-linear-gradient[] = -o-repeating-linear-gradient repeating-radial-gradient[] = -webkit-repeating-radial-gradient repeating-radial-gradient[] = -moz-repeating-radial-gradient - repeating-radial-gradient[] = -ms-repeating-radial-gradient repeating-radial-gradient[] = -o-repeating-radial-gradient From ed99c4c7e740319add31951d0dbaa91fc89586f8 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 18 Dec 2013 10:34:26 +0000 Subject: [PATCH 214/421] Refactoring out static uid variables on svg-gradients plugin to avoid testing issues. --- plugins/svg-gradients.php | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/plugins/svg-gradients.php b/plugins/svg-gradients.php index 157ccac..aa1c638 100644 --- a/plugins/svg-gradients.php +++ b/plugins/svg-gradients.php @@ -41,8 +41,11 @@ */ namespace CssCrush; +$GLOBALS['CSSCRUSH_SVG_GRADIENT_UID'] = 0; + Plugin::register('svg-gradients', array( 'enable' => function () { + $GLOBALS['CSSCRUSH_SVG_GRADIENT_UID'] = 0; Functions::register('svg-linear-gradient', 'CssCrush\fn__svg_linear_gradient'); Functions::register('svg-radial-gradient', 'CssCrush\fn__svg_radial_gradient'); }, @@ -59,7 +62,6 @@ function fn__svg_linear_gradient($input) { $gradient_markup = reset($gradient); $gradient_id = key($gradient); - // Creating the svg. $svg = ''; $svg .= ''; $svg .= $gradient_markup; @@ -67,7 +69,6 @@ function fn__svg_linear_gradient($input) { $svg .= ""; $svg .= ''; - // Create data-uri url and return token label. $url = new Url('data:image/svg+xml;base64,' . base64_encode($svg)); return $url->label; @@ -80,7 +81,6 @@ function fn__svg_radial_gradient($input) { $gradient_markup = reset($gradient); $gradient_id = key($gradient); - // Creating the svg. $svg = ''; $svg .= ''; $svg .= $gradient_markup; @@ -88,7 +88,6 @@ function fn__svg_radial_gradient($input) { $svg .= ""; $svg .= ''; - // Create data-uri url and return token label. $url = new Url('data:image/svg+xml;base64,' . base64_encode($svg)); return $url->label; @@ -213,8 +212,7 @@ function create_svg_linear_gradient($input) { $color_stops = parse_gradient_color_stops($args); // Create the gradient markup with a unique id. - static $uid = 0; - $uid++; + $uid = ++$GLOBALS['CSSCRUSH_SVG_GRADIENT_UID']; $gradient_id = "lg$uid"; $gradient = ""; @@ -280,8 +278,7 @@ function create_svg_radial_gradient($input) { $color_stops = parse_gradient_color_stops($args); // Create the gradient markup with a unique id. - static $uid = 0; - $uid++; + $uid = ++$GLOBALS['CSSCRUSH_SVG_GRADIENT_UID']; $gradient_id = "rg$uid"; $gradient = ""; From 440bfd6e011095c42ebf434e9f33b66eaae8035d Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 18 Dec 2013 12:25:01 +0000 Subject: [PATCH 215/421] Moving selector minification into selector toString. --- lib/CssCrush/Process.php | 8 +------- lib/CssCrush/Selector.php | 6 +++++- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 78ab8be..0eb02a9 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -853,13 +853,7 @@ protected function collate() $this->decruft(); - if ($minify) { - - // Trim whitespace around selector combinators. - $this->stream->pregReplace('~ ?([>\~+]) ?~S', '$1'); - } - else { - + if (! $minify) { // Add newlines after comments. foreach ($this->tokens->store->c as $token => &$comment) { $comment .= $EOL; diff --git a/lib/CssCrush/Selector.php b/lib/CssCrush/Selector.php index 8192952..f4e1013 100644 --- a/lib/CssCrush/Selector.php +++ b/lib/CssCrush/Selector.php @@ -28,7 +28,11 @@ public function __construct($raw_selector, $associated_rule = null) public function __toString() { - if (! CssCrush::$process->minifyOutput) { + if (CssCrush::$process->minifyOutput) { + // Trim whitespace around selector combinators. + $this->value = preg_replace('~ ?([>\~+]) ?~S', '$1', $this->value); + } + else { $this->value = Selector::normalizeWhiteSpace($this->value); } return $this->value; From 4e03f5259998fc860ed15d94a71426e8d3d0f081 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 19 Dec 2013 15:42:27 +0000 Subject: [PATCH 216/421] Refactoring out paren encoding on declaration values. --- lib/CssCrush/CssCrush.php | 3 +-- lib/CssCrush/Declaration.php | 14 +++-------- lib/CssCrush/PostAliasFix.php | 44 ++++++++++------------------------- lib/CssCrush/Regex.php | 6 ++--- lib/CssCrush/Tokens.php | 3 +-- plugins/hsl-to-hex.php | 13 +++++------ 6 files changed, 26 insertions(+), 57 deletions(-) diff --git a/lib/CssCrush/CssCrush.php b/lib/CssCrush/CssCrush.php index e81880c..96116cb 100644 --- a/lib/CssCrush/CssCrush.php +++ b/lib/CssCrush/CssCrush.php @@ -187,8 +187,7 @@ public static function parseAliasesFile($file) if (preg_match($regex->vendorPrefix, $alias_func, $m)) { // We'll cache the function matching regex here. - $vendor_grouped_aliases[$m[1]]['find'][] = - Regex::make('~{{LB}}' . $func_name . '{{RTB}}~i'); + $vendor_grouped_aliases[$m[1]]['find'][] = Regex::make("~{{ LB }}$func_name(?=\()~i"); $vendor_grouped_aliases[$m[1]]['replace'][] = $alias_func; } } diff --git a/lib/CssCrush/Declaration.php b/lib/CssCrush/Declaration.php index ca2159b..635863b 100644 --- a/lib/CssCrush/Declaration.php +++ b/lib/CssCrush/Declaration.php @@ -122,12 +122,9 @@ public function process($parent_rule) return; } - // Store raw value as data on the parent rule. + // Store value as data on the parent rule. $parent_rule->declarations->queryData[$this->property] = $this->value; - // Capture top-level paren pairs. - $this->value = CssCrush::$process->tokens->captureParens($this->value); - $this->indexFunctions(); } @@ -135,16 +132,11 @@ public function indexFunctions() { // Create an index of all regular functions in the value. $functions = array(); - if (preg_match_all(Regex::$patt->function, $this->value, $m)) { - foreach ($m[1] as $index => $fn_name) { + if (preg_match_all(Regex::$patt->functionTest, $this->value, $m)) { + foreach ($m['func_name'] as $index => $fn_name) { $functions[strtolower($fn_name)] = true; } } $this->functions = $functions; } - - public function getFullValue() - { - return CssCrush::$process->tokens->restore($this->value, 'p'); - } } diff --git a/lib/CssCrush/PostAliasFix.php b/lib/CssCrush/PostAliasFix.php index b00aca0..240ab1c 100644 --- a/lib/CssCrush/PostAliasFix.php +++ b/lib/CssCrush/PostAliasFix.php @@ -8,7 +8,6 @@ class PostAliasFix { - // Currently only post fixing aliased functions. public static $functions = array( ':gradients' => 'CssCrush\postalias_fix_gradients', ); @@ -16,7 +15,6 @@ class PostAliasFix public static function add($alias_type, $key, $callback) { if ($alias_type === 'function') { - // $key is the aliased css function name. self::$functions[$key] = $callback; } } @@ -24,7 +22,6 @@ public static function add($alias_type, $key, $callback) public static function remove($alias_type, $key) { if ($type === 'function') { - // $key is the aliased css function name. unset(self::$functions[$key]); } } @@ -67,8 +64,8 @@ function postalias_fix_linear_gradients($declaration_copies) { static $deg_patt, $fn_patt; if (! $deg_patt) { - $deg_patt = Regex::make('~(?<=[\( ])({{number}})deg~i'); - $fn_patt = Regex::make('~{{LB}}{{vendor}}(?:(?:repeating-)?linear-gradient)({{p-token}})~iS'); + $deg_patt = Regex::make('~(?<=[\( ])({{ number }})deg~i'); + $fn_patt = Regex::make('~{{ LB }}{{ vendor }}(?:repeating-)?linear-gradient{{ parens }}~iS'); } // Legacy angles move anti-clockwise and start from East, not North. @@ -85,27 +82,15 @@ function postalias_fix_linear_gradients($declaration_copies) { foreach (Regex::matchAll($fn_patt, $declaration_copies[0]->value) as $m) { - $original_parens[] = $m[1][0]; - $original_paren_value = CssCrush::$process->tokens->get($m[1][0]); + $original_parens[] = $m['parens'][0]; - // Convert keyword angle values. - $updated_paren_value = str_ireplace( - $angles_new, - $angles_old, - $original_paren_value - ); - - // Convert degree angle values. - $updated_paren_value = preg_replace_callback( - $deg_patt, - $deg_convert_callback, - $updated_paren_value - ); + // Keyword angle values. + $updated_paren_value = str_ireplace($angles_new, $angles_old, $m['parens'][0]); - $replacement_parens[] = CssCrush::$process->tokens->add($updated_paren_value, 'p'); + // Degree angle values. + $replacement_parens[] = preg_replace_callback($deg_patt, $deg_convert_callback, $updated_paren_value); } - // Swap in the new tokens on all the prefixed declarations. foreach ($declaration_copies as $prefixed_copy) { $prefixed_copy->value = str_replace( $original_parens, @@ -124,22 +109,17 @@ function postalias_fix_radial_gradients($declaration_copies) { // Replace the new syntax with the legacy syntax. static $fn_patt; if (! $fn_patt) { - $fn_patt = Regex::make('~{{LB}}{{vendor}}(?:(?:repeating-)?radial-gradient)({{p-token}})~iS'); + $fn_patt = Regex::make('~{{ LB }}{{ vendor }}(?:repeating-)?radial-gradient{{ parens }}~iS'); } + $original_parens = array(); $replacement_parens = array(); - foreach (Regex::matchAll($fn_patt, $declaration_copies[0]->value) as $m) { - $original_parens[] = $m[1][0]; - $replacement_parens[] = CssCrush::$process->tokens->add( - preg_replace( - '~\bat +(top|left|bottom|right|center)\b~i', - '$1', - CssCrush::$process->tokens->get($m[1][0]) - ), 'p'); + foreach (Regex::matchAll($fn_patt, $declaration_copies[0]->value) as $m) { + $original_parens[] = $m['parens'][0]; + $replacement_parens[] = preg_replace('~\bat +(top|left|bottom|right|center)\b~i', '$1', $m['parens'][0]); } - // Swap in the new tokens on all the prefixed declarations. foreach ($declaration_copies as $prefixed_copy) { $prefixed_copy->value = str_replace( $original_parens, diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php index 3c359c7..f75c4be 100644 --- a/lib/CssCrush/Regex.php +++ b/lib/CssCrush/Regex.php @@ -70,8 +70,8 @@ public static function init() $patt->abstract = Regex::make('~^@abstract \s+ (?{{ident}})~ixS'); // Functions. - $patt->function = Regex::make('~{{LB}} ({{ident}}) ({{p-token}})~xS'); - $patt->varFunction = Regex::make('~\$\( \s* ({{ident}}) \s* \)~xS'); + $patt->functionTest = Regex::make('~{{ LB }} (?{{ ident }}) \(~xS'); + $patt->varFunction = Regex::make('~\$\( \s* ({{ ident }}) \s* \)~xS'); $patt->thisFunction = Regex::makeFunctionPatt(array('this')); // Strings and comments. @@ -163,7 +163,7 @@ public static function makeFunctionPatt($list, $options = array()) $flat_list = implode('|', array_map('preg_quote', $list)); - return Regex::make("~($template{{LB}}(?:$flat_list)$question)\(~iS"); + return Regex::make("~($template{{ LB }}(?:$flat_list)$question)\(~iS"); } } diff --git a/lib/CssCrush/Tokens.php b/lib/CssCrush/Tokens.php index f9e1d57..318a63b 100644 --- a/lib/CssCrush/Tokens.php +++ b/lib/CssCrush/Tokens.php @@ -66,8 +66,7 @@ public function restore($str, $type, $release = false) { switch ($type) { case 'u': - // Currently this always releases URLs - // may need to refactor later. + // Currently this always releases URLs. $str = preg_replace_callback(Regex::$patt->u_token, function ($m) { $url = CssCrush::$process->tokens->pop($m[0]); return $url ? $url->getOriginalValue() : ''; diff --git a/plugins/hsl-to-hex.php b/plugins/hsl-to-hex.php index 2bcf232..8250c4b 100644 --- a/plugins/hsl-to-hex.php +++ b/plugins/hsl-to-hex.php @@ -3,7 +3,7 @@ * Polyfill for hsl() color values * * @before - * color: hsl( 100, 50%, 50%) + * color: hsl(100, 50%, 50%) * * @after * color: #6abf40 @@ -22,15 +22,14 @@ function hsl_to_hex(Rule $rule) { - $hsl_patt = Regex::make('~{{ LB }}hsl({{ p-token }})~i'); + $hsl_patt = Regex::make('~{{ LB }}hsl({{ parens }})~i'); foreach ($rule->declarations->filter(array('skip' => false)) as $declaration) { if (isset($declaration->functions['hsl'])) { - while (preg_match($hsl_patt, $declaration->value, $m)) { - $token = $m[1]; - $color = new Color('hsl' . CssCrush::$process->tokens->pop($token)); - $declaration->value = str_replace($m[0], $color->getHex(), $declaration->value); - } + $declaration->value = preg_replace_callback($hsl_patt, function ($m) { + $color = new Color($m[0]); + return $color->getHex(); + }, $declaration->value); } } } From 3359246699f0773d8d3209be2dcdd1ac75488501 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 19 Dec 2013 22:07:21 +0000 Subject: [PATCH 217/421] Added logging interface to composer.json for testing. Some cleanup. --- composer.json | 1 + lib/CssCrush/BalancedMatch.php | 5 ----- lib/CssCrush/CssCrush.php | 2 +- lib/CssCrush/Process.php | 10 +++++----- lib/CssCrush/Regex.php | 1 - lib/CssCrush/Selector.php | 4 ++-- 6 files changed, 9 insertions(+), 14 deletions(-) diff --git a/composer.json b/composer.json index ef4ef7e..8b2ebc9 100644 --- a/composer.json +++ b/composer.json @@ -20,6 +20,7 @@ }, "require-dev": { "phpunit/phpunit": "3.7.*@dev", + "psr/log": "1.0.*@dev", "twig/twig": "1.*" }, "bin": [ diff --git a/lib/CssCrush/BalancedMatch.php b/lib/CssCrush/BalancedMatch.php index f455fba..d62d74a 100644 --- a/lib/CssCrush/BalancedMatch.php +++ b/lib/CssCrush/BalancedMatch.php @@ -63,9 +63,4 @@ public function unWrap() { $this->stream->splice($this->inside(), $this->offset, $this->length); } - - public function nextIndexOf($needle) - { - return strpos($this->stream->raw, $needle, $this->offset); - } } diff --git a/lib/CssCrush/CssCrush.php b/lib/CssCrush/CssCrush.php index 96116cb..8cef9a7 100644 --- a/lib/CssCrush/CssCrush.php +++ b/lib/CssCrush/CssCrush.php @@ -349,7 +349,7 @@ public static function string($string, $options = null) $process->resolveContext($options->context); } else { - $process->resolveContext($process->docRoot); + $process->resolveContext(); } // Set the string on the input object. diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 0eb02a9..6bf48cc 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -98,7 +98,7 @@ public function release() ); } - public function resolveContext($input_dir, $input_file = null) + public function resolveContext($input_dir = null, $input_file = null) { if ($input_file) { $this->input->path = $input_file; @@ -110,7 +110,7 @@ public function resolveContext($input_dir, $input_file = null) $this->input->filename = null; } - $this->input->dir = $input_dir; + $this->input->dir = $input_dir ?: $this->docRoot; $this->input->dirUrl = substr($input_dir, strlen($this->docRoot)); $this->output->dir = $this->io('getOutputDir'); @@ -516,15 +516,15 @@ static protected function cb_placeVars($m) protected function resolveIfDefines() { - $matches = $this->stream->matchAll(Regex::$patt->ifDefine); + $ifdefine_patt = Regex::make('~@ifdefine \s+ (not \s+)? ({{ ident }}) \s* \{~ixS'); + + $matches = $this->stream->matchAll($ifdefine_patt); - // Move through the matches last to first. while ($match = array_pop($matches)) { $curly_match = new BalancedMatch($this->stream, $match[0][1]); if (! $curly_match->match) { - // Couldn't match the block. continue; } diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php index f75c4be..b4ede9c 100644 --- a/lib/CssCrush/Regex.php +++ b/lib/CssCrush/Regex.php @@ -64,7 +64,6 @@ public static function init() $patt->import = Regex::make('~@import \s+ ({{u-token}}) \s? ([^;]*);~ixS'); $patt->charset = Regex::make('~@charset \s+ ({{s-token}}) \s*;~ixS'); $patt->mixin = Regex::make('~@mixin \s+ (?{{ident}}) \s* {{block}}~ixS'); - $patt->ifDefine = Regex::make('~@ifdefine \s+ (not \s+)? ({{ident}}) \s* \{~ixS'); $patt->fragmentCapture = Regex::make('~@fragment \s+ (?{{ident}}) \s* {{block}}~ixS'); $patt->fragmentInvoke = Regex::make('~@fragment \s+ (?{{ident}}) {{parens}}? \s* ;~ixS'); $patt->abstract = Regex::make('~^@abstract \s+ (?{{ident}})~ixS'); diff --git a/lib/CssCrush/Selector.php b/lib/CssCrush/Selector.php index f4e1013..8c04d2b 100644 --- a/lib/CssCrush/Selector.php +++ b/lib/CssCrush/Selector.php @@ -12,7 +12,7 @@ class Selector public $readableValue; public $allowPrefix = true; - public function __construct($raw_selector, $associated_rule = null) + public function __construct($raw_selector) { // Look for rooting prefix. if (strpos($raw_selector, '^') === 0) { @@ -55,7 +55,7 @@ public static function normalizeWhiteSpace($str) return Util::normalizeWhiteSpace(preg_replace('~([>+]|\~(?!=))~S', ' $1 ', $str)); } - static function makeReadable($str) + public static function makeReadable($str) { $str = Selector::normalizeWhiteSpace($str); From 2a0eca34b9a011b08778f189835b91656d9cddf0 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 20 Dec 2013 19:40:57 +0000 Subject: [PATCH 218/421] Fixed variables in `stat_dump`. Refactored some token methods for consistency. --- lib/CssCrush/CssCrush.php | 8 +++--- lib/CssCrush/Template.php | 3 +-- lib/CssCrush/Tokens.php | 56 +++++++++------------------------------ lib/CssCrush/Url.php | 17 ------------ plugins/forms.php | 2 +- plugins/svg.php | 5 ++-- 6 files changed, 20 insertions(+), 71 deletions(-) diff --git a/lib/CssCrush/CssCrush.php b/lib/CssCrush/CssCrush.php index 8cef9a7..8df986b 100644 --- a/lib/CssCrush/CssCrush.php +++ b/lib/CssCrush/CssCrush.php @@ -428,11 +428,9 @@ public static function runStat() break; case 'vars': - $process->stat['vars'] = $process->vars; - break; - - case 'computed_vars': - $process->stat['computed_vars'] = array_map('CssCrush\Functions::executeOnString', $process->vars); + $process->stat['vars'] = array_map(function ($item) use ($process) { + return $process->tokens->restore(Functions::executeOnString($item), array('s', 'u', 'p')); + }, $process->vars); break; case 'compile_time': diff --git a/lib/CssCrush/Template.php b/lib/CssCrush/Template.php index 3a4f80a..32f688f 100644 --- a/lib/CssCrush/Template.php +++ b/lib/CssCrush/Template.php @@ -145,8 +145,7 @@ public static function tokenize($str) public static function unTokenize($str) { - $str = CssCrush::$process->tokens->restore($str, 'u', true); - $str = CssCrush::$process->tokens->restore($str, 's', true); + $str = CssCrush::$process->tokens->restore($str, array('u', 's'), true); return $str; } diff --git a/lib/CssCrush/Tokens.php b/lib/CssCrush/Tokens.php index 318a63b..8e82c65 100644 --- a/lib/CssCrush/Tokens.php +++ b/lib/CssCrush/Tokens.php @@ -62,33 +62,15 @@ public function createLabel($type) return "?$type$counter?"; } - public function restore($str, $type, $release = false) + public function restore($str, $types, $release = false) { - switch ($type) { - case 'u': - // Currently this always releases URLs. - $str = preg_replace_callback(Regex::$patt->u_token, function ($m) { - $url = CssCrush::$process->tokens->pop($m[0]); - return $url ? $url->getOriginalValue() : ''; - }, $str); - break; - default: - $token_table =& $this->store->{$type}; - - // Find matching tokens. - foreach (Regex::matchAll(Regex::$patt->{"{$type}_token"}, $str) as $m) { - $label = $m[0][0]; - if (isset($token_table[$label])) { - $str = str_replace($label, $token_table[$label], $str); - if ($release) { - unset($token_table[$label]); - } - } - } - break; - } + $types = implode('', (array) $types); + $patt = Regex::make("~\?[$types]{{ token-id }}\?~S"); + $tokens = $this; - return $str; + return preg_replace_callback($patt, function ($m) use ($tokens, $release) { + return $release ? $tokens->pop($m[0]) : $tokens->get($m[0]); + }, $str); } public function capture($str, $type) @@ -98,28 +80,16 @@ public function capture($str, $type) return $this->captureUrls($str); break; case 's': - return $this->captureStrings($str); - break; + return preg_replace_callback(Regex::$patt->string, function ($m) { + return CssCrush::$process->tokens->add($m[0], 's'); + }, $str); case 'p': - return $this->captureParens($str); - break; + return preg_replace_callback(Regex::$patt->parens, function ($m) { + return CssCrush::$process->tokens->add($m[0], 'p'); + }, $str); } } - public function captureParens($str) - { - return preg_replace_callback(Regex::$patt->parens, function ($m) { - return CssCrush::$process->tokens->add($m[0], 'p'); - }, $str); - } - - public function captureStrings($str, $add_padding = false) - { - return preg_replace_callback(Regex::$patt->string, function ($m) { - return CssCrush::$process->tokens->add($m[0], 's'); - }, $str); - } - public function captureUrls($str, $add_padding = false) { $count = preg_match_all( diff --git a/lib/CssCrush/Url.php b/lib/CssCrush/Url.php index 7b20621..f1059ef 100644 --- a/lib/CssCrush/Url.php +++ b/lib/CssCrush/Url.php @@ -115,23 +115,6 @@ public function getAbsolutePath() return $path; } - public function getOriginalValue() - { - // If a data URI we assume nothing useful can be achieved - // by returning the original value so we just return the token label. - if ($this->isData) { - - return $this->label; - } - - $function = 'url'; - if ($this->convertToData) { - $function = 'data-uri'; - } - - return "$function($this->value)"; - } - public function prepend($path_fragment) { if ($this->isRelative) { diff --git a/plugins/forms.php b/plugins/forms.php index 72050f8..357104b 100644 --- a/plugins/forms.php +++ b/plugins/forms.php @@ -39,7 +39,7 @@ function forms() { } $result = $types ? 'input:any(' . implode(',', $types) . ')' : 'input[type="text"]'; - return CssCrush::$process->tokens->captureStrings($result); + return CssCrush::$process->tokens->capture($result, 's'); }, 'checkbox' => 'input[type="checkbox"]', diff --git a/plugins/svg.php b/plugins/svg.php index 573fd11..055e4ce 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -508,7 +508,7 @@ function svg_text($element) { 'text' => '', ); - $text = CssCrush::$process->tokens->restore($element->data['text'], 's'); + $text = CssCrush::$process->tokens->restore($element->data['text'], 's', true); // Remove open and close quotes. $text = substr($text, 1, strlen($text) - 2); @@ -737,8 +737,7 @@ function svg_render($element) { $styles .= $selector . '{' . implode(';', $out) . '}'; } } - $styles = CssCrush::$process->tokens->restore($styles, 'u', true); - $styles = CssCrush::$process->tokens->restore($styles, 's'); + $styles = CssCrush::$process->tokens->restore($styles, array('u', 's'), true); // Add element styles as attributes which tend to work better with svg2png converters. $attrs = Util::htmlAttributes($element->attrs + $element->styles); From 7c71dc3ce6ccf0628132a7529baca0d6cfb3a168 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 21 Dec 2013 13:30:32 +0000 Subject: [PATCH 219/421] Consolidating options. --- lib/CssCrush/CssCrush.php | 23 +-------- lib/CssCrush/Importer.php | 5 +- lib/CssCrush/Options.php | 106 +++++++++++++++++++++++++++----------- lib/CssCrush/Process.php | 103 ++++++++++++++---------------------- lib/CssCrush/Rule.php | 4 +- 5 files changed, 120 insertions(+), 121 deletions(-) diff --git a/lib/CssCrush/CssCrush.php b/lib/CssCrush/CssCrush.php index 8df986b..aea53cd 100644 --- a/lib/CssCrush/CssCrush.php +++ b/lib/CssCrush/CssCrush.php @@ -50,28 +50,7 @@ public static function init() ); self::$config->selectorAliases = array(); self::$config->plugins = array(); - - // Set option defaults, see wiki for details. - self::$config->options = new Options(array( - 'minify' => true, - 'formatter' => null, - 'versioning' => true, - 'boilerplate' => true, - 'vars' => array(), - 'cache' => true, - 'output_file' => null, - 'output_dir' => null, - 'asset_dir' => null, - 'doc_root' => null, - 'vendor_target' => 'all', - 'rewrite_import_urls' => true, - 'enable' => null, - 'disable' => null, - 'stat_dump' => false, - 'trace' => array(), - 'source_map' => false, - 'newlines' => 'use-platform', - )); + self::$config->options = new Options(); // Register stock formatters. require_once self::$dir . '/misc/formatters.php'; diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index 28d5986..19bfa5f 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -134,12 +134,11 @@ public static function hostfile() if ($input->path && $options->cache) { $process->cacheData[$process->output->filename] = array( - 'imports' => $filenames, + 'imports' => $filenames, 'datem_sum' => array_sum($mtimes) + $input->mtime, - 'options' => $options->get(), + 'options' => $options->get(), ); - // Save config changes. $process->io('saveCacheData'); } diff --git a/lib/CssCrush/Options.php b/lib/CssCrush/Options.php index 1069dce..1c5e26d 100644 --- a/lib/CssCrush/Options.php +++ b/lib/CssCrush/Options.php @@ -6,45 +6,72 @@ */ namespace CssCrush; +use CssCrush\CssCrush as Crush; + class Options { - public $data = array(); - - public function __construct($properties) + protected $computedOptions = array(); + protected $inputOptions = array(); + + protected static $initialOptions = array( + 'minify' => true, + 'formatter' => null, + 'versioning' => true, + 'boilerplate' => true, + 'vars' => array(), + 'cache' => true, + 'output_file' => null, + 'output_dir' => null, + 'asset_dir' => null, + 'doc_root' => null, + 'vendor_target' => 'all', + 'rewrite_import_urls' => true, + 'enable' => null, + 'disable' => null, + 'stat_dump' => false, + 'trace' => array(), + 'source_map' => false, + 'newlines' => 'use-platform', + ); + + public function __construct(array $options = null, Options $defaults = null) { - if ($properties) { - foreach ($properties as $key => $value) { - $this->__set($key, $value); + $options = is_array($options) ? $options : self::$initialOptions; + foreach ($options as $key => $value) { + $this->__set($key, $value); + } + if ($defaults) { + foreach ($defaults->get() as $key => $value) { + if (! array_key_exists($key, $this->inputOptions)) { + $this->__set($key, $value); + } } } } public function __set($name, $value) { - $config = CssCrush::$config; + $this->inputOptions[$name] = $value; switch ($name) { - // For legacy debug option, check minify has not been set then - // flip the value and change property to minify. + // Legacy debug option. case 'debug': - if (! array_key_exists('minify', $this->data)) { + if (! array_key_exists('minify', $this->inputOptions)) { $name = 'minify'; $value = ! $value; } break; - // If trace value is truthy set to stubs. case 'trace': if (! is_array($value)) { $value = $value ? array('stubs') : array(); } break; - // Resolve a formatter callback name and check it's callable. case 'formatter': - if (is_string($value) && isset($config->formatters[$value])) { - $value = $config->formatters[$value]; + if (is_string($value) && isset(Crush::$config->formatters[$value])) { + $value = Crush::$config->formatters[$value]; } if (! is_callable($value)) { $value = null; @@ -90,38 +117,57 @@ public function __set($name, $value) } break; - // Normalize options that can be passed as strings but internally - // are used as arrays. + // Options used internally as arrays. case 'enable': case 'disable': $value = (array) $value; break; } - $this->data[$name] = $value; + $this->computedOptions[$name] = $value; } public function __get($name) { - return isset($this->data[$name]) ? $this->data[$name] : null; - } + $input_value = $this->inputOptions[$name]; - public function __isset($name) - { - return isset($this->data[$name]); + switch ($name) { + case 'newlines': + switch ($input_value) { + case 'windows': + case 'win': + return "\r\n"; + case 'unix': + return "\n"; + case 'use-platform': + default: + return PHP_EOL; + } + break; + + case 'minify': + if (isset($this->computedOptions['formatter'])) { + return false; + } + break; + + case 'formatter': + if (empty($this->inputOptions['minify'])) { + return isset($this->computedOptions['formatter']) ? + $this->computedOptions['formatter'] : 'CssCrush\fmtr_block'; + } + } + + return isset($this->computedOptions[$name]) ? $this->computedOptions[$name] : null; } - public function merge(Options $options_instance) + public function __isset($name) { - foreach ($options_instance->data as $key => $value) { - if (! array_key_exists($key, $this->data)) { - $this->__set($key, $value); - } - } + return isset($this->inputOptions[$name]); } - public function get() + public function get($computed = false) { - return $this->data; + return $computed ? $this->computedOptions : $this->inputOptions; } } diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 6bf48cc..2006cee 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -6,31 +6,18 @@ */ namespace CssCrush; +use CssCrush\CssCrush as Crush; + class Process { public function __construct($user_options = array(), $dev_options = array()) { - $config = CssCrush::$config; - - CssCrush::loadAssets(); - - $dev_options += array( - 'io_context' => 'filter' - ); + $config = Crush::$config; - // Create options instance for this process. - $this->options = new Options($user_options); + Crush::loadAssets(); - // Populate option defaults. - $this->options->merge($config->options); - - // Override the minify option if a formatter is specified. - if ($this->options->formatter) { - $this->options->minify = false; - } - - // Keep track of global vars to maintain cache integrity. - $this->options->global_vars = $config->vars; + $dev_options += array('io_context' => 'filter'); + $this->ioContext = $dev_options['io_context']; // Initialize properties. $this->cacheData = array(); @@ -56,30 +43,20 @@ public function __construct($user_options = array(), $dev_options = array()) $this->plugins = $config->plugins; $this->aliases = $config->aliases; + // Options. + $this->options = new Options($user_options, $config->options); + + // Keep track of global vars to maintain cache integrity. + $this->options->global_vars = $config->vars; + $this->docRoot = isset($this->options->doc_root) ? $this->options->doc_root : $config->docRoot; - $this->ioContext = $dev_options['io_context']; - // Shortcut commonly used options to avoid overhead with __get calls. - $this->minifyOutput = $this->options->minify; - $this->addTracingStubs = in_array('stubs', $this->options->trace); - $this->generateMap = $this->ioContext === 'file' && $this->options->source_map; - $this->ruleFormatter = $this->options->formatter; - - // Shortcut the newline option and attach it to the process. - switch ($this->options->newlines) { - case 'windows': - case 'win': - $this->newline = "\r\n"; - break; - case 'unix': - $this->newline = "\n"; - break; - case 'use-platform': - default: - // Fall through and use default (platform) newline. - $this->newline = PHP_EOL; - break; - } + // Shortcut commonly used options to avoid __get() overhead. + $this->addTracingStubs = in_array('stubs', $this->options->__get('trace')); + $this->generateMap = $this->ioContext === 'file' && $this->options->__get('source_map'); + $this->ruleFormatter = $this->options->__get('formatter'); + $this->minifyOutput = $this->options->__get('minify'); + $this->newline = $this->options->__get('newlines'); Hook::run('process_init'); } @@ -133,7 +110,7 @@ public function io($method) $args = func_get_args(); array_shift($args); - return call_user_func_array(array(CssCrush::$config->io, $method), $args); + return call_user_func_array(array(Crush::$config->io, $method), $args); } @@ -146,7 +123,7 @@ protected function getBoilerplate() $boilerplate_option = $this->options->boilerplate; if ($boilerplate_option === true) { - $file = CssCrush::$dir . '/boilerplate.txt'; + $file = Crush::$dir . '/boilerplate.txt'; } elseif (is_string($boilerplate_option)) { if (file_exists($boilerplate_option)) { @@ -179,7 +156,7 @@ protected function getBoilerplate() 'command' => $command_args, 'plugins' => implode(',', array_keys($this->plugins)), 'compile_time' => function () { - $now = microtime(true) - CssCrush::$process->stat['compile_start_time']; + $now = microtime(true) - Crush::$process->stat['compile_start_time']; return round($now, 4) . ' seconds'; }, ); @@ -214,11 +191,11 @@ protected function resolveSelectorAliases() Regex::make('~@selector-alias +\:?({{ident}}) +([^;]+) *;~iS'), function ($m) { $name = strtolower($m[1]); - CssCrush::$process->selectorAliases[$name] = new Template(Util::stripCommentTokens($m[2])); + Crush::$process->selectorAliases[$name] = new Template(Util::stripCommentTokens($m[2])); }); // Merge with global selector aliases. - $this->selectorAliases += CssCrush::$config->selectorAliases; + $this->selectorAliases += Crush::$config->selectorAliases; // Create the selector aliases pattern and store it. if ($this->selectorAliases) { @@ -230,7 +207,7 @@ function ($m) { public static function applySelectorAliases($str) { - $process = CssCrush::$process; + $process = Crush::$process; if (! $process->selectorAliases || ! preg_match($process->selectorAliasesPatt, $str)) { return $str; @@ -290,7 +267,7 @@ protected function filterAliases() // For expicit 'none' argument turn off aliases. if ('none' === $vendors) { - $this->aliases = CssCrush::$config->bareAliases; + $this->aliases = Crush::$config->bareAliases; return; } @@ -380,7 +357,7 @@ protected function filterAliases() protected function filterPlugins() { $options = $this->options; - $config = CssCrush::$config; + $config = Crush::$config; // Checking for table keys is more convenient than array searching. $disable = array_flip($options->disable); @@ -428,18 +405,18 @@ protected function captureVars() $this->stream->pregReplaceCallback($vars_patt, function ($m) { if (isset($m['name'])) { - CssCrush::$process->vars[$m['name']] = $m['value']; + Crush::$process->vars[$m['name']] = $m['value']; } else { - CssCrush::$process->vars = DeclarationList::parse($m['block_content'], array( + Crush::$process->vars = DeclarationList::parse($m['block_content'], array( 'keyed' => true, 'ignore_directives' => true, - )) + CssCrush::$process->vars; + )) + Crush::$process->vars; } }); // In-file variables override global variables. - $this->vars += CssCrush::$config->vars; + $this->vars += Crush::$config->vars; // Runtime variables override in-file variables. if (! empty($this->options->vars)) { @@ -489,8 +466,8 @@ static protected function placeVars(&$value) $value = Functions::executeOnString($value, '~(\$)\(~', array('$' => function ($raw_args) { list($name, $default_value) = Functions::parseArgsSimple($raw_args); - if (isset(CssCrush::$process->vars[$name])) { - return CssCrush::$process->vars[$name]; + if (isset(Crush::$process->vars[$name])) { + return Crush::$process->vars[$name]; } else { return $default_value; @@ -505,8 +482,8 @@ static protected function placeVars(&$value) static protected function cb_placeVars($m) { $var_name = $m[1]; - if (isset(CssCrush::$process->vars[$var_name])) { - return CssCrush::$process->vars[$var_name]; + if (isset(Crush::$process->vars[$var_name])) { + return Crush::$process->vars[$var_name]; } } @@ -550,7 +527,7 @@ protected function resolveIfDefines() protected function captureMixins() { $this->stream->pregReplaceCallback(Regex::$patt->mixin, function ($m) { - CssCrush::$process->mixins[$m['name']] = new Mixin($m['block_content']); + Crush::$process->mixins[$m['name']] = new Mixin($m['block_content']); }); } @@ -560,7 +537,7 @@ protected function captureMixins() protected function resolveFragments() { - $fragments =& CssCrush::$process->fragments; + $fragments =& Crush::$process->fragments; $this->stream->pregReplaceCallback(Regex::$patt->fragmentCapture, function ($m) use (&$fragments) { $fragments[$m['name']] = new Fragment( @@ -604,7 +581,7 @@ public function captureRules() // Store rules if they have declarations or extend arguments. if (! empty($rule->declarations->store) || $rule->extendArgs) { - CssCrush::$process->tokens->add($rule, 'r', $rule->label); + Crush::$process->tokens->add($rule, 'r', $rule->label); // If only using extend still return a label. return $rule->label; @@ -655,7 +632,7 @@ protected function processRules() protected function resolveInBlocks() { $matches = $this->stream->matchAll('~@in\s+([^{]+)\{~iS'); - $tokens = CssCrush::$process->tokens; + $tokens = Crush::$process->tokens; // Move through the matches in reverse order. while ($match = array_pop($matches)) { @@ -839,7 +816,7 @@ protected function collate() $this->stream->replaceTokens('r'); // Record stats then drop rule objects to reclaim memory. - CssCrush::runStat('selector_count', 'rule_count', 'vars', 'computed_vars'); + Crush::runStat('selector_count', 'rule_count', 'vars', 'computed_vars'); $this->tokens->store->r = array(); $this->stream->replaceTokens('p'); @@ -964,7 +941,7 @@ public function compile() $this->release(); - CssCrush::runStat('compile_time'); + Crush::runStat('compile_time'); return $this->stream; } diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php index bc5f1d4..f345ffb 100644 --- a/lib/CssCrush/Rule.php +++ b/lib/CssCrush/Rule.php @@ -107,13 +107,11 @@ public function __toString() $stub = $this->marker; - // Concat and return. if ($process->minifyOutput) { return "$stub{$this->selectors->join()}{{$this->declarations->join()}}"; } else { - $formatter = $process->ruleFormatter ? $process->ruleFormatter : 'CssCrush\fmtr_block'; - + $formatter = $process->ruleFormatter; return "$stub{$formatter($this)}"; } } From a87930ee5d579672e7a4e58553087366e546af23 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 21 Dec 2013 13:49:42 +0000 Subject: [PATCH 220/421] Decoupled Url objects. --- lib/CssCrush/CssCrush.php | 6 +++--- lib/CssCrush/IO.php | 30 ++++++++++++++++-------------- lib/CssCrush/Tokens.php | 9 ++++----- lib/CssCrush/Url.php | 10 +++------- plugins/canvas.php | 6 ++++-- plugins/noise.php | 5 +---- plugins/svg-gradients.php | 8 ++------ plugins/svg.php | 5 +++-- 8 files changed, 36 insertions(+), 43 deletions(-) diff --git a/lib/CssCrush/CssCrush.php b/lib/CssCrush/CssCrush.php index aea53cd..f1aa27d 100644 --- a/lib/CssCrush/CssCrush.php +++ b/lib/CssCrush/CssCrush.php @@ -269,7 +269,7 @@ public static function tag($file, $options = null, $tag_attributes = array()) */ public static function inline($file, $options = null, $tag_attributes = array()) { - // For inline output set boilerplate to not display by default + // For inline output set boilerplate to not display by default. if (! is_array($options)) { $options = array(); } @@ -281,7 +281,7 @@ public static function inline($file, $options = null, $tag_attributes = array()) if (! empty($file)) { - // On success fetch the CSS text + // On success fetch the CSS text. $content = file_get_contents(self::$process->output->dir . '/' . self::$process->output->filename); $tag_open = ''; $tag_close = ''; @@ -295,7 +295,7 @@ public static function inline($file, $options = null, $tag_attributes = array()) } else { - // Return an HTML comment with message on failure + // Return an HTML comment with message on failure. $class = __CLASS__; $errors = implode("\n", self::$process->errors); return "\n"; diff --git a/lib/CssCrush/IO.php b/lib/CssCrush/IO.php index ea93352..85c4c71 100644 --- a/lib/CssCrush/IO.php +++ b/lib/CssCrush/IO.php @@ -6,17 +6,19 @@ */ namespace CssCrush; +use CssCrush\CssCrush as Crush; + class IO { public static function init() { - $process = CssCrush::$process; + $process = Crush::$process; $process->cacheFile = "{$process->output->dir}/.csscrush"; } public static function getOutputDir() { - $process = CssCrush::$process; + $process = Crush::$process; $output_dir = $process->options->output_dir; return $output_dir ? $output_dir : $process->input->dir; @@ -24,7 +26,7 @@ public static function getOutputDir() public static function testOutputDir() { - $dir = CssCrush::$process->output->dir; + $dir = Crush::$process->output->dir; $pathtest = true; if (! file_exists($dir)) { @@ -49,7 +51,7 @@ public static function testOutputDir() public static function getOutputFileName() { - $process = CssCrush::$process; + $process = Crush::$process; $options = $process->options; $output_basename = basename($process->input->filename, '.css'); @@ -63,16 +65,16 @@ public static function getOutputFileName() public static function getOutputUrl() { - $process = CssCrush::$process; + $process = Crush::$process; $options = $process->options; $filename = $process->output->filename; $url = $process->output->dirUrl . '/' . $filename; // Make URL relative if the input path was relative. - $input_path = new Url($process->input->raw, array('standalone' => true)); + $input_path = new Url($process->input->raw); if ($input_path->isRelative) { - $url = Util::getLinkBetweenPaths(CssCrush::$config->scriptDir, $process->output->dir) . $filename; + $url = Util::getLinkBetweenPaths(Crush::$config->scriptDir, $process->output->dir) . $filename; } // Optional query-string timestamp. @@ -91,8 +93,8 @@ public static function getOutputUrl() public static function validateCache() { - $process = CssCrush::$process; - $config = CssCrush::$config; + $process = Crush::$process; + $config = Crush::$config; $options = $process->options; $input = $process->input; $output = $process->output; @@ -164,9 +166,9 @@ public static function validateCache() public static function getCacheData() { - $config = CssCrush::$config; + $config = Crush::$config; $logger = $config->logger; - $process = CssCrush::$process; + $process = Crush::$process; if ( file_exists($process->cacheFile) && @@ -206,8 +208,8 @@ public static function getCacheData() public static function saveCacheData() { - $process = CssCrush::$process; - $logger = CssCrush::$config->logger; + $process = Crush::$process; + $logger = Crush::$config->logger; debug('Saving config.'); @@ -217,7 +219,7 @@ public static function saveCacheData() public static function write(Stream $stream) { - $process = CssCrush::$process; + $process = Crush::$process; $output = $process->output; $source_map_filename = "$output->filename.map"; diff --git a/lib/CssCrush/Tokens.php b/lib/CssCrush/Tokens.php index 8e82c65..94f6328 100644 --- a/lib/CssCrush/Tokens.php +++ b/lib/CssCrush/Tokens.php @@ -106,10 +106,8 @@ public function captureUrls($str, $add_padding = false) // @import directive. if ($import_offset !== -1) { - $url = new Url(trim($import_text)); - $str = str_replace( - $import_text, - $add_padding ? str_pad($url->label, strlen($import_text)) : $url->label, $str); + $label = $this->add(new Url(trim($import_text)), 'u'); + $str = str_replace($import_text, $add_padding ? str_pad($label, strlen($import_text)) : $label, $str); } // A URL function. @@ -119,9 +117,10 @@ public function captureUrls($str, $add_padding = false) $url = new Url(trim($m['parens_content'][$count][0])); $url->convertToData = 'data-uri' === $func_name; + $label = $this->add($url, 'u'); $str = substr_replace( $str, - $add_padding ? Tokens::pad($url->label, $full_text) : $url->label, + $add_padding ? Tokens::pad($label, $full_text) : $label, $full_offset, strlen($full_text)); } diff --git a/lib/CssCrush/Url.php b/lib/CssCrush/Url.php index f1059ef..8890c82 100644 --- a/lib/CssCrush/Url.php +++ b/lib/CssCrush/Url.php @@ -20,15 +20,11 @@ class Url public $value; public $label; - public function __construct($raw_value, $options = array()) + public function __construct($raw_value) { - $standalone = ! empty($options['standalone']); - if (! $standalone) { - $tokens = CssCrush::$process->tokens; - $this->label = $tokens->add($this, 'u'); - } + $tokens = CssCrush::$process->tokens; - if (! $standalone && preg_match(Regex::$patt->s_token, $raw_value)) { + if (preg_match(Regex::$patt->s_token, $raw_value)) { $this->value = trim($tokens->get($raw_value), '\'"'); $tokens->release($raw_value); } diff --git a/plugins/canvas.php b/plugins/canvas.php index b5a6625..4ebdf3a 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -256,10 +256,12 @@ function canvas_generator($input, $context) { $url = new Url('data:image/png;base64,' . base64_encode($data)); } + $label = $process->tokens->add($url, 'u'); + // Cache the output URL. - $process->misc->canvas_cache[$cache_key] = $url->label; + $process->misc->canvas_cache[$cache_key] = $label; - return $url->label; + return $label; } diff --git a/plugins/noise.php b/plugins/noise.php index 8ca918c..231174c 100644 --- a/plugins/noise.php +++ b/plugins/noise.php @@ -219,8 +219,5 @@ function noise_generator($input, $defaults) { $svg .= ""; $svg .= ''; - // Create data-uri url and return token label. - $url = new Url('data:image/svg+xml;base64,' . base64_encode($svg)); - - return $url->label; + return CssCrush::$process->tokens->add(new Url('data:image/svg+xml;base64,' . base64_encode($svg)), 'u'); } diff --git a/plugins/svg-gradients.php b/plugins/svg-gradients.php index aa1c638..a6a9840 100644 --- a/plugins/svg-gradients.php +++ b/plugins/svg-gradients.php @@ -69,9 +69,7 @@ function fn__svg_linear_gradient($input) { $svg .= ""; $svg .= ''; - $url = new Url('data:image/svg+xml;base64,' . base64_encode($svg)); - - return $url->label; + return CssCrush::$process->tokens->add(new Url('data:image/svg+xml;base64,' . base64_encode($svg)), 'u'); } @@ -88,9 +86,7 @@ function fn__svg_radial_gradient($input) { $svg .= ""; $svg .= ''; - $url = new Url('data:image/svg+xml;base64,' . base64_encode($svg)); - - return $url->label; + return CssCrush::$process->tokens->add(new Url('data:image/svg+xml;base64,' . base64_encode($svg)), 'u'); } diff --git a/plugins/svg.php b/plugins/svg.php index 055e4ce..7577bcc 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -298,9 +298,10 @@ function svg_generator($input, $fn_name) { } // Cache the output URL. - $process->misc->svg_cache[$cache_key] = $url->label; + $label = $process->tokens->add($url, 'u'); + $process->misc->svg_cache[$cache_key] = $label; - return $url->label; + return $label; } From 49d458189ee06e2e50383decc2823ee86515574f Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 21 Dec 2013 14:05:52 +0000 Subject: [PATCH 221/421] Added some object detection to `Tokens::add()` for convenience. --- lib/CssCrush/Process.php | 2 +- lib/CssCrush/Tokens.php | 16 +++++++++------- plugins/canvas.php | 2 +- plugins/noise.php | 2 +- plugins/svg-gradients.php | 4 ++-- plugins/svg.php | 2 +- 6 files changed, 15 insertions(+), 13 deletions(-) diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 2006cee..84406c3 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -756,7 +756,7 @@ protected function aliasAtRules() $clone_rule->vendorContext = $vendor; // Store the clone. - $replacements[] = $this->tokens->add($clone_rule, 'r'); + $replacements[] = $this->tokens->add($clone_rule); } // Finally replace the original labels with the cloned rule labels. diff --git a/lib/CssCrush/Tokens.php b/lib/CssCrush/Tokens.php index 94f6328..591c749 100644 --- a/lib/CssCrush/Tokens.php +++ b/lib/CssCrush/Tokens.php @@ -49,8 +49,14 @@ public function release($label) unset($this->store->{$label[1]}[$label]); } - public function add($value, $type, $existing_label = null) + public function add($value, $type = null, $existing_label = null) { + if ($value instanceof Url) { + $type = 'u'; + } + elseif ($value instanceof Rule) { + $type = 'r'; + } $label = $existing_label ? $existing_label : $this->createLabel($type); $this->store->{$type}[$label] = $value; return $label; @@ -106,18 +112,17 @@ public function captureUrls($str, $add_padding = false) // @import directive. if ($import_offset !== -1) { - $label = $this->add(new Url(trim($import_text)), 'u'); + $label = $this->add(new Url(trim($import_text))); $str = str_replace($import_text, $add_padding ? str_pad($label, strlen($import_text)) : $label, $str); } // A URL function. else { - $func_name = strtolower($m['func'][$count][0]); $url = new Url(trim($m['parens_content'][$count][0])); $url->convertToData = 'data-uri' === $func_name; - $label = $this->add($url, 'u'); + $label = $this->add($url); $str = substr_replace( $str, $add_padding ? Tokens::pad($label, $full_text) : $label, @@ -132,12 +137,9 @@ public function captureUrls($str, $add_padding = false) public static function pad($label, $replaced_text) { // Padding token labels to maintain whitespace and newlines. - - // Match contains newlines. if (($last_newline_pos = strrpos($replaced_text, "\n")) !== false) { $label .= str_repeat("\n", substr_count($replaced_text, "\n")) . str_repeat(' ', strlen(substr($replaced_text, $last_newline_pos))-1); } - // Match contains no newlines. else { $label = str_pad($label, strlen($replaced_text)); } diff --git a/plugins/canvas.php b/plugins/canvas.php index 4ebdf3a..e4804c5 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -256,7 +256,7 @@ function canvas_generator($input, $context) { $url = new Url('data:image/png;base64,' . base64_encode($data)); } - $label = $process->tokens->add($url, 'u'); + $label = $process->tokens->add($url); // Cache the output URL. $process->misc->canvas_cache[$cache_key] = $label; diff --git a/plugins/noise.php b/plugins/noise.php index 231174c..f0385e2 100644 --- a/plugins/noise.php +++ b/plugins/noise.php @@ -219,5 +219,5 @@ function noise_generator($input, $defaults) { $svg .= ""; $svg .= ''; - return CssCrush::$process->tokens->add(new Url('data:image/svg+xml;base64,' . base64_encode($svg)), 'u'); + return CssCrush::$process->tokens->add(new Url('data:image/svg+xml;base64,' . base64_encode($svg))); } diff --git a/plugins/svg-gradients.php b/plugins/svg-gradients.php index a6a9840..557ad5d 100644 --- a/plugins/svg-gradients.php +++ b/plugins/svg-gradients.php @@ -69,7 +69,7 @@ function fn__svg_linear_gradient($input) { $svg .= ""; $svg .= ''; - return CssCrush::$process->tokens->add(new Url('data:image/svg+xml;base64,' . base64_encode($svg)), 'u'); + return CssCrush::$process->tokens->add(new Url('data:image/svg+xml;base64,' . base64_encode($svg))); } @@ -86,7 +86,7 @@ function fn__svg_radial_gradient($input) { $svg .= ""; $svg .= ''; - return CssCrush::$process->tokens->add(new Url('data:image/svg+xml;base64,' . base64_encode($svg)), 'u'); + return CssCrush::$process->tokens->add(new Url('data:image/svg+xml;base64,' . base64_encode($svg))); } diff --git a/plugins/svg.php b/plugins/svg.php index 7577bcc..b925e65 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -298,7 +298,7 @@ function svg_generator($input, $fn_name) { } // Cache the output URL. - $label = $process->tokens->add($url, 'u'); + $label = $process->tokens->add($url); $process->misc->svg_cache[$cache_key] = $label; return $label; From ded60131d1b6ff6c52cef55df9c8a1dcdf25386c Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 21 Dec 2013 19:07:17 +0000 Subject: [PATCH 222/421] Renaming CssCrush\CssCrush => CssCrush\Crush with a class alias to the old name for BC compatibility. --- lib/CssCrush/{CssCrush.php => Crush.php} | 22 +++++++++++----------- lib/CssCrush/Declaration.php | 2 +- lib/CssCrush/DeclarationList.php | 8 ++++---- lib/CssCrush/Fragment.php | 2 +- lib/CssCrush/Functions.php | 2 +- lib/CssCrush/IO.php | 2 -- lib/CssCrush/IO/Watch.php | 8 ++++---- lib/CssCrush/Importer.php | 16 ++++++++-------- lib/CssCrush/Logger.php | 10 +++++----- lib/CssCrush/Mixin.php | 2 +- lib/CssCrush/Options.php | 2 -- lib/CssCrush/Plugin.php | 4 ++-- lib/CssCrush/Process.php | 2 -- lib/CssCrush/Rule.php | 6 +++--- lib/CssCrush/Selector.php | 4 ++-- lib/CssCrush/Stream.php | 2 +- lib/CssCrush/Template.php | 6 +++--- lib/CssCrush/Tokens.php | 4 ++-- lib/CssCrush/Url.php | 8 ++++---- lib/CssCrush/Util.php | 6 +++--- lib/CssCrush/Version.php | 4 ++-- lib/functions.php | 19 ++++++++++--------- misc/formatters.php | 8 ++++---- plugins/aria.php | 4 ++-- plugins/canvas.php | 8 ++++---- plugins/forms.php | 6 +++--- plugins/hocus-pocus.php | 8 ++++---- plugins/legacy-flexbox.php | 16 ++++++++-------- plugins/loop.php | 2 +- plugins/noise.php | 2 +- plugins/property-sorter.php | 2 +- plugins/px2em.php | 8 ++++---- plugins/rem.php | 2 +- plugins/svg-gradients.php | 4 ++-- plugins/svg.php | 12 ++++++------ 35 files changed, 109 insertions(+), 114 deletions(-) rename lib/CssCrush/{CssCrush.php => Crush.php} (96%) diff --git a/lib/CssCrush/CssCrush.php b/lib/CssCrush/Crush.php similarity index 96% rename from lib/CssCrush/CssCrush.php rename to lib/CssCrush/Crush.php index f1aa27d..c32d750 100644 --- a/lib/CssCrush/CssCrush.php +++ b/lib/CssCrush/Crush.php @@ -6,7 +6,7 @@ */ namespace CssCrush; -class CssCrush +class Crush { const VERSION = '2.1.0-beta'; @@ -211,7 +211,7 @@ public static function file($file, $options = null) return ''; } - CssCrush::runStat('hostfile'); + Crush::runStat('hostfile'); if ($options->cache) { $process->cacheData = $process->io('getCacheData'); @@ -347,7 +347,7 @@ public static function string($string, $options = null) */ public static function stat() { - $process = CssCrush::$process; + $process = Crush::$process; $stats = $process->stat; // Get logged errors as late as possible. @@ -365,12 +365,12 @@ public static function stat() public static function addSelectorAlias($name, $body) { - CssCrush::$config->selectorAliases[$name] = is_callable($body) ? $body : new Template($body); + Crush::$config->selectorAliases[$name] = is_callable($body) ? $body : new Template($body); } public static function removeSelectorAlias($name) { - unset(CssCrush::$config->selectorAliases[$name]); + unset(Crush::$config->selectorAliases[$name]); } @@ -396,7 +396,7 @@ public static function printLog() public static function runStat() { - $process = CssCrush::$process; + $process = Crush::$process; $all_rules =& $process->tokens->store->r; foreach (func_get_args() as $stat_name) { @@ -434,20 +434,20 @@ public static function runStat() function log($message, $context = array(), $type = 'debug') { - CssCrush::$config->logger->{$type}($message, $context); + Crush::$config->logger->{$type}($message, $context); } function debug($message, $context = array()) { - CssCrush::$config->logger->debug($message, $context); + Crush::$config->logger->debug($message, $context); } function notice($message, $context = array()) { - CssCrush::$config->logger->notice($message, $context); + Crush::$config->logger->notice($message, $context); } function warning($message, $context = array()) { - CssCrush::$config->logger->warning($message, $context); + Crush::$config->logger->warning($message, $context); } -CssCrush::init(); +Crush::init(); diff --git a/lib/CssCrush/Declaration.php b/lib/CssCrush/Declaration.php index 635863b..a4c8900 100644 --- a/lib/CssCrush/Declaration.php +++ b/lib/CssCrush/Declaration.php @@ -65,7 +65,7 @@ public function __construct($prop, $value, $contextIndex = 0) public function __toString() { - if (CssCrush::$process->minifyOutput) { + if (Crush::$process->minifyOutput) { $whitespace = ''; } else { diff --git a/lib/CssCrush/DeclarationList.php b/lib/CssCrush/DeclarationList.php index 255b639..499b5d6 100644 --- a/lib/CssCrush/DeclarationList.php +++ b/lib/CssCrush/DeclarationList.php @@ -84,7 +84,7 @@ public function join($glue = ';') */ public function aliasProperties($vendor_context = null) { - $aliased_properties =& CssCrush::$process->aliases['properties']; + $aliased_properties =& Crush::$process->aliases['properties']; // Bail early if nothing doing. if (! array_intersect_key($aliased_properties, $this->properties)) { @@ -152,8 +152,8 @@ public function aliasProperties($vendor_context = null) public function aliasFunctions($vendor_context = null) { - $function_aliases =& CssCrush::$process->aliases['functions']; - $function_alias_groups =& CssCrush::$process->aliases['function_groups']; + $function_aliases =& Crush::$process->aliases['functions']; + $function_alias_groups =& Crush::$process->aliases['function_groups']; // The new modified set of declarations. $new_set = array(); @@ -273,7 +273,7 @@ public function aliasFunctions($vendor_context = null) public function aliasDeclarations($vendor_context = null) { - $declaration_aliases =& CssCrush::$process->aliases['declarations']; + $declaration_aliases =& Crush::$process->aliases['declarations']; // First test for the existence of any aliased properties. if (! ($intersect = array_intersect_key($declaration_aliases, $this->properties))) { diff --git a/lib/CssCrush/Fragment.php b/lib/CssCrush/Fragment.php index 395fdea..9341d09 100644 --- a/lib/CssCrush/Fragment.php +++ b/lib/CssCrush/Fragment.php @@ -24,7 +24,7 @@ public function apply(array $args = null, $str = null) while (preg_match(Regex::$patt->fragmentInvoke, $str, $m, PREG_OFFSET_CAPTURE)) { $name = strtolower($m['name'][0]); - $fragment = isset(CssCrush::$process->fragments[$name]) ? CssCrush::$process->fragments[$name] : null; + $fragment = isset(Crush::$process->fragments[$name]) ? Crush::$process->fragments[$name] : null; $replacement = ''; $start = $m[0][1]; diff --git a/lib/CssCrush/Functions.php b/lib/CssCrush/Functions.php index 6b952cc..b3203cf 100644 --- a/lib/CssCrush/Functions.php +++ b/lib/CssCrush/Functions.php @@ -270,7 +270,7 @@ function fn__query($input, $context) { } $call_property = $context->property; - $references =& CssCrush::$process->references; + $references =& Crush::$process->references; // Resolve arguments. $name = array_shift($args); diff --git a/lib/CssCrush/IO.php b/lib/CssCrush/IO.php index 85c4c71..8c6a0dc 100644 --- a/lib/CssCrush/IO.php +++ b/lib/CssCrush/IO.php @@ -6,8 +6,6 @@ */ namespace CssCrush; -use CssCrush\CssCrush as Crush; - class IO { public static function init() diff --git a/lib/CssCrush/IO/Watch.php b/lib/CssCrush/IO/Watch.php index 292b801..5662aed 100644 --- a/lib/CssCrush/IO/Watch.php +++ b/lib/CssCrush/IO/Watch.php @@ -6,7 +6,7 @@ */ namespace CssCrush\IO; -use CssCrush\CssCrush; +use CssCrush\Crush; use CssCrush\IO; class Watch extends IO @@ -15,7 +15,7 @@ class Watch extends IO public static function getOutputFileName() { - $process = CssCrush::$process; + $process = Crush::$process; $options = $process->options; $output_basename = basename($process->input->filename, '.css'); @@ -36,13 +36,13 @@ public static function getCacheData() { // Clear results from earlier processes. clearstatcache(); - CssCrush::$process->cacheData = array(); + Crush::$process->cacheData = array(); return self::$cacheData; } public static function saveCacheData() { - self::$cacheData = CssCrush::$process->cacheData; + self::$cacheData = Crush::$process->cacheData; } } diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index 19bfa5f..1ecbcd2 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -10,8 +10,8 @@ class Importer { public static function hostfile() { - $config = CssCrush::$config; - $process = CssCrush::$process; + $config = Crush::$config; + $process = Crush::$process; $options = $process->options; $regex = Regex::$patt; $input = $process->input; @@ -148,7 +148,7 @@ public static function hostfile() static protected function rewriteImportedUrls($import) { $link = Util::getLinkBetweenPaths( - CssCrush::$process->input->dir, dirname($import->path)); + Crush::$process->input->dir, dirname($import->path)); if (empty($link)) { @@ -160,7 +160,7 @@ static protected function rewriteImportedUrls($import) foreach ($matches[0] as $token) { - $url = CssCrush::$process->tokens->get($token); + $url = Crush::$process->tokens->get($token); if ($url->isRelative) { // Prepend the relative url prefix. @@ -172,7 +172,7 @@ static protected function rewriteImportedUrls($import) static protected function prepareForStream(&$str) { $regex = Regex::$patt; - $process = CssCrush::$process; + $process = Crush::$process; $tokens = $process->tokens; // Convert all end-of-lines to unix style. @@ -214,7 +214,7 @@ static protected function checkSyntax(&$str) { // Catch obvious typing errors. $errors = false; - $current_file = 'file://' . end(CssCrush::$process->sources); + $current_file = 'file://' . end(Crush::$process->sources); $balanced_parens = substr_count($str, "(") === substr_count($str, ")"); $balanced_curlies = substr_count($str, "{") === substr_count($str, "}"); @@ -272,7 +272,7 @@ static protected function checkSyntax(&$str) static protected function addMarkers(&$str) { - $process = CssCrush::$process; + $process = Crush::$process; $current_file_index = count($process->sources) -1; $count = preg_match_all(Regex::$patt->ruleFirstPass, $str, $matches, PREG_OFFSET_CAPTURE); @@ -307,7 +307,7 @@ static protected function captureCommentAndString($str) $callback = function ($m) { $full_match = $m[0]; - $process = CssCrush::$process; + $process = Crush::$process; if (strpos($full_match, '/*') === 0) { diff --git a/lib/CssCrush/Logger.php b/lib/CssCrush/Logger.php index fa38fd9..e13c765 100644 --- a/lib/CssCrush/Logger.php +++ b/lib/CssCrush/Logger.php @@ -59,7 +59,7 @@ public function critical($message, array $context = array()) */ public function error($message, array $context = array()) { - CssCrush::$process->errors[] = $message; + Crush::$process->errors[] = $message; trigger_error($message, E_USER_ERROR); } @@ -75,7 +75,7 @@ public function error($message, array $context = array()) */ public function warning($message, array $context = array()) { - CssCrush::$process->errors[] = $message; + Crush::$process->errors[] = $message; trigger_error($message, E_USER_WARNING); } @@ -88,7 +88,7 @@ public function warning($message, array $context = array()) */ public function notice($message, array $context = array()) { - CssCrush::$process->errors[] = $message; + Crush::$process->errors[] = $message; trigger_error($message, E_USER_NOTICE); } @@ -123,12 +123,12 @@ public function debug($message, array $context = array()) } if (is_string($message)) { - CssCrush::$process->debugLog[] = "$label$message"; + Crush::$process->debugLog[] = "$label$message"; } else { ob_start(); ! empty($context['var_dump']) ? var_dump($message) : print_r($message); - CssCrush::$process->debugLog[] = $label . ob_get_clean(); + Crush::$process->debugLog[] = $label . ob_get_clean(); } } diff --git a/lib/CssCrush/Mixin.php b/lib/CssCrush/Mixin.php index 87ddb93..16bb5a8 100644 --- a/lib/CssCrush/Mixin.php +++ b/lib/CssCrush/Mixin.php @@ -17,7 +17,7 @@ public function __construct($block) public static function call($message, $context = null) { - $process = CssCrush::$process; + $process = Crush::$process; $mixable = null; $message = trim($message); diff --git a/lib/CssCrush/Options.php b/lib/CssCrush/Options.php index 1c5e26d..73f5db3 100644 --- a/lib/CssCrush/Options.php +++ b/lib/CssCrush/Options.php @@ -6,8 +6,6 @@ */ namespace CssCrush; -use CssCrush\CssCrush as Crush; - class Options { protected $computedOptions = array(); diff --git a/lib/CssCrush/Plugin.php b/lib/CssCrush/Plugin.php index a2e1f46..f5d8e6d 100644 --- a/lib/CssCrush/Plugin.php +++ b/lib/CssCrush/Plugin.php @@ -12,7 +12,7 @@ class Plugin public static function info() { - $plugin_dirs = CssCrush::$config->pluginDirs; + $plugin_dirs = Crush::$config->pluginDirs; $plugin_data = array(); foreach ($plugin_dirs as $plugin_dir) { @@ -57,7 +57,7 @@ public static function load($plugin_name) $found = false; // Loop plugin_dirs to find the plugin. - foreach (CssCrush::$config->pluginDirs as $plugin_dir) { + foreach (Crush::$config->pluginDirs as $plugin_dir) { $path = "$plugin_dir/$plugin_name.php"; if (file_exists($path)) { diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 84406c3..8b1e13f 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -6,8 +6,6 @@ */ namespace CssCrush; -use CssCrush\CssCrush as Crush; - class Process { public function __construct($user_options = array(), $dev_options = array()) diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php index f345ffb..a56d0f8 100644 --- a/lib/CssCrush/Rule.php +++ b/lib/CssCrush/Rule.php @@ -24,7 +24,7 @@ class Rule public function __construct($selector_string, $declarations_string, $trace_token = null) { - $process = CssCrush::$process; + $process = Crush::$process; $this->label = $process->tokens->createLabel('r'); $this->marker = $process->addTracingStubs || $process->generateMap ? $trace_token : null; $this->selectors = new SelectorList(); @@ -91,7 +91,7 @@ public function __construct($selector_string, $declarations_string, $trace_token public function __toString() { - $process = CssCrush::$process; + $process = Crush::$process; // Merge the extend selectors. $this->selectors->store += $this->extendSelectors; @@ -144,7 +144,7 @@ public function resolveExtendables() } elseif (! $this->resolvedExtendables) { - $references =& CssCrush::$process->references; + $references =& Crush::$process->references; // Filter the extendArgs list to usable references. $filtered = array(); diff --git a/lib/CssCrush/Selector.php b/lib/CssCrush/Selector.php index 8c04d2b..75de3bd 100644 --- a/lib/CssCrush/Selector.php +++ b/lib/CssCrush/Selector.php @@ -28,7 +28,7 @@ public function __construct($raw_selector) public function __toString() { - if (CssCrush::$process->minifyOutput) { + if (Crush::$process->minifyOutput) { // Trim whitespace around selector combinators. $this->value = preg_replace('~ ?([>\~+]) ?~S', '$1', $this->value); } @@ -61,7 +61,7 @@ public static function makeReadable($str) // Quick test for string tokens. if (strpos($str, '?s') !== false) { - $str = CssCrush::$process->tokens->restore($str, 's'); + $str = Crush::$process->tokens->restore($str, 's'); } return $str; diff --git a/lib/CssCrush/Stream.php b/lib/CssCrush/Stream.php index fd42df8..d6897a6 100644 --- a/lib/CssCrush/Stream.php +++ b/lib/CssCrush/Stream.php @@ -67,7 +67,7 @@ public function replaceHash($replacements) public function replaceTokens($type, $callback = null) { - $tokens =& CssCrush::$process->tokens->store->{ $type }; + $tokens =& Crush::$process->tokens->store->{ $type }; $callback = $callback ?: function ($m) use (&$tokens) { return isset($tokens[$m[0]]) ? $tokens[$m[0]] : ''; }; diff --git a/lib/CssCrush/Template.php b/lib/CssCrush/Template.php index 32f688f..c370d5f 100644 --- a/lib/CssCrush/Template.php +++ b/lib/CssCrush/Template.php @@ -137,15 +137,15 @@ public function apply(array $args = null, $str = null) public static function tokenize($str) { - $str = CssCrush::$process->tokens->capture($str, 's'); - $str = CssCrush::$process->tokens->capture($str, 'u'); + $str = Crush::$process->tokens->capture($str, 's'); + $str = Crush::$process->tokens->capture($str, 'u'); return $str; } public static function unTokenize($str) { - $str = CssCrush::$process->tokens->restore($str, array('u', 's'), true); + $str = Crush::$process->tokens->restore($str, array('u', 's'), true); return $str; } diff --git a/lib/CssCrush/Tokens.php b/lib/CssCrush/Tokens.php index 591c749..e75e3e6 100644 --- a/lib/CssCrush/Tokens.php +++ b/lib/CssCrush/Tokens.php @@ -87,11 +87,11 @@ public function capture($str, $type) break; case 's': return preg_replace_callback(Regex::$patt->string, function ($m) { - return CssCrush::$process->tokens->add($m[0], 's'); + return Crush::$process->tokens->add($m[0], 's'); }, $str); case 'p': return preg_replace_callback(Regex::$patt->parens, function ($m) { - return CssCrush::$process->tokens->add($m[0], 'p'); + return Crush::$process->tokens->add($m[0], 'p'); }, $str); } } diff --git a/lib/CssCrush/Url.php b/lib/CssCrush/Url.php index 8890c82..6e4c808 100644 --- a/lib/CssCrush/Url.php +++ b/lib/CssCrush/Url.php @@ -22,7 +22,7 @@ class Url public function __construct($raw_value) { - $tokens = CssCrush::$process->tokens; + $tokens = Crush::$process->tokens; if (preg_match(Regex::$patt->s_token, $raw_value)) { $this->value = trim($tokens->get($raw_value), '\'"'); @@ -105,7 +105,7 @@ public function getAbsolutePath() $path = $this->value; } elseif ($this->isRelative || $this->isRooted) { - $path = CssCrush::$process->docRoot . + $path = Crush::$process->docRoot . ($this->isRelative ? $this->toRoot()->simplify()->value : $this->value); } return $path; @@ -123,7 +123,7 @@ public function prepend($path_fragment) public function toRoot() { if ($this->isRelative) { - $this->prepend(CssCrush::$process->input->dirUrl . '/'); + $this->prepend(Crush::$process->input->dirUrl . '/'); $this->setType('rooted'); } @@ -135,7 +135,7 @@ public function toData() // Only make one conversion attempt. $this->convertToData = false; - $file = CssCrush::$process->docRoot . $this->toRoot()->value; + $file = Crush::$process->docRoot . $this->toRoot()->value; // File not found. if (! file_exists($file)) { diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php index a87536a..a18a4fb 100644 --- a/lib/CssCrush/Util.php +++ b/lib/CssCrush/Util.php @@ -82,7 +82,7 @@ public static function resolveUserPath($path, $recovery = null) $path = $realpath; } else { - $doc_root = isset(CssCrush::$process) ? CssCrush::$process->docRoot : CssCrush::$config->docRoot; + $doc_root = isset(Crush::$process) ? Crush::$process->docRoot : Crush::$config->docRoot; // Absolute path. if (strpos($path, '/') === 0) { @@ -93,7 +93,7 @@ public static function resolveUserPath($path, $recovery = null) } // Relative path. Try resolving based on the directory of the executing script. else { - $path = CssCrush::$config->scriptDir . '/' . $path; + $path = Crush::$config->scriptDir . '/' . $path; } if (! file_exists($path) && is_callable($recovery)) { @@ -203,7 +203,7 @@ public static function filePutContents($file, $str) public static function loadIni($library_relative_path, $sections = false) { - if (! ($ini_array = @parse_ini_file(CssCrush::$dir . '/' . $library_relative_path, $sections))) { + if (! ($ini_array = @parse_ini_file(Crush::$dir . '/' . $library_relative_path, $sections))) { notice("[[CssCrush]] - Ini file '$library_relative_path' could not be parsed."); return false; diff --git a/lib/CssCrush/Version.php b/lib/CssCrush/Version.php index 74db90c..72547e9 100644 --- a/lib/CssCrush/Version.php +++ b/lib/CssCrush/Version.php @@ -78,9 +78,9 @@ public function compare($version_string) public static function gitDescribe() { static $attempted, $version; - if (! $attempted && file_exists(CssCrush::$dir . '/.git')) { + if (! $attempted && file_exists(Crush::$dir . '/.git')) { $attempted = true; - $command = 'cd ' . escapeshellarg(CssCrush::$dir) . ' && git describe --tag --long'; + $command = 'cd ' . escapeshellarg(Crush::$dir) . ' && git describe --tag --long'; @exec($command, $lines); if ($lines) { $version = new Version(trim($lines[0])); diff --git a/lib/functions.php b/lib/functions.php index 890d823..def7dd2 100644 --- a/lib/functions.php +++ b/lib/functions.php @@ -4,33 +4,34 @@ * High level API. * */ -use CssCrush\CssCrush; +use CssCrush\Crush; +class_alias('CssCrush\Crush', 'CssCrush\CssCrush'); function csscrush_file($file, $options = null) { - return CssCrush::file($file, $options); + return Crush::file($file, $options); } function csscrush_tag($file, $options = null, $attributes = array()) { - return CssCrush::tag($file, $options, $attributes); + return Crush::tag($file, $options, $attributes); } function csscrush_inline($file, $options = null, $attributes = array()) { - return CssCrush::inline($file, $options, $attributes); + return Crush::inline($file, $options, $attributes); } function csscrush_string($string, $options = null) { - return CssCrush::string($string, $options); + return Crush::string($string, $options); } function csscrush_stat() { - return CssCrush::stat(); + return Crush::stat(); } function csscrush_version($use_git = false) { if ($use_git && $version = \CssCrush\Version::gitDescribe()) { return $version; } - return CssCrush::$config->version; + return Crush::$config->version; } /** @@ -43,7 +44,7 @@ function csscrush_set($object_name, $modifier) { if (in_array($object_name, array('options', 'config'))) { - $pointer = $object_name === 'options' ? CssCrush::$config->options : CssCrush::$config; + $pointer = $object_name === 'options' ? Crush::$config->options : Crush::$config; if (is_callable($modifier)) { call_user_func($modifier, $pointer); @@ -66,7 +67,7 @@ function csscrush_get($object_name, $property = null) { if (in_array($object_name, array('options', 'config'))) { - $pointer = $object_name === 'options' ? CssCrush::$config->options : CssCrush::$config; + $pointer = $object_name === 'options' ? Crush::$config->options : Crush::$config; if (! isset($property)) { diff --git a/misc/formatters.php b/misc/formatters.php index 58e9c1f..c65ed75 100644 --- a/misc/formatters.php +++ b/misc/formatters.php @@ -6,7 +6,7 @@ */ namespace CssCrush; -CssCrush::$config->formatters = array( +Crush::$config->formatters = array( 'single-line' => 'CssCrush\fmtr_single', 'padded' => 'CssCrush\fmtr_padded', 'block' => 'CssCrush\fmtr_block', @@ -14,7 +14,7 @@ function fmtr_single($rule) { - $EOL = CssCrush::$process->newline; + $EOL = Crush::$process->newline; $selectors = $rule->selectors->join(', '); $block = $rule->declarations->join('; '); @@ -23,7 +23,7 @@ function fmtr_single($rule) { function fmtr_padded($rule, $padding = 40) { - $EOL = CssCrush::$process->newline; + $EOL = Crush::$process->newline; $selectors = $rule->selectors->join(', '); $block = $rule->declarations->join('; '); @@ -40,7 +40,7 @@ function fmtr_padded($rule, $padding = 40) { function fmtr_block($rule, $indent = ' ') { - $EOL = CssCrush::$process->newline; + $EOL = Crush::$process->newline; $selectors = $rule->selectors->join(",$EOL"); $block = $rule->declarations->join(";$EOL$indent"); diff --git a/plugins/aria.php b/plugins/aria.php index 04d17f2..ff6b9ba 100644 --- a/plugins/aria.php +++ b/plugins/aria.php @@ -24,12 +24,12 @@ Plugin::register('aria', array( 'enable' => function () { foreach (aria() as $name => $value) { - CssCrush::addSelectorAlias($name, $value); + Crush::addSelectorAlias($name, $value); } }, 'disable' => function () { foreach (aria() as $name => $value) { - CssCrush::removeSelectorAlias($name); + Crush::removeSelectorAlias($name); } }, )); diff --git a/plugins/canvas.php b/plugins/canvas.php index e4804c5..dba12ee 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -74,7 +74,7 @@ function ($m) { $name = strtolower($m['name']); $block = $m['block_content']; if (! empty($block)) { - CssCrush::$process->misc->canvas_defs[$name] = new Template($block); + Crush::$process->misc->canvas_defs[$name] = new Template($block); } return ''; }); @@ -82,8 +82,8 @@ function ($m) { function canvas_generator($input, $context) { - $process = CssCrush::$process; - $logger = CssCrush::$config->logger; + $process = Crush::$process; + $logger = Crush::$config->logger; // Check GD requirements are met. static $requirements; @@ -489,7 +489,7 @@ function canvas_preprocess($canvas) { function canvas_fetch_src($url_token) { - if ($url_token && $url = CssCrush::$process->tokens->get($url_token)) { + if ($url_token && $url = Crush::$process->tokens->get($url_token)) { $file = $url->getAbsolutePath(); diff --git a/plugins/forms.php b/plugins/forms.php index 357104b..fa1883e 100644 --- a/plugins/forms.php +++ b/plugins/forms.php @@ -19,12 +19,12 @@ Plugin::register('forms', array( 'enable' => function () { foreach (forms() as $name => $value) { - CssCrush::addSelectorAlias($name, $value); + Crush::addSelectorAlias($name, $value); } }, 'disable' => function () { foreach (forms() as $name => $value) { - CssCrush::removeSelectorAlias($name); + Crush::removeSelectorAlias($name); } }, )); @@ -39,7 +39,7 @@ function forms() { } $result = $types ? 'input:any(' . implode(',', $types) . ')' : 'input[type="text"]'; - return CssCrush::$process->tokens->capture($result, 's'); + return Crush::$process->tokens->capture($result, 's'); }, 'checkbox' => 'input[type="checkbox"]', diff --git a/plugins/hocus-pocus.php b/plugins/hocus-pocus.php index 5722665..52f3953 100644 --- a/plugins/hocus-pocus.php +++ b/plugins/hocus-pocus.php @@ -15,11 +15,11 @@ Plugin::register('hocus-pocus', array( 'enable' => function () { - CssCrush::addSelectorAlias('hocus', ':any(:hover,:focus)'); - CssCrush::addSelectorAlias('pocus', ':any(:hover,:focus,:active)'); + Crush::addSelectorAlias('hocus', ':any(:hover,:focus)'); + Crush::addSelectorAlias('pocus', ':any(:hover,:focus,:active)'); }, 'disable' => function () { - CssCrush::removeSelectorAlias('hocus'); - CssCrush::removeSelectorAlias('pocus'); + Crush::removeSelectorAlias('hocus'); + Crush::removeSelectorAlias('pocus'); }, )); diff --git a/plugins/legacy-flexbox.php b/plugins/legacy-flexbox.php index f5a3fbc..f066fa5 100644 --- a/plugins/legacy-flexbox.php +++ b/plugins/legacy-flexbox.php @@ -95,7 +95,7 @@ function legacy_flexbox(Rule $rule) { return; } - $declaration_aliases =& CssCrush::$process->aliases['declarations']; + $declaration_aliases =& Crush::$process->aliases['declarations']; $stack = array(); $rule_updated = false; @@ -190,7 +190,7 @@ function flex_direction($value, &$stack) { // box-orient: horizontal | vertical | inline-axis | block-axis | inherit // box-direction: normal | reverse | inherit - $prop_aliases =& CssCrush::$process->aliases['properties']; + $prop_aliases =& Crush::$process->aliases['properties']; static $directions = array( 'row' => 'normal', @@ -230,7 +230,7 @@ function flex_justify_content($value, &$stack) { // justify-content: flex-start | flex-end | center | space-between | space-around // box-pack: start | end | center | justify - $prop_aliases =& CssCrush::$process->aliases['properties']; + $prop_aliases =& Crush::$process->aliases['properties']; static $positions = array( 'flex-start' => 'start', @@ -256,7 +256,7 @@ function flex_align_items($value, &$stack) { // align-items: flex-start | flex-end | center | baseline | stretch // box-align: start | end | center | baseline | stretch - $prop_aliases =& CssCrush::$process->aliases['properties']; + $prop_aliases =& Crush::$process->aliases['properties']; static $positions = array( 'flex-start' => 'start', @@ -282,7 +282,7 @@ function flex_order($value, &$stack) { // order: // box-ordinal-group: - $prop_aliases =& CssCrush::$process->aliases['properties']; + $prop_aliases =& Crush::$process->aliases['properties']; // Bump value as box-ordinal-group requires a positive integer: // http://www.w3.org/TR/2009/WD-css3-flexbox-20090723/#displayorder @@ -308,7 +308,7 @@ function flex_wrap($value, &$stack) { // flex-wrap: nowrap | wrap | wrap-reverse // box-lines: single | multiple - $prop_aliases =& CssCrush::$process->aliases['properties']; + $prop_aliases =& Crush::$process->aliases['properties']; static $wrap_behaviours = array( 'nowrap' => 'single', @@ -333,7 +333,7 @@ function flex($value, &$stack) { // flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ] // box-flex: - $prop_aliases =& CssCrush::$process->aliases['properties']; + $prop_aliases =& Crush::$process->aliases['properties']; // Normalize keyword arguments. static $keywords = array( @@ -368,7 +368,7 @@ function flex_grow($value, &$stack) { // flex-grow: // box-flex: - $prop_aliases =& CssCrush::$process->aliases['properties']; + $prop_aliases =& Crush::$process->aliases['properties']; $rule_updated = false; if (isset($prop_aliases['box-flex'])) { diff --git a/plugins/loop.php b/plugins/loop.php index 9f14873..26a0f96 100644 --- a/plugins/loop.php +++ b/plugins/loop.php @@ -68,7 +68,7 @@ function loop() { - CssCrush::$process->stream->pregReplaceCallback(LOOP_PATT, function ($m) { + Crush::$process->stream->pregReplaceCallback(LOOP_PATT, function ($m) { return Template::tokenize(loop_unroll(Template::unTokenize($m[0]))); }); diff --git a/plugins/noise.php b/plugins/noise.php index f0385e2..3a9b904 100644 --- a/plugins/noise.php +++ b/plugins/noise.php @@ -219,5 +219,5 @@ function noise_generator($input, $defaults) { $svg .= ""; $svg .= ''; - return CssCrush::$process->tokens->add(new Url('data:image/svg+xml;base64,' . base64_encode($svg))); + return Crush::$process->tokens->add(new Url('data:image/svg+xml;base64,' . base64_encode($svg))); } diff --git a/plugins/property-sorter.php b/plugins/property-sorter.php index a7b6ff8..b0fee63 100644 --- a/plugins/property-sorter.php +++ b/plugins/property-sorter.php @@ -141,7 +141,7 @@ function &property_sorter_get_table () { else { // Load from property-sorting.ini. - $sorting_file_contents = file_get_contents(CssCrush::$dir . '/misc/property-sorting.ini'); + $sorting_file_contents = file_get_contents(Crush::$dir . '/misc/property-sorting.ini'); if ($sorting_file_contents !== false) { $sorting_file_contents = preg_replace('~;[^\r\n]*~', '', $sorting_file_contents); diff --git a/plugins/px2em.php b/plugins/px2em.php index f60306c..9cd42ab 100644 --- a/plugins/px2em.php +++ b/plugins/px2em.php @@ -32,8 +32,8 @@ function fn__px2em($input) { $base = 16; // Override default base if variable is set. - if (isset(CssCrush::$process->vars['px2em__base'])) { - $base = CssCrush::$process->vars['px2em__base']; + if (isset(Crush::$process->vars['px2em__base'])) { + $base = Crush::$process->vars['px2em__base']; } return px2em($input, 'em', $base); @@ -44,8 +44,8 @@ function fn__px2rem($input) { $base = 16; // Override default base if variable is set. - if (isset(CssCrush::$process->vars['px2rem__base'])) { - $base = CssCrush::$process->vars['px2rem__base']; + if (isset(Crush::$process->vars['px2rem__base'])) { + $base = Crush::$process->vars['px2rem__base']; } return px2em($input, 'rem', $base); diff --git a/plugins/rem.php b/plugins/rem.php index 609de3e..10ddd10 100644 --- a/plugins/rem.php +++ b/plugins/rem.php @@ -59,7 +59,7 @@ function rem(Rule $rule) { $modes = array('rem-fallback', 'px-fallback', 'convert'); } - $vars =& CssCrush::$process->vars; + $vars =& Crush::$process->vars; // Determine which properties are touched; all, or just font related. $just_font_props = ! isset($vars['rem__all']); diff --git a/plugins/svg-gradients.php b/plugins/svg-gradients.php index 557ad5d..06e252f 100644 --- a/plugins/svg-gradients.php +++ b/plugins/svg-gradients.php @@ -69,7 +69,7 @@ function fn__svg_linear_gradient($input) { $svg .= ""; $svg .= ''; - return CssCrush::$process->tokens->add(new Url('data:image/svg+xml;base64,' . base64_encode($svg))); + return Crush::$process->tokens->add(new Url('data:image/svg+xml;base64,' . base64_encode($svg))); } @@ -86,7 +86,7 @@ function fn__svg_radial_gradient($input) { $svg .= ""; $svg .= ''; - return CssCrush::$process->tokens->add(new Url('data:image/svg+xml;base64,' . base64_encode($svg))); + return Crush::$process->tokens->add(new Url('data:image/svg+xml;base64,' . base64_encode($svg))); } diff --git a/plugins/svg.php b/plugins/svg.php index b925e65..cbd9947 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -92,7 +92,7 @@ function ($m) { $name = strtolower($m['name']); $block = $m['block_content']; if (! empty($block)) { - CssCrush::$process->misc->svg_defs[$name] = new Template($block); + Crush::$process->misc->svg_defs[$name] = new Template($block); } return ''; }); @@ -100,7 +100,7 @@ function ($m) { function svg_generator($input, $fn_name) { - $process = CssCrush::$process; + $process = Crush::$process; $cache_key = $fn_name . $input; if (isset($process->misc->svg_cache[$cache_key])) { @@ -509,7 +509,7 @@ function svg_text($element) { 'text' => '', ); - $text = CssCrush::$process->tokens->restore($element->data['text'], 's', true); + $text = Crush::$process->tokens->restore($element->data['text'], 's', true); // Remove open and close quotes. $text = substr($text, 1, strlen($text) - 2); @@ -653,7 +653,7 @@ function svg_preprocess($element) { if (Tokens::is($value, 's')) { $element->attrs[$point_data_attr] = - trim(CssCrush::$process->tokens->get($value), '"\'');; + trim(Crush::$process->tokens->get($value), '"\'');; } } } @@ -738,7 +738,7 @@ function svg_render($element) { $styles .= $selector . '{' . implode(';', $out) . '}'; } } - $styles = CssCrush::$process->tokens->restore($styles, array('u', 's'), true); + $styles = Crush::$process->tokens->restore($styles, array('u', 's'), true); // Add element styles as attributes which tend to work better with svg2png converters. $attrs = Util::htmlAttributes($element->attrs + $element->styles); @@ -827,7 +827,7 @@ function svg_fn_pattern($input, $element) { Functions::parseArgs($input) + array('', '', 0, 0, 0, 0); - $url = CssCrush::$process->tokens->get($url); + $url = Crush::$process->tokens->get($url); if (! $url) { return ''; } From e78ac02ce89e14728649557cfa6a0a14efbb3bfb Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 21 Dec 2013 19:30:04 +0000 Subject: [PATCH 223/421] Adding `v` prefix to reported version name. --- cli.php | 2 +- lib/CssCrush/Process.php | 2 +- lib/CssCrush/Version.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cli.php b/cli.php index 2c0c1bd..fdcc347 100755 --- a/cli.php +++ b/cli.php @@ -130,7 +130,7 @@ if ($args->version) { - stdout('v' . csscrush_version(true)); + stdout(csscrush_version(true)->__toString()); exit(STATUS_OK); } diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 8b1e13f..2f262ac 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -150,7 +150,7 @@ protected function getBoilerplate() $tags = array( 'datetime' => @date('Y-m-d H:i:s O'), 'year' => @date('Y'), - 'version' => 'v' . csscrush_version(), + 'version' => csscrush_version(), 'command' => $command_args, 'plugins' => implode(',', array_keys($this->plugins)), 'compile_time' => function () { diff --git a/lib/CssCrush/Version.php b/lib/CssCrush/Version.php index 72547e9..e140ad2 100644 --- a/lib/CssCrush/Version.php +++ b/lib/CssCrush/Version.php @@ -49,7 +49,7 @@ public function __toString() $out .= "-$this->extra"; } - return $out; + return "v$out"; } public function compare($version_string) From ac560b8cde5fdd8e0bed609fffc70d61bca94d38 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sun, 22 Dec 2013 08:59:11 +0000 Subject: [PATCH 224/421] Removing last traces of p-tokens. --- lib/CssCrush/Process.php | 4 +--- lib/CssCrush/Regex.php | 1 - lib/CssCrush/Tokens.php | 5 ----- 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 2f262ac..27cd88c 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -814,11 +814,9 @@ protected function collate() $this->stream->replaceTokens('r'); // Record stats then drop rule objects to reclaim memory. - Crush::runStat('selector_count', 'rule_count', 'vars', 'computed_vars'); + Crush::runStat('selector_count', 'rule_count', 'vars'); $this->tokens->store->r = array(); - $this->stream->replaceTokens('p'); - // If specified, apply advanced minification. if (is_array($minify)) { if (in_array('colors', $minify)) { diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php index b4ede9c..bc79d6d 100644 --- a/lib/CssCrush/Regex.php +++ b/lib/CssCrush/Regex.php @@ -32,7 +32,6 @@ public static function init() $classes->c_token = '\?c' . $classes->token_id . '\?'; // Comments. $classes->s_token = '\?s' . $classes->token_id . '\?'; // Strings. $classes->r_token = '\?r' . $classes->token_id . '\?'; // Rules. - $classes->p_token = '\?p' . $classes->token_id . '\?'; // Parens. $classes->u_token = '\?u' . $classes->token_id . '\?'; // URLs. $classes->t_token = '\?t' . $classes->token_id . '\?'; // Traces. $classes->a_token = '\?a(' . $classes->token_id . ')\?'; // Args. diff --git a/lib/CssCrush/Tokens.php b/lib/CssCrush/Tokens.php index e75e3e6..c9b3361 100644 --- a/lib/CssCrush/Tokens.php +++ b/lib/CssCrush/Tokens.php @@ -17,7 +17,6 @@ public function __construct(array $types = null) 's', // Strings 'c', // Comments 'r', // Rules - 'p', // Parens 'u', // URLs 't', // Traces ); @@ -89,10 +88,6 @@ public function capture($str, $type) return preg_replace_callback(Regex::$patt->string, function ($m) { return Crush::$process->tokens->add($m[0], 's'); }, $str); - case 'p': - return preg_replace_callback(Regex::$patt->parens, function ($m) { - return Crush::$process->tokens->add($m[0], 'p'); - }, $str); } } From dce185d18e90cf1b39b2834908d8dc1a999a6def Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sun, 22 Dec 2013 14:14:19 +0000 Subject: [PATCH 225/421] Refining the tokens API --- lib/CssCrush/Process.php | 21 ++++++++------------- lib/CssCrush/Rule.php | 6 ++---- lib/CssCrush/Stream.php | 33 ++++++++------------------------- lib/CssCrush/Tokens.php | 38 ++++++++++++++++++++------------------ lib/CssCrush/Url.php | 6 +----- 5 files changed, 39 insertions(+), 65 deletions(-) diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 27cd88c..9a881af 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -579,10 +579,7 @@ public function captureRules() // Store rules if they have declarations or extend arguments. if (! empty($rule->declarations->store) || $rule->extendArgs) { - Crush::$process->tokens->add($rule, 'r', $rule->label); - - // If only using extend still return a label. - return $rule->label; + return Crush::$process->tokens->add($rule, 'r', $rule->label); } }); } @@ -811,7 +808,7 @@ protected function collate() // Apply all formatting replacements. $this->stream->pregReplaceHash($regex_replacements)->lTrim(); - $this->stream->replaceTokens('r'); + $this->stream->restore('r'); // Record stats then drop rule objects to reclaim memory. Crush::runStat('selector_count', 'rule_count', 'vars'); @@ -834,7 +831,7 @@ protected function collate() // Insert comments and do final whitespace cleanup. $this->stream - ->replaceTokens('c') + ->restore('c') ->trim() ->append($EOL); } @@ -868,11 +865,10 @@ protected function collate() $this->stream->prepend("@charset \"$this->charset\";$EOL"); } - $this->stream->replaceTokens('u'); - $this->stream->replaceTokens('s'); + $this->stream->restore(array('u', 's')); if ($this->addTracingStubs) { - $this->stream->replaceTokens('t', array($this, 'generateTracingStub')); + $this->stream->restore('t', false, array($this, 'generateTracingStub')); } if ($this->generateMap) { $this->generateSourceMap(); @@ -1003,12 +999,11 @@ public function generateSourceMap() public function generateTracingStub($m) { - $token = $m[0]; - $tokens =& $this->tokens->store->t; - if (! isset($tokens[$token])) { + if (! ($value = $this->tokens->get($m[0]))) { return ''; } - list($source_index, $line) = explode(',', $tokens[$token]); + + list($source_index, $line) = explode(',', $value); $line += 1; // Get the currently processed file path, and escape it. diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php index a56d0f8..5dddc81 100644 --- a/lib/CssCrush/Rule.php +++ b/lib/CssCrush/Rule.php @@ -96,12 +96,10 @@ public function __toString() // Merge the extend selectors. $this->selectors->store += $this->extendSelectors; - // If there are no selectors or declarations associated with the rule - // return empty string. + // Dereference and return empty string if there are no selectors or declarations. if (empty($this->selectors->store) || empty($this->declarations->store)) { + $process->tokens->pop($this->label); - // De-reference this instance. - $process->tokens->release($this->label); return ''; } diff --git a/lib/CssCrush/Stream.php b/lib/CssCrush/Stream.php index d6897a6..3899f86 100644 --- a/lib/CssCrush/Stream.php +++ b/lib/CssCrush/Stream.php @@ -47,9 +47,9 @@ public function matchAll($patt, $offset = 0) return Regex::matchAll($patt, $this->raw, $offset); } - public function replace($find, $replacement) + public function restore($types, $release = false, $callback = null) { - $this->raw = str_replace($find, $replacement, $this->raw); + $this->raw = Crush::$process->tokens->restore($this->raw, $types, $release, $callback); return $this; } @@ -65,29 +65,6 @@ public function replaceHash($replacements) return $this; } - public function replaceTokens($type, $callback = null) - { - $tokens =& Crush::$process->tokens->store->{ $type }; - $callback = $callback ?: function ($m) use (&$tokens) { - return isset($tokens[$m[0]]) ? $tokens[$m[0]] : ''; - }; - - $this->raw = preg_replace_callback(Regex::make("~\? $type {{token-id}} \?~xS"), $callback, $this->raw); - return $this; - } - - public function pregReplace($patt, $replacement) - { - $this->raw = preg_replace($patt, $replacement, $this->raw); - return $this; - } - - public function pregReplaceCallback($patt, $callback) - { - $this->raw = preg_replace_callback($patt, $callback, $this->raw); - return $this; - } - public function pregReplaceHash($replacements) { if ($replacements) { @@ -99,6 +76,12 @@ public function pregReplaceHash($replacements) return $this; } + public function pregReplaceCallback($patt, $callback) + { + $this->raw = preg_replace_callback($patt, $callback, $this->raw); + return $this; + } + public function append($append) { $this->raw .= $append; diff --git a/lib/CssCrush/Tokens.php b/lib/CssCrush/Tokens.php index c9b3361..ddbae90 100644 --- a/lib/CssCrush/Tokens.php +++ b/lib/CssCrush/Tokens.php @@ -14,38 +14,37 @@ class Tokens public function __construct(array $types = null) { $types = $types ?: array( - 's', // Strings - 'c', // Comments - 'r', // Rules - 'u', // URLs - 't', // Traces + 's', // strings. + 'c', // comments. + 'r', // rules. + 'u', // URLs. + 't', // traces. ); $this->store = new \stdClass; $this->ids = new \stdClass; foreach ($types as $type) { - $this->store->{$type} = array(); - $this->ids->{$type} = 0; + $this->store->$type = array(); + $this->ids->$type = 0; } } public function get($label) { $path =& $this->store->{$label[1]}; + return isset($path[$label]) ? $path[$label] : null; } public function pop($label) { $value = $this->get($label); - $this->release($label); - return $value; - } + if (isset($value)) { + unset($this->store->{$label[1]}[$label]); + } - public function release($label) - { - unset($this->store->{$label[1]}[$label]); + return $value; } public function add($value, $type = null, $existing_label = null) @@ -58,24 +57,27 @@ public function add($value, $type = null, $existing_label = null) } $label = $existing_label ? $existing_label : $this->createLabel($type); $this->store->{$type}[$label] = $value; + return $label; } public function createLabel($type) { - $counter = base_convert(++$this->ids->{$type}, 10, 36); + $counter = base_convert(++$this->ids->$type, 10, 36); + return "?$type$counter?"; } - public function restore($str, $types, $release = false) + public function restore($str, $types, $release = false, $callback = null) { $types = implode('', (array) $types); $patt = Regex::make("~\?[$types]{{ token-id }}\?~S"); $tokens = $this; - - return preg_replace_callback($patt, function ($m) use ($tokens, $release) { + $callback = $callback ?: function ($m) use ($tokens, $release) { return $release ? $tokens->pop($m[0]) : $tokens->get($m[0]); - }, $str); + }; + + return preg_replace_callback($patt, $callback, $str); } public function capture($str, $type) diff --git a/lib/CssCrush/Url.php b/lib/CssCrush/Url.php index 6e4c808..df56173 100644 --- a/lib/CssCrush/Url.php +++ b/lib/CssCrush/Url.php @@ -18,15 +18,11 @@ class Url public $noRewrite; public $convertToData; public $value; - public $label; public function __construct($raw_value) { - $tokens = Crush::$process->tokens; - if (preg_match(Regex::$patt->s_token, $raw_value)) { - $this->value = trim($tokens->get($raw_value), '\'"'); - $tokens->release($raw_value); + $this->value = trim(Crush::$process->tokens->pop($raw_value), '\'"'); } else { $this->value = $raw_value; From 361d745605a720547158860e93ed4d971b10404a Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 24 Dec 2013 19:06:09 +0000 Subject: [PATCH 226/421] Refactoring for color keywords plugin. --- lib/CssCrush/Color.php | 40 +++++++------------------------- lib/CssCrush/Crush.php | 8 +++++++ lib/CssCrush/Declaration.php | 20 +++++++--------- lib/CssCrush/DeclarationList.php | 37 ++++++++++++++++++----------- lib/CssCrush/Hook.php | 6 ++--- lib/CssCrush/Process.php | 1 + lib/CssCrush/Regex.php | 1 - lib/CssCrush/Util.php | 2 +- lib/functions.php | 2 +- plugins/canvas.php | 2 +- plugins/loop.php | 4 ++-- plugins/svg.php | 1 + 12 files changed, 59 insertions(+), 65 deletions(-) diff --git a/lib/CssCrush/Color.php b/lib/CssCrush/Color.php index 2aa16b7..73881ca 100644 --- a/lib/CssCrush/Color.php +++ b/lib/CssCrush/Color.php @@ -8,25 +8,8 @@ class Color { - // Cached color keyword tables. - public static $keywords; public static $minifyableKeywords; - public static function &loadKeywords () - { - if (! isset(self::$keywords)) { - - if ($keywords = Util::loadIni('misc/color-keywords.ini')) { - foreach ($keywords as $word => $rgb) { - $rgb = array_map('intval', explode(',', $rgb)); - self::$keywords[ $word ] = $rgb; - } - } - } - - return self::$keywords; - } - public static function &loadMinifyableKeywords () { if (! isset(self::$minifyableKeywords)) { @@ -34,22 +17,22 @@ public static function &loadMinifyableKeywords () // If color name is longer than 4 and less than 8 test to see if its hex // representation could be shortened. $table = array(); - $keywords =& Color::loadKeywords(); + $keywords = Crush::$config->colorKeywords; - foreach ($keywords as $name => &$rgb) { + foreach ($keywords as $name => $rgba) { $name_len = strlen($name); if ($name_len < 5) { continue; } - $hex = self::rgbToHex($rgb); + $hex = self::rgbToHex($rgba); if ($name_len > 7) { - self::$minifyableKeywords[ $name ] = $hex; + self::$minifyableKeywords[$name] = $hex; } else { if (preg_match(Regex::$patt->cruftyHex, $hex)) { - self::$minifyableKeywords[ $name ] = $hex; + self::$minifyableKeywords[$name] = $hex; } } } @@ -98,11 +81,7 @@ public static function parse($str) break; case 'keyword': - $keywords =& self::loadKeywords(); - $rgba = $keywords[$color]; - - // Manually add the alpha component. - $rgba[] = 1; + $rgba = Crush::$process->colorKeywords[$color]; break; } @@ -116,8 +95,8 @@ public static function test($str) $color_patt = Regex::make('~^( \#(?={{hex}}{3}) | \#(?={{hex}}{6}) | - rgba?(?=[?(]) | - hsla?(?=[?(]) + rgba?(?=\() | + hsla?(?=\() )~ixS'); } @@ -146,8 +125,7 @@ public static function test($str) // Secondly try to match a color keyword. else { - $keywords =& self::loadKeywords(); - if (isset($keywords[$str])) { + if (isset(Crush::$process->colorKeywords[$str])) { $color_test['type'] = 'keyword'; } } diff --git a/lib/CssCrush/Crush.php b/lib/CssCrush/Crush.php index c32d750..784690c 100644 --- a/lib/CssCrush/Crush.php +++ b/lib/CssCrush/Crush.php @@ -106,6 +106,14 @@ public static function loadAssets() $aliases = self::parseAliasesFile(self::$config->aliasesFile); self::$config->aliases = $aliases ?: self::$config->bareAliases; } + + if (! isset(self::$config->colorKeywords)) { + if ($keywords = Util::loadIni('misc/color-keywords.ini')) { + foreach ($keywords as $keyword => $rgb) { + self::$config->colorKeywords[$keyword] = array_map('floatval', explode(',', $rgb)) + array(0,0,0,1); + } + } + } } public static function parseAliasesFile($file) diff --git a/lib/CssCrush/Declaration.php b/lib/CssCrush/Declaration.php index a4c8900..093e5fb 100644 --- a/lib/CssCrush/Declaration.php +++ b/lib/CssCrush/Declaration.php @@ -18,27 +18,25 @@ class Declaration public $important = false; public $valid = true; - public function __construct($prop, $value, $contextIndex = 0) + public function __construct($property, $value, $contextIndex = 0) { - $regex = Regex::$patt; - // Normalize the property name. - $prop = strtolower($prop); + $property = strtolower($property); // Test for escape tilde. - if ($skip = strpos($prop, '~') === 0) { - $prop = substr($prop, 1); + if ($skip = strpos($property, '~') === 0) { + $property = substr($property, 1); } // Store the canonical property name. // Store the vendor mark if one is present. - if (preg_match($regex->vendorPrefix, $prop, $vendor)) { + if (preg_match(Regex::$patt->vendorPrefix, $property, $vendor)) { $canonical_property = $vendor[2]; $vendor = $vendor[1]; } else { $vendor = null; - $canonical_property = $prop; + $canonical_property = $property; } // Check for !important. @@ -47,14 +45,14 @@ public function __construct($prop, $value, $contextIndex = 0) $important = true; } + Hook::run('declaration_preprocess', array('property' => &$property, 'value' => &$value)); + // Reject declarations with empty CSS values. if ($value === false || $value === '') { $this->valid = false; - - return; } - $this->property = $prop; + $this->property = $property; $this->canonicalProperty = $canonical_property; $this->vendor = $vendor; $this->index = $contextIndex; diff --git a/lib/CssCrush/DeclarationList.php b/lib/CssCrush/DeclarationList.php index 499b5d6..5cc0683 100644 --- a/lib/CssCrush/DeclarationList.php +++ b/lib/CssCrush/DeclarationList.php @@ -327,13 +327,20 @@ public static function parse($str, $options = array()) { $str = Util::stripCommentTokens($str); $lines = preg_split('~\s*;\s*~', $str, null, PREG_SPLIT_NO_EMPTY); - $keyed = ! empty($options['keyed']); - $directives = empty($options['ignore_directives']); - $out = array(); + + $options += array( + 'keyed' => false, + 'ignore_directives' => false, + 'context' => null, + 'flatten' => false, + 'apply_hooks' => false, + ); + + $pairs = array(); foreach ($lines as $line) { - if ($directives && preg_match(Regex::$patt->ruleDirective, $line, $m)) { + if (! $options['ignore_directives'] && preg_match(Regex::$patt->ruleDirective, $line, $m)) { if (! empty($m[1])) { $property = 'mixin'; @@ -350,30 +357,34 @@ public static function parse($str, $options = array()) $property = trim(substr($line, 0, $colon_pos)); $value = trim(substr($line, $colon_pos + 1)); + + if ($options['apply_hooks']) { + Hook::run('declaration_preprocess', array('property' => &$property, 'value' => &$value)); + } } else { continue; } - if (! isset($property[0]) || ! isset($value[0])) { + if ($property === '' || $value === '') { continue; } - if ($property === 'mixin' && ! empty($options['flatten'])) { - $out = Mixin::merge($out, $value, array( - 'keyed' => $keyed, - 'context' => isset($options['context']) ? $options['context'] : null, + if ($property === 'mixin' && $options['flatten']) { + $pairs = Mixin::merge($pairs, $value, array( + 'keyed' => $options['keyed'], + 'context' => $options['context'], )); } - elseif ($keyed) { - $out[$property] = $value; + elseif ($options['keyed']) { + $pairs[$property] = $value; } else { - $out[] = array($property, $value); + $pairs[] = array($property, $value); } } - return $out; + return $pairs; } public function flatten($rule_context) diff --git a/lib/CssCrush/Hook.php b/lib/CssCrush/Hook.php index 84514c8..9d59f41 100644 --- a/lib/CssCrush/Hook.php +++ b/lib/CssCrush/Hook.php @@ -16,7 +16,6 @@ public static function add($hook, $fn_name) // Bail early is the named hook and callback combination is already loaded. if (! isset(self::$register[$hook][$fn_name])) { - // Register the hook and callback. // Store in associative array so no duplicates. if (function_exists($fn_name)) { self::$register[$hook][$fn_name] = true; @@ -31,10 +30,9 @@ public static function remove($hook, $fn_name) public static function run($hook, $arg_obj = null) { - // Run all callbacks attached to the hook. if (isset(self::$register[$hook])) { - foreach (array_keys(self::$register[$hook]) as $fn_name) { - call_user_func($fn_name, $arg_obj); + foreach (self::$register[$hook] as $fn_name => $flag) { + $fn_name($arg_obj); } } } diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 9a881af..38c5df2 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -40,6 +40,7 @@ public function __construct($user_options = array(), $dev_options = array()) // Copy config values. $this->plugins = $config->plugins; $this->aliases = $config->aliases; + $this->colorKeywords = $config->colorKeywords; // Options. $this->options = new Options($user_options, $config->options); diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php index bc79d6d..c4e347f 100644 --- a/lib/CssCrush/Regex.php +++ b/lib/CssCrush/Regex.php @@ -39,7 +39,6 @@ public static function init() // Boundries. $classes->LB = '(?RB = '(?![\w-])'; // Right ident boundry. - $classes->RTB = '(?=\?[a-z])'; // Right token boundry. // Recursive block matching. $classes->block = '(?\{\s*(?(?:(?>[^{}]+)|(?&block))*)\})'; diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php index a18a4fb..3e64b24 100644 --- a/lib/CssCrush/Util.php +++ b/lib/CssCrush/Util.php @@ -97,7 +97,7 @@ public static function resolveUserPath($path, $recovery = null) } if (! file_exists($path) && is_callable($recovery)) { - $path = call_user_func($recovery, $path); + $path = $recovery($path); } $path = realpath($path); } diff --git a/lib/functions.php b/lib/functions.php index def7dd2..6ccfeb2 100644 --- a/lib/functions.php +++ b/lib/functions.php @@ -47,7 +47,7 @@ function csscrush_set($object_name, $modifier) { $pointer = $object_name === 'options' ? Crush::$config->options : Crush::$config; if (is_callable($modifier)) { - call_user_func($modifier, $pointer); + $modifier($pointer); } elseif (is_array($modifier)) { foreach ($modifier as $key => $value) { diff --git a/plugins/canvas.php b/plugins/canvas.php index dba12ee..ba9c595 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -67,7 +67,6 @@ function canvas_capture($process) { - // Extract definitions. $process->stream->pregReplaceCallback( Regex::make('~@canvas \s+ (?{{ident}}) \s* {{block}}~ixS'), function ($m) { @@ -121,6 +120,7 @@ function canvas_generator($input, $context) { $raw = array_change_key_case(DeclarationList::parse($block, array( 'keyed' => true, 'flatten' => true, + 'apply_hooks' => true, ))); // Create canvas object. diff --git a/plugins/loop.php b/plugins/loop.php index 26a0f96..4ba8fd4 100644 --- a/plugins/loop.php +++ b/plugins/loop.php @@ -66,9 +66,9 @@ Regex::make('~(? @for \s+ (?{{ident}}) \s+ in \s+ (?[^{]+) ) \s* {{block}}~xiS')); -function loop() { +function loop($process) { - Crush::$process->stream->pregReplaceCallback(LOOP_PATT, function ($m) { + $process->stream->pregReplaceCallback(LOOP_PATT, function ($m) { return Template::tokenize(loop_unroll(Template::unTokenize($m[0]))); }); diff --git a/plugins/svg.php b/plugins/svg.php index cbd9947..8a8cd6c 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -199,6 +199,7 @@ function svg_generator($input, $fn_name) { $raw_data = array_change_key_case(DeclarationList::parse($block, array( 'keyed' => true, 'flatten' => true, + 'apply_hooks' => true, ))); // Resolve the type. From e688318d8b8064b4b537fc624296b332ef6cbfa8 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 26 Dec 2013 13:14:54 +0000 Subject: [PATCH 227/421] Modified declaration aliases keys to work with HHVM. --- aliases.ini | 144 +++++++++++++++---------------- lib/CssCrush/Color.php | 11 ++- lib/CssCrush/Crush.php | 9 +- lib/CssCrush/DeclarationList.php | 2 +- lib/CssCrush/PostAliasFix.php | 2 +- lib/CssCrush/Process.php | 2 +- plugins/canvas.php | 9 +- 7 files changed, 89 insertions(+), 90 deletions(-) diff --git a/aliases.ini b/aliases.ini index 434141f..d0d8506 100644 --- a/aliases.ini +++ b/aliases.ini @@ -220,88 +220,88 @@ [declarations] ; Flexbox (2012). - display:flex[] = display:-ms-flexbox - display:flex[] = display:-webkit-flex - display:inline-flex[] = display:-ms-inline-flexbox - display:inline-flex[] = display:-webkit-inline-flex + display.flex[] = display:-ms-flexbox + display.flex[] = display:-webkit-flex + display.inline-flex[] = display:-ms-inline-flexbox + display.inline-flex[] = display:-webkit-inline-flex ; Flexbox (early 2012). - align-content:flex-start[] = -ms-flex-line-pack:start - align-content:flex-end[] = -ms-flex-line-pack:end - align-content:center[] = -ms-flex-line-pack:center - align-content:space-between[] = -ms-flex-line-pack:justify - align-content:space-around[] = -ms-flex-line-pack:distribute - align-content:stretch[] = -ms-flex-line-pack:stretch - - align-items:flex-start[] = -ms-flex-align:start - align-items:flex-end[] = -ms-flex-align:end - align-items:center[] = -ms-flex-align:center - align-items:baseline[] = -ms-flex-align:baseline - align-items:stretch[] = -ms-flex-align:stretch - - align-self:auto[] = -ms-flex-item-align:auto - align-self:flex-start[] = -ms-flex-item-align:start - align-self:flex-end[] = -ms-flex-item-align:end - align-self:center[] = -ms-flex-item-align:center - align-self:baseline[] = -ms-flex-item-align:baseline - align-self:stretch[] = -ms-flex-item-align:stretch - - justify-content:flex-start[] = -ms-flex-pack:start - justify-content:flex-end[] = -ms-flex-pack:end - justify-content:center[] = -ms-flex-pack:center - justify-content:space-between[] = -ms-flex-pack:justify - justify-content:space-around[] = -ms-flex-pack:distribute + align-content.flex-start[] = -ms-flex-line-pack:start + align-content.flex-end[] = -ms-flex-line-pack:end + align-content.center[] = -ms-flex-line-pack:center + align-content.space-between[] = -ms-flex-line-pack:justify + align-content.space-around[] = -ms-flex-line-pack:distribute + align-content.stretch[] = -ms-flex-line-pack:stretch + + align-items.flex-start[] = -ms-flex-align:start + align-items.flex-end[] = -ms-flex-align:end + align-items.center[] = -ms-flex-align:center + align-items.baseline[] = -ms-flex-align:baseline + align-items.stretch[] = -ms-flex-align:stretch + + align-self.auto[] = -ms-flex-item-align:auto + align-self.flex-start[] = -ms-flex-item-align:start + align-self.flex-end[] = -ms-flex-item-align:end + align-self.center[] = -ms-flex-item-align:center + align-self.baseline[] = -ms-flex-item-align:baseline + align-self.stretch[] = -ms-flex-item-align:stretch + + justify-content.flex-start[] = -ms-flex-pack:start + justify-content.flex-end[] = -ms-flex-pack:end + justify-content.center[] = -ms-flex-pack:center + justify-content.space-between[] = -ms-flex-pack:justify + justify-content.space-around[] = -ms-flex-pack:distribute ; Flexbox (2009). - display:box[] = display:-webkit-box - display:box[] = display:-moz-box + display.box[] = display:-webkit-box + display.box[] = display:-moz-box ; Cursor values (non-standard). - cursor:zoom-in[] = cursor:-webkit-zoom-in - cursor:zoom-in[] = cursor:-moz-zoom-in - cursor:zoom-in[] = cursor:-ms-zoom-in - cursor:zoom-in[] = cursor:-o-zoom-in - cursor:zoom-out[] = cursor:-webkit-zoom-out - cursor:zoom-out[] = cursor:-moz-zoom-out - cursor:zoom-out[] = cursor:-ms-zoom-out - cursor:zoom-out[] = cursor:-o-zoom-out + cursor.zoom-in[] = cursor:-webkit-zoom-in + cursor.zoom-in[] = cursor:-moz-zoom-in + cursor.zoom-in[] = cursor:-ms-zoom-in + cursor.zoom-in[] = cursor:-o-zoom-in + cursor.zoom-out[] = cursor:-webkit-zoom-out + cursor.zoom-out[] = cursor:-moz-zoom-out + cursor.zoom-out[] = cursor:-ms-zoom-out + cursor.zoom-out[] = cursor:-o-zoom-out ; Experimental width values. - width:max-content[] = width:intrinsic - width:max-content[] = width:-webkit-max-content - width:max-content[] = width:-moz-max-content - width:min-content[] = width:-webkit-min-content - width:min-content[] = width:-moz-min-content - width:available[] = width:-webkit-available - width:available[] = width:-moz-available - width:fit-content[] = width:-webkit-fit-content - width:fit-content[] = width:-moz-fit-content - - max-width:max-content[] = max-width:intrinsic - max-width:max-content[] = max-width:-webkit-max-content - max-width:max-content[] = max-width:-moz-max-content - max-width:min-content[] = max-width:-webkit-min-content - max-width:min-content[] = max-width:-moz-min-content - max-width:available[] = max-width:-webkit-available - max-width:available[] = max-width:-moz-available - max-width:fit-content[] = max-width:-webkit-fit-content - max-width:fit-content[] = max-width:-moz-fit-content - - min-width:max-content[] = min-width:intrinsic - min-width:max-content[] = min-width:-webkit-max-content - min-width:max-content[] = min-width:-moz-max-content - min-width:min-content[] = min-width:-webkit-min-content - min-width:min-content[] = min-width:-moz-min-content - min-width:available[] = min-width:-webkit-available - min-width:available[] = min-width:-moz-available - min-width:fit-content[] = min-width:-webkit-fit-content - min-width:fit-content[] = min-width:-moz-fit-content + width.max-content[] = width:intrinsic + width.max-content[] = width:-webkit-max-content + width.max-content[] = width:-moz-max-content + width.min-content[] = width:-webkit-min-content + width.min-content[] = width:-moz-min-content + width.available[] = width:-webkit-available + width.available[] = width:-moz-available + width.fit-content[] = width:-webkit-fit-content + width.fit-content[] = width:-moz-fit-content + + max-width.max-content[] = max-width:intrinsic + max-width.max-content[] = max-width:-webkit-max-content + max-width.max-content[] = max-width:-moz-max-content + max-width.min-content[] = max-width:-webkit-min-content + max-width.min-content[] = max-width:-moz-min-content + max-width.available[] = max-width:-webkit-available + max-width.available[] = max-width:-moz-available + max-width.fit-content[] = max-width:-webkit-fit-content + max-width.fit-content[] = max-width:-moz-fit-content + + min-width.max-content[] = min-width:intrinsic + min-width.max-content[] = min-width:-webkit-max-content + min-width.max-content[] = min-width:-moz-max-content + min-width.min-content[] = min-width:-webkit-min-content + min-width.min-content[] = min-width:-moz-min-content + min-width.available[] = min-width:-webkit-available + min-width.available[] = min-width:-moz-available + min-width.fit-content[] = min-width:-webkit-fit-content + min-width.fit-content[] = min-width:-moz-fit-content ; Appearance (non-standard). - appearance:none[] = -webkit-appearance:none - appearance:none[] = -moz-appearance:none + appearance.none[] = -webkit-appearance:none + appearance.none[] = -moz-appearance:none - position:sticky[] = position:-webkit-sticky + position.sticky[] = position:-webkit-sticky ;---------------------------------------------------------------- @@ -317,7 +317,7 @@ element[] = -moz-element -[functions:gradients] +[functions.gradients] ; Gradients. linear-gradient[] = -webkit-linear-gradient diff --git a/lib/CssCrush/Color.php b/lib/CssCrush/Color.php index 73881ca..4d90b78 100644 --- a/lib/CssCrush/Color.php +++ b/lib/CssCrush/Color.php @@ -124,7 +124,6 @@ public static function test($str) // Secondly try to match a color keyword. else { - if (isset(Crush::$process->colorKeywords[$str])) { $color_test['type'] = 'keyword'; } @@ -154,8 +153,8 @@ public static function rgbToHsl(array $rgba) $b /= 255; $max = max($r, $g, $b); $min = min($r, $g, $b); - $h; - $s; + $h = 0; + $s = 0; $l = ($max + $min) / 2; if ($max == $min) { @@ -198,9 +197,9 @@ public static function hslToRgb(array $hsla) } list($h, $s, $l, $a) = $hsla; - $r; - $g; - $b; + $r = 0; + $g = 0; + $b = 0; if ($s == 0) { $r = $g = $b = $l; } diff --git a/lib/CssCrush/Crush.php b/lib/CssCrush/Crush.php index 784690c..b7ba0bb 100644 --- a/lib/CssCrush/Crush.php +++ b/lib/CssCrush/Crush.php @@ -136,7 +136,7 @@ public static function parseAliasesFile($file) $store = array(); foreach ($items as $prop_val => $aliases) { - list($prop, $value) = array_map('trim', explode(':', $prop_val)); + list($prop, $value) = array_map('trim', explode('.', $prop_val)); foreach ($aliases as &$alias) { @@ -158,7 +158,7 @@ public static function parseAliasesFile($file) } // Function groups. - elseif (strpos($section, 'functions:') === 0) { + elseif (strpos($section, 'functions.') === 0) { $group = substr($section, strlen('functions')); @@ -405,7 +405,6 @@ public static function printLog() public static function runStat() { $process = Crush::$process; - $all_rules =& $process->tokens->store->r; foreach (func_get_args() as $stat_name) { @@ -427,13 +426,13 @@ public static function runStat() case 'selector_count': $process->stat['selector_count'] = 0; - foreach ($all_rules as $rule) { + foreach ($process->tokens->store->r as $rule) { $process->stat['selector_count'] += count($rule->selectors); } break; case 'rule_count': - $process->stat['rule_count'] = count($all_rules); + $process->stat['rule_count'] = count($process->tokens->store->r); break; } } diff --git a/lib/CssCrush/DeclarationList.php b/lib/CssCrush/DeclarationList.php index 5cc0683..e83f471 100644 --- a/lib/CssCrush/DeclarationList.php +++ b/lib/CssCrush/DeclarationList.php @@ -184,7 +184,7 @@ public function aliasFunctions($vendor_context = null) $prefixed_copies = array(); // Grouped function aliases. - if ($function_aliases[$fn_name][0] === ':') { + if ($function_aliases[$fn_name][0] === '.') { $group_id = $function_aliases[$fn_name]; diff --git a/lib/CssCrush/PostAliasFix.php b/lib/CssCrush/PostAliasFix.php index 240ab1c..5973bd5 100644 --- a/lib/CssCrush/PostAliasFix.php +++ b/lib/CssCrush/PostAliasFix.php @@ -9,7 +9,7 @@ class PostAliasFix { public static $functions = array( - ':gradients' => 'CssCrush\postalias_fix_gradients', + '.gradients' => 'CssCrush\postalias_fix_gradients', ); public static function add($alias_type, $key, $callback) diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 38c5df2..af3c69e 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -325,7 +325,7 @@ protected function filterAliases() foreach ($group_array as $alias_keyword => $prefix_array) { // Skip over pointers to function groups. - if ($prefix_array[0] === ':') { + if ($prefix_array[0] === '.') { continue; } diff --git a/plugins/canvas.php b/plugins/canvas.php index ba9c595..6b6585a 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -690,11 +690,12 @@ function canvas_requirements() { warning('[[CssCrush]] - GD extension not available.'); } else { - $info = array_change_key_case(gd_info()); - foreach (array('png', 'jpeg') as $key) { - if (empty($info["$key support"])) { + $gd_info = implode('|', array_keys(array_filter(gd_info()))); + + foreach (array('jpe?g' => 'JPG', 'png' => 'PNG') as $file_ext_patt => $file_ext) { + if (! preg_match("~\b(?$file_ext_patt) support\b~i", $gd_info)) { $requirements_met = false; - warning("[[CssCrush]] - GD extension has no $key support."); + warning("[[CssCrush]] - GD extension has no $file_ext support."); } } } From 5f6041a58ca404e576c3246c3513a5035ab65c99 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 26 Dec 2013 19:00:33 +0000 Subject: [PATCH 228/421] Decoupling Color class --- lib/CssCrush/Color.php | 24 ++++++++++++++++++++---- lib/CssCrush/Crush.php | 8 -------- lib/CssCrush/Process.php | 3 +-- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/lib/CssCrush/Color.php b/lib/CssCrush/Color.php index 4d90b78..7f0c09f 100644 --- a/lib/CssCrush/Color.php +++ b/lib/CssCrush/Color.php @@ -8,16 +8,30 @@ class Color { + public static $keywords; public static $minifyableKeywords; - public static function &loadMinifyableKeywords () + public static function getKeywords() + { + if (! isset(self::$keywords)) { + if ($keywords = Util::loadIni('misc/color-keywords.ini')) { + foreach ($keywords as $keyword => $rgb) { + self::$keywords[$keyword] = array_map('floatval', explode(',', $rgb)) + array(0,0,0,1); + } + } + } + + return isset(Crush::$process->colorKeywords) ? Crush::$process->colorKeywords : self::$keywords; + } + + public static function getMinifyableKeywords () { if (! isset(self::$minifyableKeywords)) { // If color name is longer than 4 and less than 8 test to see if its hex // representation could be shortened. $table = array(); - $keywords = Crush::$config->colorKeywords; + $keywords = self::getKeywords(); foreach ($keywords as $name => $rgba) { $name_len = strlen($name); @@ -81,7 +95,8 @@ public static function parse($str) break; case 'keyword': - $rgba = Crush::$process->colorKeywords[$color]; + $keywords = self::getKeywords(); + $rgba = $keywords[$color]; break; } @@ -124,7 +139,8 @@ public static function test($str) // Secondly try to match a color keyword. else { - if (isset(Crush::$process->colorKeywords[$str])) { + $keywords = self::getKeywords(); + if (isset($keywords[$str])) { $color_test['type'] = 'keyword'; } } diff --git a/lib/CssCrush/Crush.php b/lib/CssCrush/Crush.php index b7ba0bb..17ae9aa 100644 --- a/lib/CssCrush/Crush.php +++ b/lib/CssCrush/Crush.php @@ -106,14 +106,6 @@ public static function loadAssets() $aliases = self::parseAliasesFile(self::$config->aliasesFile); self::$config->aliases = $aliases ?: self::$config->bareAliases; } - - if (! isset(self::$config->colorKeywords)) { - if ($keywords = Util::loadIni('misc/color-keywords.ini')) { - foreach ($keywords as $keyword => $rgb) { - self::$config->colorKeywords[$keyword] = array_map('floatval', explode(',', $rgb)) + array(0,0,0,1); - } - } - } } public static function parseAliasesFile($file) diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index af3c69e..1181975 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -40,7 +40,6 @@ public function __construct($user_options = array(), $dev_options = array()) // Copy config values. $this->plugins = $config->plugins; $this->aliases = $config->aliases; - $this->colorKeywords = $config->colorKeywords; // Options. $this->options = new Options($user_options, $config->options); @@ -1060,7 +1059,7 @@ protected function minifyColors() static $keywords_patt, $functions_patt; if (! $keywords_patt) { - $keywords =& Color::loadMinifyableKeywords(); + $keywords = Color::getMinifyableKeywords(); $keywords_patt = '~(? Date: Thu, 26 Dec 2013 19:29:00 +0000 Subject: [PATCH 229/421] Adding custom color keywords plugin. --- plugins/color.php | 70 +++++++++++++++++++++++++++++++++++++++++++++++ plugins/forms.php | 2 +- 2 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 plugins/color.php diff --git a/plugins/color.php b/plugins/color.php new file mode 100644 index 0000000..2c91fc6 --- /dev/null +++ b/plugins/color.php @@ -0,0 +1,70 @@ + function () { + Hook::add('capture_phase1', 'CssCrush\color_capture'); + Hook::add('declaration_preprocess', 'CssCrush\color'); + }, + 'disable' => function () { + Hook::remove('capture_phase1', 'CssCrush\color_capture'); + Hook::remove('declaration_preprocess', 'CssCrush\color'); + }, +)); + + +function color(&$declaration) { + if (defined('CSSCRUSH_COLOR_PATT')) { + $declaration['value'] = preg_replace_callback(CSSCRUSH_COLOR_PATT, function ($m) { + return new Color(Crush::$process->colorKeywords[$m['color_keyword']]); + }, $declaration['value']); + } +} + +function color_capture($process) { + + $color_directive_patt = Regex::make('~@color(?:\s*{{ block }}|\s+(?{{ ident }})\s+(?[^;]+)\s*;)~iS'); + $captured_keywords = array(); + + $process->stream->pregReplaceCallback($color_directive_patt, function ($m) use (&$captured_keywords) { + + if (! isset($m['name'])) { + $pairs = array_change_key_case(DeclarationList::parse($m['block_content'], array( + 'keyed' => true, + 'flatten' => true, + ))); + } + else { + $pairs = array(strtolower($m['name']) => $m['value']); + } + + $captured_keywords = $pairs + $captured_keywords; + + return ''; + }); + + + if ($captured_keywords) { + + $native_keywords = Color::getKeywords(); + $custom_keywords = array(); + Crush::$process->colorKeywords = $native_keywords; + + foreach ($captured_keywords as $key => $value) { + $value = Functions::executeOnString($value); + if (! isset($native_keywords[$key]) && $rgba = Color::parse($value)) { + $custom_keywords[$key] = $rgba; + Crush::$process->stat['colors'][$key] = new Color($rgba); + Crush::$process->colorKeywords[$key] = $rgba; + } + } + + if ($custom_keywords) { + define('CSSCRUSH_COLOR_PATT', Regex::make('~{{ LB }}(?' . + implode('|', array_keys($custom_keywords)) . '){{ RB }}~iS')); + } + } +} diff --git a/plugins/forms.php b/plugins/forms.php index fa1883e..5eb2db1 100644 --- a/plugins/forms.php +++ b/plugins/forms.php @@ -1,6 +1,6 @@ Date: Thu, 26 Dec 2013 22:16:24 +0000 Subject: [PATCH 230/421] Fixed `$this` issue affecting php 5.3 closures. --- lib/CssCrush/Color.php | 5 ++--- lib/CssCrush/Functions.php | 5 +++-- lib/CssCrush/Logger.php | 2 +- lib/CssCrush/Process.php | 11 ++++++----- lib/CssCrush/Template.php | 7 ++++--- 5 files changed, 16 insertions(+), 14 deletions(-) diff --git a/lib/CssCrush/Color.php b/lib/CssCrush/Color.php index 7f0c09f..a004782 100644 --- a/lib/CssCrush/Color.php +++ b/lib/CssCrush/Color.php @@ -8,8 +8,7 @@ class Color { - public static $keywords; - public static $minifyableKeywords; + protected static $keywords, $minifyableKeywords; public static function getKeywords() { @@ -24,7 +23,7 @@ public static function getKeywords() return isset(Crush::$process->colorKeywords) ? Crush::$process->colorKeywords : self::$keywords; } - public static function getMinifyableKeywords () + public static function getMinifyableKeywords() { if (! isset(self::$minifyableKeywords)) { diff --git a/lib/CssCrush/Functions.php b/lib/CssCrush/Functions.php index b3203cf..c279d38 100644 --- a/lib/CssCrush/Functions.php +++ b/lib/CssCrush/Functions.php @@ -98,12 +98,13 @@ public static function executeOnString($str, $patt = null, $process_callback = n // First look for function as directly passed. if (isset($process_callback[$fn_name])) { - $func_returns = call_user_func($process_callback[$fn_name], $raw_args, $context); + $func_returns = $process_callback[$fn_name]($raw_args, $context); } // Secondly look for built-in function. elseif (isset(self::$functions[$fn_name])) { - $func_returns = call_user_func(self::$functions[$fn_name], $raw_args, $context); + $func = self::$functions[$fn_name]; + $func_returns = $func($raw_args, $context); } // Splice in the function result. diff --git a/lib/CssCrush/Logger.php b/lib/CssCrush/Logger.php index e13c765..1c40fb3 100644 --- a/lib/CssCrush/Logger.php +++ b/lib/CssCrush/Logger.php @@ -146,7 +146,7 @@ public function log($level, $message, array $context = array()) unset($log_levels['log']); if (isset($log_levels[$level])) { - return call_user_func(array($this, $level), $message, $context); + return $this->$level($message, $context); } } } diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 1181975..bb96953 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -1058,14 +1058,15 @@ protected function minifyColors() { static $keywords_patt, $functions_patt; + $minified_keywords = Color::getMinifyableKeywords(); + if (! $keywords_patt) { - $keywords = Color::getMinifyableKeywords(); - $keywords_patt = '~(?stream->pregReplaceCallback($keywords_patt, function ($m) { - return Color::$minifyableKeywords[strtolower($m[0])]; + $this->stream->pregReplaceCallback($keywords_patt, function ($m) use ($minified_keywords) { + return $minified_keywords[strtolower($m[0])]; }); $this->stream->pregReplaceCallback($functions_patt, function ($m) { diff --git a/lib/CssCrush/Template.php b/lib/CssCrush/Template.php index c370d5f..ca8df11 100644 --- a/lib/CssCrush/Template.php +++ b/lib/CssCrush/Template.php @@ -31,7 +31,8 @@ public function __construct($str, $options = array()) // Parse all arg function calls in the passed string, // callback creates default values. - $capture_callback = function ($str) + $self = $this; + $capture_callback = function ($str) use (&$self) { $args = Functions::parseArgsSimple($str); @@ -48,12 +49,12 @@ public function __construct($str, $options = array()) $default_value = isset($args[0]) ? $args[0] : null; if (isset($default_value)) { - $this->defaults[$position] = $default_value; + $self->defaults[$position] = $default_value; } // Update the argument count. $argNumber = ((int) $position) + 1; - $this->argCount = max($this->argCount, $argNumber); + $self->argCount = max($self->argCount, $argNumber); return "?a$position?"; }; From c7a9e4da2ace560df73b67d247e963e44886c126 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 27 Dec 2013 13:58:23 +0000 Subject: [PATCH 231/421] Adding travis and git config. --- .gitattributes | 5 +++++ .gitignore | 6 ++++++ .travis.yml | 12 ++++++++++++ phpunit.xml.dist | 20 ++++++++++++++++++++ plugins/color.php | 9 +++++---- 5 files changed, 48 insertions(+), 4 deletions(-) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 phpunit.xml.dist diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..aaf1665 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +tests/ export-ignore +.gitattributes export-ignore +.gitignore export-ignore +.travis.yml export-ignore +phpunit.xml.dist export-ignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7916763 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +_* +.tm_properties +phpunit.xml +composer.lock +vendor +tests diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..4128d44 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,12 @@ + +language: php + +php: + - 5.3 + - 5.4 + +before_script: + - composer self-update + - composer install --dev + +script: ./vendor/bin/phpunit -v diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..e7d271e --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,20 @@ + + + + + + + + + ./tests/unit/ + + + + + + ./lib/CssCrush/ + + + diff --git a/plugins/color.php b/plugins/color.php index 2c91fc6..d08cb23 100644 --- a/plugins/color.php +++ b/plugins/color.php @@ -6,6 +6,7 @@ Plugin::register('color', array( 'enable' => function () { + $GLOBALS['CSSCRUSH_COLOR_PATT'] = null; Hook::add('capture_phase1', 'CssCrush\color_capture'); Hook::add('declaration_preprocess', 'CssCrush\color'); }, @@ -17,8 +18,8 @@ function color(&$declaration) { - if (defined('CSSCRUSH_COLOR_PATT')) { - $declaration['value'] = preg_replace_callback(CSSCRUSH_COLOR_PATT, function ($m) { + if (isset($GLOBALS['CSSCRUSH_COLOR_PATT'])) { + $declaration['value'] = preg_replace_callback($GLOBALS['CSSCRUSH_COLOR_PATT'], function ($m) { return new Color(Crush::$process->colorKeywords[$m['color_keyword']]); }, $declaration['value']); } @@ -63,8 +64,8 @@ function color_capture($process) { } if ($custom_keywords) { - define('CSSCRUSH_COLOR_PATT', Regex::make('~{{ LB }}(?' . - implode('|', array_keys($custom_keywords)) . '){{ RB }}~iS')); + $GLOBALS['CSSCRUSH_COLOR_PATT'] = Regex::make('~{{ LB }}(?' . + implode('|', array_keys($custom_keywords)) . '){{ RB }}~iS'); } } } From 988c17ba3818f2cebdd3b6678633d93550ad925b Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 27 Dec 2013 14:02:56 +0000 Subject: [PATCH 232/421] Adding some tests. --- .gitignore | 1 - tests/bootstrap.php | 43 +++++++ tests/unit/CssCrush/BalancedMatchTest.php | 37 ++++++ tests/unit/CssCrush/ColorTest.php | 65 +++++++++++ tests/unit/CssCrush/DeclarationTest.php | 52 +++++++++ tests/unit/CssCrush/ExtendArgTest.php | 15 +++ tests/unit/CssCrush/HookTest.php | 74 ++++++++++++ tests/unit/CssCrush/LoggerTest.php | 18 +++ tests/unit/CssCrush/OptionsTest.php | 92 +++++++++++++++ tests/unit/CssCrush/PluginTest.php | 66 +++++++++++ tests/unit/CssCrush/RegexTest.php | 39 +++++++ tests/unit/CssCrush/SelectorTest.php | 46 ++++++++ tests/unit/CssCrush/StreamTest.php | 74 ++++++++++++ tests/unit/CssCrush/TemplateTest.php | 83 ++++++++++++++ tests/unit/CssCrush/TokensTest.php | 114 +++++++++++++++++++ tests/unit/CssCrush/UrlTest.php | 133 ++++++++++++++++++++++ tests/unit/CssCrush/UtilTest.php | 91 +++++++++++++++ tests/unit/CssCrush/VersionTest.php | 45 ++++++++ tests/unit/api/apiTest.php | 118 +++++++++++++++++++ tests/unit/cli/cliTest.php | 56 +++++++++ tests/unit/cli/context/import.css | 1 + 21 files changed, 1262 insertions(+), 1 deletion(-) create mode 100644 tests/bootstrap.php create mode 100644 tests/unit/CssCrush/BalancedMatchTest.php create mode 100644 tests/unit/CssCrush/ColorTest.php create mode 100644 tests/unit/CssCrush/DeclarationTest.php create mode 100644 tests/unit/CssCrush/ExtendArgTest.php create mode 100644 tests/unit/CssCrush/HookTest.php create mode 100644 tests/unit/CssCrush/LoggerTest.php create mode 100644 tests/unit/CssCrush/OptionsTest.php create mode 100644 tests/unit/CssCrush/PluginTest.php create mode 100644 tests/unit/CssCrush/RegexTest.php create mode 100644 tests/unit/CssCrush/SelectorTest.php create mode 100644 tests/unit/CssCrush/StreamTest.php create mode 100644 tests/unit/CssCrush/TemplateTest.php create mode 100644 tests/unit/CssCrush/TokensTest.php create mode 100644 tests/unit/CssCrush/UrlTest.php create mode 100644 tests/unit/CssCrush/UtilTest.php create mode 100644 tests/unit/CssCrush/VersionTest.php create mode 100644 tests/unit/api/apiTest.php create mode 100644 tests/unit/cli/cliTest.php create mode 100644 tests/unit/cli/context/import.css diff --git a/.gitignore b/.gitignore index 7916763..908cb6a 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,3 @@ _* phpunit.xml composer.lock vendor -tests diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 0000000..0786492 --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,43 @@ +add('CssCrush\UnitTest', __DIR__ . '/unit'); +} + +namespace CssCrush\UnitTest +{ + + function bootstrap_process($options = array()) + { + $process = \CssCrush\Crush::$process = new \CssCrush\Process($options); + $process->prepare(); + $process->resolveContext(); + return $process; + } + + function temp_file($contents = '') + { + $temporary_file = tempnam(sys_get_temp_dir(), 'crush'); + if ($contents) { + file_put_contents($temporary_file, $contents); + } + return $temporary_file; + } + + function stdout($message, $prepend_newline = false) + { + if (! is_string($message)) { + $buffer = ob_start(); + print_r($message); + $message = ob_get_clean(); + } + fwrite(STDOUT, ($prepend_newline ? "\n" : '') . $message . "\n"); + } +} diff --git a/tests/unit/CssCrush/BalancedMatchTest.php b/tests/unit/CssCrush/BalancedMatchTest.php new file mode 100644 index 0000000..9a04ee3 --- /dev/null +++ b/tests/unit/CssCrush/BalancedMatchTest.php @@ -0,0 +1,37 @@ +process = bootstrap_process(); + $sample = '@foo; @bar {color: orange;} @baz'; + + $this->process->stream = new \CssCrush\Stream($sample); + } + + public function testMatch() + { + $matches = $this->process->stream->matchAll('~@bar~'); + $match_offset = $matches[0][0][1]; + + $match = new BalancedMatch($this->process->stream, $match_offset); + + $this->assertEquals('color: orange;', $match->inside()); + $this->assertEquals('@bar {color: orange;}', $match->whole()); + + $match = new BalancedMatch(clone $this->process->stream, $match_offset); + $match->unWrap(); + $this->assertEquals('@foo; color: orange; @baz', $match->stream->__toString()); + + $match = new BalancedMatch(clone $this->process->stream, $match_offset); + $match->replace('@boo;'); + $this->assertEquals('@foo; @boo; @baz', $match->stream->__toString()); + } +} diff --git a/tests/unit/CssCrush/ColorTest.php b/tests/unit/CssCrush/ColorTest.php new file mode 100644 index 0000000..b24c3d8 --- /dev/null +++ b/tests/unit/CssCrush/ColorTest.php @@ -0,0 +1,65 @@ +assertEquals('#ffefd5', (string) $color); + + $color = new Color('#ccc'); + $this->assertEquals('#cccccc', (string) $color); + + $color = new Color('hsla(120,50%,50%,.8)'); + $this->assertEquals('rgba(64,191,64,0.8)', (string) $color); + } + + public function testAdjust() + { + $color = new Color('rgb(255,0,0)'); + $color->toHsl()->adjust(array(0,0,0,-20)); + $this->assertEquals('rgba(255,0,0,0.8)', (string) $color); + } + + public function testGetHsl() + { + $color = new Color('red'); + $this->assertEquals(array(0, 1, .5, 1), $color->getHsl()); + } + + public function testGetHex() + { + $color = new Color('red'); + $this->assertEquals('#ff0000', $color->getHex()); + } + + public function testGetRgb() + { + $color = new Color('red'); + $this->assertEquals(array(255, 0, 0, 1), $color->getRgb()); + } + + public function testGetComponent() + { + $color = new Color('red'); + $this->assertEquals(255, $color->getComponent(0)); + } + + public function testSetComponent() + { + $color = new Color('red'); + $color->setComponent(0, 0); + $this->assertEquals(0, $color->getComponent(0)); + } + + public function testColorSplit() + { + list($base_color, $opacity) = Color::colorSplit('red'); + $this->assertEquals('red', $base_color); + $this->assertEquals(1, $opacity); + } +} diff --git a/tests/unit/CssCrush/DeclarationTest.php b/tests/unit/CssCrush/DeclarationTest.php new file mode 100644 index 0000000..4b5b67b --- /dev/null +++ b/tests/unit/CssCrush/DeclarationTest.php @@ -0,0 +1,52 @@ +process = bootstrap_process(array('minify' => false)); + $this->rule = new Rule('.foo', '-fOo-BAR: (10 + 10)px !important'); + $this->declaration = new Declaration('-fOo-BAR', 'baz !important'); + } + + public function test__construct() + { + $this->assertTrue($this->declaration->important); + $this->assertTrue($this->declaration->valid); + + $this->assertEquals('bar', $this->declaration->canonicalProperty); + $this->assertEquals('-foo-bar', $this->declaration->property); + $this->assertEquals('foo', $this->declaration->vendor); + $this->assertEquals('baz', $this->declaration->value); + } + + public function test__toString() + { + $this->assertEquals('-foo-bar: baz !important', (string) $this->declaration); + } + + public function testProcess() + { + foreach ($this->rule->declarations as $index => $declaration) { + $declaration->process($this->rule); + $this->assertEquals('20px', $declaration->value); + } + } + + public function testIndexFunctions() + { + $declaration = new Declaration('color', 'rgba(0,0,0,.5), calc(100px)'); + $declaration->indexFunctions(); + $this->assertEquals(array('rgba' => true, 'calc' => true), $declaration->functions); + } +} diff --git a/tests/unit/CssCrush/ExtendArgTest.php b/tests/unit/CssCrush/ExtendArgTest.php new file mode 100644 index 0000000..856c57c --- /dev/null +++ b/tests/unit/CssCrush/ExtendArgTest.php @@ -0,0 +1,15 @@ +assertEquals('.foo :hover', $extend_arg->name); + $this->assertEquals(':hover', $extend_arg->pseudo); + } +} diff --git a/tests/unit/CssCrush/HookTest.php b/tests/unit/CssCrush/HookTest.php new file mode 100644 index 0000000..62f9de7 --- /dev/null +++ b/tests/unit/CssCrush/HookTest.php @@ -0,0 +1,74 @@ +dummy_hook = __NAMESPACE__ . '\dummy_hook'; + } + + public function tearDown() + { + Hook::$register = array(); + } + + public function testAdd() + { + Hook::add('foo', $this->dummy_hook); + Hook::add('foo', 'strtoupper'); + + $this->assertEquals(array('foo' => array($this->dummy_hook=>true, 'strtoupper'=>true)), Hook::$register); + + return Hook::$register; + } + + /** + * @depends testAdd + */ + public function testRemove($register) + { + Hook::$register = $register; + + Hook::remove('foo', 'strtoupper'); + + $this->assertEquals(array('foo' => array($this->dummy_hook=>true)), Hook::$register); + + return Hook::$register; + } + + /** + * @depends testRemove + */ + public function testRun($register) + { + Hook::$register = $register; + + Hook::run('foo', $this); + + $this->assertTrue($this->hookRan); + + return Hook::$register; + } + + /** + * @depends testRun + */ + public function testReset($register) + { + Hook::$register = $register; + + Hook::reset(); + + $this->assertEquals(array(), Hook::$register); + } +} + +function dummy_hook(HookTest $test) +{ + $test->hookRan = true; +} diff --git a/tests/unit/CssCrush/LoggerTest.php b/tests/unit/CssCrush/LoggerTest.php new file mode 100644 index 0000000..bb54cc3 --- /dev/null +++ b/tests/unit/CssCrush/LoggerTest.php @@ -0,0 +1,18 @@ +assertTrue($logger instanceof LoggerInterface); + } +} diff --git a/tests/unit/CssCrush/OptionsTest.php b/tests/unit/CssCrush/OptionsTest.php new file mode 100644 index 0000000..4a44828 --- /dev/null +++ b/tests/unit/CssCrush/OptionsTest.php @@ -0,0 +1,92 @@ +testFile = temp_file("\n foo {bar: baz;} \n\n baz {bar: foo;}"); + } + + public function testBoilerplate() + { + $boilerplate = << temp_file($boilerplate), + 'newlines' => 'unix', + )); + + $this->assertContains(' * ' . csscrush_version(), $result); + $this->assertContains(" * Line breaks\n * preserved\n *", $result); + } + + public function testFormatters() + { + $sample = '/* A comment */ foo {bar: baz;}'; + + $single_line_expected = << 'single-line')); + $this->assertEquals($single_line_expected, $single_line); + + $padded_expected = << 'padded')); + $this->assertEquals($padded_expected, $padded); + + $block_expected = << 'block')); + $this->assertEquals($block_expected, $block); + } + + public function testSourceMaps() + { + csscrush_file($this->testFile, array('source_map' => true)); + $source_map_contents = file_get_contents("$this->testFile.crush.css.map"); + + $this->assertRegExp('~"version": ?"3",~', $source_map_contents); + } + + public function testTrace() + { + csscrush_file($this->testFile, array('trace' => true)); + $output_contents = file_get_contents("$this->testFile.crush.css"); + + $this->assertContains('@media -sass-debug-info', $output_contents); + + csscrush_file($this->testFile, array('trace' => array('stubs'))); + $output_contents = file_get_contents("$this->testFile.crush.css"); + + $this->assertContains('@media -sass-debug-info', $output_contents); + } + + public function testAdvancedMinify() + { + $sample = "foo { color: papayawhip; color: #cccccc;}"; + $output = csscrush_string($sample, array('minify' => array('colors'))); + + $this->assertEquals('foo{color:#ffefd5;color:#ccc}', $output); + } +} diff --git a/tests/unit/CssCrush/PluginTest.php b/tests/unit/CssCrush/PluginTest.php new file mode 100644 index 0000000..a8906ae --- /dev/null +++ b/tests/unit/CssCrush/PluginTest.php @@ -0,0 +1,66 @@ +pluginDirs[] = $dummy_plugin_dir; + + $dummy_plugin = << function () { + define('DUMMY_ENABLE_TEST', true); + }, + 'disable' => function () { + define('DUMMY_DISABLE_TEST', true); + }, +)); +TPL; + file_put_contents("$dummy_plugin_dir/dummy.php", $dummy_plugin); + + Plugin::$plugins = array(); + } + + public function tearDown() + { + Plugin::$plugins = array(); + } + + public function testInfo() + { + $info = Plugin::info(); + + $this->assertArrayHasKey('svg', $info); + } + + public function testParseDoc() + { + $test_path = Crush::$dir . '/plugins/svg.php'; + $result = Plugin::parseDoc($test_path); + + $this->assertContains('SVG', $result[0]); + } + + public function testLoad() + { + $result = Plugin::load('dummy'); + + $this->assertArrayHasKey('dummy', Plugin::$plugins); + + Plugin::enable('dummy'); + + $this->assertTrue(DUMMY_ENABLE_TEST); + + Plugin::disable('dummy'); + + $this->assertTrue(DUMMY_DISABLE_TEST); + } +} diff --git a/tests/unit/CssCrush/RegexTest.php b/tests/unit/CssCrush/RegexTest.php new file mode 100644 index 0000000..8d3ef73 --- /dev/null +++ b/tests/unit/CssCrush/RegexTest.php @@ -0,0 +1,39 @@ +assertEquals('~(?\(\s*(?(?:(?>[^()]+)|(?&parens))*)\))~S', + Regex::make('~{{ parens }}~S')); + + $this->assertEquals('~ #[[:xdigit:]]{3} ~xS', Regex::make('~ #{{hex}}{3} ~xS')); + } + + public function testMakeFunctionPatt() + { + $patt = Regex::makeFunctionPatt(array('foo', 'bar')); + $this->assertEquals('~((? true)); + $this->assertEquals('~((? true)); + $this->assertEquals('~(#|(?assertEquals($expected, $matches); + } +} diff --git a/tests/unit/CssCrush/SelectorTest.php b/tests/unit/CssCrush/SelectorTest.php new file mode 100644 index 0000000..e929496 --- /dev/null +++ b/tests/unit/CssCrush/SelectorTest.php @@ -0,0 +1,46 @@ +process = bootstrap_process(); + } + + public function testMakeReadable() + { + $sample = '#foo+bar [data="baz"]~ p:first-child .foo >bar::after'; + $sample = $this->process->tokens->capture($sample, 's'); + + $this->assertEquals('#foo + bar [data="baz"] ~ p:first-child .foo > bar::after', + Selector::makeReadable($sample)); + } + + public function testNormalizeWhiteSpace() + { + $sample = "#foo+bar [data=baz ]~ p:first-child .foo\n\n\t >bar::after"; + + $this->assertEquals('#foo + bar [data=baz] ~ p:first-child .foo > bar::after', + Selector::normalizeWhiteSpace($sample)); + } + + public function testAppendPseudo() + { + $test = new Selector('.foo'); + $test->appendPseudo(':hover'); + + $this->assertEquals('.foo:hover', $test->__toString()); + } + + public function testToString() + { + $this->process->minifyOutput = true; + $test = new Selector('.foo > .bar + .baz'); + + $this->assertEquals('.foo>.bar+.baz', $test->__toString()); + } +} diff --git a/tests/unit/CssCrush/StreamTest.php b/tests/unit/CssCrush/StreamTest.php new file mode 100644 index 0000000..a2c14ad --- /dev/null +++ b/tests/unit/CssCrush/StreamTest.php @@ -0,0 +1,74 @@ +sample); + $this->assertEquals($this->sample, (string) $stream); + } + + public function testEndsWith() + { + $this->assertTrue(Stream::endsWith('amet', 'et')); + } + + public function testUpdate() + { + $stream = new Stream($this->sample); + $updated_text = 'foo'; + $stream->update($updated_text); + $this->assertEquals($updated_text, (string) $stream); + } + + public function testTrim() + { + $stream = new Stream($this->sample); + $this->assertEquals(trim($this->sample), (string) $stream->trim()); + } + + public function testRTrim() + { + $stream = new Stream($this->sample); + $this->assertEquals(rtrim($this->sample), (string) $stream->rTrim()); + } + + public function testLTrim() + { + $stream = new Stream($this->sample); + $this->assertEquals(ltrim($this->sample), (string) $stream->lTrim()); + } + + public function testAppend() + { + $stream = new Stream($this->sample); + $append_text = 'foo'; + $this->assertEquals($this->sample . $append_text, (string) $stream->append($append_text)); + } + + public function testPrepend() + { + $stream = new Stream($this->sample); + $prepend_text = 'foo'; + $this->assertEquals($prepend_text . $this->sample, (string) $stream->prepend($prepend_text)); + } + + public function testSubstr() + { + $stream = new Stream($this->sample); + $prepend_text = 'foo'; + $this->assertEquals(substr($this->sample, 1), (string) $stream->substr(1)); + } +} + +// matchAll +// replaceHash +// pregReplaceCallback +// pregReplaceHash +// splice diff --git a/tests/unit/CssCrush/TemplateTest.php b/tests/unit/CssCrush/TemplateTest.php new file mode 100644 index 0000000..2ff2c99 --- /dev/null +++ b/tests/unit/CssCrush/TemplateTest.php @@ -0,0 +1,83 @@ +template_raw = <<template_string = <<template = new Template($this->template_raw); + } + + public function test__construct() + { + $this->assertEquals($this->template_string, $this->template->string); + } + + public function testGetArgValue() + { + $args = array('default'); + $this->assertEquals('100%', $this->template->getArgValue(0, $args)); + + $args = array('foo', 'bar'); + $this->assertEquals('bar', $this->template->getArgValue(1, $args)); + } + + public function testPrepare() + { + $this->template->prepare(array('one', 'two')); + $this->assertEquals( + array(array('?a0?', '?a1?'), array('one', 'two')), + $this->template->substitutions); + } + + public function testApply() + { + $actual = $this->template->apply(array('one', 'two')); + $expected = <<assertEquals($expected, $actual); + + $actual = $this->template->apply(array('default', 'colanut')); + $expected = <<assertEquals($expected, $actual); + } + + public function testTokenize() + { + $original_sample = <<assertContains('[foo=?s', $sample); + $this->assertContains('{baz: ?u', $sample); + + $sample = Template::unTokenize($sample); + $this->assertEquals($original_sample, $sample); + } +} diff --git a/tests/unit/CssCrush/TokensTest.php b/tests/unit/CssCrush/TokensTest.php new file mode 100644 index 0000000..2831d07 --- /dev/null +++ b/tests/unit/CssCrush/TokensTest.php @@ -0,0 +1,114 @@ +process = bootstrap_process(array('minify' => false)); + + $this->tokens = $this->process->tokens; + $this->tokens->add('"foo"', 's'); + $this->tokens->add('"bar"', 's'); + $this->tokens->add('"baz"', 's'); + } + + public function test__construct() + { + $this->assertEmpty( + array_diff_key( + array_flip(array('s', 'c', 'r', 'u', 't')), + (array) $this->tokens->store + ) + ); + } + + public function testCreateLabel() + { + $type = 's'; + $this->assertRegExp("~^\?{$type}[a-z0-9]+\?$~", $this->tokens->createLabel($type)); + } + + public function testAdd() + { + $label = $this->tokens->add('/*monkey*/', 'c'); + $this->assertContains('/*monkey*/', array_values($this->tokens->store->c)); + } + + public function testGet() + { + $value = reset($this->tokens->store->s); + $key = key($this->tokens->store->s); + $this->assertEquals($value, $this->tokens->get($key)); + } + + public function testRelease() + { + $label = $this->tokens->add('"foo"', 's'); + $this->assertTrue(isset($this->tokens->store->s[$label])); + + $this->tokens->pop($label); + $this->assertFalse(isset($this->tokens->store->s[$label])); + } + + public function testPop() + { + $label = $this->tokens->add('"foo"', 's'); + + $this->assertEquals('"foo"', $this->tokens->pop($label)); + $this->assertFalse(isset($this->tokens->store->s[$label])); + } + + public function testCapture() + { + $sample = '[class="foo"] {bar: url(http://github.com/baz.png);}'; + + $sample = $this->tokens->capture($sample, 'u'); + $this->assertContains('?u', $sample); + + $sample = $this->tokens->capture($sample, 's'); + $this->assertContains('?s', $sample); + } + + public function testCaptureUrls() + { + $sample = '[class="foo"] {bar: url(http://github.com/baz.png);}'; + + $sample = $this->tokens->captureUrls($sample); + $this->assertContains('?u', $sample); + } + + public function testRestore() + { + $sample = '[class="foo"] {bar: url(http://github.com/baz.png);}'; + + $modified = $this->tokens->captureUrls($sample); + $this->assertContains('?u', $modified); + + $modified = $this->tokens->restore($modified, 'u'); + $this->assertEquals($sample, $modified); + + $modified = $this->tokens->capture($sample, 's'); + $this->assertContains('?s', $modified); + + $modified = $this->tokens->restore($modified, 's'); + $this->assertEquals($sample, $modified); + } + + public function testPad() + { + $label = $this->tokens->createLabel('s'); + $padded_label = Tokens::pad($label, "\n lorem \n ipsum \n123"); + $this->assertEquals("$label\n\n\n ", $padded_label); + } + + public function testIs() + { + $this->assertTrue(Tokens::is($this->tokens->createLabel('s'), 's')); + } +} diff --git a/tests/unit/CssCrush/UrlTest.php b/tests/unit/CssCrush/UrlTest.php new file mode 100644 index 0000000..5759499 --- /dev/null +++ b/tests/unit/CssCrush/UrlTest.php @@ -0,0 +1,133 @@ + false)); + } + + public function testConstruct() + { + $url = new Url('http://www.public.com/'); + $this->assertEquals('http', $url->protocol); + $this->assertTrue($url->isAbsolute); + $this->assertFalse($url->isRelative); + $this->assertFalse($url->isRooted); + $this->assertFalse($url->isData); + + $url = new Url('//www.public.com'); + $this->assertEquals('relative', $url->protocol); + $this->assertTrue($url->isAbsolute); + $this->assertFalse($url->isRelative); + $this->assertFalse($url->isRooted); + $this->assertFalse($url->isData); + + $url = new Url('local/resource.png'); + $this->assertNull($url->protocol); + $this->assertFalse($url->isAbsolute); + $this->assertTrue($url->isRelative); + $this->assertFalse($url->isRooted); + $this->assertFalse($url->isData); + + $url = new Url('/local/resource.png'); + $this->assertNull($url->protocol); + $this->assertFalse($url->isAbsolute); + $this->assertFalse($url->isRelative); + $this->assertTrue($url->isRooted); + $this->assertFalse($url->isData); + + $url = new Url('data:text/html'); + $this->assertEquals('data', $url->protocol); + $this->assertFalse($url->isAbsolute); + $this->assertFalse($url->isRelative); + $this->assertFalse($url->isRooted); + $this->assertTrue($url->isData); + } + + public function testToString() + { + $url = new Url('resource/with(parens)'); + $this->assertEquals('url("http://github.com/resource/with(parens)")', (string) $url); + + $url = new Url('simple/url'); + $this->assertEquals('url(http://github.com/simple/url)', (string) $url); + } + + public function testUpdate() + { + $url = new Url('simple/url'); + $update_url = 'different/url'; + $url->update($update_url); + $this->assertEquals($update_url, $url->value); + } + + public function testGetAbsolutePath() + { + $url_raw = 'simple/url'; + $url = new Url($url_raw); + $this->assertEquals(Crush::$process->docRoot . "/$url_raw", $url->getAbsolutePath()); + } + + public function testPrepend() + { + $url = new Url('simple/url'); + $this->assertEquals('../simple/url', $url->prepend('../')->value); + } + + public function testToRoot() + { + $url = new Url('simple/url'); + $this->assertEquals(Crush::$process->input->dirUrl . '/simple/url', $url->toRoot()->value); + } + + public function testToData() + { + $test_filename = str_replace('\\', '_', __CLASS__) . '.svg'; + $test_fileurl = "/tests/unit/$test_filename"; + $test_filepath = Crush::$process->docRoot . $test_fileurl; + + if (is_writable(dirname($test_filepath))) { + $svg = ''; + file_put_contents($test_filepath, $svg); + $url = new Url($test_fileurl); + $url->toData(); + unlink($test_filepath); + + $this->assertEquals( + 'data:image/svg+xml;base64,PHN2Zz48cGF0aCBkPSJNMCwwIGgxMCBsLTEwLDEweiIvPjwvc3ZnPg==', + $url->value); + } + else { + $this->markTestSkipped('Cannot write test SVG file to disk.'); + } + } + + public function testSetType() + { + $url = new Url('simple/url'); + $url->setType('absolute'); + + $this->assertTrue($url->isAbsolute); + $this->assertFalse($url->isRelative); + $this->assertFalse($url->isRooted); + $this->assertFalse($url->isData); + } + + public function testSimplify() + { + $url = new Url("/some/../path/../something.css"); + $this->assertEquals('/something.css', $url->simplify()->value); + + $url = new Url("/some/../..//..\path/../something.css"); + $this->assertEquals('/../../something.css', $url->simplify()->value); + + $url = new Url("../../../blah/../../something.css"); + $this->assertEquals('../../../../something.css', $url->simplify()->value); + } +} diff --git a/tests/unit/CssCrush/UtilTest.php b/tests/unit/CssCrush/UtilTest.php new file mode 100644 index 0000000..e6e5484 --- /dev/null +++ b/tests/unit/CssCrush/UtilTest.php @@ -0,0 +1,91 @@ +assertEquals('/Some/crazy/Path', Util::normalizePath('C:\\Some\crazy/Path\\', true)); + $this->assertEquals('/Some/crazy/Path', Util::normalizePath('/\Some//./crazy\\\/Path/')); + $this->assertEquals('sane/path', Util::normalizePath('./sane/path/')); + } + + public function testHtmlAttributes() + { + $attributes = array( + 'rel' => 'stylesheet', + 'id' => 'foo', + 'media' => 'screen', + ); + + $this->assertEquals( + ' rel="stylesheet" id="foo" media="screen"', + Util::htmlAttributes($attributes)); + $this->assertEquals( + ' id="foo" media="screen" rel="stylesheet"', + Util::htmlAttributes($attributes, array('id', 'media', 'rel'))); + } + + public function testSimplifyPath() + { + $this->assertEquals('bar', Util::simplifyPath('foo/../bar')); + $this->assertEquals('./../', Util::simplifyPath('./foo/../bar/../../')); + } + + public function testVlqEncode() + { + $this->assertEquals('A', Util::vlqEncode(0)); + $this->assertEquals('C', Util::vlqEncode(1)); + $this->assertEquals('gB', Util::vlqEncode(16)); + $this->assertEquals('6H', Util::vlqEncode(125)); + $this->assertEquals('qmC', Util::vlqEncode(1125)); + } + + public function testStripCommentTokens() + { + $this->assertEquals('', Util::stripCommentTokens('?ca??cb?')); + } + + public function testResolveUserPath() + { + $this->assertEquals(__FILE__, Util::resolveUserPath(__FILE__)); + $this->assertFalse(Util::resolveUserPath(__FILE__ . 'nothing')); + + // Relative path resolution. + $original_path = getcwd(); + chdir(__DIR__); + $this_filename = basename(__FILE__); + $this->assertEquals(__FILE__, Util::resolveUserPath($this_filename)); + chdir($original_path); + } + + public function testNormalizeWhiteSpace() + { + $this->assertEquals( + '.foo[class]{rgb(0,0,0);}', + Util::normalizeWhiteSpace(".foo[ class ] { \t rgb( \t0\n , 0, 0\r\n ) ; } ")); + } + + public function testSplitDelimList() + { + $this->assertEquals(array('foo(1,2)','3','4'), Util::splitDelimList("foo(1,2), 3,4")); + $this->assertEquals(array(), Util::splitDelimList(" ; ; ", ';')); + } + + public function testGetLinkBetweenPaths() + { + $path1 = __DIR__; + $path2 = realpath(__DIR__ . '/../../'); + $this->assertEquals('../../', Util::getLinkBetweenPaths($path1, $path2)); + $this->assertEquals('unit/CssCrush/', Util::getLinkBetweenPaths($path2, $path1)); + } + + public function testFilePutContents() + { + $test_file = sys_get_temp_dir() . '/' . str_replace('\\', '_', __CLASS__); + $this->assertTrue(Util::filePutContents($test_file, 'Hello Mum')); + } +} diff --git a/tests/unit/CssCrush/VersionTest.php b/tests/unit/CssCrush/VersionTest.php new file mode 100644 index 0000000..4cd2453 --- /dev/null +++ b/tests/unit/CssCrush/VersionTest.php @@ -0,0 +1,45 @@ +minor = 9; + $version->extra = 'beta'; + $this->assertEquals('v2.9.5-beta', (string) $version); + + unset($version->extra); + $this->assertEquals('v2.9.5', (string) $version); + } + + public function testCompare() + { + $version = new Version('1.8.5-beta'); + $this->assertEquals(1, $version->compare('1')); + $this->assertEquals(-1, $version->compare('2')); + $this->assertEquals(0, $version->compare('1.8.5')); + } + + public function testProperties() + { + $version = new Version('1.8.5'); + $this->assertEquals(1, $version->major); + $this->assertEquals(8, $version->minor); + $this->assertEquals(5, $version->patch); + } + + public function testGitDescribe() + { + if ($version = Version::gitDescribe()) { + $this->assertRegExp('~^v\d+\.\d+\.\d+-\d+-g.+$~', $version->__toString()); + } + else { + $this->markTestSkipped('Returned null'); + } + } +} diff --git a/tests/unit/api/apiTest.php b/tests/unit/api/apiTest.php new file mode 100644 index 0000000..581caf2 --- /dev/null +++ b/tests/unit/api/apiTest.php @@ -0,0 +1,118 @@ +sample = ".foo {bar: baz;}"; + $this->sampleExpected = ".foo{bar:baz}"; + $this->sampleFile = temp_file($this->sample); + $this->originalWd = getcwd(); + chdir(dirname($this->sampleFile)); + } + + public function tearDown() + { + chdir($this->originalWd); + } + + public function testString() + { + $this->assertEquals($this->sampleExpected, csscrush_string($this->sample)); + } + + public function testInline() + { + $this->assertEquals("\n", csscrush_inline($this->sampleFile)); + $this->assertEquals("\n", csscrush_inline($this->sampleFile, null, array( + 'type' => 'text/css', + 'id' => 'foo', + ))); + } + + public function testFile() + { + $test_dir = dirname($this->sampleFile); + $test_file = csscrush_file($this->sampleFile, array( + 'versioning' => false, + 'cache' => false, + 'doc_root' => $test_dir, + 'boilerplate' => false, + )); + $filepath = "$test_dir$test_file"; + + $this->assertEquals($this->sampleExpected, file_get_contents($filepath)); + } + + public function testTag() + { + $test_dir = dirname($this->sampleFile); + $base_options = array( + 'versioning' => false, + 'cache' => false, + 'doc_root' => $test_dir, + 'boilerplate' => false, + ); + + $url = '/' . basename($this->sampleFile) . '.crush.css'; + $test_tag = csscrush_tag($this->sampleFile, $base_options); + + $this->assertEquals("\n", $test_tag); + + $test_tag = csscrush_tag($this->sampleFile, $base_options, array('media' => 'print', 'id' => 'foo')); + + $this->assertEquals("\n", $test_tag); + } + + public function testStat() + { + $sample = << false, + 'trace' => array( 'selector_count', 'rule_count' ), + 'disable' => array( 'all' ), + )); + + $stats = csscrush_stat(); + + $this->assertEquals(6, $stats['selector_count']); + $this->assertEquals(2, $stats['rule_count']); + $this->assertEquals(array(), $stats['vars']); + $this->assertEquals(array(), $stats['errors']); + $this->assertTrue(isset($stats['compile_time'])); + } + + public function testGetSet() + { + csscrush_set('config', function ($config) { + $config->foo = 'bar'; + }); + $this->assertEquals('bar', csscrush_get('config', 'foo')); + + csscrush_set('config', array( + 'hello' => 'world', + )); + $this->assertEquals('world', csscrush_get('config', 'hello')); + + $this->assertInstanceOf('stdClass', csscrush_get('config')); + + $this->assertInstanceOf('CssCrush\Options', csscrush_get('options')); + + csscrush_set('options', array('enable' => 'property-sorter')); + + $this->assertContains('property-sorter', csscrush_get('options', 'enable')); + + csscrush_set('options', array('enable' => array())); + } +} diff --git a/tests/unit/cli/cliTest.php b/tests/unit/cli/cliTest.php new file mode 100644 index 0000000..de579f6 --- /dev/null +++ b/tests/unit/cli/cliTest.php @@ -0,0 +1,56 @@ +path = Crush::$dir . '/cli.php'; + $this->sample = 'p {color: red; position: absolute; opacity: 1;}'; + } + + public function testHelp() + { + exec("php \"$this->path\"", $lines); + $help_text = implode("\n", $lines); + + $this->assertContains('USAGE:', $help_text); + } + + public function testPlugin() + { + exec("echo '$this->sample' | php \"$this->path\" --enable property-sorter", $lines); + $expected = 'p{position:absolute;opacity:1;color:red}'; + + $this->assertEquals($expected, implode('',$lines)); + } + + public function testIO() + { + $in_path = temp_file(); + $out_path = temp_file(); + + file_put_contents($in_path, $this->sample); + exec("php \"$this->path\" -i '$in_path' -o '$out_path' --enable property-sorter", $lines); + $expected = 'p{position:absolute;opacity:1;color:red}'; + + $this->assertEquals($expected, file_get_contents($out_path)); + } + + public function testContext() + { + $sample = '@import "http://github.com/context/import.css"; baz {bar: foo;}'; + $context = __DIR__; + + exec("echo '$sample' | php \"$this->path\" --context '$context'", $lines); + $expected = 'foo{bar:baz}baz{bar:foo}'; + + $this->assertEquals($expected, implode('',$lines)); + } +} diff --git a/tests/unit/cli/context/import.css b/tests/unit/cli/context/import.css new file mode 100644 index 0000000..f02c4bc --- /dev/null +++ b/tests/unit/cli/context/import.css @@ -0,0 +1 @@ +foo {bar: baz;} \ No newline at end of file From 21a836f9467627bd8ac6cf216e5fd865961b6f32 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 27 Dec 2013 19:30:35 +0000 Subject: [PATCH 233/421] Made plugin disabling explicit. Renaming plugin hsl-to-hex => hsl2hex for consistency. --- .travis.yml | 1 - lib/CssCrush/Functions.php | 2 +- lib/CssCrush/Hook.php | 11 +------ lib/CssCrush/Process.php | 44 ++++++++++++------------- plugins/{hsl-to-hex.php => hsl2hex.php} | 8 ++--- tests/bootstrap.php | 2 +- tests/unit/CssCrush/HookTest.php | 12 ------- 7 files changed, 28 insertions(+), 52 deletions(-) rename plugins/{hsl-to-hex.php => hsl2hex.php} (77%) diff --git a/.travis.yml b/.travis.yml index 4128d44..cca64a0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,3 @@ - language: php php: diff --git a/lib/CssCrush/Functions.php b/lib/CssCrush/Functions.php index c279d38..c3cdcf2 100644 --- a/lib/CssCrush/Functions.php +++ b/lib/CssCrush/Functions.php @@ -120,7 +120,7 @@ public static function executeOnString($str, $patt = null, $process_callback = n public static function register($name, $callback) { - Functions::$customFunctions[ $name] = $callback; + Functions::$customFunctions[$name] = $callback; } public static function deRegister($name) diff --git a/lib/CssCrush/Hook.php b/lib/CssCrush/Hook.php index 9d59f41..036995d 100644 --- a/lib/CssCrush/Hook.php +++ b/lib/CssCrush/Hook.php @@ -1,22 +1,18 @@ options->global_vars = $config->vars; - $this->docRoot = isset($this->options->doc_root) ? $this->options->doc_root : $config->docRoot; - // Shortcut commonly used options to avoid __get() overhead. + $this->docRoot = isset($this->options->doc_root) ? $this->options->doc_root : $config->docRoot; $this->addTracingStubs = in_array('stubs', $this->options->__get('trace')); $this->generateMap = $this->ioContext === 'file' && $this->options->__get('source_map'); $this->ruleFormatter = $this->options->__get('formatter'); @@ -361,7 +360,6 @@ protected function filterPlugins() $disable = array_flip($options->disable); $enable = array_flip($options->enable); - // Disable has the special 'all' option. if (isset($disable['all'])) { $disable = $config->plugins; } @@ -381,7 +379,6 @@ protected function filterPlugins() } } - // Enable all plugins in the remaining list. foreach ($this->plugins as $plugin_name => $bool) { Plugin::enable($plugin_name); } @@ -393,13 +390,7 @@ protected function filterPlugins() protected function captureVars() { - $vars_patt = Regex::make('~ - @define - (?: - \s* {{block}} | - \s+ (?{{ident}}) \s+ (?[^;]+) \s* ; - ) - ~ixS'); + $vars_patt = Regex::make('~@define(?:\s*{{ block }}|\s+(?{{ ident }})\s+(?[^;]+)\s*;)~iS'); $this->stream->pregReplaceCallback($vars_patt, function ($m) { if (isset($m['name'])) { @@ -407,9 +398,9 @@ protected function captureVars() } else { Crush::$process->vars = DeclarationList::parse($m['block_content'], array( - 'keyed' => true, - 'ignore_directives' => true, - )) + Crush::$process->vars; + 'keyed' => true, + 'ignore_directives' => true, + )) + Crush::$process->vars; } }); @@ -875,7 +866,7 @@ protected function collate() } } - public function prepare() + public function preCompile() { // Ensure relevant ini settings aren't too conservative. if (ini_get('pcre.backtrack_limit') < 1000000) { @@ -885,19 +876,28 @@ public function prepare() ini_set('memory_limit', '128M'); } - Hook::reset(); $this->filterPlugins(); $this->filterAliases(); Functions::setMatchPatt(); + + $this->stat['compile_start_time'] = microtime(true); } - public function compile() + public function postCompile() { - // Always store start time. - $this->stat['compile_start_time'] = microtime(true); + foreach ($this->plugins as $plugin_name => $bool) { + Plugin::disable($plugin_name); + } - $this->prepare(); + $this->release(); + + Crush::runStat('compile_time'); + } + + public function compile() + { + $this->preCompile(); // Collate hostfile and imports. $this->stream = new Stream(Importer::hostfile($this->input)); @@ -931,9 +931,7 @@ public function compile() $this->collate(); - $this->release(); - - Crush::runStat('compile_time'); + $this->postCompile(); return $this->stream; } diff --git a/plugins/hsl-to-hex.php b/plugins/hsl2hex.php similarity index 77% rename from plugins/hsl-to-hex.php rename to plugins/hsl2hex.php index 8250c4b..f600b6c 100644 --- a/plugins/hsl-to-hex.php +++ b/plugins/hsl2hex.php @@ -10,17 +10,17 @@ */ namespace CssCrush; -Plugin::register('hsl-to-hex', array( +Plugin::register('hsl2hex', array( 'enable' => function () { - Hook::add('rule_postalias', 'CssCrush\hsl_to_hex'); + Hook::add('rule_postalias', 'CssCrush\hsl2hex'); }, 'disable' => function () { - Hook::remove('rule_postalias', 'CssCrush\hsl_to_hex'); + Hook::remove('rule_postalias', 'CssCrush\hsl2hex'); }, )); -function hsl_to_hex(Rule $rule) { +function hsl2hex(Rule $rule) { $hsl_patt = Regex::make('~{{ LB }}hsl({{ parens }})~i'); diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 0786492..88427b8 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -17,7 +17,7 @@ function bootstrap_process($options = array()) { $process = \CssCrush\Crush::$process = new \CssCrush\Process($options); - $process->prepare(); + $process->preCompile(); $process->resolveContext(); return $process; } diff --git a/tests/unit/CssCrush/HookTest.php b/tests/unit/CssCrush/HookTest.php index 62f9de7..c2b00b9 100644 --- a/tests/unit/CssCrush/HookTest.php +++ b/tests/unit/CssCrush/HookTest.php @@ -54,18 +54,6 @@ public function testRun($register) return Hook::$register; } - - /** - * @depends testRun - */ - public function testReset($register) - { - Hook::$register = $register; - - Hook::reset(); - - $this->assertEquals(array(), Hook::$register); - } } function dummy_hook(HookTest $test) From ae6ce026bc5e2e1f89030f3d8203d28bf1936fa6 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 27 Dec 2013 19:53:16 +0000 Subject: [PATCH 234/421] Adding PHP 5.5 as a test version. Adding Travis build status image. [ci skip] --- .travis.yml | 1 + README.md | 2 ++ 2 files changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index cca64a0..7caf104 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ language: php php: - 5.3 - 5.4 + - 5.5 before_script: - composer self-update diff --git a/README.md b/README.md index d4718e9..73c78c8 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +[![Build Status](https://travis-ci.org/peteboere/css-crush.png)](https://travis-ci.org/peteboere/css-crush) + Logo CSS-Crush is a standards inspired preprocessor designed to enable a modern and uncluttered CSS workflow. From 887bd37da5331d1ff12b902e02c606619cceb5ba Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 28 Dec 2013 13:04:33 +0000 Subject: [PATCH 235/421] Adding a plugin load handler option to keep things tidy. A little color plugin tidying. --- lib/CssCrush/Plugin.php | 4 ++++ plugins/color.php | 4 ++-- plugins/svg-gradients.php | 5 +++-- plugins/svg.php | 7 +++++-- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/lib/CssCrush/Plugin.php b/lib/CssCrush/Plugin.php index f5d8e6d..865fcf7 100644 --- a/lib/CssCrush/Plugin.php +++ b/lib/CssCrush/Plugin.php @@ -70,6 +70,10 @@ public static function load($plugin_name) if (! $found) { notice("[[CssCrush]] - Plugin '$plugin_name' not found."); } + elseif (isset(self::$plugins[$plugin_name]['load'])) { + $plugin_load = self::$plugins[$plugin_name]['load']; + $plugin_load(); + } } return isset(self::$plugins[$plugin_name]) ? self::$plugins[$plugin_name] : null; diff --git a/plugins/color.php b/plugins/color.php index d08cb23..99e372a 100644 --- a/plugins/color.php +++ b/plugins/color.php @@ -57,7 +57,7 @@ function color_capture($process) { foreach ($captured_keywords as $key => $value) { $value = Functions::executeOnString($value); if (! isset($native_keywords[$key]) && $rgba = Color::parse($value)) { - $custom_keywords[$key] = $rgba; + $custom_keywords[] = $key; Crush::$process->stat['colors'][$key] = new Color($rgba); Crush::$process->colorKeywords[$key] = $rgba; } @@ -65,7 +65,7 @@ function color_capture($process) { if ($custom_keywords) { $GLOBALS['CSSCRUSH_COLOR_PATT'] = Regex::make('~{{ LB }}(?' . - implode('|', array_keys($custom_keywords)) . '){{ RB }}~iS'); + implode('|', $custom_keywords) . '){{ RB }}~iS'); } } } diff --git a/plugins/svg-gradients.php b/plugins/svg-gradients.php index 06e252f..f87a8e4 100644 --- a/plugins/svg-gradients.php +++ b/plugins/svg-gradients.php @@ -41,9 +41,10 @@ */ namespace CssCrush; -$GLOBALS['CSSCRUSH_SVG_GRADIENT_UID'] = 0; - Plugin::register('svg-gradients', array( + 'load' => function () { + $GLOBALS['CSSCRUSH_SVG_GRADIENT_UID'] = 0; + }, 'enable' => function () { $GLOBALS['CSSCRUSH_SVG_GRADIENT_UID'] = 0; Functions::register('svg-linear-gradient', 'CssCrush\fn__svg_linear_gradient'); diff --git a/plugins/svg.php b/plugins/svg.php index 8a8cd6c..a19c549 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -60,7 +60,11 @@ namespace CssCrush; Plugin::register('svg', array( + 'load' => function () { + $GLOBALS['CSSCRUSH_SVG_UID'] = 0; + }, 'enable' => function () { + $GLOBALS['CSSCRUSH_SVG_UID'] = 0; Hook::add('capture_phase2', 'CssCrush\svg_capture'); Functions::register('svg', 'CssCrush\fn__svg'); Functions::register('svg-data', 'CssCrush\fn__svg_data'); @@ -820,8 +824,7 @@ function svg_fn_radial_gradient($input, $element) { function svg_fn_pattern($input, $element) { - static $uid = 0; - $pid = 'p' . (++$uid); + $pid = 'p' . (++$GLOBALS['CSSCRUSH_SVG_UID']); // Get args in order with defaults. list($url, $transform_list, $width, $height, $x, $y) = From 757b80bb2dc931bfddca7c837b336adc6cfa398b Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 11 Jan 2014 20:03:22 +0000 Subject: [PATCH 236/421] Added settings interface for plugins and CSS environment. Old variable based settings (as used in rem and px2em plugins) is now deprecated. --- lib/CssCrush/DeclarationList.php | 5 +++ lib/CssCrush/Options.php | 1 + lib/CssCrush/Process.php | 40 ++++++++++++++++++---- lib/CssCrush/Settings.php | 57 ++++++++++++++++++++++++++++++++ plugins/canvas.php | 6 ++-- plugins/color.php | 20 +++++------ plugins/px2em.php | 23 ++----------- plugins/rem.php | 14 ++------ plugins/svg.php | 6 ++-- 9 files changed, 118 insertions(+), 54 deletions(-) create mode 100644 lib/CssCrush/Settings.php diff --git a/lib/CssCrush/DeclarationList.php b/lib/CssCrush/DeclarationList.php index e83f471..0dc01b4 100644 --- a/lib/CssCrush/DeclarationList.php +++ b/lib/CssCrush/DeclarationList.php @@ -331,6 +331,7 @@ public static function parse($str, $options = array()) $options += array( 'keyed' => false, 'ignore_directives' => false, + 'lowercase_keys' => false, 'context' => null, 'flatten' => false, 'apply_hooks' => false, @@ -358,6 +359,10 @@ public static function parse($str, $options = array()) $property = trim(substr($line, 0, $colon_pos)); $value = trim(substr($line, $colon_pos + 1)); + if ($options['lowercase_keys']) { + $property = strtolower($property); + } + if ($options['apply_hooks']) { Hook::run('declaration_preprocess', array('property' => &$property, 'value' => &$value)); } diff --git a/lib/CssCrush/Options.php b/lib/CssCrush/Options.php index 73f5db3..90f2497 100644 --- a/lib/CssCrush/Options.php +++ b/lib/CssCrush/Options.php @@ -26,6 +26,7 @@ class Options 'rewrite_import_urls' => true, 'enable' => null, 'disable' => null, + 'settings' => array(), 'stat_dump' => false, 'trace' => array(), 'source_map' => false, diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 96682b7..b765673 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -25,6 +25,7 @@ public function __construct($user_options = array(), $dev_options = array()) $this->charset = null; $this->sources = array(); $this->vars = array(); + $this->settings = array(); $this->misc = new \stdClass(); $this->input = new \stdClass(); $this->output = new \stdClass(); @@ -54,8 +55,6 @@ public function __construct($user_options = array(), $dev_options = array()) $this->ruleFormatter = $this->options->__get('formatter'); $this->minifyOutput = $this->options->__get('minify'); $this->newline = $this->options->__get('newlines'); - - Hook::run('process_init'); } public function release() @@ -390,9 +389,9 @@ protected function filterPlugins() protected function captureVars() { - $vars_patt = Regex::make('~@define(?:\s*{{ block }}|\s+(?{{ ident }})\s+(?[^;]+)\s*;)~iS'); + $patt = Regex::make('~@define(?:\s*{{ block }}|\s+(?{{ ident }})\s+(?[^;]+)\s*;)~iS'); - $this->stream->pregReplaceCallback($vars_patt, function ($m) { + $this->stream->pregReplaceCallback($patt, function ($m) { if (isset($m['name'])) { Crush::$process->vars[$m['name']] = $m['value']; } @@ -477,6 +476,34 @@ static protected function cb_placeVars($m) } + ############################# + # @settings blocks. + + protected function resolveSettings() + { + $patt = Regex::make('~@settings(?:\s*{{ block }}|\s+(?{{ ident }})\s+(?[^;]+)\s*;)~iS'); + $captured_settings = array(); + + $this->stream->pregReplaceCallback($patt, function ($m) use (&$captured_settings) { + if (isset($m['name'])) { + $captured_settings[strtolower($m['name'])] = $m['value']; + } + else { + $captured_settings = DeclarationList::parse($m['block_content'], array( + 'keyed' => true, + 'ignore_directives' => true, + 'lowercase_keys' => true, + )) + $captured_settings; + } + + return ''; + }); + + // Like variables, settings passed via options override settings defined in CSS. + $this->settings = new Settings($this->options->settings + $captured_settings); + } + + ############################# # @ifdefine blocks. @@ -902,14 +929,15 @@ public function compile() // Collate hostfile and imports. $this->stream = new Stream(Importer::hostfile($this->input)); - // Extract and calculate variables. $this->captureVars(); $this->placeAllVars(); $this->resolveIfDefines(); - // Capture phase 1 hook: After all vars have resolved. + $this->resolveSettings(); + + // Capture phase 1 hook: After all variables and settings have resolved. Hook::run('capture_phase1', $this); $this->resolveSelectorAliases(); diff --git a/lib/CssCrush/Settings.php b/lib/CssCrush/Settings.php new file mode 100644 index 0000000..a34d7ed --- /dev/null +++ b/lib/CssCrush/Settings.php @@ -0,0 +1,57 @@ + $value) { + $this->set($name, $value); + } + } + + public function set($name, $value) + { + $this->store[$name] = $value; + } + + public function get($name, $fallback = null) + { + $value = isset($this->store[$name]) ? $this->store[$name] : null; + + // Backwards compat for variable based settings. + if (! $value && in_array($name, array('rem-all', 'rem-mode', 'rem-base', 'px2rem-base', 'px2em-base'))) { + + $var_setting = function ($var_name) { + return isset(Crush::$process->vars[$var_name]) ? + Crush::$process->vars[$var_name] : null; + }; + switch ($name) { + case 'rem-all': + $value = $var_setting('rem__all'); + break; + case 'rem-mode': + $value = $var_setting('rem__mode'); + break; + case 'rem-base': + $value = $var_setting('rem__base'); + break; + case 'px2rem-base': + $value = $var_setting('px2rem__base'); + break; + case 'px2em-base': + $value = $var_setting('px2em__base'); + break; + } + } + + return isset($value) ? $value : $fallback; + } +} diff --git a/plugins/canvas.php b/plugins/canvas.php index 6b6585a..a7aca05 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -116,12 +116,12 @@ function canvas_generator($input, $context) { // Apply args to template. $block = $canvas_defs[$name]->apply($args); - // Parse the block into a keyed array. - $raw = array_change_key_case(DeclarationList::parse($block, array( + $raw = DeclarationList::parse($block, array( 'keyed' => true, + 'lowercase_keys' => true, 'flatten' => true, 'apply_hooks' => true, - ))); + )); // Create canvas object. $canvas = new Canvas(); diff --git a/plugins/color.php b/plugins/color.php index 99e372a..6539bd2 100644 --- a/plugins/color.php +++ b/plugins/color.php @@ -27,23 +27,21 @@ function color(&$declaration) { function color_capture($process) { - $color_directive_patt = Regex::make('~@color(?:\s*{{ block }}|\s+(?{{ ident }})\s+(?[^;]+)\s*;)~iS'); + $patt = Regex::make('~@color(?:\s*{{ block }}|\s+(?{{ ident }})\s+(?[^;]+)\s*;)~iS'); $captured_keywords = array(); - $process->stream->pregReplaceCallback($color_directive_patt, function ($m) use (&$captured_keywords) { - - if (! isset($m['name'])) { - $pairs = array_change_key_case(DeclarationList::parse($m['block_content'], array( - 'keyed' => true, - 'flatten' => true, - ))); + $process->stream->pregReplaceCallback($patt, function ($m) use (&$captured_keywords) { + if (isset($m['name'])) { + $captured_keywords[strtolower($m['name'])] = $m['value']; } else { - $pairs = array(strtolower($m['name']) => $m['value']); + $captured_keywords = DeclarationList::parse($m['block_content'], array( + 'keyed' => true, + 'lowercase_keys' => true, + 'flatten' => true, + )) + $captured_keywords; } - $captured_keywords = $pairs + $captured_keywords; - return ''; }); diff --git a/plugins/px2em.php b/plugins/px2em.php index 9cd42ab..85748be 100644 --- a/plugins/px2em.php +++ b/plugins/px2em.php @@ -29,34 +29,17 @@ function fn__px2em($input) { - $base = 16; - - // Override default base if variable is set. - if (isset(Crush::$process->vars['px2em__base'])) { - $base = Crush::$process->vars['px2em__base']; - } - - return px2em($input, 'em', $base); + return px2em($input, 'em', Crush::$process->settings->get('px2em-base', 16)); } function fn__px2rem($input) { - $base = 16; - - // Override default base if variable is set. - if (isset(Crush::$process->vars['px2rem__base'])) { - $base = Crush::$process->vars['px2rem__base']; - } - - return px2em($input, 'rem', $base); + return px2em($input, 'rem', Crush::$process->settings->get('px2rem-base', 16)); } function px2em($input, $unit, $default_base) { - list($px, $base) = Functions::parseArgsSimple($input) + array( - 16, - $default_base, - ); + list($px, $base) = Functions::parseArgsSimple($input) + array(16, $default_base); return round($px / $base, 5) . $unit; } diff --git a/plugins/rem.php b/plugins/rem.php index 10ddd10..495aac1 100644 --- a/plugins/rem.php +++ b/plugins/rem.php @@ -59,26 +59,18 @@ function rem(Rule $rule) { $modes = array('rem-fallback', 'px-fallback', 'convert'); } - $vars =& Crush::$process->vars; - // Determine which properties are touched; all, or just font related. - $just_font_props = ! isset($vars['rem__all']); + $just_font_props = ! Crush::$process->settings->get('rem-all', false); if ($just_font_props && ! array_intersect_key($rule->declarations->canonicalProperties, $font_props)) { return; } // Determine what conversion mode we're using. - $mode = $modes[0]; - if (isset($vars['rem__mode'])) { - $_mode = $vars['rem__mode']; - if (in_array($_mode, $modes)) { - $mode = $_mode; - } - } + $mode = Crush::$process->settings->get('rem-mode', $modes[0]); // Determine the default base em-size, to my knowledge always 16px. - $base = isset($vars['rem__base']) ? $vars['rem__base'] : 16; + $base = Crush::$process->settings->get('rem-base', 16); // Select the length match pattern depending on mode. $length_patt = $mode === 'rem-fallback' ? $rem_patt : $px_patt; diff --git a/plugins/svg.php b/plugins/svg.php index a19c549..f8f56b2 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -199,12 +199,12 @@ function svg_generator($input, $fn_name) { // Apply args to template. $block = $svg_defs[$name]->apply($args); - // Parse the block into a keyed assoc array. - $raw_data = array_change_key_case(DeclarationList::parse($block, array( + $raw_data = DeclarationList::parse($block, array( 'keyed' => true, + 'lowercase_keys' => true, 'flatten' => true, 'apply_hooks' => true, - ))); + )); // Resolve the type. // Bail if type not recognised. From c5641fc7c96f57ed7c53534804ab341c1722aa9f Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 11 Jan 2014 20:51:38 +0000 Subject: [PATCH 237/421] Adding text-align plugin for polyfilling the direction sensitive text-align values, start and end. --- lib/CssCrush/Settings.php | 2 +- plugins/rem.php | 6 ++-- plugins/text-align.php | 59 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 4 deletions(-) create mode 100644 plugins/text-align.php diff --git a/lib/CssCrush/Settings.php b/lib/CssCrush/Settings.php index a34d7ed..9a63795 100644 --- a/lib/CssCrush/Settings.php +++ b/lib/CssCrush/Settings.php @@ -19,7 +19,7 @@ public function __construct($pairs = array()) public function set($name, $value) { - $this->store[$name] = $value; + $this->store[strtolower($name)] = strtolower($value); } public function get($name, $fallback = null) diff --git a/plugins/rem.php b/plugins/rem.php index 495aac1..f57eaa4 100644 --- a/plugins/rem.php +++ b/plugins/rem.php @@ -27,11 +27,11 @@ * * font-size: 1rem; * - * `rem-fallback` is the default mode. To change the conversion mode set a - * variable named `rem__mode` with the mode name you want as its value. + * `rem-fallback` is the default mode. To change the conversion mode + * declare the setting `rem-mode` with the mode name you want as its value. * * To convert all values, not just values of the font related properties, - * set a variable named `rem__all` with a value of `yes`. + * declare the setting `rem-all` with the value 'true'. */ namespace CssCrush; diff --git a/plugins/text-align.php b/plugins/text-align.php new file mode 100644 index 0000000..e16a32f --- /dev/null +++ b/plugins/text-align.php @@ -0,0 +1,59 @@ + function () { + Hook::add('rule_prealias', 'CssCrush\text_align'); + }, + 'disable' => function () { + Hook::remove('rule_prealias', 'CssCrush\text_align'); + }, +)); + + +function text_align(Rule $rule) { + + static $text_align_properties = array('text-align' => true, 'text-align-last' => true); + static $text_align_special_values = array('start' => true, 'end' => true); + + if (! array_intersect_key($rule->declarations->properties, $text_align_properties)) { + return; + } + + $dir = Crush::$process->settings->get('dir', 'ltr'); + + $stack = array(); + foreach ($rule->declarations as $declaration) { + $value = strtolower($declaration->value); + if ( + ! $declaration->skip && + isset($text_align_properties[$declaration->property]) && + isset($text_align_special_values[$value]) + ) { + $fallback_declaration = clone $declaration; + if ($value == 'start') { + $fallback_declaration->value = $dir == 'ltr' ? 'left' : 'right'; + } + elseif ($value == 'end') { + $fallback_declaration->value = $dir == 'ltr' ? 'right' : 'left'; + } + $stack[] = $fallback_declaration; + } + + $stack[] = $declaration; + } + + $rule->declarations->reset($stack); +} From 72d75d709932ddf99db21c83d146a48b7d6a9ec9 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 13 Jan 2014 11:10:58 +0000 Subject: [PATCH 238/421] Refactored the plugin hooks system from being a singleton --- lib/CssCrush/Declaration.php | 2 +- lib/CssCrush/DeclarationList.php | 2 +- lib/CssCrush/Hook.php | 35 ----------------- lib/CssCrush/Hooks.php | 40 ++++++++++++++++++++ lib/CssCrush/Process.php | 11 +++--- plugins/canvas.php | 4 +- plugins/color.php | 8 ++-- plugins/ease.php | 4 +- plugins/hsl2hex.php | 4 +- plugins/ie-inline-block.php | 4 +- plugins/ie-opacity.php | 4 +- plugins/initial.php | 4 +- plugins/legacy-flexbox.php | 4 +- plugins/loop.php | 4 +- plugins/property-sorter.php | 4 +- plugins/rem.php | 4 +- plugins/svg.php | 4 +- plugins/text-align.php | 6 +-- tests/unit/CssCrush/HookTest.php | 62 ------------------------------- tests/unit/CssCrush/HooksTest.php | 33 ++++++++++++++++ 20 files changed, 110 insertions(+), 133 deletions(-) delete mode 100644 lib/CssCrush/Hook.php create mode 100644 lib/CssCrush/Hooks.php delete mode 100644 tests/unit/CssCrush/HookTest.php create mode 100644 tests/unit/CssCrush/HooksTest.php diff --git a/lib/CssCrush/Declaration.php b/lib/CssCrush/Declaration.php index 093e5fb..332954d 100644 --- a/lib/CssCrush/Declaration.php +++ b/lib/CssCrush/Declaration.php @@ -45,7 +45,7 @@ public function __construct($property, $value, $contextIndex = 0) $important = true; } - Hook::run('declaration_preprocess', array('property' => &$property, 'value' => &$value)); + Crush::$process->hooks->run('declaration_preprocess', array('property' => &$property, 'value' => &$value)); // Reject declarations with empty CSS values. if ($value === false || $value === '') { diff --git a/lib/CssCrush/DeclarationList.php b/lib/CssCrush/DeclarationList.php index 0dc01b4..441066f 100644 --- a/lib/CssCrush/DeclarationList.php +++ b/lib/CssCrush/DeclarationList.php @@ -364,7 +364,7 @@ public static function parse($str, $options = array()) } if ($options['apply_hooks']) { - Hook::run('declaration_preprocess', array('property' => &$property, 'value' => &$value)); + Crush::$process->hooks->run('declaration_preprocess', array('property' => &$property, 'value' => &$value)); } } else { diff --git a/lib/CssCrush/Hook.php b/lib/CssCrush/Hook.php deleted file mode 100644 index 036995d..0000000 --- a/lib/CssCrush/Hook.php +++ /dev/null @@ -1,35 +0,0 @@ - $flag) { - $fn_name($arg_obj); - } - } - } -} diff --git a/lib/CssCrush/Hooks.php b/lib/CssCrush/Hooks.php new file mode 100644 index 0000000..8a08c35 --- /dev/null +++ b/lib/CssCrush/Hooks.php @@ -0,0 +1,40 @@ +register[$hook][$fn_name])) { + if (function_exists($fn_name)) { + $this->register[$hook][$fn_name] = true; + } + } + } + + public function remove($hook, $fn_name) + { + unset($this->register[$hook][$fn_name]); + } + + public function run($hook, $arg_obj = null) + { + if (isset($this->register[$hook])) { + foreach ($this->register[$hook] as $fn_name => $flag) { + $fn_name($arg_obj); + } + } + } + + public function get() + { + return $this->register; + } +} diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index b765673..af7c117 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -30,6 +30,7 @@ public function __construct($user_options = array(), $dev_options = array()) $this->input = new \stdClass(); $this->output = new \stdClass(); $this->tokens = new Tokens(); + $this->hooks = new Hooks(); $this->sourceMap = null; $this->selectorAliases = array(); $this->selectorAliasesPatt = null; @@ -623,18 +624,18 @@ protected function processRules() $rule->declarations->flatten($rule); $rule->declarations->process($rule); - Hook::run('rule_prealias', $rule); + $this->hooks->run('rule_prealias', $rule); $rule->declarations->aliasProperties($rule->vendorContext); $rule->declarations->aliasFunctions($rule->vendorContext); $rule->declarations->aliasDeclarations($rule->vendorContext); - Hook::run('rule_postalias', $rule); + $this->hooks->run('rule_postalias', $rule); $rule->selectors->expand(); $rule->applyExtendables(); - Hook::run('rule_postprocess', $rule); + $this->hooks->run('rule_postprocess', $rule); } } @@ -938,7 +939,7 @@ public function compile() $this->resolveSettings(); // Capture phase 1 hook: After all variables and settings have resolved. - Hook::run('capture_phase1', $this); + $this->hooks->run('capture_phase1', $this); $this->resolveSelectorAliases(); @@ -947,7 +948,7 @@ public function compile() $this->resolveFragments(); // Capture phase 2 hook: After most built-in directives have resolved. - Hook::run('capture_phase2', $this); + $this->hooks->run('capture_phase2', $this); $this->captureRules(); diff --git a/plugins/canvas.php b/plugins/canvas.php index a7aca05..c20beac 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -53,12 +53,12 @@ Plugin::register('canvas', array( 'enable' => function () { - Hook::add('capture_phase2', 'CssCrush\canvas_capture'); + Crush::$process->hooks->add('capture_phase2', 'CssCrush\canvas_capture'); Functions::register('canvas', 'CssCrush\canvas_generator'); Functions::register('canvas-data', 'CssCrush\canvas_generator'); }, 'disable' => function () { - Hook::remove('capture_phase2', 'CssCrush\canvas_capture'); + Crush::$process->hooks->remove('capture_phase2', 'CssCrush\canvas_capture'); Functions::deRegister('canvas'); Functions::deRegister('canvas-data'); }, diff --git a/plugins/color.php b/plugins/color.php index 6539bd2..6e61ee5 100644 --- a/plugins/color.php +++ b/plugins/color.php @@ -7,12 +7,12 @@ Plugin::register('color', array( 'enable' => function () { $GLOBALS['CSSCRUSH_COLOR_PATT'] = null; - Hook::add('capture_phase1', 'CssCrush\color_capture'); - Hook::add('declaration_preprocess', 'CssCrush\color'); + Crush::$process->hooks->add('capture_phase1', 'CssCrush\color_capture'); + Crush::$process->hooks->add('declaration_preprocess', 'CssCrush\color'); }, 'disable' => function () { - Hook::remove('capture_phase1', 'CssCrush\color_capture'); - Hook::remove('declaration_preprocess', 'CssCrush\color'); + Crush::$process->hooks->remove('capture_phase1', 'CssCrush\color_capture'); + Crush::$process->hooks->remove('declaration_preprocess', 'CssCrush\color'); }, )); diff --git a/plugins/ease.php b/plugins/ease.php index 0195283..6461045 100644 --- a/plugins/ease.php +++ b/plugins/ease.php @@ -15,10 +15,10 @@ Plugin::register('ease', array( 'enable' => function () { - Hook::add('rule_prealias', 'CssCrush\ease'); + Crush::$process->hooks->add('rule_prealias', 'CssCrush\ease'); }, 'disable' => function () { - Hook::remove('rule_prealias', 'CssCrush\ease'); + Crush::$process->hooks->remove('rule_prealias', 'CssCrush\ease'); }, )); diff --git a/plugins/hsl2hex.php b/plugins/hsl2hex.php index f600b6c..cb7e712 100644 --- a/plugins/hsl2hex.php +++ b/plugins/hsl2hex.php @@ -12,10 +12,10 @@ Plugin::register('hsl2hex', array( 'enable' => function () { - Hook::add('rule_postalias', 'CssCrush\hsl2hex'); + Crush::$process->hooks->add('rule_postalias', 'CssCrush\hsl2hex'); }, 'disable' => function () { - Hook::remove('rule_postalias', 'CssCrush\hsl2hex'); + Crush::$process->hooks->remove('rule_postalias', 'CssCrush\hsl2hex'); }, )); diff --git a/plugins/ie-inline-block.php b/plugins/ie-inline-block.php index 80d73bd..b6914a2 100644 --- a/plugins/ie-inline-block.php +++ b/plugins/ie-inline-block.php @@ -14,10 +14,10 @@ Plugin::register('ie-inline-block', array( 'enable' => function () { - Hook::add('rule_postalias', 'CssCrush\ie_inline_block'); + Crush::$process->hooks->add('rule_postalias', 'CssCrush\ie_inline_block'); }, 'disable' => function () { - Hook::remove('rule_postalias', 'CssCrush\ie_inline_block'); + Crush::$process->hooks->remove('rule_postalias', 'CssCrush\ie_inline_block'); }, )); diff --git a/plugins/ie-opacity.php b/plugins/ie-opacity.php index 45accfa..6939ba5 100755 --- a/plugins/ie-opacity.php +++ b/plugins/ie-opacity.php @@ -15,10 +15,10 @@ Plugin::register('ie-opacity', array( 'enable' => function () { - Hook::add('rule_postalias', 'CssCrush\ie_opacity'); + Crush::$process->hooks->add('rule_postalias', 'CssCrush\ie_opacity'); }, 'disable' => function () { - Hook::remove('rule_postalias', 'CssCrush\ie_opacity'); + Crush::$process->hooks->remove('rule_postalias', 'CssCrush\ie_opacity'); }, )); diff --git a/plugins/initial.php b/plugins/initial.php index 4a59a71..63e9715 100644 --- a/plugins/initial.php +++ b/plugins/initial.php @@ -19,10 +19,10 @@ Plugin::register('initial', array( 'enable' => function () { - Hook::add('rule_prealias', 'CssCrush\initial'); + Crush::$process->hooks->add('rule_prealias', 'CssCrush\initial'); }, 'disable' => function () { - Hook::remove('rule_prealias', 'CssCrush\initial'); + Crush::$process->hooks->remove('rule_prealias', 'CssCrush\initial'); }, )); diff --git a/plugins/legacy-flexbox.php b/plugins/legacy-flexbox.php index f066fa5..b076b92 100644 --- a/plugins/legacy-flexbox.php +++ b/plugins/legacy-flexbox.php @@ -47,10 +47,10 @@ Plugin::register('legacy-flexbox', array( 'enable' => function () { - Hook::add('rule_prealias', 'CssCrush\legacy_flexbox'); + Crush::$process->hooks->add('rule_prealias', 'CssCrush\legacy_flexbox'); }, 'disable' => function () { - Hook::remove('rule_prealias', 'CssCrush\legacy_flexbox'); + Crush::$process->hooks->remove('rule_prealias', 'CssCrush\legacy_flexbox'); }, )); diff --git a/plugins/loop.php b/plugins/loop.php index 4ba8fd4..00bf2d8 100644 --- a/plugins/loop.php +++ b/plugins/loop.php @@ -52,10 +52,10 @@ Plugin::register('loop', array( 'enable' => function () { - Hook::add('capture_phase1', 'CssCrush\loop'); + Crush::$process->hooks->add('capture_phase1', 'CssCrush\loop'); }, 'disable' => function () { - Hook::remove('capture_phase1', 'CssCrush\loop'); + Crush::$process->hooks->remove('capture_phase1', 'CssCrush\loop'); }, )); diff --git a/plugins/property-sorter.php b/plugins/property-sorter.php index b0fee63..bb9bf96 100644 --- a/plugins/property-sorter.php +++ b/plugins/property-sorter.php @@ -26,10 +26,10 @@ Plugin::register('property-sorter', array( 'enable' => function () { - Hook::add('rule_prealias', 'CssCrush\property_sorter'); + Crush::$process->hooks->add('rule_prealias', 'CssCrush\property_sorter'); }, 'disable' => function () { - Hook::remove('rule_prealias', 'CssCrush\property_sorter'); + Crush::$process->hooks->remove('rule_prealias', 'CssCrush\property_sorter'); }, )); diff --git a/plugins/rem.php b/plugins/rem.php index f57eaa4..9027a82 100644 --- a/plugins/rem.php +++ b/plugins/rem.php @@ -37,10 +37,10 @@ Plugin::register('rem', array( 'enable' => function () { - Hook::add('rule_prealias', 'CssCrush\rem'); + Crush::$process->hooks->add('rule_prealias', 'CssCrush\rem'); }, 'disable' => function () { - Hook::remove('rule_prealias', 'CssCrush\rem'); + Crush::$process->hooks->remove('rule_prealias', 'CssCrush\rem'); }, )); diff --git a/plugins/svg.php b/plugins/svg.php index f8f56b2..4c4dcf9 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -65,12 +65,12 @@ }, 'enable' => function () { $GLOBALS['CSSCRUSH_SVG_UID'] = 0; - Hook::add('capture_phase2', 'CssCrush\svg_capture'); + Crush::$process->hooks->add('capture_phase2', 'CssCrush\svg_capture'); Functions::register('svg', 'CssCrush\fn__svg'); Functions::register('svg-data', 'CssCrush\fn__svg_data'); }, 'disable' => function () { - Hook::remove('capture_phase2', 'CssCrush\svg_capture'); + Crush::$process->hooks->remove('capture_phase2', 'CssCrush\svg_capture'); Functions::deRegister('svg'); Functions::deRegister('svg-data'); }, diff --git a/plugins/text-align.php b/plugins/text-align.php index e16a32f..29f24af 100644 --- a/plugins/text-align.php +++ b/plugins/text-align.php @@ -2,7 +2,7 @@ /** * Polyfill for direction sensitive text-align values, start and end * - * If the 'dir' setting is 'rtl' 'start' translates to 'right' and 'end' translates to 'left'. + * If the 'dir' setting is 'rtl', 'start' translates to 'right' and 'end' translates to 'left'. * * @before * text-align: start; @@ -15,10 +15,10 @@ Plugin::register('text-align', array( 'enable' => function () { - Hook::add('rule_prealias', 'CssCrush\text_align'); + Crush::$process->hooks->add('rule_prealias', 'CssCrush\text_align'); }, 'disable' => function () { - Hook::remove('rule_prealias', 'CssCrush\text_align'); + Crush::$process->hooks->remove('rule_prealias', 'CssCrush\text_align'); }, )); diff --git a/tests/unit/CssCrush/HookTest.php b/tests/unit/CssCrush/HookTest.php deleted file mode 100644 index c2b00b9..0000000 --- a/tests/unit/CssCrush/HookTest.php +++ /dev/null @@ -1,62 +0,0 @@ -dummy_hook = __NAMESPACE__ . '\dummy_hook'; - } - - public function tearDown() - { - Hook::$register = array(); - } - - public function testAdd() - { - Hook::add('foo', $this->dummy_hook); - Hook::add('foo', 'strtoupper'); - - $this->assertEquals(array('foo' => array($this->dummy_hook=>true, 'strtoupper'=>true)), Hook::$register); - - return Hook::$register; - } - - /** - * @depends testAdd - */ - public function testRemove($register) - { - Hook::$register = $register; - - Hook::remove('foo', 'strtoupper'); - - $this->assertEquals(array('foo' => array($this->dummy_hook=>true)), Hook::$register); - - return Hook::$register; - } - - /** - * @depends testRemove - */ - public function testRun($register) - { - Hook::$register = $register; - - Hook::run('foo', $this); - - $this->assertTrue($this->hookRan); - - return Hook::$register; - } -} - -function dummy_hook(HookTest $test) -{ - $test->hookRan = true; -} diff --git a/tests/unit/CssCrush/HooksTest.php b/tests/unit/CssCrush/HooksTest.php new file mode 100644 index 0000000..b3046b0 --- /dev/null +++ b/tests/unit/CssCrush/HooksTest.php @@ -0,0 +1,33 @@ +add('foo', $dummy_hook); + $hooks->add('foo', 'strtoupper'); + + $this->assertEquals(array('foo' => array($dummy_hook=>true, 'strtoupper'=>true)), $hooks->get()); + + $hooks->remove('foo', 'strtoupper'); + + $this->assertEquals(array('foo' => array($dummy_hook=>true)), $hooks->get()); + + $hooks->run('foo', $this); + + $this->assertTrue($this->hookRan); + } +} + +function dummy_hook(HookTest $test) +{ + $test->hookRan = true; +} From 1d1009bc40e9de580c1b33f25168253870fc86c7 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 14 Jan 2014 22:36:10 +0000 Subject: [PATCH 239/421] Fixing issue with incorrect default options. --- lib/CssCrush/Crush.php | 8 ++++---- lib/CssCrush/Options.php | 8 ++++++-- lib/functions.php | 8 ++++---- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/lib/CssCrush/Crush.php b/lib/CssCrush/Crush.php index 17ae9aa..140f738 100644 --- a/lib/CssCrush/Crush.php +++ b/lib/CssCrush/Crush.php @@ -189,7 +189,7 @@ public static function parseAliasesFile($file) * @param mixed $options An array of options or null. * @return string The public path to the compiled file or an empty string. */ - public static function file($file, $options = null) + public static function file($file, $options = array()) { self::$process = new Process($options, array('io_context' => 'file')); @@ -236,7 +236,7 @@ public static function file($file, $options = null) * @param array $attributes An array of HTML attributes. * @return string HTML link tag or error message inside HTML comment. */ - public static function tag($file, $options = null, $tag_attributes = array()) + public static function tag($file, $options = array(), $tag_attributes = array()) { $file = self::file($file, $options); @@ -267,7 +267,7 @@ public static function tag($file, $options = null, $tag_attributes = array()) * @param array $attributes An array of HTML attributes, set false to return CSS text without tag. * @return string HTML link tag or error message inside HTML comment. */ - public static function inline($file, $options = null, $tag_attributes = array()) + public static function inline($file, $options = array(), $tag_attributes = array()) { // For inline output set boilerplate to not display by default. if (! is_array($options)) { @@ -309,7 +309,7 @@ public static function inline($file, $options = null, $tag_attributes = array()) * @param mixed $options An array of options or null. * @return string CSS text. */ - public static function string($string, $options = null) + public static function string($string, $options = array()) { // For strings set boilerplate to not display by default if (! isset($options['boilerplate'])) { diff --git a/lib/CssCrush/Options.php b/lib/CssCrush/Options.php index 90f2497..edd38e3 100644 --- a/lib/CssCrush/Options.php +++ b/lib/CssCrush/Options.php @@ -33,9 +33,8 @@ class Options 'newlines' => 'use-platform', ); - public function __construct(array $options = null, Options $defaults = null) + public function __construct(array $options = array(), Options $defaults = null) { - $options = is_array($options) ? $options : self::$initialOptions; foreach ($options as $key => $value) { $this->__set($key, $value); } @@ -46,6 +45,11 @@ public function __construct(array $options = null, Options $defaults = null) } } } + foreach (self::$initialOptions as $key => $value) { + if (! array_key_exists($key, $this->inputOptions)) { + $this->__set($key, $value); + } + } } public function __set($name, $value) diff --git a/lib/functions.php b/lib/functions.php index 6ccfeb2..150d088 100644 --- a/lib/functions.php +++ b/lib/functions.php @@ -7,19 +7,19 @@ use CssCrush\Crush; class_alias('CssCrush\Crush', 'CssCrush\CssCrush'); -function csscrush_file($file, $options = null) { +function csscrush_file($file, $options = array()) { return Crush::file($file, $options); } -function csscrush_tag($file, $options = null, $attributes = array()) { +function csscrush_tag($file, $options = array(), $attributes = array()) { return Crush::tag($file, $options, $attributes); } -function csscrush_inline($file, $options = null, $attributes = array()) { +function csscrush_inline($file, $options = array(), $attributes = array()) { return Crush::inline($file, $options, $attributes); } -function csscrush_string($string, $options = null) { +function csscrush_string($string, $options = array()) { return Crush::string($string, $options); } From d04a9a7632ebe87bd6231dbf36fdb6680473299e Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 15 Jan 2014 09:45:15 +0000 Subject: [PATCH 240/421] Adding option defaults test. --- lib/CssCrush/Options.php | 18 +++++------------- tests/unit/CssCrush/HooksTest.php | 2 +- tests/unit/CssCrush/OptionsTest.php | 18 ++++++++++++++++++ 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/lib/CssCrush/Options.php b/lib/CssCrush/Options.php index edd38e3..dc58da0 100644 --- a/lib/CssCrush/Options.php +++ b/lib/CssCrush/Options.php @@ -11,7 +11,7 @@ class Options protected $computedOptions = array(); protected $inputOptions = array(); - protected static $initialOptions = array( + public static $initialOptions = array( 'minify' => true, 'formatter' => null, 'versioning' => true, @@ -35,20 +35,12 @@ class Options public function __construct(array $options = array(), Options $defaults = null) { - foreach ($options as $key => $value) { - $this->__set($key, $value); - } if ($defaults) { - foreach ($defaults->get() as $key => $value) { - if (! array_key_exists($key, $this->inputOptions)) { - $this->__set($key, $value); - } - } + $options += $defaults->get(); } - foreach (self::$initialOptions as $key => $value) { - if (! array_key_exists($key, $this->inputOptions)) { - $this->__set($key, $value); - } + + foreach ($options + self::$initialOptions as $key => $value) { + $this->__set($key, $value); } } diff --git a/tests/unit/CssCrush/HooksTest.php b/tests/unit/CssCrush/HooksTest.php index b3046b0..447c552 100644 --- a/tests/unit/CssCrush/HooksTest.php +++ b/tests/unit/CssCrush/HooksTest.php @@ -6,7 +6,7 @@ class HookTest extends \PHPUnit_Framework_TestCase { - public function testAdd() + public function testAll() { $hooks = new Hooks(); diff --git a/tests/unit/CssCrush/OptionsTest.php b/tests/unit/CssCrush/OptionsTest.php index 4a44828..a083bf1 100644 --- a/tests/unit/CssCrush/OptionsTest.php +++ b/tests/unit/CssCrush/OptionsTest.php @@ -2,6 +2,8 @@ namespace CssCrush\UnitTest; +use CssCrush\Options; + class OptionsTest extends \PHPUnit_Framework_TestCase { public $testFile; @@ -12,6 +14,22 @@ public function setUp() $this->testFile = temp_file("\n foo {bar: baz;} \n\n baz {bar: foo;}"); } + public function testDefaults() + { + $options = new Options(); + $initial_options = Options::$initialOptions; + + $this->assertEquals($initial_options, $options->get()); + + $test_options = array('enable' => array('foo', 'bar'), 'minify' => false); + $options = new Options($test_options); + + $initial_options_copy = $initial_options; + $initial_options_copy = $test_options + $initial_options_copy; + + $this->assertEquals($initial_options_copy, $options->get()); + } + public function testBoilerplate() { $boilerplate = << Date: Tue, 21 Jan 2014 09:37:04 +0000 Subject: [PATCH 241/421] Adding docs to the repo. Removed inline docs from plugins. --- docs/README.md | 3 + docs/api/functions.md | 66 +++++++++++++++ docs/api/options.md | 109 +++++++++++++++++++++++++ docs/core/abstract.md | 44 ++++++++++ docs/core/auto-prefixing.md | 53 ++++++++++++ docs/core/block-nesting.md | 37 +++++++++ docs/core/direct-import.md | 25 ++++++ docs/core/fragments.md | 25 ++++++ docs/core/functions/a-adjust.md | 23 ++++++ docs/core/functions/data-uri.md | 30 +++++++ docs/core/functions/h-adjust.md | 22 +++++ docs/core/functions/hsl-adjust.md | 25 ++++++ docs/core/functions/hsla-adjust.md | 25 ++++++ docs/core/functions/l-adjust.md | 22 +++++ docs/core/functions/math.md | 17 ++++ docs/core/functions/percent.md | 28 +++++++ docs/core/functions/query.md | 37 +++++++++ docs/core/functions/s-adjust.md | 23 ++++++ docs/core/functions/this.md | 39 +++++++++ docs/core/inheritance.md | 126 +++++++++++++++++++++++++++++ docs/core/mixins.md | 95 ++++++++++++++++++++++ docs/core/selector-aliases.md | 48 +++++++++++ docs/core/selector-grouping.md | 22 +++++ docs/core/variables.md | 59 ++++++++++++++ docs/getting-started/download.md | 17 ++++ docs/plugins/aria.md | 21 +++++ docs/plugins/canvas.md | 55 +++++++++++++ docs/plugins/color.md | 21 +++++ docs/plugins/ease.md | 37 +++++++++ docs/plugins/forms.md | 16 ++++ docs/plugins/hocus-pocus.md | 12 +++ docs/plugins/hsl2hex.md | 10 +++ docs/plugins/ie-inline-block.md | 12 +++ docs/plugins/ie-opacity.md | 13 +++ docs/plugins/initial.md | 16 ++++ docs/plugins/legacy-flexbox.md | 40 +++++++++ docs/plugins/loop.md | 57 +++++++++++++ docs/plugins/noise.md | 61 ++++++++++++++ docs/plugins/property-sorter.md | 21 +++++ docs/plugins/px2em.md | 15 ++++ docs/plugins/rem.md | 50 ++++++++++++ docs/plugins/svg-gradients.md | 45 +++++++++++ docs/plugins/svg.md | 74 +++++++++++++++++ docs/plugins/text-align.md | 13 +++ plugins/aria.php | 17 +--- plugins/canvas.php | 45 +---------- plugins/color.php | 2 + plugins/ease.php | 9 +-- plugins/forms.php | 12 +-- plugins/hocus-pocus.php | 11 +-- plugins/hsl2hex.php | 6 +- plugins/ie-inline-block.php | 8 +- plugins/ie-opacity.php | 9 +-- plugins/initial.php | 13 +-- plugins/legacy-flexbox.php | 41 +--------- plugins/loop.php | 46 +---------- plugins/noise.php | 70 +--------------- plugins/property-sorter.php | 20 +---- plugins/px2em.php | 11 +-- plugins/rem.php | 31 +------ plugins/svg-gradients.php | 37 +-------- plugins/svg.php | 55 +------------ plugins/text-align.php | 9 +-- 63 files changed, 1630 insertions(+), 431 deletions(-) create mode 100644 docs/README.md create mode 100644 docs/api/functions.md create mode 100644 docs/api/options.md create mode 100644 docs/core/abstract.md create mode 100644 docs/core/auto-prefixing.md create mode 100644 docs/core/block-nesting.md create mode 100644 docs/core/direct-import.md create mode 100644 docs/core/fragments.md create mode 100644 docs/core/functions/a-adjust.md create mode 100644 docs/core/functions/data-uri.md create mode 100644 docs/core/functions/h-adjust.md create mode 100644 docs/core/functions/hsl-adjust.md create mode 100644 docs/core/functions/hsla-adjust.md create mode 100644 docs/core/functions/l-adjust.md create mode 100644 docs/core/functions/math.md create mode 100644 docs/core/functions/percent.md create mode 100644 docs/core/functions/query.md create mode 100644 docs/core/functions/s-adjust.md create mode 100644 docs/core/functions/this.md create mode 100644 docs/core/inheritance.md create mode 100644 docs/core/mixins.md create mode 100644 docs/core/selector-aliases.md create mode 100644 docs/core/selector-grouping.md create mode 100644 docs/core/variables.md create mode 100644 docs/getting-started/download.md create mode 100644 docs/plugins/aria.md create mode 100644 docs/plugins/canvas.md create mode 100644 docs/plugins/color.md create mode 100644 docs/plugins/ease.md create mode 100644 docs/plugins/forms.md create mode 100644 docs/plugins/hocus-pocus.md create mode 100644 docs/plugins/hsl2hex.md create mode 100644 docs/plugins/ie-inline-block.md create mode 100644 docs/plugins/ie-opacity.md create mode 100644 docs/plugins/initial.md create mode 100644 docs/plugins/legacy-flexbox.md create mode 100644 docs/plugins/loop.md create mode 100644 docs/plugins/noise.md create mode 100644 docs/plugins/property-sorter.md create mode 100644 docs/plugins/px2em.md create mode 100644 docs/plugins/rem.md create mode 100644 docs/plugins/svg-gradients.md create mode 100644 docs/plugins/svg.md create mode 100644 docs/plugins/text-align.md diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..9c284db --- /dev/null +++ b/docs/README.md @@ -0,0 +1,3 @@ +# CSS-Crush Documentation + +Rendered online at http://the-echoplex.net/csscrush diff --git a/docs/api/functions.md b/docs/api/functions.md new file mode 100644 index 0000000..f6dbbae --- /dev/null +++ b/docs/api/functions.md @@ -0,0 +1,66 @@ + + +## csscrush_file() + +Process host CSS file and return the compiled file URL. + +csscrush_file( string $file [, array [$options](#api--options) ] ) + + +## csscrush_tag() + +Process host CSS file and return an HTML link tag with populated href. + +csscrush_tag( string $file [, array [$options](#api--options) [, array $attributes ]] ) + + +## csscrush_inline() + +Process host CSS file and return CSS as text wrapped in html `style` tags. + +csscrush_inline( string $file [, array [$options](#api--options) [, array $attributes ]] ) + + +## csscrush_string() + +Compile a raw string of CSS string and return it. + +csscrush_string( string $string [, array [$options](#api--options) ] ) + + +## csscrush_stat() + +Retrieve statistics from the most recent compiled file. Current available stats: selector_count, rule_count, compile_time and errors. + +`csscrush_stat()` + + +## csscrush_version() + +Get the library version. + +`csscrush_version()` + + +## csscrush_get() + +Retrieve a config setting or option default. + + * `$object_name` Name of object you want to inspect: 'config' or 'options'. + * `$property` + +`csscrush_get( string $object_name, string $property )` + + +## csscrush_set() + +Set a config setting or option default. + + * `$object_name` Name of object you want to modify: 'config' or 'options'. + * `$settings` Assoc array of keys and values to set, or callable which argument is the object specified in `$object_name`. + +`csscrush_set( string $object_name, mixed $settings )` diff --git a/docs/api/options.md b/docs/api/options.md new file mode 100644 index 0000000..2d4c097 --- /dev/null +++ b/docs/api/options.md @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Option + Values (default in bold) + Description +
minify + true | false | Array + Enable or disable minification. Optionally specify an array of advanced minification parameters. Currently the only advanced option is 'colors', which will compress all color values in any notation. +
formatter + block | single-line | padded + Set the formatting mode. Overrides minify option if both are set. +
newlines + use-platform | windows/win | unix + Set the output style of newlines +
boilerplate + true | false | Path + Prepend a boilerplate to the output file +
versioning + true | false + Append a timestamped querystring to the output filename +
vars + Array + An associative array of CSS variables to be applied at runtime. These will override variables declared globally or in the CSS. +
cache + true | false + Turn caching on or off. +
output_dir + Path + Specify an output directory for compiled files. Defaults to the same directory as the host file. +
output_file + Output filename + Specify an output filename (suffix is added). +
asset_dir + Path + Directory for SVG and image files generated by plugins (defaults to the main file output directory). +
stat_dump + false | true | Path + Save compile stats and variables to a file in json format. +
vendor_target + "all" | "moz", "webkit", ... | Array +Limit aliasing to a specific vendor, or an array of vendors. +
rewrite_import_urls + true | false | "absolute" + Rewrite relative URLs inside inlined imported files. +
enable + Array + An array of plugin names to enable. +
disable + Array | "all" + An array of plugin names to disable. This option is always evaluated before enable. +
source_map + true | false + Output a source map (compliant with the Source Map v3 proposal). +
trace + true | false + Output SASS debug-info stubs for use with tools like FireSass. +
context + Path + Context for importing resources from relative urls (Only applies to `csscrush_string()` and command line utility). +
doc_root + Path + Specify an alternative server document root for situations where the CSS is being served behind an alias or url rewritten path. +
+ diff --git a/docs/core/abstract.md b/docs/core/abstract.md new file mode 100644 index 0000000..4936aa8 --- /dev/null +++ b/docs/core/abstract.md @@ -0,0 +1,44 @@ + + +Abstract rules are generic rules that can be [extended](#core--inheritance) with the `@extend` directive or mixed in (without arguments) like regular [mixins](#core--mixins) with the `@include` directive. + +```crush +@abstract ellipsis { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +@abstract heading { + font: bold 1rem serif; + letter-spacing: .1em; +} + +.foo { + @extend ellipsis; + display: block; +} +.bar { + @extend ellipsis; + @include heading; +} +``` + +```css +.foo, +.bar { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.foo { + display: block; +} +.bar { + font: bold 1rem serif; + letter-spacing: .1em; +} +``` diff --git a/docs/core/auto-prefixing.md b/docs/core/auto-prefixing.md new file mode 100644 index 0000000..26b74a7 --- /dev/null +++ b/docs/core/auto-prefixing.md @@ -0,0 +1,53 @@ + + +Vendor prefixes for properties, functions, @-rules and even full declarations are **automatically generated** – based on [trusted](http://caniuse.com) [sources](http://developer.mozilla.org/en-US/docs/CSS/CSS_Reference) – so you can maintain cross-browser support while keeping your source code clean and easy to maintain. + +In some cases (e.g. CSS3 gradients) final syntax is incompatible with older prefixed syntax. In these cases the old syntax is polyfilled so you can use the correct syntax while preserving full support for older implementations. + +```crush +.foo { + background: linear-gradient(to right, red, white); +} +``` + +```css +.foo { + background: -webkit-linear-gradient(left, red, white); + background: -moz-linear-gradient(left, red, white); + background: -o-linear-gradient(left, red, white); + background: linear-gradient(to right, red, white); +} +``` + + +```crush +@keyframes bounce { + 50% {transform: scale(1.4);} +} +``` + +```css +@-webkit-keyframes bounce { + 50% {-webkit-transform: scale(1.4); + transform: scale(1.4);} +} +@-moz-keyframes bounce { + 50% {-moz-transform: scale(1.4); + transform: scale(1.4);} +} +@-o-keyframes bounce { + 50% {-o-transform: scale(1.4); + transform: scale(1.4);} +} +@keyframes bounce { + 50% {-webkit-transform: scale(1.4); + -moz-transform: scale(1.4); + -ms-transform: scale(1.4); + -o-transform: scale(1.4); + transform: scale(1.4);} +} +``` diff --git a/docs/core/block-nesting.md b/docs/core/block-nesting.md new file mode 100644 index 0000000..3a34837 --- /dev/null +++ b/docs/core/block-nesting.md @@ -0,0 +1,37 @@ + + +Block nesting is done with the `@in` directive. Especially useful for when you need to group lots of styles under a common selector prefix. + +Note use of the parent selector `&`: + +```crush2 +@in .homepage { + @in .content { + p { + font-size: 110%; + } + } + &.blue { + color: powderblue; + } + .no-js & { + max-width: 1024px; + } +} +``` + +```crush +.homepage .content p { + font-size: 110%; +} +.homepage.blue { + color: powderblue; +} +.no-js .homepage { + max-width: 1024px; +} +``` diff --git a/docs/core/direct-import.md b/docs/core/direct-import.md new file mode 100644 index 0000000..fea9d69 --- /dev/null +++ b/docs/core/direct-import.md @@ -0,0 +1,25 @@ + + +Files referenced with the `@import` directive are inlined directly to save on http requests. Relative URL paths in the CSS are also updated if necessary. + +If you specify a media designation following the import URL — as per the CSS standard — the imported file content is wrapped in a `@media` block. + + +```crush +/* Standard CSS @import statements */ +@import "http://github.com/print.css" print; +@import url(http://github.com/%22small-screen.css%22) screen and ( max-width: 500px ); +``` + +```css +@media print { + /* Contents of print.css */ +} +@media screen and ( max-width: 500px ) { + /* Contents of small-screen.css */ +} +``` diff --git a/docs/core/fragments.md b/docs/core/fragments.md new file mode 100644 index 0000000..2d3d693 --- /dev/null +++ b/docs/core/fragments.md @@ -0,0 +1,25 @@ + + +Fragments – defined and invoked with the @fragment directive – work in a similar way to [mixins](#core--mixins), except that they work at block level: + +```crush +@fragment input-placeholder { + #(1)::-webkit-input-placeholder { color: #(0); } + #(1):-moz-placeholder { color: #(0); } + #(1)::placeholder { color: #(0); } + #(1).placeholder-state { color: #(0); } +} + +@fragment input-placeholder(#777, textarea); +``` + +```css +textarea::-webkit-input-placeholder { color: #777; } +textarea:-moz-placeholder { color: #777; } +textarea::placeholder { color: #777; } +textarea.placeholder-state { color: #777; } +``` diff --git a/docs/core/functions/a-adjust.md b/docs/core/functions/a-adjust.md new file mode 100644 index 0000000..eb4bf75 --- /dev/null +++ b/docs/core/functions/a-adjust.md @@ -0,0 +1,23 @@ + + +Manipulate the opacity (alpha channel) of a color value. + +a-adjust( *color*, *offset* ) + +## Params + +* *`color`* Any valid CSS color value +* *`offset`* The percentage to offset the color opacity + +## Returns + +The modified color value + +```css +/* Reduce color opacity by 10% */ +color: a-adjust( rgb(50,50,0) -10 ); +``` diff --git a/docs/core/functions/data-uri.md b/docs/core/functions/data-uri.md new file mode 100644 index 0000000..0244213 --- /dev/null +++ b/docs/core/functions/data-uri.md @@ -0,0 +1,30 @@ + + +Create a data-uri. + +data-uri( *url* ) + +## Params + +* *`url`* URL of an asset + +`url` cannot be external, and must not be written with an http protocol prefix. + +The following file extensions are supported: jpg, jpeg, gif, png, svg, svgz, ttf, woff + + +## Returns + +The created data-uri as a string inside a CSS url(). + +```css +background: silver data-uri(../images/stripe.png); +``` + +```css +background: silver url(data:<img-data>); +``` \ No newline at end of file diff --git a/docs/core/functions/h-adjust.md b/docs/core/functions/h-adjust.md new file mode 100644 index 0000000..f71c42d --- /dev/null +++ b/docs/core/functions/h-adjust.md @@ -0,0 +1,22 @@ + + +Adjust the hue of a color value. + +h-adjust( *color*, *offset* ) + +## Params + +* *`color`* Any valid CSS color value +* *`offset`* The percentage to offset the color hue (percent mark optional) + +## Returns + +The modified color value. + +```css +color: h-adjust( deepskyblue -10 ); +``` diff --git a/docs/core/functions/hsl-adjust.md b/docs/core/functions/hsl-adjust.md new file mode 100644 index 0000000..c613ccc --- /dev/null +++ b/docs/core/functions/hsl-adjust.md @@ -0,0 +1,25 @@ + + +Manipulate the hue, saturation and lightness of a color value + +hsl-adjust( *color*, *hue-offset*, *saturation-offset*, *lightness-offset* ) + +## Params + +* *`color`* Any valid CSS color value +* *`hue-offset`* The percentage to offset the color hue +* *`saturation-offset`* The percentage to offset the color saturation +* *`lightness-offset`* The percentage to offset the color lightness + +## Returns + +The modified color value + +```css +/* Lighten and increase saturation */ +color: hsl-adjust( red 0 5 5 ); +``` diff --git a/docs/core/functions/hsla-adjust.md b/docs/core/functions/hsla-adjust.md new file mode 100644 index 0000000..6532c8e --- /dev/null +++ b/docs/core/functions/hsla-adjust.md @@ -0,0 +1,25 @@ + + +Manipulate the hue, saturation, lightness and opacity of a color value. + +hsl-adjust( *color*, *hue-offset*, *saturation-offset*, *lightness-offset*, *alpha-offset* = 0 ) + +## Params + +* *`color`* Any valid CSS color value +* *`hue-offset`* The percentage to offset the color hue +* *`saturation-offset`* The percentage to offset the color saturation +* *`lightness-offset`* The percentage to offset the color lightness +* *`alpha-offset`* The percentage to offset the color opacity + +## Returns + +The modified color value. + +```css +color: hsla-adjust( #f00 0 5 5 -10 ); +``` \ No newline at end of file diff --git a/docs/core/functions/l-adjust.md b/docs/core/functions/l-adjust.md new file mode 100644 index 0000000..2958d55 --- /dev/null +++ b/docs/core/functions/l-adjust.md @@ -0,0 +1,22 @@ + + +Adjust the lightness of a color value. + +l-adjust( *color*, *offset* ) + +## Params + +* *`color`* Any valid CSS color value +* *`offset`* The percentage to offset the color hue (percent mark optional) + +## Returns + +The modified color value. + +```css +color: l-adjust( deepskyblue 10 ); +``` diff --git a/docs/core/functions/math.md b/docs/core/functions/math.md new file mode 100644 index 0000000..2621e80 --- /dev/null +++ b/docs/core/functions/math.md @@ -0,0 +1,17 @@ + + +Evaluate a raw mathematical expression. + +math( *expression* ) *or* ( *expression* ) + +```css +font-size: ( 12 / 16 )em; +``` + +```css +font-size: 0.75em; +``` diff --git a/docs/core/functions/percent.md b/docs/core/functions/percent.md new file mode 100644 index 0000000..95c7722 --- /dev/null +++ b/docs/core/functions/percent.md @@ -0,0 +1,28 @@ + + +Calculate a percentage value based on two given values. + +percent( *value1*, *value2* [, *precision* = 5] ) + +## Params + +* *`value1`* Number +* *`value2`* Number +* *`precision`* Integer The number of decimal places to round to. Defaults to 5 + +## Returns + +*`value1`* as a percentage of *`value2`*. + + +```css +width: percent( 20, 960 ); +``` + +```css +width: 2.08333%; +``` \ No newline at end of file diff --git a/docs/core/functions/query.md b/docs/core/functions/query.md new file mode 100644 index 0000000..e9a0d8b --- /dev/null +++ b/docs/core/functions/query.md @@ -0,0 +1,37 @@ + + +Copy a value from another rule. + +query( *reference* [, *property-name* = default] [, *fallback*] ) + +## Params + +* *`reference`* A CSS selector to match, or abstract rule name +* *`property-name`* The CSS property name to copy, or just 'default' to pass over. Defaults to the calling property +* *`fallback`* A CSS value to use if the target property does not exist + + +## Returns + +The referenced property value, or the fallback if it has not been set. + + +```css +.foo { + width: 40em; + height: 100em; +} + +.bar { + width: query( .foo ); /* 40em */ + margin-top: query( .foo, height ); /* 100em */ + margin-right: query( .foo, top, auto ); /* auto */ + margin-bottom: query( .foo, default, 3em ); /* 3em */ +} +``` + + diff --git a/docs/core/functions/s-adjust.md b/docs/core/functions/s-adjust.md new file mode 100644 index 0000000..406d05f --- /dev/null +++ b/docs/core/functions/s-adjust.md @@ -0,0 +1,23 @@ + + +Adjust the saturation of a color value. + +s-adjust( *color*, *offset* ) + +## Params + +* *`color`* Any valid CSS color value +* *`offset`* The percentage to offset the color hue (percent mark optional) + +## Returns + +The modified color value. + +```css +/* Desaturate */ +color: s-adjust( deepskyblue -100 ); +``` diff --git a/docs/core/functions/this.md b/docs/core/functions/this.md new file mode 100644 index 0000000..d7ba2b8 --- /dev/null +++ b/docs/core/functions/this.md @@ -0,0 +1,39 @@ + + +Reference another property value from the same containing block. + +Restricted to referencing properties that don't already reference other properties. + +this( *property-name*, *fallback* ) + +## Params + +* *`property-name`* Property name +* *`fallback`* A CSS value + +## Returns + +The referenced property value, or the fallback if it has not been set. + + +```css +.foo { + width: this( height ); + margin-top: -( this( height ) / 2 )em; + height: 100em; +} +``` + +******** + +```css +/* The following both fail because they create circular references. */ +.bar { + height: this( width ); + width: this( height ); +} +``` diff --git a/docs/core/inheritance.md b/docs/core/inheritance.md new file mode 100644 index 0000000..e3141af --- /dev/null +++ b/docs/core/inheritance.md @@ -0,0 +1,126 @@ + + +By using the `@extend` directive and passing it a named ruleset or selector from any other rule you can share styles more effectively across a stylesheet. + +[Abstract rules](#core--abstract) can be used if you just need to extend a generic set of declarations. + +```crush +.negative-text { + overflow: hidden; + text-indent: -9999px; +} + +.sidebar-headline { + @extend .negative-text; + background: url(http://github.com/headline.png) no-repeat; +} +``` + +```css +.negative-text, +.sidebar-headline { + overflow: hidden; + text-indent: -9999px; +} + +.sidebar-headline { + background: url(http://github.com/headline.png) no-repeat; +} +``` + +Inheritance is recursive: + +```crush +.one { color: pink; } +.two { @extend .one; } +.three { @extend .two; } +.four { @extend .three; } +``` + +```css +.one, .two, .three, .four { color: pink; } +``` + + +## Extending with pseudo classes/elements + +`@extend` arguments can adopt pseudo classes/elements by appending an exclamation mark: + +```crush +.link-base { + color: #bada55; + text-decoration: underline; +} +.link-base:hover, +.link-base:focus { + text-decoration: none; +} + +.link-footer { + @extend .link-base, .link-base:hover!, .link-base:focus!; + color: blue; +} +``` + +```css +.link-base, +.link-footer { + color: #bada55; + text-decoration: underline; +} + +.link-base:hover, +.link-base:focus, +.link-footer:hover, +.link-footer:focus { + text-decoration: none; +} + +.link-footer { + color: blue; +} +``` + +The same outcome can also be achieved with an [Abstract rule](#core--abstract) wrapper to simplify repeated use: + +```crush +.link-base { + color: #bada55; + text-decoration: underline; +} +.link-base:hover, +.link-base:focus { + text-decoration: none; +} + +@abstract link-base { + @extend .link-base, .link-base:hover!, .link-base:focus!; +} + +.link-footer { + @extend link-base; + color: blue; +} +``` + +## Referencing rules by name + +If you want to reference a rule without being concerned about later changes to the identifying selector use the `@name` directive: + +```crush +.foo123 { + @name foo; + text-decoration: underline; +} + +.bar { + @include foo; +} +.baz { + @extend foo; +} +``` diff --git a/docs/core/mixins.md b/docs/core/mixins.md new file mode 100644 index 0000000..1348ff8 --- /dev/null +++ b/docs/core/mixins.md @@ -0,0 +1,95 @@ + + +Mixins make reusing small snippets of CSS much simpler. You define them with the `@mixin` directive. + +Positional arguments via the argument function `#()` extend the capability of mixins for repurposing in different contexts. + +```crush +@mixin display-font { + font-family: "Arial Black", sans-serif; + font-size: #(0); + letter-spacing: #(1); +} + +/* Another mixin with default arguments */ +@mixin blue-theme { + color: #(0 navy); + background-image: url("http://github.com/images/#(1%20cross-hatch).png"); +} + +/* Applying the mixins */ +.foo { + @include display-font(100%, .1em), blue-theme; +} +``` + +```css +.foo { + font-family: "Arial Black", sans-serif; + font-size: 100%; + letter-spacing: .1em; + color: navy; + background-image: url("http://github.com/images/cross-hatch.png"); +} +``` + +## Skipping arguments + +Mixin arguments can be skipped by using the **default** keyword: + +```crush +@mixin display-font { + font-size: #(0 100%); + letter-spacing: #(1); +} + +/* Applying the mixin skipping the first argument so the + default value is used instead */ +#foo { + @include display-font(default, .3em); +} +``` + +Sometimes you may need to use the same positional argument more than once. In this case the default value only needs to be specified once: + +```crush +@mixin square { + width: #(0 10px); + height: #(0); +} + +.foo { + @include square; +} +``` + +```css +#foo { + width: 10px; + height: 10px; +} +``` + + +## Mixing-in from other sources + +Normal rules and [abstract rules](#core--abstract) can also be used as static mixins without arguments: + +```crush +@abstract negative-text { + text-indent: -9999px; + overflow: hidden; +} + +#main-content .theme-border { + border: 1px solid maroon; +} + +.foo { + @include negative-text, #main-content .theme-border; +} +``` diff --git a/docs/core/selector-aliases.md b/docs/core/selector-aliases.md new file mode 100644 index 0000000..4d0f0b5 --- /dev/null +++ b/docs/core/selector-aliases.md @@ -0,0 +1,48 @@ + + +Selector aliases can be useful for grouping together common selector chains for reuse. + +They're defined with the `@selector-alias` directive, and can be used anywhere you might use a psuedo class. + + +```crush +/* Defining selector aliases */ +@selector-alias heading :any(h1, h2, h3, h4, h5, h6); +@selector-alias radio input[type="radio"]; + +/* Selector alias with arguments */ +@selector-alias class-prefix :any([class^="#(0)"], [class*=" #(0)"]); + +.sidebar :heading { + color: honeydew; +} + +:radio { + margin-right: 4px; +} + +:class-prefix(button) { + border: 1px solid rgba(0,0,0,.5); +} +``` + +```css +.sidebar h1, .sidebar h2, +.sidebar h3, .sidebar h4, +.sidebar h5, .sidebar h6 { + color: honeydew; +} + +input[type="radio"] { + margin-right: 4px; +} + +[class^="button"], +[class*=" button"] { + border: 1px solid rgba(0,0,0,.5); +} +``` diff --git a/docs/core/selector-grouping.md b/docs/core/selector-grouping.md new file mode 100644 index 0000000..5e4d924 --- /dev/null +++ b/docs/core/selector-grouping.md @@ -0,0 +1,22 @@ + + +Selector grouping with the `:any` pseudo class (modelled after CSS4 :matches) simplifies the creation of complex selector chains. + +```css +:any( .sidebar, .block ) a:any( :hover, :focus ) { + color: lemonchiffon; +} +``` + +```css +.block a:hover, +.block a:focus, +.sidebar a:hover, +.sidebar a:focus { + color: lemonchiffon; +} +``` diff --git a/docs/core/variables.md b/docs/core/variables.md new file mode 100644 index 0000000..f521078 --- /dev/null +++ b/docs/core/variables.md @@ -0,0 +1,59 @@ + + +Declare variables in your CSS with a `@define` directive and apply them using the `$()` function. + +Variables can be injected at runtime with the [vars option](#api--options). + + +```crush +/* Defining variables */ +@define { + helvetica: "Helvetica Neue", "Helvetica", "Arial", sans-serif; + theme-bg-color: #88CDEA; + theme-fg-color: #F4F2E2; + breakpoint-1: 960px; +} + +/* Applying variables */ +@media only screen and (max-width: $(breakpoint-1)) { + ul, p { + color: $(theme-fg-color); + /* Specifying a fallback value */ + background-color: $(accent-color #ff0); + } +} +``` + +******* + +```css +/* Interpolation */ +.username::before { + content: "$(lang-greeting)"; +} +``` + +## Conditionals + +Sections of CSS can be included and excluded on the basis of variable existence with the `@ifdefine` directive: + +```crush +@define foo #f00; + +@ifdefine foo { + p { + color: $(foo); + } +} + +p { + font-size: 12px; + @ifdefine not foo { + line-height: 1.5; + } +} +``` diff --git a/docs/getting-started/download.md b/docs/getting-started/download.md new file mode 100644 index 0000000..44073cf --- /dev/null +++ b/docs/getting-started/download.md @@ -0,0 +1,17 @@ + +If you're using [Composer](http://getcomposer.org) you can use Crush in your project with the following line in your terminal: + +```shell +composer require css-crush/css-crush:dev-master +``` + +If you're not using Composer yet just download the library ([zip](http://github.com/peteboere/css-crush/zipball/master) or [tar](http://github.com/peteboere/css-crush/tarball/master)) into a convenient location and require the bootstrap file: + +```php + +``` + diff --git a/docs/plugins/aria.md b/docs/plugins/aria.md new file mode 100644 index 0000000..86e77b1 --- /dev/null +++ b/docs/plugins/aria.md @@ -0,0 +1,21 @@ + +Pseudo classes for working with ARIA roles, states and properties. + + * [ARIA roles spec](http://www.w3.org/TR/wai-aria/roles) + * [ARIA states and properties spec](http://www.w3.org/TR/wai-aria/states_and_properties) + +````css +:role(tablist) {...} +:aria-expanded {...} +:aria-expanded(false) {...} +:aria-label {...} +:aria-label(foobarbaz) {...} +```` + +````css +[role="tablist"] {...} +[aria-expanded="true"] {...} +[aria-expanded="false"] {...} +[aria-label] {...} +[aria-label="foobarbaz"] {...} +```` diff --git a/docs/plugins/canvas.md b/docs/plugins/canvas.md new file mode 100644 index 0000000..284dd81 --- /dev/null +++ b/docs/plugins/canvas.md @@ -0,0 +1,55 @@ + +Bitmap image generator. + +Requires the GD image library bundled with PHP. + +```crush +/* Create square semi-opaque png. */ +@canvas foo { + width: 50; + height: 50; + fill: rgba(255, 0, 0, .5); +} + +body { + background: white canvas(foo); +} +``` + +***** + +```crush +/* White to transparent east facing gradient with 10px + margin and background fill. */ +@canvas horz-gradient { + width: #(0); + height: 150; + fill: canvas-linear-gradient(to right, #(1 white), #(2 rgba(255,255,255,0))); + background-fill: powderblue; + margin: 10; +} + +/* Rectangle 300x150. */ +body { + background: canvas(horz-gradient, 300); +} +/* Flipped gradient, using canvas-data() to generate a data URI. */ +.bar { + background: canvas-data(horz-gradient, 100, rgba(255,255,255,0), white); +} +``` + +***** + +```crush +/* Google logo resized to 400px width and given a sepia effect. */ +@canvas sepia { + src: url("https://www.google.co.uk/images/srpr/logo4w.png"); + width: 400; + canvas-filter: greyscale() colorize(45, 45, 0); +} + +.bar { + background: canvas(sepia); +} +``` diff --git a/docs/plugins/color.md b/docs/plugins/color.md new file mode 100644 index 0000000..6356c76 --- /dev/null +++ b/docs/plugins/color.md @@ -0,0 +1,21 @@ + +Define custom color keywords. + +Standard color keywords (blue, red etc.) cannot be overridden. + +```crush +@color { + acme-blue: s-adjust(blue -10); + kolanut: #D0474E; +} + +/* Alternative syntax */ +@color vanilla #FBF7EC; + + +/* Usage is the same as with native color keywords */ +p { + color: vanilla; + border: 1px solid acme-blue; +} +``` diff --git a/docs/plugins/ease.md b/docs/plugins/ease.md new file mode 100644 index 0000000..583f9cb --- /dev/null +++ b/docs/plugins/ease.md @@ -0,0 +1,37 @@ + +Expanded easing keywords for transitions. + +* ease-in-out-back +* ease-in-out-circ +* ease-in-out-expo +* ease-in-out-sine +* ease-in-out-quint +* ease-in-out-quart +* ease-in-out-cubic +* ease-in-out-quad +* ease-out-back +* ease-out-circ +* ease-out-expo +* ease-out-sine +* ease-out-quint +* ease-out-quart +* ease-out-cubic +* ease-out-quad +* ease-in-back +* ease-in-circ +* ease-in-expo +* ease-in-sine +* ease-in-quint +* ease-in-quart +* ease-in-cubic +* ease-in-quad + +See [easing demos](http://easings.net) for live examples. + +```css +transition: .2s ease-in-quad; +``` + +```css +transition: .2s cubic-bezier(.550,.085,.680,.530); +``` diff --git a/docs/plugins/forms.md b/docs/plugins/forms.md new file mode 100644 index 0000000..66afbb4 --- /dev/null +++ b/docs/plugins/forms.md @@ -0,0 +1,16 @@ + +Pseudo classes for working with forms. + +```css +:input(date, search, email) {...} +:checkbox {...} +:radio {...} +:text {...} +``` + +```css +input[type="date"], input[type="search"], input[type="email"] {...} +input[type="checkbox"] {...} +input[type="radio"] {...} +input[type="text"] {...} +``` diff --git a/docs/plugins/hocus-pocus.md b/docs/plugins/hocus-pocus.md new file mode 100644 index 0000000..4cc4a46 --- /dev/null +++ b/docs/plugins/hocus-pocus.md @@ -0,0 +1,12 @@ + +Composite :hover/:focus/:active pseudo classes. + +```css +a:hocus { color: red; } +a:pocus { color: red; } +``` + +```css +a:hover, a:focus { color: red; } +a:hover, a:focus, a:active { color: red; } +``` diff --git a/docs/plugins/hsl2hex.md b/docs/plugins/hsl2hex.md new file mode 100644 index 0000000..16acbe0 --- /dev/null +++ b/docs/plugins/hsl2hex.md @@ -0,0 +1,10 @@ + +Convert colors in hsl() notation to hex notation. + +```css +color: hsl( 100, 50%, 50%); +``` + +```css +color: #6abf40; +``` \ No newline at end of file diff --git a/docs/plugins/ie-inline-block.md b/docs/plugins/ie-inline-block.md new file mode 100644 index 0000000..a9d559c --- /dev/null +++ b/docs/plugins/ie-inline-block.md @@ -0,0 +1,12 @@ + +Polyfill for display:inline-block in IE < 8 + +```css +display: inline-block; +``` + +```css +display: inline-block; +*display: inline; +*zoom: 1; +``` \ No newline at end of file diff --git a/docs/plugins/ie-opacity.md b/docs/plugins/ie-opacity.md new file mode 100644 index 0000000..aacee49 --- /dev/null +++ b/docs/plugins/ie-opacity.md @@ -0,0 +1,13 @@ + +Polyfill for opacity in IE < 9 + +```css +opacity: 0.45; +``` + +```css +opacity: 0.45; +-ms-filter: "alpha(opacity=45)"; +*filter: alpha(opacity=45); +zoom: 1; +``` \ No newline at end of file diff --git a/docs/plugins/initial.md b/docs/plugins/initial.md new file mode 100644 index 0000000..c469fca --- /dev/null +++ b/docs/plugins/initial.md @@ -0,0 +1,16 @@ + +Polyfill for the 'initial' keyword + +* [Initial keyword spec](http://www.w3.org/TR/css3-cascade/#initial0) + +```css +opacity: initial; +white-space: initial; +min-height: initial; +``` + +```css +opacity: 1; +white-space: normal; +max-height: auto; +``` diff --git a/docs/plugins/legacy-flexbox.md b/docs/plugins/legacy-flexbox.md new file mode 100644 index 0000000..8194a21 --- /dev/null +++ b/docs/plugins/legacy-flexbox.md @@ -0,0 +1,40 @@ + +Polyfill to auto-generate legacy flexbox syntaxes. + +Works in conjunction with aliases to support legacy flexbox (flexbox 2009) syntax with modern flexbox. + +```css +display: flex; +flex-flow: row-reverse wrap; +justify-content: space-between; +``` + +```css +display: -webkit-box; +display: -moz-box; +display: -webkit-flex; +display: -ms-flexbox; +display: flex; +-webkit-box-direction: reverse; +-moz-box-direction: reverse; +-webkit-box-orient: horizontal; +-moz-box-orient: horizontal; +-webkit-box-lines: wrap; +-moz-box-lines: wrap; +-webkit-flex-flow: row-reverse wrap; +-ms-flex-flow: row-reverse wrap; +flex-flow: row-reverse wrap; +-webkit-box-pack: justify; +-moz-box-pack: justify; +-webkit-justify-content: space-between; +-ms-flex-pack: justify; +justify-content: space-between; +``` + +### Caveats + +Firefox's early flexbox implementation (Firefox < 22) has several non-trivial issues: + +* With flex containers `display: -moz-box` generates an inline-block element, not a block level element as in other implementations. Suggested workaround is to set `width: 100%`, in conjunction with `box-sizing: border-box` if padding is required. +* The width of flex items can only be set in pixels. +* Flex items cannot be justified. I.e. `-moz-box-pack: justify` does not work. diff --git a/docs/plugins/loop.md b/docs/plugins/loop.md new file mode 100644 index 0000000..eed8611 --- /dev/null +++ b/docs/plugins/loop.md @@ -0,0 +1,57 @@ + +For...in loops with lists and generator functions. + +```crush +@for fruit in apple, orange, pear { + .#(fruit) { + background-image: url("http://github.com/images/#(fruit).jpg"); + } +} +``` + +```css +.apple { background-image: url(http://github.com/images/apple.jpg); } +.orange { background-image: url(http://github.com/images/orange.jpg); } +.pear { background-image: url(http://github.com/images/pear.jpg); } +``` + +```crush +@for base in range(2, 24) { + @for i in range(1, #(base)) { + .grid-#(i)-of-#(base) { + width: (#(i) / #(base) * 100)%; + } + } +} +``` + +```css +.grid-1-of-2 { width: 50%; } +.grid-2-of-2 { width: 100%; } +/* + Intermediate steps ommited. +*/ +.grid-23-of-24 { width: 95.83333%; } +.grid-24-of-24 { width: 100%; } +``` + +```crush +/* The last argument to color-range() is an integer + specifying how many transition colors to generate + between the color arguments. */ + @for color in color-range(powderblue, deeppink, a-adjust(yellow, -80), 5) { + .foo-#(loop.counter) { + background-color: #(color); + } + } +``` + +```css +.foo-1 { background-color: #b0e0e6; } +.foo-2 { background-color: #bdbed8; } +/* + Intermediate steps ommited +*/ +.foo-12 { background-color: rgba(255,216,25,.33); } +.foo-13 { background-color: rgba(255,255,0,.2); } +``` diff --git a/docs/plugins/noise.md b/docs/plugins/noise.md new file mode 100644 index 0000000..ab1e76e --- /dev/null +++ b/docs/plugins/noise.md @@ -0,0 +1,61 @@ + +Functions for generating noise textures with SVG filters. + +Supported in any browser that supports SVG filters (IE > 9 and most other browsers). + + +## noise() / turbulence() + +Both functions work in the same way, the difference being `noise()` uses feTurbulence type 'fractalNoise' and `turbulence()` uses feTurbulence type 'turbulence'. + +```syntax +( + [ fill-color || size ]? + [, frequency octaves? sharpness? ]? + [, blend-mode || fade ]? + [, color-filter color-filter-value ]? +) +``` + +### Params + +* **fill-color** - Any valid CSS color value. +* **size** - Pixel size of canvas in format WxH (e.g. 320x480). +* **frequency** - Number. Noise frequency; useful values are between 0 and 1. X and Y frequencies can be specified by joining two numbers with a colon. +* **octaves** - Number. Noise complexity. +* **sharpness** - Noise sharpening; possible values "normal" and "sharpen" +* **blend-mode** - Blend mode for overlaying noise filter; possible values "normal", "multiply", "screen", "darken" and "lighten" +* **fade** - Ranged number (0-1). Opacity of noise effect. +* **color-filter** - Color filter type; possible values "hueRotate" and "saturate" +* **color-filter-value** - Mixed. For "hueRotate" a degree as number. For "saturate" a ranged number (0-1). + +### Returns + +A data-uri. + +```css +/* Grainy noise with 50% opacity and de-saturated. + Demonstrates the "default" keyword for skipping arguments. */ +background-image: noise( slategray, default, .5, saturate 0 ); +``` + +******* + +```css +/* Cloud effect. */ +background: noise( 700x700 skyblue, .01 4 normal, screen, saturate 0 ); +``` + +******* + +```css +/* Typical turbulence effect. */ +background: turbulence(); +``` + +******* + +```css +/* Sand effect. */ +background: turbulence( wheat 400x400, .35:.2 4 sharpen, normal, saturate .4 ); +``` diff --git a/docs/plugins/property-sorter.md b/docs/plugins/property-sorter.md new file mode 100644 index 0000000..0691bed --- /dev/null +++ b/docs/plugins/property-sorter.md @@ -0,0 +1,21 @@ + +Property sorting. + +Examples use the predefined property sorting table. To define a custom sorting order pass an array to `csscrush_set_property_sort_order()` + + +```css +color: red; +background: #000; +opacity: .5; +display: block; +position: absolute; +``` + +```css +position: absolute; +display: block; +opacity: .5; +color: red; +background: #000; +``` diff --git a/docs/plugins/px2em.md b/docs/plugins/px2em.md new file mode 100644 index 0000000..d9e57c5 --- /dev/null +++ b/docs/plugins/px2em.md @@ -0,0 +1,15 @@ + +Functions for converting pixel values into em (px2em) or rem (px2rem) values + +For both functions the optional second argument is base font-size for calculation +(16px by default) though usually not required when converting pixel to rem. + +```css +font-size: px2em(11 13); +font-size: px2rem(16); +``` + +```css +font-size: .84615em; +font-size: 1rem; +``` diff --git a/docs/plugins/rem.md b/docs/plugins/rem.md new file mode 100644 index 0000000..7b453dd --- /dev/null +++ b/docs/plugins/rem.md @@ -0,0 +1,50 @@ + +Polyfill for the rem (root em) length unit. + +No version of IE to date (IE <= 10) resizes text set with pixels though IE > 8 supports rem units which are resizeable. + +* [Rem unit browser support](http://caniuse.com/#feat=rem) + +## Conversion modes + +### rem-fallback (default) + +rem to px, with converted value as fallback. + +```css +font-size: 1rem; +``` + +```css +font-size: 16px; +font-size: 1rem; +``` + +### px-fallback + +px to rem, with original pixel value as fallback. + +```css +font-size: 16px; +``` + +```css +font-size: 16px; +font-size: 1rem; +``` + +### convert + +in-place px to rem conversion. + +```css +font-size: 16px; +``` + +```css +font-size: 1rem; +``` + +`rem-fallback` is the default mode. To change the conversion mode set a variable named `rem__mode` with the mode name you want as its value. + +To convert all length values, not just values of the font related properties, set a variable named `rem__all` with a value of `yes`. diff --git a/docs/plugins/svg-gradients.md b/docs/plugins/svg-gradients.md new file mode 100644 index 0000000..a52a733 --- /dev/null +++ b/docs/plugins/svg-gradients.md @@ -0,0 +1,45 @@ + +Functions for creating SVG gradients with a CSS gradient like syntax. + +Primarily useful for supporting Internet Explorer 9. + +## svg-linear-gradent() + +Syntax is the same as [linear-gradient()](http://dev.w3.org/csswg/css3-images/#linear-gradient) + +`svg-linear-gradent( [ | to ,]? [, ]+ )` + + +### Returns + +A base64 encoded svg data-uri. + +### Known issues + +Color stops can only take percentage value offsets. + +```css +background-image: svg-linear-gradient( to top left, #fff, rgba(255,255,255,0) 80% ); +background-image: svg-linear-gradient( 35deg, red, gold 20%, powderblue ); +``` + + +## svg-radial-gradent() + +Syntax is similar to but more limited than [radial-gradient()](http://dev.w3.org/csswg/css3-images/#radial-gradient) + +`svg-radial-gradent( [ | at ,]? [, ]+ )` + +### Returns + +A base64 encoded svg data-uri. + +### Known issues + +Color stops can only take percentage value offsets. +No control over shape - only circular gradients - however, the generated image can be stretched with background-size. + +```css +background-image: svg-radial-gradient( at center, red, blue 50%, yellow ); +background-image: svg-radial-gradient( 100% 50%, rgba(255,255,255,.5), rgba(255,255,255,0) ); +``` diff --git a/docs/plugins/svg.md b/docs/plugins/svg.md new file mode 100644 index 0000000..35fcc23 --- /dev/null +++ b/docs/plugins/svg.md @@ -0,0 +1,74 @@ + +Define and embed simple SVG elements, paths and effects inside CSS + + +```crush +@svg foo { + type: star; + star-points: #(0 5); + radius: 100 50; + margin: 20; + stroke: black; + fill: red; + fill-opacity: .5; +} + +/* Embed SVG with svg() function (generates an svg file). */ +body { + background: svg(foo); +} +/* As above but a 3 point star creating a data URI instead of a file. */ +body { + background: svg-data(foo, 3); +} +``` + +******* + +```crush +/* Using path data and stroke styles to create a plus sign. */ +@svg plus { + d: "M0,5 h10 M5,0 v10"; + width: 10; + height: 10; + stroke: white; + stroke-linecap: round; + stroke-width: 2; +} +``` + + +******* + +```crush +/* Skewed circle with radial gradient fill and drop shadow. */ +@svg circle { + type: circle; + transform: skewX(30); + diameter: 60; + margin: 20; + fill: svg-radial-gradient(at top right, gold 50%, red); + drop-shadow: 2 2 0 rgba(0,0,0,1); +} +``` + +******* + +```crush +/* 8-sided polygon with an image fill. + Note: images usually have to be converted to data URIs, see known issues below. */ +@svg pattern { + type: polygon; + sides: 8; + diameter: 180; + margin: 20; + fill: pattern(data-uri(kitten.jpg), scale(1) translate(-100 0)); + fill-opacity: .8; +} +``` + + +### Known issues + +Firefox [does not allow linked images](https://bugzilla.mozilla.org/show_bug.cgi?id=628747#c0) (or other svg) when svg is in "svg as image" mode. + diff --git a/docs/plugins/text-align.md b/docs/plugins/text-align.md new file mode 100644 index 0000000..0ac0f70 --- /dev/null +++ b/docs/plugins/text-align.md @@ -0,0 +1,13 @@ + +Polyfill for direction sensitive text-align values, start and end. + +If the 'dir' setting is 'rtl', 'start' translates to 'right' and 'end' translates to 'left'. + +```crush +text-align: start; +``` + +```css +text-align: left; +text-align: start; +``` diff --git a/plugins/aria.php b/plugins/aria.php index ff6b9ba..3eb7ab6 100644 --- a/plugins/aria.php +++ b/plugins/aria.php @@ -2,22 +2,7 @@ /** * Pseudo classes for working with ARIA roles, states and properties. * - * ARIA roles spec: http://www.w3.org/TR/wai-aria/roles - * ARIA states and properties spec: http://www.w3.org/TR/wai-aria/states_and_properties - * - * @before - * :role(tablist) {...} - * :aria-expanded {...} - * :aria-expanded(false) {...} - * :aria-label {...} - * :aria-label(foobarbaz) {...} - * - * @after - * [role="tablist"] {...} - * [aria-expanded="true"] {...} - * [aria-expanded="false"] {...} - * [aria-label] {...} - * [aria-label="foobarbaz"] {...} + * @see docs/plugins/aria.md */ namespace CssCrush; diff --git a/plugins/canvas.php b/plugins/canvas.php index c20beac..06238fe 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -2,50 +2,7 @@ /** * Bitmap image generator * - * Requires the GD image library bundled with PHP. - * - * @example - * - * // Red semi-transparent square. - * @canvas foo { - * width: 50; - * height: 50; - * fill: rgba(255, 0, 0, .5); - * } - * - * body { - * background: canvas(foo); - * } - * - * @example - * - * // White to transparent east facing gradient with 10px margin and - * // background fill. - * @canvas foo { - * width: arg(0, 150); - * height: 150; - * fill: canvas-linear-gradient(to right, white, rgba(255,255,255,0)); - * background-fill: powderblue; - * margin: 10; - * } - * - * // Rectangle 300x150. - * body { - * background: red canvas(foo, 300) no-repeat; - * } - * // Default dimensions 150x150 as data URI. - * .bar { - * background: canvas-data(foo) repeat-x; - * } - * - * @example - * - * // Google logo resized to 400px width and given a sepia effect. - * @canvas foo { - * src: url("https://www.google.co.uk/images/srpr/logo4w.png"); - * width: 400; - * canvas-filter: greyscale() colorize(45, 45, 0); - * } + * @see docs/plugins/canvas.md */ namespace CssCrush; diff --git a/plugins/color.php b/plugins/color.php index 6e61ee5..8e82738 100644 --- a/plugins/color.php +++ b/plugins/color.php @@ -1,6 +1,8 @@ || ]? - * [ , ? ? ]? - * [ , || ]? - * [ , ]? - * ) - * - * - * Any valid CSS color value. - * - * Pixel size of canvas in format WxH (e.g. 320x480). - * - * Number. Noise frequency; useful values are between 0 and 1. - * x and y frequencies can be specified by joining two numbers with a colon. - * - * Number. Noise complexity. - * - * Noise sharpening; possible values: - * "normal", "sharpen" - * - * Blend mode for overlaying noise filter; possible values: - * "normal", "multiply", "screen", "darken", "lighten" - * - * Ranged number (0-1). Opacity of noise effect. - * - * Color filter type; possible values: - * "hueRotate", "saturate" - * - * Mixed. For "hueRotate" a degree as number. For "saturate" a ranged number (0-1). - * - * Returns: - * A base64 encoded svg data-uri. - * - * References: - * http://www.w3.org/TR/SVG/filters.html - * http://srufaculty.sru.edu/david.dailey/svg/SVGOpen2010/Filters2.htm#S11 - * - * Examples: - * // Grainy noise with 50% opacity and de-saturated. - * // Demonstrates the "default" keyword for skipping arguments. - * background-image: noise( slategray, default, .5, saturate 0 ); - * - * // Cloud effect. - * background: noise( 700x700 skyblue, .01 4 normal, screen, saturate 0 ); - * - * turbulence() - * ------------ - * As noise() except uses default turbulance type 'turbulance' and not 'fractalNoise' - * - * Syntax: - * See noise(). - * - * Returns: - * See noise(). - * - * References: - * See noise(). - * - * Examples: - * // Typical turbulence effect. - * background: turbulence(); - * - * // Sand effect. - * background: turbulence( wheat 400x400, .35:.2 4 sharpen, normal, saturate .4 ); + * @see docs/plugins/noise.md */ namespace CssCrush; diff --git a/plugins/property-sorter.php b/plugins/property-sorter.php index bb9bf96..aa998aa 100644 --- a/plugins/property-sorter.php +++ b/plugins/property-sorter.php @@ -2,25 +2,7 @@ /** * Customizable property sorting * - * Examples use the predefined sorting table. - * - * To define a custom sorting order pass an array to csscrush_set_property_sort_order(): - * - * csscrush_set_property_sort_order( array( 'color', ... ) ); - * - * - * @before - * color: red; - * background: #000; - * opacity: .5; - * display: block; - * - * @after - * display: block; - * opacity: .5; - * color: red; - * background: #000; - * + * @see docs/plugins/property-sorter.md */ namespace CssCrush { diff --git a/plugins/px2em.php b/plugins/px2em.php index 85748be..1d71407 100644 --- a/plugins/px2em.php +++ b/plugins/px2em.php @@ -2,16 +2,7 @@ /** * Functions for converting pixel values into em (px2em) or rem (px2rem) values * - * For both functions the optional second argument is base font-size for calculation - * (16px by default) though usually not required when converting pixel to rem. - * - * @before - * font-size: px2em(11 13); - * font-size: px2rem(16); - * - * @after - * font-size: .84615em; - * font-size: 1rem; + * @see docs/plugins/px2em.md */ namespace CssCrush; diff --git a/plugins/rem.php b/plugins/rem.php index 9027a82..1e4f923 100644 --- a/plugins/rem.php +++ b/plugins/rem.php @@ -2,36 +2,7 @@ /** * Polyfill for the rem (root em) length unit * - * No version of IE to date (IE <= 10) resizes text set with pixels. - * IE > 8 supports rem units which will resize. See http://caniuse.com/#feat=rem - * - * Three conversion modes: - * - * rem-fallback (rem to px, with converted value as fallback) - * ============ - * font-size: 1rem; - * - * font-size: 16px; - * font-size: 1rem; - * - * px-fallback (px to rem, with original pixel value as fallback) - * =========== - * font-size: 16px; - * - * font-size: 16px; - * font-size: 1rem; - * - * convert (in-place px to rem conversion) - * ======= - * font-size: 16px; - * - * font-size: 1rem; - * - * `rem-fallback` is the default mode. To change the conversion mode - * declare the setting `rem-mode` with the mode name you want as its value. - * - * To convert all values, not just values of the font related properties, - * declare the setting `rem-all` with the value 'true'. + * @see docs/plugins/rem.md */ namespace CssCrush; diff --git a/plugins/svg-gradients.php b/plugins/svg-gradients.php index f87a8e4..9df74ad 100644 --- a/plugins/svg-gradients.php +++ b/plugins/svg-gradients.php @@ -2,42 +2,7 @@ /** * Functions for creating SVG gradients with a CSS gradient like syntax * - * Primarily useful for supporting Internet Explorer 9. - * - * svg-linear-gradent() - * -------------------- - * Syntax is the same as CR linear-gradient(): http://dev.w3.org/csswg/css3-images/#linear-gradient - * - * svg-linear-gradent( [ | to ,]? [, ]+ ) - * - * Returns: - * A base64 encoded svg data-uri. - * - * Known issues: - * Color stops can only take percentage value offsets. - * - * Examples: - * background-image: svg-linear-gradient( to top left, #fff, rgba(255,255,255,0) 80% ); - * background-image: svg-linear-gradient( 35deg, red, gold 20%, powderblue ); - * - * svg-radial-gradent() - * -------------------- - * Syntax is similar to but more limited than CR radial-gradient(): http://dev.w3.org/csswg/css3-images/#radial-gradient - * - * svg-radial-gradent( [ | at ,]? [, ]+ ) - * - * Returns: - * A base64 encoded svg data-uri. - * - * Known issues: - * Color stops can only take percentage value offsets. - * No control over shape - only circular gradients - however, the generated image can be stretched - * with background-size. - * - * Examples: - * background-image: svg-radial-gradient( at center, red, blue 50%, yellow ); - * background-image: svg-radial-gradient( 100% 50%, rgba(255,255,255,.5), rgba(255,255,255,0) ); - * + * @see docs/plugins/svg-gradients.md */ namespace CssCrush; diff --git a/plugins/svg.php b/plugins/svg.php index 4c4dcf9..b7cd985 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -2,60 +2,7 @@ /** * Define and embed simple SVG elements, paths and effects inside CSS * - * @example - * - * // Define SVG. - * @svg foo { - * type: star; - * star-points: 5; - * radius: 100 50; - * margin: 20; - * stroke: black; - * fill: red; - * fill-opacity: .5; - * } - * - * // Embed SVG with svg() function (generates an svg file in the - * // output directory). - * body { - * background: beige svg(foo); - * } - * // As above but creates a data URI instead of an svg file. - * body { - * background: beige svg-data(foo); - * } - * - * @example - * - * // Skewed circle with radial gradient fill and drop shadow. - * @svg circle { - * type: circle; - * transform: skewX(30); - * diameter: 60; - * margin: 20; - * fill: svg-radial-gradient(at top right, gold 50%, red); - * drop-shadow: 2 2 0 rgba(0,0,0,1); - * } - * - * @example - * - * // 8-sided polygon with an image fill. - * // Note: images usually have to be converted to data URIs, see known issues below. - * @svg pattern { - * type: polygon; - * sides: 8; - * diameter: 180; - * margin: 20; - * fill: pattern(data-uri(kitten.jpg), scale(1) translate(-100 0)); - * fill-opacity: .8; - * } - * - * @known-issues - * - * Firefox does not allow linked images (or other svg) when svg is in "svg as image" mode - - * i.e. Used in an img tag or as a CSS background: - * https://bugzilla.mozilla.org/show_bug.cgi?id=628747#c0 - * + * @see docs/plugins/svg.md */ namespace CssCrush; diff --git a/plugins/text-align.php b/plugins/text-align.php index 29f24af..97c0a58 100644 --- a/plugins/text-align.php +++ b/plugins/text-align.php @@ -2,14 +2,7 @@ /** * Polyfill for direction sensitive text-align values, start and end * - * If the 'dir' setting is 'rtl', 'start' translates to 'right' and 'end' translates to 'left'. - * - * @before - * text-align: start; - * - * @after - * text-align: left; - * text-align: start; + * @see docs/plugins/text-align.md */ namespace CssCrush; From 36dfebdffcb6bf0a4fe01722886e25dfff631a8d Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 21 Jan 2014 19:41:23 +0000 Subject: [PATCH 242/421] Employing PHP's __invoke() method. Some class re-organisation. --- lib/CssCrush/DeclarationList.php | 43 ++++++++++++++++++- lib/CssCrush/Fragment.php | 6 +-- lib/CssCrush/Mixin.php | 2 +- lib/CssCrush/Process.php | 56 +++---------------------- lib/CssCrush/Rule.php | 62 +--------------------------- lib/CssCrush/Selector.php | 47 ++++++++++++++++++++- lib/CssCrush/SelectorList.php | 15 ++++++- lib/CssCrush/Template.php | 46 ++++++++++----------- plugins/canvas.php | 2 +- plugins/svg.php | 2 +- tests/unit/CssCrush/TemplateTest.php | 4 +- 11 files changed, 141 insertions(+), 144 deletions(-) diff --git a/lib/CssCrush/DeclarationList.php b/lib/CssCrush/DeclarationList.php index 441066f..21e2456 100644 --- a/lib/CssCrush/DeclarationList.php +++ b/lib/CssCrush/DeclarationList.php @@ -20,9 +20,50 @@ class DeclarationList extends Iterator // Declarations hash table for external query() referencing. public $queryData = array(); - public function __construct() + public function __construct($declarations_string, Rule $rule) { parent::__construct(); + + $pairs = DeclarationList::parse($declarations_string); + + foreach ($pairs as $index => $pair) { + + list($prop, $value) = $pair; + + // Directives. + if ($prop === 'extends') { + $rule->setExtendSelectors($value); + unset($pairs[$index]); + } + elseif ($prop === 'name') { + if (! $rule->name) { + $rule->name = $value; + } + unset($pairs[$index]); + } + } + + // Build declaration list. + foreach ($pairs as $index => &$pair) { + + list($prop, $value) = $pair; + + if (trim($value) !== '') { + + if ($prop === 'mixin') { + $this->flattened = false; + $this->store[] = $pair; + } + else { + // Only store to $this->data if the value does not itself make a + // this() call to avoid circular references. + if (! preg_match(Regex::$patt->thisFunction, $value)) { + $this->data[strtolower($prop)] = $value; + } + $this->add($prop, $value, $index); + } + } + } } public function add($property, $value, $contextIndex = 0) diff --git a/lib/CssCrush/Fragment.php b/lib/CssCrush/Fragment.php index 9341d09..a2f7823 100644 --- a/lib/CssCrush/Fragment.php +++ b/lib/CssCrush/Fragment.php @@ -16,9 +16,9 @@ public function __construct($str, $options = array()) $this->name = $options['name']; } - public function apply(array $args = null, $str = null) + public function __invoke(array $args = null, $str = null) { - $str = parent::apply($args); + $str = parent::__invoke($args); // Flatten all fragment calls within the template string. while (preg_match(Regex::$patt->fragmentInvoke, $str, $m, PREG_OFFSET_CAPTURE)) { @@ -36,7 +36,7 @@ public function apply(array $args = null, $str = null) if (isset($m['parens'][1])) { $args = Functions::parseArgs($m['parens_content'][0]); } - $replacement = $fragment->apply($args); + $replacement = $fragment($args); } $str = substr_replace($str, $replacement, $start, $length); } diff --git a/lib/CssCrush/Mixin.php b/lib/CssCrush/Mixin.php index 16bb5a8..1fe4826 100644 --- a/lib/CssCrush/Mixin.php +++ b/lib/CssCrush/Mixin.php @@ -61,7 +61,7 @@ public static function call($message, $context = null) $args = Util::splitDelimList($raw_args); } - return DeclarationList::parse($mixable->template->apply($args), array( + return DeclarationList::parse($mixable->template->__invoke($args), array( 'flatten' => true, 'context' => $mixable, )); diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index af7c117..8e00d15 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -185,10 +185,11 @@ protected function getBoilerplate() protected function resolveSelectorAliases() { $this->stream->pregReplaceCallback( - Regex::make('~@selector-alias +\:?({{ident}}) +([^;]+) *;~iS'), + Regex::make('~@selector-(?alias|splat) +\:?(?{{ident}}) +(?[^;]+) *;~iS'), function ($m) { - $name = strtolower($m[1]); - Crush::$process->selectorAliases[$name] = new Template(Util::stripCommentTokens($m[2])); + $name = strtolower($m['name']); + $args = Util::stripCommentTokens($m['args']); + Crush::$process->selectorAliases[$name] = new Template($args); }); // Merge with global selector aliases. @@ -202,51 +203,6 @@ function ($m) { } } - public static function applySelectorAliases($str) - { - $process = Crush::$process; - - if (! $process->selectorAliases || ! preg_match($process->selectorAliasesPatt, $str)) { - return $str; - } - - $table =& $process->selectorAliases; - - while (preg_match_all($process->selectorAliasesPatt, $str, $m, PREG_OFFSET_CAPTURE | PREG_SET_ORDER)) { - - $selector_alias_call = end($m); - $selector_alias_name = strtolower($selector_alias_call[1][0]); - - $start = $selector_alias_call[0][1]; - $length = strlen($selector_alias_call[0][0]); - $args = array(); - - // It's a function alias if a start paren is matched. - if (isset($selector_alias_call[2])) { - - // Parse argument list. - if (preg_match(Regex::$patt->parens, $str, $parens, PREG_OFFSET_CAPTURE, $start)) { - $args = Functions::parseArgs($parens[2][0]); - - // Amend offsets. - $paren_start = $parens[0][1]; - $paren_len = strlen($parens[0][0]); - $length = ($paren_start + $paren_len) - $start; - } - } - - // Resolve the selector alias value to a template instance if a callable is given. - $template = $table[$selector_alias_name]; - if (is_callable($template)) { - $template = new Template($template($args)); - } - - $str = substr_replace($str, $template->apply($args), $start, $length); - } - - return $str; - } - ############################# # Aliases. @@ -571,7 +527,7 @@ protected function resolveFragments() if (isset($m['parens'])) { $args = Functions::parseArgs($m['parens_content']); } - return $fragment->apply($args); + return $fragment($args); } return ''; }); @@ -654,7 +610,7 @@ protected function resolveInBlocks() $match_start_pos = $match[0][1]; $raw_argument = trim($match[1][0]); - $arguments = Util::splitDelimList(Process::applySelectorAliases($raw_argument)); + $arguments = Util::splitDelimList(Selector::expandAliases($raw_argument)); $curly_match = new BalancedMatch($this->stream, $match_start_pos); diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php index 5dddc81..05e3ae0 100644 --- a/lib/CssCrush/Rule.php +++ b/lib/CssCrush/Rule.php @@ -27,66 +27,8 @@ public function __construct($selector_string, $declarations_string, $trace_token $process = Crush::$process; $this->label = $process->tokens->createLabel('r'); $this->marker = $process->addTracingStubs || $process->generateMap ? $trace_token : null; - $this->selectors = new SelectorList(); - $this->declarations = new DeclarationList(); - - // Parse selectors. - // Strip any other comments then create selector instances. - $selector_string = trim(Util::stripCommentTokens($selector_string)); - - foreach (Util::splitDelimList($selector_string) as $selector) { - - if (preg_match(Regex::$patt->abstract, $selector, $m)) { - $this->name = strtolower($m['name']); - $this->isAbstract = true; - } - else { - $this->selectors->add(new Selector($selector)); - } - } - - $pairs = DeclarationList::parse($declarations_string); - - foreach ($pairs as $index => $pair) { - - list($prop, $value) = $pair; - - if ($prop === 'extends') { - - // Extends are also a special case. - $this->setExtendSelectors($value); - unset($pairs[$index]); - } - elseif ($prop === 'name') { - - if (! $this->name) { - $this->name = $value; - } - unset($pairs[$index]); - } - } - - // Build declaration list. - foreach ($pairs as $index => &$pair) { - - list($prop, $value) = $pair; - - if (trim($value) !== '') { - - if ($prop === 'mixin') { - $this->declarations->flattened = false; - $this->declarations->store[] = $pair; - } - else { - // Only store to $this->data if the value does not itself make a - // this() call to avoid circular references. - if (! preg_match(Regex::$patt->thisFunction, $value)) { - $this->declarations->data[strtolower($prop)] = $value; - } - $this->declarations->add($prop, $value, $index); - } - } - } + $this->selectors = new SelectorList($selector_string, $this); + $this->declarations = new DeclarationList($declarations_string, $this); } public function __toString() diff --git a/lib/CssCrush/Selector.php b/lib/CssCrush/Selector.php index 75de3bd..bc20db7 100644 --- a/lib/CssCrush/Selector.php +++ b/lib/CssCrush/Selector.php @@ -23,7 +23,7 @@ public function __construct($raw_selector) // Take readable value from original un-altered state. $this->readableValue = Selector::makeReadable($raw_selector); - $this->value = Process::applySelectorAliases($raw_selector); + $this->value = Selector::expandAliases($raw_selector); } public function __toString() @@ -66,4 +66,49 @@ public static function makeReadable($str) return $str; } + + public static function expandAliases($str) + { + $process = Crush::$process; + + if (! $process->selectorAliases || ! preg_match($process->selectorAliasesPatt, $str)) { + return $str; + } + + $table =& $process->selectorAliases; + + while (preg_match_all($process->selectorAliasesPatt, $str, $m, PREG_OFFSET_CAPTURE | PREG_SET_ORDER)) { + + $selector_alias_call = end($m); + $selector_alias_name = strtolower($selector_alias_call[1][0]); + + $start = $selector_alias_call[0][1]; + $length = strlen($selector_alias_call[0][0]); + $args = array(); + + // It's a function alias if a start paren is matched. + if (isset($selector_alias_call[2])) { + + // Parse argument list. + if (preg_match(Regex::$patt->parens, $str, $parens, PREG_OFFSET_CAPTURE, $start)) { + $args = Functions::parseArgs($parens[2][0]); + + // Amend offsets. + $paren_start = $parens[0][1]; + $paren_len = strlen($parens[0][0]); + $length = ($paren_start + $paren_len) - $start; + } + } + + // Resolve the selector alias value to a template instance if a callable is given. + $template = $table[$selector_alias_name]; + if (is_callable($template)) { + $template = new Template($template($args)); + } + + $str = substr_replace($str, $template($args), $start, $length); + } + + return $str; + } } diff --git a/lib/CssCrush/SelectorList.php b/lib/CssCrush/SelectorList.php index f392390..eef270e 100644 --- a/lib/CssCrush/SelectorList.php +++ b/lib/CssCrush/SelectorList.php @@ -8,9 +8,22 @@ class SelectorList extends Iterator { - public function __construct() + public function __construct($selector_string, Rule $rule) { parent::__construct(); + + $selector_string = trim(Util::stripCommentTokens($selector_string)); + + foreach (Util::splitDelimList($selector_string) as $selector) { + + if (preg_match(Regex::$patt->abstract, $selector, $m)) { + $rule->name = strtolower($m['name']); + $rule->isAbstract = true; + } + else { + $this->add(new Selector($selector)); + } + } } public function add(Selector $selector) diff --git a/lib/CssCrush/Template.php b/lib/CssCrush/Template.php index ca8df11..124c3c8 100644 --- a/lib/CssCrush/Template.php +++ b/lib/CssCrush/Template.php @@ -65,6 +65,29 @@ public function __construct($str, $options = array()) )); } + public function __invoke(array $args = null, $str = null) + { + $str = isset($str) ? $str : $this->string; + + // Apply passed arguments as priority. + if (isset($args)) { + + list($find, $replace) = $this->prepare($args, false); + } + + // Secondly use prepared substitutions if available. + elseif ($this->substitutions) { + + list($find, $replace) = $this->substitutions; + } + + // Apply substitutions. + $str = isset($find) ? str_replace($find, $replace, $str) : $str; + + // Re-tokenize string on return. + return Template::tokenize($str); + } + public function getArgValue($index, &$args) { // First lookup a passed value. @@ -113,29 +136,6 @@ public function prepare(array $args, $persist = true) return $substitutions; } - public function apply(array $args = null, $str = null) - { - $str = isset($str) ? $str : $this->string; - - // Apply passed arguments as priority. - if (isset($args)) { - - list($find, $replace) = $this->prepare($args, false); - } - - // Secondly use prepared substitutions if available. - elseif ($this->substitutions) { - - list($find, $replace) = $this->substitutions; - } - - // Apply substitutions. - $str = isset($find) ? str_replace($find, $replace, $str) : $str; - - // Re-tokenize string on return. - return Template::tokenize($str); - } - public static function tokenize($str) { $str = Crush::$process->tokens->capture($str, 's'); diff --git a/plugins/canvas.php b/plugins/canvas.php index 06238fe..f93a89a 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -71,7 +71,7 @@ function canvas_generator($input, $context) { } // Apply args to template. - $block = $canvas_defs[$name]->apply($args); + $block = $canvas_defs[$name]($args); $raw = DeclarationList::parse($block, array( 'keyed' => true, diff --git a/plugins/svg.php b/plugins/svg.php index b7cd985..7cb9758 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -144,7 +144,7 @@ function svg_generator($input, $fn_name) { } // Apply args to template. - $block = $svg_defs[$name]->apply($args); + $block = $svg_defs[$name]($args); $raw_data = DeclarationList::parse($block, array( 'keyed' => true, diff --git a/tests/unit/CssCrush/TemplateTest.php b/tests/unit/CssCrush/TemplateTest.php index 2ff2c99..91e4a3e 100644 --- a/tests/unit/CssCrush/TemplateTest.php +++ b/tests/unit/CssCrush/TemplateTest.php @@ -51,7 +51,7 @@ public function testPrepare() public function testApply() { - $actual = $this->template->apply(array('one', 'two')); + $actual = $this->template->__invoke(array('one', 'two')); $expected = <<assertEquals($expected, $actual); - $actual = $this->template->apply(array('default', 'colanut')); + $actual = $this->template->__invoke(array('default', 'colanut')); $expected = << Date: Thu, 30 Jan 2014 10:09:24 +0000 Subject: [PATCH 243/421] Adding SelectorAlias class. --- lib/CssCrush/Crush.php | 4 ++-- lib/CssCrush/Process.php | 3 ++- lib/CssCrush/Selector.php | 20 +++++----------- lib/CssCrush/SelectorAlias.php | 42 ++++++++++++++++++++++++++++++++++ plugins/aria.php | 7 +++--- plugins/forms.php | 8 +++---- 6 files changed, 60 insertions(+), 24 deletions(-) create mode 100644 lib/CssCrush/SelectorAlias.php diff --git a/lib/CssCrush/Crush.php b/lib/CssCrush/Crush.php index 140f738..c1ad557 100644 --- a/lib/CssCrush/Crush.php +++ b/lib/CssCrush/Crush.php @@ -363,9 +363,9 @@ public static function stat() ############################# # Global selector aliases. - public static function addSelectorAlias($name, $body) + public static function addSelectorAlias($name, $handler, $type = 'alias') { - Crush::$config->selectorAliases[$name] = is_callable($body) ? $body : new Template($body); + Crush::$config->selectorAliases[$name] = new SelectorAlias($type, $handler); } public static function removeSelectorAlias($name) diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 8e00d15..ce436a9 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -188,8 +188,9 @@ protected function resolveSelectorAliases() Regex::make('~@selector-(?alias|splat) +\:?(?{{ident}}) +(?[^;]+) *;~iS'), function ($m) { $name = strtolower($m['name']); + $type = strtolower($m['type']); $args = Util::stripCommentTokens($m['args']); - Crush::$process->selectorAliases[$name] = new Template($args); + Crush::$process->selectorAliases[$name] = new SelectorAlias($type, $args); }); // Merge with global selector aliases. diff --git a/lib/CssCrush/Selector.php b/lib/CssCrush/Selector.php index bc20db7..4bb6926 100644 --- a/lib/CssCrush/Selector.php +++ b/lib/CssCrush/Selector.php @@ -75,19 +75,17 @@ public static function expandAliases($str) return $str; } - $table =& $process->selectorAliases; - while (preg_match_all($process->selectorAliasesPatt, $str, $m, PREG_OFFSET_CAPTURE | PREG_SET_ORDER)) { - $selector_alias_call = end($m); - $selector_alias_name = strtolower($selector_alias_call[1][0]); + $alias_call = end($m); + $alias_name = strtolower($alias_call[1][0]); - $start = $selector_alias_call[0][1]; - $length = strlen($selector_alias_call[0][0]); + $start = $alias_call[0][1]; + $length = strlen($alias_call[0][0]); $args = array(); // It's a function alias if a start paren is matched. - if (isset($selector_alias_call[2])) { + if (isset($alias_call[2])) { // Parse argument list. if (preg_match(Regex::$patt->parens, $str, $parens, PREG_OFFSET_CAPTURE, $start)) { @@ -100,13 +98,7 @@ public static function expandAliases($str) } } - // Resolve the selector alias value to a template instance if a callable is given. - $template = $table[$selector_alias_name]; - if (is_callable($template)) { - $template = new Template($template($args)); - } - - $str = substr_replace($str, $template($args), $start, $length); + $str = substr_replace($str, $process->selectorAliases[$alias_name]($args), $start, $length); } return $str; diff --git a/lib/CssCrush/SelectorAlias.php b/lib/CssCrush/SelectorAlias.php new file mode 100644 index 0000000..24dd78b --- /dev/null +++ b/lib/CssCrush/SelectorAlias.php @@ -0,0 +1,42 @@ +type = $type; + + switch ($this->type) { + case 'alias': + $this->handler = new Template($handler); + break; + case 'callback': + case 'splat': + $this->handler = $handler; + break; + } + } + + public function __invoke($args) + { + $handler = $this->handler; + + switch ($this->type) { + case 'callback': + $template = new Template($handler($args)); + return $template($args); + case 'alias': + default: + return $handler($args); + } + } +} diff --git a/plugins/aria.php b/plugins/aria.php index 3eb7ab6..e14dd3c 100644 --- a/plugins/aria.php +++ b/plugins/aria.php @@ -8,12 +8,13 @@ Plugin::register('aria', array( 'enable' => function () { - foreach (aria() as $name => $value) { - Crush::addSelectorAlias($name, $value); + foreach (aria() as $name => $handler) { + $type = is_callable($handler) ? 'callback' : 'alias'; + Crush::addSelectorAlias($name, $handler, $type); } }, 'disable' => function () { - foreach (aria() as $name => $value) { + foreach (aria() as $name => $handler) { Crush::removeSelectorAlias($name); } }, diff --git a/plugins/forms.php b/plugins/forms.php index 4a2d57c..a8bc68e 100644 --- a/plugins/forms.php +++ b/plugins/forms.php @@ -8,12 +8,13 @@ Plugin::register('forms', array( 'enable' => function () { - foreach (forms() as $name => $value) { - Crush::addSelectorAlias($name, $value); + foreach (forms() as $name => $handler) { + $type = is_callable($handler) ? 'callback' : 'alias'; + Crush::addSelectorAlias($name, $handler, $type); } }, 'disable' => function () { - foreach (forms() as $name => $value) { + foreach (forms() as $name => $handler) { Crush::removeSelectorAlias($name); } }, @@ -31,7 +32,6 @@ function forms() { $result = $types ? 'input:any(' . implode(',', $types) . ')' : 'input[type="text"]'; return Crush::$process->tokens->capture($result, 's'); }, - 'checkbox' => 'input[type="checkbox"]', 'radio' => 'input[type="radio"]', 'file' => 'input[type="file"]', From 574fb9d985f54c47ee950f18907aa304506f34cf Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 10 Feb 2014 09:47:48 +0000 Subject: [PATCH 244/421] Some doc updates. --- docs/core/selector-aliases.md | 20 +++++++++++++++----- docs/core/variables.md | 10 +++++----- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/docs/core/selector-aliases.md b/docs/core/selector-aliases.md index 4d0f0b5..f373fcd 100644 --- a/docs/core/selector-aliases.md +++ b/docs/core/selector-aliases.md @@ -13,9 +13,10 @@ They're defined with the `@selector-alias` directive, and can be used anywhere /* Defining selector aliases */ @selector-alias heading :any(h1, h2, h3, h4, h5, h6); @selector-alias radio input[type="radio"]; - +@selector-alias hocus :any(:hover, :focus); /* Selector alias with arguments */ @selector-alias class-prefix :any([class^="#(0)"], [class*=" #(0)"]); +@selector-alias col :class-prefix(-col); .sidebar :heading { color: honeydew; @@ -25,8 +26,12 @@ They're defined with the `@selector-alias` directive, and can be used anywhere margin-right: 4px; } -:class-prefix(button) { - border: 1px solid rgba(0,0,0,.5); +:col { + float: left; +} + +p a:hocus { + text-decoration: none; } ``` @@ -41,8 +46,13 @@ input[type="radio"] { margin-right: 4px; } -[class^="button"], -[class*=" button"] { +[class^="col-"], +[class*=" col-"] { border: 1px solid rgba(0,0,0,.5); } + +p a:hover, +p a:focus { + text-decoration: none; +} ``` diff --git a/docs/core/variables.md b/docs/core/variables.md index f521078..ee678ec 100644 --- a/docs/core/variables.md +++ b/docs/core/variables.md @@ -13,15 +13,15 @@ Variables can be injected at runtime with the [vars option](#api--options). /* Defining variables */ @define { helvetica: "Helvetica Neue", "Helvetica", "Arial", sans-serif; - theme-bg-color: #88CDEA; - theme-fg-color: #F4F2E2; - breakpoint-1: 960px; + dark: #333; + light: #F4F2E2; + smaller-screen: only screen and (max-width: 800px); } /* Applying variables */ -@media only screen and (max-width: $(breakpoint-1)) { +@media $(smaller-screen) { ul, p { - color: $(theme-fg-color); + color: $(dark); /* Specifying a fallback value */ background-color: $(accent-color #ff0); } From d1f74081c49714a687413231cfcdb7c1efe896e5 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 10 Feb 2014 21:54:54 +0000 Subject: [PATCH 245/421] Removing unused variables. --- lib/CssCrush/Color.php | 3 +-- lib/CssCrush/Crush.php | 3 --- lib/CssCrush/Declaration.php | 2 +- lib/CssCrush/IO.php | 4 ---- lib/CssCrush/Importer.php | 1 - lib/CssCrush/PostAliasFix.php | 2 +- lib/CssCrush/Process.php | 8 ++------ lib/CssCrush/Rule.php | 4 ++-- lib/CssCrush/SelectorList.php | 4 ++-- lib/CssCrush/Template.php | 2 +- plugins/canvas.php | 1 - plugins/legacy-flexbox.php | 3 +-- tests/bootstrap.php | 2 +- tests/unit/CssCrush/DeclarationTest.php | 2 +- tests/unit/CssCrush/PluginTest.php | 2 +- tests/unit/CssCrush/StreamTest.php | 1 - tests/unit/CssCrush/TokensTest.php | 2 +- tests/unit/cli/cliTest.php | 2 +- 18 files changed, 16 insertions(+), 32 deletions(-) diff --git a/lib/CssCrush/Color.php b/lib/CssCrush/Color.php index a004782..8cdc880 100644 --- a/lib/CssCrush/Color.php +++ b/lib/CssCrush/Color.php @@ -29,7 +29,6 @@ public static function getMinifyableKeywords() // If color name is longer than 4 and less than 8 test to see if its hex // representation could be shortened. - $table = array(); $keywords = self::getKeywords(); foreach ($keywords as $name => $rgba) { @@ -256,7 +255,7 @@ public static function cssHslToRgb(array $hsla) $h = array_shift($hsla); $h = $h % 360; if ($h < 0) { - $h = 360 + $hue; + $h = 360 + $h; } $h = $h / 360; diff --git a/lib/CssCrush/Crush.php b/lib/CssCrush/Crush.php index c1ad557..1911039 100644 --- a/lib/CssCrush/Crush.php +++ b/lib/CssCrush/Crush.php @@ -193,10 +193,8 @@ public static function file($file, $options = array()) { self::$process = new Process($options, array('io_context' => 'file')); - $config = self::$config; $process = self::$process; $options = $process->options; - $doc_root = $process->docRoot; $process->input->raw = $file; @@ -318,7 +316,6 @@ public static function string($string, $options = array()) self::$process = new Process($options, array('io_context' => 'filter')); - $config = self::$config; $process = self::$process; $options = $process->options; diff --git a/lib/CssCrush/Declaration.php b/lib/CssCrush/Declaration.php index 332954d..cb4674b 100644 --- a/lib/CssCrush/Declaration.php +++ b/lib/CssCrush/Declaration.php @@ -131,7 +131,7 @@ public function indexFunctions() // Create an index of all regular functions in the value. $functions = array(); if (preg_match_all(Regex::$patt->functionTest, $this->value, $m)) { - foreach ($m['func_name'] as $index => $fn_name) { + foreach ($m['func_name'] as $fn_name) { $functions[strtolower($fn_name)] = true; } } diff --git a/lib/CssCrush/IO.php b/lib/CssCrush/IO.php index 8c6a0dc..baefbdc 100644 --- a/lib/CssCrush/IO.php +++ b/lib/CssCrush/IO.php @@ -92,7 +92,6 @@ public static function getOutputUrl() public static function validateCache() { $process = Crush::$process; - $config = Crush::$config; $options = $process->options; $input = $process->input; $output = $process->output; @@ -164,8 +163,6 @@ public static function validateCache() public static function getCacheData() { - $config = Crush::$config; - $logger = $config->logger; $process = Crush::$process; if ( @@ -207,7 +204,6 @@ public static function getCacheData() public static function saveCacheData() { $process = Crush::$process; - $logger = Crush::$config->logger; debug('Saving config.'); diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index 1ecbcd2..d4802c1 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -10,7 +10,6 @@ class Importer { public static function hostfile() { - $config = Crush::$config; $process = Crush::$process; $options = $process->options; $regex = Regex::$patt; diff --git a/lib/CssCrush/PostAliasFix.php b/lib/CssCrush/PostAliasFix.php index 5973bd5..11de0ed 100644 --- a/lib/CssCrush/PostAliasFix.php +++ b/lib/CssCrush/PostAliasFix.php @@ -21,7 +21,7 @@ public static function add($alias_type, $key, $callback) public static function remove($alias_type, $key) { - if ($type === 'function') { + if ($alias_type === 'function') { unset(self::$functions[$key]); } } diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index ce436a9..48eef36 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -370,7 +370,7 @@ protected function captureVars() } // Place variables referenced inside variables. - foreach ($this->vars as $name => &$value) { + foreach ($this->vars as &$value) { $value = preg_replace_callback(Regex::$patt->varFunction, 'CssCrush\Process::cb_placeVars', $value); } } @@ -755,7 +755,6 @@ protected function collate() { $options = $this->options; $minify = $options->minify; - $regex = Regex::$patt; $EOL = $this->newline; // Formatting replacements. @@ -948,7 +947,7 @@ public function generateSourceMap() $previous_src_line = 0; $previous_src_col = 0; - foreach ($lines as $line_number => &$line_text) { + foreach ($lines as &$line_text) { $line_segments = array(); @@ -998,9 +997,6 @@ public function generateTracingStub($m) if (! $this->minifyOutput) { $debug_info .= $this->newline; } - if ($this->generateMap) { - $debug_info .= $token; - } return $debug_info; } diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php index 05e3ae0..b8a1fef 100644 --- a/lib/CssCrush/Rule.php +++ b/lib/CssCrush/Rule.php @@ -88,7 +88,7 @@ public function resolveExtendables() // Filter the extendArgs list to usable references. $filtered = array(); - foreach ($this->extendArgs as $key => $extend_arg) { + foreach ($this->extendArgs as $extend_arg) { $name = $extend_arg->name; @@ -135,7 +135,7 @@ public function applyExtendables() if ($extend_arg->pseudo) { $extend_selectors = array(); - foreach ($this->selectors->store as $readable => $selector) { + foreach ($this->selectors->store as $selector) { $new_selector = clone $selector; $new_readable = $new_selector->appendPseudo($extend_arg->pseudo); $extend_selectors[$new_readable] = $new_selector; diff --git a/lib/CssCrush/SelectorList.php b/lib/CssCrush/SelectorList.php index eef270e..621e031 100644 --- a/lib/CssCrush/SelectorList.php +++ b/lib/CssCrush/SelectorList.php @@ -85,13 +85,13 @@ public function expand() return $flattened_stack; } - return array($input => true); + return array($selector_string => true); }; } $expanded_set = array(); - foreach ($this->store as $readable_value => $original_selector) { + foreach ($this->store as $original_selector) { if (stripos($original_selector->value, ':any(') !== false) { foreach ($expandSelector($original_selector->value) as $selector_string => $bool) { $new = new Selector($selector_string); diff --git a/lib/CssCrush/Template.php b/lib/CssCrush/Template.php index 124c3c8..64e10bf 100644 --- a/lib/CssCrush/Template.php +++ b/lib/CssCrush/Template.php @@ -19,7 +19,7 @@ class Template // The string passed in with arg calls replaced by tokens. public $string; - public function __construct($str, $options = array()) + public function __construct($str) { static $arg_patt; if (! $arg_patt) { diff --git a/plugins/canvas.php b/plugins/canvas.php index f93a89a..79f63e3 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -39,7 +39,6 @@ function ($m) { function canvas_generator($input, $context) { $process = Crush::$process; - $logger = Crush::$config->logger; // Check GD requirements are met. static $requirements; diff --git a/plugins/legacy-flexbox.php b/plugins/legacy-flexbox.php index c29fad6..644c78e 100644 --- a/plugins/legacy-flexbox.php +++ b/plugins/legacy-flexbox.php @@ -127,10 +127,9 @@ function legacy_flexbox(Rule $rule) { $args = explode(' ', $value); $direction = isset($args[0]) ? $args[0] : 'initial'; - $wrap = isset($args[1]) ? $args[1] : 'initial'; $rule_updated = flex_direction($direction, $stack); - // $rule_updated = csscrush__flex_wrap($wrap, $stack); + break; } diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 88427b8..05e6741 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -34,7 +34,7 @@ function temp_file($contents = '') function stdout($message, $prepend_newline = false) { if (! is_string($message)) { - $buffer = ob_start(); + ob_start(); print_r($message); $message = ob_get_clean(); } diff --git a/tests/unit/CssCrush/DeclarationTest.php b/tests/unit/CssCrush/DeclarationTest.php index 4b5b67b..4a1e7b8 100644 --- a/tests/unit/CssCrush/DeclarationTest.php +++ b/tests/unit/CssCrush/DeclarationTest.php @@ -37,7 +37,7 @@ public function test__toString() public function testProcess() { - foreach ($this->rule->declarations as $index => $declaration) { + foreach ($this->rule->declarations as $declaration) { $declaration->process($this->rule); $this->assertEquals('20px', $declaration->value); } diff --git a/tests/unit/CssCrush/PluginTest.php b/tests/unit/CssCrush/PluginTest.php index a8906ae..86357d8 100644 --- a/tests/unit/CssCrush/PluginTest.php +++ b/tests/unit/CssCrush/PluginTest.php @@ -51,7 +51,7 @@ public function testParseDoc() public function testLoad() { - $result = Plugin::load('dummy'); + Plugin::load('dummy'); $this->assertArrayHasKey('dummy', Plugin::$plugins); diff --git a/tests/unit/CssCrush/StreamTest.php b/tests/unit/CssCrush/StreamTest.php index a2c14ad..efc643c 100644 --- a/tests/unit/CssCrush/StreamTest.php +++ b/tests/unit/CssCrush/StreamTest.php @@ -62,7 +62,6 @@ public function testPrepend() public function testSubstr() { $stream = new Stream($this->sample); - $prepend_text = 'foo'; $this->assertEquals(substr($this->sample, 1), (string) $stream->substr(1)); } } diff --git a/tests/unit/CssCrush/TokensTest.php b/tests/unit/CssCrush/TokensTest.php index 2831d07..f544ca0 100644 --- a/tests/unit/CssCrush/TokensTest.php +++ b/tests/unit/CssCrush/TokensTest.php @@ -36,7 +36,7 @@ public function testCreateLabel() public function testAdd() { - $label = $this->tokens->add('/*monkey*/', 'c'); + $this->tokens->add('/*monkey*/', 'c'); $this->assertContains('/*monkey*/', array_values($this->tokens->store->c)); } diff --git a/tests/unit/cli/cliTest.php b/tests/unit/cli/cliTest.php index de579f6..d87aa2a 100644 --- a/tests/unit/cli/cliTest.php +++ b/tests/unit/cli/cliTest.php @@ -37,7 +37,7 @@ public function testIO() $out_path = temp_file(); file_put_contents($in_path, $this->sample); - exec("php \"$this->path\" -i '$in_path' -o '$out_path' --enable property-sorter", $lines); + exec("php \"$this->path\" -i '$in_path' -o '$out_path' --enable property-sorter"); $expected = 'p{position:absolute;opacity:1;color:red}'; $this->assertEquals($expected, file_get_contents($out_path)); From c668ec252f367871ad2be38cf3d9097175f9a27d Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 11 Feb 2014 19:48:37 +0000 Subject: [PATCH 246/421] Adding unit argument to the math function. The bare paren math shorthand is soon to be deprecated due to their use in emerging CSS specs such as CSS grids. --- lib/CssCrush/Functions.php | 12 +++++++----- lib/CssCrush/Regex.php | 1 - 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/CssCrush/Functions.php b/lib/CssCrush/Functions.php index c3cdcf2..2426630 100644 --- a/lib/CssCrush/Functions.php +++ b/lib/CssCrush/Functions.php @@ -148,18 +148,20 @@ public static function parseArgsSimple($input) function fn__math($input) { + list($expression, $unit) = array_pad(Functions::parseArgs($input), 2, ''); + // Swap in math constants. - $input = preg_replace( + $expression = preg_replace( array('~\bpi\b~i'), array(M_PI), - $input); + $expression); // Strip blacklisted characters. - $input = preg_replace(Regex::$patt->mathBlacklist, '', $input); + $expression = preg_replace('~[^\.0-9\*\/\+\-\(\)]~S', '', $expression); - $result = @eval("return $input;"); + $result = @eval("return $expression;"); - return $result === false ? 0 : round($result, 5); + return ($result === false ? 0 : round($result, 5)) . $unit; } function fn__percent($input) { diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php index c4e347f..81a6544 100644 --- a/lib/CssCrush/Regex.php +++ b/lib/CssCrush/Regex.php @@ -110,7 +110,6 @@ public static function init() $patt->vendorPrefix = '~^-([a-z]+)-([a-z-]+)~iS'; $patt->ruleDirective = '~^(?:(@include)|(@extends?)|(@name))[\s]+~iS'; $patt->argListSplit = '~\s*[,\s]\s*~S'; - $patt->mathBlacklist = '~[^\.0-9\*\/\+\-\(\)]~S'; $patt->cruftyHex = Regex::make('~\#({{hex}})\1({{hex}})\2({{hex}})\3~S'); } From bf41662df63e330c30fd6d359396d5ec51e0b70f Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 11 Feb 2014 22:20:18 +0000 Subject: [PATCH 247/421] Added selector `splat` aliases, which expand based on arguments. Similar to what happens in the forms and aria plugins, but easier to author. Selector alias definitions are now automatically wrapped in `:any()` if they are comma separated. --- lib/CssCrush/SelectorAlias.php | 37 ++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/lib/CssCrush/SelectorAlias.php b/lib/CssCrush/SelectorAlias.php index 24dd78b..8265a2a 100644 --- a/lib/CssCrush/SelectorAlias.php +++ b/lib/CssCrush/SelectorAlias.php @@ -14,14 +14,11 @@ class SelectorAlias public function __construct($type, $handler) { $this->type = $type; + $this->handler = $handler; switch ($this->type) { case 'alias': - $this->handler = new Template($handler); - break; - case 'callback': - case 'splat': - $this->handler = $handler; + $this->handler = new Template(SelectorAlias::wrap($handler)); break; } } @@ -29,14 +26,38 @@ public function __construct($type, $handler) public function __invoke($args) { $handler = $this->handler; + $tokens = Crush::$process->tokens; + + $splat_arg_patt = Regex::make('~#\((?{{ ident }})?\)~'); switch ($this->type) { + case 'alias': + return $handler($args); case 'callback': $template = new Template($handler($args)); return $template($args); - case 'alias': - default: - return $handler($args); + case 'splat': + $handler = $tokens->restore($handler, 's'); + if ($args) { + $list = array(); + foreach ($args as $arg) { + $list[] = SelectorAlias::wrap( + $tokens->capture(preg_replace($splat_arg_patt, $arg, $handler), 's') + ); + } + $handler = implode(',', $list); + } + else { + $handler = $tokens->capture(preg_replace_callback($splat_arg_patt, function ($m) { + return $m['fallback']; + }, $handler), 's'); + } + return SelectorAlias::wrap($handler); } } + + public static function wrap($str) + { + return strpos($str, ',') !== false ? ":any($str)" : $str; + } } From eec690c810d47a9ba0906331168673b8212644c1 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 12 Feb 2014 11:06:17 +0000 Subject: [PATCH 248/421] Updates following on from selector alias changes. --- lib/CssCrush/Crush.php | 5 ++++- lib/CssCrush/Process.php | 6 +++--- lib/CssCrush/SelectorAlias.php | 4 ++-- plugins/forms.php | 18 ++++++++---------- plugins/hocus-pocus.php | 4 ++-- 5 files changed, 19 insertions(+), 18 deletions(-) diff --git a/lib/CssCrush/Crush.php b/lib/CssCrush/Crush.php index 1911039..1c5e9be 100644 --- a/lib/CssCrush/Crush.php +++ b/lib/CssCrush/Crush.php @@ -362,7 +362,10 @@ public static function stat() public static function addSelectorAlias($name, $handler, $type = 'alias') { - Crush::$config->selectorAliases[$name] = new SelectorAlias($type, $handler); + if ($type != 'callback') { + $handler = Crush::$process->tokens->capture($handler, 's'); + } + Crush::$config->selectorAliases[$name] = new SelectorAlias($handler, $type); } public static function removeSelectorAlias($name) diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 48eef36..894ed3b 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -185,12 +185,12 @@ protected function getBoilerplate() protected function resolveSelectorAliases() { $this->stream->pregReplaceCallback( - Regex::make('~@selector-(?alias|splat) +\:?(?{{ident}}) +(?[^;]+) *;~iS'), + Regex::make('~@selector-(?alias|splat) +\:?(?{{ident}}) +(?[^;]+) *;~iS'), function ($m) { $name = strtolower($m['name']); $type = strtolower($m['type']); - $args = Util::stripCommentTokens($m['args']); - Crush::$process->selectorAliases[$name] = new SelectorAlias($type, $args); + $handler = Util::stripCommentTokens($m['handler']); + Crush::$process->selectorAliases[$name] = new SelectorAlias($handler, $type); }); // Merge with global selector aliases. diff --git a/lib/CssCrush/SelectorAlias.php b/lib/CssCrush/SelectorAlias.php index 8265a2a..7c95110 100644 --- a/lib/CssCrush/SelectorAlias.php +++ b/lib/CssCrush/SelectorAlias.php @@ -11,10 +11,10 @@ class SelectorAlias public $type; public $handler; - public function __construct($type, $handler) + public function __construct($handler, $type = 'alias') { - $this->type = $type; $this->handler = $handler; + $this->type = $type; switch ($this->type) { case 'alias': diff --git a/plugins/forms.php b/plugins/forms.php index a8bc68e..343bd17 100644 --- a/plugins/forms.php +++ b/plugins/forms.php @@ -9,7 +9,10 @@ Plugin::register('forms', array( 'enable' => function () { foreach (forms() as $name => $handler) { - $type = is_callable($handler) ? 'callback' : 'alias'; + if (is_array($handler)) { + $type = $handler['type']; + $handler = $handler['handler']; + } Crush::addSelectorAlias($name, $handler, $type); } }, @@ -23,15 +26,10 @@ function forms() { return array( - 'input' => function ($args) { - $types = array(); - foreach ($args as $type) { - $types[] = "[type=$type]"; - } - - $result = $types ? 'input:any(' . implode(',', $types) . ')' : 'input[type="text"]'; - return Crush::$process->tokens->capture($result, 's'); - }, + 'input' => array( + 'type' => 'splat', + 'handler' => 'input[type=#(text)]', + ), 'checkbox' => 'input[type="checkbox"]', 'radio' => 'input[type="radio"]', 'file' => 'input[type="file"]', diff --git a/plugins/hocus-pocus.php b/plugins/hocus-pocus.php index 13eb552..886f1c3 100644 --- a/plugins/hocus-pocus.php +++ b/plugins/hocus-pocus.php @@ -8,8 +8,8 @@ Plugin::register('hocus-pocus', array( 'enable' => function () { - Crush::addSelectorAlias('hocus', ':any(:hover,:focus)'); - Crush::addSelectorAlias('pocus', ':any(:hover,:focus,:active)'); + Crush::addSelectorAlias('hocus', ':hover,:focus'); + Crush::addSelectorAlias('pocus', ':hover,:focus,:active'); }, 'disable' => function () { Crush::removeSelectorAlias('hocus'); From 2b9ab06a654d07cb443f5195b1d6725ae90ce397 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 13 Feb 2014 17:16:20 +0000 Subject: [PATCH 249/421] Added a directive capture method to simplify common uses. --- lib/CssCrush/Process.php | 35 +++++------------------------------ lib/CssCrush/Stream.php | 33 +++++++++++++++++++++++++++++++++ plugins/canvas.php | 8 ++------ plugins/color.php | 19 +------------------ plugins/svg.php | 9 ++------- 5 files changed, 43 insertions(+), 61 deletions(-) diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 894ed3b..b6f1186 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -347,19 +347,10 @@ protected function filterPlugins() protected function captureVars() { - $patt = Regex::make('~@define(?:\s*{{ block }}|\s+(?{{ ident }})\s+(?[^;]+)\s*;)~iS'); - - $this->stream->pregReplaceCallback($patt, function ($m) { - if (isset($m['name'])) { - Crush::$process->vars[$m['name']] = $m['value']; - } - else { - Crush::$process->vars = DeclarationList::parse($m['block_content'], array( - 'keyed' => true, - 'ignore_directives' => true, - )) + Crush::$process->vars; - } - }); + Crush::$process->vars = Crush::$process->stream->captureDirectives('@define', array( + 'singles' => true, + 'lowercase_keys' => false, + )) + Crush::$process->vars; // In-file variables override global variables. $this->vars += Crush::$config->vars; @@ -439,23 +430,7 @@ static protected function cb_placeVars($m) protected function resolveSettings() { - $patt = Regex::make('~@settings(?:\s*{{ block }}|\s+(?{{ ident }})\s+(?[^;]+)\s*;)~iS'); - $captured_settings = array(); - - $this->stream->pregReplaceCallback($patt, function ($m) use (&$captured_settings) { - if (isset($m['name'])) { - $captured_settings[strtolower($m['name'])] = $m['value']; - } - else { - $captured_settings = DeclarationList::parse($m['block_content'], array( - 'keyed' => true, - 'ignore_directives' => true, - 'lowercase_keys' => true, - )) + $captured_settings; - } - - return ''; - }); + $captured_settings = $this->stream->captureDirectives('@settings', array('singles' => true)); // Like variables, settings passed via options override settings defined in CSS. $this->settings = new Settings($this->options->settings + $captured_settings); diff --git a/lib/CssCrush/Stream.php b/lib/CssCrush/Stream.php index 3899f86..e1ccfa8 100644 --- a/lib/CssCrush/Stream.php +++ b/lib/CssCrush/Stream.php @@ -117,4 +117,37 @@ public function lTrim() $this->raw = ltrim($this->raw); return $this; } + + public function captureDirectives($directive, $parse_options = array()) + { + $directive = ltrim($directive, '@'); + $parse_options += array( + 'keyed' => true, + 'lowercase_keys' => true, + 'ignore_directives' => true, + 'singles' => false, + 'flatten' => false, + ); + + if ($parse_options['singles']) { + $patt = Regex::make('~@' . $directive . '(?:\s*{{ block }}|\s+(?{{ ident }})\s+(?[^;]+)\s*;)~iS'); + } + else { + $patt = Regex::make('~@' . $directive . '\s*{{ block }}~iS'); + } + + $captured_directives = array(); + $this->pregReplaceCallback($patt, function ($m) use (&$captured_directives, $parse_options) { + if (isset($m['name'])) { + $name = $parse_options['lowercase_keys'] ? strtolower($m['name']) : $m['name']; + $captured_directives[$name] = $m['value']; + } + else { + $captured_directives = DeclarationList::parse($m['block_content'], $parse_options) + $captured_directives; + } + return ''; + }); + + return $captured_directives; + } } diff --git a/plugins/canvas.php b/plugins/canvas.php index 79f63e3..66cfbb5 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -25,13 +25,9 @@ function canvas_capture($process) { $process->stream->pregReplaceCallback( - Regex::make('~@canvas \s+ (?{{ident}}) \s* {{block}}~ixS'), + Regex::make('~@canvas\s+(?{{ ident }})\s*{{ block }}~iS'), function ($m) { - $name = strtolower($m['name']); - $block = $m['block_content']; - if (! empty($block)) { - Crush::$process->misc->canvas_defs[$name] = new Template($block); - } + Crush::$process->misc->canvas_defs[strtolower($m['name'])] = new Template($m['block_content']); return ''; }); } diff --git a/plugins/color.php b/plugins/color.php index 8e82738..428b499 100644 --- a/plugins/color.php +++ b/plugins/color.php @@ -29,24 +29,7 @@ function color(&$declaration) { function color_capture($process) { - $patt = Regex::make('~@color(?:\s*{{ block }}|\s+(?{{ ident }})\s+(?[^;]+)\s*;)~iS'); - $captured_keywords = array(); - - $process->stream->pregReplaceCallback($patt, function ($m) use (&$captured_keywords) { - if (isset($m['name'])) { - $captured_keywords[strtolower($m['name'])] = $m['value']; - } - else { - $captured_keywords = DeclarationList::parse($m['block_content'], array( - 'keyed' => true, - 'lowercase_keys' => true, - 'flatten' => true, - )) + $captured_keywords; - } - - return ''; - }); - + $captured_keywords = $process->stream->captureDirectives('@color', array('singles' => true)); if ($captured_keywords) { diff --git a/plugins/svg.php b/plugins/svg.php index 7cb9758..73eb363 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -36,15 +36,10 @@ function fn__svg_data($input) { function svg_capture($process) { - // Extract svg definitions. $process->stream->pregReplaceCallback( - Regex::make('~@svg \s+ (?{{ident}}) \s* {{block}}~ixS'), + Regex::make('~@svg\s+(?{{ ident }})\s*{{ block }}~iS'), function ($m) { - $name = strtolower($m['name']); - $block = $m['block_content']; - if (! empty($block)) { - Crush::$process->misc->svg_defs[$name] = new Template($block); - } + Crush::$process->misc->svg_defs[strtolower($m['name'])] = new Template($m['block_content']); return ''; }); } From 5be40a0596a728d266185054f82dbb0c5197679f Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 13 Feb 2014 17:39:25 +0000 Subject: [PATCH 250/421] Moved plugin adding of selector aliases to the process layer. Removed unnecessary plugin disable callbacks. --- lib/CssCrush/Crush.php | 18 ------------------ lib/CssCrush/Plugin.php | 8 ++++---- lib/CssCrush/Process.php | 11 ++++++++--- plugins/aria.php | 13 ++++--------- plugins/canvas.php | 7 +++---- plugins/color.php | 12 ++++-------- plugins/ease.php | 9 +++------ plugins/forms.php | 11 +++-------- plugins/hocus-pocus.php | 12 ++++-------- plugins/hsl2hex.php | 9 +++------ plugins/ie-inline-block.php | 9 +++------ plugins/ie-opacity.php | 9 +++------ plugins/initial.php | 9 +++------ plugins/legacy-flexbox.php | 9 +++------ plugins/loop.php | 9 +++------ plugins/property-sorter.php | 9 +++------ plugins/rem.php | 9 +++------ plugins/svg.php | 5 ++--- plugins/text-align.php | 9 +++------ 19 files changed, 62 insertions(+), 125 deletions(-) diff --git a/lib/CssCrush/Crush.php b/lib/CssCrush/Crush.php index 1c5e9be..54a66f0 100644 --- a/lib/CssCrush/Crush.php +++ b/lib/CssCrush/Crush.php @@ -48,7 +48,6 @@ public static function init() 'declarations' => array(), 'at-rules' => array(), ); - self::$config->selectorAliases = array(); self::$config->plugins = array(); self::$config->options = new Options(); @@ -357,23 +356,6 @@ public static function stat() } - ############################# - # Global selector aliases. - - public static function addSelectorAlias($name, $handler, $type = 'alias') - { - if ($type != 'callback') { - $handler = Crush::$process->tokens->capture($handler, 's'); - } - Crush::$config->selectorAliases[$name] = new SelectorAlias($handler, $type); - } - - public static function removeSelectorAlias($name) - { - unset(Crush::$config->selectorAliases[$name]); - } - - ############################# # Logging and stats. diff --git a/lib/CssCrush/Plugin.php b/lib/CssCrush/Plugin.php index 865fcf7..09e7944 100644 --- a/lib/CssCrush/Plugin.php +++ b/lib/CssCrush/Plugin.php @@ -72,7 +72,7 @@ public static function load($plugin_name) } elseif (isset(self::$plugins[$plugin_name]['load'])) { $plugin_load = self::$plugins[$plugin_name]['load']; - $plugin_load(); + $plugin_load(Crush::$process); } } @@ -84,7 +84,7 @@ public static function enable($plugin_name) $plugin = self::load($plugin_name); if (is_callable($plugin['enable'])) { - $plugin['enable'](); + $plugin['enable'](Crush::$process); } return true; @@ -94,8 +94,8 @@ public static function disable($plugin_name) { $plugin = isset(self::$plugins[$plugin_name]) ? self::$plugins[$plugin_name] : null; - if (is_callable($plugin['disable'])) { - $plugin['disable'](); + if (isset($plugin['disable']) && is_callable($plugin['disable'])) { + $plugin['disable'](Crush::$process); } } } diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index b6f1186..3f6bff4 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -193,9 +193,6 @@ function ($m) { Crush::$process->selectorAliases[$name] = new SelectorAlias($handler, $type); }); - // Merge with global selector aliases. - $this->selectorAliases += Crush::$config->selectorAliases; - // Create the selector aliases pattern and store it. if ($this->selectorAliases) { $names = implode('|', array_keys($this->selectorAliases)); @@ -204,6 +201,14 @@ function ($m) { } } + public function addSelectorAlias($name, $handler, $type = 'alias') + { + if ($type != 'callback') { + $handler = $this->tokens->capture($handler, 's'); + } + $this->selectorAliases[$name] = new SelectorAlias($handler, $type); + } + ############################# # Aliases. diff --git a/plugins/aria.php b/plugins/aria.php index e14dd3c..b467217 100644 --- a/plugins/aria.php +++ b/plugins/aria.php @@ -1,23 +1,18 @@ function () { + 'enable' => function ($process) { foreach (aria() as $name => $handler) { $type = is_callable($handler) ? 'callback' : 'alias'; - Crush::addSelectorAlias($name, $handler, $type); + $process->addSelectorAlias($name, $handler, $type); } - }, - 'disable' => function () { - foreach (aria() as $name => $handler) { - Crush::removeSelectorAlias($name); - } - }, + } )); diff --git a/plugins/canvas.php b/plugins/canvas.php index 66cfbb5..4faaa32 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -9,13 +9,12 @@ use stdClass; Plugin::register('canvas', array( - 'enable' => function () { - Crush::$process->hooks->add('capture_phase2', 'CssCrush\canvas_capture'); + 'enable' => function ($process) { + $process->hooks->add('capture_phase2', 'CssCrush\canvas_capture'); Functions::register('canvas', 'CssCrush\canvas_generator'); Functions::register('canvas-data', 'CssCrush\canvas_generator'); }, - 'disable' => function () { - Crush::$process->hooks->remove('capture_phase2', 'CssCrush\canvas_capture'); + 'disable' => function ($process) { Functions::deRegister('canvas'); Functions::deRegister('canvas-data'); }, diff --git a/plugins/color.php b/plugins/color.php index 428b499..dcffcf3 100644 --- a/plugins/color.php +++ b/plugins/color.php @@ -7,15 +7,11 @@ namespace CssCrush; Plugin::register('color', array( - 'enable' => function () { + 'enable' => function ($process) { $GLOBALS['CSSCRUSH_COLOR_PATT'] = null; - Crush::$process->hooks->add('capture_phase1', 'CssCrush\color_capture'); - Crush::$process->hooks->add('declaration_preprocess', 'CssCrush\color'); - }, - 'disable' => function () { - Crush::$process->hooks->remove('capture_phase1', 'CssCrush\color_capture'); - Crush::$process->hooks->remove('declaration_preprocess', 'CssCrush\color'); - }, + $process->hooks->add('capture_phase1', 'CssCrush\color_capture'); + $process->hooks->add('declaration_preprocess', 'CssCrush\color'); + } )); diff --git a/plugins/ease.php b/plugins/ease.php index e953ad4..f0f328d 100644 --- a/plugins/ease.php +++ b/plugins/ease.php @@ -7,12 +7,9 @@ namespace CssCrush; Plugin::register('ease', array( - 'enable' => function () { - Crush::$process->hooks->add('rule_prealias', 'CssCrush\ease'); - }, - 'disable' => function () { - Crush::$process->hooks->remove('rule_prealias', 'CssCrush\ease'); - }, + 'enable' => function ($process) { + $process->hooks->add('rule_prealias', 'CssCrush\ease'); + } )); diff --git a/plugins/forms.php b/plugins/forms.php index 343bd17..84e5f80 100644 --- a/plugins/forms.php +++ b/plugins/forms.php @@ -7,20 +7,15 @@ namespace CssCrush; Plugin::register('forms', array( - 'enable' => function () { + 'enable' => function ($process) { foreach (forms() as $name => $handler) { if (is_array($handler)) { $type = $handler['type']; $handler = $handler['handler']; } - Crush::addSelectorAlias($name, $handler, $type); + $process->addSelectorAlias($name, $handler, $type); } - }, - 'disable' => function () { - foreach (forms() as $name => $handler) { - Crush::removeSelectorAlias($name); - } - }, + } )); diff --git a/plugins/hocus-pocus.php b/plugins/hocus-pocus.php index 886f1c3..48409c9 100644 --- a/plugins/hocus-pocus.php +++ b/plugins/hocus-pocus.php @@ -7,12 +7,8 @@ namespace CssCrush; Plugin::register('hocus-pocus', array( - 'enable' => function () { - Crush::addSelectorAlias('hocus', ':hover,:focus'); - Crush::addSelectorAlias('pocus', ':hover,:focus,:active'); - }, - 'disable' => function () { - Crush::removeSelectorAlias('hocus'); - Crush::removeSelectorAlias('pocus'); - }, + 'enable' => function ($process) { + $process->addSelectorAlias('hocus', ':hover,:focus'); + $process->addSelectorAlias('pocus', ':hover,:focus,:active'); + } )); diff --git a/plugins/hsl2hex.php b/plugins/hsl2hex.php index dfbd118..af66cc9 100644 --- a/plugins/hsl2hex.php +++ b/plugins/hsl2hex.php @@ -7,12 +7,9 @@ namespace CssCrush; Plugin::register('hsl2hex', array( - 'enable' => function () { - Crush::$process->hooks->add('rule_postalias', 'CssCrush\hsl2hex'); - }, - 'disable' => function () { - Crush::$process->hooks->remove('rule_postalias', 'CssCrush\hsl2hex'); - }, + 'enable' => function ($process) { + $process->hooks->add('rule_postalias', 'CssCrush\hsl2hex'); + } )); diff --git a/plugins/ie-inline-block.php b/plugins/ie-inline-block.php index 2d8f75e..c59ddf4 100644 --- a/plugins/ie-inline-block.php +++ b/plugins/ie-inline-block.php @@ -7,12 +7,9 @@ namespace CssCrush; Plugin::register('ie-inline-block', array( - 'enable' => function () { - Crush::$process->hooks->add('rule_postalias', 'CssCrush\ie_inline_block'); - }, - 'disable' => function () { - Crush::$process->hooks->remove('rule_postalias', 'CssCrush\ie_inline_block'); - }, + 'enable' => function ($process) { + $process->hooks->add('rule_postalias', 'CssCrush\ie_inline_block'); + } )); diff --git a/plugins/ie-opacity.php b/plugins/ie-opacity.php index 75371e0..b3ea52f 100755 --- a/plugins/ie-opacity.php +++ b/plugins/ie-opacity.php @@ -7,12 +7,9 @@ namespace CssCrush; Plugin::register('ie-opacity', array( - 'enable' => function () { - Crush::$process->hooks->add('rule_postalias', 'CssCrush\ie_opacity'); - }, - 'disable' => function () { - Crush::$process->hooks->remove('rule_postalias', 'CssCrush\ie_opacity'); - }, + 'enable' => function ($process) { + $process->hooks->add('rule_postalias', 'CssCrush\ie_opacity'); + } )); diff --git a/plugins/initial.php b/plugins/initial.php index a021155..c9454f3 100644 --- a/plugins/initial.php +++ b/plugins/initial.php @@ -7,12 +7,9 @@ namespace CssCrush; Plugin::register('initial', array( - 'enable' => function () { - Crush::$process->hooks->add('rule_prealias', 'CssCrush\initial'); - }, - 'disable' => function () { - Crush::$process->hooks->remove('rule_prealias', 'CssCrush\initial'); - }, + 'enable' => function ($process) { + $process->hooks->add('rule_prealias', 'CssCrush\initial'); + } )); function initial(Rule $rule) { diff --git a/plugins/legacy-flexbox.php b/plugins/legacy-flexbox.php index 644c78e..860e931 100644 --- a/plugins/legacy-flexbox.php +++ b/plugins/legacy-flexbox.php @@ -7,12 +7,9 @@ namespace CssCrush; Plugin::register('legacy-flexbox', array( - 'enable' => function () { - Crush::$process->hooks->add('rule_prealias', 'CssCrush\legacy_flexbox'); - }, - 'disable' => function () { - Crush::$process->hooks->remove('rule_prealias', 'CssCrush\legacy_flexbox'); - }, + 'enable' => function ($process) { + $process->hooks->add('rule_prealias', 'CssCrush\legacy_flexbox'); + } )); diff --git a/plugins/loop.php b/plugins/loop.php index d9defce..fd0fcb8 100644 --- a/plugins/loop.php +++ b/plugins/loop.php @@ -7,12 +7,9 @@ namespace CssCrush; Plugin::register('loop', array( - 'enable' => function () { - Crush::$process->hooks->add('capture_phase1', 'CssCrush\loop'); - }, - 'disable' => function () { - Crush::$process->hooks->remove('capture_phase1', 'CssCrush\loop'); - }, + 'enable' => function ($process) { + $process->hooks->add('capture_phase1', 'CssCrush\loop'); + } )); diff --git a/plugins/property-sorter.php b/plugins/property-sorter.php index aa998aa..1554e97 100644 --- a/plugins/property-sorter.php +++ b/plugins/property-sorter.php @@ -7,12 +7,9 @@ namespace CssCrush { Plugin::register('property-sorter', array( - 'enable' => function () { - Crush::$process->hooks->add('rule_prealias', 'CssCrush\property_sorter'); - }, - 'disable' => function () { - Crush::$process->hooks->remove('rule_prealias', 'CssCrush\property_sorter'); - }, + 'enable' => function ($process) { + $process->hooks->add('rule_prealias', 'CssCrush\property_sorter'); + } )); diff --git a/plugins/rem.php b/plugins/rem.php index 1e4f923..8e29cf8 100644 --- a/plugins/rem.php +++ b/plugins/rem.php @@ -7,12 +7,9 @@ namespace CssCrush; Plugin::register('rem', array( - 'enable' => function () { - Crush::$process->hooks->add('rule_prealias', 'CssCrush\rem'); - }, - 'disable' => function () { - Crush::$process->hooks->remove('rule_prealias', 'CssCrush\rem'); - }, + 'enable' => function ($process) { + $process->hooks->add('rule_prealias', 'CssCrush\rem'); + } )); diff --git a/plugins/svg.php b/plugins/svg.php index 73eb363..a2d638e 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -10,14 +10,13 @@ 'load' => function () { $GLOBALS['CSSCRUSH_SVG_UID'] = 0; }, - 'enable' => function () { + 'enable' => function ($process) { $GLOBALS['CSSCRUSH_SVG_UID'] = 0; - Crush::$process->hooks->add('capture_phase2', 'CssCrush\svg_capture'); + $process->hooks->add('capture_phase2', 'CssCrush\svg_capture'); Functions::register('svg', 'CssCrush\fn__svg'); Functions::register('svg-data', 'CssCrush\fn__svg_data'); }, 'disable' => function () { - Crush::$process->hooks->remove('capture_phase2', 'CssCrush\svg_capture'); Functions::deRegister('svg'); Functions::deRegister('svg-data'); }, diff --git a/plugins/text-align.php b/plugins/text-align.php index 97c0a58..4d11474 100644 --- a/plugins/text-align.php +++ b/plugins/text-align.php @@ -7,12 +7,9 @@ namespace CssCrush; Plugin::register('text-align', array( - 'enable' => function () { - Crush::$process->hooks->add('rule_prealias', 'CssCrush\text_align'); - }, - 'disable' => function () { - Crush::$process->hooks->remove('rule_prealias', 'CssCrush\text_align'); - }, + 'enable' => function ($process) { + $process->hooks->add('rule_prealias', 'CssCrush\text_align'); + } )); From 211db61069e4056c339d2bf121479f012d7afd0c Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 14 Feb 2014 09:17:57 +0000 Subject: [PATCH 251/421] Changed function registry to be per-process. --- lib/CssCrush/Functions.php | 52 +++++++++++++++++++------------------- lib/CssCrush/Process.php | 3 ++- plugins/canvas.php | 12 +++------ plugins/noise.php | 13 +++------- plugins/px2em.php | 12 +++------ plugins/svg-gradients.php | 12 +++------ plugins/svg.php | 19 +++++--------- 7 files changed, 50 insertions(+), 73 deletions(-) diff --git a/lib/CssCrush/Functions.php b/lib/CssCrush/Functions.php index 2426630..2c60116 100644 --- a/lib/CssCrush/Functions.php +++ b/lib/CssCrush/Functions.php @@ -8,14 +8,7 @@ class Functions { - // Regex pattern for finding custom functions. - public static $functionPatt; - - public static $functions; - - static protected $customFunctions = array(); - - static protected $builtinFunctions = array( + protected static $builtins = array( // These functions must come first in this order. 'query' => 'CssCrush\fn__query', @@ -32,15 +25,33 @@ class Functions 'a-adjust' => 'CssCrush\fn__a_adjust', ); - public static function setMatchPatt() + public $pattern; + + public $register = array(); + + public function add($name, $callback) + { + $this->register[$name] = $callback; + } + + public function remove($name) + { + unset($this->register[$name]); + } + + public function setMatchPatt($use_builtin = true, $options = array()) { - self::$functions = self::$builtinFunctions + self::$customFunctions; - self::$functionPatt = Regex::makeFunctionPatt( - array_keys(self::$functions), array('bare_paren' => true)); + if ($use_builtin) { + $this->register = self::$builtins + $this->register; + $options += array('bare_paren' => true); + } + $this->pattern = Regex::makeFunctionPatt(array_keys($this->register), $options); } public static function executeOnString($str, $patt = null, $process_callback = null, \stdClass $context = null) { + $processFunctions = Crush::$process->functions; + // No bracketed expressions, early return. if (strpos($str, '(') === false) { @@ -49,7 +60,7 @@ public static function executeOnString($str, $patt = null, $process_callback = n // Set default pattern if not set. if (! isset($patt)) { - $patt = Functions::$functionPatt; + $patt = $processFunctions->pattern; } // No custom functions, early return. @@ -101,9 +112,9 @@ public static function executeOnString($str, $patt = null, $process_callback = n $func_returns = $process_callback[$fn_name]($raw_args, $context); } // Secondly look for built-in function. - elseif (isset(self::$functions[$fn_name])) { + elseif (isset($processFunctions->register[$fn_name])) { - $func = self::$functions[$fn_name]; + $func = $processFunctions->register[$fn_name]; $func_returns = $func($raw_args, $context); } @@ -114,20 +125,9 @@ public static function executeOnString($str, $patt = null, $process_callback = n return $str; } - ############################# # API and helpers. - public static function register($name, $callback) - { - Functions::$customFunctions[$name] = $callback; - } - - public static function deRegister($name) - { - unset(Functions::$customFunctions[$name]); - } - public static function parseArgs($input, $allowSpaceDelim = false) { return Util::splitDelimList( diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 3f6bff4..abebc48 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -30,6 +30,7 @@ public function __construct($user_options = array(), $dev_options = array()) $this->input = new \stdClass(); $this->output = new \stdClass(); $this->tokens = new Tokens(); + $this->functions = new Functions(); $this->hooks = new Hooks(); $this->sourceMap = null; $this->selectorAliases = array(); @@ -843,7 +844,7 @@ public function preCompile() $this->filterPlugins(); $this->filterAliases(); - Functions::setMatchPatt(); + $this->functions->setMatchPatt(); $this->stat['compile_start_time'] = microtime(true); } diff --git a/plugins/canvas.php b/plugins/canvas.php index 4faaa32..b62c185 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -11,13 +11,9 @@ Plugin::register('canvas', array( 'enable' => function ($process) { $process->hooks->add('capture_phase2', 'CssCrush\canvas_capture'); - Functions::register('canvas', 'CssCrush\canvas_generator'); - Functions::register('canvas-data', 'CssCrush\canvas_generator'); - }, - 'disable' => function ($process) { - Functions::deRegister('canvas'); - Functions::deRegister('canvas-data'); - }, + $process->functions->add('canvas', 'CssCrush\canvas_generator'); + $process->functions->add('canvas-data', 'CssCrush\canvas_generator'); + } )); @@ -363,7 +359,7 @@ function canvas_apply_css_funcs($canvas) { 'functions' => $filter_functions, ); - $generic_functions = array_diff_key(Functions::$functions, $map['fill']['functions']); + $generic_functions = array_diff_key(Crush::$process->functions->register, $map['fill']['functions']); $map['generic'] = array( 'patt' => Regex::makeFunctionPatt( array_keys($generic_functions), array('bare_paren' => true)), diff --git a/plugins/noise.php b/plugins/noise.php index 6760034..ac14bec 100644 --- a/plugins/noise.php +++ b/plugins/noise.php @@ -8,8 +8,8 @@ Plugin::register('noise', array( - 'enable' => function () { - Functions::register('noise', function ($input) { + 'enable' => function ($process) { + $process->functions->add('noise', function ($input) { return noise_generator($input, array( 'type' => 'fractalNoise', 'frequency' => .7, @@ -17,7 +17,7 @@ 'dimensions' => array(150, 150), )); }); - Functions::register('turbulence', function ($input) { + $process->functions->add('turbulence', function ($input) { return noise_generator($input, array( 'type' => 'turbulence', 'frequency' => .01, @@ -25,12 +25,7 @@ 'dimensions' => array(200, 200), )); }); - }, - - 'disable' => function () { - Functions::deRegister('noise'); - Functions::deRegister('turbulence'); - }, + } )); diff --git a/plugins/px2em.php b/plugins/px2em.php index 1d71407..080f1d1 100644 --- a/plugins/px2em.php +++ b/plugins/px2em.php @@ -7,14 +7,10 @@ namespace CssCrush; Plugin::register('px2em', array( - 'enable' => function () { - Functions::register('px2em', 'CssCrush\fn__px2em'); - Functions::register('px2rem', 'CssCrush\fn__px2rem'); - }, - 'disable' => function () { - Functions::deRegister('px2em'); - Functions::deRegister('px2rem'); - }, + 'enable' => function ($process) { + $process->functions->add('px2em', 'CssCrush\fn__px2em'); + $process->functions->add('px2rem', 'CssCrush\fn__px2rem'); + } )); diff --git a/plugins/svg-gradients.php b/plugins/svg-gradients.php index 9df74ad..5fc632d 100644 --- a/plugins/svg-gradients.php +++ b/plugins/svg-gradients.php @@ -10,15 +10,11 @@ 'load' => function () { $GLOBALS['CSSCRUSH_SVG_GRADIENT_UID'] = 0; }, - 'enable' => function () { + 'enable' => function ($process) { $GLOBALS['CSSCRUSH_SVG_GRADIENT_UID'] = 0; - Functions::register('svg-linear-gradient', 'CssCrush\fn__svg_linear_gradient'); - Functions::register('svg-radial-gradient', 'CssCrush\fn__svg_radial_gradient'); - }, - 'disable' => function () { - Functions::deRegister('svg-linear-gradient'); - Functions::deRegister('svg-radial-gradient'); - }, + $process->functions->add('svg-linear-gradient', 'CssCrush\fn__svg_linear_gradient'); + $process->functions->add('svg-radial-gradient', 'CssCrush\fn__svg_radial_gradient'); + } )); diff --git a/plugins/svg.php b/plugins/svg.php index a2d638e..356d364 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -13,13 +13,9 @@ 'enable' => function ($process) { $GLOBALS['CSSCRUSH_SVG_UID'] = 0; $process->hooks->add('capture_phase2', 'CssCrush\svg_capture'); - Functions::register('svg', 'CssCrush\fn__svg'); - Functions::register('svg-data', 'CssCrush\fn__svg_data'); - }, - 'disable' => function () { - Functions::deRegister('svg'); - Functions::deRegister('svg-data'); - }, + $process->functions->add('svg', 'CssCrush\fn__svg'); + $process->functions->add('svg-data', 'CssCrush\fn__svg_data'); + } )); @@ -623,12 +619,9 @@ function svg_apply_css_funcs($element, &$raw_data) { 'svg-radial-gradient' => 'CssCrush\svg_fn_radial_gradient', 'pattern' => 'CssCrush\svg_fn_pattern', ); - $generic_functions = - array_diff_key(Functions::$functions, $fill_functions); - $generic_functions_patt = Regex::makeFunctionPatt( - array_keys($generic_functions), array('bare_paren' => true)); - $fill_functions_patt = Regex::makeFunctionPatt( - array_keys($fill_functions)); + $generic_functions = array_diff_key(Crush::$process->functions->register, $fill_functions); + $generic_functions_patt = Regex::makeFunctionPatt(array_keys($generic_functions), array('bare_paren' => true)); + $fill_functions_patt = Regex::makeFunctionPatt(array_keys($fill_functions)); } foreach ($raw_data as $property => &$value) { From 68d846fb080fd0c048716eef5d301ff0a92cebac Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 14 Feb 2014 21:32:22 +0000 Subject: [PATCH 252/421] Reverted automatic `:any()` on selector aliases. --- lib/CssCrush/SelectorAlias.php | 2 +- plugins/hocus-pocus.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/CssCrush/SelectorAlias.php b/lib/CssCrush/SelectorAlias.php index 7c95110..4654fd7 100644 --- a/lib/CssCrush/SelectorAlias.php +++ b/lib/CssCrush/SelectorAlias.php @@ -18,7 +18,7 @@ public function __construct($handler, $type = 'alias') switch ($this->type) { case 'alias': - $this->handler = new Template(SelectorAlias::wrap($handler)); + $this->handler = new Template($handler); break; } } diff --git a/plugins/hocus-pocus.php b/plugins/hocus-pocus.php index 48409c9..7b5f490 100644 --- a/plugins/hocus-pocus.php +++ b/plugins/hocus-pocus.php @@ -8,7 +8,7 @@ Plugin::register('hocus-pocus', array( 'enable' => function ($process) { - $process->addSelectorAlias('hocus', ':hover,:focus'); - $process->addSelectorAlias('pocus', ':hover,:focus,:active'); + $process->addSelectorAlias('hocus', ':any(:hover,:focus)'); + $process->addSelectorAlias('pocus', ':any(:hover,:focus,:active)'); } )); From 51aa215057f53bdea07a84a87d5ce5a6baba42dc Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 18 Feb 2014 15:44:51 +0000 Subject: [PATCH 253/421] Some refactoring of the internal functions API. Fixed issue with HHVM file_get_contents failing. --- lib/CssCrush/Crush.php | 2 +- lib/CssCrush/Declaration.php | 27 +++++++------------ lib/CssCrush/Functions.php | 50 +++++++++++++++++------------------- lib/CssCrush/Process.php | 28 +++++++++++--------- lib/CssCrush/Template.php | 16 +++++------- lib/CssCrush/Util.php | 5 +++- plugins/canvas.php | 47 ++++++++++----------------------- plugins/color.php | 8 +++--- plugins/loop.php | 2 +- plugins/svg.php | 18 ++++++------- 10 files changed, 88 insertions(+), 115 deletions(-) diff --git a/lib/CssCrush/Crush.php b/lib/CssCrush/Crush.php index 54a66f0..8213c22 100644 --- a/lib/CssCrush/Crush.php +++ b/lib/CssCrush/Crush.php @@ -389,7 +389,7 @@ public static function runStat() case 'vars': $process->stat['vars'] = array_map(function ($item) use ($process) { - return $process->tokens->restore(Functions::executeOnString($item), array('s', 'u', 'p')); + return $process->tokens->restore($process->functions->apply($item), array('s', 'u', 'p')); }, $process->vars); break; diff --git a/lib/CssCrush/Declaration.php b/lib/CssCrush/Declaration.php index cb4674b..3a296e6 100644 --- a/lib/CssCrush/Declaration.php +++ b/lib/CssCrush/Declaration.php @@ -76,26 +76,22 @@ public function __toString() /* Execute functions on value. - Capture parens. Index functions. */ public function process($parent_rule) { - // Apply custom functions. + static $this_function; + if (! $this_function) { + $this_function = new Functions(array('this' => 'CssCrush\fn__this')); + } + if (! $this->skip) { - // this() function needs to be called exclusively because - // it's self referencing. + // this() function needs to be called exclusively because it's self referencing. $context = (object) array( 'rule' => $parent_rule, ); - $this->value = Functions::executeOnString( - $this->value, - Regex::$patt->thisFunction, - array( - 'this' => 'CssCrush\fn__this', - ), - $context); + $this->value = $this_function->apply($this->value, null, $context); $parent_rule->declarations->data += array($this->property => $this->value); @@ -103,19 +99,14 @@ public function process($parent_rule) 'rule' => $parent_rule, 'property' => $this->property ); - $this->value = Functions::executeOnString( - $this->value, - null, - null, - $context); + $this->value = Crush::$process->functions->apply($this->value, null, $context); } // Trim whitespace that may have been introduced by functions. $this->value = trim($this->value); - // After functions have applied value may be empty. + // Value may now be empty. if ($this->value === '') { - $this->valid = false; return; } diff --git a/lib/CssCrush/Functions.php b/lib/CssCrush/Functions.php index 2c60116..38694de 100644 --- a/lib/CssCrush/Functions.php +++ b/lib/CssCrush/Functions.php @@ -25,10 +25,19 @@ class Functions 'a-adjust' => 'CssCrush\fn__a_adjust', ); - public $pattern; - public $register = array(); + protected $pattern; + + protected $patternOptions; + + public function __construct($register = array(), $pattern = null, $pattern_options = array()) + { + $this->register = $register; + $this->pattern = $pattern; + $this->patternOptions = $pattern_options; + } + public function add($name, $callback) { $this->register[$name] = $callback; @@ -39,8 +48,9 @@ public function remove($name) unset($this->register[$name]); } - public function setMatchPatt($use_builtin = true, $options = array()) + public function setPattern($use_builtin = false) { + $options = $this->patternOptions; if ($use_builtin) { $this->register = self::$builtins + $this->register; $options += array('bare_paren' => true); @@ -48,36 +58,26 @@ public function setMatchPatt($use_builtin = true, $options = array()) $this->pattern = Regex::makeFunctionPatt(array_keys($this->register), $options); } - public static function executeOnString($str, $patt = null, $process_callback = null, \stdClass $context = null) + public function apply($str, $callbacks = null, \stdClass $context = null) { - $processFunctions = Crush::$process->functions; - - // No bracketed expressions, early return. if (strpos($str, '(') === false) { - return $str; } - // Set default pattern if not set. - if (! isset($patt)) { - $patt = $processFunctions->pattern; + if (! $this->pattern) { + $this->setPattern(); } - // No custom functions, early return. - if (! preg_match($patt, $str)) { - + if (! preg_match($this->pattern, $str)) { return $str; } - // Always pass in a context object. if (! $context) { $context = new \stdClass(); } - // Find custom function matches. - $matches = Regex::matchAll($patt, $str); + $matches = Regex::matchAll($this->pattern, $str); - // Step through the matches from last to first. while ($match = array_pop($matches)) { $offset = $match[0][1]; @@ -106,15 +106,12 @@ public static function executeOnString($str, $patt = null, $process_callback = n $func_returns = ''; $context->function = $fn_name; - // First look for function as directly passed. - if (isset($process_callback[$fn_name])) { - - $func_returns = $process_callback[$fn_name]($raw_args, $context); + // Use override callback if one is specified. + if (isset($callbacks[$fn_name])) { + $func_returns = $callbacks[$fn_name]($raw_args, $context); } - // Secondly look for built-in function. - elseif (isset($processFunctions->register[$fn_name])) { - - $func = $processFunctions->register[$fn_name]; + elseif (isset($this->register[$fn_name])) { + $func = $this->register[$fn_name]; $func_returns = $func($raw_args, $context); } @@ -125,6 +122,7 @@ public static function executeOnString($str, $patt = null, $process_callback = n return $str; } + ############################# # API and helpers. diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index abebc48..55ad8ce 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -395,6 +395,19 @@ protected function placeAllVars() static protected function placeVars(&$value) { + static $var_function; + if (! $var_function) { + $var_function = new Functions(array('$' => function ($raw_args) { + list($name, $default_value) = Functions::parseArgsSimple($raw_args); + if (isset(Crush::$process->vars[$name])) { + return Crush::$process->vars[$name]; + } + else { + return $default_value; + } + }), '~(\$)\(~'); + } + // Variables with no default value. $value = preg_replace_callback(Regex::$patt->varFunction, 'CssCrush\Process::cb_placeVars', $value, -1, $vars_placed); @@ -403,19 +416,10 @@ static protected function placeVars(&$value) if (strpos($value, '$(') !== false) { // Assume at least one replace. - $vars_placed = 1; + $vars_placed = true; // Variables may be nested so need to apply full function parsing. - $value = Functions::executeOnString($value, '~(\$)\(~', - array('$' => function ($raw_args) { - list($name, $default_value) = Functions::parseArgsSimple($raw_args); - if (isset(Crush::$process->vars[$name])) { - return Crush::$process->vars[$name]; - } - else { - return $default_value; - } - })); + $value = $var_function->apply($value); } // If we know replacements have been made we may want to update $value. e.g URL tokens. @@ -844,7 +848,7 @@ public function preCompile() $this->filterPlugins(); $this->filterAliases(); - $this->functions->setMatchPatt(); + $this->functions->setPattern(true); $this->stat['compile_start_time'] = microtime(true); } diff --git a/lib/CssCrush/Template.php b/lib/CssCrush/Template.php index 64e10bf..eb0c5cc 100644 --- a/lib/CssCrush/Template.php +++ b/lib/CssCrush/Template.php @@ -21,10 +21,10 @@ class Template public function __construct($str) { - static $arg_patt; + static $arg_patt, $template_functions; if (! $arg_patt) { - $arg_patt = Regex::makeFunctionPatt( - array('arg'), array('templating' => true)); + $arg_patt = Regex::makeFunctionPatt(array('arg'), array('templating' => true)); + $template_functions = new Functions(null, $arg_patt); } $str = Template::unTokenize($str); @@ -40,8 +40,6 @@ public function __construct($str) // Match the argument index integer. if (! isset($position) || ! ctype_digit($position)) { - - // On failure to match an integer return empty string. return ''; } @@ -52,14 +50,14 @@ public function __construct($str) $self->defaults[$position] = $default_value; } - // Update the argument count. - $argNumber = ((int) $position) + 1; - $self->argCount = max($self->argCount, $argNumber); + // Update argument count. + $arg_number = ((int) $position) + 1; + $self->argCount = max($self->argCount, $arg_number); return "?a$position?"; }; - $this->string = Functions::executeOnString($str, $arg_patt, array( + $this->string = $template_functions->apply($str, array( 'arg' => $capture_callback, '#' => $capture_callback, )); diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php index 3e64b24..af55324 100644 --- a/lib/CssCrush/Util.php +++ b/lib/CssCrush/Util.php @@ -192,10 +192,13 @@ public static function getLinkBetweenPaths($path1, $path2, $directories = true) public static function filePutContents($file, $str) { - if (@file_put_contents($file, $str, LOCK_EX)) { + if ($stream = fopen($file, 'w')) { + fwrite($stream, $str); + fclose($stream); return true; } + warning("[[CssCrush]] - Could not write file '$file'."); return false; diff --git a/plugins/canvas.php b/plugins/canvas.php index b62c185..e29fc09 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -332,19 +332,15 @@ function canvas_apply_filters($canvas, $src) { function canvas_apply_css_funcs($canvas) { - // Setup functions for using on values. - static $map; - if (! $map) { - - $fill_functions = array( - 'canvas-linear-gradient' => 'CssCrush\canvas_fn_linear_gradient', - ); - $map['fill'] = array( - 'patt' => Regex::makeFunctionPatt(array_keys($fill_functions)), - 'functions' => $fill_functions, - ); - - $filter_functions = array( + static $generic_functions, $fill_functions, $filter_functions; + if (! $generic_functions) { + + $fill_functions = new Functions(array('canvas-linear-gradient' => 'CssCrush\canvas_fn_linear_gradient')); + + $generic_register = array_diff_key(Crush::$process->functions->register, $fill_functions->register); + $generic_functions = new Functions($generic_register, null, array('bare_paren' => true)); + + $filter_functions = new Functions(array( 'contrast' => 'CssCrush\canvas_fn_filter', 'opacity' => 'CssCrush\canvas_fn_filter', 'colorize' => 'CssCrush\canvas_fn_filter', @@ -353,21 +349,9 @@ function canvas_apply_css_funcs($canvas) { 'brightness' => 'CssCrush\canvas_fn_filter', 'invert' => 'CssCrush\canvas_fn_filter', 'blur' => 'CssCrush\canvas_fn_filter', - ); - $map['filter'] = array( - 'patt' => Regex::makeFunctionPatt(array_keys($filter_functions)), - 'functions' => $filter_functions, - ); - - $generic_functions = array_diff_key(Crush::$process->functions->register, $map['fill']['functions']); - $map['generic'] = array( - 'patt' => Regex::makeFunctionPatt( - array_keys($generic_functions), array('bare_paren' => true)), - 'functions' => $generic_functions, - ); + )); } - // Function context object. $context = new stdClass(); foreach ($canvas->raw as $property => &$value) { @@ -376,21 +360,16 @@ function canvas_apply_css_funcs($canvas) { continue; } - // Generic functions. - $value = Functions::executeOnString( - $value, $map['generic']['patt'], $map['generic']['functions']); + $value = $generic_functions->apply($value); - // Fill functions. if (in_array($property, array('fill', 'background-fill'))) { $context->currentProperty = $property; $context->canvas = $canvas; - $value = Functions::executeOnString( - $value, $map['fill']['patt'], $map['fill']['functions'], $context); + $value = $fill_functions->apply($value, null, $context); } elseif ($property === 'canvas-filter') { $context->canvas = $canvas; - $value = Functions::executeOnString( - $value, $map['filter']['patt'], $map['filter']['functions'], $context); + $value = $filter_functions->apply($value, null, $context); } } } diff --git a/plugins/color.php b/plugins/color.php index dcffcf3..28ff646 100644 --- a/plugins/color.php +++ b/plugins/color.php @@ -31,14 +31,14 @@ function color_capture($process) { $native_keywords = Color::getKeywords(); $custom_keywords = array(); - Crush::$process->colorKeywords = $native_keywords; + $process->colorKeywords = $native_keywords; foreach ($captured_keywords as $key => $value) { - $value = Functions::executeOnString($value); + $value = $process->functions->apply($value); if (! isset($native_keywords[$key]) && $rgba = Color::parse($value)) { $custom_keywords[] = $key; - Crush::$process->stat['colors'][$key] = new Color($rgba); - Crush::$process->colorKeywords[$key] = $rgba; + $process->stat['colors'][$key] = new Color($rgba); + $process->colorKeywords[$key] = $rgba; } } diff --git a/plugins/loop.php b/plugins/loop.php index fd0fcb8..6f7b662 100644 --- a/plugins/loop.php +++ b/plugins/loop.php @@ -57,7 +57,7 @@ function loop_resolve_list($list_text) { // Either a generator function or a plain list. $items = array(); - $list_text = Functions::executeOnString($list_text); + $list_text = Crush::$process->functions->apply($list_text); $generator_func_patt = Regex::make('~(?range|color-range) {{parens}}~ix'); if (preg_match($generator_func_patt, $list_text, $m)) { diff --git a/plugins/svg.php b/plugins/svg.php index 356d364..aaab4df 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -612,25 +612,25 @@ function svg_apply_css_funcs($element, &$raw_data) { // Setup functions for using on values. // Note using custom versions of svg-*-gradient(). - static $generic_functions_patt, $fill_functions, $fill_functions_patt; - if (! $generic_functions_patt) { - $fill_functions = array( + static $generic_functions, $fill_functions; + if (! $generic_functions) { + $fill_register = array( 'svg-linear-gradient' => 'CssCrush\svg_fn_linear_gradient', 'svg-radial-gradient' => 'CssCrush\svg_fn_radial_gradient', 'pattern' => 'CssCrush\svg_fn_pattern', ); - $generic_functions = array_diff_key(Crush::$process->functions->register, $fill_functions); - $generic_functions_patt = Regex::makeFunctionPatt(array_keys($generic_functions), array('bare_paren' => true)); - $fill_functions_patt = Regex::makeFunctionPatt(array_keys($fill_functions)); + $fill_functions = new Functions($fill_register); + + $generic_register = array_diff_key(Crush::$process->functions->register, $fill_register); + $generic_functions = new Functions($generic_register, null, array('bare_paren' => true)); } foreach ($raw_data as $property => &$value) { - $value = Functions::executeOnString($value, $generic_functions_patt); + $value = $generic_functions->apply($value); // Only capturing fills for fill and stoke properties. if ($property === 'fill' || $property === 'stroke') { - $value = Functions::executeOnString( - $value, $fill_functions_patt, $fill_functions, $element); + $value = $fill_functions->apply($value, null, $element); // If the value is a color with alpha component we split the color // and set the corresponding *-opacity property because Webkit doesn't From 5d96900a4d76483a6be4f7d59b65ed019ebe0432 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 18 Feb 2014 15:50:28 +0000 Subject: [PATCH 254/421] Reverting to colons for declaration aliases now hhvm has fixed its ini parsing issues. --- aliases.ini | 142 ++++++++++++++++++++--------------------- lib/CssCrush/Crush.php | 2 +- 2 files changed, 72 insertions(+), 72 deletions(-) diff --git a/aliases.ini b/aliases.ini index d0d8506..79eb462 100644 --- a/aliases.ini +++ b/aliases.ini @@ -220,88 +220,88 @@ [declarations] ; Flexbox (2012). - display.flex[] = display:-ms-flexbox - display.flex[] = display:-webkit-flex - display.inline-flex[] = display:-ms-inline-flexbox - display.inline-flex[] = display:-webkit-inline-flex + display:flex[] = display:-ms-flexbox + display:flex[] = display:-webkit-flex + display:inline-flex[] = display:-ms-inline-flexbox + display:inline-flex[] = display:-webkit-inline-flex ; Flexbox (early 2012). - align-content.flex-start[] = -ms-flex-line-pack:start - align-content.flex-end[] = -ms-flex-line-pack:end - align-content.center[] = -ms-flex-line-pack:center - align-content.space-between[] = -ms-flex-line-pack:justify - align-content.space-around[] = -ms-flex-line-pack:distribute - align-content.stretch[] = -ms-flex-line-pack:stretch - - align-items.flex-start[] = -ms-flex-align:start - align-items.flex-end[] = -ms-flex-align:end - align-items.center[] = -ms-flex-align:center - align-items.baseline[] = -ms-flex-align:baseline - align-items.stretch[] = -ms-flex-align:stretch - - align-self.auto[] = -ms-flex-item-align:auto - align-self.flex-start[] = -ms-flex-item-align:start - align-self.flex-end[] = -ms-flex-item-align:end - align-self.center[] = -ms-flex-item-align:center - align-self.baseline[] = -ms-flex-item-align:baseline - align-self.stretch[] = -ms-flex-item-align:stretch - - justify-content.flex-start[] = -ms-flex-pack:start - justify-content.flex-end[] = -ms-flex-pack:end - justify-content.center[] = -ms-flex-pack:center - justify-content.space-between[] = -ms-flex-pack:justify - justify-content.space-around[] = -ms-flex-pack:distribute + align-content:flex-start[] = -ms-flex-line-pack:start + align-content:flex-end[] = -ms-flex-line-pack:end + align-content:center[] = -ms-flex-line-pack:center + align-content:space-between[] = -ms-flex-line-pack:justify + align-content:space-around[] = -ms-flex-line-pack:distribute + align-content:stretch[] = -ms-flex-line-pack:stretch + + align-items:flex-start[] = -ms-flex-align:start + align-items:flex-end[] = -ms-flex-align:end + align-items:center[] = -ms-flex-align:center + align-items:baseline[] = -ms-flex-align:baseline + align-items:stretch[] = -ms-flex-align:stretch + + align-self:auto[] = -ms-flex-item-align:auto + align-self:flex-start[] = -ms-flex-item-align:start + align-self:flex-end[] = -ms-flex-item-align:end + align-self:center[] = -ms-flex-item-align:center + align-self:baseline[] = -ms-flex-item-align:baseline + align-self:stretch[] = -ms-flex-item-align:stretch + + justify-content:flex-start[] = -ms-flex-pack:start + justify-content:flex-end[] = -ms-flex-pack:end + justify-content:center[] = -ms-flex-pack:center + justify-content:space-between[] = -ms-flex-pack:justify + justify-content:space-around[] = -ms-flex-pack:distribute ; Flexbox (2009). - display.box[] = display:-webkit-box - display.box[] = display:-moz-box + display:box[] = display:-webkit-box + display:box[] = display:-moz-box ; Cursor values (non-standard). - cursor.zoom-in[] = cursor:-webkit-zoom-in - cursor.zoom-in[] = cursor:-moz-zoom-in - cursor.zoom-in[] = cursor:-ms-zoom-in - cursor.zoom-in[] = cursor:-o-zoom-in - cursor.zoom-out[] = cursor:-webkit-zoom-out - cursor.zoom-out[] = cursor:-moz-zoom-out - cursor.zoom-out[] = cursor:-ms-zoom-out - cursor.zoom-out[] = cursor:-o-zoom-out + cursor:zoom-in[] = cursor:-webkit-zoom-in + cursor:zoom-in[] = cursor:-moz-zoom-in + cursor:zoom-in[] = cursor:-ms-zoom-in + cursor:zoom-in[] = cursor:-o-zoom-in + cursor:zoom-out[] = cursor:-webkit-zoom-out + cursor:zoom-out[] = cursor:-moz-zoom-out + cursor:zoom-out[] = cursor:-ms-zoom-out + cursor:zoom-out[] = cursor:-o-zoom-out ; Experimental width values. - width.max-content[] = width:intrinsic - width.max-content[] = width:-webkit-max-content - width.max-content[] = width:-moz-max-content - width.min-content[] = width:-webkit-min-content - width.min-content[] = width:-moz-min-content - width.available[] = width:-webkit-available - width.available[] = width:-moz-available - width.fit-content[] = width:-webkit-fit-content - width.fit-content[] = width:-moz-fit-content - - max-width.max-content[] = max-width:intrinsic - max-width.max-content[] = max-width:-webkit-max-content - max-width.max-content[] = max-width:-moz-max-content - max-width.min-content[] = max-width:-webkit-min-content - max-width.min-content[] = max-width:-moz-min-content - max-width.available[] = max-width:-webkit-available - max-width.available[] = max-width:-moz-available - max-width.fit-content[] = max-width:-webkit-fit-content - max-width.fit-content[] = max-width:-moz-fit-content - - min-width.max-content[] = min-width:intrinsic - min-width.max-content[] = min-width:-webkit-max-content - min-width.max-content[] = min-width:-moz-max-content - min-width.min-content[] = min-width:-webkit-min-content - min-width.min-content[] = min-width:-moz-min-content - min-width.available[] = min-width:-webkit-available - min-width.available[] = min-width:-moz-available - min-width.fit-content[] = min-width:-webkit-fit-content - min-width.fit-content[] = min-width:-moz-fit-content + width:max-content[] = width:intrinsic + width:max-content[] = width:-webkit-max-content + width:max-content[] = width:-moz-max-content + width:min-content[] = width:-webkit-min-content + width:min-content[] = width:-moz-min-content + width:available[] = width:-webkit-available + width:available[] = width:-moz-available + width:fit-content[] = width:-webkit-fit-content + width:fit-content[] = width:-moz-fit-content + + max-width:max-content[] = max-width:intrinsic + max-width:max-content[] = max-width:-webkit-max-content + max-width:max-content[] = max-width:-moz-max-content + max-width:min-content[] = max-width:-webkit-min-content + max-width:min-content[] = max-width:-moz-min-content + max-width:available[] = max-width:-webkit-available + max-width:available[] = max-width:-moz-available + max-width:fit-content[] = max-width:-webkit-fit-content + max-width:fit-content[] = max-width:-moz-fit-content + + min-width:max-content[] = min-width:intrinsic + min-width:max-content[] = min-width:-webkit-max-content + min-width:max-content[] = min-width:-moz-max-content + min-width:min-content[] = min-width:-webkit-min-content + min-width:min-content[] = min-width:-moz-min-content + min-width:available[] = min-width:-webkit-available + min-width:available[] = min-width:-moz-available + min-width:fit-content[] = min-width:-webkit-fit-content + min-width:fit-content[] = min-width:-moz-fit-content ; Appearance (non-standard). - appearance.none[] = -webkit-appearance:none - appearance.none[] = -moz-appearance:none + appearance:none[] = -webkit-appearance:none + appearance:none[] = -moz-appearance:none - position.sticky[] = position:-webkit-sticky + position:sticky[] = position:-webkit-sticky ;---------------------------------------------------------------- diff --git a/lib/CssCrush/Crush.php b/lib/CssCrush/Crush.php index 8213c22..ad2f2fc 100644 --- a/lib/CssCrush/Crush.php +++ b/lib/CssCrush/Crush.php @@ -127,7 +127,7 @@ public static function parseAliasesFile($file) $store = array(); foreach ($items as $prop_val => $aliases) { - list($prop, $value) = array_map('trim', explode('.', $prop_val)); + list($prop, $value) = array_map('trim', explode(':', $prop_val)); foreach ($aliases as &$alias) { From 5021ad34e966675f113ec094b97bf507c6cb5f64 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 18 Feb 2014 15:52:15 +0000 Subject: [PATCH 255/421] Adding hhvm to the CI test config. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 7caf104..cc6bb14 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ php: - 5.3 - 5.4 - 5.5 + - hhvm before_script: - composer self-update From 3a4fbf3de5da77caf6ce6d90933851c0e626fdb4 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 19 Feb 2014 10:54:46 +0000 Subject: [PATCH 256/421] Adding git version tag for boilerplate use. --- boilerplate.txt | 4 ++-- lib/CssCrush/Process.php | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/boilerplate.txt b/boilerplate.txt index fb1ade1..7f9f6ba 100644 --- a/boilerplate.txt +++ b/boilerplate.txt @@ -1,2 +1,2 @@ -CSS-Crush(ed) on {{datetime}} -http://github.com/peteboere/css-crush ({{version}}) +CSS-Crush(ed) on {{ datetime }} +http://github.com/peteboere/css-crush ({{ version }}) diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 55ad8ce..48ddbbc 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -150,9 +150,12 @@ protected function getBoilerplate() $tags = array( 'datetime' => @date('Y-m-d H:i:s O'), 'year' => @date('Y'), - 'version' => csscrush_version(), 'command' => $command_args, 'plugins' => implode(',', array_keys($this->plugins)), + 'version' => csscrush_version(), + 'git_version' => function () { + return csscrush_version(true); + }, 'compile_time' => function () { $now = microtime(true) - Crush::$process->stat['compile_start_time']; return round($now, 4) . ' seconds'; @@ -160,7 +163,7 @@ protected function getBoilerplate() ); foreach ($boilerplate_matches[0] as $index => $tag) { - $tag_name = $boilerplate_matches[1][$index]; + $tag_name = trim($boilerplate_matches[1][$index]); $replacement = '?'; if (isset($tags[$tag_name])) { $replacement = is_callable($tags[$tag_name]) ? $tags[$tag_name]() : $tags[$tag_name]; From 46f4686916303cffaa5cccfd00dc249bde04a223 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 10 Mar 2014 10:03:54 +0000 Subject: [PATCH 257/421] Improving feedback for command line watched files. --- cli.php | 48 +++++++++++++++++++++++++++++++++--------- lib/CssCrush/Crush.php | 13 ++++++------ 2 files changed, 45 insertions(+), 16 deletions(-) diff --git a/cli.php b/cli.php index fdcc347..8211f75 100755 --- a/cli.php +++ b/cli.php @@ -310,19 +310,46 @@ stdout('CONTROL-C to quit.'); + // Surpress error reporting to avoid flooding the screen. + error_reporting(0); + $outstanding_errors = false; + while (true) { - $created_file = csscrush_file($args->input_file, $process_opts); + csscrush_file($args->input_file, $process_opts); $stats = csscrush_stat(); - if ($stats['errors']) { - stderr($stats['errors']); + $changed = $stats['compile_time'] && ! $stats['errors']; + $errors = $stats['errors']; + $show_errors = $errors && (! $outstanding_errors || ($outstanding_errors != $errors)); - exit(STATUS_ERROR); + $output_file_display = "$stats[output_filename] ($stats[output_path])"; + $input_file_display = "$stats[input_filename] ($stats[input_path])"; + + $compile_info = array(); + if ($stats['input_path']) { + $compile_info['input_file'] = $input_file_display; + } + + if ($errors) { + if ($show_errors) { + $outstanding_errors = $errors; + if ($stats['output_path']) { + stderr(colorize("ERROR: $output_file_display"), true, false); + } + stderr($errors); + } + } + elseif ($changed) { + stdout(colorize("FILE UPDATED: $output_file_display")); + $compile_info['compile_time'] = round($stats['compile_time'], 5) . ' seconds'; + $trace_options = isset($process_opts['trace']) ? array_flip($process_opts['trace']) : null; + $compile_info += $trace_options ? array_intersect_key($stats, $trace_options) : array(); + $outstanding_errors = false; } - if (is_array($args->trace) && $stats['compile_time'] > 0) { - stdout(format_stats($stats)); + if ($show_errors || $changed) { + stdout(format_stats($compile_info)); } sleep(1); @@ -350,7 +377,7 @@ if (is_array($args->trace)) { // Use stderror for stats to preserve stdout. - stderr(format_stats($stats) . PHP_EOL); + stderr(format_stats($stats) . PHP_EOL, true, 'b'); } stdout($output); @@ -362,9 +389,9 @@ ################################################################## ## Helpers. -function stderr($lines, $closing_newline = true) { +function stderr($lines, $closing_newline = true, $color = 'r') { $out = implode(PHP_EOL, (array) $lines) . ($closing_newline ? PHP_EOL : ''); - fwrite(STDERR, $out); + fwrite(STDERR, colorize($color ? "<$color>$out" : $out)); } function stdout($lines, $closing_newline = true) { @@ -403,8 +430,9 @@ function parse_list(array $option) { function format_stats($stats) { $out = array(); foreach ($stats as $name => $value) { + $name = ucfirst(str_replace('_', ' ', $name)); if (is_scalar($value)) { - $out[] = "$name: $value"; + $out[] = colorize("└── $name: $value"); } } return implode(PHP_EOL, $out); diff --git a/lib/CssCrush/Crush.php b/lib/CssCrush/Crush.php index ad2f2fc..2657eb0 100644 --- a/lib/CssCrush/Crush.php +++ b/lib/CssCrush/Crush.php @@ -208,7 +208,7 @@ public static function file($file, $options = array()) return ''; } - Crush::runStat('hostfile'); + Crush::runStat('paths'); if ($options->cache) { $process->cacheData = $process->io('getCacheData'); @@ -308,7 +308,7 @@ public static function inline($file, $options = array(), $tag_attributes = array */ public static function string($string, $options = array()) { - // For strings set boilerplate to not display by default + // Set boilerplate to not display by default. if (! isset($options['boilerplate'])) { $options['boilerplate'] = false; } @@ -318,8 +318,6 @@ public static function string($string, $options = array()) $process = self::$process; $options = $process->options; - // Set the path context if one is given. - // Fallback to document root. if (! empty($options->context)) { $process->resolveContext($options->context); } @@ -383,8 +381,11 @@ public static function runStat() foreach (func_get_args() as $stat_name) { switch ($stat_name) { - case 'hostfile': - $process->stat['hostfile'] = $process->input->filename; + case 'paths': + $process->stat['input_filename'] = $process->input->filename; + $process->stat['input_path'] = $process->input->path; + $process->stat['output_filename'] = $process->output->filename; + $process->stat['output_path'] = $process->output->dir . '/' . $process->output->filename; break; case 'vars': From 30e22a4b21ab45b283955f0911b10b29df34f575 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 10 Mar 2014 10:07:09 +0000 Subject: [PATCH 258/421] Removing data-modified from default boilerplate. Date modified information is already available on files, and printing it in-file adds un-necessary diff/commit noise. --- boilerplate.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/boilerplate.txt b/boilerplate.txt index 7f9f6ba..73338a9 100644 --- a/boilerplate.txt +++ b/boilerplate.txt @@ -1,2 +1 @@ -CSS-Crush(ed) on {{ datetime }} http://github.com/peteboere/css-crush ({{ version }}) From 97074aed4b0128af99afaeaf9b80fecd34ac47da Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 11 Mar 2014 14:49:17 +0000 Subject: [PATCH 259/421] Removed static IO method calling. --- lib/CssCrush/Crush.php | 8 +++---- lib/CssCrush/IO.php | 50 +++++++++++++++++++++------------------ lib/CssCrush/IO/Watch.php | 12 +++++----- lib/CssCrush/Importer.php | 2 +- lib/CssCrush/Process.php | 22 ++++++----------- 5 files changed, 45 insertions(+), 49 deletions(-) diff --git a/lib/CssCrush/Crush.php b/lib/CssCrush/Crush.php index 2657eb0..128dbf6 100644 --- a/lib/CssCrush/Crush.php +++ b/lib/CssCrush/Crush.php @@ -211,9 +211,9 @@ public static function file($file, $options = array()) Crush::runStat('paths'); if ($options->cache) { - $process->cacheData = $process->io('getCacheData'); - if ($process->io('validateCache')) { - $file_url = $process->io('getOutputUrl'); + $process->cacheData = $process->io->getCacheData(); + if ($process->io->validateCache()) { + $file_url = $process->io->getOutputUrl(); $process->release(); return $file_url; @@ -222,7 +222,7 @@ public static function file($file, $options = array()) $stream = $process->compile(); - return $process->io('write', $stream) ? $process->io('getOutputUrl') : ''; + return $process->io->write($stream) ? $process->io->getOutputUrl() : ''; } /** diff --git a/lib/CssCrush/IO.php b/lib/CssCrush/IO.php index baefbdc..484632a 100644 --- a/lib/CssCrush/IO.php +++ b/lib/CssCrush/IO.php @@ -8,23 +8,28 @@ class IO { - public static function init() + protected $process; + + public function __construct(Process $process) { - $process = Crush::$process; - $process->cacheFile = "{$process->output->dir}/.csscrush"; + $this->process = $process; } - public static function getOutputDir() + public function init() { - $process = Crush::$process; - $output_dir = $process->options->output_dir; + $this->process->cacheFile = "{$this->process->output->dir}/.csscrush"; + } - return $output_dir ? $output_dir : $process->input->dir; + public function getOutputDir() + { + $output_dir = $this->process->options->output_dir; + + return $output_dir ? $output_dir : $this->process->input->dir; } - public static function testOutputDir() + public function testOutputDir() { - $dir = Crush::$process->output->dir; + $dir = $this->process->output->dir; $pathtest = true; if (! file_exists($dir)) { @@ -47,12 +52,11 @@ public static function testOutputDir() return $pathtest; } - public static function getOutputFileName() + public function getOutputFileName() { - $process = Crush::$process; - $options = $process->options; + $options = $this->process->options; - $output_basename = basename($process->input->filename, '.css'); + $output_basename = basename($this->process->input->filename, '.css'); if (! empty($options->output_file)) { $output_basename = basename($options->output_file, '.css'); @@ -61,9 +65,9 @@ public static function getOutputFileName() return "$output_basename.crush.css"; } - public static function getOutputUrl() + public function getOutputUrl() { - $process = Crush::$process; + $process = $this->process; $options = $process->options; $filename = $process->output->filename; @@ -89,9 +93,9 @@ public static function getOutputUrl() return $url; } - public static function validateCache() + public function validateCache() { - $process = Crush::$process; + $process = $this->process; $options = $process->options; $input = $process->input; $output = $process->output; @@ -161,9 +165,9 @@ public static function validateCache() } } - public static function getCacheData() + public function getCacheData() { - $process = Crush::$process; + $process = $this->process; if ( file_exists($process->cacheFile) && @@ -201,9 +205,9 @@ public static function getCacheData() return $cache_data; } - public static function saveCacheData() + public function saveCacheData() { - $process = Crush::$process; + $process = $this->process; debug('Saving config.'); @@ -211,9 +215,9 @@ public static function saveCacheData() Util::filePutContents($process->cacheFile, json_encode($process->cacheData, $flags), __METHOD__); } - public static function write(Stream $stream) + public function write(Stream $stream) { - $process = Crush::$process; + $process = $this->process; $output = $process->output; $source_map_filename = "$output->filename.map"; diff --git a/lib/CssCrush/IO/Watch.php b/lib/CssCrush/IO/Watch.php index 5662aed..9c93212 100644 --- a/lib/CssCrush/IO/Watch.php +++ b/lib/CssCrush/IO/Watch.php @@ -13,9 +13,9 @@ class Watch extends IO { public static $cacheData = array(); - public static function getOutputFileName() + public function getOutputFileName() { - $process = Crush::$process; + $process = $this->process; $options = $process->options; $output_basename = basename($process->input->filename, '.css'); @@ -32,17 +32,17 @@ public static function getOutputFileName() return "$output_basename$suffix.css"; } - public static function getCacheData() + public function getCacheData() { // Clear results from earlier processes. clearstatcache(); - Crush::$process->cacheData = array(); + $this->process->cacheData = array(); return self::$cacheData; } - public static function saveCacheData() + public function saveCacheData() { - self::$cacheData = Crush::$process->cacheData; + self::$cacheData = $this->process->cacheData; } } diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index d4802c1..5cd0be0 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -138,7 +138,7 @@ public static function hostfile() 'options' => $options->get(), ); - $process->io('saveCacheData'); + $process->io->saveCacheData(); } return $str; diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 48ddbbc..aa2f339 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -35,6 +35,7 @@ public function __construct($user_options = array(), $dev_options = array()) $this->sourceMap = null; $this->selectorAliases = array(); $this->selectorAliasesPatt = null; + $this->io = new Crush::$config->io($this); $this->debugLog = array(); $this->errors = array(); @@ -88,29 +89,20 @@ public function resolveContext($input_dir = null, $input_file = null) $this->input->dir = $input_dir ?: $this->docRoot; $this->input->dirUrl = substr($input_dir, strlen($this->docRoot)); - $this->output->dir = $this->io('getOutputDir'); - $this->output->filename = $this->io('getOutputFileName'); + $this->output->dir = $this->io->getOutputDir(); + $this->output->filename = $this->io->getOutputFileName(); $this->output->dirUrl = substr($this->output->dir, strlen($this->docRoot)); $context_resolved = true; if ($input_file) { - $context_resolved = $this->io('testOutputDir'); + $context_resolved = $this->io->testOutputDir(); } - $this->io('init'); + $this->io->init(); return $context_resolved; } - public function io($method) - { - // Get argument list (excluding the method name which comes first). - $args = func_get_args(); - array_shift($args); - - return call_user_func_array(array(Crush::$config->io, $method), $args); - } - ############################# # Boilerplate. @@ -175,11 +167,11 @@ protected function getBoilerplate() // Pretty print. $EOL = $this->newline; - $boilerplate = preg_split('~[\t]*'. Regex::$classes->newline . '[\t]*~', $boilerplate); + $boilerplate = preg_split('~[\t]*'. Regex::$classes->newline . '[\t]*~', trim($boilerplate)); $boilerplate = array_map('trim', $boilerplate); $boilerplate = "$EOL * " . implode("$EOL * ", $boilerplate); - return "/*{$boilerplate}$EOL */$EOL"; + return "/*$boilerplate$EOL */$EOL"; } From f6c1d97cdb363abf3ac1171e1cefecb1eaa96e78 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 12 Mar 2014 09:27:12 +0000 Subject: [PATCH 260/421] Removing testOutputDir method from IO interface. --- lib/CssCrush/IO.php | 25 ------------------------- lib/CssCrush/Process.php | 19 ++++++++++++++++++- lib/CssCrush/SelectorAlias.php | 4 ++-- 3 files changed, 20 insertions(+), 28 deletions(-) diff --git a/lib/CssCrush/IO.php b/lib/CssCrush/IO.php index 484632a..53f73d9 100644 --- a/lib/CssCrush/IO.php +++ b/lib/CssCrush/IO.php @@ -27,31 +27,6 @@ public function getOutputDir() return $output_dir ? $output_dir : $this->process->input->dir; } - public function testOutputDir() - { - $dir = $this->process->output->dir; - $pathtest = true; - - if (! file_exists($dir)) { - warning("[[CssCrush]] - Output directory '$dir' doesn't exist."); - $pathtest = false; - } - elseif (! is_writable($dir)) { - - debug('Attempting to change permissions.'); - - if (! @chmod($dir, 0755)) { - warning("[[CssCrush]] - Output directory '$dir' is unwritable."); - $pathtest = false; - } - else { - debug('Permissions updated.'); - } - } - - return $pathtest; - } - public function getOutputFileName() { $options = $this->process->options; diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index aa2f339..2754bc2 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -95,7 +95,24 @@ public function resolveContext($input_dir = null, $input_file = null) $context_resolved = true; if ($input_file) { - $context_resolved = $this->io->testOutputDir(); + $output_dir = $this->output->dir; + + if (! file_exists($output_dir)) { + warning("[[CssCrush]] - Output directory '$output_dir' doesn't exist."); + $context_resolved = false; + } + elseif (! is_writable($output_dir)) { + + debug('Attempting to change permissions.'); + + if (! @chmod($output_dir, 0755)) { + warning("[[CssCrush]] - Output directory '$output_dir' is unwritable."); + $context_resolved = false; + } + else { + debug('Permissions updated.'); + } + } } $this->io->init(); diff --git a/lib/CssCrush/SelectorAlias.php b/lib/CssCrush/SelectorAlias.php index 4654fd7..e2470d4 100644 --- a/lib/CssCrush/SelectorAlias.php +++ b/lib/CssCrush/SelectorAlias.php @@ -8,8 +8,8 @@ class SelectorAlias { - public $type; - public $handler; + protected $type; + protected $handler; public function __construct($handler, $type = 'alias') { From 9a48f6e5eb3c7a9b1bc9d45d9632c1729c2f075a Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 17 Mar 2014 09:44:53 +0000 Subject: [PATCH 261/421] Removed error notice when creating directories via command line. --- cli.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/cli.php b/cli.php index 8211f75..c53ce14 100755 --- a/cli.php +++ b/cli.php @@ -169,13 +169,13 @@ } if ($args->output_file) { - $out_dir = realpath(dirname($args->output_file)); - if (! $out_dir) { - stderr('Output directory does not exist.'); + $out_dir = dirname($args->output_file); + if (! realpath($out_dir) && ! @mkdir($out_dir)) { + stderr('Output directory does not exist and could not be created.'); exit(STATUS_ERROR); } - $args->output_file = $out_dir . '/' . basename($args->output_file); + $args->output_file = realpath($out_dir) . '/' . basename($args->output_file); } if ($args->context) { @@ -299,7 +299,6 @@ $process_opts['output_file'] = basename($args->output_file); } - ################################################################## ## Output. @@ -374,14 +373,15 @@ exit(STATUS_ERROR); } } + else { + stdout($output); + } if (is_array($args->trace)) { // Use stderror for stats to preserve stdout. stderr(format_stats($stats) . PHP_EOL, true, 'b'); } - stdout($output); - exit(STATUS_OK); } From 8578e6a57f6e8a766fcb5bb11d6d141e31aea386 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 20 Mar 2014 10:20:40 +0000 Subject: [PATCH 262/421] Updating docs and changelog. --- CHANGELOG.md | 21 +++++++++++++++++++++ README.md | 2 +- composer.json | 2 +- docs/api/options.md | 5 +++++ docs/api/settings.md | 17 +++++++++++++++++ docs/core/functions/math.md | 4 ++-- docs/core/variables.md | 3 +-- docs/plugins/color.md | 3 +-- docs/plugins/px2em.md | 17 ++++++++++++++--- docs/plugins/rem.md | 20 +++++++++----------- docs/plugins/text-align.md | 6 +++++- 11 files changed, 77 insertions(+), 23 deletions(-) create mode 100644 docs/api/settings.md diff --git a/CHANGELOG.md b/CHANGELOG.md index bc08cbb..5f02e96 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,24 @@ +### 2.1.0 (2014-?-?) + +* Added HHVM support (HHVM >= 2.4) +* Added Travis CI support. +* Added custom color keywords plugin. +* Added text-align plugin for polyfilling the direction sensitive text-align values, start and end. +* Added selector `splat` aliases, which expand based on arguments. +* Added settings interface for plugins and CSS environment. Old variable based settings (as used in rem and px2em plugins) is now deprecated. +* Added library docs at the repository level, written in markdown. +* Added unit argument to the math function. +* Deprecated bare parens math e.g. `()` due to their use in developing CSS specs. +* Removed `-ms-` gradient aliases. +* Renamed plugin `hsl-to-hex` to `hsl2hex`. +* Updated plugin API. +* Improved feedback for command line watched files. +* Removed date modified from default boilerplate. +* Made git version available for use in boilerplates. +* Reported version now uses `git describe` style output if available. +* Changed base IO class to use non-static methods. +* Numerous under the hood improvements. + ### 2.0.0 (2013-11-2) * Raised PHP version requirement to PHP 5.3.1. diff --git a/README.md b/README.md index 73c78c8..20aacd3 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ There are several other [functions](http://the-echoplex.net/csscrush#api) for wo * `csscrush_string($css, $options)` - Compiles a raw string of css and returns the resulting css. * `csscrush_inline($file, $options, $tag_attributes)` - Returns compiled css in an inline style tag. -There are a number of [options](http://the-echoplex.net/csscrush#options) available for tailoring the output, and a collection of bundled [plugins](http://the-echoplex.net/csscrush#plugins) that cover many workflow issues in contemporary CSS development. +There are a number of [options](http://the-echoplex.net/csscrush#api--options) available for tailoring the output, and a collection of bundled [plugins](http://the-echoplex.net/csscrush#plugins) that cover many workflow issues in contemporary CSS development. ### Contributing diff --git a/composer.json b/composer.json index 8b2ebc9..dc021c8 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,7 @@ }, "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "2.1-dev" } } } diff --git a/docs/api/options.md b/docs/api/options.md index 2d4c097..b5f960d 100644 --- a/docs/api/options.md +++ b/docs/api/options.md @@ -105,5 +105,10 @@ Path Specify an alternative server document root for situations where the CSS is being served behind an alias or url rewritten path. + + settings + Array + An associative array of plugin and environment settings. Used primarily for plugin configuration. + diff --git a/docs/api/settings.md b/docs/api/settings.md new file mode 100644 index 0000000..2480233 --- /dev/null +++ b/docs/api/settings.md @@ -0,0 +1,17 @@ + + +Plugins sometimes use __settings__ to configure their behaviour. + +Settings can be specified as an [option](#api--options), or declared in CSS with block or single-line syntax: + +```crush +@settings { + dir: ltr; +} + +@settings rem-mode px-fallback; +``` diff --git a/docs/core/functions/math.md b/docs/core/functions/math.md index 2621e80..402d2d9 100644 --- a/docs/core/functions/math.md +++ b/docs/core/functions/math.md @@ -6,10 +6,10 @@ Evaluate a raw mathematical expression. -math( *expression* ) *or* ( *expression* ) +math( *expression* [, *unit*] ) ```css -font-size: ( 12 / 16 )em; +font-size: math( 12 / 16, em ); ``` ```css diff --git a/docs/core/variables.md b/docs/core/variables.md index ee678ec..1496c20 100644 --- a/docs/core/variables.md +++ b/docs/core/variables.md @@ -12,7 +12,6 @@ Variables can be injected at runtime with the [vars option](#api--options). ```crush /* Defining variables */ @define { - helvetica: "Helvetica Neue", "Helvetica", "Arial", sans-serif; dark: #333; light: #F4F2E2; smaller-screen: only screen and (max-width: 800px); @@ -22,7 +21,7 @@ Variables can be injected at runtime with the [vars option](#api--options). @media $(smaller-screen) { ul, p { color: $(dark); - /* Specifying a fallback value */ + /* Using a fallback value with an undefined variable */ background-color: $(accent-color #ff0); } } diff --git a/docs/plugins/color.md b/docs/plugins/color.md index 6356c76..026b1af 100644 --- a/docs/plugins/color.md +++ b/docs/plugins/color.md @@ -1,7 +1,7 @@ Define custom color keywords. -Standard color keywords (blue, red etc.) cannot be overridden. +Standard color keywords (e.g. blue, red) cannot be overridden. ```crush @color { @@ -9,7 +9,6 @@ Standard color keywords (blue, red etc.) cannot be overridden. kolanut: #D0474E; } -/* Alternative syntax */ @color vanilla #FBF7EC; diff --git a/docs/plugins/px2em.md b/docs/plugins/px2em.md index d9e57c5..87b6284 100644 --- a/docs/plugins/px2em.md +++ b/docs/plugins/px2em.md @@ -1,8 +1,19 @@ -Functions for converting pixel values into em (px2em) or rem (px2rem) values +Functions for converting pixel values into `em` (__px2em__) or `rem` (__px2rem__) values + +For both functions the optional second argument is base font-size for calculation though usually not required when converting pixel to rem. + + +## Settings + +### px2em-base + +The default base pixel value (16px by default) used for px2em conversion. + +### px2rem-base + +The default base pixel value (16px by default) used for px2rem conversion. -For both functions the optional second argument is base font-size for calculation -(16px by default) though usually not required when converting pixel to rem. ```css font-size: px2em(11 13); diff --git a/docs/plugins/rem.md b/docs/plugins/rem.md index 7b453dd..abbafc2 100644 --- a/docs/plugins/rem.md +++ b/docs/plugins/rem.md @@ -5,11 +5,13 @@ No version of IE to date (IE <= 10) resizes text set with pixels though IE > 8 s * [Rem unit browser support](http://caniuse.com/#feat=rem) -## Conversion modes +## Settings -### rem-fallback (default) +### rem-mode -rem to px, with converted value as fallback. +Has the following possible values: + +* `rem-fallback` (default) - rem to px, with converted value as fallback. ```css font-size: 1rem; @@ -20,9 +22,7 @@ font-size: 16px; font-size: 1rem; ``` -### px-fallback - -px to rem, with original pixel value as fallback. +* `px-fallback` - px to rem, with original pixel value as fallback. ```css font-size: 16px; @@ -33,9 +33,7 @@ font-size: 16px; font-size: 1rem; ``` -### convert - -in-place px to rem conversion. +* `convert` - in-place px to rem conversion. ```css font-size: 16px; @@ -45,6 +43,6 @@ font-size: 16px; font-size: 1rem; ``` -`rem-fallback` is the default mode. To change the conversion mode set a variable named `rem__mode` with the mode name you want as its value. +### rem-all -To convert all length values, not just values of the font related properties, set a variable named `rem__all` with a value of `yes`. +To convert all length values, not just values of the font related properties, set `rem-all` with a value of `yes`. diff --git a/docs/plugins/text-align.md b/docs/plugins/text-align.md index 0ac0f70..9f7392a 100644 --- a/docs/plugins/text-align.md +++ b/docs/plugins/text-align.md @@ -1,7 +1,11 @@ Polyfill for direction sensitive text-align values, start and end. -If the 'dir' setting is 'rtl', 'start' translates to 'right' and 'end' translates to 'left'. +## Settings + +### dir + +Text direction. Value is `ltr` by default. If set to `rtl`, `start` translates to `right` and `end` translates to `left` ```crush text-align: start; From 4818bb5be7fdba9daa91067b3b1bb61b9f0ade60 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 21 Mar 2014 13:54:58 +0000 Subject: [PATCH 263/421] Updating docs --- docs/core/auto-prefixing.md | 4 +-- docs/core/selector-aliases.md | 29 +++++++++++++++++-- docs/core/variables.md | 14 ++++----- docs/plugins/canvas.md | 30 +++++++++---------- docs/plugins/color.md | 8 +++--- docs/plugins/loop.md | 24 ++++++++-------- docs/plugins/noise.md | 2 +- docs/plugins/svg.md | 54 +++++++++++++++++------------------ 8 files changed, 94 insertions(+), 71 deletions(-) diff --git a/docs/core/auto-prefixing.md b/docs/core/auto-prefixing.md index 26b74a7..7709fad 100644 --- a/docs/core/auto-prefixing.md +++ b/docs/core/auto-prefixing.md @@ -10,7 +10,7 @@ In some cases (e.g. CSS3 gradients) final syntax is incompatible with older pref ```crush .foo { - background: linear-gradient(to right, red, white); + background: linear-gradient(to right, red, white); } ``` @@ -26,7 +26,7 @@ In some cases (e.g. CSS3 gradients) final syntax is incompatible with older pref ```crush @keyframes bounce { - 50% {transform: scale(1.4);} + 50% { transform: scale(1.4); } } ``` diff --git a/docs/core/selector-aliases.md b/docs/core/selector-aliases.md index f373fcd..19bada8 100644 --- a/docs/core/selector-aliases.md +++ b/docs/core/selector-aliases.md @@ -14,7 +14,8 @@ They're defined with the `@selector-alias` directive, and can be used anywhere @selector-alias heading :any(h1, h2, h3, h4, h5, h6); @selector-alias radio input[type="radio"]; @selector-alias hocus :any(:hover, :focus); -/* Selector alias with arguments */ + +/* Selector aliases with arguments */ @selector-alias class-prefix :any([class^="#(0)"], [class*=" #(0)"]); @selector-alias col :class-prefix(-col); @@ -31,7 +32,7 @@ They're defined with the `@selector-alias` directive, and can be used anywhere } p a:hocus { - text-decoration: none; + text-decoration: none; } ``` @@ -53,6 +54,28 @@ input[type="radio"] { p a:hover, p a:focus { - text-decoration: none; + text-decoration: none; +} +``` + +## Selector splatting + +Selector splats are a special kind of selector alias that expand using passed arguments. + +```crush +@selector-splat input input[type="#(text)"]; + +form :input(time, text, url, email, number) { + border: 1px solid; +} +``` + +```css +form input[type="time"], +form input[type="text"], +form input[type="url"], +form input[type="email"], +form input[type="number"] { + border: 1px solid; } ``` diff --git a/docs/core/variables.md b/docs/core/variables.md index 1496c20..bbef970 100644 --- a/docs/core/variables.md +++ b/docs/core/variables.md @@ -44,15 +44,15 @@ Sections of CSS can be included and excluded on the basis of variable existence @define foo #f00; @ifdefine foo { - p { - color: $(foo); - } + p { + color: $(foo); + } } p { - font-size: 12px; - @ifdefine not foo { - line-height: 1.5; - } + font-size: 12px; + @ifdefine not foo { + line-height: 1.5; + } } ``` diff --git a/docs/plugins/canvas.md b/docs/plugins/canvas.md index 284dd81..cc3414f 100644 --- a/docs/plugins/canvas.md +++ b/docs/plugins/canvas.md @@ -6,13 +6,13 @@ Requires the GD image library bundled with PHP. ```crush /* Create square semi-opaque png. */ @canvas foo { - width: 50; - height: 50; - fill: rgba(255, 0, 0, .5); + width: 50; + height: 50; + fill: rgba(255, 0, 0, .5); } body { - background: white canvas(foo); + background: white canvas(foo); } ``` @@ -22,20 +22,20 @@ body { /* White to transparent east facing gradient with 10px margin and background fill. */ @canvas horz-gradient { - width: #(0); - height: 150; - fill: canvas-linear-gradient(to right, #(1 white), #(2 rgba(255,255,255,0))); - background-fill: powderblue; - margin: 10; + width: #(0); + height: 150; + fill: canvas-linear-gradient(to right, #(1 white), #(2 rgba(255,255,255,0))); + background-fill: powderblue; + margin: 10; } /* Rectangle 300x150. */ body { - background: canvas(horz-gradient, 300); + background: canvas(horz-gradient, 300); } /* Flipped gradient, using canvas-data() to generate a data URI. */ .bar { - background: canvas-data(horz-gradient, 100, rgba(255,255,255,0), white); + background: canvas-data(horz-gradient, 100, rgba(255,255,255,0), white); } ``` @@ -44,12 +44,12 @@ body { ```crush /* Google logo resized to 400px width and given a sepia effect. */ @canvas sepia { - src: url("https://www.google.co.uk/images/srpr/logo4w.png"); - width: 400; - canvas-filter: greyscale() colorize(45, 45, 0); + src: url("https://www.google.co.uk/images/srpr/logo4w.png"); + width: 400; + canvas-filter: greyscale() colorize(45, 45, 0); } .bar { - background: canvas(sepia); + background: canvas(sepia); } ``` diff --git a/docs/plugins/color.md b/docs/plugins/color.md index 026b1af..8dba984 100644 --- a/docs/plugins/color.md +++ b/docs/plugins/color.md @@ -5,8 +5,8 @@ Standard color keywords (e.g. blue, red) cannot be overridden. ```crush @color { - acme-blue: s-adjust(blue -10); - kolanut: #D0474E; + acme-blue: s-adjust(blue -10); + kolanut: #D0474E; } @color vanilla #FBF7EC; @@ -14,7 +14,7 @@ Standard color keywords (e.g. blue, red) cannot be overridden. /* Usage is the same as with native color keywords */ p { - color: vanilla; - border: 1px solid acme-blue; + color: vanilla; + border: 1px solid acme-blue; } ``` diff --git a/docs/plugins/loop.md b/docs/plugins/loop.md index eed8611..2f7b169 100644 --- a/docs/plugins/loop.md +++ b/docs/plugins/loop.md @@ -3,9 +3,9 @@ For...in loops with lists and generator functions. ```crush @for fruit in apple, orange, pear { - .#(fruit) { - background-image: url("http://github.com/images/#(fruit).jpg"); - } + .#(fruit) { + background-image: url("http://github.com/images/#(fruit).jpg"); + } } ``` @@ -17,11 +17,11 @@ For...in loops with lists and generator functions. ```crush @for base in range(2, 24) { - @for i in range(1, #(base)) { - .grid-#(i)-of-#(base) { - width: (#(i) / #(base) * 100)%; - } + @for i in range(1, #(base)) { + .grid-#(i)-of-#(base) { + width: math(#(i) / #(base) * 100, %); } + } } ``` @@ -39,11 +39,11 @@ For...in loops with lists and generator functions. /* The last argument to color-range() is an integer specifying how many transition colors to generate between the color arguments. */ - @for color in color-range(powderblue, deeppink, a-adjust(yellow, -80), 5) { - .foo-#(loop.counter) { - background-color: #(color); - } - } +@for color in color-range(powderblue, deeppink, a-adjust(yellow, -80), 5) { + .foo-#(loop.counter) { + background-color: #(color); + } +} ``` ```css diff --git a/docs/plugins/noise.md b/docs/plugins/noise.md index ab1e76e..8b3a1c4 100644 --- a/docs/plugins/noise.md +++ b/docs/plugins/noise.md @@ -9,7 +9,7 @@ Supported in any browser that supports SVG filters (IE > 9 and most other browse Both functions work in the same way, the difference being `noise()` uses feTurbulence type 'fractalNoise' and `turbulence()` uses feTurbulence type 'turbulence'. ```syntax -( +noise/turbulence( [ fill-color || size ]? [, frequency octaves? sharpness? ]? [, blend-mode || fade ]? diff --git a/docs/plugins/svg.md b/docs/plugins/svg.md index 35fcc23..facec0d 100644 --- a/docs/plugins/svg.md +++ b/docs/plugins/svg.md @@ -4,22 +4,22 @@ Define and embed simple SVG elements, paths and effects inside CSS ```crush @svg foo { - type: star; - star-points: #(0 5); - radius: 100 50; - margin: 20; - stroke: black; - fill: red; - fill-opacity: .5; + type: star; + star-points: #(0 5); + radius: 100 50; + margin: 20; + stroke: black; + fill: red; + fill-opacity: .5; } /* Embed SVG with svg() function (generates an svg file). */ body { - background: svg(foo); + background: svg(foo); } /* As above but a 3 point star creating a data URI instead of a file. */ body { - background: svg-data(foo, 3); + background: svg-data(foo, 3); } ``` @@ -28,12 +28,12 @@ body { ```crush /* Using path data and stroke styles to create a plus sign. */ @svg plus { - d: "M0,5 h10 M5,0 v10"; - width: 10; - height: 10; - stroke: white; - stroke-linecap: round; - stroke-width: 2; + d: "M0,5 h10 M5,0 v10"; + width: 10; + height: 10; + stroke: white; + stroke-linecap: round; + stroke-width: 2; } ``` @@ -43,12 +43,12 @@ body { ```crush /* Skewed circle with radial gradient fill and drop shadow. */ @svg circle { - type: circle; - transform: skewX(30); - diameter: 60; - margin: 20; - fill: svg-radial-gradient(at top right, gold 50%, red); - drop-shadow: 2 2 0 rgba(0,0,0,1); + type: circle; + transform: skewX(30); + diameter: 60; + margin: 20; + fill: svg-radial-gradient(at top right, gold 50%, red); + drop-shadow: 2 2 0 rgba(0,0,0,1); } ``` @@ -58,12 +58,12 @@ body { /* 8-sided polygon with an image fill. Note: images usually have to be converted to data URIs, see known issues below. */ @svg pattern { - type: polygon; - sides: 8; - diameter: 180; - margin: 20; - fill: pattern(data-uri(kitten.jpg), scale(1) translate(-100 0)); - fill-opacity: .8; + type: polygon; + sides: 8; + diameter: 180; + margin: 20; + fill: pattern(data-uri(kitten.jpg), scale(1) translate(-100 0)); + fill-opacity: .8; } ``` From 142aaa44c18fa715bc8aba4c256854e69954b0b0 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 21 Mar 2014 14:04:12 +0000 Subject: [PATCH 264/421] Updated changelog and version information. --- CHANGELOG.md | 8 ++++---- LICENSE.txt | 2 +- lib/CssCrush/Crush.php | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f02e96..8090b5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,12 @@ -### 2.1.0 (2014-?-?) +### 2.1.0 (2014-03-21) * Added HHVM support (HHVM >= 2.4) * Added Travis CI support. * Added custom color keywords plugin. * Added text-align plugin for polyfilling the direction sensitive text-align values, start and end. -* Added selector `splat` aliases, which expand based on arguments. -* Added settings interface for plugins and CSS environment. Old variable based settings (as used in rem and px2em plugins) is now deprecated. -* Added library docs at the repository level, written in markdown. +* Added selector splat aliases which expand based on arguments. +* Added settings interface for plugins and CSS environment. Old variable based settings (as used in rem and px2em plugins) are now deprecated. +* Added library docs to repository. * Added unit argument to the math function. * Deprecated bare parens math e.g. `()` due to their use in developing CSS specs. * Removed `-ms-` gradient aliases. diff --git a/LICENSE.txt b/LICENSE.txt index 51cfed7..8feb8f2 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2010-2013 Pete Boere +Copyright (c) 2010-2014 Pete Boere 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 diff --git a/lib/CssCrush/Crush.php b/lib/CssCrush/Crush.php index 128dbf6..5087ac8 100644 --- a/lib/CssCrush/Crush.php +++ b/lib/CssCrush/Crush.php @@ -8,7 +8,7 @@ class Crush { - const VERSION = '2.1.0-beta'; + const VERSION = '2.1.0'; // Global settings. public static $config; From e5e5016e85bf907778190a170ba81dc11370437f Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 21 Mar 2014 19:58:09 +0000 Subject: [PATCH 265/421] Removing instances of shorthand math notation from docs. Some docs tidying up. --- docs/api/functions.md | 2 +- docs/core/block-nesting.md | 4 ++-- docs/core/functions/data-uri.md | 2 +- docs/core/functions/this.md | 1 - docs/plugins/canvas.md | 2 +- docs/plugins/noise.md | 8 ++++---- docs/plugins/svg-gradients.md | 9 ++++++--- 7 files changed, 15 insertions(+), 13 deletions(-) diff --git a/docs/api/functions.md b/docs/api/functions.md index f6dbbae..0951596 100644 --- a/docs/api/functions.md +++ b/docs/api/functions.md @@ -34,7 +34,7 @@ Compile a raw string of CSS string and return it. ## csscrush_stat() -Retrieve statistics from the most recent compiled file. Current available stats: selector_count, rule_count, compile_time and errors. +Retrieve statistics from the most recent compiled file. Available stats include `selector_count`, `rule_count` and `compile_time`. `csscrush_stat()` diff --git a/docs/core/block-nesting.md b/docs/core/block-nesting.md index 3a34837..12254d5 100644 --- a/docs/core/block-nesting.md +++ b/docs/core/block-nesting.md @@ -15,10 +15,10 @@ Note use of the parent selector `&`: font-size: 110%; } } - &.blue { + &.blue { color: powderblue; } - .no-js & { + .no-js & { max-width: 1024px; } } diff --git a/docs/core/functions/data-uri.md b/docs/core/functions/data-uri.md index 0244213..5677bba 100644 --- a/docs/core/functions/data-uri.md +++ b/docs/core/functions/data-uri.md @@ -26,5 +26,5 @@ background: silver data-uri(../images/stripe.png); ``` ```css -background: silver url(data:<img-data>); +background: silver url(data:); ``` \ No newline at end of file diff --git a/docs/core/functions/this.md b/docs/core/functions/this.md index d7ba2b8..9f79a01 100644 --- a/docs/core/functions/this.md +++ b/docs/core/functions/this.md @@ -23,7 +23,6 @@ The referenced property value, or the fallback if it has not been set. ```css .foo { width: this( height ); - margin-top: -( this( height ) / 2 )em; height: 100em; } ``` diff --git a/docs/plugins/canvas.md b/docs/plugins/canvas.md index cc3414f..df6994d 100644 --- a/docs/plugins/canvas.md +++ b/docs/plugins/canvas.md @@ -44,7 +44,7 @@ body { ```crush /* Google logo resized to 400px width and given a sepia effect. */ @canvas sepia { - src: url("https://www.google.co.uk/images/srpr/logo4w.png"); + src: url(http://www.google.com/images/logo.png); width: 400; canvas-filter: greyscale() colorize(45, 45, 0); } diff --git a/docs/plugins/noise.md b/docs/plugins/noise.md index 8b3a1c4..02e8c20 100644 --- a/docs/plugins/noise.md +++ b/docs/plugins/noise.md @@ -10,10 +10,10 @@ Both functions work in the same way, the difference being `noise()` uses feTurbu ```syntax noise/turbulence( - [ fill-color || size ]? - [, frequency octaves? sharpness? ]? - [, blend-mode || fade ]? - [, color-filter color-filter-value ]? + [ || ]? + [, ? ? ]? + [, || ]? + [, ]? ) ``` diff --git a/docs/plugins/svg-gradients.md b/docs/plugins/svg-gradients.md index a52a733..66ff410 100644 --- a/docs/plugins/svg-gradients.md +++ b/docs/plugins/svg-gradients.md @@ -7,8 +7,9 @@ Primarily useful for supporting Internet Explorer 9. Syntax is the same as [linear-gradient()](http://dev.w3.org/csswg/css3-images/#linear-gradient) -`svg-linear-gradent( [ | to ,]? [, ]+ )` - +```syntax +svg-linear-gradent( [ | to ,]? [, ]+ ) +``` ### Returns @@ -28,7 +29,9 @@ background-image: svg-linear-gradient( 35deg, red, gold 20%, powderblue ); Syntax is similar to but more limited than [radial-gradient()](http://dev.w3.org/csswg/css3-images/#radial-gradient) -`svg-radial-gradent( [ | at ,]? [, ]+ )` +```syntax +svg-radial-gradent( [ | at ,]? [, ]+ ) +``` ### Returns From b4c0c106fc1711f8e36b27ab2426958cfa46a0bc Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 28 Mar 2014 10:14:46 +0000 Subject: [PATCH 266/421] Allowing empty arguments in selector groupings for more expansion possibilities. Modified `Util::splitDelimList()` to use an array of arguments. --- lib/CssCrush/Functions.php | 8 ++++++-- lib/CssCrush/SelectorList.php | 9 ++++++--- lib/CssCrush/Util.php | 20 +++++++++++++------- tests/unit/CssCrush/UtilTest.php | 3 ++- 4 files changed, 27 insertions(+), 13 deletions(-) diff --git a/lib/CssCrush/Functions.php b/lib/CssCrush/Functions.php index 38694de..b5e3ccc 100644 --- a/lib/CssCrush/Functions.php +++ b/lib/CssCrush/Functions.php @@ -128,8 +128,12 @@ public function apply($str, $callbacks = null, \stdClass $context = null) public static function parseArgs($input, $allowSpaceDelim = false) { - return Util::splitDelimList( - $input, ($allowSpaceDelim ? '\s*[,\s]\s*' : ',')); + $options = array(); + if ($allowSpaceDelim) { + $options['regex'] = '\s*[,\s]\s*'; + } + + return Util::splitDelimList($input, $options); } // Intended as a quick arg-list parse for function that take up-to 2 arguments diff --git a/lib/CssCrush/SelectorList.php b/lib/CssCrush/SelectorList.php index 621e031..185af52 100644 --- a/lib/CssCrush/SelectorList.php +++ b/lib/CssCrush/SelectorList.php @@ -50,10 +50,13 @@ public function expand() list($full_match, $full_match_offset) = $m[0]; $before = substr($selector_string, 0, $full_match_offset); $after = substr($selector_string, strlen($full_match) + $full_match_offset); - $selectors = array(); - foreach (Util::splitDelimList($m['parens_content'][0]) as $segment) { - $selectors["$before$segment$after"] = true; + + // Allowing empty strings for more expansion possibilities. + foreach (Util::splitDelimList($m['parens_content'][0], array('allow_empty_strings' => true)) as $segment) { + if ($selector = trim("$before$segment$after")) { + $selectors[$selector] = true; + } } return $selectors; diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php index af55324..3324b98 100644 --- a/lib/CssCrush/Util.php +++ b/lib/CssCrush/Util.php @@ -129,13 +129,18 @@ public static function normalizeWhiteSpace($str) return preg_replace($find, $replace, $str); } - public static function splitDelimList($str, $delim = ',') + public static function splitDelimList($str, $options = array()) { - $do_preg_split = strlen($delim) > 1; + extract($options + array( + 'delim' => ',', + 'regex' => false, + 'allow_empty_strings' => false, + )); + $str = trim($str); - if (! $do_preg_split && strpos($str, $delim) === false) { - return strlen($str) ? array($str) : array(); + if (! $regex && strpos($str, $delim) === false) { + return ! $allow_empty_strings && ! strlen($str) ? array() : array($str); } if ($match_count = preg_match_all(Regex::$patt->parens, $str, $matches)) { @@ -146,7 +151,7 @@ public static function splitDelimList($str, $delim = ',') $str = str_replace($matches[0], $keys, $str); } - $list = $do_preg_split ? preg_split('~' . $delim . '~', $str) : explode($delim, $str); + $list = $regex ? preg_split('~' . $regex . '~', $str) : explode($delim, $str); if ($match_count) { foreach ($list as &$value) { @@ -154,8 +159,9 @@ public static function splitDelimList($str, $delim = ',') } } - // Trim items and remove empty strings before returning. - return array_filter(array_map('trim', $list), 'strlen'); + $list = array_map('trim', $list); + + return ! $allow_empty_strings ? array_filter($list, 'strlen') : $list; } public static function getLinkBetweenPaths($path1, $path2, $directories = true) diff --git a/tests/unit/CssCrush/UtilTest.php b/tests/unit/CssCrush/UtilTest.php index e6e5484..e86a51c 100644 --- a/tests/unit/CssCrush/UtilTest.php +++ b/tests/unit/CssCrush/UtilTest.php @@ -72,7 +72,8 @@ public function testNormalizeWhiteSpace() public function testSplitDelimList() { $this->assertEquals(array('foo(1,2)','3','4'), Util::splitDelimList("foo(1,2), 3,4")); - $this->assertEquals(array(), Util::splitDelimList(" ; ; ", ';')); + $this->assertEquals(array(), Util::splitDelimList(" ; ; ", array('delim' => ';'))); + $this->assertEquals(array('', ''), Util::splitDelimList(" , ", array('allow_empty_strings' => true))); } public function testGetLinkBetweenPaths() From ccc04c10a81d389ca991934b98154c2ab60d0c9a Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 31 Mar 2014 10:10:17 +0100 Subject: [PATCH 267/421] Simplifying variable parse/excecute code. --- lib/CssCrush/Process.php | 62 +++++++++++++++++++--------------------- lib/CssCrush/Regex.php | 1 - lib/CssCrush/Stream.php | 4 +-- 3 files changed, 31 insertions(+), 36 deletions(-) diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 2754bc2..e0feea7 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -380,70 +380,67 @@ protected function captureVars() // Place variables referenced inside variables. foreach ($this->vars as &$value) { - $value = preg_replace_callback(Regex::$patt->varFunction, 'CssCrush\Process::cb_placeVars', $value); + $this->placeVars($value); } } protected function placeAllVars() { // Place variables in main stream. - self::placeVars($this->stream->raw); + $this->placeVars($this->stream->raw); - $raw_tokens =& $this->tokens->store; + $rawTokens =& $this->tokens->store; // Repeat above steps for variables embedded in string tokens. - foreach ($raw_tokens->s as $label => &$value) { - self::placeVars($value); + foreach ($rawTokens->s as $label => &$value) { + $this->placeVars($value); } // Repeat above steps for variables embedded in URL tokens. - foreach ($raw_tokens->u as $label => $url) { - if (! $url->isData && self::placeVars($url->value)) { + foreach ($rawTokens->u as $label => $url) { + if (! $url->isData && $this->placeVars($url->value)) { // Re-evaluate $url->value if anything has been interpolated. $url->evaluate(); } } } - static protected function placeVars(&$value) + protected function placeVars(&$value) { - static $var_function; - if (! $var_function) { - $var_function = new Functions(array('$' => function ($raw_args) { - list($name, $default_value) = Functions::parseArgsSimple($raw_args); - if (isset(Crush::$process->vars[$name])) { - return Crush::$process->vars[$name]; - } - else { - return $default_value; - } - }), '~(\$)\(~'); + static $varFunction, $varFunctionSimple; + if (! $varFunction) { + $varFunctionSimple = Regex::make('~\$\( \s* ({{ ident }}) \s* \)~xS'); + $varFunction = new Functions(array('$' => function ($rawArgs) { + list($name, $defaultValue) = Functions::parseArgsSimple($rawArgs); + if (isset(Crush::$process->vars[$name])) { + return Crush::$process->vars[$name]; + } + else { + return $defaultValue; + } + }), '~(\$)\(~'); } // Variables with no default value. - $value = preg_replace_callback(Regex::$patt->varFunction, - 'CssCrush\Process::cb_placeVars', $value, -1, $vars_placed); + $value = preg_replace_callback($varFunctionSimple, function ($m) { + $varName = $m[1]; + if (isset(Crush::$process->vars[$varName])) { + return Crush::$process->vars[$varName]; + } + }, $value, -1, $varsPlaced); // Variables with default value. if (strpos($value, '$(') !== false) { // Assume at least one replace. - $vars_placed = true; + $varsPlaced = true; // Variables may be nested so need to apply full function parsing. - $value = $var_function->apply($value); + $value = $varFunction->apply($value); } // If we know replacements have been made we may want to update $value. e.g URL tokens. - return $vars_placed; - } - - static protected function cb_placeVars($m) - { - $var_name = $m[1]; - if (isset(Crush::$process->vars[$var_name])) { - return Crush::$process->vars[$var_name]; - } + return $varsPlaced; } @@ -454,7 +451,6 @@ protected function resolveSettings() { $captured_settings = $this->stream->captureDirectives('@settings', array('singles' => true)); - // Like variables, settings passed via options override settings defined in CSS. $this->settings = new Settings($this->options->settings + $captured_settings); } diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php index 81a6544..806ee77 100644 --- a/lib/CssCrush/Regex.php +++ b/lib/CssCrush/Regex.php @@ -68,7 +68,6 @@ public static function init() // Functions. $patt->functionTest = Regex::make('~{{ LB }} (?{{ ident }}) \(~xS'); - $patt->varFunction = Regex::make('~\$\( \s* ({{ ident }}) \s* \)~xS'); $patt->thisFunction = Regex::makeFunctionPatt(array('this')); // Strings and comments. diff --git a/lib/CssCrush/Stream.php b/lib/CssCrush/Stream.php index e1ccfa8..5b6ae3e 100644 --- a/lib/CssCrush/Stream.php +++ b/lib/CssCrush/Stream.php @@ -130,10 +130,10 @@ public function captureDirectives($directive, $parse_options = array()) ); if ($parse_options['singles']) { - $patt = Regex::make('~@' . $directive . '(?:\s*{{ block }}|\s+(?{{ ident }})\s+(?[^;]+)\s*;)~iS'); + $patt = Regex::make('~@(?i)' . $directive . '(?-i)(?:\s*{{ block }}|\s+(?{{ ident }})\s+(?[^;]+)\s*;)~S'); } else { - $patt = Regex::make('~@' . $directive . '\s*{{ block }}~iS'); + $patt = Regex::make('~@(?i)' . $directive . '(?-i)\s*{{ block }}~S'); } $captured_directives = array(); From 69730246137ab55275f0d4b615330f1767e7534e Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 1 Apr 2014 10:18:56 +0100 Subject: [PATCH 268/421] Refactoring out bare-paren math shorthand. --- lib/CssCrush/Functions.php | 31 ++++++++----------------- lib/CssCrush/Process.php | 2 +- lib/CssCrush/Regex.php | 10 +------- plugins/canvas.php | 2 +- plugins/svg.php | 2 +- tests/unit/CssCrush/DeclarationTest.php | 2 +- tests/unit/CssCrush/RegexTest.php | 7 ++---- 7 files changed, 17 insertions(+), 39 deletions(-) diff --git a/lib/CssCrush/Functions.php b/lib/CssCrush/Functions.php index b5e3ccc..df44613 100644 --- a/lib/CssCrush/Functions.php +++ b/lib/CssCrush/Functions.php @@ -53,7 +53,6 @@ public function setPattern($use_builtin = false) $options = $this->patternOptions; if ($use_builtin) { $this->register = self::$builtins + $this->register; - $options += array('bare_paren' => true); } $this->pattern = Regex::makeFunctionPatt(array_keys($this->register), $options); } @@ -80,43 +79,33 @@ public function apply($str, $callbacks = null, \stdClass $context = null) while ($match = array_pop($matches)) { - $offset = $match[0][1]; + list($function, $offset) = $match['function']; if (! preg_match(Regex::$patt->parens, $str, $parens, PREG_OFFSET_CAPTURE, $offset)) { continue; } - // No function name default to math expression. - // Store the raw function name match. - $raw_fn_name = isset($match[1]) ? strtolower($match[1][0]) : ''; - $fn_name = $raw_fn_name ? $raw_fn_name : 'math'; - if ('-' === $fn_name) { - $fn_name = 'math'; - } - $opening_paren = $parens[0][1]; $closing_paren = $opening_paren + strlen($parens[0][0]); // Get the function arguments. $raw_args = trim($parens['parens_content'][0]); - // Workaround the signs. - $before_operator = '-' === $raw_fn_name ? '-' : ''; + // Update the context function identifier. + $context->function = $function; - $func_returns = ''; - $context->function = $fn_name; + $returns = ''; // Use override callback if one is specified. - if (isset($callbacks[$fn_name])) { - $func_returns = $callbacks[$fn_name]($raw_args, $context); + if (isset($callbacks[$function])) { + $returns = $callbacks[$function]($raw_args, $context); } - elseif (isset($this->register[$fn_name])) { - $func = $this->register[$fn_name]; - $func_returns = $func($raw_args, $context); + elseif (isset($this->register[$function])) { + $fn = $this->register[$function]; + $returns = $fn($raw_args, $context); } - // Splice in the function result. - $str = substr_replace($str, "$before_operator$func_returns", $offset, $closing_paren - $offset); + $str = substr_replace($str, $returns, $offset, $closing_paren - $offset); } return $str; diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index e0feea7..60a6b01 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -418,7 +418,7 @@ protected function placeVars(&$value) else { return $defaultValue; } - }), '~(\$)\(~'); + }), '~(?\$)\(~'); } // Variables with no default value. diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php index 806ee77..adfb5db 100644 --- a/lib/CssCrush/Regex.php +++ b/lib/CssCrush/Regex.php @@ -142,14 +142,6 @@ public static function matchAll($patt, $subject, $offset = 0) public static function makeFunctionPatt($list, $options = array()) { - // Bare parens. - $question = ''; - if (! empty($options['bare_paren'])) { - $question = '?'; - // Signing on math bare parens. - $list[] = '-'; - } - // Templating func. $template = ''; if (! empty($options['templating'])) { @@ -158,7 +150,7 @@ public static function makeFunctionPatt($list, $options = array()) $flat_list = implode('|', array_map('preg_quote', $list)); - return Regex::make("~($template{{ LB }}(?:$flat_list)$question)\(~iS"); + return Regex::make("~(?$template{{ LB }}(?:$flat_list))\(~iS"); } } diff --git a/plugins/canvas.php b/plugins/canvas.php index e29fc09..6e7113a 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -338,7 +338,7 @@ function canvas_apply_css_funcs($canvas) { $fill_functions = new Functions(array('canvas-linear-gradient' => 'CssCrush\canvas_fn_linear_gradient')); $generic_register = array_diff_key(Crush::$process->functions->register, $fill_functions->register); - $generic_functions = new Functions($generic_register, null, array('bare_paren' => true)); + $generic_functions = new Functions($generic_register); $filter_functions = new Functions(array( 'contrast' => 'CssCrush\canvas_fn_filter', diff --git a/plugins/svg.php b/plugins/svg.php index aaab4df..73c751f 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -622,7 +622,7 @@ function svg_apply_css_funcs($element, &$raw_data) { $fill_functions = new Functions($fill_register); $generic_register = array_diff_key(Crush::$process->functions->register, $fill_register); - $generic_functions = new Functions($generic_register, null, array('bare_paren' => true)); + $generic_functions = new Functions($generic_register); } foreach ($raw_data as $property => &$value) { diff --git a/tests/unit/CssCrush/DeclarationTest.php b/tests/unit/CssCrush/DeclarationTest.php index 4a1e7b8..d41ab45 100644 --- a/tests/unit/CssCrush/DeclarationTest.php +++ b/tests/unit/CssCrush/DeclarationTest.php @@ -15,7 +15,7 @@ class DeclarationTest extends \PHPUnit_Framework_TestCase public function setUp() { $this->process = bootstrap_process(array('minify' => false)); - $this->rule = new Rule('.foo', '-fOo-BAR: (10 + 10)px !important'); + $this->rule = new Rule('.foo', '-fOo-BAR: math(10 + 10, px) !important'); $this->declaration = new Declaration('-fOo-BAR', 'baz !important'); } diff --git a/tests/unit/CssCrush/RegexTest.php b/tests/unit/CssCrush/RegexTest.php index 8d3ef73..c188dff 100644 --- a/tests/unit/CssCrush/RegexTest.php +++ b/tests/unit/CssCrush/RegexTest.php @@ -17,13 +17,10 @@ public function testMake() public function testMakeFunctionPatt() { $patt = Regex::makeFunctionPatt(array('foo', 'bar')); - $this->assertEquals('~((? true)); - $this->assertEquals('~((?assertEquals('~(?(? true)); - $this->assertEquals('~(#|(?assertEquals('~(?#|(? Date: Thu, 3 Apr 2014 13:44:25 +0100 Subject: [PATCH 269/421] Some refactoring in the Functions class. --- lib/CssCrush/Declaration.php | 33 +++++++--------- lib/CssCrush/Functions.php | 55 ++++++++++++++++++--------- lib/CssCrush/Process.php | 2 +- lib/CssCrush/Regex.php | 15 +------- lib/CssCrush/Template.php | 29 +++++++------- plugins/canvas.php | 21 +++++----- plugins/svg.php | 17 ++++----- tests/unit/CssCrush/FunctionsTest.php | 20 ++++++++++ tests/unit/CssCrush/RegexTest.php | 9 ----- 9 files changed, 103 insertions(+), 98 deletions(-) create mode 100644 tests/unit/CssCrush/FunctionsTest.php diff --git a/lib/CssCrush/Declaration.php b/lib/CssCrush/Declaration.php index 3a296e6..14dbf99 100644 --- a/lib/CssCrush/Declaration.php +++ b/lib/CssCrush/Declaration.php @@ -23,8 +23,7 @@ public function __construct($property, $value, $contextIndex = 0) // Normalize the property name. $property = strtolower($property); - // Test for escape tilde. - if ($skip = strpos($property, '~') === 0) { + if ($this->skip = strpos($property, '~') === 0) { $property = substr($property, 1); } @@ -42,7 +41,7 @@ public function __construct($property, $value, $contextIndex = 0) // Check for !important. if (($important = stripos($value, '!important')) !== false) { $value = rtrim(substr($value, 0, $important)); - $important = true; + $this->important = true; } Crush::$process->hooks->run('declaration_preprocess', array('property' => &$property, 'value' => &$value)); @@ -57,8 +56,6 @@ public function __construct($property, $value, $contextIndex = 0) $this->vendor = $vendor; $this->index = $contextIndex; $this->value = $value; - $this->skip = $skip; - $this->important = $important; } public function __toString() @@ -78,41 +75,39 @@ public function __toString() Execute functions on value. Index functions. */ - public function process($parent_rule) + public function process($parentRule) { - static $this_function; - if (! $this_function) { - $this_function = new Functions(array('this' => 'CssCrush\fn__this')); + static $thisFunction; + if (! $thisFunction) { + $thisFunction = new Functions(array('this' => 'CssCrush\fn__this')); } if (! $this->skip) { - // this() function needs to be called exclusively because it's self referencing. + // this() function needs to be called exclusively because it is self referencing. $context = (object) array( - 'rule' => $parent_rule, + 'rule' => $parentRule ); - $this->value = $this_function->apply($this->value, null, $context); + $this->value = $thisFunction->apply($this->value, $context); - $parent_rule->declarations->data += array($this->property => $this->value); + $parentRule->declarations->data += array($this->property => $this->value); $context = (object) array( - 'rule' => $parent_rule, + 'rule' => $parentRule, 'property' => $this->property ); - $this->value = Crush::$process->functions->apply($this->value, null, $context); + $this->value = Crush::$process->functions->apply($this->value, $context); } - // Trim whitespace that may have been introduced by functions. + // Whitespace may have been introduced by functions. $this->value = trim($this->value); - // Value may now be empty. if ($this->value === '') { $this->valid = false; return; } - // Store value as data on the parent rule. - $parent_rule->declarations->queryData[$this->property] = $this->value; + $parentRule->declarations->queryData[$this->property] = $this->value; $this->indexFunctions(); } diff --git a/lib/CssCrush/Functions.php b/lib/CssCrush/Functions.php index df44613..2470ca2 100644 --- a/lib/CssCrush/Functions.php +++ b/lib/CssCrush/Functions.php @@ -31,11 +31,9 @@ class Functions protected $patternOptions; - public function __construct($register = array(), $pattern = null, $pattern_options = array()) + public function __construct($register = array()) { $this->register = $register; - $this->pattern = $pattern; - $this->patternOptions = $pattern_options; } public function add($name, $callback) @@ -48,16 +46,16 @@ public function remove($name) unset($this->register[$name]); } - public function setPattern($use_builtin = false) + public function setPattern($useBuiltins = false) { - $options = $this->patternOptions; - if ($use_builtin) { + if ($useBuiltins) { $this->register = self::$builtins + $this->register; } - $this->pattern = Regex::makeFunctionPatt(array_keys($this->register), $options); + + $this->pattern = Functions::makePattern(array_keys($this->register)); } - public function apply($str, $callbacks = null, \stdClass $context = null) + public function apply($str, \stdClass $context = null) { if (strpos($str, '(') === false) { return $str; @@ -71,10 +69,6 @@ public function apply($str, $callbacks = null, \stdClass $context = null) return $str; } - if (! $context) { - $context = new \stdClass(); - } - $matches = Regex::matchAll($this->pattern, $str); while ($match = array_pop($matches)) { @@ -92,15 +86,12 @@ public function apply($str, $callbacks = null, \stdClass $context = null) $raw_args = trim($parens['parens_content'][0]); // Update the context function identifier. - $context->function = $function; + if ($context) { + $context->function = $function; + } $returns = ''; - - // Use override callback if one is specified. - if (isset($callbacks[$function])) { - $returns = $callbacks[$function]($raw_args, $context); - } - elseif (isset($this->register[$function])) { + if (isset($this->register[$function])) { $fn = $this->register[$function]; $returns = $fn($raw_args, $context); } @@ -131,6 +122,32 @@ public static function parseArgsSimple($input) { return preg_split(Regex::$patt->argListSplit, $input, 2); } + + public static function makePattern($functionNames) + { + $idents = array(); + $nonIdents = array(); + + foreach ($functionNames as $functionName) { + if (preg_match(Regex::$patt->ident, $functionName[0])) { + $idents[] = preg_quote($functionName); + } + else { + $nonIdents[] = preg_quote($functionName); + } + } + + $flatList = ''; + if (! $idents) { + $flatList = implode('|', $nonIdents); + } + else { + $idents = '{{ LB }}(?:' . implode('|', $idents) . ')'; + $flatList = $nonIdents ? '(?:' . implode('|', $nonIdents) . "|$idents)" : $idents; + } + + return Regex::make("~(?$flatList)\(~iS"); + } } diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 60a6b01..ac6f4ad 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -418,7 +418,7 @@ protected function placeVars(&$value) else { return $defaultValue; } - }), '~(?\$)\(~'); + })); } // Variables with no default value. diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php index adfb5db..4b3c3b2 100644 --- a/lib/CssCrush/Regex.php +++ b/lib/CssCrush/Regex.php @@ -68,7 +68,7 @@ public static function init() // Functions. $patt->functionTest = Regex::make('~{{ LB }} (?{{ ident }}) \(~xS'); - $patt->thisFunction = Regex::makeFunctionPatt(array('this')); + $patt->thisFunction = Functions::makePattern(array('this')); // Strings and comments. $patt->string = '~(\'|")(?:\\\\\1|[^\1])*?\1~xS'; @@ -139,19 +139,6 @@ public static function matchAll($patt, $subject, $offset = 0) return $count ? $matches : array(); } - - public static function makeFunctionPatt($list, $options = array()) - { - // Templating func. - $template = ''; - if (! empty($options['templating'])) { - $template = '#|'; - } - - $flat_list = implode('|', array_map('preg_quote', $list)); - - return Regex::make("~(?$template{{ LB }}(?:$flat_list))\(~iS"); - } } Regex::init(); diff --git a/lib/CssCrush/Template.php b/lib/CssCrush/Template.php index eb0c5cc..d549446 100644 --- a/lib/CssCrush/Template.php +++ b/lib/CssCrush/Template.php @@ -21,10 +21,9 @@ class Template public function __construct($str) { - static $arg_patt, $template_functions; - if (! $arg_patt) { - $arg_patt = Regex::makeFunctionPatt(array('arg'), array('templating' => true)); - $template_functions = new Functions(null, $arg_patt); + static $templateFunctions; + if (! $templateFunctions) { + $templateFunctions = new Functions(); } $str = Template::unTokenize($str); @@ -32,7 +31,7 @@ public function __construct($str) // Parse all arg function calls in the passed string, // callback creates default values. $self = $this; - $capture_callback = function ($str) use (&$self) + $captureCallback = function ($str) use (&$self) { $args = Functions::parseArgsSimple($str); @@ -44,23 +43,22 @@ public function __construct($str) } // Store the default value. - $default_value = isset($args[0]) ? $args[0] : null; - - if (isset($default_value)) { - $self->defaults[$position] = $default_value; + $defaultValue = isset($args[0]) ? $args[0] : null; + if (isset($defaultValue)) { + $self->defaults[$position] = $defaultValue; } // Update argument count. - $arg_number = ((int) $position) + 1; - $self->argCount = max($self->argCount, $arg_number); + $argNumber = ((int) $position) + 1; + $self->argCount = max($self->argCount, $argNumber); return "?a$position?"; }; - $this->string = $template_functions->apply($str, array( - 'arg' => $capture_callback, - '#' => $capture_callback, - )); + $templateFunctions->register['#'] = $captureCallback; + $templateFunctions->register['arg'] = $captureCallback; + + $this->string = $templateFunctions->apply($str); } public function __invoke(array $args = null, $str = null) @@ -82,7 +80,6 @@ public function __invoke(array $args = null, $str = null) // Apply substitutions. $str = isset($find) ? str_replace($find, $replace, $str) : $str; - // Re-tokenize string on return. return Template::tokenize($str); } diff --git a/plugins/canvas.php b/plugins/canvas.php index 6e7113a..d51d1ec 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -332,15 +332,15 @@ function canvas_apply_filters($canvas, $src) { function canvas_apply_css_funcs($canvas) { - static $generic_functions, $fill_functions, $filter_functions; - if (! $generic_functions) { + static $functions; + if (! $functions) { + $functions = new stdClass(); - $fill_functions = new Functions(array('canvas-linear-gradient' => 'CssCrush\canvas_fn_linear_gradient')); + $functions->fill = new Functions(array('canvas-linear-gradient' => 'CssCrush\canvas_fn_linear_gradient')); - $generic_register = array_diff_key(Crush::$process->functions->register, $fill_functions->register); - $generic_functions = new Functions($generic_register); + $functions->generic = new Functions(array_diff_key(Crush::$process->functions->register, $functions->fill->register)); - $filter_functions = new Functions(array( + $functions->filter = new Functions(array( 'contrast' => 'CssCrush\canvas_fn_filter', 'opacity' => 'CssCrush\canvas_fn_filter', 'colorize' => 'CssCrush\canvas_fn_filter', @@ -360,16 +360,15 @@ function canvas_apply_css_funcs($canvas) { continue; } - $value = $generic_functions->apply($value); + $value = $functions->generic->apply($value); + $context->canvas = $canvas; if (in_array($property, array('fill', 'background-fill'))) { $context->currentProperty = $property; - $context->canvas = $canvas; - $value = $fill_functions->apply($value, null, $context); + $value = $functions->fill->apply($value, $context); } elseif ($property === 'canvas-filter') { - $context->canvas = $canvas; - $value = $filter_functions->apply($value, null, $context); + $value = $functions->filter->apply($value, $context); } } } diff --git a/plugins/svg.php b/plugins/svg.php index 73c751f..e21474c 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -612,25 +612,24 @@ function svg_apply_css_funcs($element, &$raw_data) { // Setup functions for using on values. // Note using custom versions of svg-*-gradient(). - static $generic_functions, $fill_functions; - if (! $generic_functions) { - $fill_register = array( + static $functions; + if (! $functions) { + $functions = new \stdClass(); + $functions->fill = new Functions(array( 'svg-linear-gradient' => 'CssCrush\svg_fn_linear_gradient', 'svg-radial-gradient' => 'CssCrush\svg_fn_radial_gradient', 'pattern' => 'CssCrush\svg_fn_pattern', - ); - $fill_functions = new Functions($fill_register); + )); - $generic_register = array_diff_key(Crush::$process->functions->register, $fill_register); - $generic_functions = new Functions($generic_register); + $functions->generic = new Functions(array_diff_key(Crush::$process->functions->register, $functions->fill->register)); } foreach ($raw_data as $property => &$value) { - $value = $generic_functions->apply($value); + $value = $functions->generic->apply($value); // Only capturing fills for fill and stoke properties. if ($property === 'fill' || $property === 'stroke') { - $value = $fill_functions->apply($value, null, $element); + $value = $functions->fill->apply($value, $element); // If the value is a color with alpha component we split the color // and set the corresponding *-opacity property because Webkit doesn't diff --git a/tests/unit/CssCrush/FunctionsTest.php b/tests/unit/CssCrush/FunctionsTest.php new file mode 100644 index 0000000..85e46a9 --- /dev/null +++ b/tests/unit/CssCrush/FunctionsTest.php @@ -0,0 +1,20 @@ +assertEquals('~(?(?assertEquals('~(?(?:#|(?assertEquals('~(?\$|#)\(~iS', $patt); + } +} diff --git a/tests/unit/CssCrush/RegexTest.php b/tests/unit/CssCrush/RegexTest.php index c188dff..293fc12 100644 --- a/tests/unit/CssCrush/RegexTest.php +++ b/tests/unit/CssCrush/RegexTest.php @@ -14,15 +14,6 @@ public function testMake() $this->assertEquals('~ #[[:xdigit:]]{3} ~xS', Regex::make('~ #{{hex}}{3} ~xS')); } - public function testMakeFunctionPatt() - { - $patt = Regex::makeFunctionPatt(array('foo', 'bar')); - $this->assertEquals('~(?(? true)); - $this->assertEquals('~(?#|(? Date: Fri, 4 Apr 2014 14:59:51 +0100 Subject: [PATCH 270/421] Renamed Stream class to StringObject to reflect its actual nature. --- lib/CssCrush/BalancedMatch.php | 20 ++--- lib/CssCrush/Crush.php | 8 +- lib/CssCrush/IO.php | 6 +- lib/CssCrush/Importer.php | 2 +- lib/CssCrush/Process.php | 71 +++++++++--------- lib/CssCrush/Selector.php | 2 +- lib/CssCrush/{Stream.php => StringObject.php} | 18 ++--- plugins/canvas.php | 2 +- plugins/color.php | 2 +- plugins/loop.php | 2 +- plugins/svg.php | 2 +- tests/unit/CssCrush/BalancedMatchTest.php | 14 ++-- tests/unit/CssCrush/StreamTest.php | 73 ------------------- tests/unit/CssCrush/StringObjectTest.php | 73 +++++++++++++++++++ 14 files changed, 146 insertions(+), 149 deletions(-) rename lib/CssCrush/{Stream.php => StringObject.php} (98%) delete mode 100644 tests/unit/CssCrush/StreamTest.php create mode 100644 tests/unit/CssCrush/StringObjectTest.php diff --git a/lib/CssCrush/BalancedMatch.php b/lib/CssCrush/BalancedMatch.php index d62d74a..42cb940 100644 --- a/lib/CssCrush/BalancedMatch.php +++ b/lib/CssCrush/BalancedMatch.php @@ -1,29 +1,29 @@ stream = $stream; + $this->string = $string; $this->offset = $offset; $this->match = null; $this->length = 0; list($opener, $closer) = str_split($brackets, 1); - if (strpos($stream->raw, $opener, $this->offset) === false) { + if (strpos($string->raw, $opener, $this->offset) === false) { return; } - if (substr_count($stream->raw, $opener) !== substr_count($stream->raw, $closer)) { - $sample = substr($stream->raw, $this->offset, 25); + if (substr_count($string->raw, $opener) !== substr_count($string->raw, $closer)) { + $sample = substr($string->raw, $this->offset, 25); warning("[[CssCrush]] - Unmatched token near '$sample'."); return; @@ -31,7 +31,7 @@ public function __construct(Stream $stream, $offset, $brackets = '{}') $patt = ($opener === '{') ? Regex::$patt->block : Regex::$patt->parens; - if (preg_match($patt, $stream->raw, $m, PREG_OFFSET_CAPTURE, $this->offset)) { + if (preg_match($patt, $string->raw, $m, PREG_OFFSET_CAPTURE, $this->offset)) { $this->match = $m; $this->matchLength = strlen($m[0][0]); @@ -51,16 +51,16 @@ public function inside() public function whole() { - return substr($this->stream->raw, $this->offset, $this->length); + return substr($this->string->raw, $this->offset, $this->length); } public function replace($replacement) { - $this->stream->splice($replacement, $this->offset, $this->length); + $this->string->splice($replacement, $this->offset, $this->length); } public function unWrap() { - $this->stream->splice($this->inside(), $this->offset, $this->length); + $this->string->splice($this->inside(), $this->offset, $this->length); } } diff --git a/lib/CssCrush/Crush.php b/lib/CssCrush/Crush.php index 5087ac8..1255400 100644 --- a/lib/CssCrush/Crush.php +++ b/lib/CssCrush/Crush.php @@ -1,14 +1,14 @@ compile(); + $string = $process->compile(); - return $process->io->write($stream) ? $process->io->getOutputUrl() : ''; + return $process->io->write($string) ? $process->io->getOutputUrl() : ''; } /** diff --git a/lib/CssCrush/IO.php b/lib/CssCrush/IO.php index 53f73d9..09b2767 100644 --- a/lib/CssCrush/IO.php +++ b/lib/CssCrush/IO.php @@ -190,17 +190,17 @@ public function saveCacheData() Util::filePutContents($process->cacheFile, json_encode($process->cacheData, $flags), __METHOD__); } - public function write(Stream $stream) + public function write(StringObject $string) { $process = $this->process; $output = $process->output; $source_map_filename = "$output->filename.map"; if ($process->sourceMap) { - $stream->append($process->newline . "/*# sourceMappingURL=$source_map_filename */"); + $string->append($process->newline . "/*# sourceMappingURL=$source_map_filename */"); } - if (Util::filePutContents("$output->dir/$output->filename", $stream, __METHOD__)) { + if (Util::filePutContents("$output->dir/$output->filename", $string, __METHOD__)) { $json_encode_flags = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 0; diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index 5cd0be0..b02ba9d 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -240,7 +240,7 @@ static protected function checkSyntax(&$str) } } - // Reverse the stream (and brackets) to find stray closing brackets. + // Reverse the string (and brackets) to find stray closing brackets. $str = strtr(strrev($str), $pairing, strrev($pairing)); preg_match_all($opener_patt, $str, $matches, PREG_OFFSET_CAPTURE); diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index ac6f4ad..9e4fda9 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -197,7 +197,7 @@ protected function getBoilerplate() protected function resolveSelectorAliases() { - $this->stream->pregReplaceCallback( + $this->string->pregReplaceCallback( Regex::make('~@selector-(?alias|splat) +\:?(?{{ident}}) +(?[^;]+) *;~iS'), function ($m) { $name = strtolower($m['name']); @@ -365,7 +365,7 @@ protected function filterPlugins() protected function captureVars() { - Crush::$process->vars = Crush::$process->stream->captureDirectives('@define', array( + Crush::$process->vars = Crush::$process->string->captureDirectives('@define', array( 'singles' => true, 'lowercase_keys' => false, )) + Crush::$process->vars; @@ -386,8 +386,7 @@ protected function captureVars() protected function placeAllVars() { - // Place variables in main stream. - $this->placeVars($this->stream->raw); + $this->placeVars($this->string->raw); $rawTokens =& $this->tokens->store; @@ -449,7 +448,7 @@ protected function placeVars(&$value) protected function resolveSettings() { - $captured_settings = $this->stream->captureDirectives('@settings', array('singles' => true)); + $captured_settings = $this->string->captureDirectives('@settings', array('singles' => true)); $this->settings = new Settings($this->options->settings + $captured_settings); } @@ -460,29 +459,27 @@ protected function resolveSettings() protected function resolveIfDefines() { - $ifdefine_patt = Regex::make('~@ifdefine \s+ (not \s+)? ({{ ident }}) \s* \{~ixS'); + $ifdefinePatt = Regex::make('~@ifdefine \s+ (not \s+)? ({{ ident }}) \s* \{~ixS'); - $matches = $this->stream->matchAll($ifdefine_patt); + $matches = $this->string->matchAll($ifdefinePatt); while ($match = array_pop($matches)) { - $curly_match = new BalancedMatch($this->stream, $match[0][1]); + $curlyMatch = new BalancedMatch($this->string, $match[0][1]); - if (! $curly_match->match) { + if (! $curlyMatch->match) { continue; } $negate = $match[1][1] != -1; $name = $match[2][0]; - $name_defined = isset($this->vars[$name]); + $nameDefined = isset($this->vars[$name]); - if (! $negate && $name_defined || $negate && ! $name_defined) { - // Test resolved true so include the innards. - $curly_match->unWrap(); + if (! $negate && $nameDefined || $negate && ! $nameDefined) { + $curlyMatch->unWrap(); } else { - // Recontruct the stream without the innards. - $curly_match->replace(''); + $curlyMatch->replace(''); } } } @@ -493,7 +490,7 @@ protected function resolveIfDefines() protected function captureMixins() { - $this->stream->pregReplaceCallback(Regex::$patt->mixin, function ($m) { + $this->string->pregReplaceCallback(Regex::$patt->mixin, function ($m) { Crush::$process->mixins[$m['name']] = new Mixin($m['block_content']); }); } @@ -506,7 +503,7 @@ protected function resolveFragments() { $fragments =& Crush::$process->fragments; - $this->stream->pregReplaceCallback(Regex::$patt->fragmentCapture, function ($m) use (&$fragments) { + $this->string->pregReplaceCallback(Regex::$patt->fragmentCapture, function ($m) use (&$fragments) { $fragments[$m['name']] = new Fragment( $m['block_content'], array('name' => strtolower($m['name'])) @@ -514,7 +511,7 @@ protected function resolveFragments() return ''; }); - $this->stream->pregReplaceCallback(Regex::$patt->fragmentInvoke, function ($m) use (&$fragments) { + $this->string->pregReplaceCallback(Regex::$patt->fragmentInvoke, function ($m) use (&$fragments) { $fragment = isset($fragments[$m['name']]) ? $fragments[$m['name']] : null; if ($fragment) { $args = array(); @@ -533,7 +530,7 @@ protected function resolveFragments() public function captureRules() { - $this->stream->pregReplaceCallback(Regex::$patt->rule, function ($m) { + $this->string->pregReplaceCallback(Regex::$patt->rule, function ($m) { $selector = trim($m['selector']); $block = trim($m['block_content']); @@ -595,7 +592,7 @@ protected function processRules() protected function resolveInBlocks() { - $matches = $this->stream->matchAll('~@in\s+([^{]+)\{~iS'); + $matches = $this->string->matchAll('~@in\s+([^{]+)\{~iS'); $tokens = Crush::$process->tokens; // Move through the matches in reverse order. @@ -606,7 +603,7 @@ protected function resolveInBlocks() $arguments = Util::splitDelimList(Selector::expandAliases($raw_argument)); - $curly_match = new BalancedMatch($this->stream, $match_start_pos); + $curly_match = new BalancedMatch($this->string, $match_start_pos); if (! $curly_match->match || empty($raw_argument)) { continue; @@ -679,12 +676,12 @@ protected function aliasAtRules() foreach ($aliases as $at_rule => $at_rule_aliases) { - $matches = $this->stream->matchAll("~@$at_rule" . '[\s{]~i'); + $matches = $this->string->matchAll("~@$at_rule" . '[\s{]~i'); // Find at-rules that we want to alias. while ($match = array_pop($matches)) { - $curly_match = new BalancedMatch($this->stream, $match[0][1]); + $curly_match = new BalancedMatch($this->string, $match[0][1]); if (! $curly_match->match) { // Couldn't match the block. @@ -774,9 +771,9 @@ protected function collate() } // Apply all formatting replacements. - $this->stream->pregReplaceHash($regex_replacements)->lTrim(); + $this->string->pregReplaceHash($regex_replacements)->lTrim(); - $this->stream->restore('r'); + $this->string->restore('r'); // Record stats then drop rule objects to reclaim memory. Crush::runStat('selector_count', 'rule_count', 'vars'); @@ -798,7 +795,7 @@ protected function collate() } // Insert comments and do final whitespace cleanup. - $this->stream + $this->string ->restore('c') ->trim() ->append($EOL); @@ -826,17 +823,17 @@ protected function collate() } if ($options->boilerplate) { - $this->stream->prepend($this->getBoilerplate()); + $this->string->prepend($this->getBoilerplate()); } if ($this->charset) { - $this->stream->prepend("@charset \"$this->charset\";$EOL"); + $this->string->prepend("@charset \"$this->charset\";$EOL"); } - $this->stream->restore(array('u', 's')); + $this->string->restore(array('u', 's')); if ($this->addTracingStubs) { - $this->stream->restore('t', false, array($this, 'generateTracingStub')); + $this->string->restore('t', false, array($this, 'generateTracingStub')); } if ($this->generateMap) { $this->generateSourceMap(); @@ -877,7 +874,7 @@ public function compile() $this->preCompile(); // Collate hostfile and imports. - $this->stream = new Stream(Importer::hostfile($this->input)); + $this->string = new StringObject(Importer::hostfile($this->input)); $this->captureVars(); @@ -911,7 +908,7 @@ public function compile() $this->postCompile(); - return $this->stream; + return $this->string; } @@ -931,7 +928,7 @@ public function generateSourceMap() $token_patt = Regex::make('~\?[tm]{{token-id}}\?~S'); $mappings = array(); - $lines = preg_split(Regex::$patt->newline, $this->stream->raw); + $lines = preg_split(Regex::$patt->newline, $this->string->raw); $tokens =& $this->tokens->store; // All mappings are calculated as delta values. @@ -969,7 +966,7 @@ public function generateSourceMap() $mappings[] = implode(',', $line_segments); } - $this->stream->raw = implode($this->newline, $lines); + $this->string->raw = implode($this->newline, $lines); $this->sourceMap['mappings'] = implode(';', $mappings); } @@ -1000,7 +997,7 @@ public function generateTracingStub($m) protected function decruft() { - return $this->stream->pregReplaceHash(array( + return $this->string->pregReplaceHash(array( // Strip leading zeros on floats. '~([: \(,])(-?)0(\.\d+)~S' => '$1$2$3', @@ -1038,11 +1035,11 @@ protected function minifyColors() $functions_patt = Regex::make('~{{ LB }}(rgb|hsl)\(([^\)]{5,})\)~iS'); } - $this->stream->pregReplaceCallback($keywords_patt, function ($m) use ($minified_keywords) { + $this->string->pregReplaceCallback($keywords_patt, function ($m) use ($minified_keywords) { return $minified_keywords[strtolower($m[0])]; }); - $this->stream->pregReplaceCallback($functions_patt, function ($m) { + $this->string->pregReplaceCallback($functions_patt, function ($m) { $args = Functions::parseArgs(trim($m[2])); if (stripos($m[1], 'hsl') === 0) { $args = Color::cssHslToRgb($args); diff --git a/lib/CssCrush/Selector.php b/lib/CssCrush/Selector.php index 4bb6926..b17d964 100644 --- a/lib/CssCrush/Selector.php +++ b/lib/CssCrush/Selector.php @@ -41,7 +41,7 @@ public function __toString() public function appendPseudo($pseudo) { // Check to avoid doubling-up. - if (! Stream::endsWith($this->readableValue, $pseudo)) { + if (! StringObject::endsWith($this->readableValue, $pseudo)) { $this->readableValue .= $pseudo; $this->value .= $pseudo; diff --git a/lib/CssCrush/Stream.php b/lib/CssCrush/StringObject.php similarity index 98% rename from lib/CssCrush/Stream.php rename to lib/CssCrush/StringObject.php index 5b6ae3e..fdcd2d6 100644 --- a/lib/CssCrush/Stream.php +++ b/lib/CssCrush/StringObject.php @@ -1,12 +1,12 @@ raw, $offset); } - public function restore($types, $release = false, $callback = null) - { - $this->raw = Crush::$process->tokens->restore($this->raw, $types, $release, $callback); - - return $this; - } - public function replaceHash($replacements) { if ($replacements) { @@ -118,6 +111,13 @@ public function lTrim() return $this; } + public function restore($types, $release = false, $callback = null) + { + $this->raw = Crush::$process->tokens->restore($this->raw, $types, $release, $callback); + + return $this; + } + public function captureDirectives($directive, $parse_options = array()) { $directive = ltrim($directive, '@'); diff --git a/plugins/canvas.php b/plugins/canvas.php index d51d1ec..beeab74 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -19,7 +19,7 @@ function canvas_capture($process) { - $process->stream->pregReplaceCallback( + $process->string->pregReplaceCallback( Regex::make('~@canvas\s+(?{{ ident }})\s*{{ block }}~iS'), function ($m) { Crush::$process->misc->canvas_defs[strtolower($m['name'])] = new Template($m['block_content']); diff --git a/plugins/color.php b/plugins/color.php index 28ff646..ec20539 100644 --- a/plugins/color.php +++ b/plugins/color.php @@ -25,7 +25,7 @@ function color(&$declaration) { function color_capture($process) { - $captured_keywords = $process->stream->captureDirectives('@color', array('singles' => true)); + $captured_keywords = $process->string->captureDirectives('@color', array('singles' => true)); if ($captured_keywords) { diff --git a/plugins/loop.php b/plugins/loop.php index 6f7b662..4043ab8 100644 --- a/plugins/loop.php +++ b/plugins/loop.php @@ -21,7 +21,7 @@ function loop($process) { - $process->stream->pregReplaceCallback(LOOP_PATT, function ($m) { + $process->string->pregReplaceCallback(LOOP_PATT, function ($m) { return Template::tokenize(loop_unroll(Template::unTokenize($m[0]))); }); diff --git a/plugins/svg.php b/plugins/svg.php index e21474c..4561fd6 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -31,7 +31,7 @@ function fn__svg_data($input) { function svg_capture($process) { - $process->stream->pregReplaceCallback( + $process->string->pregReplaceCallback( Regex::make('~@svg\s+(?{{ ident }})\s*{{ block }}~iS'), function ($m) { Crush::$process->misc->svg_defs[strtolower($m['name'])] = new Template($m['block_content']); diff --git a/tests/unit/CssCrush/BalancedMatchTest.php b/tests/unit/CssCrush/BalancedMatchTest.php index 9a04ee3..378604f 100644 --- a/tests/unit/CssCrush/BalancedMatchTest.php +++ b/tests/unit/CssCrush/BalancedMatchTest.php @@ -13,25 +13,25 @@ public function setUp() $this->process = bootstrap_process(); $sample = '@foo; @bar {color: orange;} @baz'; - $this->process->stream = new \CssCrush\Stream($sample); + $this->process->string = new \CssCrush\StringObject($sample); } public function testMatch() { - $matches = $this->process->stream->matchAll('~@bar~'); + $matches = $this->process->string->matchAll('~@bar~'); $match_offset = $matches[0][0][1]; - $match = new BalancedMatch($this->process->stream, $match_offset); + $match = new BalancedMatch($this->process->string, $match_offset); $this->assertEquals('color: orange;', $match->inside()); $this->assertEquals('@bar {color: orange;}', $match->whole()); - $match = new BalancedMatch(clone $this->process->stream, $match_offset); + $match = new BalancedMatch(clone $this->process->string, $match_offset); $match->unWrap(); - $this->assertEquals('@foo; color: orange; @baz', $match->stream->__toString()); + $this->assertEquals('@foo; color: orange; @baz', $match->string->__toString()); - $match = new BalancedMatch(clone $this->process->stream, $match_offset); + $match = new BalancedMatch(clone $this->process->string, $match_offset); $match->replace('@boo;'); - $this->assertEquals('@foo; @boo; @baz', $match->stream->__toString()); + $this->assertEquals('@foo; @boo; @baz', $match->string->__toString()); } } diff --git a/tests/unit/CssCrush/StreamTest.php b/tests/unit/CssCrush/StreamTest.php deleted file mode 100644 index efc643c..0000000 --- a/tests/unit/CssCrush/StreamTest.php +++ /dev/null @@ -1,73 +0,0 @@ -sample); - $this->assertEquals($this->sample, (string) $stream); - } - - public function testEndsWith() - { - $this->assertTrue(Stream::endsWith('amet', 'et')); - } - - public function testUpdate() - { - $stream = new Stream($this->sample); - $updated_text = 'foo'; - $stream->update($updated_text); - $this->assertEquals($updated_text, (string) $stream); - } - - public function testTrim() - { - $stream = new Stream($this->sample); - $this->assertEquals(trim($this->sample), (string) $stream->trim()); - } - - public function testRTrim() - { - $stream = new Stream($this->sample); - $this->assertEquals(rtrim($this->sample), (string) $stream->rTrim()); - } - - public function testLTrim() - { - $stream = new Stream($this->sample); - $this->assertEquals(ltrim($this->sample), (string) $stream->lTrim()); - } - - public function testAppend() - { - $stream = new Stream($this->sample); - $append_text = 'foo'; - $this->assertEquals($this->sample . $append_text, (string) $stream->append($append_text)); - } - - public function testPrepend() - { - $stream = new Stream($this->sample); - $prepend_text = 'foo'; - $this->assertEquals($prepend_text . $this->sample, (string) $stream->prepend($prepend_text)); - } - - public function testSubstr() - { - $stream = new Stream($this->sample); - $this->assertEquals(substr($this->sample, 1), (string) $stream->substr(1)); - } -} - -// matchAll -// replaceHash -// pregReplaceCallback -// pregReplaceHash -// splice diff --git a/tests/unit/CssCrush/StringObjectTest.php b/tests/unit/CssCrush/StringObjectTest.php new file mode 100644 index 0000000..5b5e7e4 --- /dev/null +++ b/tests/unit/CssCrush/StringObjectTest.php @@ -0,0 +1,73 @@ +sample); + $this->assertEquals($this->sample, (string) $string); + } + + public function testEndsWith() + { + $this->assertTrue(StringObject::endsWith('amet', 'et')); + } + + public function testUpdate() + { + $string = new StringObject($this->sample); + $updated_text = 'foo'; + $string->update($updated_text); + $this->assertEquals($updated_text, (string) $string); + } + + public function testTrim() + { + $string = new StringObject($this->sample); + $this->assertEquals(trim($this->sample), (string) $string->trim()); + } + + public function testRTrim() + { + $string = new StringObject($this->sample); + $this->assertEquals(rtrim($this->sample), (string) $string->rTrim()); + } + + public function testLTrim() + { + $string = new StringObject($this->sample); + $this->assertEquals(ltrim($this->sample), (string) $string->lTrim()); + } + + public function testAppend() + { + $string = new StringObject($this->sample); + $append_text = 'foo'; + $this->assertEquals($this->sample . $append_text, (string) $string->append($append_text)); + } + + public function testPrepend() + { + $string = new StringObject($this->sample); + $prepend_text = 'foo'; + $this->assertEquals($prepend_text . $this->sample, (string) $string->prepend($prepend_text)); + } + + public function testSubstr() + { + $string = new StringObject($this->sample); + $this->assertEquals(substr($this->sample, 1), (string) $string->substr(1)); + } +} + +// matchAll +// replaceHash +// pregReplaceCallback +// pregReplaceHash +// splice From c2e55a54824817f1b641d2bfa416ce1600151333 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 15 Apr 2014 15:24:16 +0100 Subject: [PATCH 271/421] Removed some obsolete vendor aliases. Upgraded travis badge to svg. --- README.md | 4 ++-- aliases.ini | 41 ++++------------------------------------- 2 files changed, 6 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 20aacd3..b19b866 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -[![Build Status](https://travis-ci.org/peteboere/css-crush.png)](https://travis-ci.org/peteboere/css-crush) +[![Build Status](https://travis-ci.org/peteboere/css-crush.svg)](https://travis-ci.org/peteboere/css-crush) -Logo +Logo CSS-Crush is a standards inspired preprocessor designed to enable a modern and uncluttered CSS workflow. diff --git a/aliases.ini b/aliases.ini index 79eb462..fb8e85a 100644 --- a/aliases.ini +++ b/aliases.ini @@ -16,43 +16,26 @@ ; Animations. animation[] = -webkit-animation animation[] = -moz-animation - animation[] = -o-animation animation-delay[] = -webkit-animation-delay animation-delay[] = -moz-animation-delay - animation-delay[] = -o-animation-delay animation-direction[] = -webkit-animation-direction animation-direction[] = -moz-animation-direction - animation-direction[] = -o-animation-direction animation-duration[] = -webkit-animation-duration animation-duration[] = -moz-animation-duration - animation-duration[] = -o-animation-duration animation-fill-mode[] = -webkit-animation-fill-mode animation-fill-mode[] = -moz-animation-fill-mode - animation-fill-mode[] = -o-animation-fill-mode animation-iteration-count[] = -webkit-animation-iteration-count animation-iteration-count[] = -moz-animation-iteration-count - animation-iteration-count[] = -o-animation-iteration-count animation-name[] = -webkit-animation-name animation-name[] = -moz-animation-name - animation-name[] = -o-animation-name animation-play-state[] = -webkit-animation-play-state animation-play-state[] = -moz-animation-play-state - animation-play-state[] = -o-animation-play-state animation-timing-function[] = -webkit-animation-timing-function animation-timing-function[] = -moz-animation-timing-function - animation-timing-function[] = -o-animation-timing-function ; Backface visibility. backface-visibility[] = -webkit-backface-visibility backface-visibility[] = -moz-backface-visibility - backface-visibility[] = -ms-backface-visibility - - ; Border radius. - border-radius[] = -webkit-border-radius - border-top-left-radius[] = -webkit-border-top-left-radius - border-top-right-radius[] = -webkit-border-top-right-radius - border-bottom-left-radius[] = -webkit-border-bottom-left-radius - border-bottom-right-radius[] = -webkit-border-bottom-right-radius ; Border-image. border-image[] = -webkit-border-image @@ -155,10 +138,8 @@ ; Perspective. perspective[] = -webkit-perspective perspective[] = -moz-perspective - perspective[] = -ms-perspective perspective-origin[] = -webkit-perspective-origin perspective-origin[] = -moz-perspective-origin - perspective-origin[] = -ms-perspective-origin ; Tab size. tab-size[] = -moz-tab-size @@ -180,11 +161,9 @@ transform[] = -webkit-transform transform[] = -moz-transform transform[] = -ms-transform - transform[] = -o-transform transform-origin[] = -webkit-transform-origin transform-origin[] = -moz-transform-origin transform-origin[] = -ms-transform-origin - transform-origin[] = -o-transform-origin transform-style[] = -webkit-transform-style transform-style[] = -moz-transform-style transform-style[] = -ms-transform-style @@ -192,26 +171,19 @@ ; Transitions. transition[] = -webkit-transition transition[] = -moz-transition - transition[] = -o-transition transition-delay[] = -webkit-transition-delay transition-delay[] = -moz-transition-delay - transition-delay[] = -o-transition-delay transition-duration[] = -webkit-transition-duration transition-duration[] = -moz-transition-duration - transition-duration[] = -o-transition-duration transition-property[] = -webkit-transition-property transition-property[] = -moz-transition-property - transition-property[] = -o-transition-property transition-timing-function[] = -webkit-transition-timing-function transition-timing-function[] = -moz-transition-timing-function - transition-timing-function[] = -o-transition-timing-function ; User select (non standard). user-select[] = -webkit-user-select user-select[] = -moz-user-select user-select[] = -ms-user-select - user-select[] = -o-user-select - user-select[] = user-select ;---------------------------------------------------------------- @@ -259,12 +231,12 @@ ; Cursor values (non-standard). cursor:zoom-in[] = cursor:-webkit-zoom-in cursor:zoom-in[] = cursor:-moz-zoom-in - cursor:zoom-in[] = cursor:-ms-zoom-in - cursor:zoom-in[] = cursor:-o-zoom-in cursor:zoom-out[] = cursor:-webkit-zoom-out cursor:zoom-out[] = cursor:-moz-zoom-out - cursor:zoom-out[] = cursor:-ms-zoom-out - cursor:zoom-out[] = cursor:-o-zoom-out + cursor:grab[] = cursor:-webkit-grab + cursor:grab[] = cursor:-moz-grab + cursor:grabbing[] = cursor:-webkit-grabbing + cursor:grabbing[] = cursor:-moz-grabbing ; Experimental width values. width:max-content[] = width:intrinsic @@ -322,18 +294,14 @@ ; Gradients. linear-gradient[] = -webkit-linear-gradient linear-gradient[] = -moz-linear-gradient - linear-gradient[] = -o-linear-gradient radial-gradient[] = -webkit-radial-gradient radial-gradient[] = -moz-radial-gradient - radial-gradient[] = -o-radial-gradient ; Repeating gradients. repeating-linear-gradient[] = -webkit-repeating-linear-gradient repeating-linear-gradient[] = -moz-repeating-linear-gradient - repeating-linear-gradient[] = -o-repeating-linear-gradient repeating-radial-gradient[] = -webkit-repeating-radial-gradient repeating-radial-gradient[] = -moz-repeating-radial-gradient - repeating-radial-gradient[] = -o-repeating-radial-gradient ;---------------------------------------------------------------- @@ -344,7 +312,6 @@ ; Keyframes. keyframes[] = -webkit-keyframes keyframes[] = -moz-keyframes - keyframes[] = -o-keyframes ; Viewport. viewport[] = -webkit-viewport From a6909f9a2f95c9d71c01e1116f698cd848bdd397 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 16 Apr 2014 09:15:21 +0100 Subject: [PATCH 272/421] Minor doc updates. --- docs/core/auto-prefixing.md | 6 ------ docs/core/inheritance.md | 35 ++++++++++++++++++----------------- docs/core/variables.md | 14 +++++++------- 3 files changed, 25 insertions(+), 30 deletions(-) diff --git a/docs/core/auto-prefixing.md b/docs/core/auto-prefixing.md index 7709fad..5cb9e58 100644 --- a/docs/core/auto-prefixing.md +++ b/docs/core/auto-prefixing.md @@ -18,7 +18,6 @@ In some cases (e.g. CSS3 gradients) final syntax is incompatible with older pref .foo { background: -webkit-linear-gradient(left, red, white); background: -moz-linear-gradient(left, red, white); - background: -o-linear-gradient(left, red, white); background: linear-gradient(to right, red, white); } ``` @@ -39,15 +38,10 @@ In some cases (e.g. CSS3 gradients) final syntax is incompatible with older pref 50% {-moz-transform: scale(1.4); transform: scale(1.4);} } -@-o-keyframes bounce { - 50% {-o-transform: scale(1.4); - transform: scale(1.4);} -} @keyframes bounce { 50% {-webkit-transform: scale(1.4); -moz-transform: scale(1.4); -ms-transform: scale(1.4); - -o-transform: scale(1.4); transform: scale(1.4);} } ``` diff --git a/docs/core/inheritance.md b/docs/core/inheritance.md index e3141af..4473813 100644 --- a/docs/core/inheritance.md +++ b/docs/core/inheritance.md @@ -45,6 +45,24 @@ Inheritance is recursive: .one, .two, .three, .four { color: pink; } ``` +## Referencing by name + +If you want to reference a rule without being concerned about later changes to the identifying selector use the `@name` directive: + +```crush +.foo123 { + @name foo; + text-decoration: underline; +} + +.bar { + @include foo; +} +.baz { + @extend foo; +} +``` + ## Extending with pseudo classes/elements @@ -107,20 +125,3 @@ The same outcome can also be achieved with an [Abstract rule](#core--abstract) w } ``` -## Referencing rules by name - -If you want to reference a rule without being concerned about later changes to the identifying selector use the `@name` directive: - -```crush -.foo123 { - @name foo; - text-decoration: underline; -} - -.bar { - @include foo; -} -.baz { - @extend foo; -} -``` diff --git a/docs/core/variables.md b/docs/core/variables.md index bbef970..4a4d906 100644 --- a/docs/core/variables.md +++ b/docs/core/variables.md @@ -4,9 +4,9 @@ }--> -Declare variables in your CSS with a `@define` directive and apply them using the `$()` function. +Declare variables in your CSS with a `@define` directive and use them with the `$()` function. -Variables can be injected at runtime with the [vars option](#api--options). +Variables can also be injected at runtime with the [vars option](#api--options). ```crush @@ -14,15 +14,15 @@ Variables can be injected at runtime with the [vars option](#api--options). @define { dark: #333; light: #F4F2E2; - smaller-screen: only screen and (max-width: 800px); + smaller-screen: screen and (max-width: 800px); } -/* Applying variables */ +/* Using variables */ @media $(smaller-screen) { ul, p { color: $(dark); /* Using a fallback value with an undefined variable */ - background-color: $(accent-color #ff0); + background-color: $(accent-color, #ff0); } } ``` @@ -30,9 +30,9 @@ Variables can be injected at runtime with the [vars option](#api--options). ******* ```css -/* Interpolation */ +/* String interpolation */ .username::before { - content: "$(lang-greeting)"; + content: "$(greeting)"; } ``` From a321b737e929dca930b62f381ea0592f58bdccbf Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 22 Apr 2014 19:40:24 +0100 Subject: [PATCH 273/421] For sake of compactness adding `@set`/`@ifset` as alternatives to `@define`/`@ifdefine` and `@selector` as alternative to `@selector-alias`. If this works out documentation will be updated with the new names and the older directive names will be supported at least until the next major release (3.x). --- lib/CssCrush/Process.php | 15 +++++++-------- lib/CssCrush/StringObject.php | 5 ++++- plugins/color.php | 2 +- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 9e4fda9..841c0a4 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -198,10 +198,10 @@ protected function getBoilerplate() protected function resolveSelectorAliases() { $this->string->pregReplaceCallback( - Regex::make('~@selector-(?alias|splat) +\:?(?{{ident}}) +(?[^;]+) *;~iS'), + Regex::make('~@selector(?:-(?alias|splat))? +\:?(?{{ident}}) +(?[^;]+) *;~iS'), function ($m) { $name = strtolower($m['name']); - $type = strtolower($m['type']); + $type = ! empty($m['type']) ? strtolower($m['type']) : 'alias'; $handler = Util::stripCommentTokens($m['handler']); Crush::$process->selectorAliases[$name] = new SelectorAlias($handler, $type); }); @@ -365,7 +365,7 @@ protected function filterPlugins() protected function captureVars() { - Crush::$process->vars = Crush::$process->string->captureDirectives('@define', array( + Crush::$process->vars = Crush::$process->string->captureDirectives(array('set', 'define'), array( 'singles' => true, 'lowercase_keys' => false, )) + Crush::$process->vars; @@ -448,7 +448,7 @@ protected function placeVars(&$value) protected function resolveSettings() { - $captured_settings = $this->string->captureDirectives('@settings', array('singles' => true)); + $captured_settings = $this->string->captureDirectives('settings', array('singles' => true)); $this->settings = new Settings($this->options->settings + $captured_settings); } @@ -459,7 +459,7 @@ protected function resolveSettings() protected function resolveIfDefines() { - $ifdefinePatt = Regex::make('~@ifdefine \s+ (not \s+)? ({{ ident }}) \s* \{~ixS'); + $ifdefinePatt = Regex::make('~@if(?:set|define) \s+ (?not \s+)? (?{{ ident }}) \s* \{~ixS'); $matches = $this->string->matchAll($ifdefinePatt); @@ -471,9 +471,8 @@ protected function resolveIfDefines() continue; } - $negate = $match[1][1] != -1; - $name = $match[2][0]; - $nameDefined = isset($this->vars[$name]); + $negate = $match['negate'][1] != -1; + $nameDefined = isset($this->vars[$match['name'][0]]); if (! $negate && $nameDefined || $negate && ! $nameDefined) { $curlyMatch->unWrap(); diff --git a/lib/CssCrush/StringObject.php b/lib/CssCrush/StringObject.php index fdcd2d6..2057873 100644 --- a/lib/CssCrush/StringObject.php +++ b/lib/CssCrush/StringObject.php @@ -120,7 +120,10 @@ public function restore($types, $release = false, $callback = null) public function captureDirectives($directive, $parse_options = array()) { - $directive = ltrim($directive, '@'); + if (is_array($directive)) { + $directive = '(?:' . implode('|', $directive) . ')'; + } + $parse_options += array( 'keyed' => true, 'lowercase_keys' => true, diff --git a/plugins/color.php b/plugins/color.php index ec20539..96fcbe4 100644 --- a/plugins/color.php +++ b/plugins/color.php @@ -25,7 +25,7 @@ function color(&$declaration) { function color_capture($process) { - $captured_keywords = $process->string->captureDirectives('@color', array('singles' => true)); + $captured_keywords = $process->string->captureDirectives('color', array('singles' => true)); if ($captured_keywords) { From afe0f54b7bdb0a1acc2339816dd20e237fc88240 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 3 May 2014 19:06:42 +0100 Subject: [PATCH 274/421] Some rule parsing refactoring. --- lib/CssCrush/Importer.php | 20 ++++++++++++++++++- lib/CssCrush/Process.php | 41 ++++++++++++++++++++++++++++----------- lib/CssCrush/Regex.php | 25 ------------------------ lib/CssCrush/Selector.php | 11 +++++------ 4 files changed, 54 insertions(+), 43 deletions(-) diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index b02ba9d..474a41c 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -273,8 +273,26 @@ static protected function addMarkers(&$str) { $process = Crush::$process; $current_file_index = count($process->sources) -1; + static $patt; + if (! $patt) { + $patt = Regex::make('~ + (?:^|(?<=[;{}])) + (? + (?: \s | {{c-token}} )* + ) + (? + (?: + # Some @-rules are treated like standard rule blocks. + @(?: (?i)page|abstract|font-face(?-i) ) {{RB}} [^{]* + | + [^@;{}]+ + ) + ) + \{ + ~xS'); + } - $count = preg_match_all(Regex::$patt->ruleFirstPass, $str, $matches, PREG_OFFSET_CAPTURE); + $count = preg_match_all($patt, $str, $matches, PREG_OFFSET_CAPTURE); while ($count--) { $selector_offset = $matches['selector'][$count][1]; diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 841c0a4..88a0419 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -529,30 +529,49 @@ protected function resolveFragments() public function captureRules() { - $this->string->pregReplaceCallback(Regex::$patt->rule, function ($m) { + $rulePatt = Regex::make('~ + (? {{t-token}} ) + \s* + (? [^{]+ ) + \s* + {{block}} + ~xiS'); - $selector = trim($m['selector']); - $block = trim($m['block_content']); + $tracePatt = Regex::make('~{{t-token}}~S'); + $rulesAndMediaPatt = Regex::make('~{{r-token}}|@media[^\{]+{{block}}~iS'); - // Ignore and remove empty rules. - if (empty($block) || empty($selector)) { + $count = preg_match_all($tracePatt, $this->string->raw, $traceMatches, PREG_OFFSET_CAPTURE); + while ($count--) { + + $traceOffset = $traceMatches[0][$count][1]; + + preg_match($rulePatt, $this->string->raw, $match, null, $traceOffset); + + $replace = ''; + $block = preg_replace_callback($rulesAndMediaPatt, function ($m) use (&$replace) { + $replace .= $m[0]; return ''; - } + }, $match['block_content']); - $rule = new Rule($selector, $block, $m['trace_token']); + $rule = new Rule(trim($match['selector']), trim($block), $match['trace_token']); - // Store rules if they have declarations or extend arguments. + // Store rules only if they have declarations or extend arguments. if (! empty($rule->declarations->store) || $rule->extendArgs) { - - return Crush::$process->tokens->add($rule, 'r', $rule->label); + $replace = Crush::$process->tokens->add($rule, 'r', $rule->label) . $replace; } - }); + + $this->string->splice($replace, $traceOffset, strlen($match[0])); + } } protected function processRules() { // Create table of name/selector to rule references. $named_references = array(); + + // Flip because rules are captured from end of file. + $this->tokens->store->r = array_reverse($this->tokens->store->r); + foreach ($this->tokens->store->r as $rule) { if ($rule->name) { $named_references[$rule->name] = $rule; diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php index 4b3c3b2..42b4f22 100644 --- a/lib/CssCrush/Regex.php +++ b/lib/CssCrush/Regex.php @@ -80,31 +80,6 @@ public static function init() /\*(?:.*?)(?:\*/|$) ~xsS'; - // Rules. - $patt->ruleFirstPass = Regex::make('~ - (?:^|(?<=[;{}])) - (? - (?: \s | {{c-token}} )* - ) - (? - (?: - # Some @-rules are treated like standard rule blocks. - @(?: (?i)page|abstract|font-face(?-i) ) {{RB}} [^{]* - | - [^@;{}]+ - ) - ) - {{block}} - ~xS'); - - $patt->rule = Regex::make('~ - (? {{t-token}} ) - \s* - (? [^{]+ ) - \s* - {{block}} - ~xiS'); - // Misc. $patt->vendorPrefix = '~^-([a-z]+)-([a-z-]+)~iS'; $patt->ruleDirective = '~^(?:(@include)|(@extends?)|(@name))[\s]+~iS'; diff --git a/lib/CssCrush/Selector.php b/lib/CssCrush/Selector.php index b17d964..98facf8 100644 --- a/lib/CssCrush/Selector.php +++ b/lib/CssCrush/Selector.php @@ -12,18 +12,17 @@ class Selector public $readableValue; public $allowPrefix = true; - public function __construct($raw_selector) + public function __construct($rawSelector) { // Look for rooting prefix. - if (strpos($raw_selector, '^') === 0) { - $raw_selector = ltrim($raw_selector, "^ \n\r\t"); + if (strpos($rawSelector, '^') === 0) { + $rawSelector = ltrim($rawSelector, "^ \n\r\t"); $this->allowPrefix = false; } - // Take readable value from original un-altered state. - $this->readableValue = Selector::makeReadable($raw_selector); + $this->readableValue = Selector::makeReadable($rawSelector); - $this->value = Selector::expandAliases($raw_selector); + $this->value = Selector::expandAliases($rawSelector); } public function __toString() From c070be3e5e3241834247a62eae8f32f19556c4dd Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 3 May 2014 20:44:28 +0100 Subject: [PATCH 275/421] Made rule nesting work without `@in` directives (turns out it wasn't too hard #58). `@in` directives are still supported for backwards compatability until at-least next major release (3.x). --- lib/CssCrush/Process.php | 98 ++++++++++++----------------------- lib/CssCrush/SelectorList.php | 31 +++++++++-- 2 files changed, 61 insertions(+), 68 deletions(-) diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 88a0419..dad531c 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -529,6 +529,8 @@ protected function resolveFragments() public function captureRules() { + $tokens = Crush::$process->tokens; + $rulePatt = Regex::make('~ (? {{t-token}} ) \s* @@ -536,31 +538,44 @@ public function captureRules() \s* {{block}} ~xiS'); - - $tracePatt = Regex::make('~{{t-token}}~S'); $rulesAndMediaPatt = Regex::make('~{{r-token}}|@media[^\{]+{{block}}~iS'); + $tracePatt = Regex::make('~{{t-token}}~S'); $count = preg_match_all($tracePatt, $this->string->raw, $traceMatches, PREG_OFFSET_CAPTURE); while ($count--) { $traceOffset = $traceMatches[0][$count][1]; - preg_match($rulePatt, $this->string->raw, $match, null, $traceOffset); + preg_match($rulePatt, $this->string->raw, $ruleMatch, null, $traceOffset); + $selector = trim($ruleMatch['selector']); + $block = trim($ruleMatch['block_content']); $replace = ''; - $block = preg_replace_callback($rulesAndMediaPatt, function ($m) use (&$replace) { - $replace .= $m[0]; - return ''; - }, $match['block_content']); - $rule = new Rule(trim($match['selector']), trim($block), $match['trace_token']); + // If rules are nested they must be extracted and have selectors merged with the parent. + if (preg_match_all(Regex::$patt->r_token, $block, $childRules)) { + + $block = preg_replace_callback($rulesAndMediaPatt, function ($m) use (&$replace) { + $replace .= $m[0]; + return ''; + }, $block); + + $rule = new Rule($selector, $block, $ruleMatch['trace_token']); + $rawSelectors = array_keys($rule->selectors->store); + foreach ($childRules[0] as $childRule) { + $tokens->get($childRule)->selectors->merge($rawSelectors); + } + } + else { + $rule = new Rule($selector, $block, $ruleMatch['trace_token']); + } // Store rules only if they have declarations or extend arguments. if (! empty($rule->declarations->store) || $rule->extendArgs) { - $replace = Crush::$process->tokens->add($rule, 'r', $rule->label) . $replace; + $replace = $tokens->add($rule, 'r', $rule->label) . $replace; } - $this->string->splice($replace, $traceOffset, strlen($match[0])); + $this->string->splice($replace, $traceOffset, strlen($ruleMatch[0])); } } @@ -610,71 +625,24 @@ protected function processRules() protected function resolveInBlocks() { - $matches = $this->string->matchAll('~@in\s+([^{]+)\{~iS'); - $tokens = Crush::$process->tokens; + $matches = $this->string->matchAll('~@in\s+(?[^{]+)\{~iS'); - // Move through the matches in reverse order. while ($match = array_pop($matches)) { - $match_start_pos = $match[0][1]; - $raw_argument = trim($match[1][0]); - - $arguments = Util::splitDelimList(Selector::expandAliases($raw_argument)); - - $curly_match = new BalancedMatch($this->string, $match_start_pos); + $selectorsMatch = trim($match['selectors'][0]); + $curlyMatch = new BalancedMatch($this->string, $match[0][1]); - if (! $curly_match->match || empty($raw_argument)) { + if (! $curlyMatch->match || empty($selectorsMatch)) { continue; } - // Match all the rule tokens. - $rule_matches = Regex::matchAll( - Regex::$patt->r_token, $curly_match->inside()); - - foreach ($rule_matches as $rule_match) { - - // Get the rule instance. - $rule = $tokens->get($rule_match[0][0]); + $rawSelectors = Util::splitDelimList($selectorsMatch); - // Using arguments create new selector list for the rule. - $new_selector_list = array(); - - foreach ($arguments as $arg_selector) { - - foreach ($rule->selectors as $rule_selector) { - - $use_parent_symbol = strpos($rule_selector->value, '&') !== false; - - // Skipping the prefix. - if (! $rule_selector->allowPrefix && ! $use_parent_symbol) { - - $new_selector_list[$rule_selector->readableValue] = $rule_selector; - } - - // Positioning the prefix with parent symbol "&". - elseif ($use_parent_symbol) { - - $new_value = str_replace( - '&', - $arg_selector, - $rule_selector->value); - - $new = new Selector($new_value); - $new_selector_list[$new->readableValue] = $new; - } - - // Prepending the prefix. - else { - - $new = new Selector("$arg_selector {$rule_selector->value}"); - $new_selector_list[$new->readableValue] = $new; - } - } - } - $rule->selectors->store = $new_selector_list; + foreach (Regex::matchAll(Regex::$patt->r_token, $curlyMatch->inside()) as $ruleMatch) { + Crush::$process->tokens->get($ruleMatch[0][0])->selectors->merge($rawSelectors); } - $curly_match->unWrap(); + $curlyMatch->unWrap(); } } diff --git a/lib/CssCrush/SelectorList.php b/lib/CssCrush/SelectorList.php index 185af52..e8d433c 100644 --- a/lib/CssCrush/SelectorList.php +++ b/lib/CssCrush/SelectorList.php @@ -8,13 +8,13 @@ class SelectorList extends Iterator { - public function __construct($selector_string, Rule $rule) + public function __construct($selectorString, Rule $rule) { parent::__construct(); - $selector_string = trim(Util::stripCommentTokens($selector_string)); + $selectorString = trim(Util::stripCommentTokens($selectorString)); - foreach (Util::splitDelimList($selector_string) as $selector) { + foreach (Util::splitDelimList($selectorString) as $selector) { if (preg_match(Regex::$patt->abstract, $selector, $m)) { $rule->name = strtolower($m['name']); @@ -108,4 +108,29 @@ public function expand() $this->store = $expanded_set; } + + public function merge($rawSelectors) + { + $stack = array(); + + foreach ($rawSelectors as $rawParentSelector) { + foreach ($this->store as $selector) { + + $useParentSymbol = strpos($selector->value, '&') !== false; + + if (! $selector->allowPrefix && ! $useParentSymbol) { + $stack[$selector->readableValue] = $selector; + } + elseif ($useParentSymbol) { + $new = new Selector(str_replace('&', $rawParentSelector, $selector->value)); + $stack[$new->readableValue] = $new; + } + else { + $new = new Selector("$rawParentSelector {$selector->value}"); + $stack[$new->readableValue] = $new; + } + } + } + $this->store = $stack; + } } From 85515d085999b3418832eadb304bf2a33e248ed3 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 9 May 2014 20:17:51 +0100 Subject: [PATCH 276/421] Changed regex class substitutions to be like for like. --- lib/CssCrush/Importer.php | 4 ++-- lib/CssCrush/Process.php | 17 ++++++++--------- lib/CssCrush/Regex.php | 20 ++++++-------------- lib/CssCrush/Tokens.php | 6 +++--- 4 files changed, 19 insertions(+), 28 deletions(-) diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index 474a41c..24cc838 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -155,7 +155,7 @@ static protected function rewriteImportedUrls($import) } // Match all urls that are not imports. - preg_match_all(Regex::make('~(?content, $matches); + preg_match_all(Regex::make('~(?content, $matches); foreach ($matches[0] as $token) { @@ -278,7 +278,7 @@ static protected function addMarkers(&$str) $patt = Regex::make('~ (?:^|(?<=[;{}])) (? - (?: \s | {{c-token}} )* + (?: \s | {{c_token}} )* ) (? (?: diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index dad531c..0baf8b6 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -532,16 +532,15 @@ public function captureRules() $tokens = Crush::$process->tokens; $rulePatt = Regex::make('~ - (? {{t-token}} ) + (? {{ t_token }}) \s* - (? [^{]+ ) + (? [^{]+) \s* - {{block}} + {{ block }} ~xiS'); - $rulesAndMediaPatt = Regex::make('~{{r-token}}|@media[^\{]+{{block}}~iS'); - $tracePatt = Regex::make('~{{t-token}}~S'); + $rulesAndMediaPatt = Regex::make('~{{ r_token }}|@media[^\{]+{{ block }}~iS'); - $count = preg_match_all($tracePatt, $this->string->raw, $traceMatches, PREG_OFFSET_CAPTURE); + $count = preg_match_all(Regex::$patt->t_token, $this->string->raw, $traceMatches, PREG_OFFSET_CAPTURE); while ($count--) { $traceOffset = $traceMatches[0][$count][1]; @@ -750,10 +749,10 @@ protected function collate() $regex_replacements['~ ?(@[^;]+\;)~'] = "$1$EOL"; // Trim leading spaces on @-rules and some tokens. - $regex_replacements[Regex::make('~ +([@}]|\?[rc]{{token-id}}\?)~S')] = "$1"; + $regex_replacements[Regex::make('~ +([@}]|\?[rc]{{token_id}}\?)~S')] = "$1"; // Additional newline between adjacent rules and comments. - $regex_replacements[Regex::make('~({{r-token}}) (\s*) ({{c-token}})~xS')] = "$1$EOL$2$3"; + $regex_replacements[Regex::make('~({{r_token}}) (\s*) ({{c_token}})~xS')] = "$1$EOL$2$3"; } // Apply all formatting replacements. @@ -912,7 +911,7 @@ public function generateSourceMap() $this->sourceMap['sources'][] = Util::getLinkBetweenPaths($this->output->dir, $source, false); } - $token_patt = Regex::make('~\?[tm]{{token-id}}\?~S'); + $token_patt = Regex::make('~\?[tm]{{token_id}}\?~S'); $mappings = array(); $lines = preg_split(Regex::$patt->newline, $this->string->raw); $tokens =& $this->tokens->store; diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php index 42b4f22..4c228a6 100644 --- a/lib/CssCrush/Regex.php +++ b/lib/CssCrush/Regex.php @@ -59,8 +59,8 @@ public static function init() $patt->rooted_number = '~^' . $classes->number . '$~'; // @-rules. - $patt->import = Regex::make('~@import \s+ ({{u-token}}) \s? ([^;]*);~ixS'); - $patt->charset = Regex::make('~@charset \s+ ({{s-token}}) \s*;~ixS'); + $patt->import = Regex::make('~@import \s+ ({{u_token}}) \s? ([^;]*);~ixS'); + $patt->charset = Regex::make('~@charset \s+ ({{s_token}}) \s*;~ixS'); $patt->mixin = Regex::make('~@mixin \s+ (?{{ident}}) \s* {{block}}~ixS'); $patt->fragmentCapture = Regex::make('~@fragment \s+ (?{{ident}}) \s* {{block}}~ixS'); $patt->fragmentInvoke = Regex::make('~@fragment \s+ (?{{ident}}) {{parens}}? \s* ;~ixS'); @@ -89,23 +89,15 @@ public static function init() public static function make($pattern) { - static $cache = array(), $pattern_map; + static $cache = array(); if (isset($cache[$pattern])) { return $cache[$pattern]; } - if (! $pattern_map) { - $pattern_map = array(); - foreach (self::$classes as $name => $regex_class) { - $pattern_map[str_replace('_', '-', $name)] = $regex_class; - } - } - - return $cache[$pattern] = preg_replace_callback( - '~\{\{ *(?[\w-]+) *\}\}~S', function ($m) use ($pattern_map) { - return $pattern_map[$m['name']]; - }, $pattern); + return $cache[$pattern] = preg_replace_callback('~\{\{ *(?\w+) *\}\}~S', function ($m) { + return Regex::$classes->{ $m['name'] }; + }, $pattern); } public static function matchAll($patt, $subject, $offset = 0) diff --git a/lib/CssCrush/Tokens.php b/lib/CssCrush/Tokens.php index ddbae90..cd794ff 100644 --- a/lib/CssCrush/Tokens.php +++ b/lib/CssCrush/Tokens.php @@ -71,7 +71,7 @@ public function createLabel($type) public function restore($str, $types, $release = false, $callback = null) { $types = implode('', (array) $types); - $patt = Regex::make("~\?[$types]{{ token-id }}\?~S"); + $patt = Regex::make("~\?[$types]{{ token_id }}\?~S"); $tokens = $this; $callback = $callback ?: function ($m) use ($tokens, $release) { return $release ? $tokens->pop($m[0]) : $tokens->get($m[0]); @@ -96,7 +96,7 @@ public function capture($str, $type) public function captureUrls($str, $add_padding = false) { $count = preg_match_all( - Regex::make('~@import \s+ (?{{s-token}}) | {{LB}} (?url|data-uri) {{parens}}~ixS'), + Regex::make('~@import \s+ (?{{s_token}}) | {{LB}} (?url|data-uri) {{parens}}~ixS'), $str, $m, PREG_OFFSET_CAPTURE); @@ -146,7 +146,7 @@ public static function pad($label, $replaced_text) public static function is($label, $of_type) { - if (preg_match(Regex::make('~^ \? (?[a-zA-Z]) {{token-id}} \? $~xS'), $label, $m)) { + if (preg_match(Regex::make('~^ \? (?[a-zA-Z]) {{token_id}} \? $~xS'), $label, $m)) { return $of_type ? ($of_type === $m['type']) : true; } From 358a72387c68c09421fca8b282f4e6368a247002 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 10 May 2014 13:47:08 +0100 Subject: [PATCH 277/421] Protocoled import directives are now hoisted to the top of output. As per spec: http://www.w3.org/TR/CSS21/cascade.html#at-import most browsers ignore import directives that are not before all other rules. --- lib/CssCrush/Importer.php | 6 +++--- lib/CssCrush/Process.php | 10 ++++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index 24cc838..9c20462 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -53,14 +53,14 @@ public static function hostfile() continue; } - // Create import object for convenience. $import = new \stdClass(); $import->url = $process->tokens->get($match[1][0]); $import->media = trim($match[2][0]); - // Skip import if the import URL is protocoled. + // Protocoled import urls are not processed. Stash for prepending to output. if ($import->url->protocol) { - $search_offset = $match_end; + $str = substr_replace($str, '', $match_start, $match_len); + $process->absoluteImports[] = $import; continue; } diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 0baf8b6..4656234 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -22,6 +22,7 @@ public function __construct($user_options = array(), $dev_options = array()) $this->mixins = array(); $this->fragments = array(); $this->references = array(); + $this->absoluteImports = array(); $this->charset = null; $this->sources = array(); $this->vars = array(); @@ -807,6 +808,15 @@ protected function collate() } } + if ($this->absoluteImports) { + $absoluteImports = ''; + $closing = $minify ? ';' : ";$EOL"; + foreach ($this->absoluteImports as $import) { + $absoluteImports .= "@import $import->url" . ($import->media ? " $import->media" : '') . $closing; + } + $this->string->prepend($absoluteImports); + } + if ($options->boilerplate) { $this->string->prepend($this->getBoilerplate()); } From b5708122608ebe394a53e4d3cc98368f27aa7564 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 3 Jun 2014 12:50:38 +0100 Subject: [PATCH 278/421] Updating docs. --- docs/core/auto-prefixing.md | 2 +- docs/core/block-nesting.md | 46 +++++++++++++++++++++++-------------- docs/core/variables.md | 12 +++++----- 3 files changed, 36 insertions(+), 24 deletions(-) diff --git a/docs/core/auto-prefixing.md b/docs/core/auto-prefixing.md index 5cb9e58..9ba9045 100644 --- a/docs/core/auto-prefixing.md +++ b/docs/core/auto-prefixing.md @@ -1,6 +1,6 @@ diff --git a/docs/core/block-nesting.md b/docs/core/block-nesting.md index 12254d5..53cc0e9 100644 --- a/docs/core/block-nesting.md +++ b/docs/core/block-nesting.md @@ -4,34 +4,46 @@ }--> -Block nesting is done with the `@in` directive. Especially useful for when you need to group lots of styles under a common selector prefix. +Rules can be nested to avoid repetitive typing when scoping to a common parent selector. -Note use of the parent selector `&`: - -```crush2 -@in .homepage { - @in .content { +```crush +.homepage { + color: #333; + background: white; + .content { p { font-size: 110%; } } - &.blue { - color: powderblue; - } - .no-js & { - max-width: 1024px; - } } ``` -```crush +```css +.homepage { + color: #333; + background: white; +} .homepage .content p { font-size: 110%; } -.homepage.blue { - color: powderblue; +``` + +## Parent referencing + +Sometimes when nesting it can be convenient to use the parent selector in different ways. The parent reference symbol `&` can be used for this. + +```crush +.homepage { + .no-js & { + p { + font-size: 110%; + } + } } -.no-js .homepage { - max-width: 1024px; +``` + +```css +.no-js .homepage p { + font-size: 110%; } ``` diff --git a/docs/core/variables.md b/docs/core/variables.md index 4a4d906..c2b99ba 100644 --- a/docs/core/variables.md +++ b/docs/core/variables.md @@ -4,14 +4,14 @@ }--> -Declare variables in your CSS with a `@define` directive and use them with the `$()` function. +Declare variables in your CSS with a `@set` directive and use them with the `$()` function. Variables can also be injected at runtime with the [vars option](#api--options). ```crush /* Defining variables */ -@define { +@set { dark: #333; light: #F4F2E2; smaller-screen: screen and (max-width: 800px); @@ -38,12 +38,12 @@ Variables can also be injected at runtime with the [vars option](#api--options). ## Conditionals -Sections of CSS can be included and excluded on the basis of variable existence with the `@ifdefine` directive: +Sections of CSS can be included and excluded on the basis of variable existence with the `@ifset` directive: ```crush -@define foo #f00; +@set foo #f00; -@ifdefine foo { +@ifset foo { p { color: $(foo); } @@ -51,7 +51,7 @@ Sections of CSS can be included and excluded on the basis of variable existence p { font-size: 12px; - @ifdefine not foo { + @ifset not foo { line-height: 1.5; } } From a51b138606716f9b5cdcded20dc70bd795ddb187 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 3 Jun 2014 14:32:46 +0100 Subject: [PATCH 279/421] Some refactoring of Importer. --- lib/CssCrush/Importer.php | 29 +++++++++++++++++------------ lib/CssCrush/Process.php | 4 ++-- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index 9c20462..6d3b20c 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -8,9 +8,16 @@ class Importer { - public static function hostfile() + protected $process; + + public function __construct(Process $process) { - $process = Crush::$process; + $this->process = $process; + } + + public function collate() + { + $process = $this->process; $options = $process->options; $regex = Regex::$patt; $input = $process->input; @@ -32,7 +39,7 @@ public static function hostfile() } // If there's a parsing error go no further. - if (! self::prepareForStream($str)) { + if (! self::prepareImport($str)) { return $str; } @@ -88,7 +95,7 @@ public static function hostfile() $filenames[] = $import->url->value; // If the import content doesn't pass syntax validation skip to next import. - if (! self::prepareForStream($import->content)) { + if (! self::prepareImport($import->content)) { $str = substr_replace($str, '', $match_start, $match_len); continue; @@ -146,11 +153,9 @@ public static function hostfile() static protected function rewriteImportedUrls($import) { - $link = Util::getLinkBetweenPaths( - Crush::$process->input->dir, dirname($import->path)); + $link = Util::getLinkBetweenPaths(Crush::$process->input->dir, dirname($import->path)); if (empty($link)) { - return; } @@ -168,19 +173,19 @@ static protected function rewriteImportedUrls($import) } } - static protected function prepareForStream(&$str) + static protected function prepareImport(&$str) { $regex = Regex::$patt; $process = Crush::$process; $tokens = $process->tokens; - // Convert all end-of-lines to unix style. + // Convert all EOL to unix style. $str = preg_replace('~\r\n?~', "\n", $str); - // rtrim is necessary to avoid catastrophic backtracking in large files and some edge cases. + // Necessary to avoid catastrophic backtracking in large files and some edge cases. $str = rtrim(self::captureCommentAndString($str)); - if (! self::checkSyntax($str)) { + if (! self::syntaxCheck($str)) { $str = ''; return false; @@ -209,7 +214,7 @@ static protected function prepareForStream(&$str) return true; } - static protected function checkSyntax(&$str) + static protected function syntaxCheck(&$str) { // Catch obvious typing errors. $errors = false; diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 4656234..9e3df21 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -868,8 +868,8 @@ public function compile() { $this->preCompile(); - // Collate hostfile and imports. - $this->string = new StringObject(Importer::hostfile($this->input)); + $importer = new Importer($this); + $this->string = new StringObject($importer->collate()); $this->captureVars(); From b2483707cfb64d95f774acb786e56ca548bde3db Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 3 Jun 2014 15:09:26 +0100 Subject: [PATCH 280/421] Default output filename now uses `.crush.css` suffix only when outputting to the same directory as input. Otherwise a regular `.css` suffix is used. --- lib/CssCrush/IO.php | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/CssCrush/IO.php b/lib/CssCrush/IO.php index 09b2767..f3d9805 100644 --- a/lib/CssCrush/IO.php +++ b/lib/CssCrush/IO.php @@ -22,22 +22,26 @@ public function init() public function getOutputDir() { - $output_dir = $this->process->options->output_dir; + $outputDir = $this->process->options->output_dir; - return $output_dir ? $output_dir : $this->process->input->dir; + return $outputDir ? $outputDir : $this->process->input->dir; } public function getOutputFileName() { $options = $this->process->options; - $output_basename = basename($this->process->input->filename, '.css'); + $outputBasename = basename($this->process->input->filename, '.css'); if (! empty($options->output_file)) { - $output_basename = basename($options->output_file, '.css'); + $outputBasename = basename($options->output_file, '.css'); } - return "$output_basename.crush.css"; + if ($this->process->input->dir === $this->getOutputDir()) { + $outputBasename .= '.crush'; + } + + return "$outputBasename.css"; } public function getOutputUrl() From 6db87198f57c70deae4cd63a49b1e128f5c48ba5 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 3 Jun 2014 15:22:59 +0100 Subject: [PATCH 281/421] Doc updates. --- docs/core/{block-nesting.md => nesting.md} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename docs/core/{block-nesting.md => nesting.md} (76%) diff --git a/docs/core/block-nesting.md b/docs/core/nesting.md similarity index 76% rename from docs/core/block-nesting.md rename to docs/core/nesting.md index 53cc0e9..8bf9665 100644 --- a/docs/core/block-nesting.md +++ b/docs/core/nesting.md @@ -30,9 +30,9 @@ Rules can be nested to avoid repetitive typing when scoping to a common parent s ## Parent referencing -Sometimes when nesting it can be convenient to use the parent selector in different ways. The parent reference symbol `&` can be used for this. +You can use the parent reference symbol `&` for placing the parent selector explicitly. -```crush +```crush2 .homepage { .no-js & { p { From f9d32ff742368ba27b7ee75b2ec322842c496c36 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 4 Jun 2014 09:17:22 +0100 Subject: [PATCH 282/421] Importer refactoring. --- lib/CssCrush/Importer.php | 80 +++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 41 deletions(-) diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index 6d3b20c..4bd0a73 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -39,7 +39,7 @@ public function collate() } // If there's a parsing error go no further. - if (! self::prepareImport($str)) { + if (! $this->prepareImport($str)) { return $str; } @@ -95,7 +95,7 @@ public function collate() $filenames[] = $import->url->value; // If the import content doesn't pass syntax validation skip to next import. - if (! self::prepareImport($import->content)) { + if (! $this->prepareImport($import->content)) { $str = substr_replace($str, '', $match_start, $match_len); continue; @@ -126,7 +126,7 @@ public function collate() // Optionally rewrite relative url and custom function data-uri references. if ($options->rewrite_import_urls) { - self::rewriteImportedUrls($import); + $this->rewriteImportedUrls($import); } if ($import->media) { @@ -151,9 +151,9 @@ public function collate() return $str; } - static protected function rewriteImportedUrls($import) + protected function rewriteImportedUrls($import) { - $link = Util::getLinkBetweenPaths(Crush::$process->input->dir, dirname($import->path)); + $link = Util::getLinkBetweenPaths($this->process->input->dir, dirname($import->path)); if (empty($link)) { return; @@ -164,28 +164,27 @@ static protected function rewriteImportedUrls($import) foreach ($matches[0] as $token) { - $url = Crush::$process->tokens->get($token); + $url = $this->process->tokens->get($token); if ($url->isRelative) { - // Prepend the relative url prefix. $url->prepend($link); } } } - static protected function prepareImport(&$str) + protected function prepareImport(&$str) { $regex = Regex::$patt; - $process = Crush::$process; + $process = $this->process; $tokens = $process->tokens; // Convert all EOL to unix style. $str = preg_replace('~\r\n?~', "\n", $str); // Necessary to avoid catastrophic backtracking in large files and some edge cases. - $str = rtrim(self::captureCommentAndString($str)); + $str = rtrim($this->captureCommentAndString($str)); - if (! self::syntaxCheck($str)) { + if (! $this->syntaxCheck($str)) { $str = ''; return false; @@ -207,18 +206,18 @@ static protected function prepareImport(&$str) $str = $tokens->captureUrls($str, true); - self::addMarkers($str); + $this->addMarkers($str); $str = Util::normalizeWhiteSpace($str); return true; } - static protected function syntaxCheck(&$str) + protected function syntaxCheck(&$str) { // Catch obvious typing errors. $errors = false; - $current_file = 'file://' . end(Crush::$process->sources); + $current_file = 'file://' . end($this->process->sources); $balanced_parens = substr_count($str, "(") === substr_count($str, ")"); $balanced_curlies = substr_count($str, "{") === substr_count($str, "}"); @@ -274,10 +273,11 @@ static protected function syntaxCheck(&$str) return $errors ? false : true; } - static protected function addMarkers(&$str) + protected function addMarkers(&$str) { - $process = Crush::$process; - $current_file_index = count($process->sources) -1; + $process = $this->process; + $currentFileIndex = count($process->sources) - 1; + static $patt; if (! $patt) { $patt = Regex::make('~ @@ -300,65 +300,63 @@ static protected function addMarkers(&$str) $count = preg_match_all($patt, $str, $matches, PREG_OFFSET_CAPTURE); while ($count--) { - $selector_offset = $matches['selector'][$count][1]; + $selectorOffset = $matches['selector'][$count][1]; $line = 0; - $before = substr($str, 0, $selector_offset); - if ($selector_offset) { + $before = substr($str, 0, $selectorOffset); + if ($selectorOffset) { $line = substr_count($before, "\n"); } - $point_data = array($current_file_index, $line); + $pointData = array($currentFileIndex, $line); // Source maps require column index too. if ($process->generateMap) { - $point_data[] = strlen($before) - strrpos($before, "\n") - 1; + $pointData[] = strlen($before) - strrpos($before, "\n") - 1; } // Splice in marker token (packing point_data into string is more memory efficient). $str = substr_replace( $str, - $process->tokens->add(implode(',', $point_data), 't'), - $selector_offset, + $process->tokens->add(implode(',', $pointData), 't'), + $selectorOffset, 0); } } - static protected function captureCommentAndString($str) + protected function captureCommentAndString($str) { - $callback = function ($m) { + $process = $this->process; + $callback = function ($m) use ($process) { - $full_match = $m[0]; - $process = Crush::$process; + $fullMatch = $m[0]; - if (strpos($full_match, '/*') === 0) { + if (strpos($fullMatch, '/*') === 0) { // Bail without storing comment if output is minified or a private comment. - if ( - $process->minifyOutput || - strpos($full_match, '/*$') === 0 - ) { - return Tokens::pad('', $full_match); + if ($process->minifyOutput || strpos($fullMatch, '/*$') === 0) { + + return Tokens::pad('', $fullMatch); } // Fix broken comments as they will break any subsquent // imported files that are inlined. - if (! preg_match('~\*/$~', $full_match)) { - $full_match .= '*/'; + if (! preg_match('~\*/$~', $fullMatch)) { + $fullMatch .= '*/'; } - $label = $process->tokens->add($full_match, 'c'); + $label = $process->tokens->add($fullMatch, 'c'); } else { // Fix broken strings as they will break any subsquent // imported files that are inlined. - if ($full_match[0] !== $full_match[strlen($full_match)-1]) { - $full_match .= $full_match[0]; + if ($fullMatch[0] !== $fullMatch[strlen($fullMatch)-1]) { + $fullMatch .= $fullMatch[0]; } - $label = $process->tokens->add($full_match, 's'); + $label = $process->tokens->add($fullMatch, 's'); } - return Tokens::pad($label, $full_match); + return Tokens::pad($label, $fullMatch); }; return preg_replace_callback(Regex::$patt->commentAndString, $callback, $str); From da0691a54db7bf52a6a1878f2a4eed984b16d068 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 5 Jun 2014 09:30:43 +0100 Subject: [PATCH 283/421] Deprecated the static api methods in favour of the csscrush_* functions. Much refactoring. --- CssCrush.php | 8 +- README.md | 2 +- lib/CssCrush/Color.php | 2 +- lib/CssCrush/Crush.php | 163 ++++---------------------------------- lib/CssCrush/File.php | 45 +++++++++++ lib/CssCrush/IO.php | 33 ++++---- lib/CssCrush/Importer.php | 6 -- lib/CssCrush/Process.php | 26 +++++- lib/CssCrush/Util.php | 8 +- lib/functions.php | 136 ++++++++++++++++++++++++++----- plugins/initial.php | 2 +- 11 files changed, 229 insertions(+), 202 deletions(-) create mode 100644 lib/CssCrush/File.php diff --git a/CssCrush.php b/CssCrush.php index c109956..94e1497 100644 --- a/CssCrush.php +++ b/CssCrush.php @@ -4,20 +4,16 @@ * Bootstrap file with autoloader. * */ -function csscrush_autoload($class) { +spl_autoload_register(function ($class) { - // We're only autoloading this library. if (stripos($class, 'csscrush') !== 0) { return; } - // Tolerate some cases of lowercasing. $class = str_ireplace('csscrush', 'CssCrush', $class); $subpath = implode('/', array_map('ucfirst', explode('\\', $class))); require_once __DIR__ . "/lib/$subpath.php"; -} - -spl_autoload_register('csscrush_autoload'); +}); require_once 'lib/functions.php'; diff --git a/README.md b/README.md index b19b866..b4b3a2e 100644 --- a/README.md +++ b/README.md @@ -7,10 +7,10 @@ CSS-Crush is a standards inspired preprocessor designed to enable a modern and u * Automatic vendor prefixing * Variables * Import inlining +* Nesting * Functions (color manipulation, math, data-uris etc.) * Rule inheritance (@extends) * Mixins -* Block nesting * Minification * Lightweight plugin system diff --git a/lib/CssCrush/Color.php b/lib/CssCrush/Color.php index 8cdc880..6d28b2e 100644 --- a/lib/CssCrush/Color.php +++ b/lib/CssCrush/Color.php @@ -13,7 +13,7 @@ class Color public static function getKeywords() { if (! isset(self::$keywords)) { - if ($keywords = Util::loadIni('misc/color-keywords.ini')) { + if ($keywords = Util::parseIni(Crush::$dir . '/misc/color-keywords.ini')) { foreach ($keywords as $keyword => $rgb) { self::$keywords[$keyword] = array_map('floatval', explode(',', $rgb)) + array(0,0,0,1); } diff --git a/lib/CssCrush/Crush.php b/lib/CssCrush/Crush.php index 1255400..0d0a515 100644 --- a/lib/CssCrush/Crush.php +++ b/lib/CssCrush/Crush.php @@ -109,10 +109,7 @@ public static function loadAssets() public static function parseAliasesFile($file) { - $tree = @parse_ini_file($file, true); - - if ($tree === false) { - notice("[[CssCrush]] - Could not parse aliases file '$file'."); + if (! ($tree = Util::parseIni($file, true))) { return false; } @@ -177,180 +174,54 @@ public static function parseAliasesFile($file) return $tree + self::$config->bareAliases; } - - ############################# - # Public API. - /** - * Process host CSS file and return a new compiled file. + * Deprecated. * - * @param string $file URL or System path to the host CSS file. - * @param mixed $options An array of options or null. - * @return string The public path to the compiled file or an empty string. + * @see csscrush_file(). */ public static function file($file, $options = array()) { - self::$process = new Process($options, array('io_context' => 'file')); - - $process = self::$process; - $options = $process->options; - - $process->input->raw = $file; - - if (! ($input_file = Util::resolveUserPath($file))) { - warning('[[CssCrush]] - Input file \'' . basename($file) . '\' not found.'); - - return ''; - } - - if (! $process->resolveContext(dirname($input_file), $input_file)) { - - return ''; - } - - Crush::runStat('paths'); - - if ($options->cache) { - $process->cacheData = $process->io->getCacheData(); - if ($process->io->validateCache()) { - $file_url = $process->io->getOutputUrl(); - $process->release(); - - return $file_url; - } - } - - $string = $process->compile(); - - return $process->io->write($string) ? $process->io->getOutputUrl() : ''; + return csscrush_file($file, $options); } /** - * Process host CSS file and return an HTML link tag with populated href. + * Deprecated. * - * @param string $file Absolute or relative path to the host CSS file. - * @param mixed $options An array of options or null. - * @param array $attributes An array of HTML attributes. - * @return string HTML link tag or error message inside HTML comment. + * @see csscrush_tag(). */ public static function tag($file, $options = array(), $tag_attributes = array()) { - $file = self::file($file, $options); - - if (! empty($file)) { - $tag_attributes['href'] = $file; - $tag_attributes += array( - 'rel' => 'stylesheet', - 'media' => 'all', - ); - $attrs = Util::htmlAttributes($tag_attributes, array('rel', 'href', 'media')); - - return "\n"; - } - else { - // Return an HTML comment with message on failure - $class = __CLASS__; - $errors = implode("\n", self::$process->errors); - - return "\n"; - } + return csscrush_tag($file, $options, $tag_attributes); } /** - * Process host CSS file and return CSS as text wrapped in html style tags. + * Deprecated. * - * @param string $file Absolute or relative path to the host CSS file. - * @param mixed $options An array of options or null. - * @param array $attributes An array of HTML attributes, set false to return CSS text without tag. - * @return string HTML link tag or error message inside HTML comment. + * @see csscrush_inline(). */ public static function inline($file, $options = array(), $tag_attributes = array()) { - // For inline output set boilerplate to not display by default. - if (! is_array($options)) { - $options = array(); - } - if (! isset($options['boilerplate'])) { - $options['boilerplate'] = false; - } - - $file = self::file($file, $options); - - if (! empty($file)) { - - // On success fetch the CSS text. - $content = file_get_contents(self::$process->output->dir . '/' . self::$process->output->filename); - $tag_open = ''; - $tag_close = ''; - - if (is_array($tag_attributes)) { - $attr_string = Util::htmlAttributes($tag_attributes); - $tag_open = ""; - $tag_close = ''; - } - return "$tag_open{$content}$tag_close\n"; - } - else { - - // Return an HTML comment with message on failure. - $class = __CLASS__; - $errors = implode("\n", self::$process->errors); - return "\n"; - } + return csscrush_inline($file, $options, $tag_attributes); } /** - * Compile a raw string of CSS string and return it. + * Deprecated. * - * @param string $string CSS text. - * @param mixed $options An array of options or null. - * @return string CSS text. + * @see csscrush_string(). */ public static function string($string, $options = array()) { - // Set boilerplate to not display by default. - if (! isset($options['boilerplate'])) { - $options['boilerplate'] = false; - } - - self::$process = new Process($options, array('io_context' => 'filter')); - - $process = self::$process; - $options = $process->options; - - if (! empty($options->context)) { - $process->resolveContext($options->context); - } - else { - $process->resolveContext(); - } - - // Set the string on the input object. - $process->input->string = $string; - - // Import files may be ignored. - if (isset($options->no_import)) { - $process->input->importIgnore = true; - } - - return $process->compile()->__toString(); + return csscrush_string($string, $options); } /** - * Get debug info. + * Deprecated. + * + * @see csscrush_stat(). */ public static function stat() { - $process = Crush::$process; - $stats = $process->stat; - - // Get logged errors as late as possible. - $stats['errors'] = $process->errors; - $stats += array( - 'compile_time' => 0, - ); - - return $stats; + return csscrush_stat(); } diff --git a/lib/CssCrush/File.php b/lib/CssCrush/File.php new file mode 100644 index 0000000..28403d8 --- /dev/null +++ b/lib/CssCrush/File.php @@ -0,0 +1,45 @@ +process = $process; + $io = $process->io; + + Crush::runStat('paths'); + + if ($process->options->cache) { + $process->cacheData = $io->getCacheData(); + if ($io->validateCache()) { + $this->url = $io->getOutputUrl(); + $this->path = $io->getOutputDir() . '/' . $io->getOutputFilename(); + $process->release(); + + return; + } + } + + $string = $process->compile(); + + if ($io->write($string)) { + $this->url = $io->getOutputUrl(); + $this->path = $io->getOutputDir() . '/' . $io->getOutputFilename(); + } + } + + public function __toString() + { + return $this->url; + } +} diff --git a/lib/CssCrush/IO.php b/lib/CssCrush/IO.php index f3d9805..df35390 100644 --- a/lib/CssCrush/IO.php +++ b/lib/CssCrush/IO.php @@ -27,7 +27,7 @@ public function getOutputDir() return $outputDir ? $outputDir : $this->process->input->dir; } - public function getOutputFileName() + public function getOutputFilename() { $options = $this->process->options; @@ -77,11 +77,12 @@ public function validateCache() $process = $this->process; $options = $process->options; $input = $process->input; - $output = $process->output; - $filename = $output->filename; + $dir = $this->getOutputDir(); + $filename = $this->getOutputFilename(); + $path = "$dir/$filename"; - if (! file_exists($output->dir . '/' . $filename)) { + if (! file_exists($path)) { debug('No file cached.'); return false; @@ -197,28 +198,30 @@ public function saveCacheData() public function write(StringObject $string) { $process = $this->process; - $output = $process->output; - $source_map_filename = "$output->filename.map"; + + $dir = $this->getOutputDir(); + $filename = $this->getOutputFilename(); + $sourcemapFilename = "$filename.map"; if ($process->sourceMap) { - $string->append($process->newline . "/*# sourceMappingURL=$source_map_filename */"); + $string->append($process->newline . "/*# sourceMappingURL=$sourcemapFilename */"); } - if (Util::filePutContents("$output->dir/$output->filename", $string, __METHOD__)) { + if (Util::filePutContents("$dir/$filename", $string, __METHOD__)) { - $json_encode_flags = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 0; + $jsonFlags = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 0; if ($process->sourceMap) { - Util::filePutContents("$output->dir/$source_map_filename", - json_encode($process->sourceMap, $json_encode_flags), __METHOD__); + Util::filePutContents("$dir/$sourcemapFilename", + json_encode($process->sourceMap, $jsonFlags), __METHOD__); } if ($process->options->stat_dump) { - $stat_file = is_string($process->options->stat_dump) ? - $process->options->stat_dump : "$output->dir/$output->filename.json"; + $statFile = is_string($process->options->stat_dump) ? + $process->options->stat_dump : "$dir/$filename.json"; - $GLOBALS['CSSCRUSH_STAT_FILE'] = $stat_file; - Util::filePutContents($stat_file, json_encode(csscrush_stat(), $json_encode_flags), __METHOD__); + $GLOBALS['CSSCRUSH_STAT_FILE'] = $statFile; + Util::filePutContents($statFile, json_encode(csscrush_stat(), $jsonFlags), __METHOD__); } return true; diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index 4bd0a73..c8ec6b5 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -54,12 +54,6 @@ public function collate() $match_start = $match[0][1]; $match_end = $match_start + $match_len; - // If just stripping the import statements. - if (isset($input->importIgnore)) { - $str = substr_replace($str, '', $match_start, $match_len); - continue; - } - $import = new \stdClass(); $import->url = $process->tokens->get($match[1][0]); $import->media = trim($match[2][0]); diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 9e3df21..68c614e 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -14,9 +14,6 @@ public function __construct($user_options = array(), $dev_options = array()) Crush::loadAssets(); - $dev_options += array('io_context' => 'filter'); - $this->ioContext = $dev_options['io_context']; - // Initialize properties. $this->cacheData = array(); $this->mixins = array(); @@ -49,6 +46,10 @@ public function __construct($user_options = array(), $dev_options = array()) // Options. $this->options = new Options($user_options, $config->options); + // Dev options. + $dev_options += array('type' => 'filter', 'data' => ''); + $this->ioContext = $dev_options['type']; + // Keep track of global vars to maintain cache integrity. $this->options->global_vars = $config->vars; @@ -59,6 +60,25 @@ public function __construct($user_options = array(), $dev_options = array()) $this->ruleFormatter = $this->options->__get('formatter'); $this->minifyOutput = $this->options->__get('minify'); $this->newline = $this->options->__get('newlines'); + + + if ($dev_options['type'] === 'file') { + $file = $dev_options['data']; + $this->input->raw = $file; + if (! ($inputFile = Util::resolveUserPath($file))) { + throw new \Exception('Input file \'' . basename($file) . '\' not found.'); + } + $this->resolveContext(dirname($inputFile), $inputFile); + } + elseif ($dev_options['type'] === 'filter') { + if (! empty($this->options->context)) { + $this->resolveContext($this->options->context); + } + else { + $this->resolveContext(); + } + $this->input->string = $dev_options['data']; + } } public function release() diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php index 3324b98..f9cf876 100644 --- a/lib/CssCrush/Util.php +++ b/lib/CssCrush/Util.php @@ -210,14 +210,14 @@ public static function filePutContents($file, $str) return false; } - public static function loadIni($library_relative_path, $sections = false) + public static function parseIni($path, $sections = false) { - if (! ($ini_array = @parse_ini_file(Crush::$dir . '/' . $library_relative_path, $sections))) { - notice("[[CssCrush]] - Ini file '$library_relative_path' could not be parsed."); + if (! ($result = @parse_ini_file($path, $sections))) { + notice("[[CssCrush]] - Ini file '$path' could not be parsed."); return false; } - return $ini_array; + return $result; } /* diff --git a/lib/functions.php b/lib/functions.php index 150d088..7326366 100644 --- a/lib/functions.php +++ b/lib/functions.php @@ -1,39 +1,109 @@ 'file', 'data' => $file)); + } + catch (\Exception $e) { + CssCrush\warning("[[CssCrush]] - {$e->getMessage()}"); + + return ''; + } -function csscrush_inline($file, $options = array(), $attributes = array()) { - return Crush::inline($file, $options, $attributes); + return new CssCrush\File(Crush::$process); } -function csscrush_string($string, $options = array()) { - return Crush::string($string, $options); + +/** + * Process CSS file and return an HTML link tag with populated href. + * + * @param string $file Absolute or relative path to the host CSS file. + * @param mixed $options An array of options or null. + * @param array $tag_attributes An array of HTML attributes. + * @return string HTML link tag or error message inside HTML comment. + */ +function csscrush_tag($file, $options = array(), $tag_attributes = array()) { + + $file = csscrush_file($file, $options); + if ($file && $file->url) { + $tag_attributes['href'] = $file->url; + $tag_attributes += array( + 'rel' => 'stylesheet', + 'media' => 'all', + ); + $attrs = CssCrush\Util::htmlAttributes($tag_attributes, array('rel', 'href', 'media')); + + return "\n"; + } } -function csscrush_stat() { - return Crush::stat(); + +/** + * Process CSS file and return CSS as text wrapped in html style tags. + * + * @param string $file Absolute or relative path to the host CSS file. + * @param mixed $options An array of options or null. + * @param array $attributes An array of HTML attributes, set false to return CSS text without tag. + * @return string HTML link tag or error message inside HTML comment. + */ +function csscrush_inline($file, $options = array(), $tag_attributes = array()) { + + if (! is_array($options)) { + $options = array(); + } + if (! isset($options['boilerplate'])) { + $options['boilerplate'] = false; + } + + $file = csscrush_file($file, $options); + if ($file && $file->path) { + $tagOpen = ''; + $tagClose = ''; + if (is_array($tag_attributes)) { + $attrs = CssCrush\Util::htmlAttributes($tag_attributes); + $tagOpen = ""; + $tagClose = ''; + } + return $tagOpen . file_get_contents($file->path) . $tagClose . "\n"; + } } -function csscrush_version($use_git = false) { - if ($use_git && $version = \CssCrush\Version::gitDescribe()) { - return $version; + +/** + * Compile a raw string of CSS string and return it. + * + * @param string $string CSS text. + * @param mixed $options An array of options or null. + * @return string CSS text. + */ +function csscrush_string($string, $options = array()) { + + if (! isset($options['boilerplate'])) { + $options['boilerplate'] = false; } - return Crush::$config->version; + + Crush::$process = new CssCrush\Process($options, array('type' => 'filter', 'data' => $string)); + + return Crush::$process->compile()->__toString(); } + /** * Set default options and config settings. * @@ -57,6 +127,7 @@ function csscrush_set($object_name, $modifier) { } } + /** * Get default options and config settings. * @@ -70,14 +141,41 @@ function csscrush_get($object_name, $property = null) { $pointer = $object_name === 'options' ? Crush::$config->options : Crush::$config; if (! isset($property)) { - return $pointer; } else { - return isset($pointer->{$property}) ? $pointer->{$property} : null; } } - return null; } + + +/** + * Get version information. + * + * @param string $use_git Return version as reported by command `git describe`. + */ +function csscrush_version($use_git = false) { + + if ($use_git && $version = \CssCrush\Version::gitDescribe()) { + return $version; + } + return Crush::$config->version; +} + + +/** + * Get stats from most recent compile. + */ +function csscrush_stat() { + + $process = Crush::$process; + $stats = $process->stat; + + // Get logged errors as late as possible. + $stats['errors'] = $process->errors; + $stats += array('compile_time' => 0); + + return $stats; +} diff --git a/plugins/initial.php b/plugins/initial.php index c9454f3..837c531 100644 --- a/plugins/initial.php +++ b/plugins/initial.php @@ -16,7 +16,7 @@ function initial(Rule $rule) { static $initial_values; if (! $initial_values) { - if (! ($initial_values = Util::loadIni('misc/initial-values.ini'))) { + if (! ($initial_values = Util::parseIni(Crush::$dir . '/misc/initial-values.ini'))) { return; } } From ce2635d0b9639f5504493e8afc74c97ec46af193 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 5 Jun 2014 15:53:51 +0100 Subject: [PATCH 284/421] Added `csscrush_add_function()` as a simple way of adding custom functions without plugins. --- lib/CssCrush/Crush.php | 6 ------ lib/CssCrush/Functions.php | 31 ++++++++++++++++------------- lib/CssCrush/Options.php | 2 +- lib/CssCrush/Process.php | 19 +++++++++--------- lib/CssCrush/Util.php | 12 +++++++----- lib/functions.php | 40 ++++++++++++++++++++++++++++++++++++++ tests/bootstrap.php | 1 - 7 files changed, 75 insertions(+), 36 deletions(-) diff --git a/lib/CssCrush/Crush.php b/lib/CssCrush/Crush.php index 0d0a515..7de4f5a 100644 --- a/lib/CssCrush/Crush.php +++ b/lib/CssCrush/Crush.php @@ -19,22 +19,17 @@ class Crush // Library root directory. public static $dir; - // Init called once manually post class definition. public static function init() { self::$dir = dirname(dirname(__DIR__)); self::$config = new \stdClass(); - // Plugin directories. self::$config->pluginDirs = array(self::$dir . '/plugins'); - self::$config->version = new Version(self::VERSION); self::$config->scriptDir = dirname(realpath($_SERVER['SCRIPT_FILENAME'])); self::$config->docRoot = self::resolveDocRoot(); self::$config->logger = new Logger(); - - // Set default IO handler. self::$config->io = 'CssCrush\IO'; // Shared resources. @@ -51,7 +46,6 @@ public static function init() self::$config->plugins = array(); self::$config->options = new Options(); - // Register stock formatters. require_once self::$dir . '/misc/formatters.php'; } diff --git a/lib/CssCrush/Functions.php b/lib/CssCrush/Functions.php index 2470ca2..7a01347 100644 --- a/lib/CssCrush/Functions.php +++ b/lib/CssCrush/Functions.php @@ -46,10 +46,10 @@ public function remove($name) unset($this->register[$name]); } - public function setPattern($useBuiltins = false) + public function setPattern($useAll = false) { - if ($useBuiltins) { - $this->register = self::$builtins + $this->register; + if ($useAll) { + $this->register = self::$builtins + $this->register + csscrush_add_function(); } $this->pattern = Functions::makePattern(array_keys($this->register)); @@ -79,11 +79,9 @@ public function apply($str, \stdClass $context = null) continue; } - $opening_paren = $parens[0][1]; - $closing_paren = $opening_paren + strlen($parens[0][0]); - - // Get the function arguments. - $raw_args = trim($parens['parens_content'][0]); + $openingParen = $parens[0][1]; + $closingParen = $openingParen + strlen($parens[0][0]); + $rawArgs = trim($parens['parens_content'][0]); // Update the context function identifier. if ($context) { @@ -93,10 +91,15 @@ public function apply($str, \stdClass $context = null) $returns = ''; if (isset($this->register[$function])) { $fn = $this->register[$function]; - $returns = $fn($raw_args, $context); + if (is_array($fn) && !empty($fn['parse_args'])) { + $returns = $fn['callback'](self::parseArgs($rawArgs), $context); + } + else { + $returns = $fn($rawArgs, $context); + } } - $str = substr_replace($str, $returns, $offset, $closing_paren - $offset); + $str = substr_replace($str, $returns, $offset, $closingParen - $offset); } return $str; @@ -110,14 +113,16 @@ public static function parseArgs($input, $allowSpaceDelim = false) { $options = array(); if ($allowSpaceDelim) { - $options['regex'] = '\s*[,\s]\s*'; + $options['regex'] = Regex::$patt->argListSplit; } return Util::splitDelimList($input, $options); } - // Intended as a quick arg-list parse for function that take up-to 2 arguments - // with the proviso the first argument is an ident. + /* + Quick argument list parsing for functions that take 1 or 2 arguments + with the proviso the first argument is an ident. + */ public static function parseArgsSimple($input) { return preg_split(Regex::$patt->argListSplit, $input, 2); diff --git a/lib/CssCrush/Options.php b/lib/CssCrush/Options.php index dc58da0..86fa6bf 100644 --- a/lib/CssCrush/Options.php +++ b/lib/CssCrush/Options.php @@ -93,7 +93,7 @@ public function __set($name, $value) case 'asset_dir': if (is_string($value)) { $value = Util::resolveUserPath($value, function ($path) use ($name) { - if (! @mkdir($path)) { + if (! @mkdir($path, 0755, true)) { notice("[[CssCrush]] - Could not create directory $path (setting `$name` option)."); } else { diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 68c614e..14187c8 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -8,7 +8,7 @@ class Process { - public function __construct($user_options = array(), $dev_options = array()) + public function __construct($user_options = array(), $context = array()) { $config = Crush::$config; @@ -46,9 +46,9 @@ public function __construct($user_options = array(), $dev_options = array()) // Options. $this->options = new Options($user_options, $config->options); - // Dev options. - $dev_options += array('type' => 'filter', 'data' => ''); - $this->ioContext = $dev_options['type']; + // Context options. + $context += array('type' => 'filter', 'data' => ''); + $this->ioContext = $context['type']; // Keep track of global vars to maintain cache integrity. $this->options->global_vars = $config->vars; @@ -61,23 +61,22 @@ public function __construct($user_options = array(), $dev_options = array()) $this->minifyOutput = $this->options->__get('minify'); $this->newline = $this->options->__get('newlines'); - - if ($dev_options['type'] === 'file') { - $file = $dev_options['data']; + if ($context['type'] === 'file') { + $file = $context['data']; $this->input->raw = $file; - if (! ($inputFile = Util::resolveUserPath($file))) { + if (! ($inputFile = Util::resolveUserPath($file, null, $this->docRoot))) { throw new \Exception('Input file \'' . basename($file) . '\' not found.'); } $this->resolveContext(dirname($inputFile), $inputFile); } - elseif ($dev_options['type'] === 'filter') { + elseif ($context['type'] === 'filter') { if (! empty($this->options->context)) { $this->resolveContext($this->options->context); } else { $this->resolveContext(); } - $this->input->string = $dev_options['data']; + $this->input->string = $context['data']; } } diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php index f9cf876..749131f 100644 --- a/lib/CssCrush/Util.php +++ b/lib/CssCrush/Util.php @@ -75,20 +75,22 @@ public static function simplifyPath($path) return $path; } - public static function resolveUserPath($path, $recovery = null) + public static function resolveUserPath($path, $recovery = null, $docRoot = null) { // System path. if ($realpath = realpath($path)) { $path = $realpath; } else { - $doc_root = isset(Crush::$process) ? Crush::$process->docRoot : Crush::$config->docRoot; + if (! $docRoot) { + $docRoot = isset(Crush::$process->docRoot) ? Crush::$process->docRoot : Crush::$config->docRoot; + } // Absolute path. if (strpos($path, '/') === 0) { // If $path is not doc_root based assume it's doc_root relative and prepend doc_root. - if (strpos($path, $doc_root) !== 0) { - $path = $doc_root . $path; + if (strpos($path, $docRoot) !== 0) { + $path = $docRoot . $path; } } // Relative path. Try resolving based on the directory of the executing script. @@ -151,7 +153,7 @@ public static function splitDelimList($str, $options = array()) $str = str_replace($matches[0], $keys, $str); } - $list = $regex ? preg_split('~' . $regex . '~', $str) : explode($delim, $str); + $list = $regex ? preg_split($regex, $str) : explode($delim, $str); if ($match_count) { foreach ($list as &$value) { diff --git a/lib/functions.php b/lib/functions.php index 7326366..a7fb8f7 100644 --- a/lib/functions.php +++ b/lib/functions.php @@ -151,6 +151,46 @@ function csscrush_get($object_name, $property = null) { } +/** + * Add custom CSS functions. + * + * Custom functions added this way are stored on a stack and used by any + * subsequent compilations within the duration of the script. + * + * @param mixed $function_name Name of CSS function, or null to clear all CSS + * functions added by `csscrush_add_function()`. + * @param mixed $callback CSS function callback, or null to remove function + * named `$function_name`. If CSS function call contains arguments + * they are passed to `$callback` as a string. + */ +function csscrush_add_function($function_name = null, $callback = null) { + + static $stack = array(); + + if (! func_num_args()) { + return $stack; + } + + if (! $function_name) { + $stack = array(); + return; + } + + $function_name = strtolower($function_name); + if (! $callback) { + if (isset($stack[$function_name])) { + unset($stack[$function_name]); + } + } + else { + $stack[$function_name] = array( + 'callback' => $callback, + 'parse_args' => true, + ); + } +} + + /** * Get version information. * diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 05e6741..829d63d 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -18,7 +18,6 @@ function bootstrap_process($options = array()) { $process = \CssCrush\Crush::$process = new \CssCrush\Process($options); $process->preCompile(); - $process->resolveContext(); return $process; } From 8ffd96d3617161b7d15428a6a97c9ef7caa1bb55 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 10 Jun 2014 15:36:02 +0100 Subject: [PATCH 285/421] Updating docs. --- docs/api/functions.md | 53 ++++++++++++++++++++++++++---- docs/core/functions/a-adjust.md | 2 +- docs/core/functions/data-uri.md | 2 +- docs/core/functions/h-adjust.md | 2 +- docs/core/functions/hsl-adjust.md | 2 +- docs/core/functions/hsla-adjust.md | 4 +-- docs/core/functions/l-adjust.md | 2 +- docs/core/functions/percent.md | 2 +- docs/core/functions/query.md | 2 +- docs/core/functions/s-adjust.md | 2 +- docs/core/functions/this.md | 2 +- docs/core/selector-aliases.md | 13 ++++---- docs/plugins/noise.md | 2 +- lib/functions.php | 37 ++++++--------------- 14 files changed, 75 insertions(+), 52 deletions(-) diff --git a/docs/api/functions.md b/docs/api/functions.md index 0951596..c45dbf7 100644 --- a/docs/api/functions.md +++ b/docs/api/functions.md @@ -11,6 +11,8 @@ Process host CSS file and return the compiled file URL. csscrush_file( string $file [, array [$options](#api--options) ] ) +*************** + ## csscrush_tag() Process host CSS file and return an HTML link tag with populated href. @@ -18,6 +20,8 @@ Process host CSS file and return an HTML link tag with populated href. csscrush_tag( string $file [, array [$options](#api--options) [, array $attributes ]] ) +*************** + ## csscrush_inline() Process host CSS file and return CSS as text wrapped in html `style` tags. @@ -25,6 +29,8 @@ Process host CSS file and return CSS as text wrapped in html `style` tags. csscrush_inline( string $file [, array [$options](#api--options) [, array $attributes ]] ) +*************** + ## csscrush_string() Compile a raw string of CSS string and return it. @@ -32,35 +38,70 @@ Compile a raw string of CSS string and return it. csscrush_string( string $string [, array [$options](#api--options) ] ) -## csscrush_stat() +*************** -Retrieve statistics from the most recent compiled file. Available stats include `selector_count`, `rule_count` and `compile_time`. +## csscrush\_add_function() -`csscrush_stat()` +Add custom CSS functions. + +Custom functions added this way are stored on a stack and used by any +subsequent compilations within the duration of the script. + +`csscrush_add_function( string $function_name, callable $callback = null )` +### Parameters + + * `$function_name` Name of CSS function, or null to clear all CSS + * `$callback` CSS function callback, or null to remove function named `$function_name`. + +`callback ( array $arguments, stdClass $context )` + + +*************** ## csscrush_version() Get the library version. -`csscrush_version()` +`csscrush_version( [bool $use_git = false] )` + +### Parameters + + * `$use_git` Return version as reported by shell command `git describe`. + +*************** ## csscrush_get() Retrieve a config setting or option default. +`csscrush_get( string $object_name, string $property )` + +### Parameters + * `$object_name` Name of object you want to inspect: 'config' or 'options'. * `$property` -`csscrush_get( string $object_name, string $property )` +*************** ## csscrush_set() Set a config setting or option default. +`csscrush_set( string $object_name, mixed $settings )` + +### Parameters + * `$object_name` Name of object you want to modify: 'config' or 'options'. * `$settings` Assoc array of keys and values to set, or callable which argument is the object specified in `$object_name`. -`csscrush_set( string $object_name, mixed $settings )` + +*************** + +## csscrush_stat() + +Get compilation stats from the most recent compiled file. + +`csscrush_stat()` diff --git a/docs/core/functions/a-adjust.md b/docs/core/functions/a-adjust.md index eb4bf75..dec37cc 100644 --- a/docs/core/functions/a-adjust.md +++ b/docs/core/functions/a-adjust.md @@ -8,7 +8,7 @@ Manipulate the opacity (alpha channel) of a color value. a-adjust( *color*, *offset* ) -## Params +## Parameters * *`color`* Any valid CSS color value * *`offset`* The percentage to offset the color opacity diff --git a/docs/core/functions/data-uri.md b/docs/core/functions/data-uri.md index 5677bba..f982b99 100644 --- a/docs/core/functions/data-uri.md +++ b/docs/core/functions/data-uri.md @@ -8,7 +8,7 @@ Create a data-uri. data-uri( *url* ) -## Params +## Parameters * *`url`* URL of an asset diff --git a/docs/core/functions/h-adjust.md b/docs/core/functions/h-adjust.md index f71c42d..df43d9f 100644 --- a/docs/core/functions/h-adjust.md +++ b/docs/core/functions/h-adjust.md @@ -8,7 +8,7 @@ Adjust the hue of a color value. h-adjust( *color*, *offset* ) -## Params +## Parameters * *`color`* Any valid CSS color value * *`offset`* The percentage to offset the color hue (percent mark optional) diff --git a/docs/core/functions/hsl-adjust.md b/docs/core/functions/hsl-adjust.md index c613ccc..a7fc86f 100644 --- a/docs/core/functions/hsl-adjust.md +++ b/docs/core/functions/hsl-adjust.md @@ -8,7 +8,7 @@ Manipulate the hue, saturation and lightness of a color value hsl-adjust( *color*, *hue-offset*, *saturation-offset*, *lightness-offset* ) -## Params +## Parameters * *`color`* Any valid CSS color value * *`hue-offset`* The percentage to offset the color hue diff --git a/docs/core/functions/hsla-adjust.md b/docs/core/functions/hsla-adjust.md index 6532c8e..819b56a 100644 --- a/docs/core/functions/hsla-adjust.md +++ b/docs/core/functions/hsla-adjust.md @@ -6,9 +6,9 @@ Manipulate the hue, saturation, lightness and opacity of a color value. -hsl-adjust( *color*, *hue-offset*, *saturation-offset*, *lightness-offset*, *alpha-offset* = 0 ) +hsla-adjust( *color*, *hue-offset*, *saturation-offset*, *lightness-offset*, *alpha-offset* ) -## Params +## Parameters * *`color`* Any valid CSS color value * *`hue-offset`* The percentage to offset the color hue diff --git a/docs/core/functions/l-adjust.md b/docs/core/functions/l-adjust.md index 2958d55..572e20b 100644 --- a/docs/core/functions/l-adjust.md +++ b/docs/core/functions/l-adjust.md @@ -8,7 +8,7 @@ Adjust the lightness of a color value. l-adjust( *color*, *offset* ) -## Params +## Parameters * *`color`* Any valid CSS color value * *`offset`* The percentage to offset the color hue (percent mark optional) diff --git a/docs/core/functions/percent.md b/docs/core/functions/percent.md index 95c7722..80c53f6 100644 --- a/docs/core/functions/percent.md +++ b/docs/core/functions/percent.md @@ -8,7 +8,7 @@ Calculate a percentage value based on two given values. percent( *value1*, *value2* [, *precision* = 5] ) -## Params +## Parameters * *`value1`* Number * *`value2`* Number diff --git a/docs/core/functions/query.md b/docs/core/functions/query.md index e9a0d8b..18ce2fc 100644 --- a/docs/core/functions/query.md +++ b/docs/core/functions/query.md @@ -8,7 +8,7 @@ Copy a value from another rule. query( *reference* [, *property-name* = default] [, *fallback*] ) -## Params +## Parameters * *`reference`* A CSS selector to match, or abstract rule name * *`property-name`* The CSS property name to copy, or just 'default' to pass over. Defaults to the calling property diff --git a/docs/core/functions/s-adjust.md b/docs/core/functions/s-adjust.md index 406d05f..0e582bf 100644 --- a/docs/core/functions/s-adjust.md +++ b/docs/core/functions/s-adjust.md @@ -8,7 +8,7 @@ Adjust the saturation of a color value. s-adjust( *color*, *offset* ) -## Params +## Parameters * *`color`* Any valid CSS color value * *`offset`* The percentage to offset the color hue (percent mark optional) diff --git a/docs/core/functions/this.md b/docs/core/functions/this.md index 9f79a01..5b72379 100644 --- a/docs/core/functions/this.md +++ b/docs/core/functions/this.md @@ -10,7 +10,7 @@ Restricted to referencing properties that don't already reference other properti this( *property-name*, *fallback* ) -## Params +## Parameters * *`property-name`* Property name * *`fallback`* A CSS value diff --git a/docs/core/selector-aliases.md b/docs/core/selector-aliases.md index 19bada8..5cdf848 100644 --- a/docs/core/selector-aliases.md +++ b/docs/core/selector-aliases.md @@ -6,18 +6,17 @@ Selector aliases can be useful for grouping together common selector chains for reuse. -They're defined with the `@selector-alias` directive, and can be used anywhere you might use a psuedo class. +They're defined with the `@selector` directive, and can be used anywhere you might use a psuedo class. ```crush -/* Defining selector aliases */ -@selector-alias heading :any(h1, h2, h3, h4, h5, h6); -@selector-alias radio input[type="radio"]; -@selector-alias hocus :any(:hover, :focus); +@selector heading :any(h1, h2, h3, h4, h5, h6); +@selector radio input[type="radio"]; +@selector hocus :any(:hover, :focus); /* Selector aliases with arguments */ -@selector-alias class-prefix :any([class^="#(0)"], [class*=" #(0)"]); -@selector-alias col :class-prefix(-col); +@selector class-prefix :any([class^="#(0)"], [class*=" #(0)"]); +@selector col :class-prefix(-col); .sidebar :heading { color: honeydew; diff --git a/docs/plugins/noise.md b/docs/plugins/noise.md index 02e8c20..177d896 100644 --- a/docs/plugins/noise.md +++ b/docs/plugins/noise.md @@ -17,7 +17,7 @@ noise/turbulence( ) ``` -### Params +### Parameters * **fill-color** - Any valid CSS color value. * **size** - Pixel size of canvas in format WxH (e.g. 320x480). diff --git a/lib/functions.php b/lib/functions.php index a7fb8f7..d015f1f 100644 --- a/lib/functions.php +++ b/lib/functions.php @@ -11,9 +11,7 @@ class_alias('CssCrush\Crush', 'CssCrush\CssCrush'); /** * Process CSS file and return a new compiled file. * - * @param string $file URL or System path to the host CSS file. - * @param mixed $options An array of options or null. - * @return string The public path to the compiled file or an empty string. + * @see docs/api/functions.md */ function csscrush_file($file, $options = array()) { @@ -33,10 +31,7 @@ function csscrush_file($file, $options = array()) { /** * Process CSS file and return an HTML link tag with populated href. * - * @param string $file Absolute or relative path to the host CSS file. - * @param mixed $options An array of options or null. - * @param array $tag_attributes An array of HTML attributes. - * @return string HTML link tag or error message inside HTML comment. + * @see docs/api/functions.md */ function csscrush_tag($file, $options = array(), $tag_attributes = array()) { @@ -57,10 +52,7 @@ function csscrush_tag($file, $options = array(), $tag_attributes = array()) { /** * Process CSS file and return CSS as text wrapped in html style tags. * - * @param string $file Absolute or relative path to the host CSS file. - * @param mixed $options An array of options or null. - * @param array $attributes An array of HTML attributes, set false to return CSS text without tag. - * @return string HTML link tag or error message inside HTML comment. + * @see docs/api/functions.md */ function csscrush_inline($file, $options = array(), $tag_attributes = array()) { @@ -88,9 +80,7 @@ function csscrush_inline($file, $options = array(), $tag_attributes = array()) { /** * Compile a raw string of CSS string and return it. * - * @param string $string CSS text. - * @param mixed $options An array of options or null. - * @return string CSS text. + * @see docs/api/functions.md */ function csscrush_string($string, $options = array()) { @@ -107,8 +97,7 @@ function csscrush_string($string, $options = array()) { /** * Set default options and config settings. * - * @param string $object_name Name of object you want to modify: 'config' or 'options'. - * @param mixed $modifier Assoc array of keys and values to set, or callable which is passed the object. + * @see docs/api/functions.md */ function csscrush_set($object_name, $modifier) { @@ -131,8 +120,7 @@ function csscrush_set($object_name, $modifier) { /** * Get default options and config settings. * - * @param string $object_name Name of object you want to modify: 'config' or 'options'. - * @param mixed $property The property name to retrieve. + * @see docs/api/functions.md */ function csscrush_get($object_name, $property = null) { @@ -154,14 +142,7 @@ function csscrush_get($object_name, $property = null) { /** * Add custom CSS functions. * - * Custom functions added this way are stored on a stack and used by any - * subsequent compilations within the duration of the script. - * - * @param mixed $function_name Name of CSS function, or null to clear all CSS - * functions added by `csscrush_add_function()`. - * @param mixed $callback CSS function callback, or null to remove function - * named `$function_name`. If CSS function call contains arguments - * they are passed to `$callback` as a string. + * @see docs/api/functions.md */ function csscrush_add_function($function_name = null, $callback = null) { @@ -194,7 +175,7 @@ function csscrush_add_function($function_name = null, $callback = null) { /** * Get version information. * - * @param string $use_git Return version as reported by command `git describe`. + * @see docs/api/functions.md */ function csscrush_version($use_git = false) { @@ -207,6 +188,8 @@ function csscrush_version($use_git = false) { /** * Get stats from most recent compile. + * + * @see docs/api/functions.md */ function csscrush_stat() { From 57f8d79dedfc63592f204d4a950224dcd0d07390 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 10 Jun 2014 19:35:31 +0100 Subject: [PATCH 286/421] Added test. Some doc updates. --- docs/api/functions.md | 16 ++++++++-------- tests/unit/api/apiTest.php | 17 +++++++++++++++++ 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/docs/api/functions.md b/docs/api/functions.md index c45dbf7..1d7901c 100644 --- a/docs/api/functions.md +++ b/docs/api/functions.md @@ -6,7 +6,7 @@ ## csscrush_file() -Process host CSS file and return the compiled file URL. +Process CSS file and return the compiled file URL. csscrush_file( string $file [, array [$options](#api--options) ] ) @@ -15,18 +15,18 @@ Process host CSS file and return the compiled file URL. ## csscrush_tag() -Process host CSS file and return an HTML link tag with populated href. +Process CSS file and return an html `link` tag with populated href. -csscrush_tag( string $file [, array [$options](#api--options) [, array $attributes ]] ) +csscrush_tag( string $file [, array [$options](#api--options) [, array $tag\_attributes ]] ) *************** ## csscrush_inline() -Process host CSS file and return CSS as text wrapped in html `style` tags. +Process CSS file and return CSS as text wrapped in html `style` tags. -csscrush_inline( string $file [, array [$options](#api--options) [, array $attributes ]] ) +csscrush_inline( string $file [, array [$options](#api--options) [, array $tag\_attributes ]] ) *************** @@ -51,8 +51,8 @@ subsequent compilations within the duration of the script. ### Parameters - * `$function_name` Name of CSS function, or null to clear all CSS - * `$callback` CSS function callback, or null to remove function named `$function_name`. + * `$function_name` Name of CSS function, or `null` to clear all functions added with `csscrush_add_function()`. + * `$callback` CSS function callback, or `null` to remove function named `$function_name`. `callback ( array $arguments, stdClass $context )` @@ -95,7 +95,7 @@ Set a config setting or option default. ### Parameters * `$object_name` Name of object you want to modify: 'config' or 'options'. - * `$settings` Assoc array of keys and values to set, or callable which argument is the object specified in `$object_name`. + * `$settings` Associative array of keys and values to set, or callable which argument is the object specified in `$object_name`. *************** diff --git a/tests/unit/api/apiTest.php b/tests/unit/api/apiTest.php index 581caf2..84835eb 100644 --- a/tests/unit/api/apiTest.php +++ b/tests/unit/api/apiTest.php @@ -115,4 +115,21 @@ public function testGetSet() csscrush_set('options', array('enable' => array())); } + + public function testAddFunction() + { + csscrush_add_function(null); + + $this->assertEquals(array(), csscrush_add_function()); + + csscrush_add_function('foo', function ($arguments) {return 'bar';}); + + $functions = csscrush_add_function(); + $this->assertTrue(is_callable($functions['foo']['callback'])); + + csscrush_add_function('foo', null); + + $functions = csscrush_add_function(); + $this->assertFalse(isset($functions['foo'])); + } } From c14837f1f0cf0afcb7e917a904debce23c6d70ee Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 11 Jun 2014 09:40:20 +0100 Subject: [PATCH 287/421] Some changes to internal options handling. --- lib/CssCrush/Options.php | 14 +++++++++++--- tests/unit/CssCrush/OptionsTest.php | 14 ++++++-------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/lib/CssCrush/Options.php b/lib/CssCrush/Options.php index 86fa6bf..c2785a4 100644 --- a/lib/CssCrush/Options.php +++ b/lib/CssCrush/Options.php @@ -11,13 +11,14 @@ class Options protected $computedOptions = array(); protected $inputOptions = array(); - public static $initialOptions = array( + protected static $standardOptions = array( 'minify' => true, 'formatter' => null, 'versioning' => true, 'boilerplate' => true, 'vars' => array(), 'cache' => true, + 'context' => null, 'output_file' => null, 'output_dir' => null, 'asset_dir' => null, @@ -35,11 +36,13 @@ class Options public function __construct(array $options = array(), Options $defaults = null) { + $options = array_change_key_case($options, CASE_LOWER); + if ($defaults) { $options += $defaults->get(); } - foreach ($options + self::$initialOptions as $key => $value) { + foreach ($options + self::$standardOptions as $key => $value) { $this->__set($key, $value); } } @@ -163,6 +166,11 @@ public function __isset($name) public function get($computed = false) { - return $computed ? $this->computedOptions : $this->inputOptions; + return $computed ? $this->computedOptions : self::filter($this->inputOptions); + } + + public static function filter(array $optionsArray = null) + { + return $optionsArray ? array_intersect_key($optionsArray, self::$standardOptions) : self::$standardOptions; } } diff --git a/tests/unit/CssCrush/OptionsTest.php b/tests/unit/CssCrush/OptionsTest.php index a083bf1..d306a7b 100644 --- a/tests/unit/CssCrush/OptionsTest.php +++ b/tests/unit/CssCrush/OptionsTest.php @@ -17,17 +17,15 @@ public function setUp() public function testDefaults() { $options = new Options(); - $initial_options = Options::$initialOptions; + $standardOptions = Options::filter(); - $this->assertEquals($initial_options, $options->get()); + $this->assertEquals($standardOptions, $options->get()); - $test_options = array('enable' => array('foo', 'bar'), 'minify' => false); - $options = new Options($test_options); + $testOptions = array('enable' => array('foo', 'bar'), 'minify' => false); + $options = new Options($testOptions); - $initial_options_copy = $initial_options; - $initial_options_copy = $test_options + $initial_options_copy; - - $this->assertEquals($initial_options_copy, $options->get()); + $initialOptionsCopy = $testOptions + $standardOptions; + $this->assertEquals($initialOptionsCopy, $options->get()); } public function testBoilerplate() From 6bea9c189d9851954e76b33d5db0bafaeecfe521 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 11 Jun 2014 09:58:50 +0100 Subject: [PATCH 288/421] Added `Util::readConfigFile()` method to enable easier configration sharing between different workflows; e.g. command-line and server. --- lib/CssCrush/Util.php | 6 ++++++ lib/functions.php | 2 +- tests/unit/CssCrush/OptionsTest.php | 4 ++-- tests/unit/CssCrush/UtilTest.php | 16 ++++++++++++++++ 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php index 749131f..1e0d33f 100644 --- a/lib/CssCrush/Util.php +++ b/lib/CssCrush/Util.php @@ -222,6 +222,12 @@ public static function parseIni($path, $sections = false) return $result; } + public static function readConfigFile($path) + { + require_once $path; + return Options::filter(get_defined_vars()); + } + /* * Encode integer to Base64 VLQ. */ diff --git a/lib/functions.php b/lib/functions.php index d015f1f..614ddbf 100644 --- a/lib/functions.php +++ b/lib/functions.php @@ -90,7 +90,7 @@ function csscrush_string($string, $options = array()) { Crush::$process = new CssCrush\Process($options, array('type' => 'filter', 'data' => $string)); - return Crush::$process->compile()->__toString(); + return Crush::$process->compile(); } diff --git a/tests/unit/CssCrush/OptionsTest.php b/tests/unit/CssCrush/OptionsTest.php index d306a7b..9807177 100644 --- a/tests/unit/CssCrush/OptionsTest.php +++ b/tests/unit/CssCrush/OptionsTest.php @@ -42,8 +42,8 @@ public function testBoilerplate() 'newlines' => 'unix', )); - $this->assertContains(' * ' . csscrush_version(), $result); - $this->assertContains(" * Line breaks\n * preserved\n *", $result); + $this->assertContains(' * ' . csscrush_version(), (string) $result); + $this->assertContains(" * Line breaks\n * preserved\n *", (string) $result); } public function testFormatters() diff --git a/tests/unit/CssCrush/UtilTest.php b/tests/unit/CssCrush/UtilTest.php index e86a51c..c6b3a06 100644 --- a/tests/unit/CssCrush/UtilTest.php +++ b/tests/unit/CssCrush/UtilTest.php @@ -89,4 +89,20 @@ public function testFilePutContents() $test_file = sys_get_temp_dir() . '/' . str_replace('\\', '_', __CLASS__); $this->assertTrue(Util::filePutContents($test_file, 'Hello Mum')); } + + public function testReadConfigFile() + { + $contents = <<<'NOW_DOC' +assertArrayHasKey('enable', $options); + $this->assertArrayNotHasKey('unrecognised_option', $options); + } } From d46b2d1179eb4dca6fec37dff9117274130f5ba4 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 12 Jun 2014 14:51:49 +0100 Subject: [PATCH 289/421] Added support for a command line config file (`crushfile.php`). Some refactoring of the command line utility. --- cli.php | 454 +++++++++++++-------------- lib/CssCrush/Importer.php | 1 - lib/CssCrush/Process.php | 1 - tests/unit/api/apiTest.php | 11 +- tests/unit/cli/cliTest.php | 16 +- tests/unit/cli/context/crushfile.php | 5 + 6 files changed, 244 insertions(+), 244 deletions(-) create mode 100644 tests/unit/cli/context/crushfile.php diff --git a/cli.php b/cli.php index c53ce14..7571905 100755 --- a/cli.php +++ b/cli.php @@ -7,121 +7,26 @@ */ require_once 'CssCrush.php'; -################################################################## -## Exit statuses. - define('STATUS_OK', 0); define('STATUS_ERROR', 1); - -################################################################## -## PHP requirements check. - $version = PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION; -$required_version = 5.3; +$requiredVersion = 5.3; -if ($version < $required_version) { - - stderr(array( - "PHP version $required_version or higher is required to use this tool.", - "You are currently running PHP version $version") - ); +if ($version < $requiredVersion) { + stderr(array("PHP version $requiredVersion or higher is required to use this tool.", + "You are currently running PHP $version")); exit(STATUS_ERROR); } - -################################################################## -## Resolve options. - -$required_value_opts = array( - 'i|input|f|file', // Input file. Defaults to STDIN. - 'o|output', // Output file. Defaults to STDOUT. - 'E|enable' , - 'D|disable', - 'vars|variables', - 'formatter', - 'vendor-target', - 'context', - 'newlines', -); - -$optional_value_opts = array( - 'b|boilerplate', - 'stat-dump', - 'trace', -); - -$flag_opts = array( - 'p|pretty', - 'w|watch', - 'list', - 'help', - 'version', - 'source-map', -); - -// Create option strings for getopt(). -$short_opts = array(); -$long_opts = array(); -$join_opts = function ($opts_list, $modifier) use (&$short_opts, &$long_opts) { - foreach ($opts_list as $opt) { - foreach (explode('|', $opt) as $arg) { - if (strlen($arg) === 1) { - $short_opts[] = "$arg$modifier"; - } - else { - $long_opts[] = "$arg$modifier"; - } - } - } -}; -$join_opts($required_value_opts, ':'); -$join_opts($optional_value_opts, '::'); -$join_opts($flag_opts, ''); - - -// Parse opts. -$opts = getopt(implode($short_opts), $long_opts); -$args = new stdClass(); - -// File arguments. -$args->input_file = pick($opts, 'i', 'input', 'f', 'file'); -$args->output_file = pick($opts, 'o', 'output'); -$args->context = pick($opts, 'context'); - -// Flags. -$args->pretty = isset($opts['p']) ?: isset($opts['pretty']); -$args->watch = isset($opts['w']) ?: isset($opts['watch']); -$args->list = isset($opts['l']) ?: isset($opts['list']); -$args->help = isset($opts['h']) ?: isset($opts['help']); -$args->version = isset($opts['version']); -$args->source_map = isset($opts['source-map']); - -// Arguments that optionally accept a single value. -$args->boilerplate = pick($opts, 'b', 'boilerplate'); -$args->stat_dump = pick($opts, 'stat-dump'); -$args->trace = pick($opts, 'trace'); - -// Arguments that require a single value. -$args->formatter = pick($opts, 'formatter'); -$args->vendor_target = pick($opts, 'vendor-target'); -$args->vars = pick($opts, 'vars', 'variables'); -$args->newlines = pick($opts, 'newlines'); - -// Arguments that require a value but accept multiple values. -$args->enable_plugins = pick($opts, 'E', 'enable'); -$args->disable_plugins = pick($opts, 'D', 'disable'); - -// Detect trailing IO files from raw script arguments. -list($trailing_input_file, $trailing_output_file) = get_trailing_io_args(); - -// If detected apply, not overriding explicit IO file options. -if (! $args->input_file && $trailing_input_file) { - $args->input_file = $trailing_input_file; +try { + $args = parse_args(); } -if (! $args->output_file && $trailing_output_file) { - $args->output_file = $trailing_output_file; +catch (Exception $ex) { + stderr($ex->getMessage()); + + exit($ex->getCode()); } @@ -130,7 +35,7 @@ if ($args->version) { - stdout(csscrush_version(true)->__toString()); + stdout((string) csscrush_version(true)); exit(STATUS_OK); } @@ -145,7 +50,6 @@ $plugins = array(); foreach (CssCrush\Plugin::info() as $name => $docs) { - // Use first line of plugin doc for description. $headline = isset($docs[0]) ? $docs[0] : false; $plugins[] = colorize("$name" . ($headline ? " - $headline" : '')); } @@ -155,75 +59,20 @@ } -################################################################## -## Validate option values. - -// Filepath arguments. -if ($args->input_file) { - $input_file = $args->input_file; - if (! ($args->input_file = realpath($args->input_file))) { - stderr("Input file '$input_file' does not exist."); - - exit(STATUS_ERROR); - } -} - -if ($args->output_file) { - $out_dir = dirname($args->output_file); - if (! realpath($out_dir) && ! @mkdir($out_dir)) { - stderr('Output directory does not exist and could not be created.'); - - exit(STATUS_ERROR); - } - $args->output_file = realpath($out_dir) . '/' . basename($args->output_file); -} - -if ($args->context) { - if (! ($args->context = realpath($args->context))) { - stderr('Context path does not exist.'); - - exit(STATUS_ERROR); - } -} - -if (is_string($args->boilerplate)) { - - if (! ($args->boilerplate = realpath($args->boilerplate))) { - stderr('Boilerplate file does not exist.'); - - exit(STATUS_ERROR); - } -} - -// Run multiple value arguments through array cast. -foreach (array('enable_plugins', 'disable_plugins', 'vendor_target') as $arg) { - if ($args->{$arg}) { - $args->{$arg} = (array) $args->{$arg}; - } -} - - ################################################################## ## Resolve input. $input = null; -// File input. if ($args->input_file) { $input = file_get_contents($args->input_file); } +elseif ($stdin = get_stdin_contents()) { -// STDIN. -elseif ($stdin_contents = get_stdin_contents()) { - - $input = $stdin_contents; + $input = $stdin; } - -// Bail with manpage if no input. else { - - // No input, just output help screen. stdout(manpage()); exit(STATUS_OK); @@ -237,118 +86,112 @@ exit(STATUS_ERROR); } + ################################################################## -## Set process options. +## Resolve process options. -$process_opts = array(); -$process_opts['boilerplate'] = isset($args->boilerplate) ? $args->boilerplate : false; -$process_opts['minify'] = $args->pretty ? false : true; +$configFile = 'crushfile.php'; +if (file_exists($configFile)) { + $options = CssCrush\Util::readConfigFile($configFile); +} +else { + $options = array(); +} -if ($args->formatter) { - $process_opts['formatter'] = $args->formatter; +if ($args->pretty) { + $options['minify'] = false; } -if ($args->newlines) { - $process_opts['newlines'] = $args->newlines; +foreach (array('boilerplate', 'formatter', 'newlines', 'stat_dump', 'source_map') as $option) { + if ($args->$option) { + $options[$option] = $args->$option; + } } if ($args->enable_plugins) { - $process_opts['enable'] = parse_list($args->enable_plugins); + $options['enable'] = parse_list($args->enable_plugins); } if ($args->disable_plugins) { - $process_opts['disable'] = parse_list($args->disable_plugins); + $options['disable'] = parse_list($args->disable_plugins); +} + +if ($args->vendor_target) { + $options['vendor_target'] = parse_list($args->vendor_target); } if ($args->trace) { if (is_string($args->trace)) { $args->trace = (array) $args->trace; } - $process_opts['trace'] = is_array($args->trace) ? parse_list($args->trace) : true; -} - -if ($args->stat_dump) { - $process_opts['stat_dump'] = $args->stat_dump; -} - -if ($args->vendor_target) { - $process_opts['vendor_target'] = parse_list($args->vendor_target); -} - -if ($args->source_map) { - $process_opts['source_map'] = true; + $options['trace'] = is_array($args->trace) ? parse_list($args->trace) : true; } if ($args->vars) { parse_str($args->vars, $in_vars); - $process_opts['vars'] = $in_vars; + $options['vars'] = $in_vars; } -// Resolve an input file context for relative filepaths. -if (! $args->context) { - $args->context = $args->input_file ? dirname($args->input_file) : getcwd(); +if ($args->output_file) { + $options['output_dir'] = dirname($args->output_file); + $options['output_file'] = basename($args->output_file); } -$process_opts['context'] = $args->context; -// Set document_root to the current working directory. -$process_opts['doc_root'] = getcwd(); +$options += array( + 'doc_root' => getcwd(), + 'context' => $args->context, +); -// If output file is specified set output directory and output filename. -if ($args->output_file) { - $process_opts['output_dir'] = dirname($args->output_file); - $process_opts['output_file'] = basename($args->output_file); -} ################################################################## ## Output. if ($args->watch) { - // Override the IO class. csscrush_set('config', array('io' => 'CssCrush\IO\Watch')); stdout('CONTROL-C to quit.'); // Surpress error reporting to avoid flooding the screen. error_reporting(0); - $outstanding_errors = false; + $outstandingErrors = false; while (true) { - csscrush_file($args->input_file, $process_opts); + csscrush_file($args->input_file, $options); $stats = csscrush_stat(); $changed = $stats['compile_time'] && ! $stats['errors']; $errors = $stats['errors']; - $show_errors = $errors && (! $outstanding_errors || ($outstanding_errors != $errors)); + $showErrors = $errors && (! $outstandingErrors || ($outstandingErrors != $errors)); - $output_file_display = "$stats[output_filename] ($stats[output_path])"; - $input_file_display = "$stats[input_filename] ($stats[input_path])"; + $outputFileDisplay = "$stats[output_filename] ($stats[output_path])"; + $inputFileDisplay = "$stats[input_filename] ($stats[input_path])"; - $compile_info = array(); + $compileInfo = array(); if ($stats['input_path']) { - $compile_info['input_file'] = $input_file_display; + $compileInfo['input_file'] = $inputFileDisplay; } if ($errors) { - if ($show_errors) { - $outstanding_errors = $errors; + if ($showErrors) { + $outstandingErrors = $errors; if ($stats['output_path']) { - stderr(colorize("ERROR: $output_file_display"), true, false); + stderr(colorize("ERROR: $outputFileDisplay"), true, false); } stderr($errors); } } elseif ($changed) { - stdout(colorize("FILE UPDATED: $output_file_display")); - $compile_info['compile_time'] = round($stats['compile_time'], 5) . ' seconds'; - $trace_options = isset($process_opts['trace']) ? array_flip($process_opts['trace']) : null; - $compile_info += $trace_options ? array_intersect_key($stats, $trace_options) : array(); - $outstanding_errors = false; + stdout(colorize("FILE UPDATED: $outputFileDisplay")); + $compileInfo['compile_time'] = round($stats['compile_time'], 5) . ' seconds'; + $traceOptions = isset($options['trace']) ? array_flip($options['trace']) : null; + $compileInfo += $traceOptions ? array_intersect_key($stats, $traceOptions) : array(); + $outstandingErrors = false; } - if ($show_errors || $changed) { - stdout(format_stats($compile_info)); + if ($showErrors || $changed) { + stdout(format_stats($compileInfo)); } sleep(1); @@ -356,16 +199,8 @@ } else { - $output = csscrush_string($input, $process_opts); - $stats = csscrush_stat(); - - if ($stats['errors']) { - stderr($stats['errors']); - } - if ($args->output_file) { - - if (! @file_put_contents($args->output_file, $output, LOCK_EX)) { + if (! @file_put_contents($args->output_file, csscrush_string($input, $options))) { $message[] = "Could not write to path '{$args->output_file}'."; stderr($message); @@ -373,12 +208,23 @@ exit(STATUS_ERROR); } } + elseif (isset($options['output_dir'])) { + csscrush_file($args->input_file, $options); + } else { - stdout($output); + stdout(csscrush_string($input, $options)); + } + + $stats = csscrush_stat(); + + if ($stats['errors']) { + stderr($stats['errors']); + + exit(STATUS_ERROR); } if (is_array($args->trace)) { - // Use stderror for stats to preserve stdout. + unset($stats['errors']); stderr(format_stats($stats) . PHP_EOL, true, 'b'); } @@ -390,25 +236,27 @@ ## Helpers. function stderr($lines, $closing_newline = true, $color = 'r') { + $out = implode(PHP_EOL, (array) $lines) . ($closing_newline ? PHP_EOL : ''); fwrite(STDERR, colorize($color ? "<$color>$out" : $out)); } function stdout($lines, $closing_newline = true) { + $out = implode(PHP_EOL, (array) $lines) . ($closing_newline ? PHP_EOL : ''); - // On OSX terminal is sometimes truncating 'visual' output to terminal - // with fwrite to STDOUT. + // On OSX terminal is sometimes truncating 'visual' output to terminal with fwrite to STDOUT. echo $out; } function get_stdin_contents() { + $stdin = fopen('php://stdin', 'r'); stream_set_blocking($stdin, false); - $stdin_contents = stream_get_contents($stdin); + $stdinContents = stream_get_contents($stdin); fclose($stdin); - return $stdin_contents; + return $stdinContents; } function parse_list(array $option) { @@ -428,6 +276,7 @@ function parse_list(array $option) { } function format_stats($stats) { + $out = array(); foreach ($stats as $name => $value) { $name = ucfirst(str_replace('_', ' ', $name)); @@ -490,7 +339,7 @@ function colorize($str) { return str_replace($find, $replace, $str); } -function get_trailing_io_args() { +function get_trailing_io_args($required_value_opts) { $trailing_input_file = null; $trailing_output_file = null; @@ -500,8 +349,8 @@ function get_trailing_io_args() { array_shift($trailing_args); $trailing_args = array_slice($trailing_args, -3); - // Create patterns to detecting options. - $required_values = implode('|', $GLOBALS['required_value_opts']); + // Create patterns for detecting options. + $required_values = implode('|', $required_value_opts); $value_opt_patt = "~^-{1,2}($required_values)$~"; $other_opt_patt = "~^-{1,2}([a-z0-9\-]+)?(=|$)~ix"; @@ -541,6 +390,139 @@ function get_trailing_io_args() { return array($trailing_input_file, $trailing_output_file); } +function parse_args() { + + $required_value_opts = array( + 'i|input|f|file', // Input file. Defaults to STDIN. + 'o|output', // Output file. Defaults to STDOUT. + 'E|enable' , + 'D|disable', + 'vars|variables', + 'formatter', + 'vendor-target', + 'context', + 'newlines', + ); + + $optional_value_opts = array( + 'b|boilerplate', + 'stat-dump', + 'trace', + ); + + $flag_opts = array( + 'p|pretty', + 'w|watch', + 'list', + 'help', + 'version', + 'source-map', + ); + + // Create option strings for getopt(). + $short_opts = array(); + $long_opts = array(); + $join_opts = function ($opts_list, $modifier) use (&$short_opts, &$long_opts) { + foreach ($opts_list as $opt) { + foreach (explode('|', $opt) as $arg) { + if (strlen($arg) === 1) { + $short_opts[] = "$arg$modifier"; + } + else { + $long_opts[] = "$arg$modifier"; + } + } + } + }; + $join_opts($required_value_opts, ':'); + $join_opts($optional_value_opts, '::'); + $join_opts($flag_opts, ''); + + $opts = getopt(implode($short_opts), $long_opts); + + $args = new stdClass(); + + // Information options. + $args->help = isset($opts['h']) ?: isset($opts['help']); + $args->version = isset($opts['version']); + $args->list = isset($opts['l']) ?: isset($opts['list']); + + // File arguments. + $args->input_file = pick($opts, 'i', 'input', 'f', 'file'); + $args->output_file = pick($opts, 'o', 'output'); + $args->context = pick($opts, 'context'); + + // Flags. + $args->pretty = isset($opts['p']) ?: isset($opts['pretty']); + $args->watch = isset($opts['w']) ?: isset($opts['watch']); + $args->source_map = isset($opts['source-map']); + + // Arguments that optionally accept a single value. + $args->boilerplate = pick($opts, 'b', 'boilerplate'); + $args->stat_dump = pick($opts, 'stat-dump'); + $args->trace = pick($opts, 'trace'); + + // Arguments that require a single value. + $args->formatter = pick($opts, 'formatter'); + $args->vendor_target = pick($opts, 'vendor-target'); + $args->vars = pick($opts, 'vars', 'variables'); + $args->newlines = pick($opts, 'newlines'); + + // Arguments that require a value but accept multiple values. + $args->enable_plugins = pick($opts, 'E', 'enable'); + $args->disable_plugins = pick($opts, 'D', 'disable'); + + // Run multiple value arguments through array cast. + foreach (array('enable_plugins', 'disable_plugins', 'vendor_target') as $arg) { + if ($args->{$arg}) { + $args->{$arg} = (array) $args->{$arg}; + } + } + + // Detect trailing IO files from raw script arguments. + list($trailing_input_file, $trailing_output_file) = get_trailing_io_args($required_value_opts); + + // If detected apply, not overriding explicit IO file options. + if (! $args->input_file && $trailing_input_file) { + $args->input_file = $trailing_input_file; + } + if (! $args->output_file && $trailing_output_file) { + $args->output_file = $trailing_output_file; + } + + if ($args->input_file) { + $input_file = $args->input_file; + if (! ($args->input_file = realpath($args->input_file))) { + throw new Exception("Input file '$input_file' does not exist.", STATUS_ERROR); + } + } + + if ($args->output_file) { + $out_dir = dirname($args->output_file); + if (! realpath($out_dir) && ! @mkdir($out_dir, 0755, true)) { + throw new Exception('Output directory does not exist and could not be created.', STATUS_ERROR); + } + $args->output_file = realpath($out_dir) . '/' . basename($args->output_file); + } + + if ($args->context) { + if (! ($args->context = realpath($args->context))) { + throw new Exception('Context path does not exist.', STATUS_ERROR); + } + } + else { + $args->context = $args->input_file ? dirname($args->input_file) : getcwd(); + } + + if (is_string($args->boilerplate)) { + if (! ($args->boilerplate = realpath($args->boilerplate))) { + throw new Exception('Boilerplate file does not exist.', STATUS_ERROR); + } + } + + return $args; +} + function manpage() { $manpage = << alt-styles.css # Linting. - csscrush --pretty --E property-sorter -i styles.css -o linted.css + csscrush --pretty -E property-sorter -i styles.css -o linted.css # Watch mode. csscrush --watch -i styles.css -o compiled/styles.css diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index c8ec6b5..d24143e 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -52,7 +52,6 @@ public function collate() $match_len = strlen($match[0][0]); $match_start = $match[0][1]; - $match_end = $match_start + $match_len; $import = new \stdClass(); $import->url = $process->tokens->get($match[1][0]); diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 14187c8..5ee2e99 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -305,7 +305,6 @@ protected function filterAliases() foreach ($group_array as $func_group => $vendors) { foreach ($vendors as $vendor => $replacements) { - if (! in_array($vendor, $vendor_names)) { unset($this->aliases['function_groups'][$func_group][$vendor]); } diff --git a/tests/unit/api/apiTest.php b/tests/unit/api/apiTest.php index 84835eb..d8c21a5 100644 --- a/tests/unit/api/apiTest.php +++ b/tests/unit/api/apiTest.php @@ -122,14 +122,17 @@ public function testAddFunction() $this->assertEquals(array(), csscrush_add_function()); - csscrush_add_function('foo', function ($arguments) {return 'bar';}); + csscrush_add_function('baz', function ($arguments) {return implode('-', $arguments);}); + + $result = (string) csscrush_string('.foo {bar: baz(one, two, three);}'); + $this->assertEquals('.foo{bar:one-two-three}', $result); $functions = csscrush_add_function(); - $this->assertTrue(is_callable($functions['foo']['callback'])); + $this->assertTrue(is_callable($functions['baz']['callback'])); - csscrush_add_function('foo', null); + csscrush_add_function('baz', null); $functions = csscrush_add_function(); - $this->assertFalse(isset($functions['foo'])); + $this->assertFalse(isset($functions['baz'])); } } diff --git a/tests/unit/cli/cliTest.php b/tests/unit/cli/cliTest.php index d87aa2a..f05d787 100644 --- a/tests/unit/cli/cliTest.php +++ b/tests/unit/cli/cliTest.php @@ -49,8 +49,20 @@ public function testContext() $context = __DIR__; exec("echo '$sample' | php \"$this->path\" --context '$context'", $lines); - $expected = 'foo{bar:baz}baz{bar:foo}'; - $this->assertEquals($expected, implode('',$lines)); + $this->assertEquals('foo{bar:baz}baz{bar:foo}', implode('', $lines)); + } + + public function testConfigFile() + { + $currentDirectory = getcwd(); + chdir(__DIR__ . '/context'); + + $sample = '@import "http://github.com/import.css"; @color dark #111; baz {color: dark;}'; + exec("echo '$sample' | php \"$this->path\"", $lines); + + $this->assertEquals('foo{bar:baz}baz{color:#111}', implode('', $lines)); + + chdir($currentDirectory); } } diff --git a/tests/unit/cli/context/crushfile.php b/tests/unit/cli/context/crushfile.php new file mode 100644 index 0000000..cf9b80d --- /dev/null +++ b/tests/unit/cli/context/crushfile.php @@ -0,0 +1,5 @@ + Date: Mon, 16 Jun 2014 09:36:26 +0100 Subject: [PATCH 290/421] Possible hhvm issue with `mkdir()`? --- cli.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cli.php b/cli.php index 7571905..95a3dfa 100755 --- a/cli.php +++ b/cli.php @@ -491,18 +491,18 @@ function parse_args() { } if ($args->input_file) { - $input_file = $args->input_file; + $inputFile = $args->input_file; if (! ($args->input_file = realpath($args->input_file))) { - throw new Exception("Input file '$input_file' does not exist.", STATUS_ERROR); + throw new Exception("Input file '$inputFile' does not exist.", STATUS_ERROR); } } if ($args->output_file) { - $out_dir = dirname($args->output_file); - if (! realpath($out_dir) && ! @mkdir($out_dir, 0755, true)) { + $outDir = dirname($args->output_file); + if (! realpath($outDir) && ! @mkdir($outDir)) { throw new Exception('Output directory does not exist and could not be created.', STATUS_ERROR); } - $args->output_file = realpath($out_dir) . '/' . basename($args->output_file); + $args->output_file = realpath($outDir) . '/' . basename($args->output_file); } if ($args->context) { From ec83d4f903e6b993630c4c10252573e61dc1a855 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 16 Jun 2014 10:11:16 +0100 Subject: [PATCH 291/421] Updating dev dependencies. --- cli.php | 2 +- composer.json | 4 ++-- lib/functions.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cli.php b/cli.php index 95a3dfa..7200f99 100755 --- a/cli.php +++ b/cli.php @@ -499,7 +499,7 @@ function parse_args() { if ($args->output_file) { $outDir = dirname($args->output_file); - if (! realpath($outDir) && ! @mkdir($outDir)) { + if (! realpath($outDir) && ! @mkdir($outDir, 0755, true)) { throw new Exception('Output directory does not exist and could not be created.', STATUS_ERROR); } $args->output_file = realpath($outDir) . '/' . basename($args->output_file); diff --git a/composer.json b/composer.json index dc021c8..2e97c32 100644 --- a/composer.json +++ b/composer.json @@ -16,10 +16,10 @@ } ], "require": { - "php": ">=5.3.1" + "php": ">=5.3.3" }, "require-dev": { - "phpunit/phpunit": "3.7.*@dev", + "phpunit/phpunit": "4.1.*", "psr/log": "1.0.*@dev", "twig/twig": "1.*" }, diff --git a/lib/functions.php b/lib/functions.php index 614ddbf..d015f1f 100644 --- a/lib/functions.php +++ b/lib/functions.php @@ -90,7 +90,7 @@ function csscrush_string($string, $options = array()) { Crush::$process = new CssCrush\Process($options, array('type' => 'filter', 'data' => $string)); - return Crush::$process->compile(); + return Crush::$process->compile()->__toString(); } From 48b2ce05e938dbced0c2e7a331aadd933ffdcd29 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 16 Jun 2014 10:24:52 +0100 Subject: [PATCH 292/421] Updated changelog. Added PHP 5.6 to travis CI config. --- .travis.yml | 1 + CHANGELOG.md | 15 +++++++++++++++ composer.json | 2 +- lib/CssCrush/Crush.php | 2 +- 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index cc6bb14..23e79b4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ php: - 5.3 - 5.4 - 5.5 + - 5.6 - hhvm before_script: diff --git a/CHANGELOG.md b/CHANGELOG.md index 8090b5c..ec6b8dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,18 @@ +### 2.2.0 (2014-06-17) + +* Rule nesting now works without `@in` directives. +* Added `csscrush_add_function()` as a simple way of adding custom functions without plugins. +* Added alternative directive names: `@set`/`@ifset` for `@define`/`@ifdefine` and `@selector` for `@selector-alias`. +* Added support for a command line config file (`crushfile.php`). +* Added `Util::readConfigFile()` method to enable easier configuration sharing between different workflows; esp. command-line and server. +* Protocoled `@import` directives are now hoisted to the top of output. +* Default output filename now uses `.crush.css` suffix only when outputting to the same directory as input. Otherwise a regular `.css` suffix is used. +* Updated vendor aliases. +* Removed math shorthand syntax. +* Deprecated `@in` directives. Supported until at-least 3.x. +* Deprecated `@define`/`@ifdefine`/`@selector-alias` in favour of new directive names. Supported until at-least 3.x. +* Deprecated the static api methods in favour of the `csscrush_*` functions. Supported until at-least 3.x. + ### 2.1.0 (2014-03-21) * Added HHVM support (HHVM >= 2.4) diff --git a/composer.json b/composer.json index 2e97c32..22875b6 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,7 @@ }, "extra": { "branch-alias": { - "dev-master": "2.1-dev" + "dev-master": "2.2-dev" } } } diff --git a/lib/CssCrush/Crush.php b/lib/CssCrush/Crush.php index 7de4f5a..9427076 100644 --- a/lib/CssCrush/Crush.php +++ b/lib/CssCrush/Crush.php @@ -8,7 +8,7 @@ class Crush { - const VERSION = '2.2.0-beta'; + const VERSION = '2.2.0'; // Global settings. public static $config; From 75b87e044661a8dc5eebf5f4c35c5634e59e5805 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 27 Jun 2014 20:32:27 +0100 Subject: [PATCH 293/421] Fixed issue with parent selector. --- lib/CssCrush/Process.php | 24 ++++++++++++++++-------- lib/CssCrush/Rule.php | 1 + 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 5ee2e99..64e546f 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -570,8 +570,8 @@ public function captureRules() $block = trim($ruleMatch['block_content']); $replace = ''; - // If rules are nested they must be extracted and have selectors merged with the parent. - if (preg_match_all(Regex::$patt->r_token, $block, $childRules)) { + // If rules are nested inside we set their parent property. + if (preg_match_all(Regex::$patt->r_token, $block, $childMatches)) { $block = preg_replace_callback($rulesAndMediaPatt, function ($m) use (&$replace) { $replace .= $m[0]; @@ -579,9 +579,11 @@ public function captureRules() }, $block); $rule = new Rule($selector, $block, $ruleMatch['trace_token']); - $rawSelectors = array_keys($rule->selectors->store); - foreach ($childRules[0] as $childRule) { - $tokens->get($childRule)->selectors->merge($rawSelectors); + foreach ($childMatches[0] as $childToken) { + $childRule = $tokens->get($childToken); + if (! $childRule->parent) { + $childRule->parent = $rule; + } } } else { @@ -595,6 +597,15 @@ public function captureRules() $this->string->splice($replace, $traceOffset, strlen($ruleMatch[0])); } + + // Flip, since we just captured rules in reverse order. + $this->tokens->store->r = array_reverse($this->tokens->store->r); + + foreach ($this->tokens->store->r as $rule) { + if ($rule->parent) { + $rule->selectors->merge(array_keys($rule->parent->selectors->store)); + } + } } protected function processRules() @@ -602,9 +613,6 @@ protected function processRules() // Create table of name/selector to rule references. $named_references = array(); - // Flip because rules are captured from end of file. - $this->tokens->store->r = array_reverse($this->tokens->store->r); - foreach ($this->tokens->store->r as $rule) { if ($rule->name) { $named_references[$rule->name] = $rule; diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php index b8a1fef..aaeaaab 100644 --- a/lib/CssCrush/Rule.php +++ b/lib/CssCrush/Rule.php @@ -14,6 +14,7 @@ class Rule public $name; public $isAbstract; public $resolvedExtendables; + public $parent; public $selectors; public $declarations; From 90f3cd3e5bdd84c66c8847dbb1f5f0383686e5b2 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 28 Jun 2014 10:01:52 +0100 Subject: [PATCH 294/421] Adding rebeccapurple to colors table. --- misc/color-keywords.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/misc/color-keywords.ini b/misc/color-keywords.ini index dcbed9b..e8ec1e0 100644 --- a/misc/color-keywords.ini +++ b/misc/color-keywords.ini @@ -120,6 +120,7 @@ pink = "255,192,203" plum = "221,160,221" powderblue = "176,224,230" purple = "128,0,128" +rebeccapurple = "102,51,153" red = "255,0,0" rosybrown = "188,143,143" royalblue = "65,105,225" From 7b3b77b0d90596a2ebdd94114b28f3257b23e65d Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 28 Jun 2014 12:34:48 +0100 Subject: [PATCH 295/421] Added `top` and `parent` keywords to `query()` function. `top` references the root rule in a nested rule structure. `parent` references the parent rule in a nested rule structure. Updated docs. --- docs/core/functions/a-adjust.md | 3 ++ docs/core/functions/data-uri.md | 3 ++ docs/core/functions/h-adjust.md | 2 ++ docs/core/functions/hsl-adjust.md | 2 ++ docs/core/functions/hsla-adjust.md | 2 ++ docs/core/functions/l-adjust.md | 2 ++ docs/core/functions/math.md | 2 ++ docs/core/functions/percent.md | 1 + docs/core/functions/query.md | 25 +++++++++++--- docs/core/functions/s-adjust.md | 2 ++ docs/core/functions/this.md | 1 + lib/CssCrush/Functions.php | 55 +++++++++++++++--------------- 12 files changed, 68 insertions(+), 32 deletions(-) diff --git a/docs/core/functions/a-adjust.md b/docs/core/functions/a-adjust.md index dec37cc..59d2c93 100644 --- a/docs/core/functions/a-adjust.md +++ b/docs/core/functions/a-adjust.md @@ -17,6 +17,9 @@ Manipulate the opacity (alpha channel) of a color value. The modified color value + +## Examples + ```css /* Reduce color opacity by 10% */ color: a-adjust( rgb(50,50,0) -10 ); diff --git a/docs/core/functions/data-uri.md b/docs/core/functions/data-uri.md index f982b99..702a4c2 100644 --- a/docs/core/functions/data-uri.md +++ b/docs/core/functions/data-uri.md @@ -21,6 +21,9 @@ The following file extensions are supported: jpg, jpeg, gif, png, svg, svgz, ttf The created data-uri as a string inside a CSS url(). + +## Examples + ```css background: silver data-uri(../images/stripe.png); ``` diff --git a/docs/core/functions/h-adjust.md b/docs/core/functions/h-adjust.md index df43d9f..98605c9 100644 --- a/docs/core/functions/h-adjust.md +++ b/docs/core/functions/h-adjust.md @@ -17,6 +17,8 @@ Adjust the hue of a color value. The modified color value. +## Examples + ```css color: h-adjust( deepskyblue -10 ); ``` diff --git a/docs/core/functions/hsl-adjust.md b/docs/core/functions/hsl-adjust.md index a7fc86f..bdee0fd 100644 --- a/docs/core/functions/hsl-adjust.md +++ b/docs/core/functions/hsl-adjust.md @@ -19,6 +19,8 @@ Manipulate the hue, saturation and lightness of a color value The modified color value +## Examples + ```css /* Lighten and increase saturation */ color: hsl-adjust( red 0 5 5 ); diff --git a/docs/core/functions/hsla-adjust.md b/docs/core/functions/hsla-adjust.md index 819b56a..cc05b4b 100644 --- a/docs/core/functions/hsla-adjust.md +++ b/docs/core/functions/hsla-adjust.md @@ -20,6 +20,8 @@ Manipulate the hue, saturation, lightness and opacity of a color value. The modified color value. +## Examples + ```css color: hsla-adjust( #f00 0 5 5 -10 ); ``` \ No newline at end of file diff --git a/docs/core/functions/l-adjust.md b/docs/core/functions/l-adjust.md index 572e20b..28ac701 100644 --- a/docs/core/functions/l-adjust.md +++ b/docs/core/functions/l-adjust.md @@ -17,6 +17,8 @@ Adjust the lightness of a color value. The modified color value. +## Examples + ```css color: l-adjust( deepskyblue 10 ); ``` diff --git a/docs/core/functions/math.md b/docs/core/functions/math.md index 402d2d9..491cf10 100644 --- a/docs/core/functions/math.md +++ b/docs/core/functions/math.md @@ -8,6 +8,8 @@ Evaluate a raw mathematical expression. math( *expression* [, *unit*] ) +## Examples + ```css font-size: math( 12 / 16, em ); ``` diff --git a/docs/core/functions/percent.md b/docs/core/functions/percent.md index 80c53f6..e32a730 100644 --- a/docs/core/functions/percent.md +++ b/docs/core/functions/percent.md @@ -18,6 +18,7 @@ Calculate a percentage value based on two given values. *`value1`* as a percentage of *`value2`*. +## Examples ```css width: percent( 20, 960 ); diff --git a/docs/core/functions/query.md b/docs/core/functions/query.md index 18ce2fc..f0c49a1 100644 --- a/docs/core/functions/query.md +++ b/docs/core/functions/query.md @@ -6,12 +6,12 @@ Copy a value from another rule. -query( *reference* [, *property-name* = default] [, *fallback*] ) +query( *target* [, *property-name* = default] [, *fallback*] ) ## Parameters -* *`reference`* A CSS selector to match, or abstract rule name -* *`property-name`* The CSS property name to copy, or just 'default' to pass over. Defaults to the calling property +* *`target`* A rule selector, an abstract rule name or context keyword (`top` or `parent` with nested structures) +* *`property-name`* The CSS property name to copy, or just `default` to pass over. Defaults to the calling property * *`fallback`* A CSS value to use if the target property does not exist @@ -20,6 +20,9 @@ Copy a value from another rule. The referenced property value, or the fallback if it has not been set. +## Examples + + ```css .foo { width: 40em; @@ -29,9 +32,23 @@ The referenced property value, or the fallback if it has not been set. .bar { width: query( .foo ); /* 40em */ margin-top: query( .foo, height ); /* 100em */ - margin-right: query( .foo, top, auto ); /* auto */ margin-bottom: query( .foo, default, 3em ); /* 3em */ } ``` +Using context keywords: +```css +.foo { + width: 40em; + .bar { + width: 30em; + .baz: { + width: query( parent ); /* 30em */ + .qux { + width: query( top ); /* 40em */ + } + } + } +} +``` diff --git a/docs/core/functions/s-adjust.md b/docs/core/functions/s-adjust.md index 0e582bf..8d2cce1 100644 --- a/docs/core/functions/s-adjust.md +++ b/docs/core/functions/s-adjust.md @@ -17,6 +17,8 @@ Adjust the saturation of a color value. The modified color value. +## Examples + ```css /* Desaturate */ color: s-adjust( deepskyblue -100 ); diff --git a/docs/core/functions/this.md b/docs/core/functions/this.md index 5b72379..12029b6 100644 --- a/docs/core/functions/this.md +++ b/docs/core/functions/this.md @@ -19,6 +19,7 @@ Restricted to referencing properties that don't already reference other properti The referenced property value, or the fallback if it has not been set. +## Examples ```css .foo { diff --git a/lib/CssCrush/Functions.php b/lib/CssCrush/Functions.php index 7a01347..8d26e40 100644 --- a/lib/CssCrush/Functions.php +++ b/lib/CssCrush/Functions.php @@ -280,47 +280,46 @@ function fn__query($input, $context) { $args = Functions::parseArgs($input); - // Function relies on a context property, bail if none. - if (count($args) < 1 || ! isset($context->property)) { + // Context property is required. + if (! count($args) || ! isset($context->property)) { return ''; } - $call_property = $context->property; - $references =& Crush::$process->references; + list($target, $property, $fallback) = $args + array(null, $context->property, null); - // Resolve arguments. - $name = array_shift($args); - $property = $call_property; + if (strtolower($property) === 'default') { + $property = $context->property; + } - if (isset($args[0])) { - $args[0] = strtolower($args[0]); - if ($args[0] !== 'default') { - $property = array_shift($args); - } - else { - array_shift($args); - } + if (! preg_match(Regex::$patt->rooted_ident, $target)) { + $target = Selector::makeReadable($target); } - $default = isset($args[0]) ? $args[0] : null; - if (! preg_match(Regex::$patt->rooted_ident, $name)) { - $name = Selector::makeReadable($name); + $targetRule = null; + $references =& Crush::$process->references; + $targetLowerCase = strtolower($target); + if ($targetLowerCase === 'parent') { + $targetRule = $context->rule->parent; + } + elseif ($targetLowerCase === 'top') { + $targetRule = $context->rule->parent; + while ($targetRule && $targetRule->parent && $targetRule = $targetRule->parent); + } + elseif (isset($references[$target])) { + $targetRule = $references[$target]; } - // If a rule reference is found, query its data. $result = ''; - if (isset($references[$name])) { - $query_rule = $references[$name]; - $query_rule->declarations->process($query_rule); - $query_rule->declarations->expandData('queryData', $property); - - if (isset($query_rule->declarations->queryData[$property])) { - $result = $query_rule->declarations->queryData[$property]; + if ($targetRule) { + $targetRule->declarations->process($targetRule); + $targetRule->declarations->expandData('queryData', $property); + if (isset($targetRule->declarations->queryData[$property])) { + $result = $targetRule->declarations->queryData[$property]; } } - if ($result === '' && isset($default)) { - $result = $default; + if ($result === '' && isset($fallback)) { + $result = $fallback; } return $result; From a32252526c55ac23ba0ffefa64edab72e49087ae Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 1 Jul 2014 12:30:05 +0100 Subject: [PATCH 296/421] Resolved issue with empty nested rules. --- lib/CssCrush/Process.php | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 64e546f..139bad3 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -548,7 +548,7 @@ protected function resolveFragments() public function captureRules() { - $tokens = Crush::$process->tokens; + $tokens = $this->tokens; $rulePatt = Regex::make('~ (? {{ t_token }}) @@ -590,22 +590,30 @@ public function captureRules() $rule = new Rule($selector, $block, $ruleMatch['trace_token']); } - // Store rules only if they have declarations or extend arguments. - if (! empty($rule->declarations->store) || $rule->extendArgs) { - $replace = $tokens->add($rule, 'r', $rule->label) . $replace; - } + $replace = $tokens->add($rule, 'r', $rule->label) . $replace; $this->string->splice($replace, $traceOffset, strlen($ruleMatch[0])); } // Flip, since we just captured rules in reverse order. - $this->tokens->store->r = array_reverse($this->tokens->store->r); + $tokens->store->r = array_reverse($tokens->store->r); - foreach ($this->tokens->store->r as $rule) { + foreach ($tokens->store->r as $rule) { if ($rule->parent) { $rule->selectors->merge(array_keys($rule->parent->selectors->store)); } } + + // Cleanup unused, or unuseable, rules. + $this->string->pregReplaceCallback(Regex::$patt->r_token, function ($m) use ($tokens) { + $ruleToken = $m[0]; + $rule = $tokens->store->r[$ruleToken]; + if (empty($rule->declarations->store) && ! $rule->extendArgs) { + unset($tokens->store->r[$ruleToken]); + return ''; + } + return $ruleToken; + }); } protected function processRules() From 982ecd7398b6089c457e55571a7f93761605de54 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 2 Jul 2014 16:13:22 +0100 Subject: [PATCH 297/421] Bump patch version. --- lib/CssCrush/Crush.php | 2 +- lib/CssCrush/Process.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/CssCrush/Crush.php b/lib/CssCrush/Crush.php index 9427076..54bbd9b 100644 --- a/lib/CssCrush/Crush.php +++ b/lib/CssCrush/Crush.php @@ -8,7 +8,7 @@ class Crush { - const VERSION = '2.2.0'; + const VERSION = '2.2.1'; // Global settings. public static $config; diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 139bad3..0737b85 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -604,7 +604,7 @@ public function captureRules() } } - // Cleanup unused, or unuseable, rules. + // Cleanup unusable rules. $this->string->pregReplaceCallback(Regex::$patt->r_token, function ($m) use ($tokens) { $ruleToken = $m[0]; $rule = $tokens->store->r[$ruleToken]; From bc3c871e96c92c50ccd650bcbba1d0a9c0f45af4 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 31 Jul 2014 19:44:06 +0100 Subject: [PATCH 298/421] Some improvements to logging and error reporting. --- cli.php | 122 ++++++++++++++++++++++----------- lib/CssCrush/BalancedMatch.php | 4 +- lib/CssCrush/Crush.php | 27 +++++--- lib/CssCrush/IO.php | 13 ++-- lib/CssCrush/Importer.php | 6 +- lib/CssCrush/Logger.php | 3 - lib/CssCrush/Options.php | 2 +- lib/CssCrush/Plugin.php | 2 +- lib/CssCrush/Process.php | 7 +- lib/CssCrush/Util.php | 4 +- lib/functions.php | 3 +- plugins/canvas.php | 4 +- plugins/property-sorter.php | 2 +- tests/unit/cli/cliTest.php | 6 +- 14 files changed, 127 insertions(+), 78 deletions(-) diff --git a/cli.php b/cli.php index 7200f99..87bd1ce 100755 --- a/cli.php +++ b/cli.php @@ -20,11 +20,12 @@ exit(STATUS_ERROR); } -try { +try { $args = parse_args(); } catch (Exception $ex) { - stderr($ex->getMessage()); + + stderr(message($ex->getMessage(), array('type'=>'error'))); exit($ex->getCode()); } @@ -47,13 +48,10 @@ } elseif ($args->list) { - $plugins = array(); - foreach (CssCrush\Plugin::info() as $name => $docs) { - $headline = isset($docs[0]) ? $docs[0] : false; - $plugins[] = colorize("$name" . ($headline ? " - $headline" : '')); + $headline = isset($docs[0]) ? $docs[0] : ''; + stdout(message(array($name => $headline), array('color'=>'g'))); } - stdout($plugins); exit(STATUS_OK); } @@ -81,7 +79,7 @@ if ($args->watch && ! $args->input_file) { - stderr('Watch mode requires an input file.'); + stderr(message('Watch mode requires an input file.', array('type'=>'error'))); exit(STATUS_ERROR); } @@ -146,14 +144,14 @@ ################################################################## ## Output. +error_reporting(0); + if ($args->watch) { csscrush_set('config', array('io' => 'CssCrush\IO\Watch')); stdout('CONTROL-C to quit.'); - // Surpress error reporting to avoid flooding the screen. - error_reporting(0); $outstandingErrors = false; while (true) { @@ -163,6 +161,7 @@ $changed = $stats['compile_time'] && ! $stats['errors']; $errors = $stats['errors']; + $warnings = $stats['warnings']; $showErrors = $errors && (! $outstandingErrors || ($outstandingErrors != $errors)); $outputFileDisplay = "$stats[output_filename] ($stats[output_path])"; @@ -176,22 +175,24 @@ if ($errors) { if ($showErrors) { $outstandingErrors = $errors; - if ($stats['output_path']) { - stderr(colorize("ERROR: $outputFileDisplay"), true, false); - } - stderr($errors); + stderr(message($errors, array('type'=>'error'))); } } elseif ($changed) { - stdout(colorize("FILE UPDATED: $outputFileDisplay")); + stderr(message($outputFileDisplay, array('type'=>'write'))); + $compileInfo['compile_time'] = round($stats['compile_time'], 5) . ' seconds'; $traceOptions = isset($options['trace']) ? array_flip($options['trace']) : null; $compileInfo += $traceOptions ? array_intersect_key($stats, $traceOptions) : array(); $outstandingErrors = false; } - if ($showErrors || $changed) { - stdout(format_stats($compileInfo)); + if (($showErrors || $changed) && $warnings) { + stderr(message($warnings, array('type'=>'warning'))); + } + + if ($changed) { + stderr(message($compileInfo, array('type'=>'stats'))); } sleep(1); @@ -199,33 +200,42 @@ } else { - if ($args->output_file) { - if (! @file_put_contents($args->output_file, csscrush_string($input, $options))) { - - $message[] = "Could not write to path '{$args->output_file}'."; - stderr($message); + $stdOutput = null; - exit(STATUS_ERROR); - } - } - elseif (isset($options['output_dir'])) { + if ($args->input_file && isset($options['output_dir'])) { + $options['cache'] = false; csscrush_file($args->input_file, $options); } else { - stdout(csscrush_string($input, $options)); + $stdOutput = csscrush_string($input, $options); } $stats = csscrush_stat(); + $errors = $stats['errors']; + unset($stats['errors']); + $warnings = $stats['warnings']; + unset($stats['warnings']); - if ($stats['errors']) { - stderr($stats['errors']); + if ($errors) { + stderr(message($errors, array('type'=>'error'))); exit(STATUS_ERROR); } + elseif ($args->input_file && ! empty($stats['output_filename'])) { + $outputFileDisplay = "$stats[output_filename] ($stats[output_path])"; + stderr(message($outputFileDisplay, array('type'=>'write'))); + } + + if ($warnings) { + stderr(message($warnings, array('type'=>'warning'))); + } if (is_array($args->trace)) { - unset($stats['errors']); - stderr(format_stats($stats) . PHP_EOL, true, 'b'); + stderr(message($stats, array('type'=>'stats'))); + } + + if ($stdOutput) { + stdout($stdOutput); } exit(STATUS_OK); @@ -235,18 +245,16 @@ ################################################################## ## Helpers. -function stderr($lines, $closing_newline = true, $color = 'r') { +function stderr($lines, $closing_newline = true) { $out = implode(PHP_EOL, (array) $lines) . ($closing_newline ? PHP_EOL : ''); - fwrite(STDERR, colorize($color ? "<$color>$out" : $out)); + fwrite(defined('TESTMODE') && TESTMODE ? STDOUT : STDERR, $out); } function stdout($lines, $closing_newline = true) { $out = implode(PHP_EOL, (array) $lines) . ($closing_newline ? PHP_EOL : ''); - - // On OSX terminal is sometimes truncating 'visual' output to terminal with fwrite to STDOUT. - echo $out; + fwrite(STDOUT, $out); } function get_stdin_contents() { @@ -275,13 +283,45 @@ function parse_list(array $option) { return $out; } -function format_stats($stats) { +function message($messages, $options = array()) { + + $defaults = array( + 'color' => 'b', + 'label' => null, + 'indent' => false, + 'format_label' => false, + ); + $preset = ! empty($options['type']) ? $options['type'] : null; + switch ($preset) { + case 'error': + $defaults['color'] = 'r'; + $defaults['label'] = 'ERROR'; + break; + case 'warning': + $defaults['color'] = 'y'; + $defaults['label'] = 'WARNING'; + break; + case 'write': + $defaults['color'] = 'g'; + $defaults['label'] = 'WRITE'; + break; + case 'stats': + $defaults['indent'] = true; + $defaults['format_label'] = true; + break; + } + extract($options + $defaults); $out = array(); - foreach ($stats as $name => $value) { - $name = ucfirst(str_replace('_', ' ', $name)); + foreach ((array) $messages as $_label => $value) { + $_label = $label ?: $_label; + if ($format_label) { + $_label = ucfirst(str_replace('_', ' ', $_label)); + } + $prefix = $indent ? '└── ' : ''; + $colorUp = strtoupper($color); if (is_scalar($value)) { - $out[] = colorize("└── $name: $value"); + $out[] = colorize("<$color>$prefix<$colorUp>$_label:<$color> $value"); } } return implode(PHP_EOL, $out); @@ -417,6 +457,7 @@ function parse_args() { 'help', 'version', 'source-map', + 'test', ); // Create option strings for getopt(). @@ -456,6 +497,7 @@ function parse_args() { $args->pretty = isset($opts['p']) ?: isset($opts['pretty']); $args->watch = isset($opts['w']) ?: isset($opts['watch']); $args->source_map = isset($opts['source-map']); + define('TESTMODE', isset($opts['test'])); // Arguments that optionally accept a single value. $args->boilerplate = pick($opts, 'b', 'boilerplate'); diff --git a/lib/CssCrush/BalancedMatch.php b/lib/CssCrush/BalancedMatch.php index 42cb940..e7ac516 100644 --- a/lib/CssCrush/BalancedMatch.php +++ b/lib/CssCrush/BalancedMatch.php @@ -24,7 +24,7 @@ public function __construct(StringObject $string, $offset, $brackets = '{}') if (substr_count($string->raw, $opener) !== substr_count($string->raw, $closer)) { $sample = substr($string->raw, $this->offset, 25); - warning("[[CssCrush]] - Unmatched token near '$sample'."); + warning("Unmatched token near '$sample'."); return; } @@ -40,7 +40,7 @@ public function __construct(StringObject $string, $offset, $brackets = '{}') $this->length = $this->matchEnd - $this->offset; } else { - warning("[[CssCrush]] - Could not match '$opener'. Exiting."); + warning("Could not match '$opener'. Exiting."); } } diff --git a/lib/CssCrush/Crush.php b/lib/CssCrush/Crush.php index 54bbd9b..41fbd1b 100644 --- a/lib/CssCrush/Crush.php +++ b/lib/CssCrush/Crush.php @@ -80,7 +80,7 @@ static protected function resolveDocRoot($doc_root = null) } if (! $doc_root) { - warning("[[CssCrush]] - Could not get a valid DOCUMENT_ROOT reference."); + warning("Could not get a valid DOCUMENT_ROOT reference."); } } @@ -279,21 +279,30 @@ public static function runStat() } } +function warning($message, $context = array()) { + Crush::$process->errors[] = $message; + $logger = Crush::$config->logger; + if ($logger instanceof Logger) { + $message = "[[CssCrush]] - $message"; + } + $logger->warning($message, $context); +} -function log($message, $context = array(), $type = 'debug') { - Crush::$config->logger->{$type}($message, $context); +function notice($message, $context = array()) { + Crush::$process->warnings[] = $message; + $logger = Crush::$config->logger; + if ($logger instanceof Logger) { + $message = "[[CssCrush]] - $message"; + } + $logger->notice($message, $context); } function debug($message, $context = array()) { Crush::$config->logger->debug($message, $context); } -function notice($message, $context = array()) { - Crush::$config->logger->notice($message, $context); -} - -function warning($message, $context = array()) { - Crush::$config->logger->warning($message, $context); +function log($message, $context = array(), $type = 'debug') { + Crush::$config->logger->$type($message, $context); } diff --git a/lib/CssCrush/IO.php b/lib/CssCrush/IO.php index df35390..62ad1f1 100644 --- a/lib/CssCrush/IO.php +++ b/lib/CssCrush/IO.php @@ -31,13 +31,14 @@ public function getOutputFilename() { $options = $this->process->options; - $outputBasename = basename($this->process->input->filename, '.css'); + $inputBasename = basename($this->process->input->filename, '.css'); + $outputBasename = $inputBasename; if (! empty($options->output_file)) { $outputBasename = basename($options->output_file, '.css'); } - if ($this->process->input->dir === $this->getOutputDir()) { + if ($this->process->input->dir === $this->getOutputDir() && $inputBasename === $outputBasename) { $outputBasename .= '.crush'; } @@ -149,10 +150,8 @@ public function getCacheData() { $process = $this->process; - if ( - file_exists($process->cacheFile) && - $process->cacheData - ) { + if (file_exists($process->cacheFile) && $process->cacheData) { + // Already loaded and config file exists in the current directory return; } @@ -173,7 +172,7 @@ public function getCacheData() // Config file may exist but not be writable (may not be visible in some ftp situations?) if ($cache_data_exists) { if (! @unlink($process->cacheFile)) { - notice('[[CssCrush]] - Could not delete cache data file.'); + notice('Could not delete cache data file.'); } } else { diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index d24143e..d1c591e 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -77,7 +77,7 @@ public function collate() $import->content = @file_get_contents($import->path); if ($import->content === false) { - debug("Import file '{$import->url->value}' not found"); + notice("Import file '{$import->url->value}' not found"); $str = substr_replace($str, '', $match_start, $match_len); continue; } @@ -256,11 +256,11 @@ protected function syntaxCheck(&$str) if (! $balanced_curlies) { $errors = true; - warning('[[CssCrush]] - ' . $validate_pairings($str, '{}') ?: "Unbalanced '{' in $current_file."); + warning($validate_pairings($str, '{}') ?: "Unbalanced '{' in $current_file."); } if (! $balanced_parens) { $errors = true; - warning('[[CssCrush]] - ' . $validate_pairings($str, '()') ?: "Unbalanced '(' in $current_file."); + warning($validate_pairings($str, '()') ?: "Unbalanced '(' in $current_file."); } return $errors ? false : true; diff --git a/lib/CssCrush/Logger.php b/lib/CssCrush/Logger.php index 1c40fb3..49240c2 100644 --- a/lib/CssCrush/Logger.php +++ b/lib/CssCrush/Logger.php @@ -59,7 +59,6 @@ public function critical($message, array $context = array()) */ public function error($message, array $context = array()) { - Crush::$process->errors[] = $message; trigger_error($message, E_USER_ERROR); } @@ -75,7 +74,6 @@ public function error($message, array $context = array()) */ public function warning($message, array $context = array()) { - Crush::$process->errors[] = $message; trigger_error($message, E_USER_WARNING); } @@ -88,7 +86,6 @@ public function warning($message, array $context = array()) */ public function notice($message, array $context = array()) { - Crush::$process->errors[] = $message; trigger_error($message, E_USER_NOTICE); } diff --git a/lib/CssCrush/Options.php b/lib/CssCrush/Options.php index c2785a4..694838c 100644 --- a/lib/CssCrush/Options.php +++ b/lib/CssCrush/Options.php @@ -97,7 +97,7 @@ public function __set($name, $value) if (is_string($value)) { $value = Util::resolveUserPath($value, function ($path) use ($name) { if (! @mkdir($path, 0755, true)) { - notice("[[CssCrush]] - Could not create directory $path (setting `$name` option)."); + notice("Could not create directory $path (setting `$name` option)."); } else { debug("Created directory $path (setting `$name` option)."); diff --git a/lib/CssCrush/Plugin.php b/lib/CssCrush/Plugin.php index 09e7944..8c906b2 100644 --- a/lib/CssCrush/Plugin.php +++ b/lib/CssCrush/Plugin.php @@ -68,7 +68,7 @@ public static function load($plugin_name) } if (! $found) { - notice("[[CssCrush]] - Plugin '$plugin_name' not found."); + notice("Plugin '$plugin_name' not found."); } elseif (isset(self::$plugins[$plugin_name]['load'])) { $plugin_load = self::$plugins[$plugin_name]['load']; diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 0737b85..a1d1530 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -35,8 +35,9 @@ public function __construct($user_options = array(), $context = array()) $this->selectorAliasesPatt = null; $this->io = new Crush::$config->io($this); - $this->debugLog = array(); $this->errors = array(); + $this->warnings = array(); + $this->debugLog = array(); $this->stat = array(); // Copy config values. @@ -118,7 +119,7 @@ public function resolveContext($input_dir = null, $input_file = null) $output_dir = $this->output->dir; if (! file_exists($output_dir)) { - warning("[[CssCrush]] - Output directory '$output_dir' doesn't exist."); + warning("Output directory '$output_dir' doesn't exist."); $context_resolved = false; } elseif (! is_writable($output_dir)) { @@ -126,7 +127,7 @@ public function resolveContext($input_dir = null, $input_file = null) debug('Attempting to change permissions.'); if (! @chmod($output_dir, 0755)) { - warning("[[CssCrush]] - Output directory '$output_dir' is unwritable."); + warning("Output directory '$output_dir' is unwritable."); $context_resolved = false; } else { diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php index 1e0d33f..f3a5d00 100644 --- a/lib/CssCrush/Util.php +++ b/lib/CssCrush/Util.php @@ -207,7 +207,7 @@ public static function filePutContents($file, $str) return true; } - warning("[[CssCrush]] - Could not write file '$file'."); + warning("Could not write file '$file'."); return false; } @@ -215,7 +215,7 @@ public static function filePutContents($file, $str) public static function parseIni($path, $sections = false) { if (! ($result = @parse_ini_file($path, $sections))) { - notice("[[CssCrush]] - Ini file '$path' could not be parsed."); + notice("Ini file '$path' could not be parsed."); return false; } diff --git a/lib/functions.php b/lib/functions.php index d015f1f..5a8a955 100644 --- a/lib/functions.php +++ b/lib/functions.php @@ -19,7 +19,7 @@ function csscrush_file($file, $options = array()) { Crush::$process = new CssCrush\Process($options, array('type' => 'file', 'data' => $file)); } catch (\Exception $e) { - CssCrush\warning("[[CssCrush]] - {$e->getMessage()}"); + CssCrush\warning($e->getMessage()); return ''; } @@ -198,6 +198,7 @@ function csscrush_stat() { // Get logged errors as late as possible. $stats['errors'] = $process->errors; + $stats['warnings'] = $process->warnings; $stats += array('compile_time' => 0); return $stats; diff --git a/plugins/canvas.php b/plugins/canvas.php index beeab74..b5fe950 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -612,7 +612,7 @@ function canvas_requirements() { if (! extension_loaded('gd')) { $requirements_met = false; - warning('[[CssCrush]] - GD extension not available.'); + warning('GD extension not available.'); } else { $gd_info = implode('|', array_keys(array_filter(gd_info()))); @@ -620,7 +620,7 @@ function canvas_requirements() { foreach (array('jpe?g' => 'JPG', 'png' => 'PNG') as $file_ext_patt => $file_ext) { if (! preg_match("~\b(?$file_ext_patt) support\b~i", $gd_info)) { $requirements_met = false; - warning("[[CssCrush]] - GD extension has no $file_ext support."); + warning("GD extension has no $file_ext support."); } } } diff --git a/plugins/property-sorter.php b/plugins/property-sorter.php index 1554e97..865efb1 100644 --- a/plugins/property-sorter.php +++ b/plugins/property-sorter.php @@ -127,7 +127,7 @@ function &property_sorter_get_table () { $table = preg_split('~\s+~', trim($sorting_file_contents)); } else { - notice("[[CssCrush]] - Property sorting file not found."); + notice("Property sorting file not found."); } // Store to the global variable. diff --git a/tests/unit/cli/cliTest.php b/tests/unit/cli/cliTest.php index f05d787..f391574 100644 --- a/tests/unit/cli/cliTest.php +++ b/tests/unit/cli/cliTest.php @@ -34,13 +34,13 @@ public function testPlugin() public function testIO() { $in_path = temp_file(); - $out_path = temp_file(); + $out_path = temp_file() . '.css'; file_put_contents($in_path, $this->sample); - exec("php \"$this->path\" -i '$in_path' -o '$out_path' --enable property-sorter"); + exec("php \"$this->path\" -i '$in_path' -o '$out_path' --enable property-sorter --test"); $expected = 'p{position:absolute;opacity:1;color:red}'; - $this->assertEquals($expected, file_get_contents($out_path)); + $this->assertContains($expected, file_get_contents($out_path)); } public function testContext() From e640f0691333fc3d5e826a5e38509ec2c83de177 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 1 Aug 2014 11:47:06 +0100 Subject: [PATCH 299/421] Updating cli utility. Dropped trace option, added stats option for outputing stats to STDERR. Some cleanup. --- cli.php | 136 ++++++++++++++++++++++++++------------------------------ 1 file changed, 64 insertions(+), 72 deletions(-) diff --git a/cli.php b/cli.php index 87bd1ce..3866661 100755 --- a/cli.php +++ b/cli.php @@ -14,6 +14,7 @@ $requiredVersion = 5.3; if ($version < $requiredVersion) { + stderr(array("PHP version $requiredVersion or higher is required to use this tool.", "You are currently running PHP $version")); @@ -118,13 +119,6 @@ $options['vendor_target'] = parse_list($args->vendor_target); } -if ($args->trace) { - if (is_string($args->trace)) { - $args->trace = (array) $args->trace; - } - $options['trace'] = is_array($args->trace) ? parse_list($args->trace) : true; -} - if ($args->vars) { parse_str($args->vars, $in_vars); $options['vars'] = $in_vars; @@ -164,14 +158,6 @@ $warnings = $stats['warnings']; $showErrors = $errors && (! $outstandingErrors || ($outstandingErrors != $errors)); - $outputFileDisplay = "$stats[output_filename] ($stats[output_path])"; - $inputFileDisplay = "$stats[input_filename] ($stats[input_path])"; - - $compileInfo = array(); - if ($stats['input_path']) { - $compileInfo['input_file'] = $inputFileDisplay; - } - if ($errors) { if ($showErrors) { $outstandingErrors = $errors; @@ -179,20 +165,16 @@ } } elseif ($changed) { - stderr(message($outputFileDisplay, array('type'=>'write'))); - - $compileInfo['compile_time'] = round($stats['compile_time'], 5) . ' seconds'; - $traceOptions = isset($options['trace']) ? array_flip($options['trace']) : null; - $compileInfo += $traceOptions ? array_intersect_key($stats, $traceOptions) : array(); $outstandingErrors = false; + stderr(message(fmt_fileinfo($stats, 'output'), array('type'=>'write'))); } if (($showErrors || $changed) && $warnings) { stderr(message($warnings, array('type'=>'warning'))); } - if ($changed) { - stderr(message($compileInfo, array('type'=>'stats'))); + if ($changed && $args->stats) { + stderr(message($stats, array('type'=>'stats'))); } sleep(1); @@ -212,9 +194,7 @@ $stats = csscrush_stat(); $errors = $stats['errors']; - unset($stats['errors']); $warnings = $stats['warnings']; - unset($stats['warnings']); if ($errors) { stderr(message($errors, array('type'=>'error'))); @@ -222,15 +202,14 @@ exit(STATUS_ERROR); } elseif ($args->input_file && ! empty($stats['output_filename'])) { - $outputFileDisplay = "$stats[output_filename] ($stats[output_path])"; - stderr(message($outputFileDisplay, array('type'=>'write'))); + stderr(message(fmt_fileinfo($stats, 'output'), array('type'=>'write'))); } if ($warnings) { stderr(message($warnings, array('type'=>'warning'))); } - if (is_array($args->trace)) { + if ($args->stats) { stderr(message($stats, array('type'=>'stats'))); } @@ -259,12 +238,11 @@ function stdout($lines, $closing_newline = true) { function get_stdin_contents() { - $stdin = fopen('php://stdin', 'r'); - stream_set_blocking($stdin, false); - $stdinContents = stream_get_contents($stdin); - fclose($stdin); + stream_set_blocking(STDIN, 0); + $contents = stream_get_contents(STDIN); + stream_set_blocking(STDIN, 1); - return $stdinContents; + return $contents; } function parse_list(array $option) { @@ -306,6 +284,14 @@ function message($messages, $options = array()) { $defaults['label'] = 'WRITE'; break; case 'stats': + // Making stats concise and readable. + $messages['input_file'] = $messages['input_path']; + $messages['compile_time'] = round($messages['compile_time'], 5) . ' seconds'; + foreach (array('input_filename', 'input_path', 'output_filename', + 'output_path', 'vars', 'errors', 'warnings') as $key) { + unset($messages[$key]); + } + ksort($messages); $defaults['indent'] = true; $defaults['format_label'] = true; break; @@ -327,6 +313,10 @@ function message($messages, $options = array()) { return implode(PHP_EOL, $out); } +function fmt_fileinfo($stats, $type) { + return $stats[$type . '_filename'] . ' ' . '(' . $stats[$type . '_path'] . ')'; +} + function pick(array &$arr) { $args = func_get_args(); @@ -447,7 +437,6 @@ function parse_args() { $optional_value_opts = array( 'b|boilerplate', 'stat-dump', - 'trace', ); $flag_opts = array( @@ -457,6 +446,7 @@ function parse_args() { 'help', 'version', 'source-map', + 'stats', 'test', ); @@ -497,27 +487,27 @@ function parse_args() { $args->pretty = isset($opts['p']) ?: isset($opts['pretty']); $args->watch = isset($opts['w']) ?: isset($opts['watch']); $args->source_map = isset($opts['source-map']); + $args->stats = pick($opts, 'stats'); define('TESTMODE', isset($opts['test'])); // Arguments that optionally accept a single value. $args->boilerplate = pick($opts, 'b', 'boilerplate'); $args->stat_dump = pick($opts, 'stat-dump'); - $args->trace = pick($opts, 'trace'); // Arguments that require a single value. $args->formatter = pick($opts, 'formatter'); - $args->vendor_target = pick($opts, 'vendor-target'); $args->vars = pick($opts, 'vars', 'variables'); $args->newlines = pick($opts, 'newlines'); // Arguments that require a value but accept multiple values. $args->enable_plugins = pick($opts, 'E', 'enable'); $args->disable_plugins = pick($opts, 'D', 'disable'); + $args->vendor_target = pick($opts, 'vendor-target'); // Run multiple value arguments through array cast. foreach (array('enable_plugins', 'disable_plugins', 'vendor_target') as $arg) { - if ($args->{$arg}) { - $args->{$arg} = (array) $args->{$arg}; + if ($args->$arg) { + $args->$arg = (array) $args->$arg; } } @@ -555,7 +545,6 @@ function parse_args() { else { $args->context = $args->input_file ? dirname($args->input_file) : getcwd(); } - if (is_string($args->boilerplate)) { if (! ($args->boilerplate = realpath($args->boilerplate))) { throw new Exception('Boilerplate file does not exist.', STATUS_ERROR); @@ -573,70 +562,73 @@ function manpage() { csscrush [OPTIONS] [input-file] [output-file] OPTIONS: - -i, --input: + -i, --input Input file. If omitted takes input from STDIN. - -o, --output: + -o, --output Output file. If omitted prints to STDOUT. - -p, --pretty: + -p, --pretty Formatted, un-minified output. - -w, --watch: + -w, --watch Watch input file for changes. Writes to file specified with -o option or to the input file directory with a '.crush.css' file extension. - -D, --disable: - List of plugins to disable. Pass 'all' to disable all. + -D, --disable + List of plugins (comma separated) to disable. Pass 'all' to disable all. - -E, --enable: - List of plugins to enable. Overrides --disable. + -E, --enable + List of plugins (comma separated) to enable. Overrides --disable. - --boilerplate: + --boilerplate Whether or not to output a boilerplate. Optionally accepts filepath to a custom boilerplate template. - --context: + --context Filepath context for resolving relative URLs. Only meaningful when taking raw input from STDIN. - --formatter: - Formatting styles. + --formatter + Possible values: + 'block' (default) + Rules are block formatted. + 'single-line' + Rules are printed in single lines. + 'padded' + Rules are printed in single lines with right padded selectors. - 'block' (default) - - Rules are block formatted. - 'single-line' - - Rules are printed in single lines. - 'padded' - - Rules are printed in single lines with right padded selectors. - - --help: + --help Display this help message. - --list: + --list Show plugins. - --newlines: + --newlines Force newline style on output css. Defaults to the current platform newline. Possible values: 'windows' (or 'win'), 'unix', 'use-platform'. - --source-map: - Output a source map (compliant with the Source Map v3 proposal). + --source-map + Create a source map file (compliant with the Source Map v3 proposal). - --trace: - Output debug-info stubs compatible with client-side Sass debuggers. + --stats + Display post-compile stats. - --vars: + --vars Map of variable names in an http query string format. - --vendor-target: - Set to 'all' for all vendor prefixes (default). - Set to 'none' for no vendor prefixes. - Set to a specific vendor prefix. - - --version: - Print version number. + --vendor-target + Possible values: + 'all' + For all vendor prefixes (default). + 'none' + For no vendor prefixing. + 'moz', 'webkit', 'ms' etc. + Limit to a specific vendor prefix (or comma separated list). + + --version + Display version number. EXAMPLES: # Restrict vendor prefixing. From 61855cc16fbd44cdd6b8729f5263bf6e84231ddc Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 1 Aug 2014 15:45:32 +0100 Subject: [PATCH 300/421] Adding cli utility test. --- cli.php | 2 +- lib/CssCrush/Crush.php | 8 +++++++- tests/unit/cli/cliTest.php | 11 +++++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/cli.php b/cli.php index 3866661..06c1c6e 100755 --- a/cli.php +++ b/cli.php @@ -357,7 +357,7 @@ function colorize($str) { ); if (! isset($color_support)) { - $color_support = true; + $color_support = defined('TESTMODE') && TESTMODE ? false : true; if (DIRECTORY_SEPARATOR == '\\') { $color_support = false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI'); } diff --git a/lib/CssCrush/Crush.php b/lib/CssCrush/Crush.php index 41fbd1b..23e1a77 100644 --- a/lib/CssCrush/Crush.php +++ b/lib/CssCrush/Crush.php @@ -165,7 +165,13 @@ public static function parseAliasesFile($file) } } - return $tree + self::$config->bareAliases; + $tree += self::$config->bareAliases; + + $tree['properties']['foo'] = + $tree['at-rules']['foo'] = + $tree['functions']['foo'] = array('-webkit-foo', '-moz-foo', '-ms-foo'); + + return $tree; } /** diff --git a/tests/unit/cli/cliTest.php b/tests/unit/cli/cliTest.php index f391574..18390ee 100644 --- a/tests/unit/cli/cliTest.php +++ b/tests/unit/cli/cliTest.php @@ -43,6 +43,17 @@ public function testIO() $this->assertContains($expected, file_get_contents($out_path)); } + public function testStats() + { + exec("echo '$this->sample' | php \"$this->path\" --stats --test", $lines); + $output = implode('', $lines); + + $this->assertContains('Selector count: 1', $output); + $this->assertContains('Rule count: 1', $output); + $this->assertContains('Compile time:', $output); + $this->assertContains('p{color:red;position:absolute;opacity:1}', $output); + } + public function testContext() { $sample = '@import "http://github.com/context/import.css"; baz {bar: foo;}'; From 8ecaed6c1d43d51c79cd7b699280f6dcc4121b55 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 5 Aug 2014 17:22:55 +0100 Subject: [PATCH 301/421] Some markdown tweaks. --- CHANGELOG.md | 58 ++++++++++++++++++++++++++-------------------------- README.md | 8 ++++---- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec6b8dc..400decd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -### 2.2.0 (2014-06-17) +## 2.2.0 (2014-06-17) * Rule nesting now works without `@in` directives. * Added `csscrush_add_function()` as a simple way of adding custom functions without plugins. @@ -13,7 +13,7 @@ * Deprecated `@define`/`@ifdefine`/`@selector-alias` in favour of new directive names. Supported until at-least 3.x. * Deprecated the static api methods in favour of the `csscrush_*` functions. Supported until at-least 3.x. -### 2.1.0 (2014-03-21) +## 2.1.0 (2014-03-21) * Added HHVM support (HHVM >= 2.4) * Added Travis CI support. @@ -34,7 +34,7 @@ * Changed base IO class to use non-static methods. * Numerous under the hood improvements. -### 2.0.0 (2013-11-2) +## 2.0.0 (2013-11-2) * Raised PHP version requirement to PHP 5.3.1. * Library code (excluding API functions) is now namespaced. @@ -58,7 +58,7 @@ ******************************************************************** -### 1.11.0 (2013-8-3) +## 1.11.0 (2013-8-3) * Added source map support according to the Source Map v3 proposal (boolean option `source-map`). * Compile times are now 20-30% reduced. @@ -73,7 +73,7 @@ * Removed Prepend.css. * Various refactoring for cleaner under-the-hood APIs. -### 1.10.0 (2013-5-18) +## 1.10.0 (2013-5-18) * Added SVG plugin for defining and generating SVG files/data URIs in CSS. * Added Canvas plugin for image generation and manipulation (requires GD extension). @@ -93,7 +93,7 @@ * Fixed issue with empty imported files not registering. * Various bug fixes. -### 1.9.1 (2013-1-31) +## 1.9.1 (2013-1-31) * Added noise plugin (noise/texture generating functions). * Resolved issues #42 and #43. @@ -101,7 +101,7 @@ * Fixed error notice with no enabled plugins in Plugins.ini file. * Updated aliases file. -### 1.9 (2013-1-12) +## 1.9 (2013-1-12) * Added flexbox aliases for both 2009 and 2012 edition specs. * Added a legacy-flexbox plugin for auto-generating the flexbox 2009 spec equivilant properties. @@ -125,7 +125,7 @@ * Property/value aliases expanded and renamed as declaration aliases. * Classes now loaded via an autoloader, also some other refactoring for moving towards PSR-0 compliance. -### 1.8.0 (2012-11-13) +## 1.8.0 (2012-11-13) * Added selector aliasing with the `@selector-alias` directive. * Added `output_dir` option for specifying the destination of compiled files. @@ -144,7 +144,7 @@ * Improved minification. * Major refactoring. -### 1.7.0 (2012-9-28) +## 1.7.0 (2012-9-28) * Added `trace` option to output SASS compatible debug-info stubs for use with tools like FireSass. * Added `@ifdefine` directive for dynamically including/excluding parts of a CSS file based on the @@ -159,11 +159,11 @@ * Updated aliases and initial value files. * Fixed parsing issue introduced in 1.6.1. -### 1.6.1 (2012-8-22) +## 1.6.1 (2012-8-22) * Resolved issues #34 and #35. -### 1.6.0 (2012-8-1) +## 1.6.0 (2012-8-1) * Inheritance model improved to support adoption of pseudo classes and elements (see wiki). * Added rule self-referencing function `this()` and complimentary data-* properties. @@ -175,23 +175,23 @@ * Double-colon plugin moved to core. * Option `rewrite_import_urls` now defaults to true. -### 1.5.3 (2012-6-13) +## 1.5.3 (2012-6-13) * Refactoring. * Fixed some test cases. -### 1.5.2 (2012-6-8) +## 1.5.2 (2012-6-8) * Resolved issue #32. * `CssCrush::inline` method now defaults to not printing a boilerplate. * Updated aliases file. -### 1.5.1 (2012-6-1) +## 1.5.1 (2012-6-1) * Extended mixins to work with abstract rules and regular rules. * Fixed issue with selector grouping and inheritance in combination. -### 1.5.0 (2012-5-21) +## 1.5.0 (2012-5-21) * New feature: Rule inheritance / abstract rules. * New feature: Block nesting. @@ -205,12 +205,12 @@ * Internal refactoring. * Resolved issues #23, #24, #27, #28 and #29. -### 1.4.2 (2012-3-14) +## 1.4.2 (2012-3-14) * Fixed bug with @import statement parsing. * Some minor under the hood changes. -### 1.4.1 (2012-2-10) +## 1.4.1 (2012-2-10) * Added command line application. * Added `rewrite_import_urls` option - Ability to rewrite relative url references inside imported css files. @@ -220,7 +220,7 @@ * Initial-values updated. * Updated `CssCrush::string` method to correctly handle import statements. -### 1.4.0 (2012-1-24) +## 1.4.0 (2012-1-24) * Added initial-keyword plugin (shim for the CSS3 keyword). * Added inline method (Issue #18). @@ -232,14 +232,14 @@ * Some internal cleaning up. * Disabled IE6 min-height plugin by default. -### 1.3.6 (2011-11-9) +## 1.3.6 (2011-11-9) * Improved color functions. * Added `a-adjust()` function for altering a color's opacity. * Deprecated hsl-adjust function (you can use nested color functions instead). * Added the ability to use local versions of alias and plugin files so pull updates don't clobber local settings. -### 1.3.5 (2011-11-8) +## 1.3.5 (2011-11-8) * Added hook system for plugins. * Plugins split into seperate files. @@ -248,40 +248,40 @@ * Updated filter plugin. * Fixed nested custom function parsing (issue #14). -### 1.3.4 (2011-10-29) +## 1.3.4 (2011-10-29) * Added output_filename option. * Added vendor_target option. * Renamed 'macros' to the more general 'plugins' and split them into their own files. * Removed superfluous outer containing directory (update your include paths). -### 1.3.3 (2011-10-28) +## 1.3.3 (2011-10-28) * Fixed regression with absolute URL file imports (issue #12). * Fixed minification bug (issue #13). -### 1.3.2 (2011-10-18) +## 1.3.2 (2011-10-18) * Updated variable syntax. * Fixed minification bug. -### 1.3.1 (2011-10-9) +## 1.3.1 (2011-10-9) * Added support for svg and svgz data uris. * Added animation shorthand alias. * Added user-select alias. -### 1.3 (2011-10-20) +## 1.3 (2011-10-20) * Added the public function `CssCrush::string` for processing raw strings of CSS. * Added color functions. * Added aliases for IE10. -### 1.2.0 (2011-9-8) +## 1.2.0 (2011-9-8) * File importer rewritten. -### 1.1.0 (2011-9-2) +## 1.1.0 (2011-9-2) * Added support for global variables. * Added support for variable interpolation within string literals. @@ -293,7 +293,7 @@ * Minor correction to WAMP support. * Minor fix to rule API. -### 1.0.0 (2011-7-14) +## 1.0.0 (2011-7-14) * Major refactoring. * Custom functions. @@ -302,6 +302,6 @@ * Resolved document root issues. * Minification improvements. -### 0.9.0 (2010-9-20) +## 0.9.0 (2010-9-20) * Initial release. diff --git a/README.md b/README.md index b4b3a2e..886d54c 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ CSS-Crush is a standards inspired preprocessor designed to enable a modern and u See the [docs](http://the-echoplex.net/csscrush) for full details. -### Setup +## Setup If you're using [Composer](http://getcomposer.org) you can use Crush in your project with the following line in your terminal: @@ -32,7 +32,7 @@ If you're not using Composer yet just download the library into a convenient loc ``` -### Basic usage +## Basic usage ```php Date: Wed, 3 Sep 2014 12:29:43 +0100 Subject: [PATCH 302/421] Color functions now return nothing if the color argument is invalid. --- lib/CssCrush/Functions.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/CssCrush/Functions.php b/lib/CssCrush/Functions.php index 8d26e40..2d04e54 100644 --- a/lib/CssCrush/Functions.php +++ b/lib/CssCrush/Functions.php @@ -221,32 +221,32 @@ function fn__percent($input) { function fn__hsla_adjust($input) { list($color, $h, $s, $l, $a) = array_pad(Functions::parseArgs($input, true), 5, 0); - return Color::colorAdjust($color, array($h, $s, $l, $a)); + return Color::test($color) ? Color::colorAdjust($color, array($h, $s, $l, $a)) : ''; } function fn__hsl_adjust($input) { list($color, $h, $s, $l) = array_pad(Functions::parseArgs($input, true), 4, 0); - return Color::colorAdjust($color, array($h, $s, $l, 0)); + return Color::test($color) ? Color::colorAdjust($color, array($h, $s, $l, 0)) : ''; } function fn__h_adjust($input) { list($color, $h) = array_pad(Functions::parseArgs($input, true), 2, 0); - return Color::colorAdjust($color, array($h, 0, 0, 0)); + return Color::test($color) ? Color::colorAdjust($color, array($h, 0, 0, 0)) : ''; } function fn__s_adjust($input) { list($color, $s) = array_pad(Functions::parseArgs($input, true), 2, 0); - return Color::colorAdjust($color, array(0, $s, 0, 0)); + return Color::test($color) ? Color::colorAdjust($color, array(0, $s, 0, 0)) : ''; } function fn__l_adjust($input) { list($color, $l) = array_pad(Functions::parseArgs($input, true), 2, 0); - return Color::colorAdjust($color, array(0, 0, $l, 0)); + return Color::test($color) ? Color::colorAdjust($color, array(0, 0, $l, 0)) : ''; } function fn__a_adjust($input) { list($color, $a) = array_pad(Functions::parseArgs($input, true), 2, 0); - return Color::colorAdjust($color, array(0, 0, 0, $a)); + return Color::test($color) ? Color::colorAdjust($color, array(0, 0, 0, $a)) : ''; } function fn__this($input, $context) { From 8b63fca68e3ac427a9a49826d22ecb6b2b8a64fe Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 3 Sep 2014 12:57:06 +0100 Subject: [PATCH 303/421] Added `previous`/`next` context keywords to `query()` function. --- docs/core/functions/query.md | 2 +- lib/CssCrush/Functions.php | 30 ++++++++++++++++++++---------- lib/CssCrush/Process.php | 12 +++++++++--- lib/CssCrush/Rule.php | 3 +++ 4 files changed, 33 insertions(+), 14 deletions(-) diff --git a/docs/core/functions/query.md b/docs/core/functions/query.md index f0c49a1..c9d5240 100644 --- a/docs/core/functions/query.md +++ b/docs/core/functions/query.md @@ -10,7 +10,7 @@ Copy a value from another rule. ## Parameters -* *`target`* A rule selector, an abstract rule name or context keyword (`top` or `parent` with nested structures) +* *`target`* A rule selector, an abstract rule name or context keyword: `previous`, `next` (also `parent` and `top` within nested structures) * *`property-name`* The CSS property name to copy, or just `default` to pass over. Defaults to the calling property * *`fallback`* A CSS value to use if the target property does not exist diff --git a/lib/CssCrush/Functions.php b/lib/CssCrush/Functions.php index 2d04e54..edc999f 100644 --- a/lib/CssCrush/Functions.php +++ b/lib/CssCrush/Functions.php @@ -297,16 +297,26 @@ function fn__query($input, $context) { $targetRule = null; $references =& Crush::$process->references; - $targetLowerCase = strtolower($target); - if ($targetLowerCase === 'parent') { - $targetRule = $context->rule->parent; - } - elseif ($targetLowerCase === 'top') { - $targetRule = $context->rule->parent; - while ($targetRule && $targetRule->parent && $targetRule = $targetRule->parent); - } - elseif (isset($references[$target])) { - $targetRule = $references[$target]; + + switch (strtolower($target)) { + case 'parent': + $targetRule = $context->rule->parent; + break; + case 'previous': + $targetRule = $context->rule->previous; + break; + case 'next': + $targetRule = $context->rule->next; + break; + case 'top': + $targetRule = $context->rule->parent; + while ($targetRule && $targetRule->parent && $targetRule = $targetRule->parent); + break; + default: + if (isset($references[$target])) { + $targetRule = $references[$target]; + } + break; } $result = ''; diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index a1d1530..d4185bc 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -620,19 +620,25 @@ public function captureRules() protected function processRules() { // Create table of name/selector to rule references. - $named_references = array(); + $namedReferences = array(); + $previousRule = null; foreach ($this->tokens->store->r as $rule) { if ($rule->name) { - $named_references[$rule->name] = $rule; + $namedReferences[$rule->name] = $rule; } foreach ($rule->selectors as $selector) { $this->references[$selector->readableValue] = $rule; } + if ($previousRule) { + $rule->previous = $previousRule; + $previousRule->next = $rule; + } + $previousRule = $rule; } // Explicit named references take precedence. - $this->references = $named_references + $this->references; + $this->references = $namedReferences + $this->references; foreach ($this->tokens->store->r as $rule) { diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php index aaeaaab..daf1085 100644 --- a/lib/CssCrush/Rule.php +++ b/lib/CssCrush/Rule.php @@ -14,7 +14,10 @@ class Rule public $name; public $isAbstract; public $resolvedExtendables; + public $parent; + public $previous; + public $next; public $selectors; public $declarations; From 51991c19018669ff20931794bb7f69931dfb97ea Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 12 Sep 2014 11:25:44 +0100 Subject: [PATCH 304/421] Removing trace option (SASS debug-info is obsolete) and related functionality. CSS source maps are now well supported. --- docs/api/options.md | 5 ----- lib/CssCrush/Options.php | 7 ------- lib/CssCrush/Process.php | 25 ------------------------- lib/CssCrush/Rule.php | 8 ++++---- tests/unit/CssCrush/OptionsTest.php | 13 ------------- tests/unit/api/apiTest.php | 1 - 6 files changed, 4 insertions(+), 55 deletions(-) diff --git a/docs/api/options.md b/docs/api/options.md index b5f960d..f40f32f 100644 --- a/docs/api/options.md +++ b/docs/api/options.md @@ -90,11 +90,6 @@ true | false Output a source map (compliant with the Source Map v3 proposal). - - trace - true | false - Output SASS debug-info stubs for use with tools like FireSass. - context Path diff --git a/lib/CssCrush/Options.php b/lib/CssCrush/Options.php index 694838c..da83206 100644 --- a/lib/CssCrush/Options.php +++ b/lib/CssCrush/Options.php @@ -29,7 +29,6 @@ class Options 'disable' => null, 'settings' => array(), 'stat_dump' => false, - 'trace' => array(), 'source_map' => false, 'newlines' => 'use-platform', ); @@ -61,12 +60,6 @@ public function __set($name, $value) } break; - case 'trace': - if (! is_array($value)) { - $value = $value ? array('stubs') : array(); - } - break; - case 'formatter': if (is_string($value) && isset(Crush::$config->formatters[$value])) { $value = Crush::$config->formatters[$value]; diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index d4185bc..369bada 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -56,7 +56,6 @@ public function __construct($user_options = array(), $context = array()) // Shortcut commonly used options to avoid __get() overhead. $this->docRoot = isset($this->options->doc_root) ? $this->options->doc_root : $config->docRoot; - $this->addTracingStubs = in_array('stubs', $this->options->__get('trace')); $this->generateMap = $this->ioContext === 'file' && $this->options->__get('source_map'); $this->ruleFormatter = $this->options->__get('formatter'); $this->minifyOutput = $this->options->__get('minify'); @@ -868,9 +867,6 @@ protected function collate() $this->string->restore(array('u', 's')); - if ($this->addTracingStubs) { - $this->string->restore('t', false, array($this, 'generateTracingStub')); - } if ($this->generateMap) { $this->generateSourceMap(); } @@ -1006,27 +1002,6 @@ public function generateSourceMap() $this->sourceMap['mappings'] = implode(';', $mappings); } - public function generateTracingStub($m) - { - if (! ($value = $this->tokens->get($m[0]))) { - return ''; - } - - list($source_index, $line) = explode(',', $value); - $line += 1; - - // Get the currently processed file path, and escape it. - $current_file = 'file://' . str_replace(' ', '%20', $this->sources[$source_index]); - $current_file = preg_replace('~[^\w-]~', '\\\\$0', $current_file); - $debug_info = "@media -sass-debug-info{filename{font-family:$current_file}line{font-family:\\00003$line}}"; - - if (! $this->minifyOutput) { - $debug_info .= $this->newline; - } - - return $debug_info; - } - ############################# # Decruft. diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php index daf1085..478a4d9 100644 --- a/lib/CssCrush/Rule.php +++ b/lib/CssCrush/Rule.php @@ -26,13 +26,13 @@ class Rule public $extendArgs = array(); public $extendSelectors = array(); - public function __construct($selector_string, $declarations_string, $trace_token = null) + public function __construct($selectorString, $declarationsString, $traceToken = null) { $process = Crush::$process; $this->label = $process->tokens->createLabel('r'); - $this->marker = $process->addTracingStubs || $process->generateMap ? $trace_token : null; - $this->selectors = new SelectorList($selector_string, $this); - $this->declarations = new DeclarationList($declarations_string, $this); + $this->marker = $process->generateMap ? $traceToken : null; + $this->selectors = new SelectorList($selectorString, $this); + $this->declarations = new DeclarationList($declarationsString, $this); } public function __toString() diff --git a/tests/unit/CssCrush/OptionsTest.php b/tests/unit/CssCrush/OptionsTest.php index 9807177..05c5636 100644 --- a/tests/unit/CssCrush/OptionsTest.php +++ b/tests/unit/CssCrush/OptionsTest.php @@ -85,19 +85,6 @@ public function testSourceMaps() $this->assertRegExp('~"version": ?"3",~', $source_map_contents); } - public function testTrace() - { - csscrush_file($this->testFile, array('trace' => true)); - $output_contents = file_get_contents("$this->testFile.crush.css"); - - $this->assertContains('@media -sass-debug-info', $output_contents); - - csscrush_file($this->testFile, array('trace' => array('stubs'))); - $output_contents = file_get_contents("$this->testFile.crush.css"); - - $this->assertContains('@media -sass-debug-info', $output_contents); - } - public function testAdvancedMinify() { $sample = "foo { color: papayawhip; color: #cccccc;}"; diff --git a/tests/unit/api/apiTest.php b/tests/unit/api/apiTest.php index d8c21a5..860fac0 100644 --- a/tests/unit/api/apiTest.php +++ b/tests/unit/api/apiTest.php @@ -80,7 +80,6 @@ public function testStat() csscrush_string($sample, array( 'minify' => false, - 'trace' => array( 'selector_count', 'rule_count' ), 'disable' => array( 'all' ), )); From 2e80e723c1b443b8b666f91b4ac6ed72f346c28e Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 12 Sep 2014 11:49:59 +0100 Subject: [PATCH 305/421] Some refactoring for readabliity. --- lib/CssCrush/Hooks.php | 18 +++++++-------- lib/CssCrush/Process.php | 48 ++++++++++++++++++++-------------------- lib/CssCrush/Rule.php | 45 ++++++++++++++++++------------------- 3 files changed, 54 insertions(+), 57 deletions(-) diff --git a/lib/CssCrush/Hooks.php b/lib/CssCrush/Hooks.php index 8a08c35..113a06c 100644 --- a/lib/CssCrush/Hooks.php +++ b/lib/CssCrush/Hooks.php @@ -10,25 +10,25 @@ class Hooks { protected $register = array(); - public function add($hook, $fn_name) + public function add($hook, $functionName) { - if (! isset($this->register[$hook][$fn_name])) { - if (function_exists($fn_name)) { - $this->register[$hook][$fn_name] = true; + if (! isset($this->register[$hook][$functionName])) { + if (function_exists($functionName)) { + $this->register[$hook][$functionName] = true; } } } - public function remove($hook, $fn_name) + public function remove($hook, $functionName) { - unset($this->register[$hook][$fn_name]); + unset($this->register[$hook][$functionName]); } - public function run($hook, $arg_obj = null) + public function run($hook, $argObj = null) { if (isset($this->register[$hook])) { - foreach ($this->register[$hook] as $fn_name => $flag) { - $fn_name($arg_obj); + foreach (array_keys($this->register[$hook]) as $functionName) { + $functionName($argObj); } } } diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 369bada..45425d9 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -147,14 +147,14 @@ public function resolveContext($input_dir = null, $input_file = null) protected function getBoilerplate() { $file = false; - $boilerplate_option = $this->options->boilerplate; + $boilerplateOption = $this->options->boilerplate; - if ($boilerplate_option === true) { + if ($boilerplateOption === true) { $file = Crush::$dir . '/boilerplate.txt'; } - elseif (is_string($boilerplate_option)) { - if (file_exists($boilerplate_option)) { - $file = $boilerplate_option; + elseif (is_string($boilerplateOption)) { + if (file_exists($boilerplateOption)) { + $file = $boilerplateOption; } } @@ -166,20 +166,20 @@ protected function getBoilerplate() $boilerplate = file_get_contents($file); // Substitute any tags - if (preg_match_all('~\{\{([^}]+)\}\}~', $boilerplate, $boilerplate_matches)) { + if (preg_match_all('~\{\{([^}]+)\}\}~', $boilerplate, $boilerplateMatches)) { // Command line arguments (if any). - $command_args = 'n/a'; + $commandArgs = 'n/a'; if (isset($_SERVER['argv'])) { $argv = $_SERVER['argv']; array_shift($argv); - $command_args = 'csscrush ' . implode(' ', $argv); + $commandArgs = 'csscrush ' . implode(' ', $argv); } $tags = array( 'datetime' => @date('Y-m-d H:i:s O'), 'year' => @date('Y'), - 'command' => $command_args, + 'command' => $commandArgs, 'plugins' => implode(',', array_keys($this->plugins)), 'version' => csscrush_version(), 'git_version' => function () { @@ -191,15 +191,15 @@ protected function getBoilerplate() }, ); - foreach ($boilerplate_matches[0] as $index => $tag) { - $tag_name = trim($boilerplate_matches[1][$index]); + foreach (array_keys($boilerplateMatches[0]) as $index) { + $tagName = trim($boilerplateMatches[1][$index]); $replacement = '?'; - if (isset($tags[$tag_name])) { - $replacement = is_callable($tags[$tag_name]) ? $tags[$tag_name]() : $tags[$tag_name]; + if (isset($tags[$tagName])) { + $replacement = is_callable($tags[$tagName]) ? $tags[$tagName]() : $tags[$tagName]; } $replacements[] = $replacement; } - $boilerplate = str_replace($boilerplate_matches[0], $replacements, $boilerplate); + $boilerplate = str_replace($boilerplateMatches[0], $replacements, $boilerplate); } // Pretty print. @@ -304,7 +304,7 @@ protected function filterAliases() elseif ($section === 'function_groups') { foreach ($group_array as $func_group => $vendors) { - foreach ($vendors as $vendor => $replacements) { + foreach (array_keys($vendors) as $vendor) { if (! in_array($vendor, $vendor_names)) { unset($this->aliases['function_groups'][$func_group][$vendor]); } @@ -360,21 +360,21 @@ protected function filterPlugins() // Remove option disabled plugins from the list, and disable them. if ($disable) { - foreach ($disable as $plugin_name => $index) { - Plugin::disable($plugin_name); - unset($this->plugins[$plugin_name]); + foreach (array_keys($disable) as $pluginName) { + Plugin::disable($pluginName); + unset($this->plugins[$pluginName]); } } // Secondly add option enabled plugins to the list. if ($enable) { - foreach ($enable as $plugin_name => $index) { - $this->plugins[$plugin_name] = true; + foreach (array_keys($enable) as $pluginName) { + $this->plugins[$pluginName] = true; } } - foreach ($this->plugins as $plugin_name => $bool) { - Plugin::enable($plugin_name); + foreach (array_keys($this->plugins) as $pluginName) { + Plugin::enable($pluginName); } } @@ -892,8 +892,8 @@ public function preCompile() public function postCompile() { - foreach ($this->plugins as $plugin_name => $bool) { - Plugin::disable($plugin_name); + foreach (array_keys($this->plugins) as $pluginName) { + Plugin::disable($pluginName); } $this->release(); diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php index 478a4d9..e1d9a08 100644 --- a/lib/CssCrush/Rule.php +++ b/lib/CssCrush/Rule.php @@ -70,12 +70,12 @@ public function __clone() ############################# # Rule inheritance. - public function setExtendSelectors($raw_value) + public function setExtendSelectors($rawValue) { // Reset if called earlier, last call wins by intention. $this->extendArgs = array(); - foreach (Util::splitDelimList($raw_value) as $arg) { + foreach (Util::splitDelimList($rawValue) as $arg) { $this->extendArgs[] = new ExtendArg($arg); } } @@ -92,16 +92,13 @@ public function resolveExtendables() // Filter the extendArgs list to usable references. $filtered = array(); - foreach ($this->extendArgs as $extend_arg) { + foreach ($this->extendArgs as $extendArg) { - $name = $extend_arg->name; - - if (isset($references[$name])) { - - $parent_rule = $references[$name]; - $parent_rule->resolveExtendables(); - $extend_arg->pointer = $parent_rule; - $filtered[$parent_rule->label] = $extend_arg; + if (isset($references[$extendArg->name])) { + $parentRule = $references[$extendArg->name]; + $parentRule->resolveExtendables(); + $extendArg->pointer = $parentRule; + $filtered[$parentRule->label] = $extendArg; } } @@ -120,32 +117,32 @@ public function applyExtendables() } // Create a stack of all parent rule args. - $parent_extend_args = array(); - foreach ($this->extendArgs as $extend_arg) { - $parent_extend_args += $extend_arg->pointer->extendArgs; + $parentExtendArgs = array(); + foreach ($this->extendArgs as $extendArg) { + $parentExtendArgs += $extendArg->pointer->extendArgs; } // Merge this rule's extendArgs with parent extendArgs. - $this->extendArgs += $parent_extend_args; + $this->extendArgs += $parentExtendArgs; // Add this rule's selectors to all extendArgs. - foreach ($this->extendArgs as $extend_arg) { + foreach ($this->extendArgs as $extendArg) { - $ancestor = $extend_arg->pointer; + $ancestor = $extendArg->pointer; - $extend_selectors = $this->selectors->store; + $extendSelectors = $this->selectors->store; // If there is a pseudo class extension create a new set accordingly. - if ($extend_arg->pseudo) { + if ($extendArg->pseudo) { - $extend_selectors = array(); + $extendSelectors = array(); foreach ($this->selectors->store as $selector) { - $new_selector = clone $selector; - $new_readable = $new_selector->appendPseudo($extend_arg->pseudo); - $extend_selectors[$new_readable] = $new_selector; + $newSelector = clone $selector; + $newReadable = $newSelector->appendPseudo($extendArg->pseudo); + $extendSelectors[$newReadable] = $newSelector; } } - $ancestor->extendSelectors += $extend_selectors; + $ancestor->extendSelectors += $extendSelectors; } } } From 7c1efa65c2a0ceb8d53748ded23d0e86240e8952 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 31 Oct 2014 14:51:13 +0000 Subject: [PATCH 306/421] Adding package.json. --- package.json | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 package.json diff --git a/package.json b/package.json new file mode 100644 index 0000000..222a4b8 --- /dev/null +++ b/package.json @@ -0,0 +1,17 @@ +{ + "name": "csscrush", + "version": "2.1.1", + "description": "CSS-Crush, CSS preprocessor", + "repository": { + "type": "git", + "url": "https://github.com/peteboere/css-crush.git" + }, + "bugs": { + "url": "https://github.com/peteboere/css-crush/issues" + }, + "bin" : { + "csscrush" : "./misc/csscrush" + }, + "homepage": "http://the-echoplex.net/csscrush", + "license": "MIT" +} From 7c5726af40b9df4ca44bf4345a39955bdb46b793 Mon Sep 17 00:00:00 2001 From: Trambitskiy Yura Date: Tue, 11 Nov 2014 15:29:47 +0300 Subject: [PATCH 307/421] corrected typo in docs: core--selector-grouping --- docs/core/selector-aliases.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/core/selector-aliases.md b/docs/core/selector-aliases.md index 5cdf848..820f12b 100644 --- a/docs/core/selector-aliases.md +++ b/docs/core/selector-aliases.md @@ -6,7 +6,7 @@ Selector aliases can be useful for grouping together common selector chains for reuse. -They're defined with the `@selector` directive, and can be used anywhere you might use a psuedo class. +They're defined with the `@selector` directive, and can be used anywhere, you might use a pseudo class. ```crush From 320f44e21917845d9af68b995708e78d11636248 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 18 Nov 2014 20:19:59 +0000 Subject: [PATCH 308/421] Removing legacy-flexbox plugin. --- docs/core/selector-aliases.md | 2 +- docs/plugins/legacy-flexbox.md | 40 ---- plugins/legacy-flexbox.php | 338 --------------------------------- 3 files changed, 1 insertion(+), 379 deletions(-) delete mode 100644 docs/plugins/legacy-flexbox.md delete mode 100644 plugins/legacy-flexbox.php diff --git a/docs/core/selector-aliases.md b/docs/core/selector-aliases.md index 820f12b..ea9296d 100644 --- a/docs/core/selector-aliases.md +++ b/docs/core/selector-aliases.md @@ -6,7 +6,7 @@ Selector aliases can be useful for grouping together common selector chains for reuse. -They're defined with the `@selector` directive, and can be used anywhere, you might use a pseudo class. +They're defined with the `@selector` directive, and can be used anywhere you might use a pseudo class. ```crush diff --git a/docs/plugins/legacy-flexbox.md b/docs/plugins/legacy-flexbox.md deleted file mode 100644 index 8194a21..0000000 --- a/docs/plugins/legacy-flexbox.md +++ /dev/null @@ -1,40 +0,0 @@ - -Polyfill to auto-generate legacy flexbox syntaxes. - -Works in conjunction with aliases to support legacy flexbox (flexbox 2009) syntax with modern flexbox. - -```css -display: flex; -flex-flow: row-reverse wrap; -justify-content: space-between; -``` - -```css -display: -webkit-box; -display: -moz-box; -display: -webkit-flex; -display: -ms-flexbox; -display: flex; --webkit-box-direction: reverse; --moz-box-direction: reverse; --webkit-box-orient: horizontal; --moz-box-orient: horizontal; --webkit-box-lines: wrap; --moz-box-lines: wrap; --webkit-flex-flow: row-reverse wrap; --ms-flex-flow: row-reverse wrap; -flex-flow: row-reverse wrap; --webkit-box-pack: justify; --moz-box-pack: justify; --webkit-justify-content: space-between; --ms-flex-pack: justify; -justify-content: space-between; -``` - -### Caveats - -Firefox's early flexbox implementation (Firefox < 22) has several non-trivial issues: - -* With flex containers `display: -moz-box` generates an inline-block element, not a block level element as in other implementations. Suggested workaround is to set `width: 100%`, in conjunction with `box-sizing: border-box` if padding is required. -* The width of flex items can only be set in pixels. -* Flex items cannot be justified. I.e. `-moz-box-pack: justify` does not work. diff --git a/plugins/legacy-flexbox.php b/plugins/legacy-flexbox.php deleted file mode 100644 index 860e931..0000000 --- a/plugins/legacy-flexbox.php +++ /dev/null @@ -1,338 +0,0 @@ - function ($process) { - $process->hooks->add('rule_prealias', 'CssCrush\legacy_flexbox'); - } -)); - - -function legacy_flexbox(Rule $rule) { - - static $flex_related_props = array( - 'align-items' => true, - 'flex' => true, - 'flex-direction' => true, - 'flex-flow' => true, - 'flex-grow' => true, - 'flex-wrap' => true, - 'justify-content' => true, - 'order' => true, - - // The following properties have no legacy equivalent: - // - align-content - // - align-self - // - flex-shrink - // - flex-basis - ); - - $properties =& $rule->declarations->properties; - $intersect_props = array_intersect_key($properties, $flex_related_props); - - // Checking for flex related properties or 'display:flex'. - // First checking the display property as it's pretty common. - if (isset($properties['display'])) { - foreach ($rule->declarations as $declaration) { - if ($declaration->property === 'display' && - ($declaration->value === 'flex' || $declaration->value === 'inline-flex')) { - // Add 'display' to the intersected properties. - $intersect_props['display'] = true; - break; - } - } - } - - // Bail early if the rule has no flex related properties. - if (! $intersect_props) { - return; - } - - $declaration_aliases =& Crush::$process->aliases['declarations']; - - $stack = array(); - $rule_updated = false; - - foreach ($rule->declarations as $declaration) { - - $prop = $declaration->property; - $value = $declaration->value; - - if (! isset($intersect_props[$prop])) { - $stack[] = $declaration; - continue; - } - - switch ($prop) { - - // display:flex => display:-*-box. - case 'display': - if ( - // Treat flex and inline-flex the same in this case. - ($value === 'flex' || $value === 'inline-flex') && - isset($declaration_aliases['display']['box'])) { - foreach ($declaration_aliases['display']['box'] as $pair) { - $stack[] = new Declaration($pair[0], $pair[1]); - $rule_updated = true; - } - } - break; - - case 'align-items': - $rule_updated = flex_align_items($value, $stack); - break; - - case 'flex': - $rule_updated = flex($value, $stack); - break; - - case 'flex-direction': - $rule_updated = flex_direction($value, $stack); - break; - - case 'flex-grow': - $rule_updated = flex_grow($value, $stack); - break; - - case 'flex-wrap': - // No browser seems to have implemented the box-lines property, - // definitely not firefox. - // - https://bugzilla.mozilla.org/show_bug.cgi?id=562073 - // - http://stackoverflow.com/questions/5010083/\ - // css3-flex-box-specifying-multiple-box-lines-doesnt-work - - // $rule_updated = flex_wrap($value, $stack); - break; - - case 'justify-content': - $rule_updated = flex_justify_content($value, $stack); - break; - - case 'order': - $rule_updated = flex_order($value, $stack); - break; - - // Shorthand values. - case 'flex-flow': - - // <‘flex-direction’> || <‘flex-wrap’> - - $args = explode(' ', $value); - $direction = isset($args[0]) ? $args[0] : 'initial'; - - $rule_updated = flex_direction($direction, $stack); - - break; - } - - // The existing declaration. - $stack[] = $declaration; - } - - // Re-assign if any updates have been made. - if ($rule_updated) { - $rule->declarations->reset($stack); - } -} - - -function flex_direction($value, &$stack) { - - // flex-direction: row | row-reverse | column | column-reverse - // box-orient: horizontal | vertical | inline-axis | block-axis | inherit - // box-direction: normal | reverse | inherit - - $prop_aliases =& Crush::$process->aliases['properties']; - - static $directions = array( - 'row' => 'normal', - 'row-reverse' => 'reverse', - 'column' => 'normal', - 'column-reverse' => 'reverse', - 'inherit' => 'inherit', - 'initial' => 'normal', - ); - static $orientations = array( - 'row' => 'horizontal', - 'row-reverse' => 'horizontal', - 'column' => 'vertical', - 'column-reverse' => 'vertical', - 'inherit' => 'inherit', - ); - $rule_updated = false; - - if (isset($prop_aliases['box-direction'])) { - foreach ($prop_aliases['box-direction'] as $prop_alias) { - $stack[] = new Declaration($prop_alias, $directions[$value]); - $rule_updated = true; - } - } - if (isset($prop_aliases['box-orient'])) { - foreach ($prop_aliases['box-orient'] as $prop_alias) { - $stack[] = new Declaration($prop_alias, $orientations[$value]); - $rule_updated = true; - } - } - return $rule_updated; -} - - -function flex_justify_content($value, &$stack) { - - // justify-content: flex-start | flex-end | center | space-between | space-around - // box-pack: start | end | center | justify - - $prop_aliases =& Crush::$process->aliases['properties']; - - static $positions = array( - 'flex-start' => 'start', - 'flex-end' => 'end', - 'center' => 'center', - 'space-between' => 'justify', - 'space-around' => 'justify', - ); - $rule_updated = false; - - if (isset($prop_aliases['box-pack']) && isset($positions[$value])) { - foreach ($prop_aliases['box-pack'] as $prop_alias) { - $stack[] = new Declaration($prop_alias, $positions[$value]); - $rule_updated = true; - } - } - return $rule_updated; -} - - -function flex_align_items($value, &$stack) { - - // align-items: flex-start | flex-end | center | baseline | stretch - // box-align: start | end | center | baseline | stretch - - $prop_aliases =& Crush::$process->aliases['properties']; - - static $positions = array( - 'flex-start' => 'start', - 'flex-end' => 'end', - 'center' => 'center', - 'baseline' => 'baseline', - 'stretch' => 'stretch', - ); - $rule_updated = false; - - if (isset($prop_aliases['box-align']) && isset($positions[$value])) { - foreach ($prop_aliases['box-align'] as $prop_alias) { - $stack[] = new Declaration($prop_alias, $positions[$value]); - $rule_updated = true; - } - } - return $rule_updated; -} - - -function flex_order($value, &$stack) { - - // order: - // box-ordinal-group: - - $prop_aliases =& Crush::$process->aliases['properties']; - - // Bump value as box-ordinal-group requires a positive integer: - // http://www.w3.org/TR/2009/WD-css3-flexbox-20090723/#displayorder - // - // Spec suggests a 'natural number' as valid value which Webkit seems - // to interpret as integer > 0, whereas Firefox interprets as - // integer > -1. - $value = $value < 1 ? 1 : $value + 1; - - $rule_updated = false; - if (isset($prop_aliases['box-ordinal-group'])) { - foreach ($prop_aliases['box-ordinal-group'] as $prop_alias) { - $stack[] = new Declaration($prop_alias, $value); - $rule_updated = true; - } - } - return $rule_updated; -} - - -function flex_wrap($value, &$stack) { - - // flex-wrap: nowrap | wrap | wrap-reverse - // box-lines: single | multiple - - $prop_aliases =& Crush::$process->aliases['properties']; - - static $wrap_behaviours = array( - 'nowrap' => 'single', - 'wrap' => 'multiple', - 'wrap-reverse' => 'multiple', - 'initial' => 'single', - ); - $rule_updated = false; - - if (isset($prop_aliases['box-lines']) && isset($wrap_behaviours[$value])) { - foreach ($prop_aliases['box-lines'] as $prop_alias) { - $stack[] = new Declaration($prop_alias, $wrap_behaviours[$value]); - $rule_updated = true; - } - } - return $rule_updated; -} - - -function flex($value, &$stack) { - - // flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ] - // box-flex: - - $prop_aliases =& Crush::$process->aliases['properties']; - - // Normalize keyword arguments. - static $keywords = array( - 'none' => '0 0 auto', - 'auto' => '1 1 auto', - 'initial' => '0 1 auto', - ); - if (isset($keywords[$value])) { - $value = $keywords[$value]; - } - - // Grow (first value) not have a unit to avoid being interpreted as a basis: - // https://developer.mozilla.org/en-US/docs/CSS/flex - $grow = 1; - if (preg_match('~^(\d*\.?\d+) ~', $value, $m)) { - $grow = $m[1]; - } - - $rule_updated = false; - if (isset($prop_aliases['box-flex'])) { - foreach ($prop_aliases['box-flex'] as $prop_alias) { - $stack[] = new Declaration($prop_alias, $grow); - $rule_updated = true; - } - } - return $rule_updated; -} - - -function flex_grow($value, &$stack) { - - // flex-grow: - // box-flex: - - $prop_aliases =& Crush::$process->aliases['properties']; - - $rule_updated = false; - if (isset($prop_aliases['box-flex'])) { - foreach ($prop_aliases['box-flex'] as $prop_alias) { - $stack[] = new Declaration($prop_alias, $value); - $rule_updated = true; - } - } - return $rule_updated; -} From c9f3d42c2579cdfd8e5050afd3a5b2f3c7d00e6f Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 18 Nov 2014 21:04:58 +0000 Subject: [PATCH 309/421] Resolved issue with dropped `!important` when using mixins. --- lib/CssCrush/DeclarationList.php | 11 +++++++-- lib/CssCrush/Mixin.php | 38 ++++++++++++-------------------- 2 files changed, 23 insertions(+), 26 deletions(-) diff --git a/lib/CssCrush/DeclarationList.php b/lib/CssCrush/DeclarationList.php index 21e2456..e208f88 100644 --- a/lib/CssCrush/DeclarationList.php +++ b/lib/CssCrush/DeclarationList.php @@ -442,8 +442,15 @@ public function flatten($rule_context) $new_set = array(); foreach ($this->store as $declaration) { if (is_array($declaration) && $declaration[0] === 'mixin') { - foreach (Mixin::merge(array(), $declaration[1], array('context' => $rule_context)) as $pair) { - $new_set[] = new Declaration($pair[0], $pair[1], count($new_set)); + foreach (Mixin::merge(array(), $declaration[1], array('context' => $rule_context)) as $mixable) { + if ($mixable instanceof Declaration) { + $clone = clone $mixable; + $clone->index = count($new_set); + $new_set[] = $clone; + } + else { + $new_set[] = new Declaration($mixable[0], $mixable[1], count($new_set)); + } } } else { diff --git a/lib/CssCrush/Mixin.php b/lib/CssCrush/Mixin.php index 1fe4826..810e4b2 100644 --- a/lib/CssCrush/Mixin.php +++ b/lib/CssCrush/Mixin.php @@ -68,20 +68,7 @@ public static function call($message, $context = null) } elseif ($mixable instanceof Rule) { - $result = array(); - foreach ($mixable->declarations as $declaration) { - if ($declaration instanceof Declaration) { - $result[] = array( - $declaration->property, - $declaration->value, - ); - } - else { - $result[] = $declaration; - } - } - - return $result; + return $mixable->declarations->store; } } @@ -96,18 +83,21 @@ public static function merge(array $input, $message_list, $options = array()) } } - while ($pair = array_shift($mixables)) { - - list($property, $value) = $pair; - - if ($property === 'mixin') { - $input = Mixin::merge($input, $value, $options); - } - elseif (! empty($options['keyed'])) { - $input[$property] = $value; + while ($mixable = array_shift($mixables)) { + if ($mixable instanceof Declaration) { + $input[] = $mixable; } else { - $input[] = array($property, $value); + list($property, $value) = $mixable; + if ($property === 'mixin') { + $input = Mixin::merge($input, $value, $options); + } + elseif (! empty($options['keyed'])) { + $input[$property] = $value; + } + else { + $input[] = array($property, $value); + } } } From 88d6c09a98ff0154352b6fceb3411f3d8b6e130f Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 21 Nov 2014 14:40:02 +0000 Subject: [PATCH 310/421] Added support for function calls on media query lists. --- lib/CssCrush/Process.php | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 45425d9..743cae6 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -429,14 +429,14 @@ protected function placeVars(&$value) if (! $varFunction) { $varFunctionSimple = Regex::make('~\$\( \s* ({{ ident }}) \s* \)~xS'); $varFunction = new Functions(array('$' => function ($rawArgs) { - list($name, $defaultValue) = Functions::parseArgsSimple($rawArgs); - if (isset(Crush::$process->vars[$name])) { - return Crush::$process->vars[$name]; - } - else { - return $defaultValue; - } - })); + list($name, $defaultValue) = Functions::parseArgsSimple($rawArgs); + if (isset(Crush::$process->vars[$name])) { + return Crush::$process->vars[$name]; + } + else { + return $defaultValue; + } + })); } // Variables with no default value. @@ -932,6 +932,12 @@ public function compile() $this->resolveInBlocks(); + // Calling functions on media query lists. + $process = $this; + $this->string->pregReplaceCallback('~@media\s+(?[^{]+)\{~i', function ($m) use (&$process) { + return "@media {$process->functions->apply($m['media_list'])}{"; + }); + $this->aliasAtRules(); $this->processRules(); From 879b39e6c91a76739d0c2fea8526475267bf820e Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 26 Nov 2014 17:40:54 +0000 Subject: [PATCH 311/421] Partial fix for #67 --- lib/CssCrush/Importer.php | 22 +++++++++++----------- lib/CssCrush/Regex.php | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index d1c591e..4e41b76 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -174,8 +174,8 @@ protected function prepareImport(&$str) // Convert all EOL to unix style. $str = preg_replace('~\r\n?~', "\n", $str); - // Necessary to avoid catastrophic backtracking in large files and some edge cases. - $str = rtrim($this->captureCommentAndString($str)); + // Trimming to reduce regex backtracking. + $str = rtrim($this->captureCommentAndString(rtrim($str))); if (! $this->syntaxCheck($str)) { @@ -329,18 +329,18 @@ protected function captureCommentAndString($str) // Bail without storing comment if output is minified or a private comment. if ($process->minifyOutput || strpos($fullMatch, '/*$') === 0) { - return Tokens::pad('', $fullMatch); + $label = ''; } - - // Fix broken comments as they will break any subsquent - // imported files that are inlined. - if (! preg_match('~\*/$~', $fullMatch)) { - $fullMatch .= '*/'; + else { + // Fix broken comments as they will break any subsquent + // imported files that are inlined. + if (! preg_match('~\*/$~', $fullMatch)) { + $fullMatch .= '*/'; + } + $label = $process->tokens->add($fullMatch, 'c'); } - $label = $process->tokens->add($fullMatch, 'c'); } else { - // Fix broken strings as they will break any subsquent // imported files that are inlined. if ($fullMatch[0] !== $fullMatch[strlen($fullMatch)-1]) { @@ -349,7 +349,7 @@ protected function captureCommentAndString($str) $label = $process->tokens->add($fullMatch, 's'); } - return Tokens::pad($label, $fullMatch); + return $process->generateMap ? Tokens::pad($label, $fullMatch) : $label; }; return preg_replace_callback(Regex::$patt->commentAndString, $callback, $str); diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php index 4c228a6..eaa0218 100644 --- a/lib/CssCrush/Regex.php +++ b/lib/CssCrush/Regex.php @@ -77,7 +77,7 @@ public static function init() (\'|")(?:\\\\\1|[^\1])*?(?:\1|$) | # Block comment (to EOF if unmatched). - /\*(?:.*?)(?:\*/|$) + /\*(?:[^*]*\*+(?:[^/*][^*]*\*+)*/|.*) ~xsS'; // Misc. From 28d84ea6932386459010705f2ac000e738bd114f Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 2 Dec 2014 10:54:51 +0000 Subject: [PATCH 312/421] Removing shebang from cli.php since package managers use misc/csscrush (which already has a shebang). --- cli.php | 1 - 1 file changed, 1 deletion(-) diff --git a/cli.php b/cli.php index 06c1c6e..1c24c3f 100755 --- a/cli.php +++ b/cli.php @@ -1,4 +1,3 @@ -#!/usr/bin/env php Date: Wed, 3 Dec 2014 10:40:06 +0000 Subject: [PATCH 313/421] Moved command line app alias to a more intuitive location. --- {misc => bin}/csscrush | 2 +- cli.php | 2 +- composer.json | 2 +- package.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename {misc => bin}/csscrush (61%) diff --git a/misc/csscrush b/bin/csscrush similarity index 61% rename from misc/csscrush rename to bin/csscrush index 665fdd6..559fb84 100755 --- a/misc/csscrush +++ b/bin/csscrush @@ -2,7 +2,7 @@ --help Display this help message. - --list + --list Show plugins. --newlines diff --git a/composer.json b/composer.json index 22875b6..a2e4810 100644 --- a/composer.json +++ b/composer.json @@ -24,7 +24,7 @@ "twig/twig": "1.*" }, "bin": [ - "misc/csscrush" + "bin/csscrush" ], "autoload": { "psr-0": { "CssCrush": "lib/" }, diff --git a/package.json b/package.json index 222a4b8..936978e 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "url": "https://github.com/peteboere/css-crush/issues" }, "bin" : { - "csscrush" : "./misc/csscrush" + "csscrush" : "./bin/csscrush" }, "homepage": "http://the-echoplex.net/csscrush", "license": "MIT" From 03179dd99fff1d3e4b0c27d2285522663225229c Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 10 Dec 2014 10:09:56 +0000 Subject: [PATCH 314/421] Removed `disable` option. Renamed `enable` option to `plugins`, old name still supported. --- cli.php | 20 +++++---------- docs/api/options.md | 7 +---- lib/CssCrush/Crush.php | 1 - lib/CssCrush/Options.php | 23 +++++++++-------- lib/CssCrush/Process.php | 38 +++++----------------------- tests/unit/CssCrush/OptionsTest.php | 2 +- tests/unit/CssCrush/UtilTest.php | 4 +-- tests/unit/api/apiTest.php | 1 - tests/unit/cli/context/crushfile.php | 2 +- 9 files changed, 31 insertions(+), 67 deletions(-) diff --git a/cli.php b/cli.php index ae05390..4dccdd7 100755 --- a/cli.php +++ b/cli.php @@ -107,11 +107,7 @@ } if ($args->enable_plugins) { - $options['enable'] = parse_list($args->enable_plugins); -} - -if ($args->disable_plugins) { - $options['disable'] = parse_list($args->disable_plugins); + $options['plugins'] = parse_list($args->enable_plugins); } if ($args->vendor_target) { @@ -424,7 +420,7 @@ function parse_args() { $required_value_opts = array( 'i|input|f|file', // Input file. Defaults to STDIN. 'o|output', // Output file. Defaults to STDOUT. - 'E|enable' , + 'E|enable|plugins', 'D|disable', 'vars|variables', 'formatter', @@ -499,12 +495,11 @@ function parse_args() { $args->newlines = pick($opts, 'newlines'); // Arguments that require a value but accept multiple values. - $args->enable_plugins = pick($opts, 'E', 'enable'); - $args->disable_plugins = pick($opts, 'D', 'disable'); + $args->enable_plugins = pick($opts, 'E', 'enable', 'plugins'); $args->vendor_target = pick($opts, 'vendor-target'); // Run multiple value arguments through array cast. - foreach (array('enable_plugins', 'disable_plugins', 'vendor_target') as $arg) { + foreach (array('enable_plugins', 'vendor_target') as $arg) { if ($args->$arg) { $args->$arg = (array) $args->$arg; } @@ -575,11 +570,8 @@ function manpage() { Writes to file specified with -o option or to the input file directory with a '.crush.css' file extension. - -D, --disable - List of plugins (comma separated) to disable. Pass 'all' to disable all. - - -E, --enable - List of plugins (comma separated) to enable. Overrides --disable. + -E, --plugins + List of plugins (comma separated) to enable. --boilerplate Whether or not to output a boilerplate. Optionally accepts filepath diff --git a/docs/api/options.md b/docs/api/options.md index f40f32f..c7a81b4 100644 --- a/docs/api/options.md +++ b/docs/api/options.md @@ -76,15 +76,10 @@ Rewrite relative URLs inside inlined imported files. - enable + plugins Array An array of plugin names to enable. - - disable - Array | "all" - An array of plugin names to disable. This option is always evaluated before enable. - source_map true | false diff --git a/lib/CssCrush/Crush.php b/lib/CssCrush/Crush.php index 23e1a77..86bccd3 100644 --- a/lib/CssCrush/Crush.php +++ b/lib/CssCrush/Crush.php @@ -43,7 +43,6 @@ public static function init() 'declarations' => array(), 'at-rules' => array(), ); - self::$config->plugins = array(); self::$config->options = new Options(); require_once self::$dir . '/misc/formatters.php'; diff --git a/lib/CssCrush/Options.php b/lib/CssCrush/Options.php index da83206..ae59315 100644 --- a/lib/CssCrush/Options.php +++ b/lib/CssCrush/Options.php @@ -25,8 +25,7 @@ class Options 'doc_root' => null, 'vendor_target' => 'all', 'rewrite_import_urls' => true, - 'enable' => null, - 'disable' => null, + 'plugins' => null, 'settings' => array(), 'stat_dump' => false, 'source_map' => false, @@ -41,8 +40,15 @@ public function __construct(array $options = array(), Options $defaults = null) $options += $defaults->get(); } - foreach ($options + self::$standardOptions as $key => $value) { - $this->__set($key, $value); + if (! empty($options['enable'])) { + if (empty($options['plugins'])) { + $options['plugins'] = $options['enable']; + } + unset($options['enable']); + } + + foreach ($options + self::$standardOptions as $name => $value) { + $this->__set($name, $value); } } @@ -52,7 +58,7 @@ public function __set($name, $value) switch ($name) { - // Legacy debug option. + // Legacy option. case 'debug': if (! array_key_exists('minify', $this->inputOptions)) { $name = 'minify'; @@ -109,8 +115,7 @@ public function __set($name, $value) break; // Options used internally as arrays. - case 'enable': - case 'disable': + case 'plugins': $value = (array) $value; break; } @@ -120,11 +125,9 @@ public function __set($name, $value) public function __get($name) { - $input_value = $this->inputOptions[$name]; - switch ($name) { case 'newlines': - switch ($input_value) { + switch ($this->inputOptions[$name]) { case 'windows': case 'win': return "\r\n"; diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 743cae6..5771c94 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -23,6 +23,7 @@ public function __construct($user_options = array(), $context = array()) $this->charset = null; $this->sources = array(); $this->vars = array(); + $this->plugins = array(); $this->settings = array(); $this->misc = new \stdClass(); $this->input = new \stdClass(); @@ -41,7 +42,6 @@ public function __construct($user_options = array(), $context = array()) $this->stat = array(); // Copy config values. - $this->plugins = $config->plugins; $this->aliases = $config->aliases; // Options. @@ -180,7 +180,7 @@ protected function getBoilerplate() 'datetime' => @date('Y-m-d H:i:s O'), 'year' => @date('Y'), 'command' => $commandArgs, - 'plugins' => implode(',', array_keys($this->plugins)), + 'plugins' => implode(',', $this->plugins), 'version' => csscrush_version(), 'git_version' => function () { return csscrush_version(true); @@ -347,34 +347,10 @@ protected function filterAliases() protected function filterPlugins() { - $options = $this->options; - $config = Crush::$config; - - // Checking for table keys is more convenient than array searching. - $disable = array_flip($options->disable); - $enable = array_flip($options->enable); - - if (isset($disable['all'])) { - $disable = $config->plugins; - } - - // Remove option disabled plugins from the list, and disable them. - if ($disable) { - foreach (array_keys($disable) as $pluginName) { - Plugin::disable($pluginName); - unset($this->plugins[$pluginName]); - } - } - - // Secondly add option enabled plugins to the list. - if ($enable) { - foreach (array_keys($enable) as $pluginName) { - $this->plugins[$pluginName] = true; - } - } + $this->plugins = array_unique($this->options->plugins); - foreach (array_keys($this->plugins) as $pluginName) { - Plugin::enable($pluginName); + foreach ($this->plugins as $plugin) { + Plugin::enable($plugin); } } @@ -892,8 +868,8 @@ public function preCompile() public function postCompile() { - foreach (array_keys($this->plugins) as $pluginName) { - Plugin::disable($pluginName); + foreach ($this->plugins as $plugin) { + Plugin::disable($plugin); } $this->release(); diff --git a/tests/unit/CssCrush/OptionsTest.php b/tests/unit/CssCrush/OptionsTest.php index 05c5636..d565421 100644 --- a/tests/unit/CssCrush/OptionsTest.php +++ b/tests/unit/CssCrush/OptionsTest.php @@ -21,7 +21,7 @@ public function testDefaults() $this->assertEquals($standardOptions, $options->get()); - $testOptions = array('enable' => array('foo', 'bar'), 'minify' => false); + $testOptions = array('plugins' => array('foo', 'bar'), 'minify' => false); $options = new Options($testOptions); $initialOptionsCopy = $testOptions + $standardOptions; diff --git a/tests/unit/CssCrush/UtilTest.php b/tests/unit/CssCrush/UtilTest.php index c6b3a06..c50aefe 100644 --- a/tests/unit/CssCrush/UtilTest.php +++ b/tests/unit/CssCrush/UtilTest.php @@ -95,14 +95,14 @@ public function testReadConfigFile() $contents = <<<'NOW_DOC' assertArrayHasKey('enable', $options); + $this->assertArrayHasKey('plugins', $options); $this->assertArrayNotHasKey('unrecognised_option', $options); } } diff --git a/tests/unit/api/apiTest.php b/tests/unit/api/apiTest.php index 860fac0..3f66b1d 100644 --- a/tests/unit/api/apiTest.php +++ b/tests/unit/api/apiTest.php @@ -80,7 +80,6 @@ public function testStat() csscrush_string($sample, array( 'minify' => false, - 'disable' => array( 'all' ), )); $stats = csscrush_stat(); diff --git a/tests/unit/cli/context/crushfile.php b/tests/unit/cli/context/crushfile.php index cf9b80d..57d7c6d 100644 --- a/tests/unit/cli/context/crushfile.php +++ b/tests/unit/cli/context/crushfile.php @@ -2,4 +2,4 @@ $context = __DIR__; -$enable = array('color'); +$plugins = array('color'); From 39808b176dcdfe8784a400a2d92b7c93f1a3f418 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 16 Feb 2015 20:31:04 +0000 Subject: [PATCH 315/421] Version bump. --- CHANGELOG.md | 13 +++++++++++++ LICENSE.txt | 2 +- composer.json | 2 +- package.json | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 400decd..e33bb6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,16 @@ +## 2.3.0 (2015-02-16) + +* Added support for function calls on media query lists. +* Added package.json for node package managers. +* Added `previous`/`next` context keywords to `query()` function. +* Removed legacy-flexbox plugin. +* Removed `disable` option. Renamed `enable` option to `plugins`, old name still supported. +* Removing trace option (SASS debug-info is obsolete) and related functionality. CSS source maps are now well supported. +* Color functions now return nothing if the color argument is invalid. +* Improvements to logging and error reporting. +* Various bug fixes. + + ## 2.2.0 (2014-06-17) * Rule nesting now works without `@in` directives. diff --git a/LICENSE.txt b/LICENSE.txt index 8feb8f2..effaf8f 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2010-2014 Pete Boere +Copyright (c) 2010-2015 Pete Boere 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 diff --git a/composer.json b/composer.json index a2e4810..60bf939 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,7 @@ }, "extra": { "branch-alias": { - "dev-master": "2.2-dev" + "dev-master": "2.3-dev" } } } diff --git a/package.json b/package.json index 936978e..43f8157 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "csscrush", - "version": "2.1.1", + "version": "2.3.0", "description": "CSS-Crush, CSS preprocessor", "repository": { "type": "git", From 4d3290829245666e73574d49bd10cce86e4cb0a9 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 16 Feb 2015 20:47:46 +0000 Subject: [PATCH 316/421] Version bump. --- lib/CssCrush/Crush.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/CssCrush/Crush.php b/lib/CssCrush/Crush.php index 86bccd3..6fe7c25 100644 --- a/lib/CssCrush/Crush.php +++ b/lib/CssCrush/Crush.php @@ -8,7 +8,7 @@ class Crush { - const VERSION = '2.2.1'; + const VERSION = '2.3.0'; // Global settings. public static $config; From 1a2682108b5c96cb2dae090974f4d2e2c82188c7 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 20 Feb 2015 09:21:28 +0000 Subject: [PATCH 317/421] Adding source maps to feature list. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 886d54c..a567733 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ CSS-Crush is a standards inspired preprocessor designed to enable a modern and u * Mixins * Minification * Lightweight plugin system +* Source maps See the [docs](http://the-echoplex.net/csscrush) for full details. From b86d748732ff9385cff1b49d093b7efad337c6a2 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 4 Mar 2015 09:28:04 +0000 Subject: [PATCH 318/421] Fixed issue with spaces in URL paths. --- lib/CssCrush/Url.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/CssCrush/Url.php b/lib/CssCrush/Url.php index df56173..0ad3aec 100644 --- a/lib/CssCrush/Url.php +++ b/lib/CssCrush/Url.php @@ -43,7 +43,7 @@ public function __toString() // Only wrap url with quotes if it contains tricky characters. $quote = ''; - if ($this->isData || preg_match('~[()*]~S', $this->value)) { + if ($this->isData || preg_match('~[()*\s]~S', $this->value)) { $quote = '"'; } From 41db1fcad94d16f22dfb8e551bdecd8d4e32b23f Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 4 Mar 2015 16:46:11 +0000 Subject: [PATCH 319/421] Adding a runtime variable for cache busting linked resources. --- lib/CssCrush/Process.php | 3 +++ tests/unit/api/apiTest.php | 3 +++ 2 files changed, 6 insertions(+) diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 5771c94..b3c1607 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -365,6 +365,9 @@ protected function captureVars() 'lowercase_keys' => false, )) + Crush::$process->vars; + // For convenience adding a runtime variable for cache busting linked resources. + $this->vars['timestamp'] = (int) $this->stat['compile_start_time']; + // In-file variables override global variables. $this->vars += Crush::$config->vars; diff --git a/tests/unit/api/apiTest.php b/tests/unit/api/apiTest.php index 3f66b1d..21f3b0f 100644 --- a/tests/unit/api/apiTest.php +++ b/tests/unit/api/apiTest.php @@ -86,6 +86,9 @@ public function testStat() $this->assertEquals(6, $stats['selector_count']); $this->assertEquals(2, $stats['rule_count']); + $this->assertArrayHasKey('timestamp', $stats['vars']); + unset($stats['vars']['timestamp']); + $this->assertEquals(array(), $stats['vars']); $this->assertEquals(array(), $stats['vars']); $this->assertEquals(array(), $stats['errors']); $this->assertTrue(isset($stats['compile_time'])); From c577f13765f11fa763fa126361666bf076f61e06 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 9 Mar 2015 16:33:00 +0000 Subject: [PATCH 320/421] #71 - Adding filter prefix. --- aliases.ini | 3 +++ 1 file changed, 3 insertions(+) diff --git a/aliases.ini b/aliases.ini index fb8e85a..59ed712 100644 --- a/aliases.ini +++ b/aliases.ini @@ -73,6 +73,9 @@ column-width[] = -webkit-column-width column-width[] = -moz-column-width + ; Filter. + filter[] = -webkit-filter + ; Flexbox (2012). ; ; Merges two similar versions of the flexbox spec: From ff3e40be1cf63f58836a9f05d6f2724d2cce91de Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 2 Apr 2015 16:22:10 +0100 Subject: [PATCH 321/421] Adding vendor aliases for CSS shapes. --- aliases.ini | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/aliases.ini b/aliases.ini index 59ed712..7cd79a8 100644 --- a/aliases.ini +++ b/aliases.ini @@ -144,6 +144,11 @@ perspective-origin[] = -webkit-perspective-origin perspective-origin[] = -moz-perspective-origin + ; Shapes + shape-image-threshold[] = -webkit-shape-image-threshold + shape-outside[] = -webkit-shape-outside + shape-margin[] = -webkit-shape-margin + ; Tab size. tab-size[] = -moz-tab-size tab-size[] = -o-tab-size From 1198de413332090065df740899101ca4442ec9b5 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 2 Apr 2015 17:11:22 +0100 Subject: [PATCH 322/421] Adding vendor aliases for box-decoration-break. --- aliases.ini | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/aliases.ini b/aliases.ini index 7cd79a8..2ff343f 100644 --- a/aliases.ini +++ b/aliases.ini @@ -42,6 +42,10 @@ border-image[] = -moz-border-image border-image[] = -o-border-image + ; Box decoration. + box-decoration-break[] = -webkit-box-decoration-break + box-decoration-break[] = -o-box-decoration-break + ; Box shadow. box-shadow[] = -webkit-box-shadow From 5773a26b5205a54cd2c6833ded5278edd0df3204 Mon Sep 17 00:00:00 2001 From: Adam Cook Date: Mon, 8 Jun 2015 22:39:23 +0100 Subject: [PATCH 323/421] Allow for @ifset () checks. @set bar true; @ifset bar(true) { p { margin-bottom: 5px; } } --- docs/core/variables.md | 6 +++++- lib/CssCrush/Process.php | 12 ++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/docs/core/variables.md b/docs/core/variables.md index c2b99ba..cf2ea0e 100644 --- a/docs/core/variables.md +++ b/docs/core/variables.md @@ -42,6 +42,7 @@ Sections of CSS can be included and excluded on the basis of variable existence ```crush @set foo #f00; +@set bar true; @ifset foo { p { @@ -54,5 +55,8 @@ p { @ifset not foo { line-height: 1.5; } + @ifset bar(true) { + margin-bottom: 5px; + } } -``` +``` \ No newline at end of file diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index b3c1607..e0952c6 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -457,7 +457,7 @@ protected function resolveSettings() protected function resolveIfDefines() { - $ifdefinePatt = Regex::make('~@if(?:set|define) \s+ (?not \s+)? (?{{ ident }}) \s* \{~ixS'); + $ifdefinePatt = Regex::make('~@if(?:set|define) \s+ (?not \s+)? (?{{ ident }}) \s* (?:\( \s* (?[a-zA-Z0-9_-]+) \s* \) \s*)? \s* \{~ixS'); $matches = $this->string->matchAll($ifdefinePatt); @@ -472,7 +472,15 @@ protected function resolveIfDefines() $negate = $match['negate'][1] != -1; $nameDefined = isset($this->vars[$match['name'][0]]); - if (! $negate && $nameDefined || $negate && ! $nameDefined) { + $valueDefined = isset($match['value'][0]); + $valueMatch = $nameDefined && $valueDefined ? $this->vars[$match['name'][0]] == $match['value'][0] : false; + + if( + ( $valueDefined && !$negate && $valueMatch ) + || ( $valueDefined && $negate && !$valueMatch ) + || ( !$valueDefined && !$negate && $nameDefined ) + || ( !$valueDefined && $negate && !$nameDefined ) + ) { $curlyMatch->unWrap(); } else { From b1dfa10a02295d17ffff9cdacb021f5d6455b18b Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 9 Jun 2015 14:11:52 +0100 Subject: [PATCH 324/421] Made some ammends after @MrAdamCook's PR to support comparing against string literals, urls, or other parenthesised values. Added tests. --- lib/CssCrush/Process.php | 13 +++++++++---- lib/CssCrush/Regex.php | 1 + lib/CssCrush/Tokens.php | 7 ++++++- lib/CssCrush/Util.php | 17 +++++++++++++++++ tests/unit/CssCrush/TokensTest.php | 7 +++++++ tests/unit/CssCrush/UtilTest.php | 26 ++++++++++++++++++++++++++ 6 files changed, 66 insertions(+), 5 deletions(-) diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index e0952c6..c15c7ee 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -457,7 +457,7 @@ protected function resolveSettings() protected function resolveIfDefines() { - $ifdefinePatt = Regex::make('~@if(?:set|define) \s+ (?not \s+)? (?{{ ident }}) \s* (?:\( \s* (?[a-zA-Z0-9_-]+) \s* \) \s*)? \s* \{~ixS'); + $ifdefinePatt = Regex::make('~@if(?:set|define) \s+ (?not \s+)? (?{{ ident }}) \s* {{ parens }}? \s* \{~ixS'); $matches = $this->string->matchAll($ifdefinePatt); @@ -472,10 +472,15 @@ protected function resolveIfDefines() $negate = $match['negate'][1] != -1; $nameDefined = isset($this->vars[$match['name'][0]]); - $valueDefined = isset($match['value'][0]); - $valueMatch = $nameDefined && $valueDefined ? $this->vars[$match['name'][0]] == $match['value'][0] : false; + $valueDefined = isset($match['parens_content'][0]); + $valueMatch = false; + if ($nameDefined && $valueDefined) { + $testValue = Util::rawValue(trim($match['parens_content'][0])); + $varValue = Util::rawValue($this->vars[$match['name'][0]]); + $valueMatch = $varValue == $testValue; + } - if( + if ( ( $valueDefined && !$negate && $valueMatch ) || ( $valueDefined && $negate && !$valueMatch ) || ( !$valueDefined && !$negate && $nameDefined ) diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php index eaa0218..1928eb5 100644 --- a/lib/CssCrush/Regex.php +++ b/lib/CssCrush/Regex.php @@ -85,6 +85,7 @@ public static function init() $patt->ruleDirective = '~^(?:(@include)|(@extends?)|(@name))[\s]+~iS'; $patt->argListSplit = '~\s*[,\s]\s*~S'; $patt->cruftyHex = Regex::make('~\#({{hex}})\1({{hex}})\2({{hex}})\3~S'); + $patt->token = Regex::make('~^ \? (?[a-zA-Z]) {{token_id}} \? $~xS'); } public static function make($pattern) diff --git a/lib/CssCrush/Tokens.php b/lib/CssCrush/Tokens.php index cd794ff..43a1475 100644 --- a/lib/CssCrush/Tokens.php +++ b/lib/CssCrush/Tokens.php @@ -146,11 +146,16 @@ public static function pad($label, $replaced_text) public static function is($label, $of_type) { - if (preg_match(Regex::make('~^ \? (?[a-zA-Z]) {{token_id}} \? $~xS'), $label, $m)) { + if (preg_match(Regex::$patt->token, $label, $m)) { return $of_type ? ($of_type === $m['type']) : true; } return false; } + + public static function test($value) + { + return preg_match(Regex::$patt->token, $value, $m) ? $m['type'] : false; + } } diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php index f3a5d00..4f02c32 100644 --- a/lib/CssCrush/Util.php +++ b/lib/CssCrush/Util.php @@ -228,6 +228,23 @@ public static function readConfigFile($path) return Options::filter(get_defined_vars()); } + /* + * Get raw value (useful if testing values that may or may not be a token). + */ + public static function rawValue($value) + { + if ($tokenType = Tokens::test($value)) { + if ($tokenType == 'u') { + $value = Crush::$process->tokens->get($value)->value; + } + elseif ($tokenType == 's') { + $value = Crush::$process->tokens->get($value); + } + } + + return $value; + } + /* * Encode integer to Base64 VLQ. */ diff --git a/tests/unit/CssCrush/TokensTest.php b/tests/unit/CssCrush/TokensTest.php index f544ca0..2c4880a 100644 --- a/tests/unit/CssCrush/TokensTest.php +++ b/tests/unit/CssCrush/TokensTest.php @@ -111,4 +111,11 @@ public function testIs() { $this->assertTrue(Tokens::is($this->tokens->createLabel('s'), 's')); } + + public function testTest() + { + $this->assertFalse(Tokens::test('foobar')); + $this->assertEquals(Tokens::test($this->tokens->createLabel('u')), 'u'); + $this->assertEquals(Tokens::test($this->tokens->createLabel('s')), 's'); + } } diff --git a/tests/unit/CssCrush/UtilTest.php b/tests/unit/CssCrush/UtilTest.php index c50aefe..04536cd 100644 --- a/tests/unit/CssCrush/UtilTest.php +++ b/tests/unit/CssCrush/UtilTest.php @@ -3,9 +3,17 @@ namespace CssCrush\UnitTest; use CssCrush\Util; +use CssCrush\Tokens; +use CssCrush\Url; class UtilTest extends \PHPUnit_Framework_TestCase { + public function setUp() + { + $this->process = bootstrap_process(array('minify' => false)); + $this->tokens = $this->process->tokens; + } + public function testNormalizePath() { $this->assertEquals('/Some/crazy/Path', Util::normalizePath('C:\\Some\crazy/Path\\', true)); @@ -90,6 +98,24 @@ public function testFilePutContents() $this->assertTrue(Util::filePutContents($test_file, 'Hello Mum')); } + public function testRawValue() + { + $url1 = $this->tokens->add(new Url('foo.jpg')); + $url2 = $this->tokens->add(new Url('foo.jpg')); + $this->assertNotEquals($url1, $url2); + $this->assertEquals(Util::rawValue($url1), Util::rawValue($url2)); + $this->assertEquals(Util::rawValue($url1), 'foo.jpg'); + + $string1 = $this->tokens->add('"bar"', 's'); + $string2 = $this->tokens->add('"bar"', 's'); + $this->assertNotEquals($string1, $string2); + $this->assertEquals(Util::rawValue($string1), Util::rawValue($string2)); + $this->assertEquals(Util::rawValue($string1), '"bar"'); + + $this->assertEquals(Util::rawValue('foobar'), 'foobar'); + $this->assertNotEquals(Util::rawValue('foobar'), 'notFoobar'); + } + public function testReadConfigFile() { $contents = <<<'NOW_DOC' From 5ca2d1a5425077076c3b13fd9023d57dd8ee721f Mon Sep 17 00:00:00 2001 From: orelus Date: Wed, 17 Jun 2015 15:50:07 +0200 Subject: [PATCH 325/421] Update Process.php --- lib/CssCrush/Process.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index c15c7ee..3bd1f16 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -900,6 +900,9 @@ public function compile() $importer = new Importer($this); $this->string = new StringObject($importer->collate()); + // Capture phase 0 hook: Before all variables and settings have resolved. + $this->hooks->run('capture_phase0', $this); + $this->captureVars(); $this->placeAllVars(); From 5bb8c6bb80f371ea817f8adaca090c84112306d7 Mon Sep 17 00:00:00 2001 From: orelus Date: Wed, 17 Jun 2015 15:51:15 +0200 Subject: [PATCH 326/421] Update Process.php --- lib/CssCrush/Process.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 3bd1f16..b55fddd 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -834,7 +834,9 @@ protected function collate() } // If output dir is different to input dir prepend a link between the two. elseif ($link) { - $url->prepend($link); + // If rewrite_import_urls is active + if($options->rewrite_import_urls === true) + $url->prepend($link); } } } From 365d8df049deb2340e523e495c15dfd8e3f2c2f3 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 29 Jun 2015 10:25:10 +0100 Subject: [PATCH 327/421] Minor style update. --- lib/CssCrush/Process.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index b55fddd..30cb53f 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -833,10 +833,8 @@ protected function collate() $url->toRoot(); } // If output dir is different to input dir prepend a link between the two. - elseif ($link) { - // If rewrite_import_urls is active - if($options->rewrite_import_urls === true) - $url->prepend($link); + elseif ($link && $options->rewrite_import_urls) { + $url->prepend($link); } } } @@ -904,7 +902,7 @@ public function compile() // Capture phase 0 hook: Before all variables and settings have resolved. $this->hooks->run('capture_phase0', $this); - + $this->captureVars(); $this->placeAllVars(); From 534be14a8027f13bc470576d7e2edf17134c899a Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 24 Jul 2015 10:24:46 +0100 Subject: [PATCH 328/421] Can now get/set color object components by name (red,green,blue,alpha). Added tests. Some style updates. --- lib/CssCrush/Color.php | 49 ++++++++++++++++++------------- tests/unit/CssCrush/ColorTest.php | 16 ++++++++++ 2 files changed, 44 insertions(+), 21 deletions(-) diff --git a/lib/CssCrush/Color.php b/lib/CssCrush/Color.php index 6d28b2e..0ee10af 100644 --- a/lib/CssCrush/Color.php +++ b/lib/CssCrush/Color.php @@ -8,19 +8,20 @@ class Color { - protected static $keywords, $minifyableKeywords; + protected static $minifyableKeywords; public static function getKeywords() { - if (! isset(self::$keywords)) { - if ($keywords = Util::parseIni(Crush::$dir . '/misc/color-keywords.ini')) { - foreach ($keywords as $keyword => $rgb) { - self::$keywords[$keyword] = array_map('floatval', explode(',', $rgb)) + array(0,0,0,1); + static $namedColors; + if (! isset($namedColors)) { + if ($colors = Util::parseIni(Crush::$dir . '/misc/color-keywords.ini')) { + foreach ($colors as $name => $rgb) { + $namedColors[$name] = array_map('floatval', explode(',', $rgb)) + array(0,0,0,1); } } } - return isset(Crush::$process->colorKeywords) ? Crush::$process->colorKeywords : self::$keywords; + return isset(Crush::$process->colorKeywords) ? Crush::$process->colorKeywords : $namedColors; } public static function getMinifyableKeywords() @@ -364,13 +365,19 @@ public static function colorSplit($str) protected $value; protected $hslColorSpace; + protected $namedComponents = array( + 'red' => 0, + 'green' => 1, + 'blue' => 2, + 'alpha' => 3, + ); public $isValid; - public function __construct($color, $use_hsl_color_space = false) + public function __construct($color, $useHslColorSpace = false) { $this->value = is_array($color) ? $color : self::parse($color); $this->isValid = ! empty($this->value); - if ($use_hsl_color_space && $this->isValid) { + if ($useHslColorSpace && $this->isValid) { $this->toHsl(); } } @@ -378,20 +385,18 @@ public function __construct($color, $use_hsl_color_space = false) public function __toString() { // For opaque colors return hex notation as it's the most compact. - if ($this->value[3] === 1) { + if ($this->getComponent('alpha') == 1) { return $this->getHex(); } - else { - - // R, G and B components must be integers. - $components = array(); - foreach (($this->hslColorSpace ? $this->getRgb() : $this->value) as $component_index => $component) { - $components[] = $component_index === 3 ? $component : min(round($component), 255); - } - return 'rgba(' . implode(',', $components) . ')'; + // R, G and B components must be integers. + $components = array(); + foreach (($this->hslColorSpace ? $this->getRgb() : $this->value) as $index => $component) { + $components[] = ($index === 3) ? $component : min(round($component), 255); } + + return 'rgba(' . implode(',', $components) . ')'; } public function toRgb() @@ -431,17 +436,19 @@ public function getRgb() public function getComponent($index) { + $index = isset($this->namedComponents[$index]) ? $this->namedComponents[$index] : $index; return $this->value[$index]; } - public function setComponent($index, $new_component_value) + public function setComponent($index, $newComponentValue) { - $this->value[$index] = $new_component_value; + $index = isset($this->namedComponents[$index]) ? $this->namedComponents[$index] : $index; + $this->value[$index] = is_numeric($newComponentValue) ? $newComponentValue : 0; } public function adjust(array $adjustments) { - $was_hsl_color_space = $this->hslColorSpace; + $wasHslColor = $this->hslColorSpace; $this->toHsl(); @@ -459,6 +466,6 @@ public function adjust(array $adjustments) } } - return ! $was_hsl_color_space ? $this->toRgb() : $this; + return ! $wasHslColor ? $this->toRgb() : $this; } } diff --git a/tests/unit/CssCrush/ColorTest.php b/tests/unit/CssCrush/ColorTest.php index b24c3d8..e10ebcb 100644 --- a/tests/unit/CssCrush/ColorTest.php +++ b/tests/unit/CssCrush/ColorTest.php @@ -47,6 +47,10 @@ public function testGetComponent() { $color = new Color('red'); $this->assertEquals(255, $color->getComponent(0)); + $this->assertEquals(255, $color->getComponent('red')); + $this->assertEquals(0, $color->getComponent('green')); + $this->assertEquals(0, $color->getComponent('blue')); + $this->assertEquals(1, $color->getComponent('alpha')); } public function testSetComponent() @@ -54,6 +58,18 @@ public function testSetComponent() $color = new Color('red'); $color->setComponent(0, 0); $this->assertEquals(0, $color->getComponent(0)); + + $color = new Color('#000000'); + $color->setComponent(3, '1'); + $this->assertEquals('#000000', $color->__toString()); + $color->setComponent(3, .5); + $this->assertEquals('rgba(0,0,0,0.5)', $color->__toString()); + + $color->setComponent('red', 100); + $color->setComponent('green', 100); + $color->setComponent('blue', 100); + $color->setComponent('alpha', .1); + $this->assertEquals('rgba(100,100,100,0.1)', $color->__toString()); } public function testColorSplit() From 5d999dab7d2a34fc2c022b9971cbdb332058f821 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 28 Jul 2015 20:01:27 +0100 Subject: [PATCH 329/421] Removed some obsolete vendor prefixes. --- aliases.ini | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/aliases.ini b/aliases.ini index 2ff343f..a4dcdc9 100644 --- a/aliases.ini +++ b/aliases.ini @@ -4,7 +4,6 @@ ; ; Sources: ; http://developer.mozilla.org/en-US/docs/CSS/CSS_Reference -; http://css3please.com ; http://caniuse.com/#cats=CSS ; ;---------------------------------------------------------------- @@ -112,24 +111,6 @@ order[] = -webkit-order order[] = -ms-flex-order - ; Flexbox (2009). - box-align[] = -webkit-box-align - box-align[] = -moz-box-align - box-direction[] = -webkit-box-direction - box-direction[] = -moz-box-direction - box-flex[] = -webkit-box-flex - box-flex[] = -moz-box-flex - box-flex-group[] = -webkit-box-flex-group - box-flex-group[] = -moz-box-flex-group - box-lines[] = -webkit-box-lines - box-lines[] = -moz-box-lines - box-ordinal-group[] = -webkit-box-ordinal-group - box-ordinal-group[] = -moz-box-ordinal-group - box-orient[] = -webkit-box-orient - box-orient[] = -moz-box-orient - box-pack[] = -webkit-box-pack - box-pack[] = -moz-box-pack - ; Hyphens. hyphens[] = -webkit-hyphens hyphens[] = -moz-hyphens @@ -236,10 +217,6 @@ justify-content:space-between[] = -ms-flex-pack:justify justify-content:space-around[] = -ms-flex-pack:distribute - ; Flexbox (2009). - display:box[] = display:-webkit-box - display:box[] = display:-moz-box - ; Cursor values (non-standard). cursor:zoom-in[] = cursor:-webkit-zoom-in cursor:zoom-in[] = cursor:-moz-zoom-in @@ -297,9 +274,6 @@ calc[] = -webkit-calc calc[] = -moz-calc - ; Element. - element[] = -moz-element - [functions.gradients] @@ -330,4 +304,3 @@ viewport[] = -moz-viewport viewport[] = -ms-viewport viewport[] = -o-viewport - From f632d04940d90054efbfe7a6675dc3aa8cac183f Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 30 Jul 2015 09:37:14 +0100 Subject: [PATCH 330/421] Version bump. --- CHANGELOG.md | 7 +++++++ lib/CssCrush/Crush.php | 2 +- package.json | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e33bb6a..8ed45f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## 2.4.0 (2015-07-30) + +* Added simple value checking to `@ifset`. +* Updated vendor aliases. +* Various fixes and under the hood improvements. + + ## 2.3.0 (2015-02-16) * Added support for function calls on media query lists. diff --git a/lib/CssCrush/Crush.php b/lib/CssCrush/Crush.php index 6fe7c25..a34be40 100644 --- a/lib/CssCrush/Crush.php +++ b/lib/CssCrush/Crush.php @@ -8,7 +8,7 @@ class Crush { - const VERSION = '2.3.0'; + const VERSION = '2.4.0'; // Global settings. public static $config; diff --git a/package.json b/package.json index 43f8157..a06d045 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "csscrush", - "version": "2.3.0", + "version": "2.4.0", "description": "CSS-Crush, CSS preprocessor", "repository": { "type": "git", From 7dae6971157e098a74c4d8716e5f4f6d9d3b60c7 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 30 Jul 2015 13:45:31 +0100 Subject: [PATCH 331/421] Updating composer.json branch alias. --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 60bf939..e500cf8 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,7 @@ }, "extra": { "branch-alias": { - "dev-master": "2.3-dev" + "dev-master": "2.5.x-dev" } } } From 0873e9a003a571faccb33d7eaa26b9f61787dc0d Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 30 Jul 2015 14:33:13 +0100 Subject: [PATCH 332/421] Revert "Updating composer.json branch alias." This reverts commit 7dae6971157e098a74c4d8716e5f4f6d9d3b60c7. --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index e500cf8..60bf939 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,7 @@ }, "extra": { "branch-alias": { - "dev-master": "2.5.x-dev" + "dev-master": "2.3-dev" } } } From 4a6759030549bf50f8e6db158ef561455af1a9b9 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 30 Jul 2015 14:34:46 +0100 Subject: [PATCH 333/421] Updating composer.json branch alias. --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 60bf939..6f9c142 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,7 @@ }, "extra": { "branch-alias": { - "dev-master": "2.3-dev" + "dev-master": "2.4.x-dev" } } } From 55bbd29c8bedc3e57e80d055f966a3211121f2ce Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 31 Jul 2015 11:42:08 +0100 Subject: [PATCH 334/421] Mixins can now include `@extend` directives. Rules can now have multiple `@extend` directives. --- lib/CssCrush/Declaration.php | 4 ++- lib/CssCrush/DeclarationList.php | 42 +++++++++++++++++++------------- lib/CssCrush/ExtendArg.php | 7 ++++-- lib/CssCrush/Functions.php | 2 +- lib/CssCrush/Process.php | 4 +-- lib/CssCrush/Rule.php | 8 +++--- 6 files changed, 39 insertions(+), 28 deletions(-) diff --git a/lib/CssCrush/Declaration.php b/lib/CssCrush/Declaration.php index 14dbf99..71c5b8f 100644 --- a/lib/CssCrush/Declaration.php +++ b/lib/CssCrush/Declaration.php @@ -90,7 +90,9 @@ public function process($parentRule) ); $this->value = $thisFunction->apply($this->value, $context); - $parentRule->declarations->data += array($this->property => $this->value); + if (isset($parentRule->declarations->data)) { + $parentRule->declarations->data += array($this->property => $this->value); + } $context = (object) array( 'rule' => $parentRule, diff --git a/lib/CssCrush/DeclarationList.php b/lib/CssCrush/DeclarationList.php index e208f88..46a3a95 100644 --- a/lib/CssCrush/DeclarationList.php +++ b/lib/CssCrush/DeclarationList.php @@ -10,6 +10,7 @@ class DeclarationList extends Iterator { public $flattened = true; public $processed = false; + protected $rule; public $properties = array(); public $canonicalProperties = array(); @@ -20,11 +21,12 @@ class DeclarationList extends Iterator // Declarations hash table for external query() referencing. public $queryData = array(); - public function __construct($declarations_string, Rule $rule) + public function __construct($declarationsString, Rule $rule) { parent::__construct(); - $pairs = DeclarationList::parse($declarations_string); + $this->rule = $rule; + $pairs = DeclarationList::parse($declarationsString); foreach ($pairs as $index => $pair) { @@ -32,12 +34,12 @@ public function __construct($declarations_string, Rule $rule) // Directives. if ($prop === 'extends') { - $rule->setExtendSelectors($value); + $this->rule->addExtendSelectors($value); unset($pairs[$index]); } elseif ($prop === 'name') { - if (! $rule->name) { - $rule->name = $value; + if (! $this->rule->name) { + $this->rule->name = $value; } unset($pairs[$index]); } @@ -405,7 +407,10 @@ public static function parse($str, $options = array()) } if ($options['apply_hooks']) { - Crush::$process->hooks->run('declaration_preprocess', array('property' => &$property, 'value' => &$value)); + Crush::$process->hooks->run('declaration_preprocess', array( + 'property' => &$property, + 'value' => &$value, + )); } } else { @@ -433,37 +438,40 @@ public static function parse($str, $options = array()) return $pairs; } - public function flatten($rule_context) + public function flatten() { if ($this->flattened) { return; } - $new_set = array(); + $newSet = array(); foreach ($this->store as $declaration) { if (is_array($declaration) && $declaration[0] === 'mixin') { - foreach (Mixin::merge(array(), $declaration[1], array('context' => $rule_context)) as $mixable) { + foreach (Mixin::merge(array(), $declaration[1], array('context' => $this->rule)) as $mixable) { if ($mixable instanceof Declaration) { $clone = clone $mixable; - $clone->index = count($new_set); - $new_set[] = $clone; + $clone->index = count($newSet); + $newSet[] = $clone; + } + elseif ($mixable[0] === 'extends') { + $this->rule->addExtendSelectors($mixable[1]); } else { - $new_set[] = new Declaration($mixable[0], $mixable[1], count($new_set)); + $newSet[] = new Declaration($mixable[0], $mixable[1], count($newSet)); } } } else { - $declaration->index = count($new_set); - $new_set[] = $declaration; + $declaration->index = count($newSet); + $newSet[] = $declaration; } } - $this->reset($new_set); + $this->reset($newSet); $this->flattened = true; } - public function process($rule_context) + public function process() { if ($this->processed) { return; @@ -472,7 +480,7 @@ public function process($rule_context) foreach ($this->store as $index => $declaration) { // Execute functions, store as data etc. - $declaration->process($rule_context); + $declaration->process($this->rule); // Drop declaration if value is now empty. if (! $declaration->valid) { diff --git a/lib/CssCrush/ExtendArg.php b/lib/CssCrush/ExtendArg.php index 8492dc0..eef511e 100644 --- a/lib/CssCrush/ExtendArg.php +++ b/lib/CssCrush/ExtendArg.php @@ -10,16 +10,19 @@ class ExtendArg { public $pointer; public $name; + public $raw; public $pseudo; public function __construct($name) { - $this->name = $name; + $this->name = + $this->raw = $name; if (! preg_match(Regex::$patt->rooted_ident, $this->name)) { // Not a regular name: Some kind of selector so normalize it for later comparison. - $this->name = Selector::makeReadable($this->name); + $this->name = + $this->raw = Selector::makeReadable($this->name); // If applying the pseudo on output store. if (substr($this->name, -1) === '!') { diff --git a/lib/CssCrush/Functions.php b/lib/CssCrush/Functions.php index edc999f..5f4a27d 100644 --- a/lib/CssCrush/Functions.php +++ b/lib/CssCrush/Functions.php @@ -321,7 +321,7 @@ function fn__query($input, $context) { $result = ''; if ($targetRule) { - $targetRule->declarations->process($targetRule); + $targetRule->declarations->process(); $targetRule->declarations->expandData('queryData', $property); if (isset($targetRule->declarations->queryData[$property])) { $result = $targetRule->declarations->queryData[$property]; diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 30cb53f..da3994e 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -633,8 +633,8 @@ protected function processRules() foreach ($this->tokens->store->r as $rule) { - $rule->declarations->flatten($rule); - $rule->declarations->process($rule); + $rule->declarations->flatten(); + $rule->declarations->process(); $this->hooks->run('rule_prealias', $rule); diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php index e1d9a08..392bc8d 100644 --- a/lib/CssCrush/Rule.php +++ b/lib/CssCrush/Rule.php @@ -70,13 +70,11 @@ public function __clone() ############################# # Rule inheritance. - public function setExtendSelectors($rawValue) + public function addExtendSelectors($rawValue) { - // Reset if called earlier, last call wins by intention. - $this->extendArgs = array(); - foreach (Util::splitDelimList($rawValue) as $arg) { - $this->extendArgs[] = new ExtendArg($arg); + $extendArg = new ExtendArg($arg); + $this->extendArgs[$extendArg->raw] = $extendArg; } } From 381545391becfa79c8acaea812b9d1fcd0ba132a Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 31 Jul 2015 14:03:15 +0100 Subject: [PATCH 335/421] Functions can now parse with a leading sign (-), useful in some situations. e.g. @set foo math(10 * 10, px); p { margin-top: -$(foo); } --- lib/CssCrush/Functions.php | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/lib/CssCrush/Functions.php b/lib/CssCrush/Functions.php index 5f4a27d..28fa8ac 100644 --- a/lib/CssCrush/Functions.php +++ b/lib/CssCrush/Functions.php @@ -73,7 +73,12 @@ public function apply($str, \stdClass $context = null) while ($match = array_pop($matches)) { - list($function, $offset) = $match['function']; + if (isset($match['function']) && $match['function'][1] !== -1) { + list($function, $offset) = $match['function']; + } + else { + list($function, $offset) = $match['simple_function']; + } if (! preg_match(Regex::$patt->parens, $str, $parens, PREG_OFFSET_CAPTURE, $offset)) { continue; @@ -142,16 +147,24 @@ public static function makePattern($functionNames) } } - $flatList = ''; - if (! $idents) { - $flatList = implode('|', $nonIdents); + if ($idents) { + $idents = '{{ LB }}-?(?' . implode('|', $idents) . ')'; } - else { - $idents = '{{ LB }}(?:' . implode('|', $idents) . ')'; - $flatList = $nonIdents ? '(?:' . implode('|', $nonIdents) . "|$idents)" : $idents; + if ($nonIdents) { + $nonIdents = '(?' . implode('|', $nonIdents) . ')'; + } + + if ($idents && $nonIdents) { + $patt = "(?:$idents|$nonIdents)"; + } + elseif ($idents) { + $patt = $idents; + } + elseif ($nonIdents) { + $patt = $nonIdents; } - return Regex::make("~(?$flatList)\(~iS"); + return Regex::make("~$patt\(~iS"); } } From f2f6c3e7d589fad412993ba01c5f95114a64987e Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 31 Jul 2015 14:11:54 +0100 Subject: [PATCH 336/421] Updated test. --- tests/unit/CssCrush/FunctionsTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/unit/CssCrush/FunctionsTest.php b/tests/unit/CssCrush/FunctionsTest.php index 85e46a9..8a59120 100644 --- a/tests/unit/CssCrush/FunctionsTest.php +++ b/tests/unit/CssCrush/FunctionsTest.php @@ -9,12 +9,12 @@ class FunctionsTest extends \PHPUnit_Framework_TestCase public function testMakePattern() { $patt = Functions::makePattern(array('foo', 'bar')); - $this->assertEquals('~(?(?assertEquals('~(?foo|bar)\(~iS', $patt); $patt = Functions::makePattern(array('foo', 'bar', '#')); - $this->assertEquals('~(?(?:#|(?assertEquals('~(?:(?foo|bar)|(?#))\(~iS', $patt); $patt = Functions::makePattern(array('$', '#')); - $this->assertEquals('~(?\$|#)\(~iS', $patt); + $this->assertEquals('~(?\$|#)\(~iS', $patt); } } From ef70384400231f65009bd104df9595f8f0d93641 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 31 Jul 2015 21:10:11 +0100 Subject: [PATCH 337/421] Fixing math parse exception for php7 compatibility. --- lib/CssCrush/Functions.php | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/lib/CssCrush/Functions.php b/lib/CssCrush/Functions.php index 28fa8ac..19308be 100644 --- a/lib/CssCrush/Functions.php +++ b/lib/CssCrush/Functions.php @@ -182,10 +182,21 @@ function fn__math($input) { array(M_PI), $expression); - // Strip blacklisted characters. - $expression = preg_replace('~[^\.0-9\*\/\+\-\(\)]~S', '', $expression); + // Filter expression so it's just characters necessary for simple math. + $expression = preg_replace("~[^.0-9/*()+-]~S", '', $expression); - $result = @eval("return $expression;"); + $evalExpression = "return $expression;"; + $result = false; + + if (class_exists('\\ParseError')) { + try { + $result = eval($evalExpression); + } + catch (\ParseError $e) {} + } + else { + $result = @eval($evalExpression); + } return ($result === false ? 0 : round($result, 5)) . $unit; } From 0b1835fc07971f5d217cbe1ed6a32106560f0729 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 3 Aug 2015 08:53:10 +0100 Subject: [PATCH 338/421] Added php7 (allowing failures) to CI config. --- .travis.yml | 5 +++++ lib/CssCrush/Functions.php | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 23e79b4..8311b99 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,8 +5,13 @@ php: - 5.4 - 5.5 - 5.6 + - 7.0 - hhvm +matrix: + allow_failures: + - php: 7.0 + before_script: - composer self-update - composer install --dev diff --git a/lib/CssCrush/Functions.php b/lib/CssCrush/Functions.php index 19308be..b365682 100644 --- a/lib/CssCrush/Functions.php +++ b/lib/CssCrush/Functions.php @@ -190,9 +190,9 @@ function fn__math($input) { if (class_exists('\\ParseError')) { try { - $result = eval($evalExpression); + $result = @eval($evalExpression); } - catch (\ParseError $e) {} + catch (\Error $e) {} } else { $result = @eval($evalExpression); From 100c74e9caf7c5e2f42acb92357c71fc147b9ff9 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 27 Aug 2015 10:15:26 +0100 Subject: [PATCH 339/421] Amended doc for rem plugin. --- plugins/rem.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/rem.php b/plugins/rem.php index 8e29cf8..6a186a3 100644 --- a/plugins/rem.php +++ b/plugins/rem.php @@ -37,8 +37,8 @@ function rem(Rule $rule) { // Determine what conversion mode we're using. $mode = Crush::$process->settings->get('rem-mode', $modes[0]); - // Determine the default base em-size, to my knowledge always 16px. - $base = Crush::$process->settings->get('rem-base', 16); + // Determine a base font size for calculations, in browsers this is usually 16px. + $base = floatval(Crush::$process->settings->get('rem-base', 16)); // Select the length match pattern depending on mode. $length_patt = $mode === 'rem-fallback' ? $rem_patt : $px_patt; From 956b9d56834f4d7f09be253ddc4503e1ebb4903e Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 28 Aug 2015 10:00:09 +0100 Subject: [PATCH 340/421] Fix for #79 Regular function aliasing (not function groups) was using an out of date regex. Have added a local integration test to mark the regression. --- lib/CssCrush/Crush.php | 4 +++- lib/CssCrush/DeclarationList.php | 3 +-- lib/CssCrush/Regex.php | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/CssCrush/Crush.php b/lib/CssCrush/Crush.php index a34be40..6a6164f 100644 --- a/lib/CssCrush/Crush.php +++ b/lib/CssCrush/Crush.php @@ -155,17 +155,19 @@ public static function parseAliasesFile($file) if (preg_match($regex->vendorPrefix, $alias_func, $m)) { // We'll cache the function matching regex here. - $vendor_grouped_aliases[$m[1]]['find'][] = Regex::make("~{{ LB }}$func_name(?=\()~i"); + $vendor_grouped_aliases[$m[1]]['find'][] = Regex::make("~{{ LB }}$func_name(?=\()~iS"); $vendor_grouped_aliases[$m[1]]['replace'][] = $alias_func; } } } $tree['function_groups'][$group] = $vendor_grouped_aliases; + unset($tree[$section]); } } $tree += self::$config->bareAliases; + // Persisting dummy aliases for testing purposes. $tree['properties']['foo'] = $tree['at-rules']['foo'] = $tree['functions']['foo'] = array('-webkit-foo', '-moz-foo', '-ms-foo'); diff --git a/lib/CssCrush/DeclarationList.php b/lib/CssCrush/DeclarationList.php index 46a3a95..c14511b 100644 --- a/lib/CssCrush/DeclarationList.php +++ b/lib/CssCrush/DeclarationList.php @@ -271,7 +271,6 @@ public function aliasFunctions($vendor_context = null) // Single function aliases. else { - foreach ($function_aliases[$fn_name] as $fn_alias) { // If the declaration is vendor specific only create aliases for the same vendor. @@ -289,7 +288,7 @@ public function aliasFunctions($vendor_context = null) // Make swaps. $copy->value = preg_replace( - '~(?value ); diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php index 1928eb5..ba8c462 100644 --- a/lib/CssCrush/Regex.php +++ b/lib/CssCrush/Regex.php @@ -97,7 +97,7 @@ public static function make($pattern) } return $cache[$pattern] = preg_replace_callback('~\{\{ *(?\w+) *\}\}~S', function ($m) { - return Regex::$classes->{ $m['name'] }; + return Regex::$classes->$m['name']; }, $pattern); } From a56a01b03fbb2cdd8eac49b3f0faf795e6a9543a Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 28 Aug 2015 10:07:31 +0100 Subject: [PATCH 341/421] Reverted a small syntax change which choked on php7. --- lib/CssCrush/Regex.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php index ba8c462..1928eb5 100644 --- a/lib/CssCrush/Regex.php +++ b/lib/CssCrush/Regex.php @@ -97,7 +97,7 @@ public static function make($pattern) } return $cache[$pattern] = preg_replace_callback('~\{\{ *(?\w+) *\}\}~S', function ($m) { - return Regex::$classes->$m['name']; + return Regex::$classes->{ $m['name'] }; }, $pattern); } From cbe2a7149a8d72488ec86d4fd1fba70eb1ef5604 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 14 Sep 2015 14:01:16 +0100 Subject: [PATCH 342/421] Fix for #81. Thanks @biziclop --- lib/CssCrush/Rule.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php index 392bc8d..931ce61 100644 --- a/lib/CssCrush/Rule.php +++ b/lib/CssCrush/Rule.php @@ -55,8 +55,7 @@ public function __toString() return "$stub{$this->selectors->join()}{{$this->declarations->join()}}"; } else { - $formatter = $process->ruleFormatter; - return "$stub{$formatter($this)}"; + return $stub . call_user_func($process->ruleFormatter, $this); } } From 79b759d4252d394ecac3e80aa54520d7cacc5c86 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 2 Nov 2015 15:14:27 +0000 Subject: [PATCH 343/421] Failure to create directories should produce a warning. --- lib/CssCrush/Options.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/CssCrush/Options.php b/lib/CssCrush/Options.php index ae59315..06dca5b 100644 --- a/lib/CssCrush/Options.php +++ b/lib/CssCrush/Options.php @@ -96,7 +96,7 @@ public function __set($name, $value) if (is_string($value)) { $value = Util::resolveUserPath($value, function ($path) use ($name) { if (! @mkdir($path, 0755, true)) { - notice("Could not create directory $path (setting `$name` option)."); + warning("Could not create directory $path (setting `$name` option)."); } else { debug("Created directory $path (setting `$name` option)."); From 9dc087f64371c772a2aa36284623185e76ed8c50 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 3 Nov 2015 11:04:39 +0000 Subject: [PATCH 344/421] Better error message for un-readable imports. --- lib/CssCrush/Importer.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index 4e41b76..0760260 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -76,8 +76,8 @@ public function collate() // If unsuccessful getting import contents continue with the import line removed. $import->content = @file_get_contents($import->path); if ($import->content === false) { - - notice("Import file '{$import->url->value}' not found"); + notice("@import 'http://github.com/%7B$import-%3Eurl-%3Evalue%7D' " . + (! is_readable($import->path) ? 'is not readable' : 'does not exist')); $str = substr_replace($str, '', $match_start, $match_len); continue; } From 7f5ff42ac7b70c51853f6c9f9d55e4b2b0554b88 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 31 Aug 2017 13:33:47 +0100 Subject: [PATCH 345/421] Raise php requirement to >= 5.6 Update vendor aliases. Remove legacy IE plugins. Remove 'initial', 'hsl2hex' and 'text-align' plugins. Remove loop plugin color-range. Combine svg plugins. Remove support for @in. --- .travis.yml | 10 +- CHANGELOG.md | 3 + aliases.ini | 30 --- cli.php | 4 +- composer.json | 9 +- docs/api/functions.md | 6 +- docs/plugins/hsl2hex.md | 10 - docs/plugins/ie-inline-block.md | 12 -- docs/plugins/ie-opacity.md | 13 -- docs/plugins/initial.md | 16 -- docs/plugins/loop.md | 21 -- docs/plugins/text-align.md | 17 -- lib/CssCrush/Crush.php | 54 ------ lib/CssCrush/Options.php | 8 - lib/CssCrush/Process.php | 31 +-- lib/CssCrush/Template.php | 1 - lib/functions.php | 9 +- misc/initial-values.ini | 152 --------------- package.json | 2 +- plugins/hsl2hex.php | 28 --- plugins/ie-inline-block.php | 36 ---- plugins/ie-opacity.php | 44 ----- plugins/initial.php | 32 ---- plugins/loop.php | 49 +---- plugins/svg-gradients.php | 329 -------------------------------- plugins/svg.php | 324 ++++++++++++++++++++++++++++++- plugins/text-align.php | 49 ----- 27 files changed, 332 insertions(+), 967 deletions(-) delete mode 100644 docs/plugins/hsl2hex.md delete mode 100644 docs/plugins/ie-inline-block.md delete mode 100644 docs/plugins/ie-opacity.md delete mode 100644 docs/plugins/initial.md delete mode 100644 docs/plugins/text-align.md delete mode 100644 misc/initial-values.ini delete mode 100644 plugins/hsl2hex.php delete mode 100644 plugins/ie-inline-block.php delete mode 100755 plugins/ie-opacity.php delete mode 100644 plugins/initial.php delete mode 100644 plugins/svg-gradients.php delete mode 100644 plugins/text-align.php diff --git a/.travis.yml b/.travis.yml index 8311b99..4a11ab4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,16 +1,10 @@ language: php php: - - 5.3 - - 5.4 - - 5.5 - 5.6 - 7.0 - - hhvm - -matrix: - allow_failures: - - php: 7.0 + - 7.1 + - 7.2 before_script: - composer self-update diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ed45f7..e0df7de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 3.0.0-beta (2017-08) + + ## 2.4.0 (2015-07-30) * Added simple value checking to `@ifset`. diff --git a/aliases.ini b/aliases.ini index a4dcdc9..1cc4687 100644 --- a/aliases.ini +++ b/aliases.ini @@ -14,36 +14,23 @@ ; Animations. animation[] = -webkit-animation - animation[] = -moz-animation animation-delay[] = -webkit-animation-delay - animation-delay[] = -moz-animation-delay animation-direction[] = -webkit-animation-direction - animation-direction[] = -moz-animation-direction animation-duration[] = -webkit-animation-duration - animation-duration[] = -moz-animation-duration animation-fill-mode[] = -webkit-animation-fill-mode - animation-fill-mode[] = -moz-animation-fill-mode animation-iteration-count[] = -webkit-animation-iteration-count - animation-iteration-count[] = -moz-animation-iteration-count animation-name[] = -webkit-animation-name - animation-name[] = -moz-animation-name animation-play-state[] = -webkit-animation-play-state - animation-play-state[] = -moz-animation-play-state animation-timing-function[] = -webkit-animation-timing-function - animation-timing-function[] = -moz-animation-timing-function ; Backface visibility. backface-visibility[] = -webkit-backface-visibility - backface-visibility[] = -moz-backface-visibility ; Border-image. border-image[] = -webkit-border-image - border-image[] = -moz-border-image - border-image[] = -o-border-image ; Box decoration. box-decoration-break[] = -webkit-box-decoration-break - box-decoration-break[] = -o-box-decoration-break ; Box shadow. box-shadow[] = -webkit-box-shadow @@ -125,9 +112,7 @@ ; Perspective. perspective[] = -webkit-perspective - perspective[] = -moz-perspective perspective-origin[] = -webkit-perspective-origin - perspective-origin[] = -moz-perspective-origin ; Shapes shape-image-threshold[] = -webkit-shape-image-threshold @@ -152,26 +137,18 @@ ; Transforms. transform[] = -webkit-transform - transform[] = -moz-transform transform[] = -ms-transform transform-origin[] = -webkit-transform-origin - transform-origin[] = -moz-transform-origin transform-origin[] = -ms-transform-origin transform-style[] = -webkit-transform-style - transform-style[] = -moz-transform-style transform-style[] = -ms-transform-style ; Transitions. transition[] = -webkit-transition - transition[] = -moz-transition transition-delay[] = -webkit-transition-delay - transition-delay[] = -moz-transition-delay transition-duration[] = -webkit-transition-duration - transition-duration[] = -moz-transition-duration transition-property[] = -webkit-transition-property - transition-property[] = -moz-transition-property transition-timing-function[] = -webkit-transition-timing-function - transition-timing-function[] = -moz-transition-timing-function ; User select (non standard). user-select[] = -webkit-user-select @@ -272,22 +249,17 @@ ; Calc. calc[] = -webkit-calc - calc[] = -moz-calc [functions.gradients] ; Gradients. linear-gradient[] = -webkit-linear-gradient - linear-gradient[] = -moz-linear-gradient radial-gradient[] = -webkit-radial-gradient - radial-gradient[] = -moz-radial-gradient ; Repeating gradients. repeating-linear-gradient[] = -webkit-repeating-linear-gradient - repeating-linear-gradient[] = -moz-repeating-linear-gradient repeating-radial-gradient[] = -webkit-repeating-radial-gradient - repeating-radial-gradient[] = -moz-repeating-radial-gradient ;---------------------------------------------------------------- @@ -297,10 +269,8 @@ ; Keyframes. keyframes[] = -webkit-keyframes - keyframes[] = -moz-keyframes ; Viewport. viewport[] = -webkit-viewport - viewport[] = -moz-viewport viewport[] = -ms-viewport viewport[] = -o-viewport diff --git a/cli.php b/cli.php index 4dccdd7..d1e69d5 100755 --- a/cli.php +++ b/cli.php @@ -10,7 +10,7 @@ define('STATUS_ERROR', 1); $version = PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION; -$requiredVersion = 5.3; +$requiredVersion = 5.6; if ($version < $requiredVersion) { @@ -36,7 +36,7 @@ if ($args->version) { - stdout((string) csscrush_version(true)); + stdout((string) csscrush_version()); exit(STATUS_OK); } diff --git a/composer.json b/composer.json index 6f9c142..10cee4b 100644 --- a/composer.json +++ b/composer.json @@ -16,10 +16,10 @@ } ], "require": { - "php": ">=5.3.3" + "php": ">=5.6.1" }, "require-dev": { - "phpunit/phpunit": "4.1.*", + "phpunit/phpunit": "5.*", "psr/log": "1.0.*@dev", "twig/twig": "1.*" }, @@ -29,10 +29,5 @@ "autoload": { "psr-0": { "CssCrush": "lib/" }, "files": [ "lib/functions.php" ] - }, - "extra": { - "branch-alias": { - "dev-master": "2.4.x-dev" - } } } diff --git a/docs/api/functions.md b/docs/api/functions.md index 1d7901c..599bf11 100644 --- a/docs/api/functions.md +++ b/docs/api/functions.md @@ -63,11 +63,7 @@ subsequent compilations within the duration of the script. Get the library version. -`csscrush_version( [bool $use_git = false] )` - -### Parameters - - * `$use_git` Return version as reported by shell command `git describe`. +`csscrush_version()` *************** diff --git a/docs/plugins/hsl2hex.md b/docs/plugins/hsl2hex.md deleted file mode 100644 index 16acbe0..0000000 --- a/docs/plugins/hsl2hex.md +++ /dev/null @@ -1,10 +0,0 @@ - -Convert colors in hsl() notation to hex notation. - -```css -color: hsl( 100, 50%, 50%); -``` - -```css -color: #6abf40; -``` \ No newline at end of file diff --git a/docs/plugins/ie-inline-block.md b/docs/plugins/ie-inline-block.md deleted file mode 100644 index a9d559c..0000000 --- a/docs/plugins/ie-inline-block.md +++ /dev/null @@ -1,12 +0,0 @@ - -Polyfill for display:inline-block in IE < 8 - -```css -display: inline-block; -``` - -```css -display: inline-block; -*display: inline; -*zoom: 1; -``` \ No newline at end of file diff --git a/docs/plugins/ie-opacity.md b/docs/plugins/ie-opacity.md deleted file mode 100644 index aacee49..0000000 --- a/docs/plugins/ie-opacity.md +++ /dev/null @@ -1,13 +0,0 @@ - -Polyfill for opacity in IE < 9 - -```css -opacity: 0.45; -``` - -```css -opacity: 0.45; --ms-filter: "alpha(opacity=45)"; -*filter: alpha(opacity=45); -zoom: 1; -``` \ No newline at end of file diff --git a/docs/plugins/initial.md b/docs/plugins/initial.md deleted file mode 100644 index c469fca..0000000 --- a/docs/plugins/initial.md +++ /dev/null @@ -1,16 +0,0 @@ - -Polyfill for the 'initial' keyword - -* [Initial keyword spec](http://www.w3.org/TR/css3-cascade/#initial0) - -```css -opacity: initial; -white-space: initial; -min-height: initial; -``` - -```css -opacity: 1; -white-space: normal; -max-height: auto; -``` diff --git a/docs/plugins/loop.md b/docs/plugins/loop.md index 2f7b169..7af935d 100644 --- a/docs/plugins/loop.md +++ b/docs/plugins/loop.md @@ -34,24 +34,3 @@ For...in loops with lists and generator functions. .grid-23-of-24 { width: 95.83333%; } .grid-24-of-24 { width: 100%; } ``` - -```crush -/* The last argument to color-range() is an integer - specifying how many transition colors to generate - between the color arguments. */ -@for color in color-range(powderblue, deeppink, a-adjust(yellow, -80), 5) { - .foo-#(loop.counter) { - background-color: #(color); - } -} -``` - -```css -.foo-1 { background-color: #b0e0e6; } -.foo-2 { background-color: #bdbed8; } -/* - Intermediate steps ommited -*/ -.foo-12 { background-color: rgba(255,216,25,.33); } -.foo-13 { background-color: rgba(255,255,0,.2); } -``` diff --git a/docs/plugins/text-align.md b/docs/plugins/text-align.md deleted file mode 100644 index 9f7392a..0000000 --- a/docs/plugins/text-align.md +++ /dev/null @@ -1,17 +0,0 @@ - -Polyfill for direction sensitive text-align values, start and end. - -## Settings - -### dir - -Text direction. Value is `ltr` by default. If set to `rtl`, `start` translates to `right` and `end` translates to `left` - -```crush -text-align: start; -``` - -```css -text-align: left; -text-align: start; -``` diff --git a/lib/CssCrush/Crush.php b/lib/CssCrush/Crush.php index 6a6164f..37b2ce5 100644 --- a/lib/CssCrush/Crush.php +++ b/lib/CssCrush/Crush.php @@ -8,8 +8,6 @@ class Crush { - const VERSION = '2.4.0'; - // Global settings. public static $config; @@ -26,7 +24,6 @@ public static function init() self::$config = new \stdClass(); self::$config->pluginDirs = array(self::$dir . '/plugins'); - self::$config->version = new Version(self::VERSION); self::$config->scriptDir = dirname(realpath($_SERVER['SCRIPT_FILENAME'])); self::$config->docRoot = self::resolveDocRoot(); self::$config->logger = new Logger(); @@ -175,57 +172,6 @@ public static function parseAliasesFile($file) return $tree; } - /** - * Deprecated. - * - * @see csscrush_file(). - */ - public static function file($file, $options = array()) - { - return csscrush_file($file, $options); - } - - /** - * Deprecated. - * - * @see csscrush_tag(). - */ - public static function tag($file, $options = array(), $tag_attributes = array()) - { - return csscrush_tag($file, $options, $tag_attributes); - } - - /** - * Deprecated. - * - * @see csscrush_inline(). - */ - public static function inline($file, $options = array(), $tag_attributes = array()) - { - return csscrush_inline($file, $options, $tag_attributes); - } - - /** - * Deprecated. - * - * @see csscrush_string(). - */ - public static function string($string, $options = array()) - { - return csscrush_string($string, $options); - } - - /** - * Deprecated. - * - * @see csscrush_stat(). - */ - public static function stat() - { - return csscrush_stat(); - } - - ############################# # Logging and stats. diff --git a/lib/CssCrush/Options.php b/lib/CssCrush/Options.php index 06dca5b..5974195 100644 --- a/lib/CssCrush/Options.php +++ b/lib/CssCrush/Options.php @@ -58,14 +58,6 @@ public function __set($name, $value) switch ($name) { - // Legacy option. - case 'debug': - if (! array_key_exists('minify', $this->inputOptions)) { - $name = 'minify'; - $value = ! $value; - } - break; - case 'formatter': if (is_string($value) && isset(Crush::$config->formatters[$value])) { $value = Crush::$config->formatters[$value]; diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index da3994e..3f23409 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -183,7 +183,7 @@ protected function getBoilerplate() 'plugins' => implode(',', $this->plugins), 'version' => csscrush_version(), 'git_version' => function () { - return csscrush_version(true); + return csscrush_version(); }, 'compile_time' => function () { $now = microtime(true) - Crush::$process->stat['compile_start_time']; @@ -652,33 +652,6 @@ protected function processRules() } - ############################# - # @in blocks. - - protected function resolveInBlocks() - { - $matches = $this->string->matchAll('~@in\s+(?[^{]+)\{~iS'); - - while ($match = array_pop($matches)) { - - $selectorsMatch = trim($match['selectors'][0]); - $curlyMatch = new BalancedMatch($this->string, $match[0][1]); - - if (! $curlyMatch->match || empty($selectorsMatch)) { - continue; - } - - $rawSelectors = Util::splitDelimList($selectorsMatch); - - foreach (Regex::matchAll(Regex::$patt->r_token, $curlyMatch->inside()) as $ruleMatch) { - Crush::$process->tokens->get($ruleMatch[0][0])->selectors->merge($rawSelectors); - } - - $curlyMatch->unWrap(); - } - } - - ############################# # @-rule aliasing. @@ -925,8 +898,6 @@ public function compile() $this->captureRules(); - $this->resolveInBlocks(); - // Calling functions on media query lists. $process = $this; $this->string->pregReplaceCallback('~@media\s+(?[^{]+)\{~i', function ($m) use (&$process) { diff --git a/lib/CssCrush/Template.php b/lib/CssCrush/Template.php index d549446..3c8fc30 100644 --- a/lib/CssCrush/Template.php +++ b/lib/CssCrush/Template.php @@ -56,7 +56,6 @@ public function __construct($str) }; $templateFunctions->register['#'] = $captureCallback; - $templateFunctions->register['arg'] = $captureCallback; $this->string = $templateFunctions->apply($str); } diff --git a/lib/functions.php b/lib/functions.php index 5a8a955..f25410b 100644 --- a/lib/functions.php +++ b/lib/functions.php @@ -5,8 +5,6 @@ * */ use CssCrush\Crush; -class_alias('CssCrush\Crush', 'CssCrush\CssCrush'); - /** * Process CSS file and return a new compiled file. @@ -177,12 +175,9 @@ function csscrush_add_function($function_name = null, $callback = null) { * * @see docs/api/functions.md */ -function csscrush_version($use_git = false) { +function csscrush_version() { - if ($use_git && $version = \CssCrush\Version::gitDescribe()) { - return $version; - } - return Crush::$config->version; + return \CssCrush\Version::gitDescribe(); } diff --git a/misc/initial-values.ini b/misc/initial-values.ini deleted file mode 100644 index 7755969..0000000 --- a/misc/initial-values.ini +++ /dev/null @@ -1,152 +0,0 @@ -; Sources: -; http://reference.sitepoint.com/css -; http://developer.mozilla.org/en/CSS - -animation = "none" -animation-delay = "0" -animation-direction = "normal" -animation-duration = "0" -animation-fill-mode = "none" -animation-iteration-count = "1" -animation-name = "none" -animation-play-state = "running" -animation-timing-function = "ease" -backface-visibility = "visible" -background = "0" -background-attachment = "scroll" -background-clip = "border-box" -background-color = "transparent" -background-image = "none" -background-origin = "padding-box" -background-position = "0 0" -background-position-x = "0" -background-position-y = "0" -background-repeat = "repeat" -background-size = "auto auto" -border = "0" -border-style = "none" -border-width = "medium" -border-color = "inherit" -border-bottom = "0" -border-bottom-color = "inherit" -border-bottom-left-radius = "0" -border-bottom-right-radius = "0" -border-bottom-style = "none" -border-bottom-width = "medium" -border-collapse = "separate" -border-image = "none" -border-left = "0" -border-left-color = "inherit" -border-left-style = "none" -border-left-width = "medium" -border-radius = "0" -border-right = "0" -border-right-color = "inherit" -border-right-style = "none" -border-right-width = "medium" -border-spacing = "0" -border-top = "0" -border-top-color = "inherit" -border-top-left-radius = "0" -border-top-right-radius = "0" -border-top-style = "none" -border-top-width = "medium" -bottom = "auto" -box-shadow = "none" -box-sizing = "content-box" -caption-side = "top" -clear = "none" -clip = "auto" -color = "inherit" -columns = "auto" -column-count = "auto" -column-fill = "balance" -column-gap = "normal" -column-rule = "medium none currentColor" -column-rule-color = "currentColor" -column-rule-style = "none" -column-rule-width = "none" -column-span = "1" -column-width = "auto" -content = "normal" -counter-increment = "none" -counter-reset = "none" -cursor = "auto" -direction = "ltr" -display = "inline" -empty-cells = "show" -float = "none" -font = "normal" -font-family = "inherit" -font-size = "medium" -font-style = "normal" -font-variant = "normal" -font-weight = "normal" -height = "auto" -hyphens = "none" -left = "auto" -letter-spacing = "normal" -line-height = "normal" -list-style = "none" -list-style-image = "none" -list-style-position = "outside" -list-style-type = "disc" -margin = "0" -margin-bottom = "0" -margin-left = "0" -margin-right = "0" -margin-top = "0" -max-height = "none" -max-width = "none" -min-height = "0" -min-width = "0" -opacity = "1" -orphans = "0" -outline = "0" -outline-color = "invert" -outline-style = "none" -outline-width = "medium" -overflow = "visible" -overflow-x = "visible" -overflow-y = "visible" -padding = "0" -padding-bottom = "0" -padding-left = "0" -padding-right = "0" -padding-top = "0" -page-break-after = "auto" -page-break-before = "auto" -page-break-inside = "auto" -perspective = "none" -perspective-origin = "50% 50%" -position = "static" -; May need to alter quotes for different locales (e.g fr) -quotes = "'\201C' '\201D' '\2018' '\2019'" -right = "auto" -tab-size = "8" -table-layout = "auto" -text-align = "inherit" -text-align-last = "auto" -text-decoration = "none" -text-decoration-color = "inherit" -text-decoration-line = "none" -text-decoration-style = "solid" -text-indent = "0" -text-shadow = "none" -text-transform = "none" -top = "auto" -transform = "none" -transform-style = "flat" -transition = "none" -transition-delay = "0s" -transition-duration = "0s" -transition-property = "none" -transition-timing-function = "ease" -unicode-bidi = "normal" -vertical-align = "baseline" -visibility = "visible" -white-space = "normal" -widows = "0" -width = "auto" -word-spacing = "normal" -z-index = "auto" diff --git a/package.json b/package.json index a06d045..65f84dd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "csscrush", - "version": "2.4.0", + "version": "3.0.0", "description": "CSS-Crush, CSS preprocessor", "repository": { "type": "git", diff --git a/plugins/hsl2hex.php b/plugins/hsl2hex.php deleted file mode 100644 index af66cc9..0000000 --- a/plugins/hsl2hex.php +++ /dev/null @@ -1,28 +0,0 @@ - function ($process) { - $process->hooks->add('rule_postalias', 'CssCrush\hsl2hex'); - } -)); - - -function hsl2hex(Rule $rule) { - - $hsl_patt = Regex::make('~{{ LB }}hsl({{ parens }})~i'); - - foreach ($rule->declarations->filter(array('skip' => false)) as $declaration) { - if (isset($declaration->functions['hsl'])) { - $declaration->value = preg_replace_callback($hsl_patt, function ($m) { - $color = new Color($m[0]); - return $color->getHex(); - }, $declaration->value); - } - } -} diff --git a/plugins/ie-inline-block.php b/plugins/ie-inline-block.php deleted file mode 100644 index c59ddf4..0000000 --- a/plugins/ie-inline-block.php +++ /dev/null @@ -1,36 +0,0 @@ - function ($process) { - $process->hooks->add('rule_postalias', 'CssCrush\ie_inline_block'); - } -)); - - -function ie_inline_block(Rule $rule) { - - if ($rule->declarations->propertyCount('display') < 1) { - return; - } - - $stack = array(); - foreach ($rule->declarations as $declaration) { - $stack[] = $declaration; - $is_display = $declaration->property === 'display'; - if ( - $declaration->skip || - ! $is_display || - $is_display && $declaration->value !== 'inline-block') { - continue; - } - $stack[] = new Declaration('*display', 'inline'); - $stack[] = new Declaration('*zoom', 1); - } - $rule->declarations->reset($stack); -} diff --git a/plugins/ie-opacity.php b/plugins/ie-opacity.php deleted file mode 100755 index b3ea52f..0000000 --- a/plugins/ie-opacity.php +++ /dev/null @@ -1,44 +0,0 @@ - function ($process) { - $process->hooks->add('rule_postalias', 'CssCrush\ie_opacity'); - } -)); - - -function ie_opacity(Rule $rule) { - - if ($rule->declarations->propertyCount('opacity') < 1) { - return; - } - - $new_set = array(); - foreach ($rule->declarations as $declaration) { - $new_set[] = $declaration; - if ( - $declaration->skip || - $declaration->property != 'opacity' - ) { - continue; - } - - $opacity = (float) $declaration->value; - $opacity = round($opacity * 100); - - if (! $rule->declarations->propertyCount('zoom')) { - // Filters need hasLayout - $new_set[] = new Declaration('zoom', 1); - } - $value = "alpha(opacity=$opacity)"; - $new_set[] = new Declaration('-ms-filter', "\"$value\""); - $new_set[] = new Declaration('*filter', $value); - } - $rule->declarations->reset($new_set); -} diff --git a/plugins/initial.php b/plugins/initial.php deleted file mode 100644 index 837c531..0000000 --- a/plugins/initial.php +++ /dev/null @@ -1,32 +0,0 @@ - function ($process) { - $process->hooks->add('rule_prealias', 'CssCrush\initial'); - } -)); - -function initial(Rule $rule) { - - static $initial_values; - if (! $initial_values) { - if (! ($initial_values = Util::parseIni(Crush::$dir . '/misc/initial-values.ini'))) { - return; - } - } - - foreach ($rule->declarations->filter(array('skip' => false, 'value|lower' => 'initial')) as $declaration) { - if (isset($initial_values[$declaration->property])) { - $declaration->value = $initial_values[ $declaration->property ]; - } - else { - $declaration->value = 'inherit'; - } - } -} diff --git a/plugins/loop.php b/plugins/loop.php index 4043ab8..15d74ac 100644 --- a/plugins/loop.php +++ b/plugins/loop.php @@ -12,13 +12,11 @@ } )); - define('CssCrush\LOOP_VAR_PATT', '~\#\( \s* (?[a-zA-Z][\.a-zA-Z0-9-_]*) \s* \)~x'); define('CssCrush\LOOP_PATT', Regex::make('~(? @for \s+ (?{{ident}}) \s+ in \s+ (?[^{]+) ) \s* {{block}}~xiS')); - function loop($process) { $process->string->pregReplaceCallback(LOOP_PATT, function ($m) { @@ -58,7 +56,7 @@ function loop_resolve_list($list_text) { $items = array(); $list_text = Crush::$process->functions->apply($list_text); - $generator_func_patt = Regex::make('~(?range|color-range) {{parens}}~ix'); + $generator_func_patt = Regex::make('~(?range){{ parens }}~ix'); if (preg_match($generator_func_patt, $list_text, $m)) { $func = strtolower($m['func']); @@ -67,11 +65,6 @@ function loop_resolve_list($list_text) { case 'range': $items = call_user_func_array('range', $args); break; - default: - $func = str_replace('-', '_', $func); - if (function_exists("CssCrush\loop_$func")) { - $items = call_user_func_array("CssCrush\loop_$func", $args); - } } } else { @@ -105,43 +98,3 @@ function loop_apply_scope($str, $context) { return str_replace(array_keys($child_scopes), array_values($child_scopes), $str); } - -function loop_color_range() { - - $args = func_get_args(); - - $source_colors = array(); - while ($args && $color = Color::parse($args[0])) { - $source_colors[] = $color; - array_shift($args); - } - - $steps = max(1, isset($args[0]) ? (int) $args[0] : 1); - - $generated_colors = array(); - foreach ($source_colors as $index => $source_color) { - - $generated_colors[] = new Color($source_color); - - // Generate the in-between colors. - $next_source_color = isset($source_colors[$index + 1]) ? $source_colors[$index + 1] : null; - if (! $next_source_color) { - break; - } - for ($i = 0; $i < $steps; $i++) { - $rgba = array(); - foreach ($source_color as $component_index => $component_value) { - if ($component_diff = $next_source_color[$component_index] - $component_value) { - $component_step = $component_diff / ($steps+1); - $rgba[] = min(round($component_value + ($component_step * ($i+1)), 2), 255); - } - else { - $rgba[] = $component_value; - } - } - $generated_colors[] = new Color($rgba); - } - } - - return $generated_colors; -} diff --git a/plugins/svg-gradients.php b/plugins/svg-gradients.php deleted file mode 100644 index 5fc632d..0000000 --- a/plugins/svg-gradients.php +++ /dev/null @@ -1,329 +0,0 @@ - function () { - $GLOBALS['CSSCRUSH_SVG_GRADIENT_UID'] = 0; - }, - 'enable' => function ($process) { - $GLOBALS['CSSCRUSH_SVG_GRADIENT_UID'] = 0; - $process->functions->add('svg-linear-gradient', 'CssCrush\fn__svg_linear_gradient'); - $process->functions->add('svg-radial-gradient', 'CssCrush\fn__svg_radial_gradient'); - } -)); - - -function fn__svg_linear_gradient($input) { - - $gradient = create_svg_linear_gradient($input); - $gradient_markup = reset($gradient); - $gradient_id = key($gradient); - - $svg = ''; - $svg .= ''; - $svg .= $gradient_markup; - $svg .= ''; - $svg .= ""; - $svg .= ''; - - return Crush::$process->tokens->add(new Url('data:image/svg+xml;base64,' . base64_encode($svg))); -} - - -function fn__svg_radial_gradient($input) { - - $gradient = create_svg_radial_gradient($input); - $gradient_markup = reset($gradient); - $gradient_id = key($gradient); - - $svg = ''; - $svg .= ''; - $svg .= $gradient_markup; - $svg .= ''; - $svg .= ""; - $svg .= ''; - - return Crush::$process->tokens->add(new Url('data:image/svg+xml;base64,' . base64_encode($svg))); -} - - -function create_svg_linear_gradient($input) { - - static $angle_keywords, $deg_patt; - if (! $angle_keywords) { - $angle_keywords = array( - 'to top' => 180, - 'to right' => 270, - 'to bottom' => 0, - 'to left' => 90, - // Not very magic corners. - 'to top right' => array(array(0, 100), array(100, 0)), - 'to top left' => array(array(100, 100), array(0, 0)), - 'to bottom right' => array(array(0, 0), array(100, 100)), - 'to bottom left' => array(array(100, 0), array(0, 100)), - ); - $angle_keywords['to right top'] = $angle_keywords['to top right']; - $angle_keywords['to left top'] = $angle_keywords['to top left']; - $angle_keywords['to right bottom'] = $angle_keywords['to bottom right']; - $angle_keywords['to left bottom'] = $angle_keywords['to bottom left']; - - $deg_patt = Regex::make('~^{{number}}deg$~i'); - } - - $args = Functions::parseArgs($input); - - // If no angle argument is passed the default. - $angle = 0; - - // Parse starting and ending coordinates from the first argument if it's an angle. - $coords = null; - $first_arg = $args[0]; - $first_arg_is_angle = false; - - // Try to parse an angle value. - if (preg_match($deg_patt, $first_arg)) { - $angle = floatval($first_arg); - - // Quick fix to match standard linear-gradient() angle. - $angle += 180; - $first_arg_is_angle = true; - } - elseif (isset($angle_keywords[$first_arg])) { - if (is_array($angle_keywords[$first_arg])) { - $coords = $angle_keywords[$first_arg]; - } - else { - $angle = $angle_keywords[$first_arg]; - } - $first_arg_is_angle = true; - } - - // Shift off the first argument if it has been recognised as an angle. - if ($first_arg_is_angle) { - array_shift($args); - } - - // If not using a magic corner, create start/end coordinates from the angle. - if (! $coords) { - - // Normalize the angle. - $angle = fmod($angle, 360); - if ($angle < 0) { - $angle = 360 + $angle; - } - $angle = round($angle, 2); - - $start_x = 0; - $end_x = 0; - $start_y = 0; - $end_y = 100; - - if ($angle >= 0 && $angle <= 45) { - $start_x = (($angle / 45) * 50) + 50; - $end_x = 100 - $start_x; - $start_y = 0; - $end_y = 100; - } - elseif ($angle > 45 && $angle <= 135) { - $angle_delta = $angle - 45; - $start_x = 100; - $end_x = 0; - $start_y = ($angle_delta / 90) * 100; - $end_y = 100 - $start_y; - } - elseif ($angle > 135 && $angle <= 225) { - $angle_delta = $angle - 135; - $start_x = 100 - (($angle_delta / 90) * 100); - $end_x = 100 - $start_x; - $start_y = 100; - $end_y = 0; - } - elseif ($angle > 225 && $angle <= 315) { - $angle_delta = $angle - 225; - $start_x = 0; - $end_x = 100; - $start_y = 100 - (($angle_delta / 90) * 100); - $end_y = 100 - $start_y; - } - elseif ($angle > 315 && $angle <= 360) { - $angle_delta = $angle - 315; - $start_x = ($angle_delta / 90) * 100; - $end_x = 100 - $start_x; - $start_y = 0; - $end_y = 100; - } - $coords = array( - array(round($start_x, 1), round($start_y, 1)), - array(round($end_x, 1), round($end_y, 1)), - ); - } - - // The remaining arguments are treated as color stops. - // - Capture their color values and if specified color offset percentages. - // - Only percentages are supported as SVG gradients to accept other length values - // for color stop offsets. - $color_stops = parse_gradient_color_stops($args); - - // Create the gradient markup with a unique id. - $uid = ++$GLOBALS['CSSCRUSH_SVG_GRADIENT_UID']; - $gradient_id = "lg$uid"; - $gradient = ""; - $gradient .= $color_stops; - $gradient .= ''; - - return array($gradient_id => $gradient); -} - - -function create_svg_radial_gradient($input) { - - static $position_keywords, $origin_patt; - if (! $position_keywords) { - $position_keywords = array( - 'at top' => array('50%', '0%'), - 'at right' => array('100%', '50%'), - 'at bottom' => array('50%', '100%'), - 'at left' => array('0%', '50%'), - 'at center' => array('50%', '50%'), - // Not very magic corners. - 'at top right' => array('100%', '0%'), - 'at top left' => array('0%', '0%'), - 'at bottom right' => array('100%', '100%'), - 'at bottom left' => array('0%', '100%'), - ); - $position_keywords['at right top'] = $position_keywords['at top right']; - $position_keywords['at left top'] = $position_keywords['at top left']; - $position_keywords['at right bottom'] = $position_keywords['at bottom right']; - $position_keywords['at left bottom'] = $position_keywords['at bottom left']; - - $origin_patt = Regex::make('~^({{number}}%?) +({{number}}%?)$~'); - } - - $args = Functions::parseArgs($input); - - // Default origin, - $position = $position_keywords['at center']; - - // Parse origin coordinates from the first argument if it's an origin. - $first_arg = $args[0]; - $first_arg_is_position = false; - - // Try to parse an origin value. - if (preg_match($origin_patt, $first_arg, $m)) { - $position = array($m[1], $m[2]); - $first_arg_is_position = true; - } - elseif (isset($position_keywords[$first_arg])) { - $position = $position_keywords[$first_arg]; - $first_arg_is_position = true; - } - - // Shift off the first argument if it has been recognised as an origin. - if ($first_arg_is_position) { - array_shift($args); - } - - // The remaining arguments are treated as color stops. - // - Capture their color values and if specified color offset percentages. - // - Only percentages are supported as SVG gradients to accept other length values - // for color stop offsets. - $color_stops = parse_gradient_color_stops($args); - - // Create the gradient markup with a unique id. - $uid = ++$GLOBALS['CSSCRUSH_SVG_GRADIENT_UID']; - $gradient_id = "rg$uid"; - $gradient = ""; - $gradient .= $color_stops; - $gradient .= ''; - - return array($gradient_id => $gradient); -} - - -function parse_gradient_color_stops(array $color_stop_args) { - - $offsets = array(); - $colors = array(); - $offset_patt = '~ +([\d\.]+%)$~'; - $last_index = count($color_stop_args) - 1; - - foreach ($color_stop_args as $index => $color_arg) { - - if (preg_match($offset_patt, $color_arg, $m)) { - $offsets[] = floatval($m[1]); - $color = preg_replace($offset_patt, '', $color_arg); - } - else { - if ($index === 0) { - $offsets[] = 0; - } - elseif ($index === $last_index) { - $offsets[] = 100; - } - else { - $offsets[] = null; - } - $color = $color_arg; - } - - // For hsla()/rgba() extract alpha component from color values and - // convert to hsl()/rgb(). - // Webkit doesn't support them for SVG colors. - $colors[] = Color::colorSplit($color); - } - - // For unspecified color offsets fill in the blanks. - $next_index_not_null = 0; - $prev_index_not_null = 0; - $n = count($offsets); - - foreach ($offsets as $index => $offset) { - - if (! isset($offset)) { - - // Scan for next non-null offset. - for ($i = $index; $i < $n; $i++) { - if (isset($offsets[$i])) { - $next_index_not_null = $i; - break; - } - } - - // Get the difference between previous 'not null' offset and the next 'not null' offset. - // Divide by the number of null offsets to get a value for padding between them. - $padding_increment = - ($offsets[$next_index_not_null] - $offsets[$prev_index_not_null]) / - ($next_index_not_null - $index + 1); - $padding = $padding_increment; - - for ($i = $index; $i < $n; $i++) { - if (isset($offsets[$i])) { - break; - } - // Replace the null offset with the new padded value. - $offsets[$i] = $offsets[$prev_index_not_null] + $padding; - // Bump the padding for the next null offset. - $padding += $padding_increment; - } - } - else { - $prev_index_not_null = $index; - } - } - - $stops = ''; - foreach (array_combine($offsets, $colors) as $offset => $color) { - list($color_value, $opacity) = $color; - $stop_opacity = $opacity < 1 ? " stop-opacity=\"$opacity\"" : ''; - $stops .= ""; - } - - return $stops; -} diff --git a/plugins/svg.php b/plugins/svg.php index 4561fd6..f505531 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -9,16 +9,18 @@ Plugin::register('svg', array( 'load' => function () { $GLOBALS['CSSCRUSH_SVG_UID'] = 0; + $GLOBALS['CSSCRUSH_SVG_GRADIENT_UID'] = 0; }, 'enable' => function ($process) { $GLOBALS['CSSCRUSH_SVG_UID'] = 0; $process->hooks->add('capture_phase2', 'CssCrush\svg_capture'); $process->functions->add('svg', 'CssCrush\fn__svg'); $process->functions->add('svg-data', 'CssCrush\fn__svg_data'); + $process->functions->add('svg-linear-gradient', 'CssCrush\fn__svg_linear_gradient'); + $process->functions->add('svg-radial-gradient', 'CssCrush\fn__svg_radial_gradient'); } )); - function fn__svg($input) { return svg_generator($input, 'svg'); @@ -735,9 +737,6 @@ function svg_render($element) { */ function svg_fn_linear_gradient($input, $element) { - // Relies on functions from svg-gradients plugin. - Plugin::load('svg-gradients'); - $generated_gradient = create_svg_linear_gradient($input); $element->fills['gradients'][] = reset($generated_gradient); @@ -746,9 +745,6 @@ function svg_fn_linear_gradient($input, $element) { function svg_fn_radial_gradient($input, $element) { - // Relies on functions from svg-gradients plugin. - Plugin::load('svg-gradients'); - $generated_gradient = create_svg_radial_gradient($input); $element->fills['gradients'][] = reset($generated_gradient); @@ -806,3 +802,317 @@ function svg_ifset(&$var, $fallback = null) { } return $fallback; } + + +/* + SVG gradients. +*/ +function fn__svg_linear_gradient($input) { + + $gradient = create_svg_linear_gradient($input); + $gradient_markup = reset($gradient); + $gradient_id = key($gradient); + + $svg = ''; + $svg .= ''; + $svg .= $gradient_markup; + $svg .= ''; + $svg .= ""; + $svg .= ''; + + return Crush::$process->tokens->add(new Url('data:image/svg+xml;base64,' . base64_encode($svg))); +} + + +function fn__svg_radial_gradient($input) { + + $gradient = create_svg_radial_gradient($input); + $gradient_markup = reset($gradient); + $gradient_id = key($gradient); + + $svg = ''; + $svg .= ''; + $svg .= $gradient_markup; + $svg .= ''; + $svg .= ""; + $svg .= ''; + + return Crush::$process->tokens->add(new Url('data:image/svg+xml;base64,' . base64_encode($svg))); +} + + +function create_svg_linear_gradient($input) { + + static $angle_keywords, $deg_patt; + if (! $angle_keywords) { + $angle_keywords = array( + 'to top' => 180, + 'to right' => 270, + 'to bottom' => 0, + 'to left' => 90, + // Not very magic corners. + 'to top right' => array(array(0, 100), array(100, 0)), + 'to top left' => array(array(100, 100), array(0, 0)), + 'to bottom right' => array(array(0, 0), array(100, 100)), + 'to bottom left' => array(array(100, 0), array(0, 100)), + ); + $angle_keywords['to right top'] = $angle_keywords['to top right']; + $angle_keywords['to left top'] = $angle_keywords['to top left']; + $angle_keywords['to right bottom'] = $angle_keywords['to bottom right']; + $angle_keywords['to left bottom'] = $angle_keywords['to bottom left']; + + $deg_patt = Regex::make('~^{{number}}deg$~i'); + } + + $args = Functions::parseArgs($input); + + // If no angle argument is passed the default. + $angle = 0; + + // Parse starting and ending coordinates from the first argument if it's an angle. + $coords = null; + $first_arg = $args[0]; + $first_arg_is_angle = false; + + // Try to parse an angle value. + if (preg_match($deg_patt, $first_arg)) { + $angle = floatval($first_arg); + + // Quick fix to match standard linear-gradient() angle. + $angle += 180; + $first_arg_is_angle = true; + } + elseif (isset($angle_keywords[$first_arg])) { + if (is_array($angle_keywords[$first_arg])) { + $coords = $angle_keywords[$first_arg]; + } + else { + $angle = $angle_keywords[$first_arg]; + } + $first_arg_is_angle = true; + } + + // Shift off the first argument if it has been recognised as an angle. + if ($first_arg_is_angle) { + array_shift($args); + } + + // If not using a magic corner, create start/end coordinates from the angle. + if (! $coords) { + + // Normalize the angle. + $angle = fmod($angle, 360); + if ($angle < 0) { + $angle = 360 + $angle; + } + $angle = round($angle, 2); + + $start_x = 0; + $end_x = 0; + $start_y = 0; + $end_y = 100; + + if ($angle >= 0 && $angle <= 45) { + $start_x = (($angle / 45) * 50) + 50; + $end_x = 100 - $start_x; + $start_y = 0; + $end_y = 100; + } + elseif ($angle > 45 && $angle <= 135) { + $angle_delta = $angle - 45; + $start_x = 100; + $end_x = 0; + $start_y = ($angle_delta / 90) * 100; + $end_y = 100 - $start_y; + } + elseif ($angle > 135 && $angle <= 225) { + $angle_delta = $angle - 135; + $start_x = 100 - (($angle_delta / 90) * 100); + $end_x = 100 - $start_x; + $start_y = 100; + $end_y = 0; + } + elseif ($angle > 225 && $angle <= 315) { + $angle_delta = $angle - 225; + $start_x = 0; + $end_x = 100; + $start_y = 100 - (($angle_delta / 90) * 100); + $end_y = 100 - $start_y; + } + elseif ($angle > 315 && $angle <= 360) { + $angle_delta = $angle - 315; + $start_x = ($angle_delta / 90) * 100; + $end_x = 100 - $start_x; + $start_y = 0; + $end_y = 100; + } + $coords = array( + array(round($start_x, 1), round($start_y, 1)), + array(round($end_x, 1), round($end_y, 1)), + ); + } + + // The remaining arguments are treated as color stops. + // - Capture their color values and if specified color offset percentages. + // - Only percentages are supported as SVG gradients to accept other length values + // for color stop offsets. + $color_stops = parse_gradient_color_stops($args); + + // Create the gradient markup with a unique id. + $uid = ++$GLOBALS['CSSCRUSH_SVG_GRADIENT_UID']; + $gradient_id = "lg$uid"; + $gradient = ""; + $gradient .= $color_stops; + $gradient .= ''; + + return array($gradient_id => $gradient); +} + + +function create_svg_radial_gradient($input) { + + static $position_keywords, $origin_patt; + if (! $position_keywords) { + $position_keywords = array( + 'at top' => array('50%', '0%'), + 'at right' => array('100%', '50%'), + 'at bottom' => array('50%', '100%'), + 'at left' => array('0%', '50%'), + 'at center' => array('50%', '50%'), + // Not very magic corners. + 'at top right' => array('100%', '0%'), + 'at top left' => array('0%', '0%'), + 'at bottom right' => array('100%', '100%'), + 'at bottom left' => array('0%', '100%'), + ); + $position_keywords['at right top'] = $position_keywords['at top right']; + $position_keywords['at left top'] = $position_keywords['at top left']; + $position_keywords['at right bottom'] = $position_keywords['at bottom right']; + $position_keywords['at left bottom'] = $position_keywords['at bottom left']; + + $origin_patt = Regex::make('~^({{number}}%?) +({{number}}%?)$~'); + } + + $args = Functions::parseArgs($input); + + // Default origin, + $position = $position_keywords['at center']; + + // Parse origin coordinates from the first argument if it's an origin. + $first_arg = $args[0]; + $first_arg_is_position = false; + + // Try to parse an origin value. + if (preg_match($origin_patt, $first_arg, $m)) { + $position = array($m[1], $m[2]); + $first_arg_is_position = true; + } + elseif (isset($position_keywords[$first_arg])) { + $position = $position_keywords[$first_arg]; + $first_arg_is_position = true; + } + + // Shift off the first argument if it has been recognised as an origin. + if ($first_arg_is_position) { + array_shift($args); + } + + // The remaining arguments are treated as color stops. + // - Capture their color values and if specified color offset percentages. + // - Only percentages are supported as SVG gradients to accept other length values + // for color stop offsets. + $color_stops = parse_gradient_color_stops($args); + + // Create the gradient markup with a unique id. + $uid = ++$GLOBALS['CSSCRUSH_SVG_GRADIENT_UID']; + $gradient_id = "rg$uid"; + $gradient = ""; + $gradient .= $color_stops; + $gradient .= ''; + + return array($gradient_id => $gradient); +} + + +function parse_gradient_color_stops(array $color_stop_args) { + + $offsets = array(); + $colors = array(); + $offset_patt = '~ +([\d\.]+%)$~'; + $last_index = count($color_stop_args) - 1; + + foreach ($color_stop_args as $index => $color_arg) { + + if (preg_match($offset_patt, $color_arg, $m)) { + $offsets[] = floatval($m[1]); + $color = preg_replace($offset_patt, '', $color_arg); + } + else { + if ($index === 0) { + $offsets[] = 0; + } + elseif ($index === $last_index) { + $offsets[] = 100; + } + else { + $offsets[] = null; + } + $color = $color_arg; + } + + // For hsla()/rgba() extract alpha component from color values and + // convert to hsl()/rgb(). + // Webkit doesn't support them for SVG colors. + $colors[] = Color::colorSplit($color); + } + + // For unspecified color offsets fill in the blanks. + $next_index_not_null = 0; + $prev_index_not_null = 0; + $n = count($offsets); + + foreach ($offsets as $index => $offset) { + + if (! isset($offset)) { + + // Scan for next non-null offset. + for ($i = $index; $i < $n; $i++) { + if (isset($offsets[$i])) { + $next_index_not_null = $i; + break; + } + } + + // Get the difference between previous 'not null' offset and the next 'not null' offset. + // Divide by the number of null offsets to get a value for padding between them. + $padding_increment = + ($offsets[$next_index_not_null] - $offsets[$prev_index_not_null]) / + ($next_index_not_null - $index + 1); + $padding = $padding_increment; + + for ($i = $index; $i < $n; $i++) { + if (isset($offsets[$i])) { + break; + } + // Replace the null offset with the new padded value. + $offsets[$i] = $offsets[$prev_index_not_null] + $padding; + // Bump the padding for the next null offset. + $padding += $padding_increment; + } + } + else { + $prev_index_not_null = $index; + } + } + + $stops = ''; + foreach (array_combine($offsets, $colors) as $offset => $color) { + list($color_value, $opacity) = $color; + $stop_opacity = $opacity < 1 ? " stop-opacity=\"$opacity\"" : ''; + $stops .= ""; + } + + return $stops; +} diff --git a/plugins/text-align.php b/plugins/text-align.php deleted file mode 100644 index 4d11474..0000000 --- a/plugins/text-align.php +++ /dev/null @@ -1,49 +0,0 @@ - function ($process) { - $process->hooks->add('rule_prealias', 'CssCrush\text_align'); - } -)); - - -function text_align(Rule $rule) { - - static $text_align_properties = array('text-align' => true, 'text-align-last' => true); - static $text_align_special_values = array('start' => true, 'end' => true); - - if (! array_intersect_key($rule->declarations->properties, $text_align_properties)) { - return; - } - - $dir = Crush::$process->settings->get('dir', 'ltr'); - - $stack = array(); - foreach ($rule->declarations as $declaration) { - $value = strtolower($declaration->value); - if ( - ! $declaration->skip && - isset($text_align_properties[$declaration->property]) && - isset($text_align_special_values[$value]) - ) { - $fallback_declaration = clone $declaration; - if ($value == 'start') { - $fallback_declaration->value = $dir == 'ltr' ? 'left' : 'right'; - } - elseif ($value == 'end') { - $fallback_declaration->value = $dir == 'ltr' ? 'right' : 'left'; - } - $stack[] = $fallback_declaration; - } - - $stack[] = $declaration; - } - - $rule->declarations->reset($stack); -} From 0d79d893a9da586410ddb1d3f1049703c911ba35 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 1 Sep 2017 10:20:39 +0100 Subject: [PATCH 346/421] Removed noise plugin. Made post-process restoration for original ini settings. --- .gitignore | 2 + docs/plugins/noise.md | 61 ---------------- lib/CssCrush/Process.php | 18 +++-- package.json | 9 ++- plugins/noise.php | 150 --------------------------------------- plugins/svg.php | 15 +--- 6 files changed, 23 insertions(+), 232 deletions(-) delete mode 100644 docs/plugins/noise.md delete mode 100644 plugins/noise.php diff --git a/.gitignore b/.gitignore index 908cb6a..81853d7 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ _* phpunit.xml composer.lock vendor +node_modules +package-lock.json diff --git a/docs/plugins/noise.md b/docs/plugins/noise.md deleted file mode 100644 index 177d896..0000000 --- a/docs/plugins/noise.md +++ /dev/null @@ -1,61 +0,0 @@ - -Functions for generating noise textures with SVG filters. - -Supported in any browser that supports SVG filters (IE > 9 and most other browsers). - - -## noise() / turbulence() - -Both functions work in the same way, the difference being `noise()` uses feTurbulence type 'fractalNoise' and `turbulence()` uses feTurbulence type 'turbulence'. - -```syntax -noise/turbulence( - [ || ]? - [, ? ? ]? - [, || ]? - [, ]? -) -``` - -### Parameters - -* **fill-color** - Any valid CSS color value. -* **size** - Pixel size of canvas in format WxH (e.g. 320x480). -* **frequency** - Number. Noise frequency; useful values are between 0 and 1. X and Y frequencies can be specified by joining two numbers with a colon. -* **octaves** - Number. Noise complexity. -* **sharpness** - Noise sharpening; possible values "normal" and "sharpen" -* **blend-mode** - Blend mode for overlaying noise filter; possible values "normal", "multiply", "screen", "darken" and "lighten" -* **fade** - Ranged number (0-1). Opacity of noise effect. -* **color-filter** - Color filter type; possible values "hueRotate" and "saturate" -* **color-filter-value** - Mixed. For "hueRotate" a degree as number. For "saturate" a ranged number (0-1). - -### Returns - -A data-uri. - -```css -/* Grainy noise with 50% opacity and de-saturated. - Demonstrates the "default" keyword for skipping arguments. */ -background-image: noise( slategray, default, .5, saturate 0 ); -``` - -******* - -```css -/* Cloud effect. */ -background: noise( 700x700 skyblue, .01 4 normal, screen, saturate 0 ); -``` - -******* - -```css -/* Typical turbulence effect. */ -background: turbulence(); -``` - -******* - -```css -/* Sand effect. */ -background: turbulence( wheat 400x400, .35:.2 4 sharpen, normal, saturate .4 ); -``` diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 3f23409..082a11e 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -837,14 +837,16 @@ protected function collate() } } + private $iniOriginal = []; public function preCompile() { - // Ensure relevant ini settings aren't too conservative. - if (ini_get('pcre.backtrack_limit') < 1000000) { - ini_set('pcre.backtrack_limit', 1000000); - } - if (preg_match('~^(\d+)M$~', ini_get('memory_limit'), $m) && $m[1] < 128) { - ini_set('memory_limit', '128M'); + foreach ([ + 'pcre.backtrack_limit' => 1000000, + 'pcre.jit' => 0, // Have run into PREG_JIT_STACKLIMIT_ERROR (issue #82). + 'memory_limit' => '128M', + ] as $name => $value) { + $this->iniOriginal[$name] = ini_get($name); + ini_set($name, $value); } $this->filterPlugins(); @@ -864,6 +866,10 @@ public function postCompile() $this->release(); Crush::runStat('compile_time'); + + foreach ($this->iniOriginal as $name => $value) { + ini_set($name, $value); + } } public function compile() diff --git a/package.json b/package.json index 65f84dd..6dede45 100644 --- a/package.json +++ b/package.json @@ -9,9 +9,12 @@ "bugs": { "url": "https://github.com/peteboere/css-crush/issues" }, - "bin" : { - "csscrush" : "./bin/csscrush" + "bin": { + "csscrush": "./bin/csscrush" }, "homepage": "http://the-echoplex.net/csscrush", - "license": "MIT" + "license": "MIT", + "devDependencies": { + "eslint": "~4.5.0" + } } diff --git a/plugins/noise.php b/plugins/noise.php deleted file mode 100644 index ac14bec..0000000 --- a/plugins/noise.php +++ /dev/null @@ -1,150 +0,0 @@ - function ($process) { - $process->functions->add('noise', function ($input) { - return noise_generator($input, array( - 'type' => 'fractalNoise', - 'frequency' => .7, - 'sharpen' => 'sharpen', - 'dimensions' => array(150, 150), - )); - }); - $process->functions->add('turbulence', function ($input) { - return noise_generator($input, array( - 'type' => 'turbulence', - 'frequency' => .01, - 'sharpen' => 'normal', - 'dimensions' => array(200, 200), - )); - }); - } -)); - - -function noise_generator($input, $defaults) { - - $args = array_pad(Functions::parseArgs($input), 4, 'default'); - - $type = $defaults['type']; - - // Color-fill and dimensions. - $fill_color = 'transparent'; - $dimensions = $defaults['dimensions']; - if (($arg = array_shift($args)) !== 'default') { - // May be a color function so explode(' ', $value) is not sufficient. - foreach (Functions::parseArgs($arg, true) as $part) { - if (preg_match('~^(\d+)x(\d+)$~i', $part, $m)) { - $dimensions = array_slice($m, 1); - } - else { - $fill_color = $part; - } - } - } - - // Frequency, octaves and sharpening. - static $sharpen_modes = array('normal', 'sharpen'); - $frequency = $defaults['frequency']; - $octaves = 1; - $sharpen = $defaults['sharpen']; - - if (($arg = array_shift($args)) !== 'default') { - foreach (explode(' ', $arg) as $index => $value) { - switch ($index) { - case 0: - // x and y frequency values can be specified by joining with a colon. - $frequency = str_replace(':', ',', $value); - break; - case 1: - case 2: - if (preg_match(Regex::$patt->rooted_number, $value)) { - $octaves = $value; - } - elseif (in_array($value, $sharpen_modes)) { - $sharpen = $value; - } - } - } - } - - // Blend-mode and fade. - static $blend_modes = array('normal', 'multiply', 'screen', 'darken', 'lighten'); - $blend_mode = 'normal'; - $opacity = 1; - if (($arg = array_shift($args)) !== 'default') { - foreach (explode(' ', $arg) as $part) { - if (ctype_alpha($part)) { - if (in_array($part, $blend_modes)) { - $blend_mode = $part; - } - } - else { - $opacity = $part; - } - } - } - - // Color filter. - static $color_filters = array('saturate', 'hueRotate', 'luminanceToAlpha'); - $color_filter = null; - if (($arg = array_shift($args)) !== 'default') { - // Saturate by default. - $color_filter = array('saturate', 1); - foreach (explode(' ', $arg) as $part) { - if (ctype_alpha($part)) { - if (in_array($part, $color_filters)) { - $color_filter[0] = $part; - } - } - else { - $color_filter[1] = $part; - } - } - } - - // Creating the svg. - $svg = ""; - $svg .= ''; - $svg .= ""; - - $svg .= ""; - - $component_adjustments = array(); - if ($sharpen === 'sharpen') { - // It's more posterizing than sharpening, but it hits a sweet spot. - $component_adjustments[] = ""; - $component_adjustments[] = ""; - // Some unpredictable results with this: - // $component_adjustments[] = ""; - } - if ($opacity != '1') { - $component_adjustments[] = ""; - } - if ($component_adjustments) { - $svg .= ""; - $svg .= implode('', $component_adjustments); - $svg .= ""; - } - - if ($color_filter) { - $svg .= ""; - } - if ($blend_mode !== 'normal') { - $svg .= ""; - } - $svg .= ''; - $svg .= ''; - $svg .= ""; - $svg .= ""; - $svg .= ''; - - return Crush::$process->tokens->add(new Url('data:image/svg+xml;base64,' . base64_encode($svg))); -} diff --git a/plugins/svg.php b/plugins/svg.php index f505531..3d1472e 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -7,12 +7,7 @@ namespace CssCrush; Plugin::register('svg', array( - 'load' => function () { - $GLOBALS['CSSCRUSH_SVG_UID'] = 0; - $GLOBALS['CSSCRUSH_SVG_GRADIENT_UID'] = 0; - }, 'enable' => function ($process) { - $GLOBALS['CSSCRUSH_SVG_UID'] = 0; $process->hooks->add('capture_phase2', 'CssCrush\svg_capture'); $process->functions->add('svg', 'CssCrush\fn__svg'); $process->functions->add('svg-data', 'CssCrush\fn__svg_data'); @@ -753,7 +748,7 @@ function svg_fn_radial_gradient($input, $element) { function svg_fn_pattern($input, $element) { - $pid = 'p' . (++$GLOBALS['CSSCRUSH_SVG_UID']); + $pid = 'p'; // Get args in order with defaults. list($url, $transform_list, $width, $height, $x, $y) = @@ -958,9 +953,7 @@ function create_svg_linear_gradient($input) { // for color stop offsets. $color_stops = parse_gradient_color_stops($args); - // Create the gradient markup with a unique id. - $uid = ++$GLOBALS['CSSCRUSH_SVG_GRADIENT_UID']; - $gradient_id = "lg$uid"; + $gradient_id = "lg"; $gradient = ""; $gradient .= $color_stops; @@ -1024,9 +1017,7 @@ function create_svg_radial_gradient($input) { // for color stop offsets. $color_stops = parse_gradient_color_stops($args); - // Create the gradient markup with a unique id. - $uid = ++$GLOBALS['CSSCRUSH_SVG_GRADIENT_UID']; - $gradient_id = "rg$uid"; + $gradient_id = "rg"; $gradient = ""; $gradient .= $color_stops; From a6e471cb54de23112fae26676e3302b885c23b0e Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 1 Sep 2017 14:17:21 +0100 Subject: [PATCH 347/421] Added import path option. --- cli.php | 15 +++++++++---- lib/CssCrush/Crush.php | 4 ++-- lib/CssCrush/IO.php | 2 +- lib/CssCrush/IO/Watch.php | 4 ++-- lib/CssCrush/Importer.php | 34 ++++++++++++++++------------- lib/CssCrush/Options.php | 12 ++++++++++ package.json | 3 ++- tests/unit/CssCrush/VersionTest.php | 10 ++++++++- 8 files changed, 58 insertions(+), 26 deletions(-) diff --git a/cli.php b/cli.php index d1e69d5..2a6890d 100755 --- a/cli.php +++ b/cli.php @@ -100,7 +100,8 @@ $options['minify'] = false; } -foreach (array('boilerplate', 'formatter', 'newlines', 'stat_dump', 'source_map') as $option) { +foreach (['boilerplate', 'formatter', 'newlines', + 'stat_dump', 'source_map', 'import_path'] as $option) { if ($args->$option) { $options[$option] = $args->$option; } @@ -426,6 +427,7 @@ function parse_args() { 'formatter', 'vendor-target', 'context', + 'import-path', 'newlines', ); @@ -497,9 +499,10 @@ function parse_args() { // Arguments that require a value but accept multiple values. $args->enable_plugins = pick($opts, 'E', 'enable', 'plugins'); $args->vendor_target = pick($opts, 'vendor-target'); + $args->import_path = pick($opts, 'import-path'); // Run multiple value arguments through array cast. - foreach (array('enable_plugins', 'vendor_target') as $arg) { + foreach (['enable_plugins', 'vendor_target'] as $arg) { if ($args->$arg) { $args->$arg = (array) $args->$arg; } @@ -578,8 +581,12 @@ function manpage() { to a custom boilerplate template. --context - Filepath context for resolving relative URLs. Only meaningful when - taking raw input from STDIN. + Filepath context for resolving relative import URLs. + Only meaningful when taking raw input from STDIN. + + --import-path + Comma separated list of additional paths to search when resolving + relative import URLs. --formatter Possible values: diff --git a/lib/CssCrush/Crush.php b/lib/CssCrush/Crush.php index 37b2ce5..d71d66c 100644 --- a/lib/CssCrush/Crush.php +++ b/lib/CssCrush/Crush.php @@ -236,7 +236,7 @@ function warning($message, $context = array()) { Crush::$process->errors[] = $message; $logger = Crush::$config->logger; if ($logger instanceof Logger) { - $message = "[[CssCrush]] - $message"; + $message = "[CssCrush] $message"; } $logger->warning($message, $context); } @@ -245,7 +245,7 @@ function notice($message, $context = array()) { Crush::$process->warnings[] = $message; $logger = Crush::$config->logger; if ($logger instanceof Logger) { - $message = "[[CssCrush]] - $message"; + $message = "[CssCrush] $message"; } $logger->notice($message, $context); } diff --git a/lib/CssCrush/IO.php b/lib/CssCrush/IO.php index 62ad1f1..1c83d2f 100644 --- a/lib/CssCrush/IO.php +++ b/lib/CssCrush/IO.php @@ -84,7 +84,7 @@ public function validateCache() $path = "$dir/$filename"; if (! file_exists($path)) { - debug('No file cached.'); + debug("File '$path' not cached."); return false; } diff --git a/lib/CssCrush/IO/Watch.php b/lib/CssCrush/IO/Watch.php index 9c93212..bdf87de 100644 --- a/lib/CssCrush/IO/Watch.php +++ b/lib/CssCrush/IO/Watch.php @@ -18,14 +18,14 @@ public function getOutputFileName() $process = $this->process; $options = $process->options; - $output_basename = basename($process->input->filename, '.css'); + $input_basename = $output_basename = basename($process->input->filename, '.css'); if (! empty($options->output_file)) { $output_basename = basename($options->output_file, '.css'); } $suffix = '.crush'; - if ($process->input->dir !== $process->output->dir) { + if (($process->input->dir !== $process->output->dir) || ($input_basename !== $output_basename)) { $suffix = ''; } diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index 0760260..e2a14ee 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -65,27 +65,40 @@ public function collate() } // Resolve import path information. + $import->path = null; if ($import->url->isRooted) { $import->path = realpath($process->docRoot . $import->url->value); } else { - $import->path = realpath("$input->dir/{$import->url->value}"); + $searchPaths = array_merge([$input->dir], $options->import_path ?: []); + foreach ($searchPaths as $searchPath) { + $candidate = "$searchPath/{$import->url->value}"; + if (file_exists($candidate)) { + $import->path = realpath($candidate); + break; + } + } } - $import->dir = dirname($import->path); // If unsuccessful getting import contents continue with the import line removed. - $import->content = @file_get_contents($import->path); + $import->content = $import->path ? @file_get_contents($import->path) : false; if ($import->content === false) { - notice("@import 'http://github.com/%7B$import-%3Eurl-%3Evalue%7D' " . - (! is_readable($import->path) ? 'is not readable' : 'does not exist')); + $errDesc = 'was not found'; + if ($import->path && ! is_readable($import->path)) { + $errDesc = 'is not readable'; + } + notice("@import 'http://github.com/%7B$import-%3Eurl-%3Evalue%7D' $errDesc"); $str = substr_replace($str, '', $match_start, $match_len); continue; } + $import->dir = dirname($import->path); + $import->relativeDir = Util::getLinkBetweenPaths($input->dir, $import->dir); + // Import file exists so register it. $process->sources[] = $import->path; $mtimes[] = filemtime($import->path); - $filenames[] = $import->url->value; + $filenames[] = $import->relativeDir . basename($import->path); // If the import content doesn't pass syntax validation skip to next import. if (! $this->prepareImport($import->content)) { @@ -94,14 +107,6 @@ public function collate() continue; } - // Resolve a relative link between the import file and the host-file. - if ($import->url->isRooted) { - $import->relativeDir = Util::getLinkBetweenPaths($import->dir, $input->dir); - } - else { - $import->relativeDir = dirname($import->url->value); - } - // Alter all embedded import URLs to be relative to the host-file. foreach (Regex::matchAll($regex->import, $import->content) as $m) { @@ -137,7 +142,6 @@ public function collate() 'datem_sum' => array_sum($mtimes) + $input->mtime, 'options' => $options->get(), ); - $process->io->saveCacheData(); } diff --git a/lib/CssCrush/Options.php b/lib/CssCrush/Options.php index 5974195..8083d82 100644 --- a/lib/CssCrush/Options.php +++ b/lib/CssCrush/Options.php @@ -19,6 +19,7 @@ class Options 'vars' => array(), 'cache' => true, 'context' => null, + 'import_path' => null, 'output_file' => null, 'output_dir' => null, 'asset_dir' => null, @@ -106,6 +107,17 @@ public function __set($name, $value) } break; + case 'import_path': + if ($value) { + if (is_string($value)) { + $value = preg_split('~\s*,\s*~', trim($value)); + } + $value = array_filter(array_map(function ($path) { + return Util::normalizePath(realpath($path)); + }, $value)); + } + break; + // Options used internally as arrays. case 'plugins': $value = (array) $value; diff --git a/package.json b/package.json index 6dede45..904eede 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "homepage": "http://the-echoplex.net/csscrush", "license": "MIT", "devDependencies": { - "eslint": "~4.5.0" + "eslint": "~4.5.0", + "normalize.css": "7.0.0" } } diff --git a/tests/unit/CssCrush/VersionTest.php b/tests/unit/CssCrush/VersionTest.php index 4cd2453..d7b5e05 100644 --- a/tests/unit/CssCrush/VersionTest.php +++ b/tests/unit/CssCrush/VersionTest.php @@ -36,7 +36,15 @@ public function testProperties() public function testGitDescribe() { if ($version = Version::gitDescribe()) { - $this->assertRegExp('~^v\d+\.\d+\.\d+-\d+-g.+$~', $version->__toString()); + $this->assertRegExp('~^ + v + \d+\. + \d+\. + \d+ + (-(?:alpha|beta)\.\d+)? + -\d+ + -g.+ + $~x', $version->__toString()); } else { $this->markTestSkipped('Returned null'); From 7db9351e842483981b2f8a8cad09ea61816e6a1a Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 1 Sep 2017 19:49:59 +0100 Subject: [PATCH 348/421] Moved loop plugin into core. --- lib/CssCrush/PostAliasFix.php | 106 +--------------------------------- lib/CssCrush/Process.php | 80 +++++++++++++++++++++++++ plugins/loop.php | 100 -------------------------------- tests/bootstrap.php | 4 +- 4 files changed, 83 insertions(+), 207 deletions(-) delete mode 100644 plugins/loop.php diff --git a/lib/CssCrush/PostAliasFix.php b/lib/CssCrush/PostAliasFix.php index 11de0ed..2b7b61e 100644 --- a/lib/CssCrush/PostAliasFix.php +++ b/lib/CssCrush/PostAliasFix.php @@ -8,9 +8,7 @@ class PostAliasFix { - public static $functions = array( - '.gradients' => 'CssCrush\postalias_fix_gradients', - ); + public static $functions = []; public static function add($alias_type, $key, $callback) { @@ -26,105 +24,3 @@ public static function remove($alias_type, $key) } } } - -/** - * Post alias fix callback for all gradients. - */ -function postalias_fix_gradients($declaration_copies) { - postalias_fix_linear_gradients($declaration_copies); - postalias_fix_radial_gradients($declaration_copies); -} - -/** - * Convert the new angle syntax (keyword and degree) on -x-linear-gradient() functions - * to legacy equivalents. - */ -function postalias_fix_linear_gradients($declaration_copies) { - - static $angles_new, $angles_old; - if (! $angles_new) { - $angles = array( - 'to top' => 'bottom', - 'to right' => 'left', - 'to bottom' => 'top', - 'to left' => 'right', - // 'magic' corners. - 'to top left' => 'bottom right', - 'to left top' => 'bottom right', - 'to top right' => 'bottom left', - 'to right top' => 'bottom left', - 'to bottom left' => 'top right', - 'to left bottom' => 'top right', - 'to bottom right' => 'top left', - 'to right bottom' => 'top left', - ); - $angles_new = array_keys($angles); - $angles_old = array_values($angles); - } - - static $deg_patt, $fn_patt; - if (! $deg_patt) { - $deg_patt = Regex::make('~(?<=[\( ])({{ number }})deg~i'); - $fn_patt = Regex::make('~{{ LB }}{{ vendor }}(?:repeating-)?linear-gradient{{ parens }}~iS'); - } - - // Legacy angles move anti-clockwise and start from East, not North. - $deg_convert_callback = function ($m) { - $angle = floatval($m[1]); - $angle = ($angle + 90) - ($angle * 2); - return ($angle < 0 ? $angle + 360 : $angle) . 'deg'; - }; - - // Create new paren tokens based on the first prefixed declaration. - // Replace the new syntax with the legacy syntax. - $original_parens = array(); - $replacement_parens = array(); - - foreach (Regex::matchAll($fn_patt, $declaration_copies[0]->value) as $m) { - - $original_parens[] = $m['parens'][0]; - - // Keyword angle values. - $updated_paren_value = str_ireplace($angles_new, $angles_old, $m['parens'][0]); - - // Degree angle values. - $replacement_parens[] = preg_replace_callback($deg_patt, $deg_convert_callback, $updated_paren_value); - } - - foreach ($declaration_copies as $prefixed_copy) { - $prefixed_copy->value = str_replace( - $original_parens, - $replacement_parens, - $prefixed_copy->value - ); - } -} - -/** - * Remove the 'at' keyword from -x-radial-gradient() for legacy implementations. - */ -function postalias_fix_radial_gradients($declaration_copies) { - - // Create new paren tokens based on the first prefixed declaration. - // Replace the new syntax with the legacy syntax. - static $fn_patt; - if (! $fn_patt) { - $fn_patt = Regex::make('~{{ LB }}{{ vendor }}(?:repeating-)?radial-gradient{{ parens }}~iS'); - } - - $original_parens = array(); - $replacement_parens = array(); - - foreach (Regex::matchAll($fn_patt, $declaration_copies[0]->value) as $m) { - $original_parens[] = $m['parens'][0]; - $replacement_parens[] = preg_replace('~\bat +(top|left|bottom|right|center)\b~i', '$1', $m['parens'][0]); - } - - foreach ($declaration_copies as $prefixed_copy) { - $prefixed_copy->value = str_replace( - $original_parens, - $replacement_parens, - $prefixed_copy->value - ); - } -} diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 082a11e..c7da6e1 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -451,6 +451,84 @@ protected function resolveSettings() $this->settings = new Settings($this->options->settings + $captured_settings); } + ############################# + # @for..in blocks. + + protected function resolveLoops() + { + $LOOP_VAR_PATT = '~\#\( \s* (?[a-zA-Z][\.a-zA-Z0-9-_]*) \s* \)~x'; + $LOOP_PATT = Regex::make('~ + (? + @for \s+ (?{{ident}}) \s+ in \s+ (?[^{]+) + ) \s* + {{ block }} + ~xiS'); + + $apply_scope = function ($str, $context) use ($LOOP_VAR_PATT, $LOOP_PATT) { + // Need to temporarily hide child block scopes. + $child_scopes = []; + $str = preg_replace_callback($LOOP_PATT, function ($m) use (&$child_scopes) { + $label = '?B' . count($child_scopes) . '?'; + $child_scopes[$label] = $m['block']; + return $m['expression'] . $label; + }, $str); + + $str = preg_replace_callback($LOOP_VAR_PATT, function ($m) use ($context) { + // Normalize casing of built-in loop variables. + // User variables are case-sensitive. + $arg = preg_replace_callback('~^loop\.(parent\.)?counter0?$~i', function ($m) { + return strtolower($m[0]); + }, $m['arg']); + + return isset($context[$arg]) ? $context[$arg] : ''; + }, $str); + + return str_replace(array_keys($child_scopes), array_values($child_scopes), $str); + }; + + $resolve_list = function ($list) { + // Resolve the list of items for iteration. + // Either a generator function or a plain list. + $items = []; + $list = $this->functions->apply($list); + if (preg_match(Regex::make('~(?range){{ parens }}~ix'), $list, $m)) { + $func = strtolower($m['func']); + $args = Functions::parseArgs($m['parens_content']); + switch ($func) { + case 'range': + $items = range(...$args); + break; + } + } + else { + $items = Util::splitDelimList($list); + } + + return $items; + }; + + $unroll = function ($str, $context = []) use (&$unroll, $LOOP_PATT, $apply_scope, $resolve_list) { + $str = $apply_scope($str, $context); + while (preg_match($LOOP_PATT, $str, $m, PREG_OFFSET_CAPTURE)) { + $str = substr_replace($str, '', $m[0][1], strlen($m[0][0])); + $context['loop.parent.counter'] = isset($context['loop.counter']) ? $context['loop.counter'] : -1; + $context['loop.parent.counter0'] = isset($context['loop.counter0']) ? $context['loop.counter0'] : -1; + foreach ($resolve_list($m['list'][0]) as $index => $value) { + $str .= $unroll($m['block_content'][0], [ + $m['var'][0] => $value, + 'loop.counter' => $index + 1, + 'loop.counter0' => $index, + ] + $context); + } + } + + return $str; + }; + + $this->string->pregReplaceCallback($LOOP_PATT, function ($m) use ($unroll) { + return Template::tokenize($unroll(Template::unTokenize($m[0]))); + }); + } ############################# # @ifdefine blocks. @@ -890,6 +968,8 @@ public function compile() $this->resolveSettings(); + $this->resolveLoops(); + // Capture phase 1 hook: After all variables and settings have resolved. $this->hooks->run('capture_phase1', $this); diff --git a/plugins/loop.php b/plugins/loop.php deleted file mode 100644 index 15d74ac..0000000 --- a/plugins/loop.php +++ /dev/null @@ -1,100 +0,0 @@ - function ($process) { - $process->hooks->add('capture_phase1', 'CssCrush\loop'); - } -)); - -define('CssCrush\LOOP_VAR_PATT', - '~\#\( \s* (?[a-zA-Z][\.a-zA-Z0-9-_]*) \s* \)~x'); -define('CssCrush\LOOP_PATT', - Regex::make('~(? @for \s+ (?{{ident}}) \s+ in \s+ (?[^{]+) ) \s* {{block}}~xiS')); - -function loop($process) { - - $process->string->pregReplaceCallback(LOOP_PATT, function ($m) { - - return Template::tokenize(loop_unroll(Template::unTokenize($m[0]))); - }); -} - -function loop_unroll($str, $context = array()) { - - $str = loop_apply_scope($str, $context); - - while (preg_match(LOOP_PATT, $str, $m, PREG_OFFSET_CAPTURE)) { - - $str = substr_replace($str, '', $m[0][1], strlen($m[0][0])); - - $context['loop.parent.counter'] = isset($context['loop.counter']) ? $context['loop.counter'] : -1; - $context['loop.parent.counter0'] = isset($context['loop.counter0']) ? $context['loop.counter0'] : -1; - - foreach (loop_resolve_list($m['list'][0]) as $index => $value) { - - $str .= loop_unroll($m['block_content'][0], array( - $m['var'][0] => $value, - 'loop.counter' => $index + 1, - 'loop.counter0' => $index, - ) + $context); - } - } - - return $str; -} - -function loop_resolve_list($list_text) { - - // Resolve the list of items for iteration. - // Either a generator function or a plain list. - $items = array(); - - $list_text = Crush::$process->functions->apply($list_text); - $generator_func_patt = Regex::make('~(?range){{ parens }}~ix'); - - if (preg_match($generator_func_patt, $list_text, $m)) { - $func = strtolower($m['func']); - $args = Functions::parseArgs($m['parens_content']); - switch ($func) { - case 'range': - $items = call_user_func_array('range', $args); - break; - } - } - else { - $items = Util::splitDelimList($list_text); - } - - return $items; -} - -function loop_apply_scope($str, $context) { - - // Need to temporarily hide child block scopes. - $child_scopes = array(); - - $str = preg_replace_callback(LOOP_PATT, function ($m) use (&$child_scopes) { - $label = '?B' . count($child_scopes) . '?'; - $child_scopes[$label] = $m['block']; - return $m['expression'] . $label; - }, $str); - - $str = preg_replace_callback(LOOP_VAR_PATT, function ($m) use ($context) { - - // Normalize casing of built-in loop variables. - // User variables are case-sensitive. - $arg = preg_replace_callback('~^loop\.(parent\.)?counter0?$~i', function ($m) { - return strtolower($m[0]); - }, $m['arg']); - - return isset($context[$arg]) ? $context[$arg] : ''; - }, $str); - - return str_replace(array_keys($child_scopes), array_values($child_scopes), $str); -} diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 829d63d..c0bfb42 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -30,13 +30,13 @@ function temp_file($contents = '') return $temporary_file; } - function stdout($message, $prepend_newline = false) + function stdout($message, $prepend_newline = false, $append_newline = true) { if (! is_string($message)) { ob_start(); print_r($message); $message = ob_get_clean(); } - fwrite(STDOUT, ($prepend_newline ? "\n" : '') . $message . "\n"); + fwrite(STDOUT, ($prepend_newline ? "\n" : '') . $message . ($append_newline ? "\n" : '')); } } From 689ee46bd448199e66b5615dd74301895b86c3c1 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 2 Sep 2017 14:03:26 +0100 Subject: [PATCH 349/421] Version now pulling from package.json --- docs/api/options.md | 6 +++++- docs/core/auto-prefixing.md | 8 +------- docs/core/variables.md | 4 ++-- lib/CssCrush/Process.php | 3 +-- lib/CssCrush/Version.php | 21 +++++++++++++++++++++ lib/functions.php | 2 +- package.json | 2 +- 7 files changed, 32 insertions(+), 14 deletions(-) diff --git a/docs/api/options.md b/docs/api/options.md index c7a81b4..5c66c29 100644 --- a/docs/api/options.md +++ b/docs/api/options.md @@ -75,6 +75,11 @@ true | false | "absolute" Rewrite relative URLs inside inlined imported files. + + import_paths + Array + Additional paths to search when resolving relative import URLs. + plugins Array @@ -101,4 +106,3 @@ An associative array of plugin and environment settings. Used primarily for plugin configuration. - diff --git a/docs/core/auto-prefixing.md b/docs/core/auto-prefixing.md index 9ba9045..825e670 100644 --- a/docs/core/auto-prefixing.md +++ b/docs/core/auto-prefixing.md @@ -16,8 +16,7 @@ In some cases (e.g. CSS3 gradients) final syntax is incompatible with older pref ```css .foo { - background: -webkit-linear-gradient(left, red, white); - background: -moz-linear-gradient(left, red, white); + background: -webkit-linear-gradient(to right, red, white); background: linear-gradient(to right, red, white); } ``` @@ -34,13 +33,8 @@ In some cases (e.g. CSS3 gradients) final syntax is incompatible with older pref 50% {-webkit-transform: scale(1.4); transform: scale(1.4);} } -@-moz-keyframes bounce { - 50% {-moz-transform: scale(1.4); - transform: scale(1.4);} -} @keyframes bounce { 50% {-webkit-transform: scale(1.4); - -moz-transform: scale(1.4); -ms-transform: scale(1.4); transform: scale(1.4);} } diff --git a/docs/core/variables.md b/docs/core/variables.md index cf2ea0e..117f53f 100644 --- a/docs/core/variables.md +++ b/docs/core/variables.md @@ -30,7 +30,7 @@ Variables can also be injected at runtime with the [vars option](#api--options). ******* ```css -/* String interpolation */ +/* Interpolation */ .username::before { content: "$(greeting)"; } @@ -59,4 +59,4 @@ p { margin-bottom: 5px; } } -``` \ No newline at end of file +``` diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index c7da6e1..6a8dd0e 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -181,8 +181,7 @@ protected function getBoilerplate() 'year' => @date('Y'), 'command' => $commandArgs, 'plugins' => implode(',', $this->plugins), - 'version' => csscrush_version(), - 'git_version' => function () { + 'version' => function () { return csscrush_version(); }, 'compile_time' => function () { diff --git a/lib/CssCrush/Version.php b/lib/CssCrush/Version.php index e140ad2..efa51f8 100644 --- a/lib/CssCrush/Version.php +++ b/lib/CssCrush/Version.php @@ -75,6 +75,10 @@ public function compare($version_string) return $EQUAL; } + public static function detect() { + return self::gitDescribe() ?: self::packageDescribe(); + } + public static function gitDescribe() { static $attempted, $version; @@ -92,4 +96,21 @@ public static function gitDescribe() return $version; } + + public static function packageDescribe() + { + static $attempted, $version; + if (! $attempted && file_exists(Crush::$dir . '/package.json')) { + $attempted = true; + $package = json_decode(file_get_contents(Crush::$dir . '/package.json')); + if ($package->version) { + $version = new Version($package->version); + if (is_null($version->major)) { + $version = null; + } + } + } + + return $version; + } } diff --git a/lib/functions.php b/lib/functions.php index f25410b..cf60963 100644 --- a/lib/functions.php +++ b/lib/functions.php @@ -177,7 +177,7 @@ function csscrush_add_function($function_name = null, $callback = null) { */ function csscrush_version() { - return \CssCrush\Version::gitDescribe(); + return \CssCrush\Version::detect(); } diff --git a/package.json b/package.json index 904eede..75ac7eb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "csscrush", - "version": "3.0.0", + "version": "3.0.0-alpha.0", "description": "CSS-Crush, CSS preprocessor", "repository": { "type": "git", From 60e89ee2a604c4c65c8540aa4c0036ea7a8eeb3e Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 2 Sep 2017 19:56:55 +0100 Subject: [PATCH 350/421] Replaced Hooks with EventEmitter trait. --- .gitignore | 1 - README.md | 32 ++++++++++++++++--- lib/CssCrush/Declaration.php | 2 +- lib/CssCrush/DeclarationList.php | 2 +- lib/CssCrush/EventEmitter.php | 36 +++++++++++++++++++++ lib/CssCrush/Hooks.php | 40 ------------------------ lib/CssCrush/IO.php | 9 ++---- lib/CssCrush/Process.php | 15 ++++----- plugins/canvas.php | 2 +- plugins/color.php | 4 +-- plugins/ease.php | 2 +- plugins/property-sorter.php | 2 +- plugins/rem.php | 2 +- plugins/svg.php | 2 +- tests/unit/CssCrush/EventEmitterTest.php | 32 +++++++++++++++++++ tests/unit/CssCrush/HooksTest.php | 33 ------------------- 16 files changed, 116 insertions(+), 100 deletions(-) create mode 100644 lib/CssCrush/EventEmitter.php delete mode 100644 lib/CssCrush/Hooks.php create mode 100644 tests/unit/CssCrush/EventEmitterTest.php delete mode 100644 tests/unit/CssCrush/HooksTest.php diff --git a/.gitignore b/.gitignore index 81853d7..a20159e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,3 @@ phpunit.xml composer.lock vendor node_modules -package-lock.json diff --git a/README.md b/README.md index a567733..e9400fa 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,9 @@ CSS-Crush is a standards inspired preprocessor designed to enable a modern and u See the [docs](http://the-echoplex.net/csscrush) for full details. +******************************** -## Setup +## Setup (PHP) If you're using [Composer](http://getcomposer.org) you can use Crush in your project with the following line in your terminal: @@ -32,8 +33,7 @@ If you're not using Composer yet just download the library into a convenient loc ``` - -## Basic usage +## Basic usage (PHP) ```php important = true; } - Crush::$process->hooks->run('declaration_preprocess', array('property' => &$property, 'value' => &$value)); + Crush::$process->emit('declaration_preprocess', array('property' => &$property, 'value' => &$value)); // Reject declarations with empty CSS values. if ($value === false || $value === '') { diff --git a/lib/CssCrush/DeclarationList.php b/lib/CssCrush/DeclarationList.php index c14511b..da7f804 100644 --- a/lib/CssCrush/DeclarationList.php +++ b/lib/CssCrush/DeclarationList.php @@ -406,7 +406,7 @@ public static function parse($str, $options = array()) } if ($options['apply_hooks']) { - Crush::$process->hooks->run('declaration_preprocess', array( + Crush::$process->emit('declaration_preprocess', array( 'property' => &$property, 'value' => &$value, )); diff --git a/lib/CssCrush/EventEmitter.php b/lib/CssCrush/EventEmitter.php new file mode 100644 index 0000000..2ab4452 --- /dev/null +++ b/lib/CssCrush/EventEmitter.php @@ -0,0 +1,36 @@ +eventEmitterStorage[$event])) { + $this->eventEmitterStorage[$event] = []; + } + + $id = ++$this->eventEmitterUid; + $this->eventEmitterStorage[$event][$id] = $function; + + return function () use ($event, $id) { + unset($this->eventEmitterStorage[$event][$id]); + }; + } + + public function emit($event, $data = null) + { + if (isset($this->eventEmitterStorage[$event])) { + foreach ($this->eventEmitterStorage[$event] as $function) { + $function($data); + } + } + } +} diff --git a/lib/CssCrush/Hooks.php b/lib/CssCrush/Hooks.php deleted file mode 100644 index 113a06c..0000000 --- a/lib/CssCrush/Hooks.php +++ /dev/null @@ -1,40 +0,0 @@ -register[$hook][$functionName])) { - if (function_exists($functionName)) { - $this->register[$hook][$functionName] = true; - } - } - } - - public function remove($hook, $functionName) - { - unset($this->register[$hook][$functionName]); - } - - public function run($hook, $argObj = null) - { - if (isset($this->register[$hook])) { - foreach (array_keys($this->register[$hook]) as $functionName) { - $functionName($argObj); - } - } - } - - public function get() - { - return $this->register; - } -} diff --git a/lib/CssCrush/IO.php b/lib/CssCrush/IO.php index 1c83d2f..4f53c00 100644 --- a/lib/CssCrush/IO.php +++ b/lib/CssCrush/IO.php @@ -190,8 +190,7 @@ public function saveCacheData() debug('Saving config.'); - $flags = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 0; - Util::filePutContents($process->cacheFile, json_encode($process->cacheData, $flags), __METHOD__); + Util::filePutContents($process->cacheFile, json_encode($process->cacheData, JSON_PRETTY_PRINT), __METHOD__); } public function write(StringObject $string) @@ -208,11 +207,9 @@ public function write(StringObject $string) if (Util::filePutContents("$dir/$filename", $string, __METHOD__)) { - $jsonFlags = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 0; - if ($process->sourceMap) { Util::filePutContents("$dir/$sourcemapFilename", - json_encode($process->sourceMap, $jsonFlags), __METHOD__); + json_encode($process->sourceMap, JSON_PRETTY_PRINT), __METHOD__); } if ($process->options->stat_dump) { @@ -220,7 +217,7 @@ public function write(StringObject $string) $process->options->stat_dump : "$dir/$filename.json"; $GLOBALS['CSSCRUSH_STAT_FILE'] = $statFile; - Util::filePutContents($statFile, json_encode(csscrush_stat(), $jsonFlags), __METHOD__); + Util::filePutContents($statFile, json_encode(csscrush_stat(), JSON_PRETTY_PRINT), __METHOD__); } return true; diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 6a8dd0e..80e9829 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -8,6 +8,8 @@ class Process { + use EventEmitter; + public function __construct($user_options = array(), $context = array()) { $config = Crush::$config; @@ -30,7 +32,6 @@ public function __construct($user_options = array(), $context = array()) $this->output = new \stdClass(); $this->tokens = new Tokens(); $this->functions = new Functions(); - $this->hooks = new Hooks(); $this->sourceMap = null; $this->selectorAliases = array(); $this->selectorAliasesPatt = null; @@ -713,18 +714,18 @@ protected function processRules() $rule->declarations->flatten(); $rule->declarations->process(); - $this->hooks->run('rule_prealias', $rule); + $this->emit('rule_prealias', $rule); $rule->declarations->aliasProperties($rule->vendorContext); $rule->declarations->aliasFunctions($rule->vendorContext); $rule->declarations->aliasDeclarations($rule->vendorContext); - $this->hooks->run('rule_postalias', $rule); + $this->emit('rule_postalias', $rule); $rule->selectors->expand(); $rule->applyExtendables(); - $this->hooks->run('rule_postprocess', $rule); + $this->emit('rule_postprocess', $rule); } } @@ -957,7 +958,7 @@ public function compile() $this->string = new StringObject($importer->collate()); // Capture phase 0 hook: Before all variables and settings have resolved. - $this->hooks->run('capture_phase0', $this); + $this->emit('capture_phase0', $this); $this->captureVars(); @@ -970,7 +971,7 @@ public function compile() $this->resolveLoops(); // Capture phase 1 hook: After all variables and settings have resolved. - $this->hooks->run('capture_phase1', $this); + $this->emit('capture_phase1', $this); $this->resolveSelectorAliases(); @@ -979,7 +980,7 @@ public function compile() $this->resolveFragments(); // Capture phase 2 hook: After most built-in directives have resolved. - $this->hooks->run('capture_phase2', $this); + $this->emit('capture_phase2', $this); $this->captureRules(); diff --git a/plugins/canvas.php b/plugins/canvas.php index b5fe950..cc52270 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -10,7 +10,7 @@ Plugin::register('canvas', array( 'enable' => function ($process) { - $process->hooks->add('capture_phase2', 'CssCrush\canvas_capture'); + $process->on('capture_phase2', 'CssCrush\canvas_capture'); $process->functions->add('canvas', 'CssCrush\canvas_generator'); $process->functions->add('canvas-data', 'CssCrush\canvas_generator'); } diff --git a/plugins/color.php b/plugins/color.php index 96fcbe4..dbc9db4 100644 --- a/plugins/color.php +++ b/plugins/color.php @@ -9,8 +9,8 @@ Plugin::register('color', array( 'enable' => function ($process) { $GLOBALS['CSSCRUSH_COLOR_PATT'] = null; - $process->hooks->add('capture_phase1', 'CssCrush\color_capture'); - $process->hooks->add('declaration_preprocess', 'CssCrush\color'); + $process->on('capture_phase1', 'CssCrush\color_capture'); + $process->on('declaration_preprocess', 'CssCrush\color'); } )); diff --git a/plugins/ease.php b/plugins/ease.php index f0f328d..024ab54 100644 --- a/plugins/ease.php +++ b/plugins/ease.php @@ -8,7 +8,7 @@ Plugin::register('ease', array( 'enable' => function ($process) { - $process->hooks->add('rule_prealias', 'CssCrush\ease'); + $process->on('rule_prealias', 'CssCrush\ease'); } )); diff --git a/plugins/property-sorter.php b/plugins/property-sorter.php index 865efb1..855da42 100644 --- a/plugins/property-sorter.php +++ b/plugins/property-sorter.php @@ -8,7 +8,7 @@ Plugin::register('property-sorter', array( 'enable' => function ($process) { - $process->hooks->add('rule_prealias', 'CssCrush\property_sorter'); + $process->on('rule_prealias', 'CssCrush\property_sorter'); } )); diff --git a/plugins/rem.php b/plugins/rem.php index 6a186a3..3be4700 100644 --- a/plugins/rem.php +++ b/plugins/rem.php @@ -8,7 +8,7 @@ Plugin::register('rem', array( 'enable' => function ($process) { - $process->hooks->add('rule_prealias', 'CssCrush\rem'); + $process->on('rule_prealias', 'CssCrush\rem'); } )); diff --git a/plugins/svg.php b/plugins/svg.php index 3d1472e..975f010 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -8,7 +8,7 @@ Plugin::register('svg', array( 'enable' => function ($process) { - $process->hooks->add('capture_phase2', 'CssCrush\svg_capture'); + $process->on('capture_phase2', 'CssCrush\svg_capture'); $process->functions->add('svg', 'CssCrush\fn__svg'); $process->functions->add('svg-data', 'CssCrush\fn__svg_data'); $process->functions->add('svg-linear-gradient', 'CssCrush\fn__svg_linear_gradient'); diff --git a/tests/unit/CssCrush/EventEmitterTest.php b/tests/unit/CssCrush/EventEmitterTest.php new file mode 100644 index 0000000..1ca0cff --- /dev/null +++ b/tests/unit/CssCrush/EventEmitterTest.php @@ -0,0 +1,32 @@ +on('foo', function ($data) use (&$foo) { + $foo = $data; + }); + + $this->assertEquals($foo, null); + + $emitter->emit('foo', 10); + + $this->assertEquals($foo, 10); + + $cancelEvent(); + + $emitter->emit('foo', 20); + + $this->assertEquals($foo, 10); + } +} + +class EventEmitterHost { use EventEmitter; } diff --git a/tests/unit/CssCrush/HooksTest.php b/tests/unit/CssCrush/HooksTest.php deleted file mode 100644 index 447c552..0000000 --- a/tests/unit/CssCrush/HooksTest.php +++ /dev/null @@ -1,33 +0,0 @@ -add('foo', $dummy_hook); - $hooks->add('foo', 'strtoupper'); - - $this->assertEquals(array('foo' => array($dummy_hook=>true, 'strtoupper'=>true)), $hooks->get()); - - $hooks->remove('foo', 'strtoupper'); - - $this->assertEquals(array('foo' => array($dummy_hook=>true)), $hooks->get()); - - $hooks->run('foo', $this); - - $this->assertTrue($this->hookRan); - } -} - -function dummy_hook(HookTest $test) -{ - $test->hookRan = true; -} From 3acb15cf955196313f0fa8675b4abd9c074ec118 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 2 Sep 2017 21:01:31 +0100 Subject: [PATCH 351/421] Simplified plugin api. --- cli.php | 14 ---- lib/CssCrush/Crush.php | 27 ++++++++ lib/CssCrush/Plugin.php | 101 ----------------------------- lib/CssCrush/Process.php | 86 ++++++++++++------------ lib/functions.php | 11 ++++ plugins/aria.php | 13 ++-- plugins/canvas.php | 13 ++-- plugins/color.php | 13 ++-- plugins/ease.php | 9 +-- plugins/forms.php | 17 ++--- plugins/hocus-pocus.php | 12 ++-- plugins/property-sorter.php | 9 +-- plugins/px2em.php | 11 ++-- plugins/rem.php | 9 +-- plugins/svg.php | 16 ++--- tests/unit/CssCrush/PluginTest.php | 66 ------------------- 16 files changed, 125 insertions(+), 302 deletions(-) delete mode 100644 lib/CssCrush/Plugin.php delete mode 100644 tests/unit/CssCrush/PluginTest.php diff --git a/cli.php b/cli.php index 2a6890d..cc1afa5 100755 --- a/cli.php +++ b/cli.php @@ -46,15 +46,6 @@ exit(STATUS_OK); } -elseif ($args->list) { - - foreach (CssCrush\Plugin::info() as $name => $docs) { - $headline = isset($docs[0]) ? $docs[0] : ''; - stdout(message(array($name => $headline), array('color'=>'g'))); - } - - exit(STATUS_OK); -} ################################################################## @@ -439,7 +430,6 @@ function parse_args() { $flag_opts = array( 'p|pretty', 'w|watch', - 'list', 'help', 'version', 'source-map', @@ -473,7 +463,6 @@ function parse_args() { // Information options. $args->help = isset($opts['h']) ?: isset($opts['help']); $args->version = isset($opts['version']); - $args->list = isset($opts['l']) ?: isset($opts['list']); // File arguments. $args->input_file = pick($opts, 'i', 'input', 'f', 'file'); @@ -600,9 +589,6 @@ function manpage() { --help Display this help message. - --list - Show plugins. - --newlines Force newline style on output css. Defaults to the current platform newline. Possible values: 'windows' (or 'win'), 'unix', 'use-platform'. diff --git a/lib/CssCrush/Crush.php b/lib/CssCrush/Crush.php index d71d66c..3d6aa29 100644 --- a/lib/CssCrush/Crush.php +++ b/lib/CssCrush/Crush.php @@ -97,6 +97,33 @@ public static function loadAssets() } } + public static function plugin($name = null, callable $callback = null) + { + static $plugins = []; + + if (! $callback) { + return isset($plugins[$name]) ? $plugins[$name] : null; + } + + $plugins[$name] = $callback; + } + + public static function enablePlugin($name) + { + $plugin = self::plugin($name); + if (! $plugin) { + $path = self::$dir . "/plugins/$name.php"; + if (! file_exists($path)) { + notice("Plugin '$name' not found."); + return; + } + require_once $path; + $plugin = self::plugin($name); + } + + $plugin(self::$process); + } + public static function parseAliasesFile($file) { if (! ($tree = Util::parseIni($file, true))) { diff --git a/lib/CssCrush/Plugin.php b/lib/CssCrush/Plugin.php deleted file mode 100644 index 8c906b2..0000000 --- a/lib/CssCrush/Plugin.php +++ /dev/null @@ -1,101 +0,0 @@ -pluginDirs; - $plugin_data = array(); - - foreach ($plugin_dirs as $plugin_dir) { - - foreach (glob("$plugin_dir/*.php") as $path) { - $name = basename($path, '.php'); - $plugin_data += array($name => Plugin::parseDoc($path)); - } - } - - return $plugin_data; - } - - public static function parseDoc($plugin_path) - { - $contents = file_get_contents($plugin_path); - if (preg_match('~/\*\*(.*?)\*/~s', $contents, $m)) { - - $lines = preg_split(Regex::$patt->newline, $m[1]); - foreach ($lines as &$line) { - $line = trim(ltrim($line, "* \t")); - } - // Remove empty strings and reset indexes. - $lines = array_values(array_filter($lines, 'strlen')); - - return $lines; - } - - return false; - } - - public static function register($plugin_name, $callbacks) - { - self::$plugins[$plugin_name] = $callbacks; - } - - public static function load($plugin_name) - { - // Assume the the plugin file is not loaded if null. - if (! isset(self::$plugins[$plugin_name])) { - - $found = false; - - // Loop plugin_dirs to find the plugin. - foreach (Crush::$config->pluginDirs as $plugin_dir) { - - $path = "$plugin_dir/$plugin_name.php"; - if (file_exists($path)) { - require_once $path; - $found = true; - break; - } - } - - if (! $found) { - notice("Plugin '$plugin_name' not found."); - } - elseif (isset(self::$plugins[$plugin_name]['load'])) { - $plugin_load = self::$plugins[$plugin_name]['load']; - $plugin_load(Crush::$process); - } - } - - return isset(self::$plugins[$plugin_name]) ? self::$plugins[$plugin_name] : null; - } - - public static function enable($plugin_name) - { - $plugin = self::load($plugin_name); - - if (is_callable($plugin['enable'])) { - $plugin['enable'](Crush::$process); - } - - return true; - } - - public static function disable($plugin_name) - { - $plugin = isset(self::$plugins[$plugin_name]) ? self::$plugins[$plugin_name] : null; - - if (isset($plugin['disable']) && is_callable($plugin['disable'])) { - $plugin['disable'](Crush::$process); - } - } -} diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 80e9829..0a2dee3 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -10,37 +10,37 @@ class Process { use EventEmitter; - public function __construct($user_options = array(), $context = array()) + public function __construct($user_options = [], $context = []) { $config = Crush::$config; Crush::loadAssets(); // Initialize properties. - $this->cacheData = array(); - $this->mixins = array(); - $this->fragments = array(); - $this->references = array(); - $this->absoluteImports = array(); + $this->cacheData = []; + $this->mixins = []; + $this->fragments = []; + $this->references = []; + $this->absoluteImports = []; $this->charset = null; - $this->sources = array(); - $this->vars = array(); - $this->plugins = array(); - $this->settings = array(); + $this->sources = []; + $this->vars = []; + $this->plugins = []; + $this->settings = []; $this->misc = new \stdClass(); $this->input = new \stdClass(); $this->output = new \stdClass(); $this->tokens = new Tokens(); $this->functions = new Functions(); $this->sourceMap = null; - $this->selectorAliases = array(); + $this->selectorAliases = []; $this->selectorAliasesPatt = null; $this->io = new Crush::$config->io($this); - $this->errors = array(); - $this->warnings = array(); - $this->debugLog = array(); - $this->stat = array(); + $this->errors = []; + $this->warnings = []; + $this->debugLog = []; + $this->stat = []; // Copy config values. $this->aliases = $config->aliases; @@ -49,7 +49,7 @@ public function __construct($user_options = array(), $context = array()) $this->options = new Options($user_options, $config->options); // Context options. - $context += array('type' => 'filter', 'data' => ''); + $context += ['type' => 'filter', 'data' => '']; $this->ioContext = $context['type']; // Keep track of global vars to maintain cache integrity. @@ -177,7 +177,7 @@ protected function getBoilerplate() $commandArgs = 'csscrush ' . implode(' ', $argv); } - $tags = array( + $tags = [ 'datetime' => @date('Y-m-d H:i:s O'), 'year' => @date('Y'), 'command' => $commandArgs, @@ -189,7 +189,7 @@ protected function getBoilerplate() $now = microtime(true) - Crush::$process->stat['compile_start_time']; return round($now, 4) . ' seconds'; }, - ); + ]; foreach (array_keys($boilerplateMatches[0]) as $index) { $tagName = trim($boilerplateMatches[1][$index]); @@ -321,7 +321,7 @@ protected function filterAliases() continue; } - $result = array(); + $result = []; foreach ($prefix_array as $prefix) { if (preg_match($vendor_patt, $prefix)) { @@ -350,7 +350,7 @@ protected function filterPlugins() $this->plugins = array_unique($this->options->plugins); foreach ($this->plugins as $plugin) { - Plugin::enable($plugin); + Crush::enablePlugin($plugin); } } @@ -360,10 +360,10 @@ protected function filterPlugins() protected function captureVars() { - Crush::$process->vars = Crush::$process->string->captureDirectives(array('set', 'define'), array( + Crush::$process->vars = Crush::$process->string->captureDirectives(['set', 'define'], [ 'singles' => true, 'lowercase_keys' => false, - )) + Crush::$process->vars; + ]) + Crush::$process->vars; // For convenience adding a runtime variable for cache busting linked resources. $this->vars['timestamp'] = (int) $this->stat['compile_start_time']; @@ -407,7 +407,7 @@ protected function placeVars(&$value) static $varFunction, $varFunctionSimple; if (! $varFunction) { $varFunctionSimple = Regex::make('~\$\( \s* ({{ ident }}) \s* \)~xS'); - $varFunction = new Functions(array('$' => function ($rawArgs) { + $varFunction = new Functions(['$' => function ($rawArgs) { list($name, $defaultValue) = Functions::parseArgsSimple($rawArgs); if (isset(Crush::$process->vars[$name])) { return Crush::$process->vars[$name]; @@ -415,7 +415,7 @@ protected function placeVars(&$value) else { return $defaultValue; } - })); + }]); } // Variables with no default value. @@ -446,7 +446,7 @@ protected function placeVars(&$value) protected function resolveSettings() { - $captured_settings = $this->string->captureDirectives('settings', array('singles' => true)); + $captured_settings = $this->string->captureDirectives('settings', ['singles' => true]); $this->settings = new Settings($this->options->settings + $captured_settings); } @@ -594,7 +594,7 @@ protected function resolveFragments() $this->string->pregReplaceCallback(Regex::$patt->fragmentCapture, function ($m) use (&$fragments) { $fragments[$m['name']] = new Fragment( $m['block_content'], - array('name' => strtolower($m['name'])) + ['name' => strtolower($m['name'])] ); return ''; }); @@ -602,7 +602,7 @@ protected function resolveFragments() $this->string->pregReplaceCallback(Regex::$patt->fragmentInvoke, function ($m) use (&$fragments) { $fragment = isset($fragments[$m['name']]) ? $fragments[$m['name']] : null; if ($fragment) { - $args = array(); + $args = []; if (isset($m['parens'])) { $args = Functions::parseArgs($m['parens_content']); } @@ -689,7 +689,7 @@ public function captureRules() protected function processRules() { // Create table of name/selector to rule references. - $namedReferences = array(); + $namedReferences = []; $previousRule = null; foreach ($this->tokens->store->r as $rule) { @@ -759,7 +759,7 @@ protected function aliasAtRules() // Build up string with aliased blocks for splicing. $original_block = $curly_match->whole(); - $new_blocks = array(); + $new_blocks = []; foreach ($at_rule_aliases as $alias) { @@ -774,8 +774,8 @@ protected function aliasAtRules() // Duplicate rules. if (preg_match_all($regex->r_token, $copy_block, $copy_matches)) { - $originals = array(); - $replacements = array(); + $originals = []; + $replacements = []; foreach ($copy_matches[0] as $rule_label) { @@ -818,7 +818,7 @@ protected function collate() // Formatting replacements. // Strip newlines added during processing. - $regex_replacements = array(); + $regex_replacements = []; $regex_replacements['~\n+~'] = ''; if ($minify) { @@ -846,7 +846,7 @@ protected function collate() // Record stats then drop rule objects to reclaim memory. Crush::runStat('selector_count', 'rule_count', 'vars'); - $this->tokens->store->r = array(); + $this->tokens->store->r = []; // If specified, apply advanced minification. if (is_array($minify)) { @@ -908,7 +908,7 @@ protected function collate() $this->string->prepend("@charset \"$this->charset\";$EOL"); } - $this->string->restore(array('u', 's')); + $this->string->restore(['u', 's']); if ($this->generateMap) { $this->generateSourceMap(); @@ -937,10 +937,6 @@ public function preCompile() public function postCompile() { - foreach ($this->plugins as $plugin) { - Plugin::disable($plugin); - } - $this->release(); Crush::runStat('compile_time'); @@ -1007,17 +1003,17 @@ public function compile() public function generateSourceMap() { - $this->sourceMap = array( + $this->sourceMap = [ 'version' => '3', 'file' => $this->output->filename, - 'sources' => array(), - ); + 'sources' => [], + ]; foreach ($this->sources as $source) { $this->sourceMap['sources'][] = Util::getLinkBetweenPaths($this->output->dir, $source, false); } $token_patt = Regex::make('~\?[tm]{{token_id}}\?~S'); - $mappings = array(); + $mappings = []; $lines = preg_split(Regex::$patt->newline, $this->string->raw); $tokens =& $this->tokens->store; @@ -1029,7 +1025,7 @@ public function generateSourceMap() foreach ($lines as &$line_text) { - $line_segments = array(); + $line_segments = []; while (preg_match($token_patt, $line_text, $m, PREG_OFFSET_CAPTURE)) { @@ -1066,7 +1062,7 @@ public function generateSourceMap() protected function decruft() { - return $this->string->pregReplaceHash(array( + return $this->string->pregReplaceHash([ // Strip leading zeros on floats. '~([: \(,])(-?)0(\.\d+)~S' => '$1$2$3', @@ -1086,7 +1082,7 @@ protected function decruft() // Compress hex codes. Regex::$patt->cruftyHex => '#$1$2$3', - )); + ]); } diff --git a/lib/functions.php b/lib/functions.php index cf60963..c435ffb 100644 --- a/lib/functions.php +++ b/lib/functions.php @@ -170,6 +170,17 @@ function csscrush_add_function($function_name = null, $callback = null) { } +/** + * Add plugin. + * + * @see docs/api/functions.md + */ +function csscrush_plugin($name, callable $callback) { + + Crush::plugin($name, $callback); +} + + /** * Get version information. * diff --git a/plugins/aria.php b/plugins/aria.php index b467217..afbc220 100644 --- a/plugins/aria.php +++ b/plugins/aria.php @@ -6,15 +6,12 @@ */ namespace CssCrush; -Plugin::register('aria', array( - 'enable' => function ($process) { - foreach (aria() as $name => $handler) { - $type = is_callable($handler) ? 'callback' : 'alias'; - $process->addSelectorAlias($name, $handler, $type); - } +\csscrush_plugin('aria', function ($process) { + foreach (aria() as $name => $handler) { + $type = is_callable($handler) ? 'callback' : 'alias'; + $process->addSelectorAlias($name, $handler, $type); } -)); - +}); function aria() { diff --git a/plugins/canvas.php b/plugins/canvas.php index cc52270..812fa7d 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -8,14 +8,11 @@ use stdClass; -Plugin::register('canvas', array( - 'enable' => function ($process) { - $process->on('capture_phase2', 'CssCrush\canvas_capture'); - $process->functions->add('canvas', 'CssCrush\canvas_generator'); - $process->functions->add('canvas-data', 'CssCrush\canvas_generator'); - } -)); - +\csscrush_plugin('canvas', function ($process) { + $process->on('capture_phase2', 'CssCrush\canvas_capture'); + $process->functions->add('canvas', 'CssCrush\canvas_generator'); + $process->functions->add('canvas-data', 'CssCrush\canvas_generator'); +}); function canvas_capture($process) { diff --git a/plugins/color.php b/plugins/color.php index dbc9db4..b32696a 100644 --- a/plugins/color.php +++ b/plugins/color.php @@ -6,14 +6,11 @@ */ namespace CssCrush; -Plugin::register('color', array( - 'enable' => function ($process) { - $GLOBALS['CSSCRUSH_COLOR_PATT'] = null; - $process->on('capture_phase1', 'CssCrush\color_capture'); - $process->on('declaration_preprocess', 'CssCrush\color'); - } -)); - +\csscrush_plugin('color', function ($process) { + $GLOBALS['CSSCRUSH_COLOR_PATT'] = null; + $process->on('capture_phase1', 'CssCrush\color_capture'); + $process->on('declaration_preprocess', 'CssCrush\color'); +}); function color(&$declaration) { if (isset($GLOBALS['CSSCRUSH_COLOR_PATT'])) { diff --git a/plugins/ease.php b/plugins/ease.php index 024ab54..cacc4de 100644 --- a/plugins/ease.php +++ b/plugins/ease.php @@ -6,12 +6,9 @@ */ namespace CssCrush; -Plugin::register('ease', array( - 'enable' => function ($process) { - $process->on('rule_prealias', 'CssCrush\ease'); - } -)); - +\csscrush_plugin('ease', function ($process) { + $process->on('rule_prealias', 'CssCrush\ease'); +}); function ease(Rule $rule) { diff --git a/plugins/forms.php b/plugins/forms.php index 84e5f80..842ec7c 100644 --- a/plugins/forms.php +++ b/plugins/forms.php @@ -6,18 +6,15 @@ */ namespace CssCrush; -Plugin::register('forms', array( - 'enable' => function ($process) { - foreach (forms() as $name => $handler) { - if (is_array($handler)) { - $type = $handler['type']; - $handler = $handler['handler']; - } - $process->addSelectorAlias($name, $handler, $type); +\csscrush_plugin('forms', function ($process) { + foreach (forms() as $name => $handler) { + if (is_array($handler)) { + $type = $handler['type']; + $handler = $handler['handler']; } + $process->addSelectorAlias($name, $handler, $type); } -)); - +}); function forms() { return array( diff --git a/plugins/hocus-pocus.php b/plugins/hocus-pocus.php index 7b5f490..a7cda1d 100644 --- a/plugins/hocus-pocus.php +++ b/plugins/hocus-pocus.php @@ -4,11 +4,7 @@ * * @see docs/plugins/hocus-pocus.md */ -namespace CssCrush; - -Plugin::register('hocus-pocus', array( - 'enable' => function ($process) { - $process->addSelectorAlias('hocus', ':any(:hover,:focus)'); - $process->addSelectorAlias('pocus', ':any(:hover,:focus,:active)'); - } -)); +csscrush_plugin('hocus-pocus', function ($process) { + $process->addSelectorAlias('hocus', ':any(:hover,:focus)'); + $process->addSelectorAlias('pocus', ':any(:hover,:focus,:active)'); +}); diff --git a/plugins/property-sorter.php b/plugins/property-sorter.php index 855da42..7c8aace 100644 --- a/plugins/property-sorter.php +++ b/plugins/property-sorter.php @@ -6,12 +6,9 @@ */ namespace CssCrush { - Plugin::register('property-sorter', array( - 'enable' => function ($process) { - $process->on('rule_prealias', 'CssCrush\property_sorter'); - } - )); - + \csscrush_plugin('property-sorter', function ($process) { + $process->on('rule_prealias', 'CssCrush\property_sorter'); + }); function property_sorter(Rule $rule) { diff --git a/plugins/px2em.php b/plugins/px2em.php index 080f1d1..5a7af94 100644 --- a/plugins/px2em.php +++ b/plugins/px2em.php @@ -6,13 +6,10 @@ */ namespace CssCrush; -Plugin::register('px2em', array( - 'enable' => function ($process) { - $process->functions->add('px2em', 'CssCrush\fn__px2em'); - $process->functions->add('px2rem', 'CssCrush\fn__px2rem'); - } -)); - +\csscrush_plugin('px2em', function ($process) { + $process->functions->add('px2em', 'CssCrush\fn__px2em'); + $process->functions->add('px2rem', 'CssCrush\fn__px2rem'); +}); function fn__px2em($input) { diff --git a/plugins/rem.php b/plugins/rem.php index 3be4700..ef6b362 100644 --- a/plugins/rem.php +++ b/plugins/rem.php @@ -6,12 +6,9 @@ */ namespace CssCrush; -Plugin::register('rem', array( - 'enable' => function ($process) { - $process->on('rule_prealias', 'CssCrush\rem'); - } -)); - +\csscrush_plugin('rem', function ($process) { + $process->on('rule_prealias', 'CssCrush\rem'); +}); function rem(Rule $rule) { diff --git a/plugins/svg.php b/plugins/svg.php index 975f010..d49de7c 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -6,15 +6,13 @@ */ namespace CssCrush; -Plugin::register('svg', array( - 'enable' => function ($process) { - $process->on('capture_phase2', 'CssCrush\svg_capture'); - $process->functions->add('svg', 'CssCrush\fn__svg'); - $process->functions->add('svg-data', 'CssCrush\fn__svg_data'); - $process->functions->add('svg-linear-gradient', 'CssCrush\fn__svg_linear_gradient'); - $process->functions->add('svg-radial-gradient', 'CssCrush\fn__svg_radial_gradient'); - } -)); +\csscrush_plugin('svg', function ($process) { + $process->on('capture_phase2', 'CssCrush\svg_capture'); + $process->functions->add('svg', 'CssCrush\fn__svg'); + $process->functions->add('svg-data', 'CssCrush\fn__svg_data'); + $process->functions->add('svg-linear-gradient', 'CssCrush\fn__svg_linear_gradient'); + $process->functions->add('svg-radial-gradient', 'CssCrush\fn__svg_radial_gradient'); +}); function fn__svg($input) { diff --git a/tests/unit/CssCrush/PluginTest.php b/tests/unit/CssCrush/PluginTest.php deleted file mode 100644 index 86357d8..0000000 --- a/tests/unit/CssCrush/PluginTest.php +++ /dev/null @@ -1,66 +0,0 @@ -pluginDirs[] = $dummy_plugin_dir; - - $dummy_plugin = << function () { - define('DUMMY_ENABLE_TEST', true); - }, - 'disable' => function () { - define('DUMMY_DISABLE_TEST', true); - }, -)); -TPL; - file_put_contents("$dummy_plugin_dir/dummy.php", $dummy_plugin); - - Plugin::$plugins = array(); - } - - public function tearDown() - { - Plugin::$plugins = array(); - } - - public function testInfo() - { - $info = Plugin::info(); - - $this->assertArrayHasKey('svg', $info); - } - - public function testParseDoc() - { - $test_path = Crush::$dir . '/plugins/svg.php'; - $result = Plugin::parseDoc($test_path); - - $this->assertContains('SVG', $result[0]); - } - - public function testLoad() - { - Plugin::load('dummy'); - - $this->assertArrayHasKey('dummy', Plugin::$plugins); - - Plugin::enable('dummy'); - - $this->assertTrue(DUMMY_ENABLE_TEST); - - Plugin::disable('dummy'); - - $this->assertTrue(DUMMY_DISABLE_TEST); - } -} From d0c13f3a68bfde479ef93c05594f4509bdfaddb4 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sun, 3 Sep 2017 08:09:42 +0100 Subject: [PATCH 352/421] Removed csscrush_version(). Removed csscrush_add_function() (use plugin instead). --- cli.php | 2 +- docs/api/functions.md | 37 ++++++------------------ lib/CssCrush/Functions.php | 2 +- lib/CssCrush/Process.php | 2 +- lib/functions.php | 44 ----------------------------- tests/unit/CssCrush/OptionsTest.php | 3 +- tests/unit/api/apiTest.php | 20 ------------- 7 files changed, 14 insertions(+), 96 deletions(-) diff --git a/cli.php b/cli.php index cc1afa5..4c616ff 100755 --- a/cli.php +++ b/cli.php @@ -36,7 +36,7 @@ if ($args->version) { - stdout((string) csscrush_version()); + stdout((string) CssCrush\Version::detect()); exit(STATUS_OK); } diff --git a/docs/api/functions.md b/docs/api/functions.md index 599bf11..639f9fb 100644 --- a/docs/api/functions.md +++ b/docs/api/functions.md @@ -38,34 +38,6 @@ Compile a raw string of CSS string and return it. csscrush_string( string $string [, array [$options](#api--options) ] ) -*************** - -## csscrush\_add_function() - -Add custom CSS functions. - -Custom functions added this way are stored on a stack and used by any -subsequent compilations within the duration of the script. - -`csscrush_add_function( string $function_name, callable $callback = null )` - -### Parameters - - * `$function_name` Name of CSS function, or `null` to clear all functions added with `csscrush_add_function()`. - * `$callback` CSS function callback, or `null` to remove function named `$function_name`. - -`callback ( array $arguments, stdClass $context )` - - -*************** - -## csscrush_version() - -Get the library version. - -`csscrush_version()` - - *************** ## csscrush_get() @@ -94,6 +66,15 @@ Set a config setting or option default. * `$settings` Associative array of keys and values to set, or callable which argument is the object specified in `$object_name`. +*************** + +## csscrush_plugin() + +Register a plugin. + +`csscrush_plugin( string $name, callable $callback )` + + *************** ## csscrush_stat() diff --git a/lib/CssCrush/Functions.php b/lib/CssCrush/Functions.php index b365682..08d6273 100644 --- a/lib/CssCrush/Functions.php +++ b/lib/CssCrush/Functions.php @@ -49,7 +49,7 @@ public function remove($name) public function setPattern($useAll = false) { if ($useAll) { - $this->register = self::$builtins + $this->register + csscrush_add_function(); + $this->register = self::$builtins + $this->register; } $this->pattern = Functions::makePattern(array_keys($this->register)); diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 0a2dee3..ebb5901 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -183,7 +183,7 @@ protected function getBoilerplate() 'command' => $commandArgs, 'plugins' => implode(',', $this->plugins), 'version' => function () { - return csscrush_version(); + return Version::detect(); }, 'compile_time' => function () { $now = microtime(true) - Crush::$process->stat['compile_start_time']; diff --git a/lib/functions.php b/lib/functions.php index c435ffb..6f89aa3 100644 --- a/lib/functions.php +++ b/lib/functions.php @@ -137,39 +137,6 @@ function csscrush_get($object_name, $property = null) { } -/** - * Add custom CSS functions. - * - * @see docs/api/functions.md - */ -function csscrush_add_function($function_name = null, $callback = null) { - - static $stack = array(); - - if (! func_num_args()) { - return $stack; - } - - if (! $function_name) { - $stack = array(); - return; - } - - $function_name = strtolower($function_name); - if (! $callback) { - if (isset($stack[$function_name])) { - unset($stack[$function_name]); - } - } - else { - $stack[$function_name] = array( - 'callback' => $callback, - 'parse_args' => true, - ); - } -} - - /** * Add plugin. * @@ -181,17 +148,6 @@ function csscrush_plugin($name, callable $callback) { } -/** - * Get version information. - * - * @see docs/api/functions.md - */ -function csscrush_version() { - - return \CssCrush\Version::detect(); -} - - /** * Get stats from most recent compile. * diff --git a/tests/unit/CssCrush/OptionsTest.php b/tests/unit/CssCrush/OptionsTest.php index d565421..75116f2 100644 --- a/tests/unit/CssCrush/OptionsTest.php +++ b/tests/unit/CssCrush/OptionsTest.php @@ -3,6 +3,7 @@ namespace CssCrush\UnitTest; use CssCrush\Options; +use CssCrush\Version; class OptionsTest extends \PHPUnit_Framework_TestCase { @@ -42,7 +43,7 @@ public function testBoilerplate() 'newlines' => 'unix', )); - $this->assertContains(' * ' . csscrush_version(), (string) $result); + $this->assertContains(' * ' . Version::detect(), (string) $result); $this->assertContains(" * Line breaks\n * preserved\n *", (string) $result); } diff --git a/tests/unit/api/apiTest.php b/tests/unit/api/apiTest.php index 21f3b0f..0485dcb 100644 --- a/tests/unit/api/apiTest.php +++ b/tests/unit/api/apiTest.php @@ -116,24 +116,4 @@ public function testGetSet() csscrush_set('options', array('enable' => array())); } - - public function testAddFunction() - { - csscrush_add_function(null); - - $this->assertEquals(array(), csscrush_add_function()); - - csscrush_add_function('baz', function ($arguments) {return implode('-', $arguments);}); - - $result = (string) csscrush_string('.foo {bar: baz(one, two, three);}'); - $this->assertEquals('.foo{bar:one-two-three}', $result); - - $functions = csscrush_add_function(); - $this->assertTrue(is_callable($functions['baz']['callback'])); - - csscrush_add_function('baz', null); - - $functions = csscrush_add_function(); - $this->assertFalse(isset($functions['baz'])); - } } From a3f64a7d4c4c79bce490896ebeedf74c0f368d04 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sun, 3 Sep 2017 19:23:43 +0100 Subject: [PATCH 353/421] Updated syntax. --- cli.php | 18 +++++++++--------- lib/CssCrush/Color.php | 6 +++--- lib/CssCrush/Crush.php | 28 ++++++++++++++-------------- lib/CssCrush/Declaration.php | 2 +- lib/CssCrush/DeclarationList.php | 32 ++++++++++++++++---------------- lib/CssCrush/Fragment.php | 4 ++-- lib/CssCrush/Functions.php | 10 +++++----- lib/CssCrush/IO.php | 4 ++-- lib/CssCrush/IO/Watch.php | 4 ++-- lib/CssCrush/Importer.php | 4 ++-- lib/CssCrush/Iterator.php | 2 +- lib/CssCrush/Logger.php | 18 +++++++++--------- lib/CssCrush/Mixin.php | 6 +++--- lib/CssCrush/Options.php | 10 +++++----- lib/CssCrush/Regex.php | 4 ++-- lib/CssCrush/Rule.php | 10 +++++----- lib/CssCrush/Selector.php | 2 +- lib/CssCrush/SelectorAlias.php | 2 +- lib/CssCrush/SelectorList.php | 10 +++++----- lib/CssCrush/Settings.php | 4 ++-- lib/CssCrush/StringObject.php | 4 ++-- lib/CssCrush/Template.php | 6 +++--- lib/CssCrush/Tokens.php | 2 +- lib/CssCrush/Util.php | 10 +++++----- lib/functions.php | 10 +++++----- plugins/canvas.php | 4 ++-- plugins/color.php | 2 +- plugins/property-sorter.php | 2 +- plugins/rem.php | 4 ++-- plugins/svg.php | 24 ++++++++++++------------ tests/bootstrap.php | 2 +- tests/unit/CssCrush/UtilTest.php | 2 +- tests/unit/api/apiTest.php | 8 ++++---- 33 files changed, 130 insertions(+), 130 deletions(-) diff --git a/cli.php b/cli.php index 4c616ff..db77322 100755 --- a/cli.php +++ b/cli.php @@ -84,7 +84,7 @@ $options = CssCrush\Util::readConfigFile($configFile); } else { - $options = array(); + $options = []; } if ($args->pretty) { @@ -234,7 +234,7 @@ function get_stdin_contents() { function parse_list(array $option) { - $out = array(); + $out = []; foreach ($option as $arg) { if (is_string($arg)) { foreach (preg_split('~\s*,\s*~', $arg) as $item) { @@ -248,7 +248,7 @@ function parse_list(array $option) { return $out; } -function message($messages, $options = array()) { +function message($messages, $options = []) { $defaults = array( 'color' => 'b', @@ -285,7 +285,7 @@ function message($messages, $options = array()) { } extract($options + $defaults); - $out = array(); + $out = []; foreach ((array) $messages as $_label => $value) { $_label = $label ?: $_label; if ($format_label) { @@ -372,19 +372,19 @@ function get_trailing_io_args($required_value_opts) { $other_opt_patt = "~^-{1,2}([a-z0-9\-]+)?(=|$)~ix"; // Step through the args. - $filtered = array(); + $filtered = []; for ($i = 0; $i < count($trailing_args); $i++) { $current = $trailing_args[$i]; // If tests as a required value option, reset and skip next. if (preg_match($value_opt_patt, $current)) { - $filtered = array(); + $filtered = []; $i++; } // If it looks like any other kind of flag, or optional value option, reset. elseif (preg_match($other_opt_patt, $current)) { - $filtered = array(); + $filtered = []; } else { $filtered[] = $current; @@ -438,8 +438,8 @@ function parse_args() { ); // Create option strings for getopt(). - $short_opts = array(); - $long_opts = array(); + $short_opts = []; + $long_opts = []; $join_opts = function ($opts_list, $modifier) use (&$short_opts, &$long_opts) { foreach ($opts_list as $opt) { foreach (explode('|', $opt) as $arg) { diff --git a/lib/CssCrush/Color.php b/lib/CssCrush/Color.php index 0ee10af..899ac6c 100644 --- a/lib/CssCrush/Color.php +++ b/lib/CssCrush/Color.php @@ -114,7 +114,7 @@ public static function test($str) )~ixS'); } - $color_test = array(); + $color_test = []; $str = strtolower(trim($str)); // First match a hex value or the start of a function. @@ -299,7 +299,7 @@ public static function hexToRgb($hex) // Handle shortened format. if (strlen($hex) === 3) { - $long_hex = array(); + $long_hex = []; foreach (str_split($hex) as $val) { $long_hex[] = $val . $val; } @@ -391,7 +391,7 @@ public function __toString() } // R, G and B components must be integers. - $components = array(); + $components = []; foreach (($this->hslColorSpace ? $this->getRgb() : $this->value) as $index => $component) { $components[] = ($index === 3) ? $component : min(round($component), 255); } diff --git a/lib/CssCrush/Crush.php b/lib/CssCrush/Crush.php index 3d6aa29..0d2db47 100644 --- a/lib/CssCrush/Crush.php +++ b/lib/CssCrush/Crush.php @@ -30,15 +30,15 @@ public static function init() self::$config->io = 'CssCrush\IO'; // Shared resources. - self::$config->vars = array(); + self::$config->vars = []; self::$config->aliasesFile = self::$dir . '/aliases.ini'; - self::$config->aliases = array(); + self::$config->aliases = []; self::$config->bareAliases = array( - 'properties' => array(), - 'functions' => array(), - 'function_groups' => array(), - 'declarations' => array(), - 'at-rules' => array(), + 'properties' => [], + 'functions' => [], + 'function_groups' => [], + 'declarations' => [], + 'at-rules' => [], ); self::$config->options = new Options(); @@ -138,7 +138,7 @@ public static function parseAliasesFile($file) if ($section === 'declarations') { - $store = array(); + $store = []; foreach ($items as $prop_val => $aliases) { list($prop, $value) = array_map('trim', explode(':', $prop_val)); @@ -167,7 +167,7 @@ public static function parseAliasesFile($file) $group = substr($section, strlen('functions')); - $vendor_grouped_aliases = array(); + $vendor_grouped_aliases = []; foreach ($items as $func_name => $aliases) { // Assign group name to the aliasable function. @@ -207,7 +207,7 @@ public static function printLog() if (! empty(self::$process->debugLog)) { if (PHP_SAPI !== 'cli') { - $out = array(); + $out = []; foreach (self::$process->debugLog as $item) { $out[] = '
' . htmlspecialchars($item) . '
'; } @@ -259,7 +259,7 @@ public static function runStat() } } -function warning($message, $context = array()) { +function warning($message, $context = []) { Crush::$process->errors[] = $message; $logger = Crush::$config->logger; if ($logger instanceof Logger) { @@ -268,7 +268,7 @@ function warning($message, $context = array()) { $logger->warning($message, $context); } -function notice($message, $context = array()) { +function notice($message, $context = []) { Crush::$process->warnings[] = $message; $logger = Crush::$config->logger; if ($logger instanceof Logger) { @@ -277,11 +277,11 @@ function notice($message, $context = array()) { $logger->notice($message, $context); } -function debug($message, $context = array()) { +function debug($message, $context = []) { Crush::$config->logger->debug($message, $context); } -function log($message, $context = array(), $type = 'debug') { +function log($message, $context = [], $type = 'debug') { Crush::$config->logger->$type($message, $context); } diff --git a/lib/CssCrush/Declaration.php b/lib/CssCrush/Declaration.php index 07c679c..3816e97 100644 --- a/lib/CssCrush/Declaration.php +++ b/lib/CssCrush/Declaration.php @@ -117,7 +117,7 @@ public function process($parentRule) public function indexFunctions() { // Create an index of all regular functions in the value. - $functions = array(); + $functions = []; if (preg_match_all(Regex::$patt->functionTest, $this->value, $m)) { foreach ($m['func_name'] as $fn_name) { $functions[strtolower($fn_name)] = true; diff --git a/lib/CssCrush/DeclarationList.php b/lib/CssCrush/DeclarationList.php index da7f804..34dab53 100644 --- a/lib/CssCrush/DeclarationList.php +++ b/lib/CssCrush/DeclarationList.php @@ -12,14 +12,14 @@ class DeclarationList extends Iterator public $processed = false; protected $rule; - public $properties = array(); - public $canonicalProperties = array(); + public $properties = []; + public $canonicalProperties = []; // Declarations hash table for inter-rule this() referencing. - public $data = array(); + public $data = []; // Declarations hash table for external query() referencing. - public $queryData = array(); + public $queryData = []; public function __construct($declarationsString, Rule $rule) { @@ -104,8 +104,8 @@ public function index($declaration) public function updateIndex() { - $this->properties = array(); - $this->canonicalProperties = array(); + $this->properties = []; + $this->canonicalProperties = []; foreach ($this->store as $declaration) { $this->index($declaration); @@ -134,7 +134,7 @@ public function aliasProperties($vendor_context = null) return; } - $stack = array(); + $stack = []; $rule_updated = false; $regex = Regex::$patt; @@ -199,7 +199,7 @@ public function aliasFunctions($vendor_context = null) $function_alias_groups =& Crush::$process->aliases['function_groups']; // The new modified set of declarations. - $new_set = array(); + $new_set = []; $rule_updated = false; // Shim in aliased functions. @@ -219,12 +219,12 @@ public function aliasFunctions($vendor_context = null) } // Keep record of which groups have been applied. - $processed_groups = array(); + $processed_groups = []; foreach (array_keys($intersect) as $fn_name) { // Store for all the duplicated declarations. - $prefixed_copies = array(); + $prefixed_copies = []; // Grouped function aliases. if ($function_aliases[$fn_name][0] === '.') { @@ -323,7 +323,7 @@ public function aliasDeclarations($vendor_context = null) } $intersect = array_flip(array_keys($intersect)); - $new_set = array(); + $new_set = []; $rule_updated = false; foreach ($this->store as $declaration) { @@ -365,7 +365,7 @@ public function aliasDeclarations($vendor_context = null) } } - public static function parse($str, $options = array()) + public static function parse($str, $options = []) { $str = Util::stripCommentTokens($str); $lines = preg_split('~\s*;\s*~', $str, null, PREG_SPLIT_NO_EMPTY); @@ -379,7 +379,7 @@ public static function parse($str, $options = array()) 'apply_hooks' => false, ); - $pairs = array(); + $pairs = []; foreach ($lines as $line) { @@ -443,10 +443,10 @@ public function flatten() return; } - $newSet = array(); + $newSet = []; foreach ($this->store as $declaration) { if (is_array($declaration) && $declaration[0] === 'mixin') { - foreach (Mixin::merge(array(), $declaration[1], array('context' => $this->rule)) as $mixable) { + foreach (Mixin::merge([], $declaration[1], array('context' => $this->rule)) as $mixable) { if ($mixable instanceof Declaration) { $clone = clone $mixable; $clone->index = count($newSet); @@ -552,7 +552,7 @@ public function expandData($dataset, $property) } if ($trbl_fmt) { $parts = explode(' ', $value); - $placeholders = array(); + $placeholders = []; // 4 values. if (isset($parts[3])) { diff --git a/lib/CssCrush/Fragment.php b/lib/CssCrush/Fragment.php index a2f7823..0f4b6e3 100644 --- a/lib/CssCrush/Fragment.php +++ b/lib/CssCrush/Fragment.php @@ -10,7 +10,7 @@ class Fragment extends Template { public $name; - public function __construct($str, $options = array()) + public function __construct($str, $options = []) { parent::__construct($str, $options); $this->name = $options['name']; @@ -32,7 +32,7 @@ public function __invoke(array $args = null, $str = null) // Skip over same named fragments to avoid infinite recursion. if ($fragment && $name !== $this->name) { - $args = array(); + $args = []; if (isset($m['parens'][1])) { $args = Functions::parseArgs($m['parens_content'][0]); } diff --git a/lib/CssCrush/Functions.php b/lib/CssCrush/Functions.php index 08d6273..dda1c3f 100644 --- a/lib/CssCrush/Functions.php +++ b/lib/CssCrush/Functions.php @@ -25,13 +25,13 @@ class Functions 'a-adjust' => 'CssCrush\fn__a_adjust', ); - public $register = array(); + public $register = []; protected $pattern; protected $patternOptions; - public function __construct($register = array()) + public function __construct($register = []) { $this->register = $register; } @@ -116,7 +116,7 @@ public function apply($str, \stdClass $context = null) public static function parseArgs($input, $allowSpaceDelim = false) { - $options = array(); + $options = []; if ($allowSpaceDelim) { $options['regex'] = Regex::$patt->argListSplit; } @@ -135,8 +135,8 @@ public static function parseArgsSimple($input) public static function makePattern($functionNames) { - $idents = array(); - $nonIdents = array(); + $idents = []; + $nonIdents = []; foreach ($functionNames as $functionName) { if (preg_match(Regex::$patt->ident, $functionName[0])) { diff --git a/lib/CssCrush/IO.php b/lib/CssCrush/IO.php index 4f53c00..373ceed 100644 --- a/lib/CssCrush/IO.php +++ b/lib/CssCrush/IO.php @@ -158,7 +158,7 @@ public function getCacheData() $cache_data_exists = file_exists($process->cacheFile); $cache_data_file_is_writable = $cache_data_exists ? is_writable($process->cacheFile) : false; - $cache_data = array(); + $cache_data = []; if ( $cache_data_exists && @@ -178,7 +178,7 @@ public function getCacheData() else { debug('Creating cache data file.'); } - Util::filePutContents($process->cacheFile, json_encode(array()), __METHOD__); + Util::filePutContents($process->cacheFile, json_encode([]), __METHOD__); } return $cache_data; diff --git a/lib/CssCrush/IO/Watch.php b/lib/CssCrush/IO/Watch.php index bdf87de..1604ecd 100644 --- a/lib/CssCrush/IO/Watch.php +++ b/lib/CssCrush/IO/Watch.php @@ -11,7 +11,7 @@ class Watch extends IO { - public static $cacheData = array(); + public static $cacheData = []; public function getOutputFileName() { @@ -36,7 +36,7 @@ public function getCacheData() { // Clear results from earlier processes. clearstatcache(); - $this->process->cacheData = array(); + $this->process->cacheData = []; return self::$cacheData; } diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index e2a14ee..1d75832 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -25,8 +25,8 @@ public function collate() $str = ''; // Keep track of all import file info for cache data. - $mtimes = array(); - $filenames = array(); + $mtimes = []; + $filenames = []; // Resolve main input; a string of css or a file. if (isset($input->string)) { diff --git a/lib/CssCrush/Iterator.php b/lib/CssCrush/Iterator.php index 1fd9da9..dfba721 100644 --- a/lib/CssCrush/Iterator.php +++ b/lib/CssCrush/Iterator.php @@ -10,7 +10,7 @@ class Iterator implements \IteratorAggregate, \ArrayAccess, \Countable { public $store; - public function __construct($items = array()) + public function __construct($items = []) { $this->store = $items; } diff --git a/lib/CssCrush/Logger.php b/lib/CssCrush/Logger.php index 49240c2..8f2228d 100644 --- a/lib/CssCrush/Logger.php +++ b/lib/CssCrush/Logger.php @@ -15,7 +15,7 @@ class Logger * @param array $context * @return null */ - public function emergency($message, array $context = array()) + public function emergency($message, array $context = []) { $this->error($message, $context); } @@ -30,7 +30,7 @@ public function emergency($message, array $context = array()) * @param array $context * @return null */ - public function alert($message, array $context = array()) + public function alert($message, array $context = []) { $this->error($message, $context); } @@ -44,7 +44,7 @@ public function alert($message, array $context = array()) * @param array $context * @return null */ - public function critical($message, array $context = array()) + public function critical($message, array $context = []) { $this->error($message, $context); } @@ -57,7 +57,7 @@ public function critical($message, array $context = array()) * @param array $context * @return null */ - public function error($message, array $context = array()) + public function error($message, array $context = []) { trigger_error($message, E_USER_ERROR); } @@ -72,7 +72,7 @@ public function error($message, array $context = array()) * @param array $context * @return null */ - public function warning($message, array $context = array()) + public function warning($message, array $context = []) { trigger_error($message, E_USER_WARNING); } @@ -84,7 +84,7 @@ public function warning($message, array $context = array()) * @param array $context * @return null */ - public function notice($message, array $context = array()) + public function notice($message, array $context = []) { trigger_error($message, E_USER_NOTICE); } @@ -98,7 +98,7 @@ public function notice($message, array $context = array()) * @param array $context * @return null */ - public function info($message, array $context = array()) + public function info($message, array $context = []) { $this->debug($message, $context); } @@ -110,7 +110,7 @@ public function info($message, array $context = array()) * @param array $context * @return null */ - public function debug($message, array $context = array()) + public function debug($message, array $context = []) { if (! empty($context['label'])) { $label = PHP_EOL . "$label" . PHP_EOL . str_repeat('=', strlen($label)) . PHP_EOL; @@ -137,7 +137,7 @@ public function debug($message, array $context = array()) * @param array $context * @return null */ - public function log($level, $message, array $context = array()) + public function log($level, $message, array $context = []) { $log_levels = array_flip(get_class_methods(__CLASS__)); unset($log_levels['log']); diff --git a/lib/CssCrush/Mixin.php b/lib/CssCrush/Mixin.php index 810e4b2..4714b3e 100644 --- a/lib/CssCrush/Mixin.php +++ b/lib/CssCrush/Mixin.php @@ -55,7 +55,7 @@ public static function call($message, $context = null) } elseif ($mixable instanceof Mixin) { - $args = array(); + $args = []; $raw_args = isset($message_match['parens_content']) ? trim($message_match['parens_content']) : null; if ($raw_args) { $args = Util::splitDelimList($raw_args); @@ -72,11 +72,11 @@ public static function call($message, $context = null) } } - public static function merge(array $input, $message_list, $options = array()) + public static function merge(array $input, $message_list, $options = []) { $context = isset($options['context']) ? $options['context'] : null; - $mixables = array(); + $mixables = []; foreach (Util::splitDelimList($message_list) as $message) { if ($result = self::call($message, $context)) { $mixables = array_merge($mixables, $result); diff --git a/lib/CssCrush/Options.php b/lib/CssCrush/Options.php index 8083d82..aadb2d2 100644 --- a/lib/CssCrush/Options.php +++ b/lib/CssCrush/Options.php @@ -8,15 +8,15 @@ class Options { - protected $computedOptions = array(); - protected $inputOptions = array(); + protected $computedOptions = []; + protected $inputOptions = []; protected static $standardOptions = array( 'minify' => true, 'formatter' => null, 'versioning' => true, 'boilerplate' => true, - 'vars' => array(), + 'vars' => [], 'cache' => true, 'context' => null, 'import_path' => null, @@ -27,13 +27,13 @@ class Options 'vendor_target' => 'all', 'rewrite_import_urls' => true, 'plugins' => null, - 'settings' => array(), + 'settings' => [], 'stat_dump' => false, 'source_map' => false, 'newlines' => 'use-platform', ); - public function __construct(array $options = array(), Options $defaults = null) + public function __construct(array $options = [], Options $defaults = null) { $options = array_change_key_case($options, CASE_LOWER); diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php index 1928eb5..fab903e 100644 --- a/lib/CssCrush/Regex.php +++ b/lib/CssCrush/Regex.php @@ -90,7 +90,7 @@ public static function init() public static function make($pattern) { - static $cache = array(); + static $cache = []; if (isset($cache[$pattern])) { return $cache[$pattern]; @@ -105,7 +105,7 @@ public static function matchAll($patt, $subject, $offset = 0) { $count = preg_match_all($patt, $subject, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER, $offset); - return $count ? $matches : array(); + return $count ? $matches : []; } } diff --git a/lib/CssCrush/Rule.php b/lib/CssCrush/Rule.php index 931ce61..2e899a5 100644 --- a/lib/CssCrush/Rule.php +++ b/lib/CssCrush/Rule.php @@ -23,8 +23,8 @@ class Rule public $declarations; // Arugments passed via @extend. - public $extendArgs = array(); - public $extendSelectors = array(); + public $extendArgs = []; + public $extendSelectors = []; public function __construct($selectorString, $declarationsString, $traceToken = null) { @@ -88,7 +88,7 @@ public function resolveExtendables() $references =& Crush::$process->references; // Filter the extendArgs list to usable references. - $filtered = array(); + $filtered = []; foreach ($this->extendArgs as $extendArg) { if (isset($references[$extendArg->name])) { @@ -114,7 +114,7 @@ public function applyExtendables() } // Create a stack of all parent rule args. - $parentExtendArgs = array(); + $parentExtendArgs = []; foreach ($this->extendArgs as $extendArg) { $parentExtendArgs += $extendArg->pointer->extendArgs; } @@ -132,7 +132,7 @@ public function applyExtendables() // If there is a pseudo class extension create a new set accordingly. if ($extendArg->pseudo) { - $extendSelectors = array(); + $extendSelectors = []; foreach ($this->selectors->store as $selector) { $newSelector = clone $selector; $newReadable = $newSelector->appendPseudo($extendArg->pseudo); diff --git a/lib/CssCrush/Selector.php b/lib/CssCrush/Selector.php index 98facf8..108ceb3 100644 --- a/lib/CssCrush/Selector.php +++ b/lib/CssCrush/Selector.php @@ -81,7 +81,7 @@ public static function expandAliases($str) $start = $alias_call[0][1]; $length = strlen($alias_call[0][0]); - $args = array(); + $args = []; // It's a function alias if a start paren is matched. if (isset($alias_call[2])) { diff --git a/lib/CssCrush/SelectorAlias.php b/lib/CssCrush/SelectorAlias.php index e2470d4..8189d62 100644 --- a/lib/CssCrush/SelectorAlias.php +++ b/lib/CssCrush/SelectorAlias.php @@ -39,7 +39,7 @@ public function __invoke($args) case 'splat': $handler = $tokens->restore($handler, 's'); if ($args) { - $list = array(); + $list = []; foreach ($args as $arg) { $list[] = SelectorAlias::wrap( $tokens->capture(preg_replace($splat_arg_patt, $arg, $handler), 's') diff --git a/lib/CssCrush/SelectorList.php b/lib/CssCrush/SelectorList.php index e8d433c..b4c98a9 100644 --- a/lib/CssCrush/SelectorList.php +++ b/lib/CssCrush/SelectorList.php @@ -50,7 +50,7 @@ public function expand() list($full_match, $full_match_offset) = $m[0]; $before = substr($selector_string, 0, $full_match_offset); $after = substr($selector_string, strlen($full_match) + $full_match_offset); - $selectors = array(); + $selectors = []; // Allowing empty strings for more expansion possibilities. foreach (Util::splitDelimList($m['parens_content'][0], array('allow_empty_strings' => true)) as $segment) { @@ -69,9 +69,9 @@ public function expand() { if ($running_stack = $expand($selector_string)) { - $flattened_stack = array(); + $flattened_stack = []; do { - $loop_stack = array(); + $loop_stack = []; foreach ($running_stack as $selector => $bool) { $selectors = $expand($selector); if (! $selectors) { @@ -92,7 +92,7 @@ public function expand() }; } - $expanded_set = array(); + $expanded_set = []; foreach ($this->store as $original_selector) { if (stripos($original_selector->value, ':any(') !== false) { @@ -111,7 +111,7 @@ public function expand() public function merge($rawSelectors) { - $stack = array(); + $stack = []; foreach ($rawSelectors as $rawParentSelector) { foreach ($this->store as $selector) { diff --git a/lib/CssCrush/Settings.php b/lib/CssCrush/Settings.php index 9a63795..adff31a 100644 --- a/lib/CssCrush/Settings.php +++ b/lib/CssCrush/Settings.php @@ -8,9 +8,9 @@ class Settings { - protected $store = array(); + protected $store = []; - public function __construct($pairs = array()) + public function __construct($pairs = []) { foreach ($pairs as $name => $value) { $this->set($name, $value); diff --git a/lib/CssCrush/StringObject.php b/lib/CssCrush/StringObject.php index 2057873..573a66a 100644 --- a/lib/CssCrush/StringObject.php +++ b/lib/CssCrush/StringObject.php @@ -118,7 +118,7 @@ public function restore($types, $release = false, $callback = null) return $this; } - public function captureDirectives($directive, $parse_options = array()) + public function captureDirectives($directive, $parse_options = []) { if (is_array($directive)) { $directive = '(?:' . implode('|', $directive) . ')'; @@ -139,7 +139,7 @@ public function captureDirectives($directive, $parse_options = array()) $patt = Regex::make('~@(?i)' . $directive . '(?-i)\s*{{ block }}~S'); } - $captured_directives = array(); + $captured_directives = []; $this->pregReplaceCallback($patt, function ($m) use (&$captured_directives, $parse_options) { if (isset($m['name'])) { $name = $parse_options['lowercase_keys'] ? strtolower($m['name']) : $m['name']; diff --git a/lib/CssCrush/Template.php b/lib/CssCrush/Template.php index 3c8fc30..f2d1c4c 100644 --- a/lib/CssCrush/Template.php +++ b/lib/CssCrush/Template.php @@ -9,7 +9,7 @@ class Template { // Positional argument default values. - public $defaults = array(); + public $defaults = []; // The number of expected arguments. public $argCount = 0; @@ -107,8 +107,8 @@ public function getArgValue($index, &$args) public function prepare(array $args, $persist = true) { // Create table of substitutions. - $find = array(); - $replace = array(); + $find = []; + $replace = []; if ($this->argCount) { diff --git a/lib/CssCrush/Tokens.php b/lib/CssCrush/Tokens.php index 43a1475..92bccf7 100644 --- a/lib/CssCrush/Tokens.php +++ b/lib/CssCrush/Tokens.php @@ -25,7 +25,7 @@ public function __construct(array $types = null) $this->ids = new \stdClass; foreach ($types as $type) { - $this->store->$type = array(); + $this->store->$type = []; $this->ids->$type = 0; } } diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php index 4f02c32..cf00fa3 100644 --- a/lib/CssCrush/Util.php +++ b/lib/CssCrush/Util.php @@ -75,7 +75,7 @@ public static function simplifyPath($path) return $path; } - public static function resolveUserPath($path, $recovery = null, $docRoot = null) + public static function resolveUserPath($path, callable $recovery = null, $docRoot = null) { // System path. if ($realpath = realpath($path)) { @@ -98,7 +98,7 @@ public static function resolveUserPath($path, $recovery = null, $docRoot = null) $path = Crush::$config->scriptDir . '/' . $path; } - if (! file_exists($path) && is_callable($recovery)) { + if (! file_exists($path) && $recovery) { $path = $recovery($path); } $path = realpath($path); @@ -131,7 +131,7 @@ public static function normalizeWhiteSpace($str) return preg_replace($find, $replace, $str); } - public static function splitDelimList($str, $options = array()) + public static function splitDelimList($str, $options = []) { extract($options + array( 'delim' => ',', @@ -142,11 +142,11 @@ public static function splitDelimList($str, $options = array()) $str = trim($str); if (! $regex && strpos($str, $delim) === false) { - return ! $allow_empty_strings && ! strlen($str) ? array() : array($str); + return ! $allow_empty_strings && ! strlen($str) ? [] : array($str); } if ($match_count = preg_match_all(Regex::$patt->parens, $str, $matches)) { - $keys = array(); + $keys = []; foreach ($matches[0] as $index => &$value) { $keys[] = "?$index?"; } diff --git a/lib/functions.php b/lib/functions.php index 6f89aa3..2348e2e 100644 --- a/lib/functions.php +++ b/lib/functions.php @@ -11,7 +11,7 @@ * * @see docs/api/functions.md */ -function csscrush_file($file, $options = array()) { +function csscrush_file($file, $options = []) { try { Crush::$process = new CssCrush\Process($options, array('type' => 'file', 'data' => $file)); @@ -31,7 +31,7 @@ function csscrush_file($file, $options = array()) { * * @see docs/api/functions.md */ -function csscrush_tag($file, $options = array(), $tag_attributes = array()) { +function csscrush_tag($file, $options = [], $tag_attributes = []) { $file = csscrush_file($file, $options); if ($file && $file->url) { @@ -52,10 +52,10 @@ function csscrush_tag($file, $options = array(), $tag_attributes = array()) { * * @see docs/api/functions.md */ -function csscrush_inline($file, $options = array(), $tag_attributes = array()) { +function csscrush_inline($file, $options = [], $tag_attributes = []) { if (! is_array($options)) { - $options = array(); + $options = []; } if (! isset($options['boilerplate'])) { $options['boilerplate'] = false; @@ -80,7 +80,7 @@ function csscrush_inline($file, $options = array(), $tag_attributes = array()) { * * @see docs/api/functions.md */ -function csscrush_string($string, $options = array()) { +function csscrush_string($string, $options = []) { if (! isset($options['boilerplate'])) { $options['boilerplate'] = false; diff --git a/plugins/canvas.php b/plugins/canvas.php index 812fa7d..75e3343 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -234,7 +234,7 @@ function canvas_fn_linear_gradient($input, $context) { // Create fill object. $fill = new stdClass(); - $fill->stops = array(); + $fill->stops = []; $fill->direction = $direction; canvas_set_fill_dims($fill, $context->canvas); @@ -631,7 +631,7 @@ function canvas_requirements() { */ class Canvas { - public $image, $fills = array(), $filters = array(); + public $image, $fills = [], $filters = []; public function __destruct() { diff --git a/plugins/color.php b/plugins/color.php index b32696a..30cedaa 100644 --- a/plugins/color.php +++ b/plugins/color.php @@ -27,7 +27,7 @@ function color_capture($process) { if ($captured_keywords) { $native_keywords = Color::getKeywords(); - $custom_keywords = array(); + $custom_keywords = []; $process->colorKeywords = $native_keywords; foreach ($captured_keywords as $key => $value) { diff --git a/plugins/property-sorter.php b/plugins/property-sorter.php index 7c8aace..8930d66 100644 --- a/plugins/property-sorter.php +++ b/plugins/property-sorter.php @@ -106,7 +106,7 @@ function &property_sorter_get_table () { return $GLOBALS['CSSCRUSH_PROPERTY_SORT_ORDER_CACHE']; } - $table = array(); + $table = []; // Nothing cached, check for a user-defined table. if (isset($GLOBALS['CSSCRUSH_PROPERTY_SORT_ORDER'])) { diff --git a/plugins/rem.php b/plugins/rem.php index ef6b362..2a65e2a 100644 --- a/plugins/rem.php +++ b/plugins/rem.php @@ -40,7 +40,7 @@ function rem(Rule $rule) { // Select the length match pattern depending on mode. $length_patt = $mode === 'rem-fallback' ? $rem_patt : $px_patt; - $new_set = array(); + $new_set = []; $rule_updated = false; foreach ($rule->declarations as $declaration) { if ( @@ -54,7 +54,7 @@ function rem(Rule $rule) { // Value has matching length components. $find = $m[0]; - $replace = array(); + $replace = []; $numbers = $m[1]; switch ($mode) { diff --git a/plugins/svg.php b/plugins/svg.php index d49de7c..12c1368 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -150,18 +150,18 @@ function svg_generator($input, $fn_name) { $element = (object) array( 'tag' => $schemas[$type]['tag'], 'fills' => array( - 'gradients' => array(), - 'patterns' => array(), + 'gradients' => [], + 'patterns' => [], ), - 'filters' => array(), - 'data' => array(), - 'attrs' => array(), - 'styles' => array(), + 'filters' => [], + 'data' => [], + 'attrs' => [], + 'styles' => [], 'svg_attrs' => array( 'xmlns' => 'http://www.w3.org/2000/svg', ), - 'svg_styles' => array(), - 'face_styles' => array(), + 'svg_styles' => [], + 'face_styles' => [], ); // Filter off prefixed properties that are for the svg element or @font-face. @@ -471,7 +471,7 @@ function svg_text($element) { */ function svg_starpath($cx, $cy, $points, $outer_r, $inner_r = null, $twist = 0, $orient = 'point') { - $d = array(); + $d = []; // Enforce minimum number of points. $points = max(3, $points); @@ -664,7 +664,7 @@ function svg_render($element) { ); foreach ($styles_data as $selector => $declarations) { if ($declarations) { - $out = array(); + $out = []; foreach ($declarations as $property => $value) { $out[] = "$property:$value"; } @@ -1027,8 +1027,8 @@ function create_svg_radial_gradient($input) { function parse_gradient_color_stops(array $color_stop_args) { - $offsets = array(); - $colors = array(); + $offsets = []; + $colors = []; $offset_patt = '~ +([\d\.]+%)$~'; $last_index = count($color_stop_args) - 1; diff --git a/tests/bootstrap.php b/tests/bootstrap.php index c0bfb42..5e86877 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -14,7 +14,7 @@ namespace CssCrush\UnitTest { - function bootstrap_process($options = array()) + function bootstrap_process($options = []) { $process = \CssCrush\Crush::$process = new \CssCrush\Process($options); $process->preCompile(); diff --git a/tests/unit/CssCrush/UtilTest.php b/tests/unit/CssCrush/UtilTest.php index 04536cd..9deb517 100644 --- a/tests/unit/CssCrush/UtilTest.php +++ b/tests/unit/CssCrush/UtilTest.php @@ -80,7 +80,7 @@ public function testNormalizeWhiteSpace() public function testSplitDelimList() { $this->assertEquals(array('foo(1,2)','3','4'), Util::splitDelimList("foo(1,2), 3,4")); - $this->assertEquals(array(), Util::splitDelimList(" ; ; ", array('delim' => ';'))); + $this->assertEquals([], Util::splitDelimList(" ; ; ", array('delim' => ';'))); $this->assertEquals(array('', ''), Util::splitDelimList(" , ", array('allow_empty_strings' => true))); } diff --git a/tests/unit/api/apiTest.php b/tests/unit/api/apiTest.php index 0485dcb..fa98288 100644 --- a/tests/unit/api/apiTest.php +++ b/tests/unit/api/apiTest.php @@ -88,9 +88,9 @@ public function testStat() $this->assertEquals(2, $stats['rule_count']); $this->assertArrayHasKey('timestamp', $stats['vars']); unset($stats['vars']['timestamp']); - $this->assertEquals(array(), $stats['vars']); - $this->assertEquals(array(), $stats['vars']); - $this->assertEquals(array(), $stats['errors']); + $this->assertEquals([], $stats['vars']); + $this->assertEquals([], $stats['vars']); + $this->assertEquals([], $stats['errors']); $this->assertTrue(isset($stats['compile_time'])); } @@ -114,6 +114,6 @@ public function testGetSet() $this->assertContains('property-sorter', csscrush_get('options', 'enable')); - csscrush_set('options', array('enable' => array())); + csscrush_set('options', array('enable' => [])); } } From 8654db9176c38dcd8345a232638762f86d3c48a9 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sun, 3 Sep 2017 20:41:52 +0100 Subject: [PATCH 354/421] Updated docs and changelog. Added JS api. --- .eslintrc.js | 58 ++++++++++ CHANGELOG.md | 18 +++- docs/core/auto-prefixing.md | 3 +- docs/{plugins => core}/loop.md | 5 + docs/getting-started/js.md | 24 +++++ docs/getting-started/{download.md => php.md} | 4 +- index.js | 107 +++++++++++++++++++ 7 files changed, 212 insertions(+), 7 deletions(-) create mode 100644 .eslintrc.js rename docs/{plugins => core}/loop.md (95%) create mode 100644 docs/getting-started/js.md rename docs/getting-started/{download.md => php.md} (96%) create mode 100644 index.js diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..3d39a94 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,58 @@ +module.exports = { + "env": { + "es6": true, + "node": true + }, + "parserOptions": { + "ecmaVersion": 8 + }, + "extends": "eslint:recommended", + "rules": { + "curly": 2, + "yoda": 2, + "no-console": 0, + "no-var": 2, + "no-tabs": 2, + "no-trailing-spaces": 2, + "no-dupe-keys": 2, + "no-else-return": 2, + "no-useless-call": 2, + "no-useless-return": 2, + "no-unused-expressions": [2, {"allowShortCircuit": true}], + "no-lonely-if": 2, + "arrow-parens": [2, "as-needed"], + "arrow-spacing": 2, + "object-shorthand": [2, "properties"], + "no-empty": [2, { + "allowEmptyCatch": true + }], + "eol-last": [2, "always"], + "no-multiple-empty-lines": [2, {"max": 2, "maxEOF": 1, "maxBOF": 1}], + "quotes": [2, "single", {"avoidEscape": true, "allowTemplateLiterals": true}], + "keyword-spacing": 2, + "space-before-blocks": 2, + "linebreak-style": [2, "unix"], + "object-curly-spacing": [2, "never"], + "array-bracket-spacing": [2, "never"], + "brace-style": [2, "stroustrup"], + "semi": [2, "always"], + "quote-props": [2, "as-needed"], + "comma-dangle": [2, { + "objects": "always-multiline", + "functions": "never", + }], + "comma-spacing": [2, { + "before": false, + "after": true + }], + "key-spacing": [2, { + "beforeColon": false, + "afterColon": true + }], + "space-before-function-paren": [2, { + "anonymous": "always", + "asyncArrow": "always", + "named": "never" + }], + } +}; diff --git a/CHANGELOG.md b/CHANGELOG.md index e0df7de..b35040a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ -## 3.0.0-beta (2017-08) +## 3.0.0-prerelease +* Raise php requirement to >= 5.6 +* Removed `csscrush_version()` +* Removed `csscrush_add_function()` (can use plugin instead). +* Added `csscrush_plugin()` with simplified plugin api. +* Added `import_path` option. Additional paths to search when resolving relative imports. +* Updated vendor aliases. +* Moved loop plugin to core. +* Remove `@in` directive. +* Remove legacy IE plugins. +* Remove hsl2hex, initial, noise and text-align plugins. +* Combined svg plugins (svg-gradients and svg). + + +******************************************************************** ## 2.4.0 (2015-07-30) @@ -7,7 +21,6 @@ * Updated vendor aliases. * Various fixes and under the hood improvements. - ## 2.3.0 (2015-02-16) * Added support for function calls on media query lists. @@ -20,7 +33,6 @@ * Improvements to logging and error reporting. * Various bug fixes. - ## 2.2.0 (2014-06-17) * Rule nesting now works without `@in` directives. diff --git a/docs/core/auto-prefixing.md b/docs/core/auto-prefixing.md index 825e670..289db3b 100644 --- a/docs/core/auto-prefixing.md +++ b/docs/core/auto-prefixing.md @@ -4,9 +4,8 @@ }--> -Vendor prefixes for properties, functions, @-rules and even full declarations are **automatically generated** – based on [trusted](http://caniuse.com) [sources](http://developer.mozilla.org/en-US/docs/CSS/CSS_Reference) – so you can maintain cross-browser support while keeping your source code clean and easy to maintain. +Vendor prefixes for properties, functions, @-rules and declarations are **automatically generated** – based on [trusted](http://caniuse.com) [sources](http://developer.mozilla.org/en-US/docs/CSS/CSS_Reference) – so you can maintain cross-browser support while keeping your source code clean and easy to maintain. -In some cases (e.g. CSS3 gradients) final syntax is incompatible with older prefixed syntax. In these cases the old syntax is polyfilled so you can use the correct syntax while preserving full support for older implementations. ```crush .foo { diff --git a/docs/plugins/loop.md b/docs/core/loop.md similarity index 95% rename from docs/plugins/loop.md rename to docs/core/loop.md index 7af935d..66ae6f7 100644 --- a/docs/plugins/loop.md +++ b/docs/core/loop.md @@ -1,3 +1,8 @@ + For...in loops with lists and generator functions. diff --git a/docs/getting-started/js.md b/docs/getting-started/js.md new file mode 100644 index 0000000..4631be7 --- /dev/null +++ b/docs/getting-started/js.md @@ -0,0 +1,24 @@ + + +```shell +npm install csscrush +``` + +All methods can take the standard options (camelCase) as the second argument. + +```php +const csscrush = require('csscrush'); + +// Compile. Returns promise. +csscrush.file('./styles.css', {sourceMap: true}); + +// Compile string of CSS. Returns promise. +csscrush.string('* {box-sizing: border-box;}'); + +// Compile and watch file. Returns event emitter (triggers 'data' on compile). +csscrush.watch('./styles.css'); +``` diff --git a/docs/getting-started/download.md b/docs/getting-started/php.md similarity index 96% rename from docs/getting-started/download.md rename to docs/getting-started/php.md index 44073cf..5be00ff 100644 --- a/docs/getting-started/download.md +++ b/docs/getting-started/php.md @@ -1,8 +1,9 @@ + If you're using [Composer](http://getcomposer.org) you can use Crush in your project with the following line in your terminal: ```shell @@ -14,4 +15,3 @@ If you're not using Composer yet just download the library ([zip](http://github ```php ``` - diff --git a/index.js b/index.js new file mode 100644 index 0000000..c40a21c --- /dev/null +++ b/index.js @@ -0,0 +1,107 @@ +const childProcess = require('child_process'); +const path = require('path'); +const EventEmitter = require('events'); +const cliPath = path.resolve(__dirname, './bin/csscrush'); + +const self = module.exports = {}; + +class Process extends EventEmitter { + exec(options) { + return new Promise(resolve => { + let command = this.assembleCommand(options); + let {stdIn} = options; + if (stdIn) { + command = `echo '${stdIn.replace(/'/g, "\\'")}' | ${command}`; + } + childProcess.exec(command, (error, stdout, stderr) => { + process.stderr.write(stderr.toString()); + if (error) { + return resolve(false); + } + let stdOut = stdout.toString(); + if (stdIn) { + process.stdout.write(stdOut); + } + return resolve(stdOut || true); + }); + }); + } + + watch(options) { + options.watch = true; + const command = this.assembleCommand(options); + const proc = childProcess.exec(command); + proc.stderr.on('data', msg => { + process.stderr.write(msg.toString()); + this.emit('data', msg.toString()); + }); + return this; + } + + assembleCommand(options) { + return `${cliPath} ${this.stringifyOptions(options)}`; + } + + stringifyOptions(options) { + const args = []; + options = Object.assign({}, options); + for (let name in options) { + // Normalize to hypenated case. + let cssCase = name.replace(/[A-Z]/g, m => `-${m.toLowerCase()}`); + if (name !== cssCase) { + options[cssCase] = options[name]; + delete options[name]; + name = cssCase; + } + let value = options[name]; + switch (name) { + // Booleans. + case 'watch': // fallthrough + case 'source-map': // fallthrough + case 'boilerplate': // fallthrough + if (value) { + args.push(`--${name}`); + } + break; + case 'minify': + if (! value) { + args.push(`--pretty`); + } + break; + // Array/list values. + case 'vendor-target': // fallthrough + case 'plugins': // fallthrough + case 'import-path': + if (value) { + value = (Array.isArray(value) ? value : [value]).join(','); + args.push(`--${name}=${value}`); + } + break; + // String values. + case 'newlines': // fallthrough + case 'formatter': // fallthrough + case 'input': // fallthrough + case 'context': // fallthrough + case 'output': + if (value) { + args.push(`--${name}='${value}'`); + } + break; + } + } + return args.join(' '); + } +} + +self.watch = (file, options={}) => { + options.input = file; + return (new Process()).watch(options); +}; +self.file = (file, options={}) => { + options.input = file; + return (new Process()).exec(options); +}; +self.string = (string, options={}) => { + options.stdIn = string; + return (new Process()).exec(options); +}; From 99248d3a625f8bc9796f46756988aff637867975 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sun, 3 Sep 2017 20:48:41 +0100 Subject: [PATCH 355/421] Removed percent function. --- CHANGELOG.md | 7 +++--- docs/core/functions/percent.md | 29 ---------------------- lib/CssCrush/Functions.php | 44 ---------------------------------- 3 files changed, 4 insertions(+), 76 deletions(-) delete mode 100644 docs/core/functions/percent.md diff --git a/CHANGELOG.md b/CHANGELOG.md index b35040a..019a75a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,10 +7,11 @@ * Added `import_path` option. Additional paths to search when resolving relative imports. * Updated vendor aliases. * Moved loop plugin to core. -* Remove `@in` directive. -* Remove legacy IE plugins. -* Remove hsl2hex, initial, noise and text-align plugins. +* Removed `@in` directive. +* Removed legacy IE plugins. +* Removed hsl2hex, initial, noise and text-align plugins. * Combined svg plugins (svg-gradients and svg). +* Removed `percent` function. ******************************************************************** diff --git a/docs/core/functions/percent.md b/docs/core/functions/percent.md deleted file mode 100644 index e32a730..0000000 --- a/docs/core/functions/percent.md +++ /dev/null @@ -1,29 +0,0 @@ - - -Calculate a percentage value based on two given values. - -percent( *value1*, *value2* [, *precision* = 5] ) - -## Parameters - -* *`value1`* Number -* *`value2`* Number -* *`precision`* Integer The number of decimal places to round to. Defaults to 5 - -## Returns - -*`value1`* as a percentage of *`value2`*. - -## Examples - -```css -width: percent( 20, 960 ); -``` - -```css -width: 2.08333%; -``` \ No newline at end of file diff --git a/lib/CssCrush/Functions.php b/lib/CssCrush/Functions.php index dda1c3f..77260bb 100644 --- a/lib/CssCrush/Functions.php +++ b/lib/CssCrush/Functions.php @@ -15,8 +15,6 @@ class Functions // These functions can be any order. 'math' => 'CssCrush\fn__math', - 'percent' => 'CssCrush\fn__percent', - 'pc' => 'CssCrush\fn__percent', 'hsla-adjust' => 'CssCrush\fn__hsla_adjust', 'hsl-adjust' => 'CssCrush\fn__hsl_adjust', 'h-adjust' => 'CssCrush\fn__h_adjust', @@ -201,48 +199,6 @@ function fn__math($input) { return ($result === false ? 0 : round($result, 5)) . $unit; } -function fn__percent($input) { - - // Strip non-numeric and non delimiter characters - $input = preg_replace('~[^\d\.\s,]~S', '', $input); - - $args = preg_split(Regex::$patt->argListSplit, $input, -1, PREG_SPLIT_NO_EMPTY); - - // Use precision argument if it exists, use default otherwise - $precision = isset($args[2]) ? $args[2] : 5; - - // Output zero on failure - $result = 0; - - // Need to check arguments or we may see divide by zero errors - if (count($args) > 1 && ! empty($args[0]) && ! empty($args[1])) { - - // Use bcmath if it's available for higher precision - - // Arbitary high precision division - if (function_exists('bcdiv')) { - $div = bcdiv($args[0], $args[1], 25); - } - else { - $div = $args[0] / $args[1]; - } - - // Set precision percentage value - if (function_exists('bcmul')) { - $result = bcmul((string) $div, '100', $precision); - } - else { - $result = round($div * 100, $precision); - } - - // Trim unnecessary zeros and decimals - $result = trim((string) $result, '0'); - $result = rtrim($result, '.'); - } - - return $result . '%'; -} - function fn__hsla_adjust($input) { list($color, $h, $s, $l, $a) = array_pad(Functions::parseArgs($input, true), 5, 0); return Color::test($color) ? Color::colorAdjust($color, array($h, $s, $l, $a)) : ''; From 6df9e9cdb2bb197c1a544b0813c6d8f1321496e2 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 4 Sep 2017 14:12:16 +0100 Subject: [PATCH 356/421] Set to beta. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 75ac7eb..f6860db 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "csscrush", - "version": "3.0.0-alpha.0", + "version": "3.0.0-beta.0", "description": "CSS-Crush, CSS preprocessor", "repository": { "type": "git", From ba87d6d1d337ef5fa3353138f9c28f99a6f7daf2 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 4 Sep 2017 14:12:38 +0100 Subject: [PATCH 357/421] Bump to 3.0.0-beta.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f6860db..b9ff69a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "csscrush", - "version": "3.0.0-beta.0", + "version": "3.0.0-beta.1", "description": "CSS-Crush, CSS preprocessor", "repository": { "type": "git", From 4d083ccd1fefffd4419b9c06824f176f646114ca Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 23 Oct 2017 16:34:54 +0100 Subject: [PATCH 358/421] Fixed issue with ambiguous import paths. --- lib/CssCrush/Importer.php | 17 ++++++++++++++--- lib/CssCrush/Url.php | 7 +++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index 1d75832..2f76742 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -70,9 +70,17 @@ public function collate() $import->path = realpath($process->docRoot . $import->url->value); } else { - $searchPaths = array_merge([$input->dir], $options->import_path ?: []); - foreach ($searchPaths as $searchPath) { - $candidate = "$searchPath/{$import->url->value}"; + $url =& $import->url; + $candidates = ["$input->dir/$url->value"]; + + // If `import_path` option is set implicit relative urls + // are additionally searched under specified import path(s). + if (is_array($options->import_path) && $url->isRelativeImplicit()) { + foreach ($options->import_path as $importPath) { + $candidates[] = "$importPath/$url->originalValue"; + } + } + foreach ($candidates as $candidate) { if (file_exists($candidate)) { $import->path = realpath($candidate); break; @@ -87,6 +95,9 @@ public function collate() if ($import->path && ! is_readable($import->path)) { $errDesc = 'is not readable'; } + if (! empty($process->sources)) { + $errDesc .= " (from within {$process->sources[0]})"; + } notice("@import 'http://github.com/%7B$import-%3Eurl-%3Evalue%7D' $errDesc"); $str = substr_replace($str, '', $match_start, $match_len); continue; diff --git a/lib/CssCrush/Url.php b/lib/CssCrush/Url.php index 0ad3aec..da8acbb 100644 --- a/lib/CssCrush/Url.php +++ b/lib/CssCrush/Url.php @@ -18,6 +18,7 @@ class Url public $noRewrite; public $convertToData; public $value; + public $originalValue; public function __construct($raw_value) { @@ -28,6 +29,7 @@ public function __construct($raw_value) $this->value = $raw_value; } + $this->originalValue = $this->value; $this->evaluate(); } @@ -94,6 +96,11 @@ public function evaluate() return $this; } + public function isRelativeImplicit() + { + return $this->isRelative && preg_match('~^([\w$-]|\.[^\/.])~', $this->originalValue); + } + public function getAbsolutePath() { $path = false; From fdcd670109c8791339f087c5f22dc5e65629f46e Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 24 Oct 2017 11:18:33 +0100 Subject: [PATCH 359/421] Added .js error event. --- index.js | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index c40a21c..8a505da 100644 --- a/index.js +++ b/index.js @@ -31,9 +31,26 @@ class Process extends EventEmitter { options.watch = true; const command = this.assembleCommand(options); const proc = childProcess.exec(command); + + // Emitting 'error' events from EventEmitter without + // any error listener will throw uncaught exception. + this.on('error', () => {}); + proc.stderr.on('data', msg => { - process.stderr.write(msg.toString()); - this.emit('data', msg.toString()); + msg = msg.toString(); + process.stderr.write(msg); + + let errorText = msg.replace(/\x1B\[[^m]*m/g, '').trim(); + let errorMatch = /^(WARNING|ERROR)\:\s*(.+)/i.exec(errorText); + if (errorMatch) { + let [, severity, message] = errorMatch; + let error = new Error(message); + error.severity = severity.toLowerCase(); + this.emit('error', error); + } + else { + this.emit('data', msg); + } }); return this; } From eee0e271e363709cb7f4480afb64cf9ab76c29b0 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 24 Oct 2017 15:24:15 +0100 Subject: [PATCH 360/421] math() now infers unit if none specified. --- lib/CssCrush/Functions.php | 8 ++++++++ lib/CssCrush/Process.php | 2 +- tests/unit/CssCrush/OptionsTest.php | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/CssCrush/Functions.php b/lib/CssCrush/Functions.php index 77260bb..e962217 100644 --- a/lib/CssCrush/Functions.php +++ b/lib/CssCrush/Functions.php @@ -180,6 +180,14 @@ function fn__math($input) { array(M_PI), $expression); + // If no unit is specified scan expression. + if (! $unit) { + $numPatt = Regex::$classes->number; + if (preg_match("~\b{$numPatt}(?[A-Za-z]{2,4}\b|%)~", $expression, $m)) { + $unit = $m['unit']; + } + } + // Filter expression so it's just characters necessary for simple math. $expression = preg_replace("~[^.0-9/*()+-]~S", '', $expression); diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index ebb5901..655f460 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -1004,7 +1004,7 @@ public function compile() public function generateSourceMap() { $this->sourceMap = [ - 'version' => '3', + 'version' => 3, 'file' => $this->output->filename, 'sources' => [], ]; diff --git a/tests/unit/CssCrush/OptionsTest.php b/tests/unit/CssCrush/OptionsTest.php index 75116f2..d6d0e3e 100644 --- a/tests/unit/CssCrush/OptionsTest.php +++ b/tests/unit/CssCrush/OptionsTest.php @@ -83,7 +83,7 @@ public function testSourceMaps() csscrush_file($this->testFile, array('source_map' => true)); $source_map_contents = file_get_contents("$this->testFile.crush.css.map"); - $this->assertRegExp('~"version": ?"3",~', $source_map_contents); + $this->assertRegExp('~"version": ?3,~', $source_map_contents); } public function testAdvancedMinify() From 4741f92e3137267fd9020880c8210cf98aedb50d Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 6 Nov 2017 17:02:48 +0000 Subject: [PATCH 361/421] Bump to 3.0.0-beta.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b9ff69a..b297a1d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "csscrush", - "version": "3.0.0-beta.1", + "version": "3.0.0-beta.2", "description": "CSS-Crush, CSS preprocessor", "repository": { "type": "git", From a9af5f56b81e4b122538acdebc0f3af154b92097 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 12 Dec 2017 09:39:26 +0000 Subject: [PATCH 362/421] Added better event data to watch event emitters. --- .eslintrc.js | 6 +- index.js | 22 +- package-lock.json | 1146 +++++++++++++++++++++++++++++++++++++++++++++ package.json | 5 +- 4 files changed, 1170 insertions(+), 9 deletions(-) create mode 100644 package-lock.json diff --git a/.eslintrc.js b/.eslintrc.js index 3d39a94..9a973b4 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -4,7 +4,10 @@ module.exports = { "node": true }, "parserOptions": { - "ecmaVersion": 8 + "ecmaVersion": 8, + "ecmaFeatures": { + "experimentalObjectRestSpread": true, + }, }, "extends": "eslint:recommended", "rules": { @@ -18,6 +21,7 @@ module.exports = { "no-else-return": 2, "no-useless-call": 2, "no-useless-return": 2, + "no-control-regex": 0, "no-unused-expressions": [2, {"allowShortCircuit": true}], "no-lonely-if": 2, "arrow-parens": [2, "as-needed"], diff --git a/index.js b/index.js index 8a505da..0eccad5 100644 --- a/index.js +++ b/index.js @@ -39,17 +39,25 @@ class Process extends EventEmitter { proc.stderr.on('data', msg => { msg = msg.toString(); process.stderr.write(msg); + msg = msg.replace(/\x1B\[[^m]*m/g, '').trim(); - let errorText = msg.replace(/\x1B\[[^m]*m/g, '').trim(); - let errorMatch = /^(WARNING|ERROR)\:\s*(.+)/i.exec(errorText); - if (errorMatch) { - let [, severity, message] = errorMatch; - let error = new Error(message); - error.severity = severity.toLowerCase(); + let [, signal, detail] = /^([A-Z]+):\s*(.+)/i.exec(msg) || []; + let {input, output} = options; + let eventData = { + signal, + options: { + input: input ? path.resolve(input) : null, + output: output ? path.resolve(output) : null, + }, + }; + + if (/^(WARNING|ERROR)$/.test(signal)) { + let error = new Error(detail); + Object.assign(error, eventData, {severity: signal.toLowerCase()}); this.emit('error', error); } else { - this.emit('data', msg); + this.emit('data', {message: detail, ...eventData}); } }); return this; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..8a67003 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1146 @@ +{ + "name": "csscrush", + "version": "3.0.0-beta.2", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "acorn": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.2.1.tgz", + "integrity": "sha512-jG0u7c4Ly+3QkkW18V+NRDN+4bWHdln30NL1ZL2AvFZZmQe/BfopYCtghCKKVBUSetZ4QKcyA0pY6/4Gw8Pv8w==", + "dev": true + }, + "acorn-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", + "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", + "dev": true, + "requires": { + "acorn": "3.3.0" + }, + "dependencies": { + "acorn": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", + "dev": true + } + } + }, + "ajv": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.1.tgz", + "integrity": "sha1-s4u4h22ehr7plJVqBOch6IskjrI=", + "dev": true, + "requires": { + "co": "4.6.0", + "fast-deep-equal": "1.0.0", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.3.1" + } + }, + "ajv-keywords": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz", + "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=", + "dev": true + }, + "ansi-escapes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.0.0.tgz", + "integrity": "sha512-O/klc27mWNUigtv0F8NJWbLF00OcegQalkqKURWdosW08YZKi4m6CnSUSvIZG1otNJbTWhN01Hhz389DW7mvDQ==", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "argparse": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", + "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=", + "dev": true, + "requires": { + "sprintf-js": "1.0.3" + } + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "1.0.3" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "1.1.3", + "esutils": "2.0.2", + "js-tokens": "3.0.2" + }, + "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + } + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", + "dev": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "caller-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", + "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", + "dev": true, + "requires": { + "callsites": "0.2.0" + } + }, + "callsites": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", + "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", + "dev": true + }, + "chalk": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", + "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", + "dev": true, + "requires": { + "ansi-styles": "3.2.0", + "escape-string-regexp": "1.0.5", + "supports-color": "4.5.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", + "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", + "dev": true, + "requires": { + "color-convert": "1.9.1" + } + }, + "supports-color": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", + "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "dev": true, + "requires": { + "has-flag": "2.0.0" + } + } + } + }, + "chardet": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", + "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", + "dev": true + }, + "circular-json": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", + "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", + "dev": true + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "2.0.0" + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", + "dev": true + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "color-convert": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", + "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", + "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", + "dev": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.3", + "typedarray": "0.0.6" + } + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "requires": { + "lru-cache": "4.1.1", + "shebang-command": "1.2.0", + "which": "1.3.0" + } + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "del": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", + "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", + "dev": true, + "requires": { + "globby": "5.0.0", + "is-path-cwd": "1.0.0", + "is-path-in-cwd": "1.0.0", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "rimraf": "2.6.2" + } + }, + "doctrine": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.0.2.tgz", + "integrity": "sha512-y0tm5Pq6ywp3qSTZ1vPgVdAnbDEoeoc5wlOHXoY1c4Wug/a7JvqHIl7BTvwodaHmejWkK/9dSb3sCYfyo/om8A==", + "dev": true, + "requires": { + "esutils": "2.0.2" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "eslint": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.12.1.tgz", + "integrity": "sha512-28hOYej+NZ/R5H1yMvyKa1+bPlu+fnsIAQffK6hxXgvmXnImos2bA5XfCn5dYv2k2mrKj+/U/Z4L5ICWxC7TQw==", + "dev": true, + "requires": { + "ajv": "5.5.1", + "babel-code-frame": "6.26.0", + "chalk": "2.3.0", + "concat-stream": "1.6.0", + "cross-spawn": "5.1.0", + "debug": "3.1.0", + "doctrine": "2.0.2", + "eslint-scope": "3.7.1", + "espree": "3.5.2", + "esquery": "1.0.0", + "estraverse": "4.2.0", + "esutils": "2.0.2", + "file-entry-cache": "2.0.0", + "functional-red-black-tree": "1.0.1", + "glob": "7.1.2", + "globals": "11.1.0", + "ignore": "3.3.7", + "imurmurhash": "0.1.4", + "inquirer": "3.3.0", + "is-resolvable": "1.0.0", + "js-yaml": "3.10.0", + "json-stable-stringify-without-jsonify": "1.0.1", + "levn": "0.3.0", + "lodash": "4.17.4", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "natural-compare": "1.4.0", + "optionator": "0.8.2", + "path-is-inside": "1.0.2", + "pluralize": "7.0.0", + "progress": "2.0.0", + "require-uncached": "1.0.3", + "semver": "5.4.1", + "strip-ansi": "4.0.0", + "strip-json-comments": "2.0.1", + "table": "4.0.2", + "text-table": "0.2.0" + } + }, + "eslint-scope": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz", + "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=", + "dev": true, + "requires": { + "esrecurse": "4.2.0", + "estraverse": "4.2.0" + } + }, + "espree": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.2.tgz", + "integrity": "sha512-sadKeYwaR/aJ3stC2CdvgXu1T16TdYN+qwCpcWbMnGJ8s0zNWemzrvb2GbD4OhmJ/fwpJjudThAlLobGbWZbCQ==", + "dev": true, + "requires": { + "acorn": "5.2.1", + "acorn-jsx": "3.0.1" + } + }, + "esprima": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", + "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", + "dev": true + }, + "esquery": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.0.tgz", + "integrity": "sha1-z7qLV9f7qT8XKYqKAGoEzaE9gPo=", + "dev": true, + "requires": { + "estraverse": "4.2.0" + } + }, + "esrecurse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.0.tgz", + "integrity": "sha1-+pVo2Y04I/mkHZHpAtyrnqblsWM=", + "dev": true, + "requires": { + "estraverse": "4.2.0", + "object-assign": "4.1.1" + } + }, + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "external-editor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.1.0.tgz", + "integrity": "sha512-E44iT5QVOUJBKij4IIV3uvxuNlbKS38Tw1HiupxEIHPv9qtC2PrDYohbXV5U+1jnfIXttny8gUhj+oZvflFlzA==", + "dev": true, + "requires": { + "chardet": "0.4.2", + "iconv-lite": "0.4.19", + "tmp": "0.0.33" + } + }, + "fast-deep-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", + "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "1.0.5" + } + }, + "file-entry-cache": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", + "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", + "dev": true, + "requires": { + "flat-cache": "1.3.0", + "object-assign": "4.1.1" + } + }, + "flat-cache": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.0.tgz", + "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=", + "dev": true, + "requires": { + "circular-json": "0.3.3", + "del": "2.2.2", + "graceful-fs": "4.1.11", + "write": "0.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "globals": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.1.0.tgz", + "integrity": "sha512-uEuWt9mqTlPDwSqi+sHjD4nWU/1N+q0fiWI9T1mZpD2UENqX20CFD5T/ziLZvztPaBKl7ZylUi1q6Qfm7E2CiQ==", + "dev": true + }, + "globby": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", + "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", + "dev": true, + "requires": { + "array-union": "1.0.2", + "arrify": "1.0.1", + "glob": "7.1.2", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" + } + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "dev": true + }, + "iconv-lite": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", + "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==", + "dev": true + }, + "ignore": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.7.tgz", + "integrity": "sha512-YGG3ejvBNHRqu0559EOxxNFihD0AjpvHlC/pdGKd3X3ofe+CoJkYazwNJYTNebqpPKN+VVQbh4ZFn1DivMNuHA==", + "dev": true + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "inquirer": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", + "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", + "dev": true, + "requires": { + "ansi-escapes": "3.0.0", + "chalk": "2.3.0", + "cli-cursor": "2.1.0", + "cli-width": "2.2.0", + "external-editor": "2.1.0", + "figures": "2.0.0", + "lodash": "4.17.4", + "mute-stream": "0.0.7", + "run-async": "2.3.0", + "rx-lite": "4.0.8", + "rx-lite-aggregates": "4.0.8", + "string-width": "2.1.1", + "strip-ansi": "4.0.0", + "through": "2.3.8" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", + "dev": true + }, + "is-path-in-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz", + "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=", + "dev": true, + "requires": { + "is-path-inside": "1.0.1" + } + }, + "is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "dev": true, + "requires": { + "path-is-inside": "1.0.2" + } + }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "dev": true + }, + "is-resolvable": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.0.0.tgz", + "integrity": "sha1-jfV8YeouPFAUCNEA+wE8+NbgzGI=", + "dev": true, + "requires": { + "tryit": "1.0.3" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + }, + "js-yaml": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.10.0.tgz", + "integrity": "sha512-O2v52ffjLa9VeM43J4XocZE//WT9N0IiwDa3KSHH7Tu8CtH+1qM8SIZvnsTh6v+4yFy5KUY3BHUVwjpfAWsjIA==", + "dev": true, + "requires": { + "argparse": "1.0.9", + "esprima": "4.0.0" + } + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "1.1.2", + "type-check": "0.3.2" + } + }, + "lodash": { + "version": "4.17.4", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", + "dev": true + }, + "lru-cache": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", + "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==", + "dev": true, + "requires": { + "pseudomap": "1.0.2", + "yallist": "2.1.2" + } + }, + "mimic-fn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.1.0.tgz", + "integrity": "sha1-5md4PZLonb00KBi1IwudYqZyrRg=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "1.1.8" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "normalize.css": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-7.0.0.tgz", + "integrity": "sha1-q/sd2CRwZ04DIrU86xqvQSk45L8=", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "1.1.0" + } + }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "dev": true, + "requires": { + "deep-is": "0.1.3", + "fast-levenshtein": "2.0.6", + "levn": "0.3.0", + "prelude-ls": "1.1.2", + "type-check": "0.3.2", + "wordwrap": "1.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "2.0.4" + } + }, + "pluralize": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", + "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", + "dev": true + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", + "dev": true + }, + "progress": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz", + "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=", + "dev": true + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "readable-stream": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } + }, + "require-uncached": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", + "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", + "dev": true, + "requires": { + "caller-path": "0.1.0", + "resolve-from": "1.0.1" + } + }, + "resolve-from": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", + "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", + "dev": true + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "2.0.1", + "signal-exit": "3.0.2" + } + }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "dev": true, + "requires": { + "glob": "7.1.2" + } + }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "dev": true, + "requires": { + "is-promise": "2.1.0" + } + }, + "rx-lite": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", + "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=", + "dev": true + }, + "rx-lite-aggregates": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz", + "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=", + "dev": true, + "requires": { + "rx-lite": "4.0.8" + } + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "dev": true + }, + "semver": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", + "dev": true + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "slice-ansi": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", + "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "2.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" + } + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "3.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + } + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "table": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz", + "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==", + "dev": true, + "requires": { + "ajv": "5.5.1", + "ajv-keywords": "2.1.1", + "chalk": "2.3.0", + "lodash": "4.17.4", + "slice-ansi": "1.0.0", + "string-width": "2.1.1" + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "1.0.2" + } + }, + "tryit": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tryit/-/tryit-1.0.3.tgz", + "integrity": "sha1-OTvnMKlEb9Hq1tpZoBQwjzbCics=", + "dev": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "1.1.2" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "which": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", + "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", + "dev": true, + "requires": { + "isexe": "2.0.0" + } + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "write": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", + "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", + "dev": true, + "requires": { + "mkdirp": "0.5.1" + } + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + } + } +} diff --git a/package.json b/package.json index b297a1d..98d9057 100644 --- a/package.json +++ b/package.json @@ -12,10 +12,13 @@ "bin": { "csscrush": "./bin/csscrush" }, + "scripts": { + "lint": "eslint --fix index.js" + }, "homepage": "http://the-echoplex.net/csscrush", "license": "MIT", "devDependencies": { - "eslint": "~4.5.0", + "eslint": "~4.12.1", "normalize.css": "7.0.0" } } From 859d061b17001075767191b2ebe3b6d9c19f8852 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 12 Dec 2017 11:49:29 +0000 Subject: [PATCH 363/421] Exposed cli phpBin setting. Added compile time to logged output. --- cli.php | 59 ++++++++++++++++++++++++----------------------- index.js | 4 ++-- package-lock.json | 2 +- 3 files changed, 33 insertions(+), 32 deletions(-) diff --git a/cli.php b/cli.php index db77322..ff10483 100755 --- a/cli.php +++ b/cli.php @@ -14,8 +14,8 @@ if ($version < $requiredVersion) { - stderr(array("PHP version $requiredVersion or higher is required to use this tool.", - "You are currently running PHP $version")); + stderr(["PHP version $requiredVersion or higher is required to use this tool.", + "You are currently running PHP $version"]); exit(STATUS_ERROR); } @@ -25,7 +25,7 @@ } catch (Exception $ex) { - stderr(message($ex->getMessage(), array('type'=>'error'))); + stderr(message($ex->getMessage(), ['type'=>'error'])); exit($ex->getCode()); } @@ -70,7 +70,7 @@ if ($args->watch && ! $args->input_file) { - stderr(message('Watch mode requires an input file.', array('type'=>'error'))); + stderr(message('Watch mode requires an input file.', ['type'=>'error'])); exit(STATUS_ERROR); } @@ -116,10 +116,10 @@ $options['output_file'] = basename($args->output_file); } -$options += array( +$options += [ 'doc_root' => getcwd(), 'context' => $args->context, -); +]; ################################################################## @@ -129,7 +129,7 @@ if ($args->watch) { - csscrush_set('config', array('io' => 'CssCrush\IO\Watch')); + csscrush_set('config', ['io' => 'CssCrush\IO\Watch']); stdout('CONTROL-C to quit.'); @@ -148,20 +148,20 @@ if ($errors) { if ($showErrors) { $outstandingErrors = $errors; - stderr(message($errors, array('type'=>'error'))); + stderr(message($errors, ['type'=>'error'])); } } elseif ($changed) { $outstandingErrors = false; - stderr(message(fmt_fileinfo($stats, 'output'), array('type'=>'write'))); + stderr(message(fmt_fileinfo($stats, 'output'), ['type'=>'write'])); } if (($showErrors || $changed) && $warnings) { - stderr(message($warnings, array('type'=>'warning'))); + stderr(message($warnings, ['type'=>'warning'])); } if ($changed && $args->stats) { - stderr(message($stats, array('type'=>'stats'))); + stderr(message($stats, ['type'=>'stats'])); } sleep(1); @@ -184,20 +184,20 @@ $warnings = $stats['warnings']; if ($errors) { - stderr(message($errors, array('type'=>'error'))); + stderr(message($errors, ['type'=>'error'])); exit(STATUS_ERROR); } elseif ($args->input_file && ! empty($stats['output_filename'])) { - stderr(message(fmt_fileinfo($stats, 'output'), array('type'=>'write'))); + stderr(message(fmt_fileinfo($stats, 'output'), ['type'=>'write'])); } if ($warnings) { - stderr(message($warnings, array('type'=>'warning'))); + stderr(message($warnings, ['type'=>'warning'])); } if ($args->stats) { - stderr(message($stats, array('type'=>'stats'))); + stderr(message($stats, ['type'=>'stats'])); } if ($stdOutput) { @@ -250,12 +250,12 @@ function parse_list(array $option) { function message($messages, $options = []) { - $defaults = array( + $defaults = [ 'color' => 'b', 'label' => null, 'indent' => false, 'format_label' => false, - ); + ]; $preset = ! empty($options['type']) ? $options['type'] : null; switch ($preset) { case 'error': @@ -274,8 +274,8 @@ function message($messages, $options = []) { // Making stats concise and readable. $messages['input_file'] = $messages['input_path']; $messages['compile_time'] = round($messages['compile_time'], 5) . ' seconds'; - foreach (array('input_filename', 'input_path', 'output_filename', - 'output_path', 'vars', 'errors', 'warnings') as $key) { + foreach (['input_filename', 'input_path', 'output_filename', + 'output_path', 'vars', 'errors', 'warnings'] as $key) { unset($messages[$key]); } ksort($messages); @@ -301,7 +301,8 @@ function message($messages, $options = []) { } function fmt_fileinfo($stats, $type) { - return $stats[$type . '_filename'] . ' ' . '(' . $stats[$type . '_path'] . ')'; + $time = round($stats['compile_time'], 3); + return $stats[$type . '_path'] . " ({$time}s)"; } function pick(array &$arr) { @@ -321,7 +322,7 @@ function pick(array &$arr) { function colorize($str) { static $color_support; - static $tags = array( + static $tags = [ '' => "\033[0;30m", '' => "\033[0;31m", '' => "\033[0;32m", @@ -341,7 +342,7 @@ function colorize($str) { '' => "\033[1;37m", '' => "\033[m", - ); + ]; if (! isset($color_support)) { $color_support = defined('TESTMODE') && TESTMODE ? false : true; @@ -404,12 +405,12 @@ function get_trailing_io_args($required_value_opts) { break; } - return array($trailing_input_file, $trailing_output_file); + return [$trailing_input_file, $trailing_output_file]; } function parse_args() { - $required_value_opts = array( + $required_value_opts = [ 'i|input|f|file', // Input file. Defaults to STDIN. 'o|output', // Output file. Defaults to STDOUT. 'E|enable|plugins', @@ -420,14 +421,14 @@ function parse_args() { 'context', 'import-path', 'newlines', - ); + ]; - $optional_value_opts = array( + $optional_value_opts = [ 'b|boilerplate', 'stat-dump', - ); + ]; - $flag_opts = array( + $flag_opts = [ 'p|pretty', 'w|watch', 'help', @@ -435,7 +436,7 @@ function parse_args() { 'source-map', 'stats', 'test', - ); + ]; // Create option strings for getopt(). $short_opts = []; diff --git a/index.js b/index.js index 0eccad5..c23f932 100644 --- a/index.js +++ b/index.js @@ -1,7 +1,7 @@ const childProcess = require('child_process'); const path = require('path'); const EventEmitter = require('events'); -const cliPath = path.resolve(__dirname, './bin/csscrush'); +const cliPath = path.resolve(__dirname, './cli.php'); const self = module.exports = {}; @@ -64,7 +64,7 @@ class Process extends EventEmitter { } assembleCommand(options) { - return `${cliPath} ${this.stringifyOptions(options)}`; + return `${self.phpBin || 'php'} ${cliPath} ${this.stringifyOptions(options)}`; } stringifyOptions(options) { diff --git a/package-lock.json b/package-lock.json index 8a67003..d93634c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "csscrush", - "version": "3.0.0-beta.2", + "version": "3.0.0-beta.3", "lockfileVersion": 1, "requires": true, "dependencies": { From 01141cba4f65af1d2ab8d06508ba17f5e3d232f0 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 14 Dec 2017 12:09:15 +0000 Subject: [PATCH 364/421] Bump to 3.0.0-beta.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 98d9057..856fbef 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "csscrush", - "version": "3.0.0-beta.2", + "version": "3.0.0-beta.3", "description": "CSS-Crush, CSS preprocessor", "repository": { "type": "git", From e2dc6265a040d4fe947026a4395d8e2d0851623b Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 1 Jan 2018 19:44:11 +0000 Subject: [PATCH 365/421] Allow dynamic variable references inside loops. --- lib/CssCrush/Process.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 655f460..3fb3c93 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -447,8 +447,13 @@ protected function placeVars(&$value) protected function resolveSettings() { $captured_settings = $this->string->captureDirectives('settings', ['singles' => true]); + $combined_settings = $this->options->settings + $captured_settings; - $this->settings = new Settings($this->options->settings + $captured_settings); + foreach ($combined_settings as $name => &$value) { + $this->placeVars($value); + } + + $this->settings = new Settings($combined_settings); } ############################# @@ -490,6 +495,7 @@ protected function resolveLoops() // Resolve the list of items for iteration. // Either a generator function or a plain list. $items = []; + $this->placeVars($list); $list = $this->functions->apply($list); if (preg_match(Regex::make('~(?range){{ parens }}~ix'), $list, $m)) { $func = strtolower($m['func']); @@ -958,14 +964,14 @@ public function compile() $this->captureVars(); - $this->placeAllVars(); - $this->resolveIfDefines(); $this->resolveSettings(); $this->resolveLoops(); + $this->placeAllVars(); + // Capture phase 1 hook: After all variables and settings have resolved. $this->emit('capture_phase1', $this); From a7904403b7de440d368ff74db164a589731b3152 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 2 Jan 2018 15:29:00 +0000 Subject: [PATCH 366/421] Removed unused variable. --- lib/CssCrush/Process.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 3fb3c93..cec1af1 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -449,7 +449,7 @@ protected function resolveSettings() $captured_settings = $this->string->captureDirectives('settings', ['singles' => true]); $combined_settings = $this->options->settings + $captured_settings; - foreach ($combined_settings as $name => &$value) { + foreach ($combined_settings as &$value) { $this->placeVars($value); } From 8f7d1f861507708ada49f555d95a9773970192a8 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 9 Feb 2018 15:41:47 +0000 Subject: [PATCH 367/421] Cleanup processes on exit. --- index.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index c23f932..0257656 100644 --- a/index.js +++ b/index.js @@ -1,8 +1,17 @@ -const childProcess = require('child_process'); const path = require('path'); const EventEmitter = require('events'); const cliPath = path.resolve(__dirname, './cli.php'); +const processes = []; +const processExec = (...args) => { + processes.push(require('child_process').exec(...args)); + return processes[processes.length-1]; +}; + +process.on('exit', () => { + processes.filter(it => it).forEach(proc => proc.kill()); +}); + const self = module.exports = {}; class Process extends EventEmitter { @@ -13,7 +22,7 @@ class Process extends EventEmitter { if (stdIn) { command = `echo '${stdIn.replace(/'/g, "\\'")}' | ${command}`; } - childProcess.exec(command, (error, stdout, stderr) => { + processExec(command, (error, stdout, stderr) => { process.stderr.write(stderr.toString()); if (error) { return resolve(false); @@ -30,7 +39,7 @@ class Process extends EventEmitter { watch(options) { options.watch = true; const command = this.assembleCommand(options); - const proc = childProcess.exec(command); + const proc = processExec(command); // Emitting 'error' events from EventEmitter without // any error listener will throw uncaught exception. From d917fac47668b9117a759848fdeb3bdf578d3d39 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 10 Aug 2018 11:22:43 +0100 Subject: [PATCH 368/421] Properly handle bare fragment URLs --- lib/CssCrush/Url.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/CssCrush/Url.php b/lib/CssCrush/Url.php index da8acbb..c1d8381 100644 --- a/lib/CssCrush/Url.php +++ b/lib/CssCrush/Url.php @@ -61,8 +61,8 @@ public function update($new_value) public function evaluate() { - // Protocol or protocol-relative (//) based URL. - if (preg_match('~^(?: (?[a-z]+)\: | \/{2} )~ix', $this->value, $m)) { + // Protocol, protocol-relative (//) or fragment URL. + if (preg_match('~^(?: (?[a-z]+)\: | \/{2} | \# )~ix', $this->value, $m)) { $this->protocol = ! empty($m['protocol']) ? strtolower($m['protocol']) : 'relative'; From 6127251bc7d53548661b092d0e1d6f06317d6b8b Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 18 Aug 2018 19:02:27 +0100 Subject: [PATCH 369/421] Wrapping node cli args in double quotes. Updated eslint. --- .eslintrc.js | 161 ++++++++----- .gitattributes | 1 + index.js | 29 ++- package-lock.json | 587 +++++++++++++++++++++++++--------------------- package.json | 2 +- 5 files changed, 440 insertions(+), 340 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 9a973b4..d9ee412 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,62 +1,107 @@ +/*eslint sort-keys: 2*/ +/*eslint object-property-newline: 2*/ +/*eslint quote-props: [2, "consistent"]*/ + module.exports = { - "env": { - "es6": true, - "node": true + env: { + es6: true, + node: true, }, - "parserOptions": { - "ecmaVersion": 8, - "ecmaFeatures": { - "experimentalObjectRestSpread": true, - }, + extends: 'eslint:recommended', + parserOptions: { + ecmaVersion: 9, + }, + rules: { + 'array-bracket-spacing': [2, 'never'], + 'array-element-newline': [2, 'consistent'], + 'arrow-parens': [2, 'as-needed'], + 'arrow-spacing': 2, + 'brace-style': [2, 'stroustrup'], + 'camelcase': [2, { + ignoreDestructuring: true, + properties: 'never', + }], + 'comma-dangle': [2, { + arrays: 'always-multiline', + functions: 'never', + objects: 'always-multiline', + }], + 'comma-spacing': [2, { + after: true, + before: false, + }], + 'curly': 2, + 'eol-last': [2, 'always'], + 'eqeqeq': 2, + 'key-spacing': [2, { + afterColon: true, + beforeColon: false, + }], + 'keyword-spacing': 2, + 'linebreak-style': [2, 'unix'], + 'multiline-comment-style': [2, 'starred-block'], + 'no-console': 2, + 'no-dupe-keys': 2, + 'no-else-return': 2, + 'no-empty': [2, { + allowEmptyCatch: true, + }], + 'no-lonely-if': 2, + 'no-multi-spaces': 2, + 'no-multiple-empty-lines': [2, { + max: 2, + maxBOF: 1, + maxEOF: 1, + }], + 'no-new-object': 2, + 'no-template-curly-in-string': 2, + 'no-tabs': 2, + 'no-throw-literal': 2, + 'no-trailing-spaces': 2, + 'no-unneeded-ternary': 2, + 'no-unused-expressions': [2, {allowShortCircuit: true}], + 'no-unused-vars': [2, { + args: 'all', + argsIgnorePattern: '^(req|res|next)$|^_', + varsIgnorePattern: '^_$', + }], + 'no-useless-call': 2, + 'no-useless-concat': 2, + 'no-useless-return': 2, + 'no-var': 2, + 'object-curly-newline': [2, {consistent: true}], + 'object-curly-spacing': [2, 'never'], + 'object-shorthand': [2, 'properties'], + 'operator-linebreak': [2, 'before', { + overrides: { + ':': 'ignore', + '?': 'ignore', + }, + }], + 'prefer-arrow-callback': 2, + 'prefer-const': [2, {destructuring: 'all'}], + 'prefer-destructuring': [2, { + array: false, + object: true, + }], + 'prefer-object-spread': 2, + 'quote-props': [2, 'as-needed'], + 'quotes': [2, 'single', { + allowTemplateLiterals: true, + avoidEscape: true, + }], + 'semi': [2, 'always'], + 'space-before-blocks': 2, + 'space-before-function-paren': [2, { + anonymous: 'always', + asyncArrow: 'always', + named: 'never', + }], + 'space-unary-ops': [2, { + nonwords: false, + overrides: {'!': true}, + words: true, + }], + 'yoda': 2, }, - "extends": "eslint:recommended", - "rules": { - "curly": 2, - "yoda": 2, - "no-console": 0, - "no-var": 2, - "no-tabs": 2, - "no-trailing-spaces": 2, - "no-dupe-keys": 2, - "no-else-return": 2, - "no-useless-call": 2, - "no-useless-return": 2, - "no-control-regex": 0, - "no-unused-expressions": [2, {"allowShortCircuit": true}], - "no-lonely-if": 2, - "arrow-parens": [2, "as-needed"], - "arrow-spacing": 2, - "object-shorthand": [2, "properties"], - "no-empty": [2, { - "allowEmptyCatch": true - }], - "eol-last": [2, "always"], - "no-multiple-empty-lines": [2, {"max": 2, "maxEOF": 1, "maxBOF": 1}], - "quotes": [2, "single", {"avoidEscape": true, "allowTemplateLiterals": true}], - "keyword-spacing": 2, - "space-before-blocks": 2, - "linebreak-style": [2, "unix"], - "object-curly-spacing": [2, "never"], - "array-bracket-spacing": [2, "never"], - "brace-style": [2, "stroustrup"], - "semi": [2, "always"], - "quote-props": [2, "as-needed"], - "comma-dangle": [2, { - "objects": "always-multiline", - "functions": "never", - }], - "comma-spacing": [2, { - "before": false, - "after": true - }], - "key-spacing": [2, { - "beforeColon": false, - "afterColon": true - }], - "space-before-function-paren": [2, { - "anonymous": "always", - "asyncArrow": "always", - "named": "never" - }], - } }; diff --git a/.gitattributes b/.gitattributes index aaf1665..97a1a66 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,3 +3,4 @@ tests/ export-ignore .gitignore export-ignore .travis.yml export-ignore phpunit.xml.dist export-ignore +package-lock.json -diff diff --git a/index.js b/index.js index 0257656..dc58d8a 100644 --- a/index.js +++ b/index.js @@ -15,10 +15,11 @@ process.on('exit', () => { const self = module.exports = {}; class Process extends EventEmitter { + exec(options) { return new Promise(resolve => { let command = this.assembleCommand(options); - let {stdIn} = options; + const {stdIn} = options; if (stdIn) { command = `echo '${stdIn.replace(/'/g, "\\'")}' | ${command}`; } @@ -27,7 +28,7 @@ class Process extends EventEmitter { if (error) { return resolve(false); } - let stdOut = stdout.toString(); + const stdOut = stdout.toString(); if (stdIn) { process.stdout.write(stdOut); } @@ -41,8 +42,10 @@ class Process extends EventEmitter { const command = this.assembleCommand(options); const proc = processExec(command); - // Emitting 'error' events from EventEmitter without - // any error listener will throw uncaught exception. + /* + * Emitting 'error' events from EventEmitter without + * any error listener will throw uncaught exception. + */ this.on('error', () => {}); proc.stderr.on('data', msg => { @@ -50,9 +53,9 @@ class Process extends EventEmitter { process.stderr.write(msg); msg = msg.replace(/\x1B\[[^m]*m/g, '').trim(); - let [, signal, detail] = /^([A-Z]+):\s*(.+)/i.exec(msg) || []; - let {input, output} = options; - let eventData = { + const [, signal, detail] = /^([A-Z]+):\s*(.+)/i.exec(msg) || []; + const {input, output} = options; + const eventData = { signal, options: { input: input ? path.resolve(input) : null, @@ -61,7 +64,7 @@ class Process extends EventEmitter { }; if (/^(WARNING|ERROR)$/.test(signal)) { - let error = new Error(detail); + const error = new Error(detail); Object.assign(error, eventData, {severity: signal.toLowerCase()}); this.emit('error', error); } @@ -78,10 +81,10 @@ class Process extends EventEmitter { stringifyOptions(options) { const args = []; - options = Object.assign({}, options); + options = {...options}; for (let name in options) { // Normalize to hypenated case. - let cssCase = name.replace(/[A-Z]/g, m => `-${m.toLowerCase()}`); + const cssCase = name.replace(/[A-Z]/g, m => `-${m.toLowerCase()}`); if (name !== cssCase) { options[cssCase] = options[name]; delete options[name]; @@ -108,7 +111,7 @@ class Process extends EventEmitter { case 'import-path': if (value) { value = (Array.isArray(value) ? value : [value]).join(','); - args.push(`--${name}=${value}`); + args.push(`--${name}="${value}"`); } break; // String values. @@ -118,7 +121,7 @@ class Process extends EventEmitter { case 'context': // fallthrough case 'output': if (value) { - args.push(`--${name}='${value}'`); + args.push(`--${name}="${value}"`); } break; } @@ -131,10 +134,12 @@ self.watch = (file, options={}) => { options.input = file; return (new Process()).watch(options); }; + self.file = (file, options={}) => { options.input = file; return (new Process()).exec(options); }; + self.string = (string, options={}) => { options.stdIn = string; return (new Process()).exec(options); diff --git a/package-lock.json b/package-lock.json index d93634c..0454ff8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,50 +5,42 @@ "requires": true, "dependencies": { "acorn": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.2.1.tgz", - "integrity": "sha512-jG0u7c4Ly+3QkkW18V+NRDN+4bWHdln30NL1ZL2AvFZZmQe/BfopYCtghCKKVBUSetZ4QKcyA0pY6/4Gw8Pv8w==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.1.tgz", + "integrity": "sha512-d+nbxBUGKg7Arpsvbnlq61mc12ek3EY8EQldM3GPAhWJ1UVxC6TDGbIvUMNU6obBX3i1+ptCIzV4vq0gFPEGVQ==", "dev": true }, "acorn-jsx": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", - "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-4.1.1.tgz", + "integrity": "sha512-JY+iV6r+cO21KtntVvFkD+iqjtdpRUpGqKWgfkCdZq1R+kbreEl8EcdcJR4SmiIgsIQT33s6QzheQ9a275Q8xw==", "dev": true, "requires": { - "acorn": "3.3.0" - }, - "dependencies": { - "acorn": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", - "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", - "dev": true - } + "acorn": "5.7.1" } }, "ajv": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.1.tgz", - "integrity": "sha1-s4u4h22ehr7plJVqBOch6IskjrI=", + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.3.tgz", + "integrity": "sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==", "dev": true, "requires": { - "co": "4.6.0", - "fast-deep-equal": "1.0.0", + "fast-deep-equal": "2.0.1", "fast-json-stable-stringify": "2.0.0", - "json-schema-traverse": "0.3.1" + "json-schema-traverse": "0.4.1", + "uri-js": "4.2.2" } }, "ajv-keywords": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz", - "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz", + "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=", "dev": true }, "ansi-escapes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.0.0.tgz", - "integrity": "sha512-O/klc27mWNUigtv0F8NJWbLF00OcegQalkqKURWdosW08YZKi4m6CnSUSvIZG1otNJbTWhN01Hhz389DW7mvDQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", + "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", "dev": true }, "ansi-regex": { @@ -64,9 +56,9 @@ "dev": true }, "argparse": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", - "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=", + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, "requires": { "sprintf-js": "1.0.3" @@ -135,9 +127,9 @@ "dev": true }, "brace-expansion": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", - "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { "balanced-match": "1.0.0", @@ -160,32 +152,32 @@ "dev": true }, "chalk": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", - "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "3.2.0", + "ansi-styles": "3.2.1", "escape-string-regexp": "1.0.5", - "supports-color": "4.5.0" + "supports-color": "5.4.0" }, "dependencies": { "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "1.9.2" } }, "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", "dev": true, "requires": { - "has-flag": "2.0.0" + "has-flag": "3.0.0" } } } @@ -217,25 +209,19 @@ "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", "dev": true }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true - }, "color-convert": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", - "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.2.tgz", + "integrity": "sha512-3NUJZdhMhcdPn8vJ9v2UQJoH0qqoGUkYTgFEPZaPjEtwmmKUfNV46zZmgB2M5M4DCEQHMaCfWHCxiBflLm04Tg==", "dev": true, "requires": { - "color-name": "1.1.3" + "color-name": "1.1.1" } }, "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha1-SxQVMEz1ACjqgWQ2Q72C6gWANok=", "dev": true }, "concat-map": { @@ -244,32 +230,17 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, - "concat-stream": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", - "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "readable-stream": "2.3.3", - "typedarray": "0.0.6" - } - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", "dev": true, "requires": { - "lru-cache": "4.1.1", + "nice-try": "1.0.4", + "path-key": "2.0.1", + "semver": "5.5.1", "shebang-command": "1.2.0", - "which": "1.3.0" + "which": "1.3.1" } }, "debug": { @@ -287,6 +258,15 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "1.0.12" + } + }, "del": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", @@ -295,7 +275,7 @@ "requires": { "globby": "5.0.0", "is-path-cwd": "1.0.0", - "is-path-in-cwd": "1.0.0", + "is-path-in-cwd": "1.0.1", "object-assign": "4.1.1", "pify": "2.3.0", "pinkie-promise": "2.0.1", @@ -303,14 +283,38 @@ } }, "doctrine": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.0.2.tgz", - "integrity": "sha512-y0tm5Pq6ywp3qSTZ1vPgVdAnbDEoeoc5wlOHXoY1c4Wug/a7JvqHIl7BTvwodaHmejWkK/9dSb3sCYfyo/om8A==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, "requires": { "esutils": "2.0.2" } }, + "es-abstract": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.12.0.tgz", + "integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==", + "dev": true, + "requires": { + "es-to-primitive": "1.1.1", + "function-bind": "1.1.1", + "has": "1.0.3", + "is-callable": "1.1.4", + "is-regex": "1.0.4" + } + }, + "es-to-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", + "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", + "dev": true, + "requires": { + "is-callable": "1.1.4", + "is-date-object": "1.0.1", + "is-symbol": "1.0.1" + } + }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -318,35 +322,35 @@ "dev": true }, "eslint": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.12.1.tgz", - "integrity": "sha512-28hOYej+NZ/R5H1yMvyKa1+bPlu+fnsIAQffK6hxXgvmXnImos2bA5XfCn5dYv2k2mrKj+/U/Z4L5ICWxC7TQw==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.3.0.tgz", + "integrity": "sha512-N/tCqlMKkyNvAvLu+zI9AqDasnSLt00K+Hu8kdsERliC9jYEc8ck12XtjvOXrBKu8fK6RrBcN9bat6Xk++9jAg==", "dev": true, "requires": { - "ajv": "5.5.1", + "ajv": "6.5.3", "babel-code-frame": "6.26.0", - "chalk": "2.3.0", - "concat-stream": "1.6.0", - "cross-spawn": "5.1.0", + "chalk": "2.4.1", + "cross-spawn": "6.0.5", "debug": "3.1.0", - "doctrine": "2.0.2", - "eslint-scope": "3.7.1", - "espree": "3.5.2", - "esquery": "1.0.0", - "estraverse": "4.2.0", + "doctrine": "2.1.0", + "eslint-scope": "4.0.0", + "eslint-utils": "1.3.1", + "eslint-visitor-keys": "1.0.0", + "espree": "4.0.0", + "esquery": "1.0.1", "esutils": "2.0.2", "file-entry-cache": "2.0.0", "functional-red-black-tree": "1.0.1", "glob": "7.1.2", - "globals": "11.1.0", - "ignore": "3.3.7", + "globals": "11.7.0", + "ignore": "4.0.6", "imurmurhash": "0.1.4", - "inquirer": "3.3.0", - "is-resolvable": "1.0.0", - "js-yaml": "3.10.0", + "inquirer": "5.2.0", + "is-resolvable": "1.1.0", + "js-yaml": "3.12.0", "json-stable-stringify-without-jsonify": "1.0.1", "levn": "0.3.0", - "lodash": "4.17.4", + "lodash": "4.17.10", "minimatch": "3.0.4", "mkdirp": "0.5.1", "natural-compare": "1.4.0", @@ -354,57 +358,70 @@ "path-is-inside": "1.0.2", "pluralize": "7.0.0", "progress": "2.0.0", + "regexpp": "2.0.0", "require-uncached": "1.0.3", - "semver": "5.4.1", + "semver": "5.5.1", + "string.prototype.matchall": "2.0.0", "strip-ansi": "4.0.0", "strip-json-comments": "2.0.1", - "table": "4.0.2", + "table": "4.0.3", "text-table": "0.2.0" } }, "eslint-scope": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz", - "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz", + "integrity": "sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA==", "dev": true, "requires": { - "esrecurse": "4.2.0", + "esrecurse": "4.2.1", "estraverse": "4.2.0" } }, + "eslint-utils": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz", + "integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==", + "dev": true + }, + "eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", + "dev": true + }, "espree": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.2.tgz", - "integrity": "sha512-sadKeYwaR/aJ3stC2CdvgXu1T16TdYN+qwCpcWbMnGJ8s0zNWemzrvb2GbD4OhmJ/fwpJjudThAlLobGbWZbCQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-4.0.0.tgz", + "integrity": "sha512-kapdTCt1bjmspxStVKX6huolXVV5ZfyZguY1lcfhVVZstce3bqxH9mcLzNn3/mlgW6wQ732+0fuG9v7h0ZQoKg==", "dev": true, "requires": { - "acorn": "5.2.1", - "acorn-jsx": "3.0.1" + "acorn": "5.7.1", + "acorn-jsx": "4.1.1" } }, "esprima": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", - "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, "esquery": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.0.tgz", - "integrity": "sha1-z7qLV9f7qT8XKYqKAGoEzaE9gPo=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", + "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", "dev": true, "requires": { "estraverse": "4.2.0" } }, "esrecurse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.0.tgz", - "integrity": "sha1-+pVo2Y04I/mkHZHpAtyrnqblsWM=", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", "dev": true, "requires": { - "estraverse": "4.2.0", - "object-assign": "4.1.1" + "estraverse": "4.2.0" } }, "estraverse": { @@ -420,20 +437,20 @@ "dev": true }, "external-editor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.1.0.tgz", - "integrity": "sha512-E44iT5QVOUJBKij4IIV3uvxuNlbKS38Tw1HiupxEIHPv9qtC2PrDYohbXV5U+1jnfIXttny8gUhj+oZvflFlzA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", + "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", "dev": true, "requires": { "chardet": "0.4.2", - "iconv-lite": "0.4.19", + "iconv-lite": "0.4.23", "tmp": "0.0.33" } }, "fast-deep-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", - "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", "dev": true }, "fast-json-stable-stringify": { @@ -485,6 +502,12 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, "functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", @@ -506,9 +529,9 @@ } }, "globals": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.1.0.tgz", - "integrity": "sha512-uEuWt9mqTlPDwSqi+sHjD4nWU/1N+q0fiWI9T1mZpD2UENqX20CFD5T/ziLZvztPaBKl7ZylUi1q6Qfm7E2CiQ==", + "version": "11.7.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.7.0.tgz", + "integrity": "sha512-K8BNSPySfeShBQXsahYB/AbbWruVOTyVpgoIDnl8odPpeSfP2J5QO2oLFFdl2j7GfDCtZj2bMKar2T49itTPCg==", "dev": true }, "globby": { @@ -531,6 +554,15 @@ "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", "dev": true }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "1.1.1" + } + }, "has-ansi": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", @@ -541,21 +573,30 @@ } }, "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, - "iconv-lite": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", - "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==", + "has-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", "dev": true }, + "iconv-lite": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "dev": true, + "requires": { + "safer-buffer": "2.1.2" + } + }, "ignore": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.7.tgz", - "integrity": "sha512-YGG3ejvBNHRqu0559EOxxNFihD0AjpvHlC/pdGKd3X3ofe+CoJkYazwNJYTNebqpPKN+VVQbh4ZFn1DivMNuHA==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true }, "imurmurhash": { @@ -581,27 +622,38 @@ "dev": true }, "inquirer": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", - "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-5.2.0.tgz", + "integrity": "sha512-E9BmnJbAKLPGonz0HeWHtbKf+EeSP93paWO3ZYoUpq/aowXvYGjjCSuashhXPpzbArIjBbji39THkxTz9ZeEUQ==", "dev": true, "requires": { - "ansi-escapes": "3.0.0", - "chalk": "2.3.0", + "ansi-escapes": "3.1.0", + "chalk": "2.4.1", "cli-cursor": "2.1.0", "cli-width": "2.2.0", - "external-editor": "2.1.0", + "external-editor": "2.2.0", "figures": "2.0.0", - "lodash": "4.17.4", + "lodash": "4.17.10", "mute-stream": "0.0.7", "run-async": "2.3.0", - "rx-lite": "4.0.8", - "rx-lite-aggregates": "4.0.8", + "rxjs": "5.5.11", "string-width": "2.1.1", "strip-ansi": "4.0.0", "through": "2.3.8" } }, + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", + "dev": true + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "dev": true + }, "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", @@ -615,9 +667,9 @@ "dev": true }, "is-path-in-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz", - "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", + "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", "dev": true, "requires": { "is-path-inside": "1.0.1" @@ -638,19 +690,25 @@ "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", "dev": true }, - "is-resolvable": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.0.0.tgz", - "integrity": "sha1-jfV8YeouPFAUCNEA+wE8+NbgzGI=", + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", "dev": true, "requires": { - "tryit": "1.0.3" + "has": "1.0.3" } }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "is-resolvable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", + "dev": true + }, + "is-symbol": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", + "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=", "dev": true }, "isexe": { @@ -666,19 +724,19 @@ "dev": true }, "js-yaml": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.10.0.tgz", - "integrity": "sha512-O2v52ffjLa9VeM43J4XocZE//WT9N0IiwDa3KSHH7Tu8CtH+1qM8SIZvnsTh6v+4yFy5KUY3BHUVwjpfAWsjIA==", + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", + "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", "dev": true, "requires": { - "argparse": "1.0.9", - "esprima": "4.0.0" + "argparse": "1.0.10", + "esprima": "4.0.1" } }, "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, "json-stable-stringify-without-jsonify": { @@ -698,25 +756,15 @@ } }, "lodash": { - "version": "4.17.4", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", - "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", "dev": true }, - "lru-cache": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", - "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==", - "dev": true, - "requires": { - "pseudomap": "1.0.2", - "yallist": "2.1.2" - } - }, "mimic-fn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.1.0.tgz", - "integrity": "sha1-5md4PZLonb00KBi1IwudYqZyrRg=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", "dev": true }, "minimatch": { @@ -725,7 +773,7 @@ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { - "brace-expansion": "1.1.8" + "brace-expansion": "1.1.11" } }, "minimist": { @@ -761,6 +809,12 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "nice-try": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.4.tgz", + "integrity": "sha512-2NpiFHqC87y/zFke0fC0spBXL3bBsoh/p5H1EFhshxjCR5+0g2d6BiXbUFz9v1sAcxsk2htp2eQnNIci2dIYcA==", + "dev": true + }, "normalize.css": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-7.0.0.tgz", @@ -773,6 +827,12 @@ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true }, + "object-keys": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz", + "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==", + "dev": true + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -788,7 +848,7 @@ "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", "dev": true, "requires": { - "mimic-fn": "1.1.0" + "mimic-fn": "1.2.0" } }, "optionator": { @@ -823,6 +883,12 @@ "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", "dev": true }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, "pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", @@ -856,39 +922,33 @@ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", "dev": true }, - "process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", - "dev": true - }, "progress": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz", "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=", "dev": true }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, - "readable-stream": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", - "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", + "regexp.prototype.flags": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.2.0.tgz", + "integrity": "sha512-ztaw4M1VqgMwl9HlPpOuiYgItcHlunW0He2fE6eNfT6E/CF2FtYi9ofOYe4mKntstYk0Fyh/rDRBdS3AnxjlrA==", "dev": true, "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", - "util-deprecate": "1.0.2" + "define-properties": "1.1.3" } }, + "regexpp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.0.tgz", + "integrity": "sha512-g2FAVtR8Uh8GO1Nv5wpxW7VFVwHcCEr4wyA8/MHiRkO8uHoR5ntAA8Uq3P1vvMTX/BeQiRVSpDGLd+Wn5HNOTA==", + "dev": true + }, "require-uncached": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", @@ -933,31 +993,25 @@ "is-promise": "2.1.0" } }, - "rx-lite": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", - "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=", - "dev": true - }, - "rx-lite-aggregates": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz", - "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=", + "rxjs": { + "version": "5.5.11", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.11.tgz", + "integrity": "sha512-3bjO7UwWfA2CV7lmwYMBzj4fQ6Cq+ftHc2MvUe+WMS7wcdJ1LosDWmdjPQanYp2dBRj572p7PeU81JUxHKOcBA==", "dev": true, "requires": { - "rx-lite": "4.0.8" + "symbol-observable": "1.0.1" } }, - "safe-buffer": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, "semver": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", - "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", + "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==", "dev": true }, "shebang-command": { @@ -1006,13 +1060,17 @@ "strip-ansi": "4.0.0" } }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "string.prototype.matchall": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-2.0.0.tgz", + "integrity": "sha512-WoZ+B2ypng1dp4iFLF2kmZlwwlE19gmjgKuhL1FJfDgCREWb3ye3SDVHSzLH6bxfnvYmkCxbzkmWcQZHA4P//Q==", "dev": true, "requires": { - "safe-buffer": "5.1.1" + "define-properties": "1.1.3", + "es-abstract": "1.12.0", + "function-bind": "1.1.1", + "has-symbols": "1.0.0", + "regexp.prototype.flags": "1.2.0" } }, "strip-ansi": { @@ -1044,16 +1102,22 @@ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", "dev": true }, + "symbol-observable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", + "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=", + "dev": true + }, "table": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz", - "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/table/-/table-4.0.3.tgz", + "integrity": "sha512-S7rnFITmBH1EnyKcvxBh1LjYeQMmnZtCXSEbHcH6S0NoKit24ZuFO/T1vDcLdYsLQkM188PVVhQmzKIuThNkKg==", "dev": true, "requires": { - "ajv": "5.5.1", - "ajv-keywords": "2.1.1", - "chalk": "2.3.0", - "lodash": "4.17.4", + "ajv": "6.5.3", + "ajv-keywords": "3.2.0", + "chalk": "2.4.1", + "lodash": "4.17.10", "slice-ansi": "1.0.0", "string-width": "2.1.1" } @@ -1079,12 +1143,6 @@ "os-tmpdir": "1.0.2" } }, - "tryit": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tryit/-/tryit-1.0.3.tgz", - "integrity": "sha1-OTvnMKlEb9Hq1tpZoBQwjzbCics=", - "dev": true - }, "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", @@ -1094,22 +1152,19 @@ "prelude-ls": "1.1.2" } }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "requires": { + "punycode": "2.1.1" + } }, "which": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", - "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, "requires": { "isexe": "2.0.0" @@ -1135,12 +1190,6 @@ "requires": { "mkdirp": "0.5.1" } - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true } } } diff --git a/package.json b/package.json index 856fbef..eaa24fe 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "homepage": "http://the-echoplex.net/csscrush", "license": "MIT", "devDependencies": { - "eslint": "~4.12.1", + "eslint": "~5.3.0", "normalize.css": "7.0.0" } } From 9b63185fddf4f0db89fb4c1aa9501ed50da8444d Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 18 Aug 2018 19:05:10 +0100 Subject: [PATCH 370/421] Bump to 3.0.0-beta.4 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0454ff8..8079f37 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "csscrush", - "version": "3.0.0-beta.3", + "version": "3.0.0-beta.4", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index eaa24fe..f37dfe2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "csscrush", - "version": "3.0.0-beta.3", + "version": "3.0.0-beta.4", "description": "CSS-Crush, CSS preprocessor", "repository": { "type": "git", From ac3c40b0a10a54211f846816e82d752f027c6939 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 20 Aug 2018 09:45:34 +0100 Subject: [PATCH 371/421] Fix for #84 --- lib/CssCrush/Color.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/CssCrush/Color.php b/lib/CssCrush/Color.php index 899ac6c..5e45818 100644 --- a/lib/CssCrush/Color.php +++ b/lib/CssCrush/Color.php @@ -283,7 +283,9 @@ public static function hueToRgb($p, $q, $t) public static function rgbToHex(array $rgba) { // Drop alpha component. - array_pop($rgba); + if (isset($rgba[3])) { + array_pop($rgba); + } $hex_out = '#'; foreach ($rgba as $val) { From 7c2075a10cd47a17dce0db67e524ba76f63436e6 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 23 Jan 2019 17:24:56 +0000 Subject: [PATCH 372/421] Issue with invalid source map column index --- lib/CssCrush/Importer.php | 2 +- lib/CssCrush/Util.php | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index 2f76742..3544433 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -320,7 +320,7 @@ protected function addMarkers(&$str) // Source maps require column index too. if ($process->generateMap) { - $pointData[] = strlen($before) - strrpos($before, "\n") - 1; + $pointData[] = strlen($before) - (strrpos($before, "\n") ?: 0); } // Splice in marker token (packing point_data into string is more memory efficient). diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php index cf00fa3..53d400a 100644 --- a/lib/CssCrush/Util.php +++ b/lib/CssCrush/Util.php @@ -263,12 +263,12 @@ public static function vlqEncode($value) $encoded = ""; do { - $digit = $vlq & $VLQ_BASE_MASK; - $vlq >>= $VLQ_BASE_SHIFT; - if ($vlq > 0) { - $digit |= $VLQ_CONTINUATION_BIT; - } - $encoded .= $BASE64_MAP[$digit]; + $digit = $vlq & $VLQ_BASE_MASK; + $vlq >>= $VLQ_BASE_SHIFT; + if ($vlq > 0) { + $digit |= $VLQ_CONTINUATION_BIT; + } + $encoded .= $BASE64_MAP[$digit]; } while ($vlq > 0); From 39665ecc5220b6030b7704d1267cce0b17cc7f56 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 23 Jan 2019 17:30:16 +0000 Subject: [PATCH 373/421] Bump to 3.0.0-beta.5 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8079f37..b0e96d8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "csscrush", - "version": "3.0.0-beta.4", + "version": "3.0.0-beta.5", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index f37dfe2..8979514 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "csscrush", - "version": "3.0.0-beta.4", + "version": "3.0.0-beta.5", "description": "CSS-Crush, CSS preprocessor", "repository": { "type": "git", From 802d80392bfd1438dfdbe8239be79e132d2164b0 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 24 Jan 2019 13:28:27 +0000 Subject: [PATCH 374/421] Aliases update --- aliases.ini | 34 +++------------------------------- 1 file changed, 3 insertions(+), 31 deletions(-) diff --git a/aliases.ini b/aliases.ini index 1cc4687..8e797d1 100644 --- a/aliases.ini +++ b/aliases.ini @@ -41,27 +41,16 @@ ; Columns. columns[] = -webkit-columns - columns[] = -moz-columns column-count[] = -webkit-column-count - column-count[] = -moz-column-count column-fill[] = -webkit-column-fill - column-fill[] = -moz-column-fill column-gap[] = -webkit-column-gap - column-gap[] = -moz-column-gap column-rule[] = -webkit-column-rule - column-rule[] = -moz-column-rule column-rule-style[] = -webkit-column-rule-style - column-rule-style[] = -moz-column-rule-style column-rule-width[] = -webkit-column-rule-width - column-rule-width[] = -moz-column-rule-width column-rule-style[] = -webkit-column-rule-style - column-rule-style[] = -moz-column-rule-style column-rule-color[] = -webkit-column-rule-color - column-rule-color[] = -moz-column-rule-color column-span[] = -webkit-column-span - column-span[] = -moz-column-span column-width[] = -webkit-column-width - column-width[] = -moz-column-width ; Filter. filter[] = -webkit-filter @@ -100,7 +89,6 @@ ; Hyphens. hyphens[] = -webkit-hyphens - hyphens[] = -moz-hyphens hyphens[] = -ms-hyphens ; Outline radius. @@ -123,17 +111,10 @@ tab-size[] = -moz-tab-size tab-size[] = -o-tab-size - ; Text align last. - text-align-last[] = -webkit-text-align-last - text-align-last[] = -moz-text-align-last - ; Text decoration. - text-decoration-color[] = -moz-text-decoration-color - text-decoration-line[] = -moz-text-decoration-line - text-decoration-style[] = -moz-text-decoration-style - - ; Text overflow (Opera mini support). - text-overflow[] = -o-text-overflow + text-decoration-color[] = -webkit-text-decoration-color + text-decoration-line[] = -webkit-text-decoration-line + text-decoration-style[] = -webkit-text-decoration-style ; Transforms. transform[] = -webkit-transform @@ -196,13 +177,9 @@ ; Cursor values (non-standard). cursor:zoom-in[] = cursor:-webkit-zoom-in - cursor:zoom-in[] = cursor:-moz-zoom-in cursor:zoom-out[] = cursor:-webkit-zoom-out - cursor:zoom-out[] = cursor:-moz-zoom-out cursor:grab[] = cursor:-webkit-grab - cursor:grab[] = cursor:-moz-grab cursor:grabbing[] = cursor:-webkit-grabbing - cursor:grabbing[] = cursor:-moz-grabbing ; Experimental width values. width:max-content[] = width:intrinsic @@ -269,8 +246,3 @@ ; Keyframes. keyframes[] = -webkit-keyframes - - ; Viewport. - viewport[] = -webkit-viewport - viewport[] = -ms-viewport - viewport[] = -o-viewport From 10ef6d05c84585c850acb713d9db6b4de6deb89a Mon Sep 17 00:00:00 2001 From: Jason Cooke Date: Sun, 29 Sep 2019 12:29:42 +1300 Subject: [PATCH 375/421] docs: fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e9400fa..10f8f6c 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ csscrush.watch('./styles.css'); If you think you've found a bug please create an [issue](https://github.com/peteboere/css-crush/issues) explaining the problem and expected result. -Likewise, if you'd like to request a feature please create an [issue](https://github.com/peteboere/css-crush/issues) with some explaination of the requested feature and use-cases. +Likewise, if you'd like to request a feature please create an [issue](https://github.com/peteboere/css-crush/issues) with some explanation of the requested feature and use-cases. [Pull requests](https://help.github.com/articles/using-pull-requests) are welcome, though please keep coding style consistent with the project (which is based on [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)). From 0efd6c84b256483ad8684e326c054a356357a449 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 25 Oct 2019 12:22:15 +0100 Subject: [PATCH 376/421] Updated eslint --- index.js | 2 + package-lock.json | 993 ++++++++++++++++++---------------------------- package.json | 2 +- 3 files changed, 396 insertions(+), 601 deletions(-) diff --git a/index.js b/index.js index dc58d8a..e82f646 100644 --- a/index.js +++ b/index.js @@ -1,3 +1,5 @@ +/*eslint no-control-regex: 0*/ + const path = require('path'); const EventEmitter = require('events'); const cliPath = path.resolve(__dirname, './cli.php'); diff --git a/package-lock.json b/package-lock.json index b0e96d8..7a12011 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,56 +4,70 @@ "lockfileVersion": 1, "requires": true, "dependencies": { - "acorn": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.1.tgz", - "integrity": "sha512-d+nbxBUGKg7Arpsvbnlq61mc12ek3EY8EQldM3GPAhWJ1UVxC6TDGbIvUMNU6obBX3i1+ptCIzV4vq0gFPEGVQ==", - "dev": true - }, - "acorn-jsx": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-4.1.1.tgz", - "integrity": "sha512-JY+iV6r+cO21KtntVvFkD+iqjtdpRUpGqKWgfkCdZq1R+kbreEl8EcdcJR4SmiIgsIQT33s6QzheQ9a275Q8xw==", + "@babel/code-frame": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", + "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", "dev": true, "requires": { - "acorn": "5.7.1" + "@babel/highlight": "^7.0.0" } }, - "ajv": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.3.tgz", - "integrity": "sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==", + "@babel/highlight": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", + "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==", "dev": true, "requires": { - "fast-deep-equal": "2.0.1", - "fast-json-stable-stringify": "2.0.0", - "json-schema-traverse": "0.4.1", - "uri-js": "4.2.2" + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" } }, - "ajv-keywords": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz", - "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=", + "acorn": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.0.tgz", + "integrity": "sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ==", + "dev": true + }, + "acorn-jsx": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.1.0.tgz", + "integrity": "sha512-tMUqwBWfLFbJbizRmEcWSLw6HnFzfdJs2sOJEOwwtVPMoH/0Ay+E703oZz78VSXZiiDcZrQ5XKjPIUQixhmgVw==", "dev": true }, + "ajv": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "dev": true, + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, "ansi-escapes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", - "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", "dev": true }, "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", "dev": true }, "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } }, "argparse": { "version": "1.0.10", @@ -61,65 +75,15 @@ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, "requires": { - "sprintf-js": "1.0.3" - } - }, - "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", - "dev": true, - "requires": { - "array-uniq": "1.0.3" + "sprintf-js": "~1.0.2" } }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", - "dev": true - }, - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", "dev": true }, - "babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", - "dev": true, - "requires": { - "chalk": "1.1.3", - "esutils": "2.0.2", - "js-tokens": "3.0.2" - }, - "dependencies": { - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "2.1.1" - } - } - } - }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -132,66 +96,31 @@ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { - "balanced-match": "1.0.0", + "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, - "caller-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", - "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", - "dev": true, - "requires": { - "callsites": "0.2.0" - } - }, "callsites": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", - "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true }, "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.4.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "1.9.2" - } - }, - "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", - "dev": true, - "requires": { - "has-flag": "3.0.0" - } - } + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" } }, "chardet": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", - "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", - "dev": true - }, - "circular-json": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", - "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, "cli-cursor": { @@ -200,7 +129,7 @@ "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", "dev": true, "requires": { - "restore-cursor": "2.0.0" + "restore-cursor": "^2.0.0" } }, "cli-width": { @@ -210,18 +139,18 @@ "dev": true }, "color-convert": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.2.tgz", - "integrity": "sha512-3NUJZdhMhcdPn8vJ9v2UQJoH0qqoGUkYTgFEPZaPjEtwmmKUfNV46zZmgB2M5M4DCEQHMaCfWHCxiBflLm04Tg==", + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, "requires": { - "color-name": "1.1.1" + "color-name": "1.1.3" } }, "color-name": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha1-SxQVMEz1ACjqgWQ2Q72C6gWANok=", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, "concat-map": { @@ -236,20 +165,28 @@ "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", "dev": true, "requires": { - "nice-try": "1.0.4", - "path-key": "2.0.1", - "semver": "5.5.1", - "shebang-command": "1.2.0", - "which": "1.3.1" + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } } }, "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, "deep-is": { @@ -258,62 +195,20 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "requires": { - "object-keys": "1.0.12" - } - }, - "del": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", - "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", - "dev": true, - "requires": { - "globby": "5.0.0", - "is-path-cwd": "1.0.0", - "is-path-in-cwd": "1.0.1", - "object-assign": "4.1.1", - "pify": "2.3.0", - "pinkie-promise": "2.0.1", - "rimraf": "2.6.2" - } - }, "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "requires": { - "esutils": "2.0.2" - } - }, - "es-abstract": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.12.0.tgz", - "integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, "requires": { - "es-to-primitive": "1.1.1", - "function-bind": "1.1.1", - "has": "1.0.3", - "is-callable": "1.1.4", - "is-regex": "1.0.4" + "esutils": "^2.0.2" } }, - "es-to-primitive": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", - "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", - "dev": true, - "requires": { - "is-callable": "1.1.4", - "is-date-object": "1.0.1", - "is-symbol": "1.0.1" - } + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true }, "escape-string-regexp": { "version": "1.0.5", @@ -322,82 +217,84 @@ "dev": true }, "eslint": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.3.0.tgz", - "integrity": "sha512-N/tCqlMKkyNvAvLu+zI9AqDasnSLt00K+Hu8kdsERliC9jYEc8ck12XtjvOXrBKu8fK6RrBcN9bat6Xk++9jAg==", - "dev": true, - "requires": { - "ajv": "6.5.3", - "babel-code-frame": "6.26.0", - "chalk": "2.4.1", - "cross-spawn": "6.0.5", - "debug": "3.1.0", - "doctrine": "2.1.0", - "eslint-scope": "4.0.0", - "eslint-utils": "1.3.1", - "eslint-visitor-keys": "1.0.0", - "espree": "4.0.0", - "esquery": "1.0.1", - "esutils": "2.0.2", - "file-entry-cache": "2.0.0", - "functional-red-black-tree": "1.0.1", - "glob": "7.1.2", - "globals": "11.7.0", - "ignore": "4.0.6", - "imurmurhash": "0.1.4", - "inquirer": "5.2.0", - "is-resolvable": "1.1.0", - "js-yaml": "3.12.0", - "json-stable-stringify-without-jsonify": "1.0.1", - "levn": "0.3.0", - "lodash": "4.17.10", - "minimatch": "3.0.4", - "mkdirp": "0.5.1", - "natural-compare": "1.4.0", - "optionator": "0.8.2", - "path-is-inside": "1.0.2", - "pluralize": "7.0.0", - "progress": "2.0.0", - "regexpp": "2.0.0", - "require-uncached": "1.0.3", - "semver": "5.5.1", - "string.prototype.matchall": "2.0.0", - "strip-ansi": "4.0.0", - "strip-json-comments": "2.0.1", - "table": "4.0.3", - "text-table": "0.2.0" + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.5.1.tgz", + "integrity": "sha512-32h99BoLYStT1iq1v2P9uwpyznQ4M2jRiFB6acitKz52Gqn+vPaMDUTB1bYi1WN4Nquj2w+t+bimYUG83DC55A==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "ajv": "^6.10.0", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^1.4.2", + "eslint-visitor-keys": "^1.1.0", + "espree": "^6.1.1", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^11.7.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "inquirer": "^6.4.1", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.14", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.2", + "progress": "^2.0.0", + "regexpp": "^2.0.1", + "semver": "^6.1.2", + "strip-ansi": "^5.2.0", + "strip-json-comments": "^3.0.1", + "table": "^5.2.3", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" } }, "eslint-scope": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz", - "integrity": "sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", + "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", "dev": true, "requires": { - "esrecurse": "4.2.1", - "estraverse": "4.2.0" + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" } }, "eslint-utils": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz", - "integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==", - "dev": true + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", + "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } }, "eslint-visitor-keys": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", - "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", + "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", "dev": true }, "espree": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-4.0.0.tgz", - "integrity": "sha512-kapdTCt1bjmspxStVKX6huolXVV5ZfyZguY1lcfhVVZstce3bqxH9mcLzNn3/mlgW6wQ732+0fuG9v7h0ZQoKg==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-6.1.2.tgz", + "integrity": "sha512-2iUPuuPP+yW1PZaMSDM9eyVf8D5P0Hi8h83YtZ5bPc/zHYjII5khoixIUTMO794NOY8F/ThF1Bo8ncZILarUTA==", "dev": true, "requires": { - "acorn": "5.7.1", - "acorn-jsx": "4.1.1" + "acorn": "^7.1.0", + "acorn-jsx": "^5.1.0", + "eslint-visitor-keys": "^1.1.0" } }, "esprima": { @@ -412,7 +309,7 @@ "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", "dev": true, "requires": { - "estraverse": "4.2.0" + "estraverse": "^4.0.0" } }, "esrecurse": { @@ -421,30 +318,30 @@ "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", "dev": true, "requires": { - "estraverse": "4.2.0" + "estraverse": "^4.1.0" } }, "estraverse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true }, "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, "external-editor": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", - "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", "dev": true, "requires": { - "chardet": "0.4.2", - "iconv-lite": "0.4.23", - "tmp": "0.0.33" + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" } }, "fast-deep-equal": { @@ -471,43 +368,41 @@ "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", "dev": true, "requires": { - "escape-string-regexp": "1.0.5" + "escape-string-regexp": "^1.0.5" } }, "file-entry-cache": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", - "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", "dev": true, "requires": { - "flat-cache": "1.3.0", - "object-assign": "4.1.1" + "flat-cache": "^2.0.1" } }, "flat-cache": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.0.tgz", - "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", "dev": true, "requires": { - "circular-json": "0.3.3", - "del": "2.2.2", - "graceful-fs": "4.1.11", - "write": "0.2.1" + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" } }, + "flatted": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz", + "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==", + "dev": true + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, "functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", @@ -515,82 +410,47 @@ "dev": true }, "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.5.tgz", + "integrity": "sha512-J9dlskqUXK1OeTOYBEn5s8aMukWMwWfs+rPTn/jn50Ux4MNXVhubL1wu/j2t+H4NVI+cXEcCaYellqaPVGXNqQ==", "dev": true, "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, - "globals": { - "version": "11.7.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.7.0.tgz", - "integrity": "sha512-K8BNSPySfeShBQXsahYB/AbbWruVOTyVpgoIDnl8odPpeSfP2J5QO2oLFFdl2j7GfDCtZj2bMKar2T49itTPCg==", - "dev": true - }, - "globby": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", - "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", + "glob-parent": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", + "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", "dev": true, "requires": { - "array-union": "1.0.2", - "arrify": "1.0.1", - "glob": "7.1.2", - "object-assign": "4.1.1", - "pify": "2.3.0", - "pinkie-promise": "2.0.1" + "is-glob": "^4.0.1" } }, - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "1.1.1" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, - "requires": { - "ansi-regex": "2.1.1" - } - }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, - "has-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", - "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", - "dev": true - }, "iconv-lite": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", - "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "requires": { - "safer-buffer": "2.1.2" + "safer-buffer": ">= 2.1.2 < 3" } }, "ignore": { @@ -599,6 +459,16 @@ "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true }, + "import-fresh": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.1.0.tgz", + "integrity": "sha512-PpuksHKGt8rXfWEr9m9EHIpgyyaltBy8+eF6GJM0QCAxMgxCfucMF3mjecK2QsJr0amJW7gTqh5/wht0z2UhEQ==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -611,47 +481,41 @@ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" + "once": "^1.3.0", + "wrappy": "1" } }, "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, "inquirer": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-5.2.0.tgz", - "integrity": "sha512-E9BmnJbAKLPGonz0HeWHtbKf+EeSP93paWO3ZYoUpq/aowXvYGjjCSuashhXPpzbArIjBbji39THkxTz9ZeEUQ==", - "dev": true, - "requires": { - "ansi-escapes": "3.1.0", - "chalk": "2.4.1", - "cli-cursor": "2.1.0", - "cli-width": "2.2.0", - "external-editor": "2.2.0", - "figures": "2.0.0", - "lodash": "4.17.10", + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz", + "integrity": "sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==", + "dev": true, + "requires": { + "ansi-escapes": "^3.2.0", + "chalk": "^2.4.2", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^2.0.0", + "lodash": "^4.17.12", "mute-stream": "0.0.7", - "run-async": "2.3.0", - "rxjs": "5.5.11", - "string-width": "2.1.1", - "strip-ansi": "4.0.0", - "through": "2.3.8" + "run-async": "^2.2.0", + "rxjs": "^6.4.0", + "string-width": "^2.1.0", + "strip-ansi": "^5.1.0", + "through": "^2.3.6" } }, - "is-callable": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", - "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", - "dev": true - }, - "is-date-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", "dev": true }, "is-fullwidth-code-point": { @@ -660,28 +524,13 @@ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, - "is-path-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", - "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", - "dev": true - }, - "is-path-in-cwd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", - "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", - "dev": true, - "requires": { - "is-path-inside": "1.0.1" - } - }, - "is-path-inside": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", - "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", "dev": true, "requires": { - "path-is-inside": "1.0.2" + "is-extglob": "^2.1.1" } }, "is-promise": { @@ -690,27 +539,6 @@ "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", "dev": true }, - "is-regex": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", - "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", - "dev": true, - "requires": { - "has": "1.0.3" - } - }, - "is-resolvable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", - "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", - "dev": true - }, - "is-symbol": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", - "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=", - "dev": true - }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -718,19 +546,19 @@ "dev": true }, "js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true }, "js-yaml": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", - "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", "dev": true, "requires": { - "argparse": "1.0.10", - "esprima": "4.0.1" + "argparse": "^1.0.7", + "esprima": "^4.0.0" } }, "json-schema-traverse": { @@ -751,14 +579,14 @@ "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", "dev": true, "requires": { - "prelude-ls": "1.1.2", - "type-check": "0.3.2" + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" } }, "lodash": { - "version": "4.17.10", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", - "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", "dev": true }, "mimic-fn": { @@ -773,7 +601,7 @@ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { - "brace-expansion": "1.1.11" + "brace-expansion": "^1.1.7" } }, "minimist": { @@ -792,9 +620,9 @@ } }, "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, "mute-stream": { @@ -810,9 +638,9 @@ "dev": true }, "nice-try": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.4.tgz", - "integrity": "sha512-2NpiFHqC87y/zFke0fC0spBXL3bBsoh/p5H1EFhshxjCR5+0g2d6BiXbUFz9v1sAcxsk2htp2eQnNIci2dIYcA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, "normalize.css": { @@ -821,25 +649,13 @@ "integrity": "sha1-q/sd2CRwZ04DIrU86xqvQSk45L8=", "dev": true }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, - "object-keys": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz", - "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==", - "dev": true - }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { - "wrappy": "1.0.2" + "wrappy": "1" } }, "onetime": { @@ -848,7 +664,7 @@ "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", "dev": true, "requires": { - "mimic-fn": "1.2.0" + "mimic-fn": "^1.0.0" } }, "optionator": { @@ -857,12 +673,12 @@ "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", "dev": true, "requires": { - "deep-is": "0.1.3", - "fast-levenshtein": "2.0.6", - "levn": "0.3.0", - "prelude-ls": "1.1.2", - "type-check": "0.3.2", - "wordwrap": "1.0.0" + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.4", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "wordwrap": "~1.0.0" } }, "os-tmpdir": { @@ -871,51 +687,27 @@ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true - }, "path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", "dev": true }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "dev": true - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "dev": true, - "requires": { - "pinkie": "2.0.4" - } - }, - "pluralize": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", - "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", - "dev": true - }, "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -923,9 +715,9 @@ "dev": true }, "progress": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz", - "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true }, "punycode": { @@ -934,35 +726,16 @@ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, - "regexp.prototype.flags": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.2.0.tgz", - "integrity": "sha512-ztaw4M1VqgMwl9HlPpOuiYgItcHlunW0He2fE6eNfT6E/CF2FtYi9ofOYe4mKntstYk0Fyh/rDRBdS3AnxjlrA==", - "dev": true, - "requires": { - "define-properties": "1.1.3" - } - }, "regexpp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.0.tgz", - "integrity": "sha512-g2FAVtR8Uh8GO1Nv5wpxW7VFVwHcCEr4wyA8/MHiRkO8uHoR5ntAA8Uq3P1vvMTX/BeQiRVSpDGLd+Wn5HNOTA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", "dev": true }, - "require-uncached": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", - "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", - "dev": true, - "requires": { - "caller-path": "0.1.0", - "resolve-from": "1.0.1" - } - }, "resolve-from": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", - "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, "restore-cursor": { @@ -971,17 +744,17 @@ "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", "dev": true, "requires": { - "onetime": "2.0.1", - "signal-exit": "3.0.2" + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" } }, "rimraf": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", "dev": true, "requires": { - "glob": "7.1.2" + "glob": "^7.1.3" } }, "run-async": { @@ -990,16 +763,16 @@ "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", "dev": true, "requires": { - "is-promise": "2.1.0" + "is-promise": "^2.1.0" } }, "rxjs": { - "version": "5.5.11", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.11.tgz", - "integrity": "sha512-3bjO7UwWfA2CV7lmwYMBzj4fQ6Cq+ftHc2MvUe+WMS7wcdJ1LosDWmdjPQanYp2dBRj572p7PeU81JUxHKOcBA==", + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.3.tgz", + "integrity": "sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA==", "dev": true, "requires": { - "symbol-observable": "1.0.1" + "tslib": "^1.9.0" } }, "safer-buffer": { @@ -1009,9 +782,9 @@ "dev": true }, "semver": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", - "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true }, "shebang-command": { @@ -1020,7 +793,7 @@ "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", "dev": true, "requires": { - "shebang-regex": "1.0.0" + "shebang-regex": "^1.0.0" } }, "shebang-regex": { @@ -1036,12 +809,14 @@ "dev": true }, "slice-ansi": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", - "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", "dev": true, "requires": { - "is-fullwidth-code-point": "2.0.0" + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" } }, "sprintf-js": { @@ -1056,70 +831,76 @@ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "4.0.0" - } - }, - "string.prototype.matchall": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-2.0.0.tgz", - "integrity": "sha512-WoZ+B2ypng1dp4iFLF2kmZlwwlE19gmjgKuhL1FJfDgCREWb3ye3SDVHSzLH6bxfnvYmkCxbzkmWcQZHA4P//Q==", - "dev": true, - "requires": { - "define-properties": "1.1.3", - "es-abstract": "1.12.0", - "function-bind": "1.1.1", - "has-symbols": "1.0.0", - "regexp.prototype.flags": "1.2.0" + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } } }, "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "dev": true, "requires": { - "ansi-regex": "3.0.0" + "ansi-regex": "^4.1.0" }, "dependencies": { "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true } } }, "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", + "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", "dev": true }, "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - }, - "symbol-observable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", - "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=", - "dev": true + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } }, "table": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/table/-/table-4.0.3.tgz", - "integrity": "sha512-S7rnFITmBH1EnyKcvxBh1LjYeQMmnZtCXSEbHcH6S0NoKit24ZuFO/T1vDcLdYsLQkM188PVVhQmzKIuThNkKg==", + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", "dev": true, "requires": { - "ajv": "6.5.3", - "ajv-keywords": "3.2.0", - "chalk": "2.4.1", - "lodash": "4.17.10", - "slice-ansi": "1.0.0", - "string-width": "2.1.1" + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + }, + "dependencies": { + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + } } }, "text-table": { @@ -1140,16 +921,22 @@ "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", "dev": true, "requires": { - "os-tmpdir": "1.0.2" + "os-tmpdir": "~1.0.2" } }, + "tslib": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", + "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==", + "dev": true + }, "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", "dev": true, "requires": { - "prelude-ls": "1.1.2" + "prelude-ls": "~1.1.2" } }, "uri-js": { @@ -1158,16 +945,22 @@ "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", "dev": true, "requires": { - "punycode": "2.1.1" + "punycode": "^2.1.0" } }, + "v8-compile-cache": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz", + "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==", + "dev": true + }, "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, "requires": { - "isexe": "2.0.0" + "isexe": "^2.0.0" } }, "wordwrap": { @@ -1183,12 +976,12 @@ "dev": true }, "write": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", - "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", "dev": true, "requires": { - "mkdirp": "0.5.1" + "mkdirp": "^0.5.1" } } } diff --git a/package.json b/package.json index 8979514..80597c6 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "homepage": "http://the-echoplex.net/csscrush", "license": "MIT", "devDependencies": { - "eslint": "~5.3.0", + "eslint": "~6.5.1", "normalize.css": "7.0.0" } } From 3c656dac7f60cf5ae232439c13a8d97360ab28c7 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 24 Dec 2019 19:47:25 +0000 Subject: [PATCH 377/421] Preserve custom property case --- lib/CssCrush/Declaration.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/CssCrush/Declaration.php b/lib/CssCrush/Declaration.php index 3816e97..840120c 100644 --- a/lib/CssCrush/Declaration.php +++ b/lib/CssCrush/Declaration.php @@ -16,12 +16,19 @@ class Declaration public $index; public $skip = false; public $important = false; + public $custom = false; public $valid = true; public function __construct($property, $value, $contextIndex = 0) { - // Normalize the property name. - $property = strtolower($property); + // Normalize, but preserve case if a custom property. + if (strpos($property, '--') === 0) { + $this->custom = true; + $this->skip = true; + } + else { + $property = strtolower($property); + } if ($this->skip = strpos($property, '~') === 0) { $property = substr($property, 1); From 2657ccd0e7324443403921c6908a5c7533b986f0 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 26 Dec 2019 20:02:38 +0000 Subject: [PATCH 378/421] Add support for svg literals. Fix related issue with tokens being dropped from mixins --- lib/CssCrush/Template.php | 2 +- lib/CssCrush/Url.php | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/CssCrush/Template.php b/lib/CssCrush/Template.php index f2d1c4c..a984d18 100644 --- a/lib/CssCrush/Template.php +++ b/lib/CssCrush/Template.php @@ -140,7 +140,7 @@ public static function tokenize($str) public static function unTokenize($str) { - $str = Crush::$process->tokens->restore($str, array('u', 's'), true); + $str = Crush::$process->tokens->restore($str, ['u', 's']); return $str; } diff --git a/lib/CssCrush/Url.php b/lib/CssCrush/Url.php index c1d8381..05f680f 100644 --- a/lib/CssCrush/Url.php +++ b/lib/CssCrush/Url.php @@ -43,9 +43,13 @@ public function __toString() $this->simplify(); } + if ($this->isData) { + return 'url("http://github.com/'%20.%20preg_replace('~(?%3C!\x5c)"~', '\\"', $this->value) . '")'; + } + // Only wrap url with quotes if it contains tricky characters. $quote = ''; - if ($this->isData || preg_match('~[()*\s]~S', $this->value)) { + if (preg_match('~[()*\s]~S', $this->value)) { $quote = '"'; } From af12dac35847c929147c1ae958de3c4ce86b7e67 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 26 Dec 2019 20:16:30 +0000 Subject: [PATCH 379/421] Support backtick string matching for literal custom property values --- lib/CssCrush/Importer.php | 6 ++++++ lib/CssCrush/Regex.php | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index 3544433..caad705 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -361,6 +361,12 @@ protected function captureCommentAndString($str) if ($fullMatch[0] !== $fullMatch[strlen($fullMatch)-1]) { $fullMatch .= $fullMatch[0]; } + + // Backticked literals may have been used for custom property values. + if ($fullMatch[0] === '`') { + $fullMatch = preg_replace('~\x5c`~', '`', trim($fullMatch, '`')); + } + $label = $process->tokens->add($fullMatch, 's'); } diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php index fab903e..ceb2de3 100644 --- a/lib/CssCrush/Regex.php +++ b/lib/CssCrush/Regex.php @@ -74,7 +74,7 @@ public static function init() $patt->string = '~(\'|")(?:\\\\\1|[^\1])*?\1~xS'; $patt->commentAndString = '~ # Quoted string (to EOF if unmatched). - (\'|")(?:\\\\\1|[^\1])*?(?:\1|$) + (\'|"|`)(?:\\\\\1|[^\1])*?(?:\1|$) | # Block comment (to EOF if unmatched). /\*(?:[^*]*\*+(?:[^/*][^*]*\*+)*/|.*) From 34cc484bbae85b8b4800384903d0d81db6b24cd2 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 26 Dec 2019 20:39:22 +0000 Subject: [PATCH 380/421] Fix a compat issue with PCRE2 --- lib/CssCrush/Process.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index cec1af1..44e870e 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -1102,7 +1102,7 @@ protected function minifyColors() $minified_keywords = Color::getMinifyableKeywords(); if (! $keywords_patt) { - $keywords_patt = '~(? Date: Thu, 26 Dec 2019 20:51:00 +0000 Subject: [PATCH 381/421] Add php 7.3 and 7.4 to CI test config --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 4a11ab4..26aff96 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,8 @@ php: - 7.0 - 7.1 - 7.2 + - 7.3 + - 7.4 before_script: - composer self-update From abd7ceb7a9d8e728abc43d6717f8173c5bd40f0d Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 27 Dec 2019 11:57:30 +0000 Subject: [PATCH 382/421] Fix php 7.4 compat problems --- lib/CssCrush/Process.php | 8 ++++---- tests/unit/CssCrush/FunctionsTest.php | 8 ++++++-- tests/unit/CssCrush/UtilTest.php | 3 ++- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 44e870e..d0a3172 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -408,12 +408,12 @@ protected function placeVars(&$value) if (! $varFunction) { $varFunctionSimple = Regex::make('~\$\( \s* ({{ ident }}) \s* \)~xS'); $varFunction = new Functions(['$' => function ($rawArgs) { - list($name, $defaultValue) = Functions::parseArgsSimple($rawArgs); - if (isset(Crush::$process->vars[$name])) { - return Crush::$process->vars[$name]; + $args = Functions::parseArgsSimple($rawArgs); + if (isset(Crush::$process->vars[$args[0]])) { + return Crush::$process->vars[$args[0]]; } else { - return $defaultValue; + return isset($args[1]) ? $args[1] : ''; } }]); } diff --git a/tests/unit/CssCrush/FunctionsTest.php b/tests/unit/CssCrush/FunctionsTest.php index 8a59120..7872da8 100644 --- a/tests/unit/CssCrush/FunctionsTest.php +++ b/tests/unit/CssCrush/FunctionsTest.php @@ -11,10 +11,14 @@ public function testMakePattern() $patt = Functions::makePattern(array('foo', 'bar')); $this->assertEquals('~(?foo|bar)\(~iS', $patt); + $hashChar = version_compare(PHP_VERSION, '7.3.0') >= 0 + ? '\\#' + : '#'; + $patt = Functions::makePattern(array('foo', 'bar', '#')); - $this->assertEquals('~(?:(?foo|bar)|(?#))\(~iS', $patt); + $this->assertEquals('~(?:(?foo|bar)|(?' . $hashChar . '))\(~iS', $patt); $patt = Functions::makePattern(array('$', '#')); - $this->assertEquals('~(?\$|#)\(~iS', $patt); + $this->assertEquals('~(?\$|' . $hashChar . ')\(~iS', $patt); } } diff --git a/tests/unit/CssCrush/UtilTest.php b/tests/unit/CssCrush/UtilTest.php index 9deb517..1aeb089 100644 --- a/tests/unit/CssCrush/UtilTest.php +++ b/tests/unit/CssCrush/UtilTest.php @@ -66,7 +66,8 @@ public function testResolveUserPath() $original_path = getcwd(); chdir(__DIR__); $this_filename = basename(__FILE__); - $this->assertEquals(__FILE__, Util::resolveUserPath($this_filename)); + // Case-insensitive file systems may normalize case. + $this->assertEquals(strtolower(__FILE__), strtolower(Util::resolveUserPath($this_filename))); chdir($original_path); } From ca846ca533873819b46e819f854f38e9ea00350c Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 27 Dec 2019 12:36:15 +0000 Subject: [PATCH 383/421] Update array syntax throughout project --- lib/CssCrush/Collection.php | 4 +- lib/CssCrush/Color.php | 19 ++- lib/CssCrush/Crush.php | 16 +- lib/CssCrush/Declaration.php | 14 +- lib/CssCrush/DeclarationList.php | 34 ++-- lib/CssCrush/Functions.php | 22 +-- lib/CssCrush/IO.php | 2 +- lib/CssCrush/Importer.php | 6 +- lib/CssCrush/Mixin.php | 6 +- lib/CssCrush/Options.php | 4 +- lib/CssCrush/Regex.php | 2 +- lib/CssCrush/SelectorList.php | 6 +- lib/CssCrush/Settings.php | 2 +- lib/CssCrush/StringObject.php | 4 +- lib/CssCrush/Template.php | 2 +- lib/CssCrush/Tokens.php | 4 +- lib/CssCrush/Url.php | 4 +- lib/CssCrush/Util.php | 10 +- lib/CssCrush/Version.php | 2 +- lib/functions.php | 16 +- misc/formatters.php | 4 +- plugins/aria.php | 4 +- plugins/canvas.php | 64 ++++---- plugins/color.php | 2 +- plugins/ease.php | 10 +- plugins/forms.php | 8 +- plugins/px2em.php | 2 +- plugins/rem.php | 6 +- plugins/svg.php | 198 ++++++++++++------------ tests/unit/CssCrush/ColorTest.php | 6 +- tests/unit/CssCrush/DeclarationTest.php | 4 +- tests/unit/CssCrush/FunctionsTest.php | 6 +- tests/unit/CssCrush/OptionsTest.php | 16 +- tests/unit/CssCrush/RegexTest.php | 8 +- tests/unit/CssCrush/TemplateTest.php | 12 +- tests/unit/CssCrush/TokensTest.php | 4 +- tests/unit/CssCrush/UrlTest.php | 2 +- tests/unit/CssCrush/UtilTest.php | 16 +- tests/unit/api/apiTest.php | 26 ++-- tests/unit/cli/context/crushfile.php | 2 +- 40 files changed, 287 insertions(+), 292 deletions(-) diff --git a/lib/CssCrush/Collection.php b/lib/CssCrush/Collection.php index dbaf92f..d811e0f 100644 --- a/lib/CssCrush/Collection.php +++ b/lib/CssCrush/Collection.php @@ -40,7 +40,7 @@ public function filter($filterer, $op = '===') { if (is_array($filterer)) { - $ops = array( + $ops = [ '===' => function ($item) use ($filterer) { foreach ($filterer as $property => $value) { if (Collection::value($item, $property) !== $value) { @@ -57,7 +57,7 @@ public function filter($filterer, $op = '===') } return true; }, - ); + ]; $callback = $ops[$op]; } diff --git a/lib/CssCrush/Color.php b/lib/CssCrush/Color.php index 5e45818..048ddda 100644 --- a/lib/CssCrush/Color.php +++ b/lib/CssCrush/Color.php @@ -16,7 +16,7 @@ public static function getKeywords() if (! isset($namedColors)) { if ($colors = Util::parseIni(Crush::$dir . '/misc/color-keywords.ini')) { foreach ($colors as $name => $rgb) { - $namedColors[$name] = array_map('floatval', explode(',', $rgb)) + array(0,0,0,1); + $namedColors[$name] = array_map('floatval', explode(',', $rgb)) + [0, 0, 0, 1]; } } } @@ -192,7 +192,7 @@ public static function rgbToHsl(array $rgba) $h /= 6; } - return array($h, $s, $l, $a); + return [$h, $s, $l, $a]; } /** @@ -226,7 +226,7 @@ public static function hslToRgb(array $hsla) $b = self::hueToRgb($p, $q, $h - 1 / 3); } - return array(round($r * 255), round($g * 255), round($b * 255), $a); + return [round($r * 255), round($g * 255), round($b * 255), $a]; } // Convert percentages to points (0-255). @@ -267,7 +267,7 @@ public static function cssHslToRgb(array $hsla) } list($s, $l) = $hsla; - return self::hslToRgb(array($h, $s, $l, $a)); + return self::hslToRgb([$h, $s, $l, $a]); } public static function hueToRgb($p, $q, $t) @@ -338,9 +338,9 @@ public static function colorSplit($str) } // If non-alpha color return early. - if (! in_array($type, array('hsla', 'rgba'))) { + if (! in_array($type, ['hsla', 'rgba'])) { - return array($color, 1); + return [$color, 1]; } // Strip all whitespace. @@ -357,8 +357,7 @@ public static function colorSplit($str) $color = "$m[1]($m[2])"; } - // Return color value and alpha component seperated. - return array($color, $opacity); + return [$color, $opacity]; } @@ -367,12 +366,12 @@ public static function colorSplit($str) protected $value; protected $hslColorSpace; - protected $namedComponents = array( + protected $namedComponents = [ 'red' => 0, 'green' => 1, 'blue' => 2, 'alpha' => 3, - ); + ]; public $isValid; public function __construct($color, $useHslColorSpace = false) diff --git a/lib/CssCrush/Crush.php b/lib/CssCrush/Crush.php index 0d2db47..f3b103e 100644 --- a/lib/CssCrush/Crush.php +++ b/lib/CssCrush/Crush.php @@ -23,7 +23,7 @@ public static function init() self::$config = new \stdClass(); - self::$config->pluginDirs = array(self::$dir . '/plugins'); + self::$config->pluginDirs = [self::$dir . '/plugins']; self::$config->scriptDir = dirname(realpath($_SERVER['SCRIPT_FILENAME'])); self::$config->docRoot = self::resolveDocRoot(); self::$config->logger = new Logger(); @@ -33,13 +33,13 @@ public static function init() self::$config->vars = []; self::$config->aliasesFile = self::$dir . '/aliases.ini'; self::$config->aliases = []; - self::$config->bareAliases = array( + self::$config->bareAliases = [ 'properties' => [], 'functions' => [], 'function_groups' => [], 'declarations' => [], 'at-rules' => [], - ); + ]; self::$config->options = new Options(); require_once self::$dir . '/misc/formatters.php'; @@ -150,12 +150,12 @@ public static function parseAliasesFile($file) // Try to detect the vendor from property and value in turn. if ( - preg_match($regex->vendorPrefix, $p, $m) || - preg_match($regex->vendorPrefix, $v, $m) + preg_match($regex->vendorPrefix, $p, $m) + || preg_match($regex->vendorPrefix, $v, $m) ) { $vendor = $m[1]; } - $alias = array($p, $v, $vendor); + $alias = [$p, $v, $vendor]; } $store[$prop][$value] = $aliases; } @@ -194,7 +194,7 @@ public static function parseAliasesFile($file) // Persisting dummy aliases for testing purposes. $tree['properties']['foo'] = $tree['at-rules']['foo'] = - $tree['functions']['foo'] = array('-webkit-foo', '-moz-foo', '-ms-foo'); + $tree['functions']['foo'] = ['-webkit-foo', '-moz-foo', '-ms-foo']; return $tree; } @@ -235,7 +235,7 @@ public static function runStat() case 'vars': $process->stat['vars'] = array_map(function ($item) use ($process) { - return $process->tokens->restore($process->functions->apply($item), array('s', 'u', 'p')); + return $process->tokens->restore($process->functions->apply($item), ['s', 'u', 'p']); }, $process->vars); break; diff --git a/lib/CssCrush/Declaration.php b/lib/CssCrush/Declaration.php index 840120c..34cdf18 100644 --- a/lib/CssCrush/Declaration.php +++ b/lib/CssCrush/Declaration.php @@ -51,7 +51,7 @@ public function __construct($property, $value, $contextIndex = 0) $this->important = true; } - Crush::$process->emit('declaration_preprocess', array('property' => &$property, 'value' => &$value)); + Crush::$process->emit('declaration_preprocess', ['property' => &$property, 'value' => &$value]); // Reject declarations with empty CSS values. if ($value === false || $value === '') { @@ -86,25 +86,25 @@ public function process($parentRule) { static $thisFunction; if (! $thisFunction) { - $thisFunction = new Functions(array('this' => 'CssCrush\fn__this')); + $thisFunction = new Functions(['this' => 'CssCrush\fn__this']); } if (! $this->skip) { // this() function needs to be called exclusively because it is self referencing. - $context = (object) array( + $context = (object) [ 'rule' => $parentRule - ); + ]; $this->value = $thisFunction->apply($this->value, $context); if (isset($parentRule->declarations->data)) { - $parentRule->declarations->data += array($this->property => $this->value); + $parentRule->declarations->data += [$this->property => $this->value]; } - $context = (object) array( + $context = (object) [ 'rule' => $parentRule, 'property' => $this->property - ); + ]; $this->value = Crush::$process->functions->apply($this->value, $context); } diff --git a/lib/CssCrush/DeclarationList.php b/lib/CssCrush/DeclarationList.php index 34dab53..c61c649 100644 --- a/lib/CssCrush/DeclarationList.php +++ b/lib/CssCrush/DeclarationList.php @@ -370,14 +370,14 @@ public static function parse($str, $options = []) $str = Util::stripCommentTokens($str); $lines = preg_split('~\s*;\s*~', $str, null, PREG_SPLIT_NO_EMPTY); - $options += array( + $options += [ 'keyed' => false, 'ignore_directives' => false, 'lowercase_keys' => false, 'context' => null, 'flatten' => false, 'apply_hooks' => false, - ); + ]; $pairs = []; @@ -406,10 +406,10 @@ public static function parse($str, $options = []) } if ($options['apply_hooks']) { - Crush::$process->emit('declaration_preprocess', array( + Crush::$process->emit('declaration_preprocess', [ 'property' => &$property, 'value' => &$value, - )); + ]); } } else { @@ -421,16 +421,16 @@ public static function parse($str, $options = []) } if ($property === 'mixin' && $options['flatten']) { - $pairs = Mixin::merge($pairs, $value, array( + $pairs = Mixin::merge($pairs, $value, [ 'keyed' => $options['keyed'], 'context' => $options['context'], - )); + ]); } elseif ($options['keyed']) { $pairs[$property] = $value; } else { - $pairs[] = array($property, $value); + $pairs[] = [$property, $value]; } } @@ -446,7 +446,7 @@ public function flatten() $newSet = []; foreach ($this->store as $declaration) { if (is_array($declaration) && $declaration[0] === 'mixin') { - foreach (Mixin::merge([], $declaration[1], array('context' => $this->rule)) as $mixable) { + foreach (Mixin::merge([], $declaration[1], ['context' => $this->rule]) as $mixable) { if ($mixable instanceof Declaration) { $clone = clone $mixable; $clone->index = count($newSet); @@ -497,7 +497,7 @@ public function expandData($dataset, $property) { // Expand shorthand properties to make them available // as data for this() and query(). - static $expandables = array( + static $expandables = [ 'margin-top' => 'margin', 'margin-right' => 'margin', 'margin-bottom' => 'margin', @@ -518,7 +518,7 @@ public function expandData($dataset, $property) 'border-right-color' => 'border-color', 'border-bottom-color' => 'border-color', 'border-left-color' => 'border-color', - ); + ]; $dataset =& $this->{$dataset}; $property_group = isset($expandables[$property]) ? $expandables[$property] : null; @@ -560,11 +560,11 @@ public function expandData($dataset, $property) } // 3 values. elseif (isset($parts[2])) { - $placeholders = array($parts[0], $parts[1], $parts[2], $parts[1]); + $placeholders = [$parts[0], $parts[1], $parts[2], $parts[1]]; } // 2 values. elseif (isset($parts[1])) { - $placeholders = array($parts[0], $parts[1], $parts[0], $parts[1]); + $placeholders = [$parts[0], $parts[1], $parts[0], $parts[1]]; } // 1 value. else { @@ -573,25 +573,25 @@ public function expandData($dataset, $property) // Set positional variants. if ($property_group === 'border-radius') { - $positions = array( + $positions = [ 'top-left', 'top-right', 'bottom-right', 'bottom-left', - ); + ]; } else { - $positions = array( + $positions = [ 'top', 'right', 'bottom', 'left', - ); + ]; } foreach ($positions as $index => $position) { $prop = sprintf($trbl_fmt, $position); - $dataset += array($prop => $placeholders[$index]); + $dataset += [$prop => $placeholders[$index]]; } } } diff --git a/lib/CssCrush/Functions.php b/lib/CssCrush/Functions.php index e962217..999937c 100644 --- a/lib/CssCrush/Functions.php +++ b/lib/CssCrush/Functions.php @@ -8,7 +8,7 @@ class Functions { - protected static $builtins = array( + protected static $builtins = [ // These functions must come first in this order. 'query' => 'CssCrush\fn__query', @@ -21,7 +21,7 @@ class Functions 's-adjust' => 'CssCrush\fn__s_adjust', 'l-adjust' => 'CssCrush\fn__l_adjust', 'a-adjust' => 'CssCrush\fn__a_adjust', - ); + ]; public $register = []; @@ -176,8 +176,8 @@ function fn__math($input) { // Swap in math constants. $expression = preg_replace( - array('~\bpi\b~i'), - array(M_PI), + ['~\bpi\b~i'], + [M_PI], $expression); // If no unit is specified scan expression. @@ -209,32 +209,32 @@ function fn__math($input) { function fn__hsla_adjust($input) { list($color, $h, $s, $l, $a) = array_pad(Functions::parseArgs($input, true), 5, 0); - return Color::test($color) ? Color::colorAdjust($color, array($h, $s, $l, $a)) : ''; + return Color::test($color) ? Color::colorAdjust($color, [$h, $s, $l, $a]) : ''; } function fn__hsl_adjust($input) { list($color, $h, $s, $l) = array_pad(Functions::parseArgs($input, true), 4, 0); - return Color::test($color) ? Color::colorAdjust($color, array($h, $s, $l, 0)) : ''; + return Color::test($color) ? Color::colorAdjust($color, [$h, $s, $l, 0]) : ''; } function fn__h_adjust($input) { list($color, $h) = array_pad(Functions::parseArgs($input, true), 2, 0); - return Color::test($color) ? Color::colorAdjust($color, array($h, 0, 0, 0)) : ''; + return Color::test($color) ? Color::colorAdjust($color, [$h, 0, 0, 0]) : ''; } function fn__s_adjust($input) { list($color, $s) = array_pad(Functions::parseArgs($input, true), 2, 0); - return Color::test($color) ? Color::colorAdjust($color, array(0, $s, 0, 0)) : ''; + return Color::test($color) ? Color::colorAdjust($color, [0, $s, 0, 0]) : ''; } function fn__l_adjust($input) { list($color, $l) = array_pad(Functions::parseArgs($input, true), 2, 0); - return Color::test($color) ? Color::colorAdjust($color, array(0, 0, $l, 0)) : ''; + return Color::test($color) ? Color::colorAdjust($color, [0, 0, $l, 0]) : ''; } function fn__a_adjust($input) { list($color, $a) = array_pad(Functions::parseArgs($input, true), 2, 0); - return Color::test($color) ? Color::colorAdjust($color, array(0, 0, 0, $a)) : ''; + return Color::test($color) ? Color::colorAdjust($color, [0, 0, 0, $a]) : ''; } function fn__this($input, $context) { @@ -273,7 +273,7 @@ function fn__query($input, $context) { return ''; } - list($target, $property, $fallback) = $args + array(null, $context->property, null); + list($target, $property, $fallback) = $args + [null, $context->property, null]; if (strtolower($property) === 'default') { $property = $context->property; diff --git a/lib/CssCrush/IO.php b/lib/CssCrush/IO.php index 373ceed..cba1611 100644 --- a/lib/CssCrush/IO.php +++ b/lib/CssCrush/IO.php @@ -98,7 +98,7 @@ public function validateCache() $data =& $process->cacheData[$filename]; // Make stack of file mtimes starting with the input file. - $file_sums = array($input->mtime); + $file_sums = [$input->mtime]; foreach ($data['imports'] as $import_file) { // Check if this is docroot relative or input dir relative. diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index caad705..bed7a23 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -148,11 +148,11 @@ public function collate() // Save only if caching is on and the hostfile object is associated with a real file. if ($input->path && $options->cache) { - $process->cacheData[$process->output->filename] = array( + $process->cacheData[$process->output->filename] = [ 'imports' => $filenames, 'datem_sum' => array_sum($mtimes) + $input->mtime, 'options' => $options->get(), - ); + ]; $process->io->saveCacheData(); } @@ -316,7 +316,7 @@ protected function addMarkers(&$str) $line = substr_count($before, "\n"); } - $pointData = array($currentFileIndex, $line); + $pointData = [$currentFileIndex, $line]; // Source maps require column index too. if ($process->generateMap) { diff --git a/lib/CssCrush/Mixin.php b/lib/CssCrush/Mixin.php index 4714b3e..b4df1a4 100644 --- a/lib/CssCrush/Mixin.php +++ b/lib/CssCrush/Mixin.php @@ -61,10 +61,10 @@ public static function call($message, $context = null) $args = Util::splitDelimList($raw_args); } - return DeclarationList::parse($mixable->template->__invoke($args), array( + return DeclarationList::parse($mixable->template->__invoke($args), [ 'flatten' => true, 'context' => $mixable, - )); + ]); } elseif ($mixable instanceof Rule) { @@ -96,7 +96,7 @@ public static function merge(array $input, $message_list, $options = []) $input[$property] = $value; } else { - $input[] = array($property, $value); + $input[] = [$property, $value]; } } } diff --git a/lib/CssCrush/Options.php b/lib/CssCrush/Options.php index aadb2d2..0081e10 100644 --- a/lib/CssCrush/Options.php +++ b/lib/CssCrush/Options.php @@ -11,7 +11,7 @@ class Options protected $computedOptions = []; protected $inputOptions = []; - protected static $standardOptions = array( + protected static $standardOptions = [ 'minify' => true, 'formatter' => null, 'versioning' => true, @@ -31,7 +31,7 @@ class Options 'stat_dump' => false, 'source_map' => false, 'newlines' => 'use-platform', - ); + ]; public function __construct(array $options = [], Options $defaults = null) { diff --git a/lib/CssCrush/Regex.php b/lib/CssCrush/Regex.php index ceb2de3..deba844 100644 --- a/lib/CssCrush/Regex.php +++ b/lib/CssCrush/Regex.php @@ -68,7 +68,7 @@ public static function init() // Functions. $patt->functionTest = Regex::make('~{{ LB }} (?{{ ident }}) \(~xS'); - $patt->thisFunction = Functions::makePattern(array('this')); + $patt->thisFunction = Functions::makePattern(['this']); // Strings and comments. $patt->string = '~(\'|")(?:\\\\\1|[^\1])*?\1~xS'; diff --git a/lib/CssCrush/SelectorList.php b/lib/CssCrush/SelectorList.php index b4c98a9..87ec177 100644 --- a/lib/CssCrush/SelectorList.php +++ b/lib/CssCrush/SelectorList.php @@ -53,7 +53,7 @@ public function expand() $selectors = []; // Allowing empty strings for more expansion possibilities. - foreach (Util::splitDelimList($m['parens_content'][0], array('allow_empty_strings' => true)) as $segment) { + foreach (Util::splitDelimList($m['parens_content'][0], ['allow_empty_strings' => true]) as $segment) { if ($selector = trim("$before$segment$after")) { $selectors[$selector] = true; } @@ -75,7 +75,7 @@ public function expand() foreach ($running_stack as $selector => $bool) { $selectors = $expand($selector); if (! $selectors) { - $flattened_stack += array($selector => true); + $flattened_stack += [$selector => true]; } else { $loop_stack += $selectors; @@ -88,7 +88,7 @@ public function expand() return $flattened_stack; } - return array($selector_string => true); + return [$selector_string => true]; }; } diff --git a/lib/CssCrush/Settings.php b/lib/CssCrush/Settings.php index adff31a..77938da 100644 --- a/lib/CssCrush/Settings.php +++ b/lib/CssCrush/Settings.php @@ -27,7 +27,7 @@ public function get($name, $fallback = null) $value = isset($this->store[$name]) ? $this->store[$name] : null; // Backwards compat for variable based settings. - if (! $value && in_array($name, array('rem-all', 'rem-mode', 'rem-base', 'px2rem-base', 'px2em-base'))) { + if (! $value && in_array($name, ['rem-all', 'rem-mode', 'rem-base', 'px2rem-base', 'px2em-base'])) { $var_setting = function ($var_name) { return isset(Crush::$process->vars[$var_name]) ? diff --git a/lib/CssCrush/StringObject.php b/lib/CssCrush/StringObject.php index 573a66a..18b4eec 100644 --- a/lib/CssCrush/StringObject.php +++ b/lib/CssCrush/StringObject.php @@ -124,13 +124,13 @@ public function captureDirectives($directive, $parse_options = []) $directive = '(?:' . implode('|', $directive) . ')'; } - $parse_options += array( + $parse_options += [ 'keyed' => true, 'lowercase_keys' => true, 'ignore_directives' => true, 'singles' => false, 'flatten' => false, - ); + ]; if ($parse_options['singles']) { $patt = Regex::make('~@(?i)' . $directive . '(?-i)(?:\s*{{ block }}|\s+(?{{ ident }})\s+(?[^;]+)\s*;)~S'); diff --git a/lib/CssCrush/Template.php b/lib/CssCrush/Template.php index a984d18..034ed27 100644 --- a/lib/CssCrush/Template.php +++ b/lib/CssCrush/Template.php @@ -120,7 +120,7 @@ public function prepare(array $args, $persist = true) } } - $substitutions = array($find, $replace); + $substitutions = [$find, $replace]; // Persist substitutions by default. if ($persist) { diff --git a/lib/CssCrush/Tokens.php b/lib/CssCrush/Tokens.php index 92bccf7..7726955 100644 --- a/lib/CssCrush/Tokens.php +++ b/lib/CssCrush/Tokens.php @@ -13,13 +13,13 @@ class Tokens public function __construct(array $types = null) { - $types = $types ?: array( + $types = $types ?: [ 's', // strings. 'c', // comments. 'r', // rules. 'u', // URLs. 't', // traces. - ); + ]; $this->store = new \stdClass; $this->ids = new \stdClass; diff --git a/lib/CssCrush/Url.php b/lib/CssCrush/Url.php index 05f680f..0ed604d 100644 --- a/lib/CssCrush/Url.php +++ b/lib/CssCrush/Url.php @@ -153,7 +153,7 @@ public function toData() $file_ext = pathinfo($file, PATHINFO_EXTENSION); // Only allow certain extensions - static $allowed_file_extensions = array( + static $allowed_file_extensions = [ 'woff' => 'application/x-font-woff;charset=utf-8', 'ttf' => 'font/truetype;charset=utf-8', 'svg' => 'image/svg+xml', @@ -162,7 +162,7 @@ public function toData() 'jpeg' => 'image/jpg', 'jpg' => 'image/jpg', 'png' => 'image/png', - ); + ]; if (! isset($allowed_file_extensions[$file_ext])) { diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php index 53d400a..f0743e2 100644 --- a/lib/CssCrush/Util.php +++ b/lib/CssCrush/Util.php @@ -116,14 +116,14 @@ public static function normalizeWhiteSpace($str) { static $find, $replace; if (! $find) { - $replacements = array( + $replacements = [ // Convert all whitespace sequences to a single space. '~\s+~S' => ' ', // Trim bracket whitespace where it's safe to do it. '~([\[(]) | ([\])])| ?([{}]) ?~S' => '${1}${2}${3}', // Trim whitespace around delimiters and special characters. '~ ?([;,]) ?~S' => '$1', - ); + ]; $find = array_keys($replacements); $replace = array_values($replacements); } @@ -133,16 +133,16 @@ public static function normalizeWhiteSpace($str) public static function splitDelimList($str, $options = []) { - extract($options + array( + extract($options + [ 'delim' => ',', 'regex' => false, 'allow_empty_strings' => false, - )); + ]); $str = trim($str); if (! $regex && strpos($str, $delim) === false) { - return ! $allow_empty_strings && ! strlen($str) ? [] : array($str); + return ! $allow_empty_strings && ! strlen($str) ? [] : [$str]; } if ($match_count = preg_match_all(Regex::$patt->parens, $str, $matches)) { diff --git a/lib/CssCrush/Version.php b/lib/CssCrush/Version.php index efa51f8..f15d05b 100644 --- a/lib/CssCrush/Version.php +++ b/lib/CssCrush/Version.php @@ -60,7 +60,7 @@ public function compare($version_string) $test = new Version($version_string); - foreach (array('major', 'minor', 'patch') as $level) { + foreach (['major', 'minor', 'patch'] as $level) { if ($this->{$level} < $test->{$level}) { diff --git a/lib/functions.php b/lib/functions.php index 2348e2e..c8abc02 100644 --- a/lib/functions.php +++ b/lib/functions.php @@ -14,7 +14,7 @@ function csscrush_file($file, $options = []) { try { - Crush::$process = new CssCrush\Process($options, array('type' => 'file', 'data' => $file)); + Crush::$process = new CssCrush\Process($options, ['type' => 'file', 'data' => $file]); } catch (\Exception $e) { CssCrush\warning($e->getMessage()); @@ -36,11 +36,11 @@ function csscrush_tag($file, $options = [], $tag_attributes = []) { $file = csscrush_file($file, $options); if ($file && $file->url) { $tag_attributes['href'] = $file->url; - $tag_attributes += array( + $tag_attributes += [ 'rel' => 'stylesheet', 'media' => 'all', - ); - $attrs = CssCrush\Util::htmlAttributes($tag_attributes, array('rel', 'href', 'media')); + ]; + $attrs = CssCrush\Util::htmlAttributes($tag_attributes, ['rel', 'href', 'media']); return "\n"; } @@ -86,7 +86,7 @@ function csscrush_string($string, $options = []) { $options['boilerplate'] = false; } - Crush::$process = new CssCrush\Process($options, array('type' => 'filter', 'data' => $string)); + Crush::$process = new CssCrush\Process($options, ['type' => 'filter', 'data' => $string]); return Crush::$process->compile()->__toString(); } @@ -99,7 +99,7 @@ function csscrush_string($string, $options = []) { */ function csscrush_set($object_name, $modifier) { - if (in_array($object_name, array('options', 'config'))) { + if (in_array($object_name, ['options', 'config'])) { $pointer = $object_name === 'options' ? Crush::$config->options : Crush::$config; @@ -122,7 +122,7 @@ function csscrush_set($object_name, $modifier) { */ function csscrush_get($object_name, $property = null) { - if (in_array($object_name, array('options', 'config'))) { + if (in_array($object_name, ['options', 'config'])) { $pointer = $object_name === 'options' ? Crush::$config->options : Crush::$config; @@ -161,7 +161,7 @@ function csscrush_stat() { // Get logged errors as late as possible. $stats['errors'] = $process->errors; $stats['warnings'] = $process->warnings; - $stats += array('compile_time' => 0); + $stats += ['compile_time' => 0]; return $stats; } diff --git a/misc/formatters.php b/misc/formatters.php index c65ed75..2117837 100644 --- a/misc/formatters.php +++ b/misc/formatters.php @@ -6,11 +6,11 @@ */ namespace CssCrush; -Crush::$config->formatters = array( +Crush::$config->formatters = [ 'single-line' => 'CssCrush\fmtr_single', 'padded' => 'CssCrush\fmtr_padded', 'block' => 'CssCrush\fmtr_block', -); +]; function fmtr_single($rule) { diff --git a/plugins/aria.php b/plugins/aria.php index afbc220..e8408ed 100644 --- a/plugins/aria.php +++ b/plugins/aria.php @@ -22,7 +22,7 @@ function aria() { return $args ? "[$property=\"#(0)\"]" : "[$property]"; }; }; - $aria = array( + $aria = [ // Roles. 'role' => $optional_value('role'), @@ -63,7 +63,7 @@ function aria() { 'aria-valuemin' => $optional_value('aria-valuemin'), 'aria-valuenow' => $optional_value('aria-valuenow'), 'aria-valuetext' => $optional_value('aria-valuetext'), - ); + ]; } return $aria; diff --git a/plugins/canvas.php b/plugins/canvas.php index 75e3343..85cdc10 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -60,18 +60,18 @@ function canvas_generator($input, $context) { // Apply args to template. $block = $canvas_defs[$name]($args); - $raw = DeclarationList::parse($block, array( + $raw = DeclarationList::parse($block, [ 'keyed' => true, 'lowercase_keys' => true, 'flatten' => true, 'apply_hooks' => true, - )); + ]); // Create canvas object. $canvas = new Canvas(); // Parseable canvas attributes with default values. - static $schema = array( + static $schema = [ 'fill' => null, 'background-fill' => null, 'src' => null, @@ -79,7 +79,7 @@ function canvas_generator($input, $context) { 'width' => null, 'height' => null, 'margin' => 0, - ); + ]; // Resolve properties, set defaults if not present. $canvas->raw = array_intersect_key($raw, $schema) + $schema; @@ -161,7 +161,7 @@ function canvas_generator($input, $context) { // Set defaults. $canvas->width = isset($canvas->width) ? intval($canvas->width) : 100; $canvas->height = isset($canvas->height) ? intval($canvas->height) : 100; - $canvas->fills += array('fill' => 'black'); + $canvas->fills += ['fill' => 'black']; // Create base. canvas_create($canvas); @@ -211,18 +211,16 @@ function canvas_generator($input, $context) { function canvas_fn_linear_gradient($input, $context) { - $args = Functions::parseArgs($input) + array( - 'white', 'black', - ); + $args = Functions::parseArgs($input) + ['white', 'black']; $first_arg = strtolower($args[0]); - static $directions = array( - 'to top' => array('vertical', true), - 'to right' => array('horizontal', false), - 'to bottom' => array('vertical', false), - 'to left' => array('horizontal', true), - ); + static $directions = [ + 'to top' => ['vertical', true], + 'to right' => ['horizontal', false], + 'to bottom' => ['vertical', false], + 'to left' => ['horizontal', true], + ]; if (isset($directions[$first_arg])) { list($direction, $flip) = $directions[$first_arg]; @@ -241,11 +239,11 @@ function canvas_fn_linear_gradient($input, $context) { // Start color. $color = Color::parse($args[0]); - $fill->stops[] = $color ? $color : array(0,0,0,1); + $fill->stops[] = $color ? $color : [0, 0, 0, 1]; // End color. $color = Color::parse($args[1]); - $fill->stops[] = $color ? $color : array(255,255,255,1); + $fill->stops[] = $color ? $color : [255, 255, 255, 1]; if ($flip) { $fill->stops = array_reverse($fill->stops); @@ -258,7 +256,7 @@ function canvas_fn_filter($input, $context) { $args = Functions::parseArgs($input); - array_unshift($context->canvas->filters, array($context->function, $args)); + array_unshift($context->canvas->filters, [$context->function, $args]); } @@ -282,12 +280,12 @@ function canvas_apply_filters($canvas, $src) { break; case 'colorize': - $rgb = $args + array('black'); + $rgb = $args + ['black']; if (count($rgb) === 1) { // If only one argument parse it as a CSS color value. $rgb = Color::parse($rgb[0]); if (! $rgb) { - $rgb = array(0,0,0); + $rgb = [0, 0, 0]; } } imagefilter($src->image, IMG_FILTER_COLORIZE, $rgb[0], $rgb[1], $rgb[2]); @@ -333,11 +331,11 @@ function canvas_apply_css_funcs($canvas) { if (! $functions) { $functions = new stdClass(); - $functions->fill = new Functions(array('canvas-linear-gradient' => 'CssCrush\canvas_fn_linear_gradient')); + $functions->fill = new Functions(['canvas-linear-gradient' => 'CssCrush\canvas_fn_linear_gradient']); $functions->generic = new Functions(array_diff_key(Crush::$process->functions->register, $functions->fill->register)); - $functions->filter = new Functions(array( + $functions->filter = new Functions([ 'contrast' => 'CssCrush\canvas_fn_filter', 'opacity' => 'CssCrush\canvas_fn_filter', 'colorize' => 'CssCrush\canvas_fn_filter', @@ -346,7 +344,7 @@ function canvas_apply_css_funcs($canvas) { 'brightness' => 'CssCrush\canvas_fn_filter', 'invert' => 'CssCrush\canvas_fn_filter', 'blur' => 'CssCrush\canvas_fn_filter', - )); + ]); } $context = new stdClass(); @@ -360,7 +358,7 @@ function canvas_apply_css_funcs($canvas) { $value = $functions->generic->apply($value); $context->canvas = $canvas; - if (in_array($property, array('fill', 'background-fill'))) { + if (in_array($property, ['fill', 'background-fill'])) { $context->currentProperty = $property; $value = $functions->fill->apply($value, $context); } @@ -377,34 +375,34 @@ function canvas_preprocess($canvas) { $parts = canvas_parselist($canvas->raw['margin']); $count = count($parts); if ($count === 1) { - $margin = array($parts[0], $parts[0], $parts[0], $parts[0]); + $margin = [$parts[0], $parts[0], $parts[0], $parts[0]]; } elseif ($count === 2) { - $margin = array($parts[0], $parts[1], $parts[0], $parts[1]); + $margin = [$parts[0], $parts[1], $parts[0], $parts[1]]; } elseif ($count === 3) { - $margin = array($parts[0], $parts[1], $parts[2], $parts[1]); + $margin = [$parts[0], $parts[1], $parts[2], $parts[1]]; } else { $margin = $parts; } } else { - $margin = array(0,0,0,0); + $margin = [0, 0, 0, 0]; } - foreach (array('fill', 'background-fill') as $fill_name) { + foreach (['fill', 'background-fill'] as $fill_name) { if (isset($canvas->raw[$fill_name])) { $canvas->fills[$fill_name] = $canvas->raw[$fill_name]; } } - $canvas->margin = (object) array( + $canvas->margin = (object) [ 'top' => $margin[0], 'right' => $margin[1], 'bottom' => $margin[2], 'left' => $margin[3], - ); + ]; $canvas->width = $canvas->raw['width']; $canvas->height = $canvas->raw['height']; } @@ -437,13 +435,13 @@ function canvas_fetch_src($url_token) { break; } if ($image) { - return (object) array( + return (object) [ 'file' => $file, 'info' => $info, 'width' => $info[0], 'height' => $info[1], 'image' => $image, - ); + ]; } } } @@ -614,7 +612,7 @@ function canvas_requirements() { else { $gd_info = implode('|', array_keys(array_filter(gd_info()))); - foreach (array('jpe?g' => 'JPG', 'png' => 'PNG') as $file_ext_patt => $file_ext) { + foreach (['jpe?g' => 'JPG', 'png' => 'PNG'] as $file_ext_patt => $file_ext) { if (! preg_match("~\b(?$file_ext_patt) support\b~i", $gd_info)) { $requirements_met = false; warning("GD extension has no $file_ext support."); diff --git a/plugins/color.php b/plugins/color.php index 30cedaa..f5c42af 100644 --- a/plugins/color.php +++ b/plugins/color.php @@ -22,7 +22,7 @@ function color(&$declaration) { function color_capture($process) { - $captured_keywords = $process->string->captureDirectives('color', array('singles' => true)); + $captured_keywords = $process->string->captureDirectives('color', ['singles' => true]); if ($captured_keywords) { diff --git a/plugins/ease.php b/plugins/ease.php index cacc4de..fce8ad6 100644 --- a/plugins/ease.php +++ b/plugins/ease.php @@ -14,7 +14,7 @@ function ease(Rule $rule) { static $find, $replace, $easing_properties; if (! $find) { - $easings = array( + $easings = [ 'ease-in-out-back' => 'cubic-bezier(.680,-0.550,.265,1.550)', 'ease-in-out-circ' => 'cubic-bezier(.785,.135,.150,.860)', 'ease-in-out-expo' => 'cubic-bezier(1,0,0,1)', @@ -39,12 +39,12 @@ function ease(Rule $rule) { 'ease-in-quart' => 'cubic-bezier(.895,.030,.685,.220)', 'ease-in-cubic' => 'cubic-bezier(.550,.055,.675,.190)', 'ease-in-quad' => 'cubic-bezier(.550,.085,.680,.530)', - ); + ]; - $easing_properties = array( + $easing_properties = [ 'transition' => true, 'transition-timing-function' => true, - ); + ]; foreach ($easings as $property => $value) { $patt = Regex::make("~{{ LB }}$property{{ RB }}~i"); @@ -57,7 +57,7 @@ function ease(Rule $rule) { return; } - foreach ($rule->declarations->filter(array('skip' => false)) as $declaration) { + foreach ($rule->declarations->filter(['skip' => false]) as $declaration) { if (isset($easing_properties[$declaration->canonicalProperty])) { $declaration->value = preg_replace($find, $replace, $declaration->value); } diff --git a/plugins/forms.php b/plugins/forms.php index 842ec7c..8d4321b 100644 --- a/plugins/forms.php +++ b/plugins/forms.php @@ -17,11 +17,11 @@ }); function forms() { - return array( - 'input' => array( + return [ + 'input' => [ 'type' => 'splat', 'handler' => 'input[type=#(text)]', - ), + ], 'checkbox' => 'input[type="checkbox"]', 'radio' => 'input[type="radio"]', 'file' => 'input[type="file"]', @@ -29,5 +29,5 @@ function forms() { 'password' => 'input[type="password"]', 'submit' => 'input[type="submit"]', 'text' => 'input[type="text"]', - ); + ]; } diff --git a/plugins/px2em.php b/plugins/px2em.php index 5a7af94..4be522d 100644 --- a/plugins/px2em.php +++ b/plugins/px2em.php @@ -23,7 +23,7 @@ function fn__px2rem($input) { function px2em($input, $unit, $default_base) { - list($px, $base) = Functions::parseArgsSimple($input) + array(16, $default_base); + list($px, $base) = Functions::parseArgsSimple($input) + [16, $default_base]; return round($px / $base, 5) . $unit; } diff --git a/plugins/rem.php b/plugins/rem.php index 2a65e2a..292b631 100644 --- a/plugins/rem.php +++ b/plugins/rem.php @@ -16,12 +16,12 @@ function rem(Rule $rule) { if (! $modes) { $rem_patt = Regex::make('~{{LB}}({{number}})rem{{RB}}~iS'); $px_patt = Regex::make('~{{LB}}({{number}})px{{RB}}~iS'); - $font_props = array( + $font_props = [ 'font' => true, 'font-size' => true, 'line-height' => true, - ); - $modes = array('rem-fallback', 'px-fallback', 'convert'); + ]; + $modes = ['rem-fallback', 'px-fallback', 'convert']; } // Determine which properties are touched; all, or just font related. diff --git a/plugins/svg.php b/plugins/svg.php index 12c1368..3eb74f7 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -47,57 +47,55 @@ function svg_generator($input, $fn_name) { // Map types to element names. static $schemas; if (! $schemas) { - $schemas = array( - 'circle' => array( + $schemas = [ + 'circle' => [ 'tag' => 'circle', 'attrs' => 'cx cy r', - ), - 'ellipse' => array( + ], + 'ellipse' => [ 'tag' => 'ellipse', 'attrs' => 'cx cy rx ry', - ), - 'rect' => array( + ], + 'rect' => [ 'tag' => 'rect', 'attrs' => 'x y rx ry width height', - ), - 'polygon' => array( + ], + 'polygon' => [ 'tag' => 'polygon', 'attrs' => 'points', - ), - 'line' => array( + ], + 'line' => [ 'tag' => 'line', 'attrs' => 'x1 y1 x2 y2', - ), - 'polyline' => array( + ], + 'polyline' => [ 'tag' => 'polyline', 'attrs' => 'points', - ), - 'path' => array( + ], + 'path' => [ 'tag' => 'path', 'attrs' => 'd', - ), - 'star' => array( + ], + 'star' => [ 'tag' => 'path', 'attrs' => '', - ), - 'text' => array( + ], + 'text' => [ 'tag' => 'text', 'attrs' => 'x y dx dy rotate', - ), - ); + ], + ]; // Convert attributes to keyed array. // Add global attributes. foreach ($schemas as $type => &$schema) { $schema['attrs'] = array_flip(explode(' ', $schema['attrs'])) - + array( - 'transform' => true, - ); + + ['transform' => true]; } } // Non standard attributes. - static $custom_attrs = array( + static $custom_attrs = [ 'type' => true, 'data' => true, 'twist' => true, @@ -110,7 +108,7 @@ function svg_generator($input, $fn_name) { 'text' => true, 'width' => true, 'height' => true, - ); + ]; // Bail if no args. $args = Functions::parseArgs($input); @@ -131,12 +129,12 @@ function svg_generator($input, $fn_name) { // Apply args to template. $block = $svg_defs[$name]($args); - $raw_data = DeclarationList::parse($block, array( + $raw_data = DeclarationList::parse($block, [ 'keyed' => true, 'lowercase_keys' => true, 'flatten' => true, 'apply_hooks' => true, - )); + ]); // Resolve the type. // Bail if type not recognised. @@ -147,22 +145,22 @@ function svg_generator($input, $fn_name) { } // Create element object for attaching all required rendering data. - $element = (object) array( + $element = (object) [ 'tag' => $schemas[$type]['tag'], - 'fills' => array( + 'fills' => [ 'gradients' => [], 'patterns' => [], - ), + ], 'filters' => [], 'data' => [], 'attrs' => [], 'styles' => [], - 'svg_attrs' => array( + 'svg_attrs' => [ 'xmlns' => 'http://www.w3.org/2000/svg', - ), + ], 'svg_styles' => [], 'face_styles' => [], - ); + ]; // Filter off prefixed properties that are for the svg element or @font-face. foreach ($raw_data as $property => $value) { @@ -248,9 +246,9 @@ function svg_generator($input, $fn_name) { function svg_circle($element) { // Ensure required attributes have defaults set. - $element->data += array( + $element->data += [ 'diameter' => 50, - ); + ]; list($margin_top, $margin_right, $margin_bottom, $margin_left) = $element->data['margin']; @@ -271,10 +269,10 @@ function svg_circle($element) { */ function svg_rect($element) { - $element->data += array( + $element->data += [ 'width' => 50, 'height' => 50, - ); + ]; list($margin_top, $margin_right, $margin_bottom, $margin_left) = $element->data['margin']; @@ -298,9 +296,9 @@ function svg_rect($element) { */ function svg_ellipse($element) { - $element->data += array( + $element->data += [ 'diameter' => '100 50', - ); + ]; if (! isset($element->attrs['rx']) && ! isset($element->attrs['ry'])) { $diameter = svg_parselist($element->data['diameter']); @@ -323,14 +321,14 @@ function svg_ellipse($element) { function svg_path($element) { // Ensure minimum required attributes have defaults set. - $element->data += array( + $element->data += [ 'd' => 'M 10,10 l 10,0 l 0,10 l 10,0 l 0,10', - ); + ]; // Unclosed paths have implicit fill. - $element->styles += array( + $element->styles += [ 'fill' => 'none', - ); + ]; } /* @@ -339,14 +337,14 @@ function svg_path($element) { function svg_polyline($element) { // Ensure required attributes have defaults set. - $element->data += array( + $element->data += [ 'points' => '20,20 40,20 40,40 60,40 60,60', - ); + ]; // Polylines have implicit fill. - $element->styles += array( + $element->styles += [ 'fill' => 'none', - ); + ]; } /* @@ -355,16 +353,16 @@ function svg_polyline($element) { function svg_line($element) { // Set a default stroke. - $element->styles += array( + $element->styles += [ 'stroke' => '#000', - ); + ]; - $element->attrs += array( + $element->attrs += [ 'x1' => 0, 'x2' => 0, 'y1' => 0, 'y2' => 0, - ); + ]; } /* @@ -377,10 +375,10 @@ function svg_polygon($element) { // Switch to path element. $element->tag = 'path'; - $element->data += array( + $element->data += [ 'sides' => 3, 'diameter' => 100, - ); + ]; list($margin_top, $margin_right, $margin_bottom, $margin_left) = $element->data['margin']; @@ -405,11 +403,11 @@ function svg_polygon($element) { function svg_star($element) { // Minimum required attributes have defaults. - $element->data += array( + $element->data += [ 'star-points' => 4, 'diameter' => '50 30', 'twist' => 0, - ); + ]; list($margin_top, $margin_right, $margin_bottom, $margin_left) = $element->data['margin']; @@ -438,13 +436,13 @@ function svg_star($element) { function svg_text($element) { // Minimum required attributes have defaults. - $element->data += array( + $element->data += [ 'x' => 0, 'y' => 0, 'width' => 100, 'height' => 100, 'text' => '', - ); + ]; $text = Crush::$process->tokens->restore($element->data['text'], 's', true); @@ -522,12 +520,12 @@ function svg_apply_filters($element) { $parts = svg_parselist($element->data['drop-shadow'], false); - list($ds_x, $ds_y, $ds_strength, $ds_color) = $parts += array( + list($ds_x, $ds_y, $ds_strength, $ds_color) = $parts += [ 2, // x offset. 2, // y offset. 2, // strength. 'black', // color. - ); + ]; // Opacity. $drop_shadow_opacity = null; @@ -565,24 +563,24 @@ function svg_preprocess($element) { $parts = svg_parselist($margin); $count = count($parts); if ($count === 1) { - $margin = array($parts[0], $parts[0], $parts[0], $parts[0]); + $margin = [$parts[0], $parts[0], $parts[0], $parts[0]]; } elseif ($count === 2) { - $margin = array($parts[0], $parts[1], $parts[0], $parts[1]); + $margin = [$parts[0], $parts[1], $parts[0], $parts[1]]; } elseif ($count === 3) { - $margin = array($parts[0], $parts[1], $parts[2], $parts[1]); + $margin = [$parts[0], $parts[1], $parts[2], $parts[1]]; } else { $margin = $parts; } } else { - $element->data['margin'] = array(0,0,0,0); + $element->data['margin'] = [0, 0, 0, 0]; } // 'Unzip' string tokens on data attributes. - foreach (array('points', 'd') as $point_data_attr) { + foreach (['points', 'd'] as $point_data_attr) { if (isset($element->attrs[$point_data_attr])) { @@ -610,11 +608,11 @@ function svg_apply_css_funcs($element, &$raw_data) { static $functions; if (! $functions) { $functions = new \stdClass(); - $functions->fill = new Functions(array( + $functions->fill = new Functions([ 'svg-linear-gradient' => 'CssCrush\svg_fn_linear_gradient', 'svg-radial-gradient' => 'CssCrush\svg_fn_radial_gradient', 'pattern' => 'CssCrush\svg_fn_pattern', - )); + ]); $functions->generic = new Functions(array_diff_key(Crush::$process->functions->register, $functions->fill->register)); } @@ -633,7 +631,7 @@ function svg_apply_css_funcs($element, &$raw_data) { list($color, $opacity) = $components; $raw_data[$property] = $color; if ($opacity < 1) { - $raw_data += array("$property-opacity" => $opacity); + $raw_data += ["$property-opacity" => $opacity]; } } } @@ -645,7 +643,7 @@ function svg_compress($element) { foreach ($element->attrs as $key => &$value) { // Compress numbers on data attributes. - if (in_array($key, array('points', 'd'))) { + if (in_array($key, ['points', 'd'])) { $value = preg_replace_callback( Regex::$patt->number, function ($m) { return round($m[0], 2); }, @@ -658,10 +656,10 @@ function svg_render($element) { // Flatten styles. $styles = ''; - $styles_data = array( + $styles_data = [ '@font-face' => $element->face_styles, 'svg' => $element->svg_styles, - ); + ]; foreach ($styles_data as $selector => $declarations) { if ($declarations) { $out = []; @@ -671,21 +669,21 @@ function svg_render($element) { $styles .= $selector . '{' . implode(';', $out) . '}'; } } - $styles = Crush::$process->tokens->restore($styles, array('u', 's'), true); + $styles = Crush::$process->tokens->restore($styles, ['u', 's'], true); // Add element styles as attributes which tend to work better with svg2png converters. $attrs = Util::htmlAttributes($element->attrs + $element->styles); // Add viewbox to help IE scale correctly. if (isset($element->svg_attrs['width']) && isset($element->svg_attrs['height'])) { - $element->svg_attrs += array( - 'viewbox' => implode(' ', array( + $element->svg_attrs += [ + 'viewbox' => implode(' ', [ 0, 0, $element->svg_attrs['width'], $element->svg_attrs['height'] - )), - ); + ]), + ]; } $svg_attrs = Util::htmlAttributes($element->svg_attrs); @@ -751,7 +749,7 @@ function svg_fn_pattern($input, $element) { // Get args in order with defaults. list($url, $transform_list, $width, $height, $x, $y) = Functions::parseArgs($input) + - array('', '', 0, 0, 0, 0); + ['', '', 0, 0, 0, 0]; $url = Crush::$process->tokens->get($url); if (! $url) { @@ -838,17 +836,17 @@ function create_svg_linear_gradient($input) { static $angle_keywords, $deg_patt; if (! $angle_keywords) { - $angle_keywords = array( + $angle_keywords = [ 'to top' => 180, 'to right' => 270, 'to bottom' => 0, 'to left' => 90, // Not very magic corners. - 'to top right' => array(array(0, 100), array(100, 0)), - 'to top left' => array(array(100, 100), array(0, 0)), - 'to bottom right' => array(array(0, 0), array(100, 100)), - 'to bottom left' => array(array(100, 0), array(0, 100)), - ); + 'to top right' => [[0, 100], [100, 0]], + 'to top left' => [[100, 100], [0, 0]], + 'to bottom right' => [[0, 0], [100, 100]], + 'to bottom left' => [[100, 0], [0, 100]], + ]; $angle_keywords['to right top'] = $angle_keywords['to top right']; $angle_keywords['to left top'] = $angle_keywords['to top left']; $angle_keywords['to right bottom'] = $angle_keywords['to bottom right']; @@ -939,10 +937,10 @@ function create_svg_linear_gradient($input) { $start_y = 0; $end_y = 100; } - $coords = array( - array(round($start_x, 1), round($start_y, 1)), - array(round($end_x, 1), round($end_y, 1)), - ); + $coords = [ + [round($start_x, 1), round($start_y, 1)], + [round($end_x, 1), round($end_y, 1)], + ]; } // The remaining arguments are treated as color stops. @@ -957,7 +955,7 @@ function create_svg_linear_gradient($input) { $gradient .= $color_stops; $gradient .= '
'; - return array($gradient_id => $gradient); + return [$gradient_id => $gradient]; } @@ -965,18 +963,18 @@ function create_svg_radial_gradient($input) { static $position_keywords, $origin_patt; if (! $position_keywords) { - $position_keywords = array( - 'at top' => array('50%', '0%'), - 'at right' => array('100%', '50%'), - 'at bottom' => array('50%', '100%'), - 'at left' => array('0%', '50%'), - 'at center' => array('50%', '50%'), + $position_keywords = [ + 'at top' => ['50%', '0%'], + 'at right' => ['100%', '50%'], + 'at bottom' => ['50%', '100%'], + 'at left' => ['0%', '50%'], + 'at center' => ['50%', '50%'], // Not very magic corners. - 'at top right' => array('100%', '0%'), - 'at top left' => array('0%', '0%'), - 'at bottom right' => array('100%', '100%'), - 'at bottom left' => array('0%', '100%'), - ); + 'at top right' => ['100%', '0%'], + 'at top left' => ['0%', '0%'], + 'at bottom right' => ['100%', '100%'], + 'at bottom left' => ['0%', '100%'], + ]; $position_keywords['at right top'] = $position_keywords['at top right']; $position_keywords['at left top'] = $position_keywords['at top left']; $position_keywords['at right bottom'] = $position_keywords['at bottom right']; @@ -996,7 +994,7 @@ function create_svg_radial_gradient($input) { // Try to parse an origin value. if (preg_match($origin_patt, $first_arg, $m)) { - $position = array($m[1], $m[2]); + $position = [$m[1], $m[2]]; $first_arg_is_position = true; } elseif (isset($position_keywords[$first_arg])) { @@ -1021,7 +1019,7 @@ function create_svg_radial_gradient($input) { $gradient .= $color_stops; $gradient .= '
'; - return array($gradient_id => $gradient); + return [$gradient_id => $gradient]; } diff --git a/tests/unit/CssCrush/ColorTest.php b/tests/unit/CssCrush/ColorTest.php index e10ebcb..a7b3997 100644 --- a/tests/unit/CssCrush/ColorTest.php +++ b/tests/unit/CssCrush/ColorTest.php @@ -21,14 +21,14 @@ public function testConstruct() public function testAdjust() { $color = new Color('rgb(255,0,0)'); - $color->toHsl()->adjust(array(0,0,0,-20)); + $color->toHsl()->adjust([0, 0, 0, -20]); $this->assertEquals('rgba(255,0,0,0.8)', (string) $color); } public function testGetHsl() { $color = new Color('red'); - $this->assertEquals(array(0, 1, .5, 1), $color->getHsl()); + $this->assertEquals([0, 1, .5, 1], $color->getHsl()); } public function testGetHex() @@ -40,7 +40,7 @@ public function testGetHex() public function testGetRgb() { $color = new Color('red'); - $this->assertEquals(array(255, 0, 0, 1), $color->getRgb()); + $this->assertEquals([255, 0, 0, 1], $color->getRgb()); } public function testGetComponent() diff --git a/tests/unit/CssCrush/DeclarationTest.php b/tests/unit/CssCrush/DeclarationTest.php index d41ab45..6e66111 100644 --- a/tests/unit/CssCrush/DeclarationTest.php +++ b/tests/unit/CssCrush/DeclarationTest.php @@ -14,7 +14,7 @@ class DeclarationTest extends \PHPUnit_Framework_TestCase public function setUp() { - $this->process = bootstrap_process(array('minify' => false)); + $this->process = bootstrap_process(['minify' => false]); $this->rule = new Rule('.foo', '-fOo-BAR: math(10 + 10, px) !important'); $this->declaration = new Declaration('-fOo-BAR', 'baz !important'); } @@ -47,6 +47,6 @@ public function testIndexFunctions() { $declaration = new Declaration('color', 'rgba(0,0,0,.5), calc(100px)'); $declaration->indexFunctions(); - $this->assertEquals(array('rgba' => true, 'calc' => true), $declaration->functions); + $this->assertEquals(['rgba' => true, 'calc' => true], $declaration->functions); } } diff --git a/tests/unit/CssCrush/FunctionsTest.php b/tests/unit/CssCrush/FunctionsTest.php index 7872da8..eaf5c78 100644 --- a/tests/unit/CssCrush/FunctionsTest.php +++ b/tests/unit/CssCrush/FunctionsTest.php @@ -8,17 +8,17 @@ class FunctionsTest extends \PHPUnit_Framework_TestCase { public function testMakePattern() { - $patt = Functions::makePattern(array('foo', 'bar')); + $patt = Functions::makePattern(['foo', 'bar']); $this->assertEquals('~(?foo|bar)\(~iS', $patt); $hashChar = version_compare(PHP_VERSION, '7.3.0') >= 0 ? '\\#' : '#'; - $patt = Functions::makePattern(array('foo', 'bar', '#')); + $patt = Functions::makePattern(['foo', 'bar', '#']); $this->assertEquals('~(?:(?foo|bar)|(?' . $hashChar . '))\(~iS', $patt); - $patt = Functions::makePattern(array('$', '#')); + $patt = Functions::makePattern(['$', '#']); $this->assertEquals('~(?\$|' . $hashChar . ')\(~iS', $patt); } } diff --git a/tests/unit/CssCrush/OptionsTest.php b/tests/unit/CssCrush/OptionsTest.php index d6d0e3e..2ccb74a 100644 --- a/tests/unit/CssCrush/OptionsTest.php +++ b/tests/unit/CssCrush/OptionsTest.php @@ -22,7 +22,7 @@ public function testDefaults() $this->assertEquals($standardOptions, $options->get()); - $testOptions = array('plugins' => array('foo', 'bar'), 'minify' => false); + $testOptions = ['plugins' => ['foo', 'bar'], 'minify' => false]; $options = new Options($testOptions); $initialOptionsCopy = $testOptions + $standardOptions; @@ -38,10 +38,10 @@ public function testBoilerplate() {{version}} TPL; - $result = csscrush_string('foo { bar: baz; }', array( + $result = csscrush_string('foo { bar: baz; }', [ 'boilerplate' => temp_file($boilerplate), 'newlines' => 'unix', - )); + ]); $this->assertContains(' * ' . Version::detect(), (string) $result); $this->assertContains(" * Line breaks\n * preserved\n *", (string) $result); @@ -56,7 +56,7 @@ public function testFormatters() foo { bar: baz; } TPL; - $single_line = csscrush_string($sample, array('formatter' => 'single-line')); + $single_line = csscrush_string($sample, ['formatter' => 'single-line']); $this->assertEquals($single_line_expected, $single_line); $padded_expected = << 'padded')); + $padded = csscrush_string($sample, ['formatter' => 'padded']); $this->assertEquals($padded_expected, $padded); $block_expected = << 'block')); + $block = csscrush_string($sample, ['formatter' => 'block']); $this->assertEquals($block_expected, $block); } public function testSourceMaps() { - csscrush_file($this->testFile, array('source_map' => true)); + csscrush_file($this->testFile, ['source_map' => true]); $source_map_contents = file_get_contents("$this->testFile.crush.css.map"); $this->assertRegExp('~"version": ?3,~', $source_map_contents); @@ -89,7 +89,7 @@ public function testSourceMaps() public function testAdvancedMinify() { $sample = "foo { color: papayawhip; color: #cccccc;}"; - $output = csscrush_string($sample, array('minify' => array('colors'))); + $output = csscrush_string($sample, ['minify' => ['colors']]); $this->assertEquals('foo{color:#ffefd5;color:#ccc}', $output); } diff --git a/tests/unit/CssCrush/RegexTest.php b/tests/unit/CssCrush/RegexTest.php index 293fc12..7015e8f 100644 --- a/tests/unit/CssCrush/RegexTest.php +++ b/tests/unit/CssCrush/RegexTest.php @@ -16,10 +16,10 @@ public function testMake() public function testMatchAll() { - $expected = array( - array(array('foo', 0)), - array(array('foo', 12)), - ); + $expected = [ + [['foo', 0]], + [['foo', 12]], + ]; $matches = Regex::matchAll('~foo~', 'foo bar baz foo bar baz'); $this->assertEquals($expected, $matches); diff --git a/tests/unit/CssCrush/TemplateTest.php b/tests/unit/CssCrush/TemplateTest.php index 91e4a3e..2ca8412 100644 --- a/tests/unit/CssCrush/TemplateTest.php +++ b/tests/unit/CssCrush/TemplateTest.php @@ -34,24 +34,24 @@ public function test__construct() public function testGetArgValue() { - $args = array('default'); + $args = ['default']; $this->assertEquals('100%', $this->template->getArgValue(0, $args)); - $args = array('foo', 'bar'); + $args = ['foo', 'bar']; $this->assertEquals('bar', $this->template->getArgValue(1, $args)); } public function testPrepare() { - $this->template->prepare(array('one', 'two')); + $this->template->prepare(['one', 'two']); $this->assertEquals( - array(array('?a0?', '?a1?'), array('one', 'two')), + [['?a0?', '?a1?'], ['one', 'two']], $this->template->substitutions); } public function testApply() { - $actual = $this->template->__invoke(array('one', 'two')); + $actual = $this->template->__invoke(['one', 'two']); $expected = <<assertEquals($expected, $actual); - $actual = $this->template->__invoke(array('default', 'colanut')); + $actual = $this->template->__invoke(['default', 'colanut']); $expected = <<process = bootstrap_process(array('minify' => false)); + $this->process = bootstrap_process(['minify' => false]); $this->tokens = $this->process->tokens; $this->tokens->add('"foo"', 's'); @@ -22,7 +22,7 @@ public function test__construct() { $this->assertEmpty( array_diff_key( - array_flip(array('s', 'c', 'r', 'u', 't')), + array_flip(['s', 'c', 'r', 'u', 't']), (array) $this->tokens->store ) ); diff --git a/tests/unit/CssCrush/UrlTest.php b/tests/unit/CssCrush/UrlTest.php index 5759499..f7dbf2e 100644 --- a/tests/unit/CssCrush/UrlTest.php +++ b/tests/unit/CssCrush/UrlTest.php @@ -9,7 +9,7 @@ class UrlTest extends \PHPUnit_Framework_TestCase { public function setUp() { - bootstrap_process(array('minify' => false)); + bootstrap_process(['minify' => false]); } public function testConstruct() diff --git a/tests/unit/CssCrush/UtilTest.php b/tests/unit/CssCrush/UtilTest.php index 1aeb089..0a08542 100644 --- a/tests/unit/CssCrush/UtilTest.php +++ b/tests/unit/CssCrush/UtilTest.php @@ -10,7 +10,7 @@ class UtilTest extends \PHPUnit_Framework_TestCase { public function setUp() { - $this->process = bootstrap_process(array('minify' => false)); + $this->process = bootstrap_process(['minify' => false]); $this->tokens = $this->process->tokens; } @@ -23,18 +23,18 @@ public function testNormalizePath() public function testHtmlAttributes() { - $attributes = array( + $attributes = [ 'rel' => 'stylesheet', 'id' => 'foo', 'media' => 'screen', - ); + ]; $this->assertEquals( ' rel="stylesheet" id="foo" media="screen"', Util::htmlAttributes($attributes)); $this->assertEquals( ' id="foo" media="screen" rel="stylesheet"', - Util::htmlAttributes($attributes, array('id', 'media', 'rel'))); + Util::htmlAttributes($attributes, ['id', 'media', 'rel'])); } public function testSimplifyPath() @@ -80,9 +80,9 @@ public function testNormalizeWhiteSpace() public function testSplitDelimList() { - $this->assertEquals(array('foo(1,2)','3','4'), Util::splitDelimList("foo(1,2), 3,4")); - $this->assertEquals([], Util::splitDelimList(" ; ; ", array('delim' => ';'))); - $this->assertEquals(array('', ''), Util::splitDelimList(" , ", array('allow_empty_strings' => true))); + $this->assertEquals(['foo(1,2)','3','4'], Util::splitDelimList("foo(1,2), 3,4")); + $this->assertEquals([], Util::splitDelimList(" ; ; ", ['delim' => ';'])); + $this->assertEquals(['', ''], Util::splitDelimList(" , ", ['allow_empty_strings' => true])); } public function testGetLinkBetweenPaths() @@ -122,7 +122,7 @@ public function testReadConfigFile() $contents = <<<'NOW_DOC' assertEquals("\n", csscrush_inline($this->sampleFile)); - $this->assertEquals("\n", csscrush_inline($this->sampleFile, null, array( + $this->assertEquals("\n", csscrush_inline($this->sampleFile, null, [ 'type' => 'text/css', 'id' => 'foo', - ))); + ])); } public function testFile() { $test_dir = dirname($this->sampleFile); - $test_file = csscrush_file($this->sampleFile, array( + $test_file = csscrush_file($this->sampleFile, [ 'versioning' => false, 'cache' => false, 'doc_root' => $test_dir, 'boilerplate' => false, - )); + ]); $filepath = "$test_dir$test_file"; $this->assertEquals($this->sampleExpected, file_get_contents($filepath)); @@ -53,19 +53,19 @@ public function testFile() public function testTag() { $test_dir = dirname($this->sampleFile); - $base_options = array( + $base_options = [ 'versioning' => false, 'cache' => false, 'doc_root' => $test_dir, 'boilerplate' => false, - ); + ]; $url = '/' . basename($this->sampleFile) . '.crush.css'; $test_tag = csscrush_tag($this->sampleFile, $base_options); $this->assertEquals("\n", $test_tag); - $test_tag = csscrush_tag($this->sampleFile, $base_options, array('media' => 'print', 'id' => 'foo')); + $test_tag = csscrush_tag($this->sampleFile, $base_options, ['media' => 'print', 'id' => 'foo']); $this->assertEquals("\n", $test_tag); } @@ -78,9 +78,9 @@ public function testStat() one, two, three, four, five {color: purple;} TPL; - csscrush_string($sample, array( + csscrush_string($sample, [ 'minify' => false, - )); + ]); $stats = csscrush_stat(); @@ -101,19 +101,19 @@ public function testGetSet() }); $this->assertEquals('bar', csscrush_get('config', 'foo')); - csscrush_set('config', array( + csscrush_set('config', [ 'hello' => 'world', - )); + ]); $this->assertEquals('world', csscrush_get('config', 'hello')); $this->assertInstanceOf('stdClass', csscrush_get('config')); $this->assertInstanceOf('CssCrush\Options', csscrush_get('options')); - csscrush_set('options', array('enable' => 'property-sorter')); + csscrush_set('options', ['enable' => 'property-sorter']); $this->assertContains('property-sorter', csscrush_get('options', 'enable')); - csscrush_set('options', array('enable' => [])); + csscrush_set('options', ['enable' => []]); } } diff --git a/tests/unit/cli/context/crushfile.php b/tests/unit/cli/context/crushfile.php index 57d7c6d..def094c 100644 --- a/tests/unit/cli/context/crushfile.php +++ b/tests/unit/cli/context/crushfile.php @@ -2,4 +2,4 @@ $context = __DIR__; -$plugins = array('color'); +$plugins = ['color']; From fcd9143318e6ed76e712f071e90a9772cdf9ccb1 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 28 Dec 2019 18:51:40 +0000 Subject: [PATCH 384/421] Removed the settings directive --- docs/api/options.md | 5 -- docs/api/settings.md | 17 ------- docs/plugins/px2em.md | 26 ---------- docs/plugins/rem.md | 48 ------------------ lib/CssCrush/Process.php | 23 +-------- lib/CssCrush/Settings.php | 57 --------------------- plugins/px2em.php | 29 ----------- plugins/rem.php | 102 -------------------------------------- 8 files changed, 2 insertions(+), 305 deletions(-) delete mode 100644 docs/api/settings.md delete mode 100644 docs/plugins/px2em.md delete mode 100644 docs/plugins/rem.md delete mode 100644 lib/CssCrush/Settings.php delete mode 100644 plugins/px2em.php delete mode 100644 plugins/rem.php diff --git a/docs/api/options.md b/docs/api/options.md index 5c66c29..1aa3fb2 100644 --- a/docs/api/options.md +++ b/docs/api/options.md @@ -100,9 +100,4 @@ Path Specify an alternative server document root for situations where the CSS is being served behind an alias or url rewritten path. - - settings - Array - An associative array of plugin and environment settings. Used primarily for plugin configuration. - diff --git a/docs/api/settings.md b/docs/api/settings.md deleted file mode 100644 index 2480233..0000000 --- a/docs/api/settings.md +++ /dev/null @@ -1,17 +0,0 @@ - - -Plugins sometimes use __settings__ to configure their behaviour. - -Settings can be specified as an [option](#api--options), or declared in CSS with block or single-line syntax: - -```crush -@settings { - dir: ltr; -} - -@settings rem-mode px-fallback; -``` diff --git a/docs/plugins/px2em.md b/docs/plugins/px2em.md deleted file mode 100644 index 87b6284..0000000 --- a/docs/plugins/px2em.md +++ /dev/null @@ -1,26 +0,0 @@ - -Functions for converting pixel values into `em` (__px2em__) or `rem` (__px2rem__) values - -For both functions the optional second argument is base font-size for calculation though usually not required when converting pixel to rem. - - -## Settings - -### px2em-base - -The default base pixel value (16px by default) used for px2em conversion. - -### px2rem-base - -The default base pixel value (16px by default) used for px2rem conversion. - - -```css -font-size: px2em(11 13); -font-size: px2rem(16); -``` - -```css -font-size: .84615em; -font-size: 1rem; -``` diff --git a/docs/plugins/rem.md b/docs/plugins/rem.md deleted file mode 100644 index abbafc2..0000000 --- a/docs/plugins/rem.md +++ /dev/null @@ -1,48 +0,0 @@ - -Polyfill for the rem (root em) length unit. - -No version of IE to date (IE <= 10) resizes text set with pixels though IE > 8 supports rem units which are resizeable. - -* [Rem unit browser support](http://caniuse.com/#feat=rem) - -## Settings - -### rem-mode - -Has the following possible values: - -* `rem-fallback` (default) - rem to px, with converted value as fallback. - -```css -font-size: 1rem; -``` - -```css -font-size: 16px; -font-size: 1rem; -``` - -* `px-fallback` - px to rem, with original pixel value as fallback. - -```css -font-size: 16px; -``` - -```css -font-size: 16px; -font-size: 1rem; -``` - -* `convert` - in-place px to rem conversion. - -```css -font-size: 16px; -``` - -```css -font-size: 1rem; -``` - -### rem-all - -To convert all length values, not just values of the font related properties, set `rem-all` with a value of `yes`. diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index d0a3172..58f5f25 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -26,7 +26,6 @@ public function __construct($user_options = [], $context = []) $this->sources = []; $this->vars = []; $this->plugins = []; - $this->settings = []; $this->misc = new \stdClass(); $this->input = new \stdClass(); $this->output = new \stdClass(); @@ -440,22 +439,6 @@ protected function placeVars(&$value) return $varsPlaced; } - - ############################# - # @settings blocks. - - protected function resolveSettings() - { - $captured_settings = $this->string->captureDirectives('settings', ['singles' => true]); - $combined_settings = $this->options->settings + $captured_settings; - - foreach ($combined_settings as &$value) { - $this->placeVars($value); - } - - $this->settings = new Settings($combined_settings); - } - ############################# # @for..in blocks. @@ -959,20 +942,18 @@ public function compile() $importer = new Importer($this); $this->string = new StringObject($importer->collate()); - // Capture phase 0 hook: Before all variables and settings have resolved. + // Capture phase 0 hook: Before all variables have resolved. $this->emit('capture_phase0', $this); $this->captureVars(); $this->resolveIfDefines(); - $this->resolveSettings(); - $this->resolveLoops(); $this->placeAllVars(); - // Capture phase 1 hook: After all variables and settings have resolved. + // Capture phase 1 hook: After all variables have resolved. $this->emit('capture_phase1', $this); $this->resolveSelectorAliases(); diff --git a/lib/CssCrush/Settings.php b/lib/CssCrush/Settings.php deleted file mode 100644 index 77938da..0000000 --- a/lib/CssCrush/Settings.php +++ /dev/null @@ -1,57 +0,0 @@ - $value) { - $this->set($name, $value); - } - } - - public function set($name, $value) - { - $this->store[strtolower($name)] = strtolower($value); - } - - public function get($name, $fallback = null) - { - $value = isset($this->store[$name]) ? $this->store[$name] : null; - - // Backwards compat for variable based settings. - if (! $value && in_array($name, ['rem-all', 'rem-mode', 'rem-base', 'px2rem-base', 'px2em-base'])) { - - $var_setting = function ($var_name) { - return isset(Crush::$process->vars[$var_name]) ? - Crush::$process->vars[$var_name] : null; - }; - switch ($name) { - case 'rem-all': - $value = $var_setting('rem__all'); - break; - case 'rem-mode': - $value = $var_setting('rem__mode'); - break; - case 'rem-base': - $value = $var_setting('rem__base'); - break; - case 'px2rem-base': - $value = $var_setting('px2rem__base'); - break; - case 'px2em-base': - $value = $var_setting('px2em__base'); - break; - } - } - - return isset($value) ? $value : $fallback; - } -} diff --git a/plugins/px2em.php b/plugins/px2em.php deleted file mode 100644 index 4be522d..0000000 --- a/plugins/px2em.php +++ /dev/null @@ -1,29 +0,0 @@ -functions->add('px2em', 'CssCrush\fn__px2em'); - $process->functions->add('px2rem', 'CssCrush\fn__px2rem'); -}); - -function fn__px2em($input) { - - return px2em($input, 'em', Crush::$process->settings->get('px2em-base', 16)); -} - -function fn__px2rem($input) { - - return px2em($input, 'rem', Crush::$process->settings->get('px2rem-base', 16)); -} - -function px2em($input, $unit, $default_base) { - - list($px, $base) = Functions::parseArgsSimple($input) + [16, $default_base]; - - return round($px / $base, 5) . $unit; -} diff --git a/plugins/rem.php b/plugins/rem.php deleted file mode 100644 index 292b631..0000000 --- a/plugins/rem.php +++ /dev/null @@ -1,102 +0,0 @@ -on('rule_prealias', 'CssCrush\rem'); -}); - -function rem(Rule $rule) { - - static $rem_patt, $px_patt, $font_props, $modes; - if (! $modes) { - $rem_patt = Regex::make('~{{LB}}({{number}})rem{{RB}}~iS'); - $px_patt = Regex::make('~{{LB}}({{number}})px{{RB}}~iS'); - $font_props = [ - 'font' => true, - 'font-size' => true, - 'line-height' => true, - ]; - $modes = ['rem-fallback', 'px-fallback', 'convert']; - } - - // Determine which properties are touched; all, or just font related. - $just_font_props = ! Crush::$process->settings->get('rem-all', false); - - if ($just_font_props && ! array_intersect_key($rule->declarations->canonicalProperties, $font_props)) { - return; - } - - // Determine what conversion mode we're using. - $mode = Crush::$process->settings->get('rem-mode', $modes[0]); - - // Determine a base font size for calculations, in browsers this is usually 16px. - $base = floatval(Crush::$process->settings->get('rem-base', 16)); - - // Select the length match pattern depending on mode. - $length_patt = $mode === 'rem-fallback' ? $rem_patt : $px_patt; - - $new_set = []; - $rule_updated = false; - foreach ($rule->declarations as $declaration) { - if ( - $declaration->skip || - ($just_font_props && ! isset($font_props[$declaration->canonicalProperty])) || - ! preg_match_all($length_patt, $declaration->value, $m) - ) { - $new_set[] = $declaration; - continue; - } - - // Value has matching length components. - $find = $m[0]; - $replace = []; - $numbers = $m[1]; - - switch ($mode) { - // Converting a rem value to px. - case 'rem-fallback': - foreach ($numbers as $num) { - $replace[] = round(floatval($num) * $base, 5) . 'px'; - } - break; - - // Converting a px value to rem. - case 'convert': - case 'px-fallback': - foreach ($numbers as $num) { - $replace[] = round(floatval($num) / $base, 5) . 'rem'; - } - break; - } - - $converted_value = str_replace($find, $replace, $declaration->value); - - if ($mode === 'convert') { - $declaration->value = $converted_value; - $new_set[] = $declaration; - } - else { - $clone = clone $declaration; - $clone->value = $converted_value; - $rule_updated = true; - - if ($mode === 'px-fallback') { - $new_set[] = $declaration; - $new_set[] = $clone; - } - else { - $new_set[] = $clone; - $new_set[] = $declaration; - } - } - } - - if ($rule_updated) { - $rule->declarations->reset($new_set); - } -} From bf8f4f242921cff61995dc2d2332c4be6215a35c Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 28 Dec 2019 20:10:33 +0000 Subject: [PATCH 385/421] Remove color plugin --- docs/plugins/color.md | 20 ------------ plugins/color.php | 47 ---------------------------- tests/unit/cli/cliTest.php | 2 +- tests/unit/cli/context/crushfile.php | 2 +- 4 files changed, 2 insertions(+), 69 deletions(-) delete mode 100644 docs/plugins/color.md delete mode 100644 plugins/color.php diff --git a/docs/plugins/color.md b/docs/plugins/color.md deleted file mode 100644 index 8dba984..0000000 --- a/docs/plugins/color.md +++ /dev/null @@ -1,20 +0,0 @@ - -Define custom color keywords. - -Standard color keywords (e.g. blue, red) cannot be overridden. - -```crush -@color { - acme-blue: s-adjust(blue -10); - kolanut: #D0474E; -} - -@color vanilla #FBF7EC; - - -/* Usage is the same as with native color keywords */ -p { - color: vanilla; - border: 1px solid acme-blue; -} -``` diff --git a/plugins/color.php b/plugins/color.php deleted file mode 100644 index f5c42af..0000000 --- a/plugins/color.php +++ /dev/null @@ -1,47 +0,0 @@ -on('capture_phase1', 'CssCrush\color_capture'); - $process->on('declaration_preprocess', 'CssCrush\color'); -}); - -function color(&$declaration) { - if (isset($GLOBALS['CSSCRUSH_COLOR_PATT'])) { - $declaration['value'] = preg_replace_callback($GLOBALS['CSSCRUSH_COLOR_PATT'], function ($m) { - return new Color(Crush::$process->colorKeywords[$m['color_keyword']]); - }, $declaration['value']); - } -} - -function color_capture($process) { - - $captured_keywords = $process->string->captureDirectives('color', ['singles' => true]); - - if ($captured_keywords) { - - $native_keywords = Color::getKeywords(); - $custom_keywords = []; - $process->colorKeywords = $native_keywords; - - foreach ($captured_keywords as $key => $value) { - $value = $process->functions->apply($value); - if (! isset($native_keywords[$key]) && $rgba = Color::parse($value)) { - $custom_keywords[] = $key; - $process->stat['colors'][$key] = new Color($rgba); - $process->colorKeywords[$key] = $rgba; - } - } - - if ($custom_keywords) { - $GLOBALS['CSSCRUSH_COLOR_PATT'] = Regex::make('~{{ LB }}(?' . - implode('|', $custom_keywords) . '){{ RB }}~iS'); - } - } -} diff --git a/tests/unit/cli/cliTest.php b/tests/unit/cli/cliTest.php index 18390ee..84e197f 100644 --- a/tests/unit/cli/cliTest.php +++ b/tests/unit/cli/cliTest.php @@ -69,7 +69,7 @@ public function testConfigFile() $currentDirectory = getcwd(); chdir(__DIR__ . '/context'); - $sample = '@import "http://github.com/import.css"; @color dark #111; baz {color: dark;}'; + $sample = '@import "http://github.com/import.css"; baz {color: #111;}'; exec("echo '$sample' | php \"$this->path\"", $lines); $this->assertEquals('foo{bar:baz}baz{color:#111}', implode('', $lines)); diff --git a/tests/unit/cli/context/crushfile.php b/tests/unit/cli/context/crushfile.php index def094c..3e4e005 100644 --- a/tests/unit/cli/context/crushfile.php +++ b/tests/unit/cli/context/crushfile.php @@ -2,4 +2,4 @@ $context = __DIR__; -$plugins = ['color']; +$plugins = ['ease']; From 63908da045583d5a071f0e98e572bd2a171e209f Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 28 Dec 2019 20:31:04 +0000 Subject: [PATCH 386/421] Updated docs --- CHANGELOG.md | 9 ++++++--- README.md | 2 +- docs/core/auto-prefixing.md | 1 - docs/core/nesting.md | 2 +- docs/getting-started/js.md | 2 ++ 5 files changed, 10 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 019a75a..af788c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,15 +1,18 @@ -## 3.0.0-prerelease +## 3.0.0 -* Raise php requirement to >= 5.6 +* Raised php requirement to >= 5.6 * Removed `csscrush_version()` * Removed `csscrush_add_function()` (can use plugin instead). * Added `csscrush_plugin()` with simplified plugin api. * Added `import_path` option. Additional paths to search when resolving relative imports. +* Added support for non-CSS declaration values via backticks (for custom property values). +* Custom properties `--*` now preserve case. * Updated vendor aliases. * Moved loop plugin to core. * Removed `@in` directive. +* Removed `@settings` directive and its api. * Removed legacy IE plugins. -* Removed hsl2hex, initial, noise and text-align plugins. +* Removed hsl2hex, initial, noise, rem, px2em, color and text-align plugins. * Combined svg plugins (svg-gradients and svg). * Removed `percent` function. diff --git a/README.md b/README.md index 10f8f6c..eec7f1d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ [![Build Status](https://travis-ci.org/peteboere/css-crush.svg)](https://travis-ci.org/peteboere/css-crush) -Logo +Logo CSS-Crush is a standards inspired preprocessor designed to enable a modern and uncluttered CSS workflow. diff --git a/docs/core/auto-prefixing.md b/docs/core/auto-prefixing.md index 289db3b..02c67b6 100644 --- a/docs/core/auto-prefixing.md +++ b/docs/core/auto-prefixing.md @@ -34,7 +34,6 @@ Vendor prefixes for properties, functions, @-rules and declarations are **automa } @keyframes bounce { 50% {-webkit-transform: scale(1.4); - -ms-transform: scale(1.4); transform: scale(1.4);} } ``` diff --git a/docs/core/nesting.md b/docs/core/nesting.md index 8bf9665..999be4f 100644 --- a/docs/core/nesting.md +++ b/docs/core/nesting.md @@ -32,7 +32,7 @@ Rules can be nested to avoid repetitive typing when scoping to a common parent s You can use the parent reference symbol `&` for placing the parent selector explicitly. -```crush2 +```crush .homepage { .no-js & { p { diff --git a/docs/getting-started/js.md b/docs/getting-started/js.md index 4631be7..605727f 100644 --- a/docs/getting-started/js.md +++ b/docs/getting-started/js.md @@ -4,6 +4,8 @@ }--> +This preprocessor is written in PHP, so as prerequisite you will need to have PHP installed on your system to use the JS api. + ```shell npm install csscrush ``` From deb058b2e9386d97377c027177c7dbc406388473 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 31 Dec 2019 14:38:25 +0000 Subject: [PATCH 387/421] Bump to 3.0.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7a12011..94835bf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "csscrush", - "version": "3.0.0-beta.5", + "version": "3.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 80597c6..e328200 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "csscrush", - "version": "3.0.0-beta.5", + "version": "3.0.0", "description": "CSS-Crush, CSS preprocessor", "repository": { "type": "git", From 98098ff702afd96f19eb7671f5a2522a96d642b0 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 31 Dec 2019 14:46:38 +0000 Subject: [PATCH 388/421] Improve introduction wording --- README.md | 2 +- docs/core/functions/data-uri.md | 4 ++-- docs/core/functions/math.md | 2 +- docs/core/selector-grouping.md | 2 +- docs/getting-started/php.md | 2 +- docs/plugins/aria.md | 2 +- docs/plugins/ease.md | 2 +- docs/plugins/forms.md | 2 +- docs/plugins/hocus-pocus.md | 2 +- docs/plugins/property-sorter.md | 2 +- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index eec7f1d..cef3f57 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Logo -CSS-Crush is a standards inspired preprocessor designed to enable a modern and uncluttered CSS workflow. +A CSS preprocessor designed to enable a modern and uncluttered CSS workflow. * Automatic vendor prefixing * Variables diff --git a/docs/core/functions/data-uri.md b/docs/core/functions/data-uri.md index 702a4c2..32b0459 100644 --- a/docs/core/functions/data-uri.md +++ b/docs/core/functions/data-uri.md @@ -24,10 +24,10 @@ The created data-uri as a string inside a CSS url(). ## Examples -```css +```crush background: silver data-uri(../images/stripe.png); ``` ```css background: silver url(data:); -``` \ No newline at end of file +``` diff --git a/docs/core/functions/math.md b/docs/core/functions/math.md index 491cf10..2d65b40 100644 --- a/docs/core/functions/math.md +++ b/docs/core/functions/math.md @@ -10,7 +10,7 @@ Evaluate a raw mathematical expression. ## Examples -```css +```crush font-size: math( 12 / 16, em ); ``` diff --git a/docs/core/selector-grouping.md b/docs/core/selector-grouping.md index 5e4d924..0cafa1f 100644 --- a/docs/core/selector-grouping.md +++ b/docs/core/selector-grouping.md @@ -6,7 +6,7 @@ Selector grouping with the `:any` pseudo class (modelled after CSS4 :matches) simplifies the creation of complex selector chains. -```css +```crush :any( .sidebar, .block ) a:any( :hover, :focus ) { color: lemonchiffon; } diff --git a/docs/getting-started/php.md b/docs/getting-started/php.md index 5be00ff..1c6f836 100644 --- a/docs/getting-started/php.md +++ b/docs/getting-started/php.md @@ -7,7 +7,7 @@ If you're using [Composer](http://getcomposer.org) you can use Crush in your project with the following line in your terminal: ```shell -composer require css-crush/css-crush:dev-master +composer require css-crush/css-crush ``` If you're not using Composer yet just download the library ([zip](http://github.com/peteboere/css-crush/zipball/master) or [tar](http://github.com/peteboere/css-crush/tarball/master)) into a convenient location and require the bootstrap file: diff --git a/docs/plugins/aria.md b/docs/plugins/aria.md index 86e77b1..c00608d 100644 --- a/docs/plugins/aria.md +++ b/docs/plugins/aria.md @@ -4,7 +4,7 @@ Pseudo classes for working with ARIA roles, states and properties. * [ARIA roles spec](http://www.w3.org/TR/wai-aria/roles) * [ARIA states and properties spec](http://www.w3.org/TR/wai-aria/states_and_properties) -````css +````crush :role(tablist) {...} :aria-expanded {...} :aria-expanded(false) {...} diff --git a/docs/plugins/ease.md b/docs/plugins/ease.md index 583f9cb..37ce5f9 100644 --- a/docs/plugins/ease.md +++ b/docs/plugins/ease.md @@ -28,7 +28,7 @@ Expanded easing keywords for transitions. See [easing demos](http://easings.net) for live examples. -```css +```crush transition: .2s ease-in-quad; ``` diff --git a/docs/plugins/forms.md b/docs/plugins/forms.md index 66afbb4..3f4fcbc 100644 --- a/docs/plugins/forms.md +++ b/docs/plugins/forms.md @@ -1,7 +1,7 @@ Pseudo classes for working with forms. -```css +```crush :input(date, search, email) {...} :checkbox {...} :radio {...} diff --git a/docs/plugins/hocus-pocus.md b/docs/plugins/hocus-pocus.md index 4cc4a46..b8f3efa 100644 --- a/docs/plugins/hocus-pocus.md +++ b/docs/plugins/hocus-pocus.md @@ -1,7 +1,7 @@ Composite :hover/:focus/:active pseudo classes. -```css +```crush a:hocus { color: red; } a:pocus { color: red; } ``` diff --git a/docs/plugins/property-sorter.md b/docs/plugins/property-sorter.md index 0691bed..cd17817 100644 --- a/docs/plugins/property-sorter.md +++ b/docs/plugins/property-sorter.md @@ -4,7 +4,7 @@ Property sorting. Examples use the predefined property sorting table. To define a custom sorting order pass an array to `csscrush_set_property_sort_order()` -```css +```crush color: red; background: #000; opacity: .5; From 6c55047e5b3e8aae6170496efd795d4cb784f82f Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 16 Jan 2020 12:01:56 +0000 Subject: [PATCH 389/421] Added missing vars argument passthough to the JS api --- index.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index e82f646..925f6c5 100644 --- a/index.js +++ b/index.js @@ -1,7 +1,8 @@ /*eslint no-control-regex: 0*/ const path = require('path'); -const EventEmitter = require('events'); +const querystring = require('querystring'); +const {EventEmitter} = require('events'); const cliPath = path.resolve(__dirname, './cli.php'); const processes = []; @@ -126,8 +127,12 @@ class Process extends EventEmitter { args.push(`--${name}="${value}"`); } break; + case 'vars': + args.push(`--${name}="${querystring.stringify(value)}"`); + break; } } + return args.join(' '); } } From b37cd5f5a462baebac08bb033482dcc3876ef73c Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 16 Jan 2020 12:05:00 +0000 Subject: [PATCH 390/421] Bump to 3.0.1 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 94835bf..2df0a7b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "csscrush", - "version": "3.0.0", + "version": "3.0.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index e328200..2c38578 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "csscrush", - "version": "3.0.0", + "version": "3.0.1", "description": "CSS-Crush, CSS preprocessor", "repository": { "type": "git", From 7621c421b85089f7d802fd78d798f905fa7e8104 Mon Sep 17 00:00:00 2001 From: GalileoWebagentur <40769979+GalileoWebagentur@users.noreply.github.com> Date: Wed, 1 Sep 2021 13:43:59 +0200 Subject: [PATCH 391/421] Fix of php warning: Set memory_limit only if the current value is smaller (#96) * Fix of php warning: Set memory_limit only if the current value is smaller * Parse the value to compare the bytes See example #1 on https://www.php.net/manual/de/function.ini-get.php --- lib/CssCrush/Process.php | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 58f5f25..490ae13 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -913,6 +913,9 @@ public function preCompile() 'memory_limit' => '128M', ] as $name => $value) { $this->iniOriginal[$name] = ini_get($name); + if ($name === 'memory_limit' && $this->returnBytes(ini_get($name)) > $this->returnBytes($value)) { + continue; + } ini_set($name, $value); } @@ -923,6 +926,23 @@ public function preCompile() $this->stat['compile_start_time'] = microtime(true); } + + private function returnBytes(string $value) + { + $value = trim($value); + $last = strtolower($value[strlen($value) - 1]); + switch ($last) { + // The 'G' modifier is available + case 'g': + $value *= 1024; + case 'm': + $value *= 1024; + case 'k': + $value *= 1024; + } + + return $value; + } public function postCompile() { From 7cd5d73f67212dfc7ec0f85e4a84932a32ce95d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20F=C3=B6rster?= Date: Fri, 31 Dec 2021 12:20:48 +0100 Subject: [PATCH 392/421] [OPTIMIZE] data-uri( .svg ) (#98) * [OPTIMIZE] data-uri( .svg ) * prevent multi(in)line SVGs & cleanup * fix behavior concerning `}` converts to `.` --- lib/CssCrush/Url.php | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/CssCrush/Url.php b/lib/CssCrush/Url.php index 0ed604d..61ef4ba 100644 --- a/lib/CssCrush/Url.php +++ b/lib/CssCrush/Url.php @@ -170,8 +170,18 @@ public function toData() } $mime_type = $allowed_file_extensions[$file_ext]; - $base64 = base64_encode(file_get_contents($file)); - $this->value = "data:$mime_type;base64,$base64"; + + if('svg' == $file_ext) { + + $string = preg_replace('/\v+|\s{2,}/', ' ', file_get_contents($file)); + $string = str_replace(['"','#','<','}'], ['\'','%23','%3C','%7D'], $string); + $this->value = "data:$mime_type;utf8,$string"; + } + else { + + $base64 = base64_encode(file_get_contents($file)); + $this->value = "data:$mime_type;base64,$base64"; + } $this->setType('data')->protocol = 'data'; From 7d51a42e83eaaa4e6d2ab28eab2c664a23e51cbb Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 31 Dec 2021 12:01:10 +0000 Subject: [PATCH 393/421] Only the newline characters require escaping for data uris --- lib/CssCrush/Url.php | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/CssCrush/Url.php b/lib/CssCrush/Url.php index 61ef4ba..a0164f1 100644 --- a/lib/CssCrush/Url.php +++ b/lib/CssCrush/Url.php @@ -170,16 +170,14 @@ public function toData() } $mime_type = $allowed_file_extensions[$file_ext]; - - if('svg' == $file_ext) { + $file_contents = file_get_contents($file); - $string = preg_replace('/\v+|\s{2,}/', ' ', file_get_contents($file)); - $string = str_replace(['"','#','<','}'], ['\'','%23','%3C','%7D'], $string); + if ($file_ext === 'svg') { + $string = preg_replace('/\R/', '%0A', trim($file_contents)); $this->value = "data:$mime_type;utf8,$string"; } else { - - $base64 = base64_encode(file_get_contents($file)); + $base64 = base64_encode($file_contents); $this->value = "data:$mime_type;base64,$base64"; } From 0ac244887e9391e4dcd72e32f16abc7e683f04a5 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 31 Dec 2021 12:02:03 +0000 Subject: [PATCH 394/421] Update eslint dep. --- package-lock.json | 1803 +++++++++++++++++++++++++++++++-------------- package.json | 2 +- 2 files changed, 1266 insertions(+), 539 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2df0a7b..e630de3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,93 +1,1078 @@ { "name": "csscrush", "version": "3.0.1", - "lockfileVersion": 1, + "lockfileVersion": 2, "requires": true, + "packages": { + "": { + "name": "csscrush", + "version": "3.0.1", + "license": "MIT", + "bin": { + "csscrush": "bin/csscrush" + }, + "devDependencies": { + "eslint": "~8.5.0", + "normalize.css": "7.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.5.tgz", + "integrity": "sha512-BLxsnmK3KyPunz5wmCCpqy0YelEoxxGmH73Is+Z74oOTMtExcjkr3dDR6quwrjh1YspA8DH9gnX1o069KiS9AQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.2.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.2.tgz", + "integrity": "sha512-UXOuFCGcwciWckOpmfKDq/GyhlTf9pN/BzG//x8p8zTOFEcGuA68ANXheFS0AGvy3qgZqLBUkMs7hqzqCKOVwA==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/acorn": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.5.0.tgz", + "integrity": "sha512-tVGSkgNbOfiHyVte8bCM8OmX+xG9PzVG/B4UCF60zx7j61WIVY/AqJECDgpLD4DbbESD0e174gOg3ZlrX15GDg==", + "dev": true, + "dependencies": { + "@eslint/eslintrc": "^1.0.5", + "@humanwhocodes/config-array": "^0.9.2", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.0", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.1.0", + "espree": "^9.2.0", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^6.0.1", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.2.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-scope": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.0.tgz", + "integrity": "sha512-aWwkhnS0qAXqNOgKOK0dJ2nvzEbhEvpy8OlJ9kZ0FeZnA6zpjv1/Vei+puGFFX7zkPCkHHXb7IDX3A+7yPrRWg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.1.0.tgz", + "integrity": "sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/espree": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.2.0.tgz", + "integrity": "sha512-oP3utRkynpZWF/F2x/HZJ+AGtnIclaR7z1pYPxy7NYM2fSO6LgK/Rkny8anRSPK/VwEA1eqm2squui0T7ZMOBg==", + "dev": true, + "dependencies": { + "acorn": "^8.6.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^3.1.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz", + "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", + "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "node_modules/normalize.css": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-7.0.0.tgz", + "integrity": "sha1-q/sd2CRwZ04DIrU86xqvQSk45L8=", + "dev": true + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/v8-compile-cache": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz", + "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==", + "dev": true + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + }, "dependencies": { - "@babel/code-frame": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", - "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", + "@eslint/eslintrc": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.5.tgz", + "integrity": "sha512-BLxsnmK3KyPunz5wmCCpqy0YelEoxxGmH73Is+Z74oOTMtExcjkr3dDR6quwrjh1YspA8DH9gnX1o069KiS9AQ==", "dev": true, "requires": { - "@babel/highlight": "^7.0.0" + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.2.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" } }, - "@babel/highlight": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", - "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==", + "@humanwhocodes/config-array": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.2.tgz", + "integrity": "sha512-UXOuFCGcwciWckOpmfKDq/GyhlTf9pN/BzG//x8p8zTOFEcGuA68ANXheFS0AGvy3qgZqLBUkMs7hqzqCKOVwA==", "dev": true, "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^4.0.0" + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.4" } }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, "acorn": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.0.tgz", - "integrity": "sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ==", + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", "dev": true }, "acorn-jsx": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.1.0.tgz", - "integrity": "sha512-tMUqwBWfLFbJbizRmEcWSLw6HnFzfdJs2sOJEOwwtVPMoH/0Ay+E703oZz78VSXZiiDcZrQ5XKjPIUQixhmgVw==", - "dev": true + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "requires": {} }, "ajv": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", - "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "requires": { - "fast-deep-equal": "^2.0.1", + "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, - "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true }, "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "color-convert": "^1.9.0" + "color-convert": "^2.0.1" } }, "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, "brace-expansion": { @@ -107,50 +1092,28 @@ "dev": true }, "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "requires": { - "restore-cursor": "^2.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } }, - "cli-width": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", - "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", - "dev": true - }, "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { - "color-name": "1.1.3" + "color-name": "~1.1.4" } }, "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, "concat-map": { @@ -160,39 +1123,29 @@ "dev": true }, "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" } }, "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, "doctrine": { @@ -204,127 +1157,133 @@ "esutils": "^2.0.2" } }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1" + } }, "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, "eslint": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.5.1.tgz", - "integrity": "sha512-32h99BoLYStT1iq1v2P9uwpyznQ4M2jRiFB6acitKz52Gqn+vPaMDUTB1bYi1WN4Nquj2w+t+bimYUG83DC55A==", + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.5.0.tgz", + "integrity": "sha512-tVGSkgNbOfiHyVte8bCM8OmX+xG9PzVG/B4UCF60zx7j61WIVY/AqJECDgpLD4DbbESD0e174gOg3ZlrX15GDg==", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0", + "@eslint/eslintrc": "^1.0.5", + "@humanwhocodes/config-array": "^0.9.2", "ajv": "^6.10.0", - "chalk": "^2.1.0", - "cross-spawn": "^6.0.5", - "debug": "^4.0.1", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", "doctrine": "^3.0.0", - "eslint-scope": "^5.0.0", - "eslint-utils": "^1.4.2", - "eslint-visitor-keys": "^1.1.0", - "espree": "^6.1.1", - "esquery": "^1.0.1", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.0", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.1.0", + "espree": "^9.2.0", + "esquery": "^1.4.0", "esutils": "^2.0.2", - "file-entry-cache": "^5.0.1", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", - "globals": "^11.7.0", + "glob-parent": "^6.0.1", + "globals": "^13.6.0", "ignore": "^4.0.6", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", - "inquirer": "^6.4.1", "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", + "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.14", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", "natural-compare": "^1.4.0", - "optionator": "^0.8.2", + "optionator": "^0.9.1", "progress": "^2.0.0", - "regexpp": "^2.0.1", - "semver": "^6.1.2", - "strip-ansi": "^5.2.0", - "strip-json-comments": "^3.0.1", - "table": "^5.2.3", + "regexpp": "^3.2.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" } }, "eslint-scope": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", - "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.0.tgz", + "integrity": "sha512-aWwkhnS0qAXqNOgKOK0dJ2nvzEbhEvpy8OlJ9kZ0FeZnA6zpjv1/Vei+puGFFX7zkPCkHHXb7IDX3A+7yPrRWg==", "dev": true, "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" } }, "eslint-utils": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", - "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", "dev": true, "requires": { - "eslint-visitor-keys": "^1.1.0" + "eslint-visitor-keys": "^2.0.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + } } }, "eslint-visitor-keys": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", - "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.1.0.tgz", + "integrity": "sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA==", "dev": true }, "espree": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/espree/-/espree-6.1.2.tgz", - "integrity": "sha512-2iUPuuPP+yW1PZaMSDM9eyVf8D5P0Hi8h83YtZ5bPc/zHYjII5khoixIUTMO794NOY8F/ThF1Bo8ncZILarUTA==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.2.0.tgz", + "integrity": "sha512-oP3utRkynpZWF/F2x/HZJ+AGtnIclaR7z1pYPxy7NYM2fSO6LgK/Rkny8anRSPK/VwEA1eqm2squui0T7ZMOBg==", "dev": true, "requires": { - "acorn": "^7.1.0", - "acorn-jsx": "^5.1.0", - "eslint-visitor-keys": "^1.1.0" + "acorn": "^8.6.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^3.1.0" } }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, "esquery": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", - "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", "dev": true, "requires": { - "estraverse": "^4.0.0" + "estraverse": "^5.1.0" } }, "esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "requires": { - "estraverse": "^4.1.0" + "estraverse": "^5.2.0" } }, "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true }, "esutils": { @@ -333,27 +1292,16 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, - "external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - } - }, "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, "fast-levenshtein": { @@ -362,39 +1310,29 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, - "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, "file-entry-cache": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", - "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, "requires": { - "flat-cache": "^2.0.1" + "flat-cache": "^3.0.4" } }, "flat-cache": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", - "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", "dev": true, "requires": { - "flatted": "^2.0.0", - "rimraf": "2.6.3", - "write": "1.0.3" + "flatted": "^3.1.0", + "rimraf": "^3.0.2" } }, "flatted": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz", - "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz", + "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==", "dev": true }, "fs.realpath": { @@ -410,9 +1348,9 @@ "dev": true }, "glob": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.5.tgz", - "integrity": "sha512-J9dlskqUXK1OeTOYBEn5s8aMukWMwWfs+rPTn/jn50Ux4MNXVhubL1wu/j2t+H4NVI+cXEcCaYellqaPVGXNqQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -424,35 +1362,29 @@ } }, "glob-parent": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", - "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "requires": { - "is-glob": "^4.0.1" + "is-glob": "^4.0.3" } }, "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", + "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", "dev": true, "requires": { - "safer-buffer": ">= 2.1.2 < 3" + "type-fest": "^0.20.2" } }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, "ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", @@ -460,9 +1392,9 @@ "dev": true }, "import-fresh": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.1.0.tgz", - "integrity": "sha512-PpuksHKGt8rXfWEr9m9EHIpgyyaltBy8+eF6GJM0QCAxMgxCfucMF3mjecK2QsJr0amJW7gTqh5/wht0z2UhEQ==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, "requires": { "parent-module": "^1.0.0", @@ -491,74 +1423,34 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "inquirer": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz", - "integrity": "sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==", - "dev": true, - "requires": { - "ansi-escapes": "^3.2.0", - "chalk": "^2.4.2", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^3.0.3", - "figures": "^2.0.0", - "lodash": "^4.17.12", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rxjs": "^6.4.0", - "string-width": "^2.1.0", - "strip-ansi": "^5.1.0", - "through": "^2.3.6" - } - }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", "dev": true }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "requires": { "is-extglob": "^2.1.1" } }, - "is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", - "dev": true - }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "^2.0.1" } }, "json-schema-traverse": { @@ -574,26 +1466,29 @@ "dev": true }, "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" } }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "dev": true + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } }, "minimatch": { "version": "3.0.4", @@ -604,45 +1499,18 @@ "brace-expansion": "^1.1.7" } }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "requires": { - "minimist": "0.0.8" - } - }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "mute-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", - "dev": true - }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, "normalize.css": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-7.0.0.tgz", @@ -658,35 +1526,20 @@ "wrappy": "1" } }, - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", - "dev": true, - "requires": { - "mimic-fn": "^1.0.0" - } - }, "optionator": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", - "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", "dev": true, "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.4", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "wordwrap": "~1.0.0" + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" } }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -703,15 +1556,15 @@ "dev": true }, "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, "progress": { @@ -727,9 +1580,9 @@ "dev": true }, "regexpp": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", - "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", "dev": true }, "resolve-from": { @@ -738,169 +1591,61 @@ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, - "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", - "dev": true, - "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - } - }, "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, "requires": { "glob": "^7.1.3" } }, - "run-async": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", - "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", - "dev": true, - "requires": { - "is-promise": "^2.1.0" - } - }, - "rxjs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.3.tgz", - "integrity": "sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA==", + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", "dev": true, "requires": { - "tslib": "^1.9.0" + "lru-cache": "^6.0.0" } }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "requires": { - "shebang-regex": "^1.0.0" + "shebang-regex": "^3.0.0" } }, "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true - }, - "slice-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", - "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "astral-regex": "^1.0.0", - "is-fullwidth-code-point": "^2.0.0" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { - "ansi-regex": "^4.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - } + "ansi-regex": "^5.0.1" } }, "strip-json-comments": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", - "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "table": { - "version": "5.4.6", - "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", - "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { - "ajv": "^6.10.2", - "lodash": "^4.17.14", - "slice-ansi": "^2.1.0", - "string-width": "^3.0.0" - }, - "dependencies": { - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - } + "has-flag": "^4.0.0" } }, "text-table": { @@ -909,40 +1654,25 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "requires": { - "os-tmpdir": "~1.0.2" + "prelude-ls": "^1.2.1" } }, - "tslib": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", - "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==", + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2" - } - }, "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "requires": { "punycode": "^2.1.0" @@ -955,18 +1685,18 @@ "dev": true }, "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "requires": { "isexe": "^2.0.0" } }, - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", "dev": true }, "wrappy": { @@ -975,14 +1705,11 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, - "write": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", - "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", - "dev": true, - "requires": { - "mkdirp": "^0.5.1" - } + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true } } } diff --git a/package.json b/package.json index 2c38578..89e21fb 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "homepage": "http://the-echoplex.net/csscrush", "license": "MIT", "devDependencies": { - "eslint": "~6.5.1", + "eslint": "~8.5.0", "normalize.css": "7.0.0" } } From c7698015e7a614c1a22a9df5bb90768b0c769f91 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 31 Dec 2021 12:12:03 +0000 Subject: [PATCH 395/421] Update unit test --- lib/CssCrush/Process.php | 4 +++- tests/unit/CssCrush/UrlTest.php | 2 +- tests/unit/CssCrush/UtilTest.php | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 490ae13..7b15cc7 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -926,11 +926,13 @@ public function preCompile() $this->stat['compile_start_time'] = microtime(true); } - + private function returnBytes(string $value) { $value = trim($value); $last = strtolower($value[strlen($value) - 1]); + $value = (float) $value; + switch ($last) { // The 'G' modifier is available case 'g': diff --git a/tests/unit/CssCrush/UrlTest.php b/tests/unit/CssCrush/UrlTest.php index f7dbf2e..9c04e0c 100644 --- a/tests/unit/CssCrush/UrlTest.php +++ b/tests/unit/CssCrush/UrlTest.php @@ -100,7 +100,7 @@ public function testToData() unlink($test_filepath); $this->assertEquals( - 'data:image/svg+xml;base64,PHN2Zz48cGF0aCBkPSJNMCwwIGgxMCBsLTEwLDEweiIvPjwvc3ZnPg==', + 'data:image/svg+xml;utf8,', $url->value); } else { diff --git a/tests/unit/CssCrush/UtilTest.php b/tests/unit/CssCrush/UtilTest.php index 0a08542..4d1fdfb 100644 --- a/tests/unit/CssCrush/UtilTest.php +++ b/tests/unit/CssCrush/UtilTest.php @@ -90,7 +90,7 @@ public function testGetLinkBetweenPaths() $path1 = __DIR__; $path2 = realpath(__DIR__ . '/../../'); $this->assertEquals('../../', Util::getLinkBetweenPaths($path1, $path2)); - $this->assertEquals('unit/CssCrush/', Util::getLinkBetweenPaths($path2, $path1)); + $this->assertEquals('Unit/CssCrush/', Util::getLinkBetweenPaths($path2, $path1)); } public function testFilePutContents() From 63e7fc945d857169646740d3bc090c0d28b771b2 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 31 Dec 2021 20:10:23 +0000 Subject: [PATCH 396/421] Update vendor aliases. Update changelog. Add bitmap image data-uri unit test. --- CHANGELOG.md | 8 +++++- aliases.ini | 46 -------------------------------- tests/unit/CssCrush/UrlTest.php | 6 +++++ tests/unit/dummy-data/tiny.png | Bin 0 -> 4795 bytes 4 files changed, 13 insertions(+), 47 deletions(-) create mode 100644 tests/unit/dummy-data/tiny.png diff --git a/CHANGELOG.md b/CHANGELOG.md index af788c7..91d201c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,10 @@ -## 3.0.0 +## 3.1.0 (2021-12-31) + +* Added support for utf-8 markup in data-uri *.svg files. (@xerc) +* Fixed issue with phpini.memory_limit (@GalileoWebagentur) +* Updated vendor aliases. + +## 3.0.0 (2019-12-31) * Raised php requirement to >= 5.6 * Removed `csscrush_version()` diff --git a/aliases.ini b/aliases.ini index 8e797d1..5ebae6f 100644 --- a/aliases.ini +++ b/aliases.ini @@ -73,40 +73,23 @@ align-items[] = -webkit-align-items align-self[] = -webkit-align-self flex[] = -webkit-flex - flex[] = -ms-flex flex-basis[] = -webkit-flex-basis flex-direction[] = -webkit-flex-direction - flex-direction[] = -ms-flex-direction flex-flow[] = -webkit-flex-flow - flex-flow[] = -ms-flex-flow flex-grow[] = -webkit-flex-grow flex-shrink[] = -webkit-flex-shrink flex-wrap[] = -webkit-flex-wrap - flex-wrap[] = -ms-flex-wrap justify-content[] = -webkit-justify-content order[] = -webkit-order - order[] = -ms-flex-order ; Hyphens. hyphens[] = -webkit-hyphens hyphens[] = -ms-hyphens - ; Outline radius. - outline-radius[] = -moz-outline-radius - outline-top-left-radius[] = -moz-outline-radius-topleft - outline-top-right-radius[] = -moz-outline-radius-topright - outline-bottom-left-radius[] = -moz-outline-radius-bottomleft - outline-bottom-right-radius[] = -moz-outline-radius-bottomright - ; Perspective. perspective[] = -webkit-perspective perspective-origin[] = -webkit-perspective-origin - ; Shapes - shape-image-threshold[] = -webkit-shape-image-threshold - shape-outside[] = -webkit-shape-outside - shape-margin[] = -webkit-shape-margin - ; Tab size. tab-size[] = -moz-tab-size tab-size[] = -o-tab-size @@ -143,38 +126,9 @@ [declarations] ; Flexbox (2012). - display:flex[] = display:-ms-flexbox display:flex[] = display:-webkit-flex - display:inline-flex[] = display:-ms-inline-flexbox display:inline-flex[] = display:-webkit-inline-flex - ; Flexbox (early 2012). - align-content:flex-start[] = -ms-flex-line-pack:start - align-content:flex-end[] = -ms-flex-line-pack:end - align-content:center[] = -ms-flex-line-pack:center - align-content:space-between[] = -ms-flex-line-pack:justify - align-content:space-around[] = -ms-flex-line-pack:distribute - align-content:stretch[] = -ms-flex-line-pack:stretch - - align-items:flex-start[] = -ms-flex-align:start - align-items:flex-end[] = -ms-flex-align:end - align-items:center[] = -ms-flex-align:center - align-items:baseline[] = -ms-flex-align:baseline - align-items:stretch[] = -ms-flex-align:stretch - - align-self:auto[] = -ms-flex-item-align:auto - align-self:flex-start[] = -ms-flex-item-align:start - align-self:flex-end[] = -ms-flex-item-align:end - align-self:center[] = -ms-flex-item-align:center - align-self:baseline[] = -ms-flex-item-align:baseline - align-self:stretch[] = -ms-flex-item-align:stretch - - justify-content:flex-start[] = -ms-flex-pack:start - justify-content:flex-end[] = -ms-flex-pack:end - justify-content:center[] = -ms-flex-pack:center - justify-content:space-between[] = -ms-flex-pack:justify - justify-content:space-around[] = -ms-flex-pack:distribute - ; Cursor values (non-standard). cursor:zoom-in[] = cursor:-webkit-zoom-in cursor:zoom-out[] = cursor:-webkit-zoom-out diff --git a/tests/unit/CssCrush/UrlTest.php b/tests/unit/CssCrush/UrlTest.php index 9c04e0c..a566519 100644 --- a/tests/unit/CssCrush/UrlTest.php +++ b/tests/unit/CssCrush/UrlTest.php @@ -106,6 +106,12 @@ public function testToData() else { $this->markTestSkipped('Cannot write test SVG file to disk.'); } + + $url = new Url('/tests/unit/dummy-data/tiny.png'); + $url->toData(); + $this->assertStringStartsWith( + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASoAAAEqCAMAAACV5O0dAAAA', + $url->value); } public function testSetType() diff --git a/tests/unit/dummy-data/tiny.png b/tests/unit/dummy-data/tiny.png new file mode 100644 index 0000000000000000000000000000000000000000..8fbc2d37703d0da9b8311bb66d1d36e59461004c GIT binary patch literal 4795 zcmaJ_2{@GN+t-m2e`Mcla43UeWXUpVG}bJG88fnE#*B3qW(Gsl$rdwNBFiac31e(g zs1z+o)?}BZtR+j4wBbKG)j8jHedl|xce$VYy??*^e(vXfuj_u^1h}1r0G|vW7Z;a+ zl_kuPi)$Bf=i=q&Xwqkv`KqI7Mw zYy(f=ef=!MDR`%FJA_wwfR_PI(c~Dwm~O}+AmV8l0G&u6Q4Q%3#qYd^9C=5rsR;OP zLJNQ>{uLD377jQ;rr-g(AUzE)u+C9{z5xiVr=xX5PaUAG1vb#sI;sgiqM-#g)Y3E5 z)d&1I6gl1~IB!EonE4N1oDoFPmqrUT)YJ?O4F!cB0g)*_nqUJ14u`g;wzdYxLW3Gc zqG9M7B&yO+1{j{|Mez%y`H@M09Yze6987~Kaw7d}3dF!)v?S_}G;tE9Nyh|gfr;v$&@3S`a`}bJ%|CR3t7WeP59Q{|UCMOupowfbPTK`mW3TQ|CRk@tOuiD3x zI0aAPRCcz~Nh8izp#`@=n2k=Nd9q$LtbBFvTuYJ(Rg#tzCuEhsEIDZ6=yEAHpzg`` zgrw3zz=}jG7km5MtBp{d#WgN2!FN_LD1tt^m@^Vp2-t6i5s?shv$fqMZ<})e7FQQm z(!MWEZQqj)MX|Om2UC@gE14o7VW6oQi&s2f5fQ@>`F@A*s1m;4S-b;JR9d9+ABnH( zDVwqO=U%ZIm&ykY=@$>eTdr;65mvDs@LFWskpk;p!sh0@!}xI>_0QRGRo;O_xAft- zT`b4cnD)n(aNWVCG(m|$cqmTn=n+Z}ChLn4D-|eiR1(9+Tn-jQj998J z%pkU&jB>4yv4)An*J2MnTQ2P`PB%#_qi_K&g4L4bwvx&us_4%3|49Z8HoXuSSI8~e zYr~Y&onVY-0grvqGA`}QI|Y2M@x^tQP1= zBy;6M8f3WtmQj)6*Ua2qF8E%x-n06^BI|Kal^Vaut^CWc{NRvNzO^4Cj)>=I%>8-H zlc&~NJI3~77i)1?)#WP2s&~3Mbsm?Y?{Nq=dj;>jFcH<`bc&6h>>qrvdDVUPWR>&r z49|46wFHOi0;igu!Nl|q`w?#KHr0i?bg|!kN(h5#A+E@j_7Z6QUYE&ZyOFV1q7~As zUJ{N?MQr=Jk2BKv=+1PDrfT^rc%-q^N;7uNW-h|&Cs3sKL1WneS&YgjDog* zerI37?Hqiz&HLLTi*19fmpiA82rg#d9LOxVQV4OuP)eU8cw9?;21}qtQSXh9Oa1VB@w(OlN1@DP%+0z& zz5~0Zoz%))$6280elu%umGls_wpX%>d*F-6OF@>3op-upbB7!blu3fH1m!F2*99*; zoqkUF7R_qCGw~2z$yNUJ)U{ZKe4>9m6tWeT^BiKmwml>MUJHHgHT(8l!rmgIENu^q zhhK~l)B)zgD4$BQn%}LE=pq@Px7`=r*=Di z)%a5o%sPYQyRU4MT>Z#1FUR6-@}D_p5rty5alps^`h&eae{8NHov0K3EJp}IZ!qZy z|3cQEu{T!7jh^fJPMF{t8B2zzb(b-vooL&1c*}XZ{LMTc56?$$79$j?#oP=w+YdetO zADA4m;ogT_>|VGLOqXM~I8B958n|adqZjo=%c`QbMC$qmt>#O|t#}d}7D@RFhlUHw z1_W7qFoH_rH)QT!T^*eRN=Md^5S1%NF$n!KPab)+)1*I3s^-loO!t6Idp!!`)jI_W z#idnhLErKsBqMc7{}>o{+j!1B#WpmFW4xP$t^ekjn@*}c<2O5k6&H120k#DYkJT*0 zoNF3ZUjbjS?+Fah`{{gGU7dkO#|js}v5_x*UVYsfL-27S`<|dP?o{OAuuozF5BTc2 z&7Yx#*st){mp9_(7_&zHX+$dnjSQW6b$#qf9=WB%n_u`48pxD!DQ=6U5w_i387Ie^ zt+u3|g+m^u?LuZRrt$rwa$?4i+mEML)yhu8dGMO5&ZR3RJ{alZ(<@bY{3+Bu|KzMs z71@mQ4o^{OHOL!5!iSW2D$2fYR~f(A4Xe#s*m{H`>V8PU9^a5aHWUN-3&QW0-X$On z(^MmVPfCV*X_NyHgE5|mb25SPt7IYmS>>hPe^I1ZE;NfC# zWVH)a#+f@Q*ahNRylRXD8zPcRuMpEU_C21DV(0YmxJ!($Z0L{p6M~T8cIV!EdISeR z`kF5K)6+_?$c8u@Cv%6!Ro`7#C*AKtgb)c-)T@yT4pde1037`W#EUVb&?`d(mAq%5#k_i<)&P12cp=V63<70R=uH~4Mg zN2Z~71*6P4HVrB3G2?7H8^?zVoIY{81T&jf%q*`65!w%#+We!g)LMVlJ-QWk{Mzr5 z?r$|kipdjF*n#ahJ1Jx%StoZe(K?gd#W1-ex!S-j8BBgK623ShFu;09tP6Zv`OZRC z^8DBAu4>mup-|=3XpxBOst1Qq2SM34Pu=1DQathGL1vyzK}Fpu>!pxMM!@Y$miE^^ z?P-ggX^v&i^-1J0=g52;C(eljtFxIY%MWs*g_jOR+nMx++%PG3!>uw8+yrI5ERb(_ zPzcw!s#G}Kd3zW0ikm7{ju~R3cRkK1P}A`KVLDsHdjs?ifT$C*5(_}zo;fRI<@yax zh}=3Ey{^idv*K=aLzvv(03k+vN5qX9#w&+=OpQ=Hubzel^K~B07^*d?;O~UHe4Wu~ zIsCvQBL5RDDKtIQ4RcxS^yNaygUSByw|Qi)NQA4Pcpl5;@(g%}>hZFyPAv~UY2YI` zS$y(-Q6tb)T9$p{EEK@4gV}t~B0yyGS{`Dbh^1>#(sPQRJ*y#H`sUlXZ-CeKj^qWS z6X=CDbF>;2Y=#FyXEesvd)SsOCuyTNyHhSNS&D*g~D zoX+Bx_!rARhVUKePg0JnC06%qcF-_nWUZ<@W0>n2(Mr`&CmAlx9#8Xj6y6P`-PGE7cu#A?7}(StMJ zA`+iqu+;~Fng)J+Wm)WQPo&nrtqA4P@<5wPh#hv zRtax|9j3+3MC-6c|I>nXB}8Q#EG+w9Zdig0ADGs>FZZnu z*7ff9Zb>CGMsA!<9!p`6y*8#bUA1F|Em1YL@3XIY#YvZ2!Mc|-N9VXC>?==Iw>#38 z+}u^?vM@_?$>pMZqkoS<=d}=??a8jC#?E8Z)bl^v2;Z7>#fmc4d$IQwGQ#{$R{mx( zcha1xPk2An(s3c&T@INzYawxnSdw4D+Rse6EZH%Tt;B?48)Ox?v7pS*M_*6PY*~<) zpBcmP*M+=T-}#3GbTAck=qtHmK-3ACHgmGx*y7;<1Sb*)*8Lp z|0zZLhFn^fd8G$7-a3rTQ>kWkjEFTqy_pS4i0yF6Sil|D^B}Zih3CHwdr|kTYM&Q; zy@Ja`mosfsZ=P>a=@>ClZlg@g$8TQat?mk+I6CEeg~snzYC8w@Si<&c45pw%n6Zcb zuO)Z5?a?0_xXfHQ z+{rsSI12U7wh%*AWKYijDsOgZVXCe|=QV{n$E|zEv-84jP zkpf~B{PmA6tCx@4%B3QO1jRd4g5ug=IzW2*%b>AcBM8{ORUiWCP!ZH^hG=^p;G|Tk zkrHMCTswxKH#x)N>+($J}(;bcokH62p;4vYIa(fcn2oZAJ6jyC8~~c zp^B!)ZtO;ywUzPLL+cmlL6P0Xz*X4oq~rl#M4RIa;K~XD!ax#mDhHpAe#w0{a=0WSpxf^{J*Hh#dySF( zQDW~d>G*6l(}YNMuwC|yT)|ETDzFJn>w6i(bQrlO6^2R}*#S zUGC+jLZ|cU(Xm&T>RQfwI?3^7$nrD7KoTs=_lDI0_lObcNiwh3_j?}@Kp0pPWM3!J z=jn@21$Av&bOe(h14`&a+wE&HI;a|=kay2?QNyR9$Uv%Le}L=-trx>z3=B_-9T4EE z{*5{ZJOBx0+T z1r2S+>Qs8lbDN(HKdS|M5vr^yLbX(XhCO!2dM%NKn_iM$ z%z!4GYcy4dV`20QZjVyILAyaQ(h75v%Egvaw4H6~kFBV<@;`T~{`T8EQe3uP?(!ma S(zZLlldR0_V3nqxQU3)1xQ0al literal 0 HcmV?d00001 From fae58e3a90a3fe22d4ebc5deb13f05096deda7ec Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 31 Dec 2021 20:36:39 +0000 Subject: [PATCH 397/421] Create php.yml --- .github/workflows/php.yml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 .github/workflows/php.yml diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml new file mode 100644 index 0000000..edd94f4 --- /dev/null +++ b/.github/workflows/php.yml @@ -0,0 +1,29 @@ +name: CI + +on: [push] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Validate composer.json and composer.lock + run: composer validate --strict + + - name: Cache Composer packages + id: composer-cache + uses: actions/cache@v2 + with: + path: vendor + key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-php- + + - name: Install dependencies + run: composer install --prefer-dist --no-progress + + - name: Run test suite + run: composer run-script test From 5d8260d0fa13f3ecee25e6b7e9ca069ba0d1b558 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 31 Dec 2021 20:40:31 +0000 Subject: [PATCH 398/421] Remove travis ci. Add composer test command. --- .travis.yml | 15 --------------- README.md | 2 -- composer.json | 3 +++ 3 files changed, 3 insertions(+), 17 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 26aff96..0000000 --- a/.travis.yml +++ /dev/null @@ -1,15 +0,0 @@ -language: php - -php: - - 5.6 - - 7.0 - - 7.1 - - 7.2 - - 7.3 - - 7.4 - -before_script: - - composer self-update - - composer install --dev - -script: ./vendor/bin/phpunit -v diff --git a/README.md b/README.md index cef3f57..91f0d16 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,3 @@ -[![Build Status](https://travis-ci.org/peteboere/css-crush.svg)](https://travis-ci.org/peteboere/css-crush) - Logo A CSS preprocessor designed to enable a modern and uncluttered CSS workflow. diff --git a/composer.json b/composer.json index 10cee4b..4eb60ec 100644 --- a/composer.json +++ b/composer.json @@ -5,6 +5,9 @@ "keywords": ["css", "preprocessor"], "homepage": "http://the-echoplex.net/csscrush", "license": "MIT", + "scripts": { + "test": "vendor/bin/phpunit tests" + }, "authors": [ { "name": "Pete Boere", From ef58e886b8c215a37add95bd96602cc9dbdf07a7 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 31 Dec 2021 21:30:53 +0000 Subject: [PATCH 399/421] Upgrade phpunit dep. and tests --- .gitignore | 1 + composer.json | 2 +- lib/CssCrush/Process.php | 2 +- tests/bootstrap.php | 2 +- tests/unit/CssCrush/BalancedMatchTest.php | 4 ++-- tests/unit/CssCrush/ColorTest.php | 2 +- tests/unit/CssCrush/DeclarationTest.php | 4 ++-- tests/unit/CssCrush/EventEmitterTest.php | 2 +- tests/unit/CssCrush/ExtendArgTest.php | 2 +- tests/unit/CssCrush/FunctionsTest.php | 2 +- tests/unit/CssCrush/LoggerTest.php | 2 +- tests/unit/CssCrush/OptionsTest.php | 8 ++++---- tests/unit/CssCrush/RegexTest.php | 2 +- tests/unit/CssCrush/SelectorTest.php | 4 ++-- tests/unit/CssCrush/StringObjectTest.php | 2 +- tests/unit/CssCrush/TemplateTest.php | 8 ++++---- tests/unit/CssCrush/TokensTest.php | 14 +++++++------- tests/unit/CssCrush/UrlTest.php | 4 ++-- tests/unit/CssCrush/UtilTest.php | 4 ++-- tests/unit/CssCrush/VersionTest.php | 2 +- tests/unit/api/apiTest.php | 8 ++++---- tests/unit/cli/cliTest.php | 16 ++++++++-------- 22 files changed, 49 insertions(+), 48 deletions(-) diff --git a/.gitignore b/.gitignore index a20159e..1be7b0a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ _* .tm_properties +.phpunit.result.cache phpunit.xml composer.lock vendor diff --git a/composer.json b/composer.json index 4eb60ec..5fe4ab6 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,7 @@ "php": ">=5.6.1" }, "require-dev": { - "phpunit/phpunit": "5.*", + "phpunit/phpunit": "8.*", "psr/log": "1.0.*@dev", "twig/twig": "1.*" }, diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 7b15cc7..32f0dd8 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -268,7 +268,7 @@ protected function filterAliases() foreach ($vendor_names as &$vendor_name) { $vendor_name = trim($vendor_name, '-'); } - $vendor_patt = '~^\-(' . implode($vendor_names, '|') . ')\-~i'; + $vendor_patt = '~^\-(' . implode('|', $vendor_names) . ')\-~i'; // Loop the aliases array, filter down to the target vendor. diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 5e86877..d6dfee3 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -13,9 +13,9 @@ namespace CssCrush\UnitTest { - function bootstrap_process($options = []) { + error_reporting(E_ALL ^ E_DEPRECATED); $process = \CssCrush\Crush::$process = new \CssCrush\Process($options); $process->preCompile(); return $process; diff --git a/tests/unit/CssCrush/BalancedMatchTest.php b/tests/unit/CssCrush/BalancedMatchTest.php index 378604f..d7880f9 100644 --- a/tests/unit/CssCrush/BalancedMatchTest.php +++ b/tests/unit/CssCrush/BalancedMatchTest.php @@ -4,11 +4,11 @@ use CssCrush\BalancedMatch; -class BalancedMatchTest extends \PHPUnit_Framework_TestCase +class BalancedMatchTest extends \PHPUnit\Framework\TestCase { public $process; - public function setUp() + public function setUp(): void { $this->process = bootstrap_process(); $sample = '@foo; @bar {color: orange;} @baz'; diff --git a/tests/unit/CssCrush/ColorTest.php b/tests/unit/CssCrush/ColorTest.php index a7b3997..ddac560 100644 --- a/tests/unit/CssCrush/ColorTest.php +++ b/tests/unit/CssCrush/ColorTest.php @@ -4,7 +4,7 @@ use CssCrush\Color; -class ColorTest extends \PHPUnit_Framework_TestCase +class ColorTest extends \PHPUnit\Framework\TestCase { public function testConstruct() { diff --git a/tests/unit/CssCrush/DeclarationTest.php b/tests/unit/CssCrush/DeclarationTest.php index 6e66111..f8dbf1a 100644 --- a/tests/unit/CssCrush/DeclarationTest.php +++ b/tests/unit/CssCrush/DeclarationTest.php @@ -6,13 +6,13 @@ use CssCrush\Declaration; use CssCrush\Rule; -class DeclarationTest extends \PHPUnit_Framework_TestCase +class DeclarationTest extends \PHPUnit\Framework\TestCase { protected $process; protected $rule; protected $declaration; - public function setUp() + public function setUp(): void { $this->process = bootstrap_process(['minify' => false]); $this->rule = new Rule('.foo', '-fOo-BAR: math(10 + 10, px) !important'); diff --git a/tests/unit/CssCrush/EventEmitterTest.php b/tests/unit/CssCrush/EventEmitterTest.php index 1ca0cff..658a63b 100644 --- a/tests/unit/CssCrush/EventEmitterTest.php +++ b/tests/unit/CssCrush/EventEmitterTest.php @@ -4,7 +4,7 @@ use CssCrush\EventEmitter; -class EventEmitterTest extends \PHPUnit_Framework_TestCase +class EventEmitterTest extends \PHPUnit\Framework\TestCase { public function testAll() { diff --git a/tests/unit/CssCrush/ExtendArgTest.php b/tests/unit/CssCrush/ExtendArgTest.php index 856c57c..3145e03 100644 --- a/tests/unit/CssCrush/ExtendArgTest.php +++ b/tests/unit/CssCrush/ExtendArgTest.php @@ -4,7 +4,7 @@ use CssCrush\ExtendArg; -class ExtendArgTest extends \PHPUnit_Framework_TestCase +class ExtendArgTest extends \PHPUnit\Framework\TestCase { public function test__construct() { diff --git a/tests/unit/CssCrush/FunctionsTest.php b/tests/unit/CssCrush/FunctionsTest.php index eaf5c78..6cff8e0 100644 --- a/tests/unit/CssCrush/FunctionsTest.php +++ b/tests/unit/CssCrush/FunctionsTest.php @@ -4,7 +4,7 @@ use CssCrush\Functions; -class FunctionsTest extends \PHPUnit_Framework_TestCase +class FunctionsTest extends \PHPUnit\Framework\TestCase { public function testMakePattern() { diff --git a/tests/unit/CssCrush/LoggerTest.php b/tests/unit/CssCrush/LoggerTest.php index bb54cc3..531f7d2 100644 --- a/tests/unit/CssCrush/LoggerTest.php +++ b/tests/unit/CssCrush/LoggerTest.php @@ -7,7 +7,7 @@ class LoggerDummy extends Logger implements LoggerInterface {} -class LoggerTest extends \PHPUnit_Framework_TestCase +class LoggerTest extends \PHPUnit\Framework\TestCase { public function testInterface() { diff --git a/tests/unit/CssCrush/OptionsTest.php b/tests/unit/CssCrush/OptionsTest.php index 2ccb74a..c09655b 100644 --- a/tests/unit/CssCrush/OptionsTest.php +++ b/tests/unit/CssCrush/OptionsTest.php @@ -5,11 +5,11 @@ use CssCrush\Options; use CssCrush\Version; -class OptionsTest extends \PHPUnit_Framework_TestCase +class OptionsTest extends \PHPUnit\Framework\TestCase { public $testFile; - public function setUp() + public function setUp(): void { bootstrap_process(); $this->testFile = temp_file("\n foo {bar: baz;} \n\n baz {bar: foo;}"); @@ -43,8 +43,8 @@ public function testBoilerplate() 'newlines' => 'unix', ]); - $this->assertContains(' * ' . Version::detect(), (string) $result); - $this->assertContains(" * Line breaks\n * preserved\n *", (string) $result); + $this->assertStringContainsStringIgnoringCase(' * ' . Version::detect(), (string) $result); + $this->assertStringContainsStringIgnoringCase(" * Line breaks\n * preserved\n *", (string) $result); } public function testFormatters() diff --git a/tests/unit/CssCrush/RegexTest.php b/tests/unit/CssCrush/RegexTest.php index 7015e8f..e656810 100644 --- a/tests/unit/CssCrush/RegexTest.php +++ b/tests/unit/CssCrush/RegexTest.php @@ -4,7 +4,7 @@ use CssCrush\Regex; -class RegexTest extends \PHPUnit_Framework_TestCase +class RegexTest extends \PHPUnit\Framework\TestCase { public function testMake() { diff --git a/tests/unit/CssCrush/SelectorTest.php b/tests/unit/CssCrush/SelectorTest.php index e929496..6009443 100644 --- a/tests/unit/CssCrush/SelectorTest.php +++ b/tests/unit/CssCrush/SelectorTest.php @@ -4,9 +4,9 @@ use CssCrush\Selector; -class SelectorTest extends \PHPUnit_Framework_TestCase +class SelectorTest extends \PHPUnit\Framework\TestCase { - public function setUp() + public function setUp(): void { $this->process = bootstrap_process(); } diff --git a/tests/unit/CssCrush/StringObjectTest.php b/tests/unit/CssCrush/StringObjectTest.php index 5b5e7e4..55b1acc 100644 --- a/tests/unit/CssCrush/StringObjectTest.php +++ b/tests/unit/CssCrush/StringObjectTest.php @@ -4,7 +4,7 @@ use CssCrush\StringObject; -class StringObjectTest extends \PHPUnit_Framework_TestCase +class StringObjectTest extends \PHPUnit\Framework\TestCase { protected $sample = " Lorem ipsum dolor sit amet "; diff --git a/tests/unit/CssCrush/TemplateTest.php b/tests/unit/CssCrush/TemplateTest.php index 2ca8412..9587f2e 100644 --- a/tests/unit/CssCrush/TemplateTest.php +++ b/tests/unit/CssCrush/TemplateTest.php @@ -4,13 +4,13 @@ use CssCrush\Template; -class TemplateTest extends \PHPUnit_Framework_TestCase +class TemplateTest extends \PHPUnit\Framework\TestCase { protected $template; protected $template_raw; protected $template_string; - public function setUp() + public function setUp(): void { bootstrap_process(); @@ -74,8 +74,8 @@ public function testTokenize() [foo="bar"] {baz: url(http://github.com/image.png);} TPL; $sample = Template::tokenize($original_sample); - $this->assertContains('[foo=?s', $sample); - $this->assertContains('{baz: ?u', $sample); + $this->assertStringContainsStringIgnoringCase('[foo=?s', $sample); + $this->assertStringContainsStringIgnoringCase('{baz: ?u', $sample); $sample = Template::unTokenize($sample); $this->assertEquals($original_sample, $sample); diff --git a/tests/unit/CssCrush/TokensTest.php b/tests/unit/CssCrush/TokensTest.php index 1d3e4f8..812b2cb 100644 --- a/tests/unit/CssCrush/TokensTest.php +++ b/tests/unit/CssCrush/TokensTest.php @@ -4,11 +4,11 @@ use CssCrush\Tokens; -class TokensTest extends \PHPUnit_Framework_TestCase +class TokensTest extends \PHPUnit\Framework\TestCase { protected $tokens; - public function setUp() + public function setUp(): void { $this->process = bootstrap_process(['minify' => false]); @@ -69,10 +69,10 @@ public function testCapture() $sample = '[class="foo"] {bar: url(http://github.com/baz.png);}'; $sample = $this->tokens->capture($sample, 'u'); - $this->assertContains('?u', $sample); + $this->assertStringContainsStringIgnoringCase('?u', $sample); $sample = $this->tokens->capture($sample, 's'); - $this->assertContains('?s', $sample); + $this->assertStringContainsStringIgnoringCase('?s', $sample); } public function testCaptureUrls() @@ -80,7 +80,7 @@ public function testCaptureUrls() $sample = '[class="foo"] {bar: url(http://github.com/baz.png);}'; $sample = $this->tokens->captureUrls($sample); - $this->assertContains('?u', $sample); + $this->assertStringContainsStringIgnoringCase('?u', $sample); } public function testRestore() @@ -88,13 +88,13 @@ public function testRestore() $sample = '[class="foo"] {bar: url(http://github.com/baz.png);}'; $modified = $this->tokens->captureUrls($sample); - $this->assertContains('?u', $modified); + $this->assertStringContainsStringIgnoringCase('?u', $modified); $modified = $this->tokens->restore($modified, 'u'); $this->assertEquals($sample, $modified); $modified = $this->tokens->capture($sample, 's'); - $this->assertContains('?s', $modified); + $this->assertStringContainsStringIgnoringCase('?s', $modified); $modified = $this->tokens->restore($modified, 's'); $this->assertEquals($sample, $modified); diff --git a/tests/unit/CssCrush/UrlTest.php b/tests/unit/CssCrush/UrlTest.php index a566519..62f81c1 100644 --- a/tests/unit/CssCrush/UrlTest.php +++ b/tests/unit/CssCrush/UrlTest.php @@ -5,9 +5,9 @@ use CssCrush\Url; use CssCrush\Crush; -class UrlTest extends \PHPUnit_Framework_TestCase +class UrlTest extends \PHPUnit\Framework\TestCase { - public function setUp() + public function setUp(): void { bootstrap_process(['minify' => false]); } diff --git a/tests/unit/CssCrush/UtilTest.php b/tests/unit/CssCrush/UtilTest.php index 4d1fdfb..f3f3cf1 100644 --- a/tests/unit/CssCrush/UtilTest.php +++ b/tests/unit/CssCrush/UtilTest.php @@ -6,9 +6,9 @@ use CssCrush\Tokens; use CssCrush\Url; -class UtilTest extends \PHPUnit_Framework_TestCase +class UtilTest extends \PHPUnit\Framework\TestCase { - public function setUp() + public function setUp(): void { $this->process = bootstrap_process(['minify' => false]); $this->tokens = $this->process->tokens; diff --git a/tests/unit/CssCrush/VersionTest.php b/tests/unit/CssCrush/VersionTest.php index d7b5e05..3f8fd31 100644 --- a/tests/unit/CssCrush/VersionTest.php +++ b/tests/unit/CssCrush/VersionTest.php @@ -4,7 +4,7 @@ use CssCrush\Version; -class VersionTest extends \PHPUnit_Framework_TestCase +class VersionTest extends \PHPUnit\Framework\TestCase { public function test__toString() { diff --git a/tests/unit/api/apiTest.php b/tests/unit/api/apiTest.php index cfe501f..05f8ac5 100644 --- a/tests/unit/api/apiTest.php +++ b/tests/unit/api/apiTest.php @@ -2,13 +2,13 @@ namespace CssCrush\UnitTest; -class ApiTest extends \PHPUnit_Framework_TestCase +class ApiTest extends \PHPUnit\Framework\TestCase { protected $sample; protected $sampleFile; protected $sampleExpected; - public function setUp() + public function setUp(): void { $this->sample = ".foo {bar: baz;}"; $this->sampleExpected = ".foo{bar:baz}"; @@ -17,7 +17,7 @@ public function setUp() chdir(dirname($this->sampleFile)); } - public function tearDown() + public function tearDown(): void { chdir($this->originalWd); } @@ -112,7 +112,7 @@ public function testGetSet() csscrush_set('options', ['enable' => 'property-sorter']); - $this->assertContains('property-sorter', csscrush_get('options', 'enable')); + $this->assertStringContainsStringIgnoringCase('property-sorter', csscrush_get('options', 'enable')); csscrush_set('options', ['enable' => []]); } diff --git a/tests/unit/cli/cliTest.php b/tests/unit/cli/cliTest.php index 84e197f..694f307 100644 --- a/tests/unit/cli/cliTest.php +++ b/tests/unit/cli/cliTest.php @@ -4,12 +4,12 @@ use CssCrush\Crush; -class CliTest extends \PHPUnit_Framework_TestCase +class CliTest extends \PHPUnit\Framework\TestCase { protected $path; protected $sample; - public function setUp() + public function setUp(): void { $this->path = Crush::$dir . '/cli.php'; $this->sample = 'p {color: red; position: absolute; opacity: 1;}'; @@ -20,7 +20,7 @@ public function testHelp() exec("php \"$this->path\"", $lines); $help_text = implode("\n", $lines); - $this->assertContains('USAGE:', $help_text); + $this->assertStringContainsStringIgnoringCase('USAGE:', $help_text); } public function testPlugin() @@ -40,7 +40,7 @@ public function testIO() exec("php \"$this->path\" -i '$in_path' -o '$out_path' --enable property-sorter --test"); $expected = 'p{position:absolute;opacity:1;color:red}'; - $this->assertContains($expected, file_get_contents($out_path)); + $this->assertStringContainsStringIgnoringCase($expected, file_get_contents($out_path)); } public function testStats() @@ -48,10 +48,10 @@ public function testStats() exec("echo '$this->sample' | php \"$this->path\" --stats --test", $lines); $output = implode('', $lines); - $this->assertContains('Selector count: 1', $output); - $this->assertContains('Rule count: 1', $output); - $this->assertContains('Compile time:', $output); - $this->assertContains('p{color:red;position:absolute;opacity:1}', $output); + $this->assertStringContainsStringIgnoringCase('Selector count: 1', $output); + $this->assertStringContainsStringIgnoringCase('Rule count: 1', $output); + $this->assertStringContainsStringIgnoringCase('Compile time:', $output); + $this->assertStringContainsStringIgnoringCase('p{color:red;position:absolute;opacity:1}', $output); } public function testContext() From 93ea079c49859de962d285de5fd84367e38dd5df Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 1 Jan 2022 11:15:46 +0000 Subject: [PATCH 400/421] Update for compatability with PHP 8 Raising compatibility to >= 7 to support PHP 8 without deprecation warnings. --- lib/CssCrush/Crush.php | 4 ++++ lib/CssCrush/DeclarationList.php | 2 +- lib/CssCrush/Functions.php | 4 +++- lib/CssCrush/IO.php | 5 ++++- lib/CssCrush/Iterator.php | 12 ++++++------ lib/CssCrush/Process.php | 4 ++-- tests/bootstrap.php | 1 - tests/unit/CssCrush/UtilTest.php | 2 +- 8 files changed, 21 insertions(+), 13 deletions(-) diff --git a/lib/CssCrush/Crush.php b/lib/CssCrush/Crush.php index f3b103e..82f1fb1 100644 --- a/lib/CssCrush/Crush.php +++ b/lib/CssCrush/Crush.php @@ -285,5 +285,9 @@ function log($message, $context = [], $type = 'debug') { Crush::$config->logger->$type($message, $context); } +// Compat with PHP < 7.2. +if (! defined('PREG_UNMATCHED_AS_NULL')) { + define('PREG_UNMATCHED_AS_NULL', null); +} Crush::init(); diff --git a/lib/CssCrush/DeclarationList.php b/lib/CssCrush/DeclarationList.php index c61c649..1dad737 100644 --- a/lib/CssCrush/DeclarationList.php +++ b/lib/CssCrush/DeclarationList.php @@ -368,7 +368,7 @@ public function aliasDeclarations($vendor_context = null) public static function parse($str, $options = []) { $str = Util::stripCommentTokens($str); - $lines = preg_split('~\s*;\s*~', $str, null, PREG_SPLIT_NO_EMPTY); + $lines = preg_split('~\s*;\s*~', $str, -1, PREG_SPLIT_NO_EMPTY); $options += [ 'keyed' => false, diff --git a/lib/CssCrush/Functions.php b/lib/CssCrush/Functions.php index 999937c..5532aa5 100644 --- a/lib/CssCrush/Functions.php +++ b/lib/CssCrush/Functions.php @@ -102,7 +102,9 @@ public function apply($str, \stdClass $context = null) } } - $str = substr_replace($str, $returns, $offset, $closingParen - $offset); + if (! is_null($returns)) { + $str = substr_replace($str, $returns, $offset, $closingParen - $offset); + } } return $str; diff --git a/lib/CssCrush/IO.php b/lib/CssCrush/IO.php index cba1611..9527f35 100644 --- a/lib/CssCrush/IO.php +++ b/lib/CssCrush/IO.php @@ -31,7 +31,10 @@ public function getOutputFilename() { $options = $this->process->options; - $inputBasename = basename($this->process->input->filename, '.css'); + $inputBasename = $this->process->input->filename + ? basename($this->process->input->filename, '.css') + : 'styles'; + $outputBasename = $inputBasename; if (! empty($options->output_file)) { diff --git a/lib/CssCrush/Iterator.php b/lib/CssCrush/Iterator.php index dfba721..38940ac 100644 --- a/lib/CssCrush/Iterator.php +++ b/lib/CssCrush/Iterator.php @@ -18,7 +18,7 @@ public function __construct($items = []) /* IteratorAggregate implementation. */ - public function getIterator() + public function getIterator(): \Traversable { return new \ArrayIterator($this->store); } @@ -26,22 +26,22 @@ public function getIterator() /* ArrayAccess implementation. */ - public function offsetExists($index) + public function offsetExists($index): bool { return array_key_exists($index, $this->store); } - public function offsetGet($index) + public function offsetGet($index): mixed { return isset($this->store[$index]) ? $this->store[$index] : null; } - public function offsetSet($index, $value) + public function offsetSet($index, $value): void { $this->store[$index] = $value; } - public function offsetUnset($index) + public function offsetUnset($index): void { unset($this->store[$index]); } @@ -54,7 +54,7 @@ public function getContents() /* Countable implementation. */ - public function count() + public function count(): int { return count($this->store); } diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 32f0dd8..010ad99 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -107,7 +107,7 @@ public function resolveContext($input_dir = null, $input_file = null) } $this->input->dir = $input_dir ?: $this->docRoot; - $this->input->dirUrl = substr($input_dir, strlen($this->docRoot)); + $this->input->dirUrl = substr($this->input->dir, strlen($this->docRoot)); $this->output->dir = $this->io->getOutputDir(); $this->output->filename = $this->io->getOutputFileName(); @@ -623,7 +623,7 @@ public function captureRules() $traceOffset = $traceMatches[0][$count][1]; - preg_match($rulePatt, $this->string->raw, $ruleMatch, null, $traceOffset); + preg_match($rulePatt, $this->string->raw, $ruleMatch, PREG_UNMATCHED_AS_NULL, $traceOffset); $selector = trim($ruleMatch['selector']); $block = trim($ruleMatch['block_content']); diff --git a/tests/bootstrap.php b/tests/bootstrap.php index d6dfee3..08a9b9b 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -15,7 +15,6 @@ { function bootstrap_process($options = []) { - error_reporting(E_ALL ^ E_DEPRECATED); $process = \CssCrush\Crush::$process = new \CssCrush\Process($options); $process->preCompile(); return $process; diff --git a/tests/unit/CssCrush/UtilTest.php b/tests/unit/CssCrush/UtilTest.php index f3f3cf1..6d700d1 100644 --- a/tests/unit/CssCrush/UtilTest.php +++ b/tests/unit/CssCrush/UtilTest.php @@ -90,7 +90,7 @@ public function testGetLinkBetweenPaths() $path1 = __DIR__; $path2 = realpath(__DIR__ . '/../../'); $this->assertEquals('../../', Util::getLinkBetweenPaths($path1, $path2)); - $this->assertEquals('Unit/CssCrush/', Util::getLinkBetweenPaths($path2, $path1)); + $this->assertEqualsIgnoringCase('Unit/CssCrush/', Util::getLinkBetweenPaths($path2, $path1)); } public function testFilePutContents() From cdb380595d7a40cad5152fc7633904ee3e116d45 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 1 Jan 2022 11:15:46 +0000 Subject: [PATCH 401/421] Update for compatability with PHP 8 Raising compatibility to >= 7 to support PHP 8 without deprecation warnings. --- composer.json | 2 +- lib/CssCrush/Crush.php | 4 ++++ lib/CssCrush/DeclarationList.php | 2 +- lib/CssCrush/Functions.php | 4 +++- lib/CssCrush/IO.php | 5 ++++- lib/CssCrush/Iterator.php | 12 ++++++------ lib/CssCrush/Process.php | 4 ++-- tests/bootstrap.php | 1 - tests/unit/CssCrush/UtilTest.php | 2 +- 9 files changed, 22 insertions(+), 14 deletions(-) diff --git a/composer.json b/composer.json index 5fe4ab6..916451d 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,7 @@ } ], "require": { - "php": ">=5.6.1" + "php": ">=7" }, "require-dev": { "phpunit/phpunit": "8.*", diff --git a/lib/CssCrush/Crush.php b/lib/CssCrush/Crush.php index f3b103e..82f1fb1 100644 --- a/lib/CssCrush/Crush.php +++ b/lib/CssCrush/Crush.php @@ -285,5 +285,9 @@ function log($message, $context = [], $type = 'debug') { Crush::$config->logger->$type($message, $context); } +// Compat with PHP < 7.2. +if (! defined('PREG_UNMATCHED_AS_NULL')) { + define('PREG_UNMATCHED_AS_NULL', null); +} Crush::init(); diff --git a/lib/CssCrush/DeclarationList.php b/lib/CssCrush/DeclarationList.php index c61c649..1dad737 100644 --- a/lib/CssCrush/DeclarationList.php +++ b/lib/CssCrush/DeclarationList.php @@ -368,7 +368,7 @@ public function aliasDeclarations($vendor_context = null) public static function parse($str, $options = []) { $str = Util::stripCommentTokens($str); - $lines = preg_split('~\s*;\s*~', $str, null, PREG_SPLIT_NO_EMPTY); + $lines = preg_split('~\s*;\s*~', $str, -1, PREG_SPLIT_NO_EMPTY); $options += [ 'keyed' => false, diff --git a/lib/CssCrush/Functions.php b/lib/CssCrush/Functions.php index 999937c..5532aa5 100644 --- a/lib/CssCrush/Functions.php +++ b/lib/CssCrush/Functions.php @@ -102,7 +102,9 @@ public function apply($str, \stdClass $context = null) } } - $str = substr_replace($str, $returns, $offset, $closingParen - $offset); + if (! is_null($returns)) { + $str = substr_replace($str, $returns, $offset, $closingParen - $offset); + } } return $str; diff --git a/lib/CssCrush/IO.php b/lib/CssCrush/IO.php index cba1611..9527f35 100644 --- a/lib/CssCrush/IO.php +++ b/lib/CssCrush/IO.php @@ -31,7 +31,10 @@ public function getOutputFilename() { $options = $this->process->options; - $inputBasename = basename($this->process->input->filename, '.css'); + $inputBasename = $this->process->input->filename + ? basename($this->process->input->filename, '.css') + : 'styles'; + $outputBasename = $inputBasename; if (! empty($options->output_file)) { diff --git a/lib/CssCrush/Iterator.php b/lib/CssCrush/Iterator.php index dfba721..38940ac 100644 --- a/lib/CssCrush/Iterator.php +++ b/lib/CssCrush/Iterator.php @@ -18,7 +18,7 @@ public function __construct($items = []) /* IteratorAggregate implementation. */ - public function getIterator() + public function getIterator(): \Traversable { return new \ArrayIterator($this->store); } @@ -26,22 +26,22 @@ public function getIterator() /* ArrayAccess implementation. */ - public function offsetExists($index) + public function offsetExists($index): bool { return array_key_exists($index, $this->store); } - public function offsetGet($index) + public function offsetGet($index): mixed { return isset($this->store[$index]) ? $this->store[$index] : null; } - public function offsetSet($index, $value) + public function offsetSet($index, $value): void { $this->store[$index] = $value; } - public function offsetUnset($index) + public function offsetUnset($index): void { unset($this->store[$index]); } @@ -54,7 +54,7 @@ public function getContents() /* Countable implementation. */ - public function count() + public function count(): int { return count($this->store); } diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 32f0dd8..010ad99 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -107,7 +107,7 @@ public function resolveContext($input_dir = null, $input_file = null) } $this->input->dir = $input_dir ?: $this->docRoot; - $this->input->dirUrl = substr($input_dir, strlen($this->docRoot)); + $this->input->dirUrl = substr($this->input->dir, strlen($this->docRoot)); $this->output->dir = $this->io->getOutputDir(); $this->output->filename = $this->io->getOutputFileName(); @@ -623,7 +623,7 @@ public function captureRules() $traceOffset = $traceMatches[0][$count][1]; - preg_match($rulePatt, $this->string->raw, $ruleMatch, null, $traceOffset); + preg_match($rulePatt, $this->string->raw, $ruleMatch, PREG_UNMATCHED_AS_NULL, $traceOffset); $selector = trim($ruleMatch['selector']); $block = trim($ruleMatch['block_content']); diff --git a/tests/bootstrap.php b/tests/bootstrap.php index d6dfee3..08a9b9b 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -15,7 +15,6 @@ { function bootstrap_process($options = []) { - error_reporting(E_ALL ^ E_DEPRECATED); $process = \CssCrush\Crush::$process = new \CssCrush\Process($options); $process->preCompile(); return $process; diff --git a/tests/unit/CssCrush/UtilTest.php b/tests/unit/CssCrush/UtilTest.php index f3f3cf1..6d700d1 100644 --- a/tests/unit/CssCrush/UtilTest.php +++ b/tests/unit/CssCrush/UtilTest.php @@ -90,7 +90,7 @@ public function testGetLinkBetweenPaths() $path1 = __DIR__; $path2 = realpath(__DIR__ . '/../../'); $this->assertEquals('../../', Util::getLinkBetweenPaths($path1, $path2)); - $this->assertEquals('Unit/CssCrush/', Util::getLinkBetweenPaths($path2, $path1)); + $this->assertEqualsIgnoringCase('Unit/CssCrush/', Util::getLinkBetweenPaths($path2, $path1)); } public function testFilePutContents() From 6080a8a4b31e89b912645c81d321fac7ab3c72f3 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 1 Jan 2022 11:27:39 +0000 Subject: [PATCH 402/421] Update changelog --- CHANGELOG.md | 6 +++++- README.md | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 91d201c..3ee1d67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,13 @@ -## 3.1.0 (2021-12-31) +## 4.0.0 (2022-01-01) +* Raised php requirement to >= 7 * Added support for utf-8 markup in data-uri *.svg files. (@xerc) * Fixed issue with phpini.memory_limit (@GalileoWebagentur) * Updated vendor aliases. + +******************************************************************** + ## 3.0.0 (2019-12-31) * Raised php requirement to >= 5.6 diff --git a/README.md b/README.md index 91f0d16..7daddbc 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +![CI](https://github.com/peteboere/css-crush/actions/workflows/php.yml/badge.svg) + Logo A CSS preprocessor designed to enable a modern and uncluttered CSS workflow. From a5bcc85874c943f81f897bc758e183c6137f2e94 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 1 Jan 2022 11:32:39 +0000 Subject: [PATCH 403/421] Bump to 4.0.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index e630de3..291c80d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "csscrush", - "version": "3.0.1", + "version": "4.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "csscrush", - "version": "3.0.1", + "version": "4.0.0", "license": "MIT", "bin": { "csscrush": "bin/csscrush" diff --git a/package.json b/package.json index 89e21fb..eae7e44 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "csscrush", - "version": "3.0.1", + "version": "4.0.0", "description": "CSS-Crush, CSS preprocessor", "repository": { "type": "git", From 53b1f0b82653c2d6220854732bc489abd336b48b Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Sat, 29 Jan 2022 20:14:34 +0000 Subject: [PATCH 404/421] Add missing PHP minor version requirement. Add file glob option to JS API for multiple entry points. Refactor JS API to use esm. --- .eslintrc.js => .eslintrc.cjs | 3 +- cli.php | 2 +- composer.json | 2 +- index.js | 165 ++++++++++++++++++++----- lib/CssCrush/Importer.php | 2 +- lib/CssCrush/Process.php | 11 +- lib/CssCrush/Url.php | 4 +- package-lock.json | 226 +++++++++++++--------------------- package.json | 9 +- 9 files changed, 244 insertions(+), 180 deletions(-) rename .eslintrc.js => .eslintrc.cjs (98%) diff --git a/.eslintrc.js b/.eslintrc.cjs similarity index 98% rename from .eslintrc.js rename to .eslintrc.cjs index d9ee412..e50d366 100644 --- a/.eslintrc.js +++ b/.eslintrc.cjs @@ -9,7 +9,8 @@ module.exports = { }, extends: 'eslint:recommended', parserOptions: { - ecmaVersion: 9, + ecmaVersion: 'latest', + sourceType: 'module', }, rules: { 'array-bracket-spacing': [2, 'never'], diff --git a/cli.php b/cli.php index ff10483..39186f0 100755 --- a/cli.php +++ b/cli.php @@ -10,7 +10,7 @@ define('STATUS_ERROR', 1); $version = PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION; -$requiredVersion = 5.6; +$requiredVersion = 7.1; if ($version < $requiredVersion) { diff --git a/composer.json b/composer.json index 916451d..246f7b4 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,7 @@ } ], "require": { - "php": ">=7" + "php": ">=7.1" }, "require-dev": { "phpunit/phpunit": "8.*", diff --git a/index.js b/index.js index 925f6c5..5dbe042 100644 --- a/index.js +++ b/index.js @@ -1,21 +1,33 @@ /*eslint no-control-regex: 0*/ +import os from 'os'; +import fs from 'fs'; +import pathUtil from 'path'; +import {fileURLToPath} from 'url'; +import querystring from 'querystring'; +import {EventEmitter} from 'events'; +import {exec} from 'child_process'; +import {createHash} from 'crypto'; +import glob from 'glob'; -const path = require('path'); -const querystring = require('querystring'); -const {EventEmitter} = require('events'); -const cliPath = path.resolve(__dirname, './cli.php'); +const cliPath = pathUtil + .resolve(pathUtil + .dirname(fileURLToPath(import.meta.url)), './cli.php'); const processes = []; const processExec = (...args) => { - processes.push(require('child_process').exec(...args)); - return processes[processes.length-1]; + processes.push(exec(...args)); + return processes.at(-1); }; -process.on('exit', () => { - processes.filter(it => it).forEach(proc => proc.kill()); -}); - -const self = module.exports = {}; +for (const event of ['exit', 'SIGINT', 'SIGUSR1', 'SIGUSR2', 'uncaughtException', 'SIGTERM']) { + process.on(event, () => { + let proc; + while ((proc = processes.pop())) { + proc?.kill(); + } + process.exit(); + }); +} class Process extends EventEmitter { @@ -61,8 +73,8 @@ class Process extends EventEmitter { const eventData = { signal, options: { - input: input ? path.resolve(input) : null, - output: output ? path.resolve(output) : null, + input: input ? pathUtil.resolve(input) : null, + output: output ? pathUtil.resolve(output) : null, }, }; @@ -79,7 +91,7 @@ class Process extends EventEmitter { } assembleCommand(options) { - return `${self.phpBin || 'php'} ${cliPath} ${this.stringifyOptions(options)}`; + return `${process.env.CSSCRUSH_PHP_BIN || 'php'} ${cliPath} ${this.stringifyOptions(options)}`; } stringifyOptions(options) { @@ -96,9 +108,9 @@ class Process extends EventEmitter { let value = options[name]; switch (name) { // Booleans. - case 'watch': // fallthrough - case 'source-map': // fallthrough - case 'boilerplate': // fallthrough + case 'watch': + case 'source-map': + case 'boilerplate': if (value) { args.push(`--${name}`); } @@ -109,8 +121,8 @@ class Process extends EventEmitter { } break; // Array/list values. - case 'vendor-target': // fallthrough - case 'plugins': // fallthrough + case 'vendor-target': + case 'plugins': case 'import-path': if (value) { value = (Array.isArray(value) ? value : [value]).join(','); @@ -118,10 +130,10 @@ class Process extends EventEmitter { } break; // String values. - case 'newlines': // fallthrough - case 'formatter': // fallthrough - case 'input': // fallthrough - case 'context': // fallthrough + case 'newlines': + case 'formatter': + case 'input': + case 'context': case 'output': if (value) { args.push(`--${name}="${value}"`); @@ -137,17 +149,110 @@ class Process extends EventEmitter { } } -self.watch = (file, options={}) => { - options.input = file; - return (new Process()).watch(options); +export default { + watch, + file, + string, }; -self.file = (file, options={}) => { - options.input = file; +export function watch(file, options={}) { + ({file: options.input, context: options.context} = resolveFile(file)); + return (new Process()).watch(options); +} + +export function file(file, options={}) { + ({file: options.input, context: options.context} = resolveFile(file)); return (new Process()).exec(options); -}; +} -self.string = (string, options={}) => { +export function string(string, options={}) { options.stdIn = string; return (new Process()).exec(options); +} + +const resolveFile = input => { + + if (Array.isArray(input)) { + + let initial; + let previous; + + /* + * Generate temporary file containing entrypoints. + * Poll to update on additions and deletions. + */ + const poller = () => { + const result = resolveInputs(input); + + if (result.fingerprint !== previous?.fingerprint) { + fs.writeFileSync(initial?.file || result.file, result.content, { + mode: 0o777, + }); + } + + initial ||= result; + previous = result; + + setTimeout(poller, 2000); + + return result; + }; + + return poller(); + } + + return { + file: input, + }; +}; + +const resolveInputs = fileGlobs => { + + const result = {}; + + let files = new Set(); + for (const it of fileGlobs) { + for (const path of (glob.sync(it) || []).sort()) { + files.add(path); + } + } + + if (! files.size) { + return result; + } + + files = [...files]; + + const rootPath = files + .shift(); + const context = pathUtil + .dirname(rootPath); + const rootFile = pathUtil + .basename(rootPath); + + const content = [rootFile] + .concat(files + .map(it => pathUtil + .relative(context, it))) + .map(it => `@import "http://github.com/$%7Bit%7D";`) + .join('\n'); + + const fingerprint = createHash('md5') + .update(content) + .digest('hex'); + + const outputDir = `${os.tmpdir()}/csscrush`; + if (! fs.existsSync(outputDir)) { + fs.mkdirSync(outputDir, { + mode: 0o777, + }); + } + + return Object + .assign(result, { + context, + content, + fingerprint, + file: `${outputDir}/${fingerprint}.css`, + }); }; diff --git a/lib/CssCrush/Importer.php b/lib/CssCrush/Importer.php index bed7a23..48653be 100644 --- a/lib/CssCrush/Importer.php +++ b/lib/CssCrush/Importer.php @@ -96,7 +96,7 @@ public function collate() $errDesc = 'is not readable'; } if (! empty($process->sources)) { - $errDesc .= " (from within {$process->sources[0]})"; + $errDesc .= " (from within {$process->input->dir})"; } notice("@import 'http://github.com/%7B$import-%3Eurl-%3Evalue%7D' $errDesc"); $str = substr_replace($str, '', $match_start, $match_len); diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 010ad99..26079a3 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -61,16 +61,22 @@ public function __construct($user_options = [], $context = []) $this->minifyOutput = $this->options->__get('minify'); $this->newline = $this->options->__get('newlines'); + $useContextOption = ! empty($this->options->context) + && (php_sapi_name() === 'cli' || $context['type'] === 'filter'); + if ($context['type'] === 'file') { $file = $context['data']; $this->input->raw = $file; if (! ($inputFile = Util::resolveUserPath($file, null, $this->docRoot))) { throw new \Exception('Input file \'' . basename($file) . '\' not found.'); } - $this->resolveContext(dirname($inputFile), $inputFile); + $inputDir = $useContextOption + ? $this->options->context + : dirname($inputFile); + $this->resolveContext($inputDir, $inputFile); } elseif ($context['type'] === 'filter') { - if (! empty($this->options->context)) { + if ($useContextOption) { $this->resolveContext($this->options->context); } else { @@ -108,7 +114,6 @@ public function resolveContext($input_dir = null, $input_file = null) $this->input->dir = $input_dir ?: $this->docRoot; $this->input->dirUrl = substr($this->input->dir, strlen($this->docRoot)); - $this->output->dir = $this->io->getOutputDir(); $this->output->filename = $this->io->getOutputFileName(); $this->output->dirUrl = substr($this->output->dir, strlen($this->docRoot)); diff --git a/lib/CssCrush/Url.php b/lib/CssCrush/Url.php index a0164f1..cbbcd92 100644 --- a/lib/CssCrush/Url.php +++ b/lib/CssCrush/Url.php @@ -121,7 +121,9 @@ public function getAbsolutePath() public function prepend($path_fragment) { if ($this->isRelative) { - $this->value = $path_fragment . $this->value; + $this->value = rtrim($path_fragment, DIRECTORY_SEPARATOR) + . DIRECTORY_SEPARATOR + . ltrim($this->value, DIRECTORY_SEPARATOR); } return $this; diff --git a/package-lock.json b/package-lock.json index 291c80d..57495ee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,11 +8,14 @@ "name": "csscrush", "version": "4.0.0", "license": "MIT", + "dependencies": { + "node-glob": "~1.2.0" + }, "bin": { "csscrush": "bin/csscrush" }, "devDependencies": { - "eslint": "~8.5.0", + "eslint": "~8.8.0", "normalize.css": "7.0.0" } }, @@ -93,15 +96,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -132,6 +126,11 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, + "node_modules/async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -246,18 +245,6 @@ "node": ">=6.0.0" } }, - "node_modules/enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "dependencies": { - "ansi-colors": "^4.1.1" - }, - "engines": { - "node": ">=8.6" - } - }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -271,9 +258,9 @@ } }, "node_modules/eslint": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.5.0.tgz", - "integrity": "sha512-tVGSkgNbOfiHyVte8bCM8OmX+xG9PzVG/B4UCF60zx7j61WIVY/AqJECDgpLD4DbbESD0e174gOg3ZlrX15GDg==", + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.8.0.tgz", + "integrity": "sha512-H3KXAzQGBH1plhYS3okDix2ZthuYJlQQEGE5k0IKuEqUSiyu4AmxxlJ2MtTYeJ3xB4jDhcYCwGOg2TXYdnDXlQ==", "dev": true, "dependencies": { "@eslint/eslintrc": "^1.0.5", @@ -283,12 +270,11 @@ "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", - "enquirer": "^2.3.5", "escape-string-regexp": "^4.0.0", "eslint-scope": "^7.1.0", "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.1.0", - "espree": "^9.2.0", + "eslint-visitor-keys": "^3.2.0", + "espree": "^9.3.0", "esquery": "^1.4.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -296,7 +282,7 @@ "functional-red-black-tree": "^1.0.1", "glob-parent": "^6.0.1", "globals": "^13.6.0", - "ignore": "^4.0.6", + "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", @@ -307,9 +293,7 @@ "minimatch": "^3.0.4", "natural-compare": "^1.4.0", "optionator": "^0.9.1", - "progress": "^2.0.0", "regexpp": "^3.2.0", - "semver": "^7.2.1", "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", "text-table": "^0.2.0", @@ -366,21 +350,30 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.1.0.tgz", - "integrity": "sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.2.0.tgz", + "integrity": "sha512-IOzT0X126zn7ALX0dwFiUQEdsfzrm4+ISsQS8nukaJXwEyYKRSnEIIDULYg1mCtGp7UUXgfGl7BIolXREQK+XQ==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/eslint/node_modules/ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, "node_modules/espree": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.2.0.tgz", - "integrity": "sha512-oP3utRkynpZWF/F2x/HZJ+AGtnIclaR7z1pYPxy7NYM2fSO6LgK/Rkny8anRSPK/VwEA1eqm2squui0T7ZMOBg==", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.0.tgz", + "integrity": "sha512-d/5nCsb0JcqsSEeQzFZ8DH1RmxPcglRWh24EFTlUEmCKoehXGdpsx0RkHDubqUI8LSAIKMQp4r9SzQ3n+sm4HQ==", "dev": true, "dependencies": { - "acorn": "^8.6.0", + "acorn": "^8.7.0", "acorn-jsx": "^5.3.1", "eslint-visitor-keys": "^3.1.0" }, @@ -523,6 +516,11 @@ "node": ">=10.13.0" } }, + "node_modules/glob-to-regexp": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.1.0.tgz", + "integrity": "sha1-4DadQmV4/UVtR9wjsJ3gXB2p6l0=" + }, "node_modules/globals": { "version": "13.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", @@ -667,18 +665,6 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -703,6 +689,15 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "node_modules/node-glob": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/node-glob/-/node-glob-1.2.0.tgz", + "integrity": "sha1-UkD/7e/G1mPOhRXleWpNR6dQwNU=", + "dependencies": { + "async": "^1.3.0", + "glob-to-regexp": "^0.1.0" + } + }, "node_modules/normalize.css": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-7.0.0.tgz", @@ -774,15 +769,6 @@ "node": ">= 0.8.0" } }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -828,21 +814,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -974,12 +945,6 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true } }, "dependencies": { @@ -1042,12 +1007,6 @@ "uri-js": "^4.2.2" } }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, "ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -1069,6 +1028,11 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" + }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1157,15 +1121,6 @@ "esutils": "^2.0.2" } }, - "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "requires": { - "ansi-colors": "^4.1.1" - } - }, "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -1173,9 +1128,9 @@ "dev": true }, "eslint": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.5.0.tgz", - "integrity": "sha512-tVGSkgNbOfiHyVte8bCM8OmX+xG9PzVG/B4UCF60zx7j61WIVY/AqJECDgpLD4DbbESD0e174gOg3ZlrX15GDg==", + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.8.0.tgz", + "integrity": "sha512-H3KXAzQGBH1plhYS3okDix2ZthuYJlQQEGE5k0IKuEqUSiyu4AmxxlJ2MtTYeJ3xB4jDhcYCwGOg2TXYdnDXlQ==", "dev": true, "requires": { "@eslint/eslintrc": "^1.0.5", @@ -1185,12 +1140,11 @@ "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", - "enquirer": "^2.3.5", "escape-string-regexp": "^4.0.0", "eslint-scope": "^7.1.0", "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.1.0", - "espree": "^9.2.0", + "eslint-visitor-keys": "^3.2.0", + "espree": "^9.3.0", "esquery": "^1.4.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -1198,7 +1152,7 @@ "functional-red-black-tree": "^1.0.1", "glob-parent": "^6.0.1", "globals": "^13.6.0", - "ignore": "^4.0.6", + "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", @@ -1209,13 +1163,19 @@ "minimatch": "^3.0.4", "natural-compare": "^1.4.0", "optionator": "^0.9.1", - "progress": "^2.0.0", "regexpp": "^3.2.0", - "semver": "^7.2.1", "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true + } } }, "eslint-scope": { @@ -1246,18 +1206,18 @@ } }, "eslint-visitor-keys": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.1.0.tgz", - "integrity": "sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.2.0.tgz", + "integrity": "sha512-IOzT0X126zn7ALX0dwFiUQEdsfzrm4+ISsQS8nukaJXwEyYKRSnEIIDULYg1mCtGp7UUXgfGl7BIolXREQK+XQ==", "dev": true }, "espree": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.2.0.tgz", - "integrity": "sha512-oP3utRkynpZWF/F2x/HZJ+AGtnIclaR7z1pYPxy7NYM2fSO6LgK/Rkny8anRSPK/VwEA1eqm2squui0T7ZMOBg==", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.0.tgz", + "integrity": "sha512-d/5nCsb0JcqsSEeQzFZ8DH1RmxPcglRWh24EFTlUEmCKoehXGdpsx0RkHDubqUI8LSAIKMQp4r9SzQ3n+sm4HQ==", "dev": true, "requires": { - "acorn": "^8.6.0", + "acorn": "^8.7.0", "acorn-jsx": "^5.3.1", "eslint-visitor-keys": "^3.1.0" } @@ -1370,6 +1330,11 @@ "is-glob": "^4.0.3" } }, + "glob-to-regexp": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.1.0.tgz", + "integrity": "sha1-4DadQmV4/UVtR9wjsJ3gXB2p6l0=" + }, "globals": { "version": "13.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", @@ -1481,15 +1446,6 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -1511,6 +1467,15 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "node-glob": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/node-glob/-/node-glob-1.2.0.tgz", + "integrity": "sha1-UkD/7e/G1mPOhRXleWpNR6dQwNU=", + "requires": { + "async": "^1.3.0", + "glob-to-regexp": "^0.1.0" + } + }, "normalize.css": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-7.0.0.tgz", @@ -1567,12 +1532,6 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -1600,15 +1559,6 @@ "glob": "^7.1.3" } }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -1704,12 +1654,6 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true } } } diff --git a/package.json b/package.json index eae7e44..b1d72e8 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,14 @@ "homepage": "http://the-echoplex.net/csscrush", "license": "MIT", "devDependencies": { - "eslint": "~8.5.0", + "eslint": "~8.8.0", "normalize.css": "7.0.0" + }, + "dependencies": { + "node-glob": "~1.2.0" + }, + "type": "module", + "engines": { + "node": ">=16" } } From 5e01d5859cba40495b7d5a9df1203e6afda88676 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 2 Feb 2022 17:52:58 +0000 Subject: [PATCH 405/421] Bump to 4.1.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 57495ee..dfefd6c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "csscrush", - "version": "4.0.0", + "version": "4.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "csscrush", - "version": "4.0.0", + "version": "4.1.0", "license": "MIT", "dependencies": { "node-glob": "~1.2.0" diff --git a/package.json b/package.json index b1d72e8..a0f90b0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "csscrush", - "version": "4.0.0", + "version": "4.1.0", "description": "CSS-Crush, CSS preprocessor", "repository": { "type": "git", From aa9f5a75207588717f8c7d777e7915cb8ccfa776 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 25 May 2022 12:29:19 +0100 Subject: [PATCH 406/421] Add recovery for deleted tmp watch files. Add package.json entrypoint. --- cli.php | 5 +++++ index.js | 18 +++++++++++------- package.json | 1 + 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/cli.php b/cli.php index 39186f0..d09d127 100755 --- a/cli.php +++ b/cli.php @@ -137,6 +137,11 @@ while (true) { + if (! file_exists($args->input_file)) { + stderr(message(['Input file was not found'], ['type'=>'error'])); + exit(STATUS_ERROR); + } + csscrush_file($args->input_file, $options); $stats = csscrush_stat(); diff --git a/index.js b/index.js index 5dbe042..c12c671 100644 --- a/index.js +++ b/index.js @@ -18,15 +18,16 @@ const processExec = (...args) => { processes.push(exec(...args)); return processes.at(-1); }; +const exit = () => { + let proc; + while ((proc = processes.pop())) { + proc?.kill(); + } + process.exit(); +}; for (const event of ['exit', 'SIGINT', 'SIGUSR1', 'SIGUSR2', 'uncaughtException', 'SIGTERM']) { - process.on(event, () => { - let proc; - while ((proc = processes.pop())) { - proc?.kill(); - } - process.exit(); - }); + process.on(event, exit); } class Process extends EventEmitter { @@ -87,6 +88,9 @@ class Process extends EventEmitter { this.emit('data', {message: detail, ...eventData}); } }); + + proc.on('exit', exit); + return this; } diff --git a/package.json b/package.json index a0f90b0..d98456e 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "name": "csscrush", "version": "4.1.0", "description": "CSS-Crush, CSS preprocessor", + "main": "./index.js", "repository": { "type": "git", "url": "https://github.com/peteboere/css-crush.git" From 61d8af290f0d03241fd35b45b3c60a4e37d0a787 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 25 May 2022 12:29:50 +0100 Subject: [PATCH 407/421] Bump to 4.1.1 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index dfefd6c..e835463 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "csscrush", - "version": "4.1.0", + "version": "4.1.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "csscrush", - "version": "4.1.0", + "version": "4.1.1", "license": "MIT", "dependencies": { "node-glob": "~1.2.0" diff --git a/package.json b/package.json index d98456e..4957fb9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "csscrush", - "version": "4.1.0", + "version": "4.1.1", "description": "CSS-Crush, CSS preprocessor", "main": "./index.js", "repository": { From 6f507bd42444e8b524b58ee56aa19a5aff6cd203 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 25 May 2022 12:46:27 +0100 Subject: [PATCH 408/421] Update deps. --- package-lock.json | 395 +++++++++++++++++++++++----------------------- package.json | 4 +- 2 files changed, 202 insertions(+), 197 deletions(-) diff --git a/package-lock.json b/package-lock.json index e835463..984d7b4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,30 +9,33 @@ "version": "4.1.1", "license": "MIT", "dependencies": { - "node-glob": "~1.2.0" + "glob": "~8.0.3" }, "bin": { "csscrush": "bin/csscrush" }, "devDependencies": { - "eslint": "~8.8.0", + "eslint": "~8.16.0", "normalize.css": "7.0.0" + }, + "engines": { + "node": ">=16" } }, "node_modules/@eslint/eslintrc": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.5.tgz", - "integrity": "sha512-BLxsnmK3KyPunz5wmCCpqy0YelEoxxGmH73Is+Z74oOTMtExcjkr3dDR6quwrjh1YspA8DH9gnX1o069KiS9AQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", + "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.2.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", + "espree": "^9.3.2", + "globals": "^13.15.0", + "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", - "minimatch": "^3.0.4", + "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" }, "engines": { @@ -40,9 +43,9 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.2.tgz", - "integrity": "sha512-UXOuFCGcwciWckOpmfKDq/GyhlTf9pN/BzG//x8p8zTOFEcGuA68ANXheFS0AGvy3qgZqLBUkMs7hqzqCKOVwA==", + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", + "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", "dev": true, "dependencies": { "@humanwhocodes/object-schema": "^1.2.1", @@ -60,9 +63,9 @@ "dev": true }, "node_modules/acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", + "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -126,16 +129,10 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "node_modules/async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" - }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/brace-expansion": { "version": "1.1.11", @@ -211,9 +208,9 @@ } }, "node_modules/debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "dependencies": { "ms": "2.1.2" @@ -258,12 +255,12 @@ } }, "node_modules/eslint": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.8.0.tgz", - "integrity": "sha512-H3KXAzQGBH1plhYS3okDix2ZthuYJlQQEGE5k0IKuEqUSiyu4AmxxlJ2MtTYeJ3xB4jDhcYCwGOg2TXYdnDXlQ==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.16.0.tgz", + "integrity": "sha512-MBndsoXY/PeVTDJeWsYj7kLZ5hQpJOfMYLsF6LicLHQWbRDG19lK5jOix4DPl8yY4SUFcE3txy86OzFLWT+yoA==", "dev": true, "dependencies": { - "@eslint/eslintrc": "^1.0.5", + "@eslint/eslintrc": "^1.3.0", "@humanwhocodes/config-array": "^0.9.2", "ajv": "^6.10.0", "chalk": "^4.0.0", @@ -271,17 +268,17 @@ "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.0", + "eslint-scope": "^7.1.1", "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.2.0", - "espree": "^9.3.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.2", "esquery": "^1.4.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", "glob-parent": "^6.0.1", - "globals": "^13.6.0", + "globals": "^13.15.0", "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", @@ -290,7 +287,7 @@ "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.1", "regexpp": "^3.2.0", @@ -310,9 +307,9 @@ } }, "node_modules/eslint-scope": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.0.tgz", - "integrity": "sha512-aWwkhnS0qAXqNOgKOK0dJ2nvzEbhEvpy8OlJ9kZ0FeZnA6zpjv1/Vei+puGFFX7zkPCkHHXb7IDX3A+7yPrRWg==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", @@ -350,32 +347,23 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.2.0.tgz", - "integrity": "sha512-IOzT0X126zn7ALX0dwFiUQEdsfzrm4+ISsQS8nukaJXwEyYKRSnEIIDULYg1mCtGp7UUXgfGl7BIolXREQK+XQ==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/eslint/node_modules/ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, "node_modules/espree": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.0.tgz", - "integrity": "sha512-d/5nCsb0JcqsSEeQzFZ8DH1RmxPcglRWh24EFTlUEmCKoehXGdpsx0RkHDubqUI8LSAIKMQp4r9SzQ3n+sm4HQ==", + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz", + "integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==", "dev": true, "dependencies": { - "acorn": "^8.7.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^3.1.0" + "acorn": "^8.7.1", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -467,16 +455,15 @@ } }, "node_modules/flatted": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz", - "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", + "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", "dev": true }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "node_modules/functional-red-black-tree": { "version": "1.0.1", @@ -485,20 +472,18 @@ "dev": true }, "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "minimatch": "^5.0.1", + "once": "^1.3.0" }, "engines": { - "node": "*" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -516,15 +501,29 @@ "node": ">=10.13.0" } }, - "node_modules/glob-to-regexp": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.1.0.tgz", - "integrity": "sha1-4DadQmV4/UVtR9wjsJ3gXB2p6l0=" + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } }, "node_modules/globals": { - "version": "13.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", - "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", + "version": "13.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", + "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -546,9 +545,9 @@ } }, "node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", "dev": true, "engines": { "node": ">= 4" @@ -583,7 +582,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -592,8 +590,7 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/is-extglob": { "version": "2.1.1", @@ -666,9 +663,9 @@ "dev": true }, "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "dependencies": { "brace-expansion": "^1.1.7" @@ -689,15 +686,6 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, - "node_modules/node-glob": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/node-glob/-/node-glob-1.2.0.tgz", - "integrity": "sha1-UkD/7e/G1mPOhRXleWpNR6dQwNU=", - "dependencies": { - "async": "^1.3.0", - "glob-to-regexp": "^0.1.0" - } - }, "node_modules/normalize.css": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-7.0.0.tgz", @@ -708,7 +696,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "dependencies": { "wrappy": "1" } @@ -814,6 +801,26 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -911,9 +918,9 @@ } }, "node_modules/v8-compile-cache": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz", - "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", "dev": true }, "node_modules/which": { @@ -943,32 +950,31 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" } }, "dependencies": { "@eslint/eslintrc": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.5.tgz", - "integrity": "sha512-BLxsnmK3KyPunz5wmCCpqy0YelEoxxGmH73Is+Z74oOTMtExcjkr3dDR6quwrjh1YspA8DH9gnX1o069KiS9AQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", + "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==", "dev": true, "requires": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.2.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", + "espree": "^9.3.2", + "globals": "^13.15.0", + "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", - "minimatch": "^3.0.4", + "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "@humanwhocodes/config-array": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.2.tgz", - "integrity": "sha512-UXOuFCGcwciWckOpmfKDq/GyhlTf9pN/BzG//x8p8zTOFEcGuA68ANXheFS0AGvy3qgZqLBUkMs7hqzqCKOVwA==", + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", + "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", "dev": true, "requires": { "@humanwhocodes/object-schema": "^1.2.1", @@ -983,9 +989,9 @@ "dev": true }, "acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", + "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", "dev": true }, "acorn-jsx": { @@ -1028,16 +1034,10 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" - }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "brace-expansion": { "version": "1.1.11", @@ -1098,9 +1098,9 @@ } }, "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "requires": { "ms": "2.1.2" @@ -1128,12 +1128,12 @@ "dev": true }, "eslint": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.8.0.tgz", - "integrity": "sha512-H3KXAzQGBH1plhYS3okDix2ZthuYJlQQEGE5k0IKuEqUSiyu4AmxxlJ2MtTYeJ3xB4jDhcYCwGOg2TXYdnDXlQ==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.16.0.tgz", + "integrity": "sha512-MBndsoXY/PeVTDJeWsYj7kLZ5hQpJOfMYLsF6LicLHQWbRDG19lK5jOix4DPl8yY4SUFcE3txy86OzFLWT+yoA==", "dev": true, "requires": { - "@eslint/eslintrc": "^1.0.5", + "@eslint/eslintrc": "^1.3.0", "@humanwhocodes/config-array": "^0.9.2", "ajv": "^6.10.0", "chalk": "^4.0.0", @@ -1141,17 +1141,17 @@ "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.0", + "eslint-scope": "^7.1.1", "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.2.0", - "espree": "^9.3.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.2", "esquery": "^1.4.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", "glob-parent": "^6.0.1", - "globals": "^13.6.0", + "globals": "^13.15.0", "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", @@ -1160,7 +1160,7 @@ "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.1", "regexpp": "^3.2.0", @@ -1168,20 +1168,12 @@ "strip-json-comments": "^3.1.0", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" - }, - "dependencies": { - "ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", - "dev": true - } } }, "eslint-scope": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.0.tgz", - "integrity": "sha512-aWwkhnS0qAXqNOgKOK0dJ2nvzEbhEvpy8OlJ9kZ0FeZnA6zpjv1/Vei+puGFFX7zkPCkHHXb7IDX3A+7yPrRWg==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", "dev": true, "requires": { "esrecurse": "^4.3.0", @@ -1206,20 +1198,20 @@ } }, "eslint-visitor-keys": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.2.0.tgz", - "integrity": "sha512-IOzT0X126zn7ALX0dwFiUQEdsfzrm4+ISsQS8nukaJXwEyYKRSnEIIDULYg1mCtGp7UUXgfGl7BIolXREQK+XQ==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", "dev": true }, "espree": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.0.tgz", - "integrity": "sha512-d/5nCsb0JcqsSEeQzFZ8DH1RmxPcglRWh24EFTlUEmCKoehXGdpsx0RkHDubqUI8LSAIKMQp4r9SzQ3n+sm4HQ==", + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz", + "integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==", "dev": true, "requires": { - "acorn": "^8.7.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^3.1.0" + "acorn": "^8.7.1", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" } }, "esquery": { @@ -1290,16 +1282,15 @@ } }, "flatted": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz", - "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", + "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", "dev": true }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "functional-red-black-tree": { "version": "1.0.1", @@ -1308,17 +1299,33 @@ "dev": true }, "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "requires": { + "brace-expansion": "^2.0.1" + } + } } }, "glob-parent": { @@ -1330,15 +1337,10 @@ "is-glob": "^4.0.3" } }, - "glob-to-regexp": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.1.0.tgz", - "integrity": "sha1-4DadQmV4/UVtR9wjsJ3gXB2p6l0=" - }, "globals": { - "version": "13.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", - "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", + "version": "13.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", + "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -1351,9 +1353,9 @@ "dev": true }, "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", "dev": true }, "import-fresh": { @@ -1376,7 +1378,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -1385,8 +1386,7 @@ "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "is-extglob": { "version": "2.1.1", @@ -1447,9 +1447,9 @@ "dev": true }, "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -1467,15 +1467,6 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, - "node-glob": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/node-glob/-/node-glob-1.2.0.tgz", - "integrity": "sha1-UkD/7e/G1mPOhRXleWpNR6dQwNU=", - "requires": { - "async": "^1.3.0", - "glob-to-regexp": "^0.1.0" - } - }, "normalize.css": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-7.0.0.tgz", @@ -1486,7 +1477,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "requires": { "wrappy": "1" } @@ -1557,6 +1547,22 @@ "dev": true, "requires": { "glob": "^7.1.3" + }, + "dependencies": { + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } } }, "shebang-command": { @@ -1629,9 +1635,9 @@ } }, "v8-compile-cache": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz", - "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", "dev": true }, "which": { @@ -1652,8 +1658,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" } } } diff --git a/package.json b/package.json index 4957fb9..a2a4c27 100644 --- a/package.json +++ b/package.json @@ -19,11 +19,11 @@ "homepage": "http://the-echoplex.net/csscrush", "license": "MIT", "devDependencies": { - "eslint": "~8.8.0", + "eslint": "~8.16.0", "normalize.css": "7.0.0" }, "dependencies": { - "node-glob": "~1.2.0" + "glob": "~8.0.3" }, "type": "module", "engines": { From 31ed0347a61850e2bb65640277d750faf31011d7 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 25 May 2022 13:04:47 +0100 Subject: [PATCH 409/421] Bump to 4.1.2 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 984d7b4..cfa7954 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "csscrush", - "version": "4.1.1", + "version": "4.1.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "csscrush", - "version": "4.1.1", + "version": "4.1.2", "license": "MIT", "dependencies": { "glob": "~8.0.3" diff --git a/package.json b/package.json index a2a4c27..a30c58a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "csscrush", - "version": "4.1.1", + "version": "4.1.2", "description": "CSS-Crush, CSS preprocessor", "main": "./index.js", "repository": { From 2b326fe75fd69b8217db719db136b856ce4587aa Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 5 Sep 2022 11:03:28 +0100 Subject: [PATCH 410/421] js: Prevent process hang when using glob input with file() --- index.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index c12c671..07afb0b 100644 --- a/index.js +++ b/index.js @@ -160,7 +160,7 @@ export default { }; export function watch(file, options={}) { - ({file: options.input, context: options.context} = resolveFile(file)); + ({file: options.input, context: options.context} = resolveFile(file, {watch: true})); return (new Process()).watch(options); } @@ -174,7 +174,7 @@ export function string(string, options={}) { return (new Process()).exec(options); } -const resolveFile = input => { +const resolveFile = (input, {watch}={}) => { if (Array.isArray(input)) { @@ -197,7 +197,9 @@ const resolveFile = input => { initial ||= result; previous = result; - setTimeout(poller, 2000); + if (watch) { + setTimeout(poller, 2000); + } return result; }; From 19956cbf25263204db5b832b784deebad5236c0e Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Mon, 5 Sep 2022 11:23:32 +0100 Subject: [PATCH 411/421] Bump to 4.1.3 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index cfa7954..c4ff45d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "csscrush", - "version": "4.1.2", + "version": "4.1.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "csscrush", - "version": "4.1.2", + "version": "4.1.3", "license": "MIT", "dependencies": { "glob": "~8.0.3" diff --git a/package.json b/package.json index a30c58a..a94376e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "csscrush", - "version": "4.1.2", + "version": "4.1.3", "description": "CSS-Crush, CSS preprocessor", "main": "./index.js", "repository": { From af07a860ebf3436b957dae6bd3b4cad66c40c6a5 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 8 Aug 2023 16:36:34 +0100 Subject: [PATCH 412/421] Define class fields for deprecation warnings --- composer.json | 4 ++-- lib/CssCrush/BalancedMatch.php | 9 +++++++ lib/CssCrush/Process.php | 35 ++++++++++++++++++++++++++++ lib/CssCrush/StringObject.php | 2 ++ plugins/canvas.php | 9 ++++++- tests/unit/CssCrush/SelectorTest.php | 2 ++ tests/unit/CssCrush/TokensTest.php | 3 ++- tests/unit/CssCrush/UtilTest.php | 3 +++ tests/unit/api/apiTest.php | 7 +++--- 9 files changed, 67 insertions(+), 7 deletions(-) diff --git a/composer.json b/composer.json index 246f7b4..c24ea0a 100644 --- a/composer.json +++ b/composer.json @@ -22,9 +22,9 @@ "php": ">=7.1" }, "require-dev": { - "phpunit/phpunit": "8.*", + "phpunit/phpunit": "8.5.*", "psr/log": "1.0.*@dev", - "twig/twig": "1.*" + "twig/twig": "3.7.*" }, "bin": [ "bin/csscrush" diff --git a/lib/CssCrush/BalancedMatch.php b/lib/CssCrush/BalancedMatch.php index e7ac516..05e455d 100644 --- a/lib/CssCrush/BalancedMatch.php +++ b/lib/CssCrush/BalancedMatch.php @@ -8,6 +8,15 @@ class BalancedMatch { + public $match; + public $string; + + private $length; + private $matchEnd; + private $matchLength; + private $matchStart; + private $offset; + public function __construct(StringObject $string, $offset, $brackets = '{}') { $this->string = $string; diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 26079a3..ecfd395 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -10,6 +10,41 @@ class Process { use EventEmitter; + public $absoluteImports; + public $aliases; + public $cacheData; + public $cacheFile; + public $charset; + public $docRoot; + public $fragments; + public $functions; + public $generateMap; + public $input; + public $io; + public $ioContext; + public $minifyOutput; + public $misc; + public $mixins; + public $newline; + public $options; + public $output; + public $references; + public $ruleFormatter; + public $selectorAliases; + public $selectorAliasesPatt; + public $sourceMap; + public $sources; + public $string; + public $tokens; + public $vars; + + public $debugLog; + public $errors; + public $stat; + public $warnings; + + private $plugins; + public function __construct($user_options = [], $context = []) { $config = Crush::$config; diff --git a/lib/CssCrush/StringObject.php b/lib/CssCrush/StringObject.php index 18b4eec..f647cb8 100644 --- a/lib/CssCrush/StringObject.php +++ b/lib/CssCrush/StringObject.php @@ -8,6 +8,8 @@ class StringObject { + public $raw; + public function __construct($str) { $this->raw = $str; diff --git a/plugins/canvas.php b/plugins/canvas.php index 85cdc10..ba34de0 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -629,7 +629,14 @@ function canvas_requirements() { */ class Canvas { - public $image, $fills = [], $filters = []; + public $currentProperty; + public $fills = []; + public $filters = []; + public $height; + public $image; + public $margin; + public $raw; + public $width; public function __destruct() { diff --git a/tests/unit/CssCrush/SelectorTest.php b/tests/unit/CssCrush/SelectorTest.php index 6009443..24b07cc 100644 --- a/tests/unit/CssCrush/SelectorTest.php +++ b/tests/unit/CssCrush/SelectorTest.php @@ -6,6 +6,8 @@ class SelectorTest extends \PHPUnit\Framework\TestCase { + private $process; + public function setUp(): void { $this->process = bootstrap_process(); diff --git a/tests/unit/CssCrush/TokensTest.php b/tests/unit/CssCrush/TokensTest.php index 812b2cb..2abc3e6 100644 --- a/tests/unit/CssCrush/TokensTest.php +++ b/tests/unit/CssCrush/TokensTest.php @@ -6,7 +6,8 @@ class TokensTest extends \PHPUnit\Framework\TestCase { - protected $tokens; + private $process; + private $tokens; public function setUp(): void { diff --git a/tests/unit/CssCrush/UtilTest.php b/tests/unit/CssCrush/UtilTest.php index 6d700d1..2f72f82 100644 --- a/tests/unit/CssCrush/UtilTest.php +++ b/tests/unit/CssCrush/UtilTest.php @@ -8,6 +8,9 @@ class UtilTest extends \PHPUnit\Framework\TestCase { + private $process; + private $tokens; + public function setUp(): void { $this->process = bootstrap_process(['minify' => false]); diff --git a/tests/unit/api/apiTest.php b/tests/unit/api/apiTest.php index 05f8ac5..56b04b1 100644 --- a/tests/unit/api/apiTest.php +++ b/tests/unit/api/apiTest.php @@ -4,9 +4,10 @@ class ApiTest extends \PHPUnit\Framework\TestCase { - protected $sample; - protected $sampleFile; - protected $sampleExpected; + private $originalWd; + private $sample; + private $sampleExpected; + private $sampleFile; public function setUp(): void { From a967249b437551bfaad63f83251c3bdae71072e1 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 8 Aug 2023 17:31:50 +0100 Subject: [PATCH 413/421] Add phpstan --- composer.json | 3 ++- lib/CssCrush/Fragment.php | 2 +- lib/CssCrush/Functions.php | 2 +- lib/CssCrush/IO.php | 10 +++++----- lib/CssCrush/Logger.php | 1 + lib/CssCrush/Process.php | 2 ++ lib/CssCrush/Template.php | 4 +++- lib/CssCrush/Util.php | 8 +++++--- phpstan.neon | 5 +++++ plugins/canvas.php | 8 ++++---- plugins/forms.php | 2 +- plugins/svg.php | 2 +- 12 files changed, 31 insertions(+), 18 deletions(-) create mode 100644 phpstan.neon diff --git a/composer.json b/composer.json index c24ea0a..b413a5e 100644 --- a/composer.json +++ b/composer.json @@ -24,7 +24,8 @@ "require-dev": { "phpunit/phpunit": "8.5.*", "psr/log": "1.0.*@dev", - "twig/twig": "3.7.*" + "twig/twig": "3.7.*", + "phpstan/phpstan": "^1.10" }, "bin": [ "bin/csscrush" diff --git a/lib/CssCrush/Fragment.php b/lib/CssCrush/Fragment.php index 0f4b6e3..563ee04 100644 --- a/lib/CssCrush/Fragment.php +++ b/lib/CssCrush/Fragment.php @@ -12,7 +12,7 @@ class Fragment extends Template public function __construct($str, $options = []) { - parent::__construct($str, $options); + parent::__construct($str); $this->name = $options['name']; } diff --git a/lib/CssCrush/Functions.php b/lib/CssCrush/Functions.php index 5532aa5..74b938a 100644 --- a/lib/CssCrush/Functions.php +++ b/lib/CssCrush/Functions.php @@ -164,7 +164,7 @@ public static function makePattern($functionNames) $patt = $nonIdents; } - return Regex::make("~$patt\(~iS"); + return Regex::make("~$patt\(~iS"); // @phpstan-ignore-line variable.undefined } } diff --git a/lib/CssCrush/IO.php b/lib/CssCrush/IO.php index 9527f35..9af935b 100644 --- a/lib/CssCrush/IO.php +++ b/lib/CssCrush/IO.php @@ -181,7 +181,7 @@ public function getCacheData() else { debug('Creating cache data file.'); } - Util::filePutContents($process->cacheFile, json_encode([]), __METHOD__); + Util::filePutContents($process->cacheFile, json_encode([])); } return $cache_data; @@ -193,7 +193,7 @@ public function saveCacheData() debug('Saving config.'); - Util::filePutContents($process->cacheFile, json_encode($process->cacheData, JSON_PRETTY_PRINT), __METHOD__); + Util::filePutContents($process->cacheFile, json_encode($process->cacheData, JSON_PRETTY_PRINT)); } public function write(StringObject $string) @@ -208,11 +208,11 @@ public function write(StringObject $string) $string->append($process->newline . "/*# sourceMappingURL=$sourcemapFilename */"); } - if (Util::filePutContents("$dir/$filename", $string, __METHOD__)) { + if (Util::filePutContents("$dir/$filename", $string)) { if ($process->sourceMap) { Util::filePutContents("$dir/$sourcemapFilename", - json_encode($process->sourceMap, JSON_PRETTY_PRINT), __METHOD__); + json_encode($process->sourceMap, JSON_PRETTY_PRINT)); } if ($process->options->stat_dump) { @@ -220,7 +220,7 @@ public function write(StringObject $string) $process->options->stat_dump : "$dir/$filename.json"; $GLOBALS['CSSCRUSH_STAT_FILE'] = $statFile; - Util::filePutContents($statFile, json_encode(csscrush_stat(), JSON_PRETTY_PRINT), __METHOD__); + Util::filePutContents($statFile, json_encode(csscrush_stat(), JSON_PRETTY_PRINT)); } return true; diff --git a/lib/CssCrush/Logger.php b/lib/CssCrush/Logger.php index 8f2228d..28b52d2 100644 --- a/lib/CssCrush/Logger.php +++ b/lib/CssCrush/Logger.php @@ -113,6 +113,7 @@ public function info($message, array $context = []) public function debug($message, array $context = []) { if (! empty($context['label'])) { + $label = $context['label']; $label = PHP_EOL . "$label" . PHP_EOL . str_repeat('=', strlen($label)) . PHP_EOL; } else { diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index ecfd395..2b344ea 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -230,6 +230,8 @@ protected function getBoilerplate() }, ]; + $replacements = []; + foreach (array_keys($boilerplateMatches[0]) as $index) { $tagName = trim($boilerplateMatches[1][$index]); $replacement = '?'; diff --git a/lib/CssCrush/Template.php b/lib/CssCrush/Template.php index 034ed27..653f558 100644 --- a/lib/CssCrush/Template.php +++ b/lib/CssCrush/Template.php @@ -77,7 +77,9 @@ public function __invoke(array $args = null, $str = null) } // Apply substitutions. - $str = isset($find) ? str_replace($find, $replace, $str) : $str; + if (isset($find) && isset($replace)) { + $str = str_replace($find, $replace, $str); + } return Template::tokenize($str); } diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php index f0743e2..92a3351 100644 --- a/lib/CssCrush/Util.php +++ b/lib/CssCrush/Util.php @@ -141,10 +141,10 @@ public static function splitDelimList($str, $options = []) $str = trim($str); - if (! $regex && strpos($str, $delim) === false) { - return ! $allow_empty_strings && ! strlen($str) ? [] : [$str]; + if (! $regex && strpos($str, $delim) === false) { // @phpstan-ignore-line variable.undefined + return ! $allow_empty_strings && ! strlen($str) ? [] : [$str]; // @phpstan-ignore-line variable.undefined } - + $foobar = 1; if ($match_count = preg_match_all(Regex::$patt->parens, $str, $matches)) { $keys = []; foreach ($matches[0] as $index => &$value) { @@ -153,6 +153,7 @@ public static function splitDelimList($str, $options = []) $str = str_replace($matches[0], $keys, $str); } + // @phpstan-ignore-next-line variable.undefined $list = $regex ? preg_split($regex, $str) : explode($delim, $str); if ($match_count) { @@ -163,6 +164,7 @@ public static function splitDelimList($str, $options = []) $list = array_map('trim', $list); + // @phpstan-ignore-next-line variable.undefined return ! $allow_empty_strings ? array_filter($list, 'strlen') : $list; } diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..2a70c1f --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,5 @@ +parameters: + level: 1 + paths: + - lib + - plugins diff --git a/plugins/canvas.php b/plugins/canvas.php index ba34de0..2b08a28 100644 --- a/plugins/canvas.php +++ b/plugins/canvas.php @@ -311,7 +311,7 @@ function canvas_apply_filters($canvas, $src) { // (min) -100 <- 0 -> +100 (max) $level = intval($args[0]) * -1; } - imagefilter($src->image, IMG_FILTER_CONTRAST, $level); + imagefilter($src->image, IMG_FILTER_CONTRAST, $level); // @phpstan-ignore-line variable.undefined break; case 'brightness': @@ -319,7 +319,7 @@ function canvas_apply_filters($canvas, $src) { // -255 <- 0 -> +255 $level = intval($args[0]); } - imagefilter($src->image, IMG_FILTER_BRIGHTNESS, $level); + imagefilter($src->image, IMG_FILTER_BRIGHTNESS, $level); // @phpstan-ignore-line variable.undefined break; } } @@ -491,7 +491,7 @@ function canvas_gradient($canvas, $fill) { $fill->y1, $fill->x1 + $line, $fill->y2, - $color); + $color); // @phpstan-ignore-line variable.undefined break; case 'vertical': @@ -501,7 +501,7 @@ function canvas_gradient($canvas, $fill) { $fill->y1 + $line, $fill->x2, $fill->y1 + $line, - $color); + $color); // @phpstan-ignore-line variable.undefined break; } imagealphablending($image, true); diff --git a/plugins/forms.php b/plugins/forms.php index 8d4321b..466e4e3 100644 --- a/plugins/forms.php +++ b/plugins/forms.php @@ -12,7 +12,7 @@ $type = $handler['type']; $handler = $handler['handler']; } - $process->addSelectorAlias($name, $handler, $type); + $process->addSelectorAlias($name, $handler, $type); // @phpstan-ignore-line variable.undefined } }); diff --git a/plugins/svg.php b/plugins/svg.php index 3eb74f7..9fdbef9 100644 --- a/plugins/svg.php +++ b/plugins/svg.php @@ -222,7 +222,7 @@ function svg_generator($input, $fn_name) { $generated_url = $generated_filename; } - Util::filePutContents($generated_filepath, $flattened_svg, __METHOD__); + Util::filePutContents($generated_filepath, $flattened_svg); $url = new Url($generated_url); $url->noRewrite = true; From 60b697726e20a171c2379b2223391af6d154fcbb Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Wed, 9 Aug 2023 11:18:05 +0100 Subject: [PATCH 414/421] Reorganise JS api and add types --- js/index.d.ts | 77 +++++++++++++++++++++ index.js => js/index.js | 149 +++++++++++++++++++++++++++++----------- js/tests/test.js | 80 +++++++++++++++++++++ jsconfig.json | 21 ++++++ package-lock.json | 35 +++++++++- package.json | 13 ++-- 6 files changed, 331 insertions(+), 44 deletions(-) create mode 100644 js/index.d.ts rename index.js => js/index.js (67%) create mode 100644 js/tests/test.js create mode 100644 jsconfig.json diff --git a/js/index.d.ts b/js/index.d.ts new file mode 100644 index 0000000..4e0383c --- /dev/null +++ b/js/index.d.ts @@ -0,0 +1,77 @@ +/** + * @param {string} file - CSS file path + * @param {CSSCrushOptions} [options] + * @returns {CSSCrushProcess} + */ +export function watch(file: string, options?: CSSCrushOptions): CSSCrushProcess; +/** + * @param {string} file - CSS file path + * @param {CSSCrushOptions} [options] + * @returns {Promise} + */ +export function file(file: string, options?: CSSCrushOptions): Promise; +/** + * @param {string} string - CSS text + * @param {CSSCrushOptions} [options] + * @returns {Promise} + */ +export function string(string: string, options?: CSSCrushOptions): Promise; +declare namespace _default { + export { watch }; + export { file }; + export { string }; +} +export default _default; +export type CSSCrushOptions = { + sourceMap?: boolean; + boilerplate?: boolean; + minify?: boolean; + vendorTarget?: ('all' | 'none' | 'moz' | 'ms' | 'webkit'); + plugins?: string | [string]; + importPath?: string | [string]; + newlines?: ('use-platform' | 'windows' | 'unix'); + formatter?: ('block' | 'single-line' | 'padded'); + input?: string; + context?: string; + output?: string; + vars?: object; +}; +export type CSSCrushProcessOptions = CSSCrushOptions & { + stdIn?: string; + watch?: boolean; +}; +/** + * @typedef {object} CSSCrushOptions + * @property {boolean} [sourceMap] + * @property {boolean} [boilerplate] + * @property {boolean} [minify=true] + * @property {('all' | 'none' | 'moz' | 'ms' | 'webkit')} [vendorTarget='all'] + * @property {string | [string]} [plugins] + * @property {string | [string]} [importPath] + * @property {('use-platform' | 'windows' | 'unix')} [newlines='use-platform'] + * @property {('block' | 'single-line' | 'padded')} [formatter] + * @property {string} [input] + * @property {string} [context] + * @property {string} [output] + * @property {object} [vars] + */ +/** + * @typedef {CSSCrushOptions & { + * stdIn?: string; + * watch?: boolean; + * }} CSSCrushProcessOptions + */ +declare class CSSCrushProcess extends EventEmitter { + /** + * @param {CSSCrushProcessOptions} options + * @returns {Promise} + */ + exec(options: CSSCrushProcessOptions): Promise; + /** + * @param {CSSCrushProcessOptions} options + * @returns {CSSCrushProcess} + */ + watch(options: CSSCrushProcessOptions): CSSCrushProcess; + #private; +} +import { EventEmitter } from 'node:events'; diff --git a/index.js b/js/index.js similarity index 67% rename from index.js rename to js/index.js index 07afb0b..cbb469e 100644 --- a/index.js +++ b/js/index.js @@ -1,40 +1,64 @@ /*eslint no-control-regex: 0*/ -import os from 'os'; -import fs from 'fs'; -import pathUtil from 'path'; -import {fileURLToPath} from 'url'; -import querystring from 'querystring'; -import {EventEmitter} from 'events'; -import {exec} from 'child_process'; -import {createHash} from 'crypto'; +import os from 'node:os'; +import fs from 'node:fs'; +import pathUtil from 'node:path'; +import {fileURLToPath} from 'node:url'; +import querystring from 'node:querystring'; +import {EventEmitter} from 'node:events'; +import {exec} from 'node:child_process'; +import {createHash} from 'node:crypto'; import glob from 'glob'; const cliPath = pathUtil .resolve(pathUtil - .dirname(fileURLToPath(import.meta.url)), './cli.php'); + .dirname(fileURLToPath(import.meta.url)), '../cli.php'); const processes = []; -const processExec = (...args) => { - processes.push(exec(...args)); - return processes.at(-1); -}; -const exit = () => { - let proc; - while ((proc = processes.pop())) { - proc?.kill(); - } - process.exit(); -}; -for (const event of ['exit', 'SIGINT', 'SIGUSR1', 'SIGUSR2', 'uncaughtException', 'SIGTERM']) { +for (const event of [ + 'exit', + 'SIGINT', + 'SIGTERM', + 'SIGUSR1', + 'SIGUSR2', + 'uncaughtException', +]) { process.on(event, exit); } -class Process extends EventEmitter { - +/** + * @typedef {object} CSSCrushOptions + * @property {boolean} [sourceMap] + * @property {boolean} [boilerplate] + * @property {boolean} [minify=true] + * @property {('all' | 'none' | 'moz' | 'ms' | 'webkit')} [vendorTarget='all'] + * @property {string | [string]} [plugins] + * @property {string | [string]} [importPath] + * @property {('use-platform' | 'windows' | 'unix')} [newlines='use-platform'] + * @property {('block' | 'single-line' | 'padded')} [formatter] + * @property {string} [input] + * @property {string} [context] + * @property {string} [output] + * @property {object} [vars] + */ +/** + * @typedef {CSSCrushOptions & { + * stdIn?: string; + * watch?: boolean; + * }} CSSCrushProcessOptions + */ + +class CSSCrushProcess extends EventEmitter { + + #process; + + /** + * @param {CSSCrushProcessOptions} options + * @returns {Promise} + */ exec(options) { return new Promise(resolve => { - let command = this.assembleCommand(options); + let command = this.#assembleCommand(options); const {stdIn} = options; if (stdIn) { command = `echo '${stdIn.replace(/'/g, "\\'")}' | ${command}`; @@ -53,10 +77,14 @@ class Process extends EventEmitter { }); } + /** + * @param {CSSCrushProcessOptions} options + * @returns {CSSCrushProcess} + */ watch(options) { options.watch = true; - const command = this.assembleCommand(options); - const proc = processExec(command); + const command = this.#assembleCommand(options); + this.#process = processExec(command); /* * Emitting 'error' events from EventEmitter without @@ -64,7 +92,7 @@ class Process extends EventEmitter { */ this.on('error', () => {}); - proc.stderr.on('data', msg => { + this.#process.stderr.on('data', msg => { msg = msg.toString(); process.stderr.write(msg); msg = msg.replace(/\x1B\[[^m]*m/g, '').trim(); @@ -89,16 +117,20 @@ class Process extends EventEmitter { } }); - proc.on('exit', exit); + this.#process.on('exit', exit); return this; } - assembleCommand(options) { - return `${process.env.CSSCRUSH_PHP_BIN || 'php'} ${cliPath} ${this.stringifyOptions(options)}`; + kill() { + this.#process?.kill(); + } + + #assembleCommand(options) { + return `${process.env.CSSCRUSH_PHP_BIN || 'php'} ${cliPath} ${this.#stringifyOptions(options)}`; } - stringifyOptions(options) { + #stringifyOptions(options) { const args = []; options = {...options}; for (let name in options) { @@ -118,6 +150,9 @@ class Process extends EventEmitter { if (value) { args.push(`--${name}`); } + else if (value === false) { + args.push(`--${name}=false`); + } break; case 'minify': if (! value) { @@ -159,22 +194,43 @@ export default { string, }; +/** + * @param {string} file - CSS file path + * @param {CSSCrushOptions} [options] + * @returns {CSSCrushProcess} + */ export function watch(file, options={}) { ({file: options.input, context: options.context} = resolveFile(file, {watch: true})); - return (new Process()).watch(options); + return (new CSSCrushProcess()).watch(options); } +/** + * @param {string} file - CSS file path + * @param {CSSCrushOptions} [options] + * @returns {Promise} + */ export function file(file, options={}) { ({file: options.input, context: options.context} = resolveFile(file)); - return (new Process()).exec(options); + return (new CSSCrushProcess()).exec(options); } +/** + * @param {string} string - CSS text + * @param {CSSCrushOptions} [options] + * @returns {Promise} + */ export function string(string, options={}) { - options.stdIn = string; - return (new Process()).exec(options); + + /** @type {CSSCrushProcessOptions} */ (options).stdIn = string; + return (new CSSCrushProcess()).exec(options); } -const resolveFile = (input, {watch}={}) => { +/** + * @param {string} input + * @param {object} [options] + * @param {boolean} [options.watch] + */ +function resolveFile(input, {watch}={}) { if (Array.isArray(input)) { @@ -210,13 +266,15 @@ const resolveFile = (input, {watch}={}) => { return { file: input, }; -}; +} -const resolveInputs = fileGlobs => { +function resolveInputs(fileGlobs) { const result = {}; + /** @type {Set | array} */ let files = new Set(); + for (const it of fileGlobs) { for (const path of (glob.sync(it) || []).sort()) { files.add(path); @@ -261,4 +319,17 @@ const resolveInputs = fileGlobs => { fingerprint, file: `${outputDir}/${fingerprint}.css`, }); -}; +} + +function processExec(command, done) { + processes.push(exec(command, done)); + return processes.at(-1); +} + +function exit() { + let proc; + while ((proc = processes.pop())) { + proc?.kill(); + } + process.exit(); +} diff --git a/js/tests/test.js b/js/tests/test.js new file mode 100644 index 0000000..1b3cf9c --- /dev/null +++ b/js/tests/test.js @@ -0,0 +1,80 @@ +import assert from 'node:assert/strict'; +import {describe, it} from 'node:test'; +import {EventEmitter} from 'node:events'; +import {writeFileSync, readFileSync} from 'node:fs'; +import {tmpdir} from 'node:os'; +import pathUtil from 'node:path'; +import * as csscrush from "../index.js"; + +describe('csscrush.string', () => { + + it('should minify CSS text', async () => { + + const result = await csscrush + .string('foo {color: #ff0000;}'); + + assert.strictEqual(result, 'foo{color:#f00}\n'); + }); +}); + +describe('csscrush.file', () => { + + it('should minify CSS text', async () => { + + const cssText = 'foo {color: #ff0000;}'; + + const testFile = pathUtil + .join(tmpdir(), 'test.css'); + + writeFileSync(testFile, cssText); + + const result = await csscrush + .file(testFile); + + assert(result, 'foo{color:#f00}\n'); + }); +}); + +describe('csscrush.watch', {only: true}, () => { + + it('should minify CSS text', async () => { + + const cssText = 'foo {color: #ff0000;}'; + + const testFile = pathUtil + .join(tmpdir(), 'test.css'); + + const testFileOutput = `${testFile}.result.css`; + + writeFileSync(testFile, cssText); + + const result = await csscrush + .watch(testFile, { + output: testFileOutput, + boilerplate: false, + }); + + assert(result instanceof EventEmitter); + + setTimeout(() => { + writeFileSync(testFile, 'foo {color: #ffffff;}'); + }); + + const event = await new Promise((resolve, reject) => { + result + .on('error', error => { + reject(error); + }) + .on('data', resolve); + }); + + assert.strictEqual(event?.signal, 'WRITE'); + + const contents = readFileSync(testFileOutput) + .toString(); + + assert.strictEqual(contents, 'foo{color:#fff}'); + + result.kill(); + }); +}); diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..a58652f --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,21 @@ +{ + "include": [ + "./js/index.js", + ], + "exclude": [ + "node_modules", + ], + "compilerOptions": { + "allowSyntheticDefaultImports": true, + "baseUrl": ".", + "checkJs": true, + "declaration": true, + "emitDeclarationOnly": true, + "maxNodeModuleJsDepth": 0, + "module": "esnext", + "moduleResolution": "nodenext", + "noEmit": false, + "outDir": "./js", + "target": "esnext", + }, +} diff --git a/package-lock.json b/package-lock.json index c4ff45d..119a1c9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,8 +15,10 @@ "csscrush": "bin/csscrush" }, "devDependencies": { + "@types/node": "~20.4.9", "eslint": "~8.16.0", - "normalize.css": "7.0.0" + "normalize.css": "7.0.0", + "typescript": "~5.1.6" }, "engines": { "node": ">=16" @@ -62,6 +64,12 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "node_modules/@types/node": { + "version": "20.4.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.9.tgz", + "integrity": "sha512-8e2HYcg7ohnTUbHk8focoklEQYvemQmu9M/f43DZVx43kHn0tE3BY/6gSDxS7k0SprtS0NHvj+L80cGLnoOUcQ==", + "dev": true + }, "node_modules/acorn": { "version": "8.7.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", @@ -908,6 +916,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/typescript": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", + "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -988,6 +1009,12 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "@types/node": { + "version": "20.4.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.9.tgz", + "integrity": "sha512-8e2HYcg7ohnTUbHk8focoklEQYvemQmu9M/f43DZVx43kHn0tE3BY/6gSDxS7k0SprtS0NHvj+L80cGLnoOUcQ==", + "dev": true + }, "acorn": { "version": "8.7.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", @@ -1625,6 +1652,12 @@ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true }, + "typescript": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", + "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", + "dev": true + }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", diff --git a/package.json b/package.json index a94376e..57599ba 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,8 @@ "name": "csscrush", "version": "4.1.3", "description": "CSS-Crush, CSS preprocessor", - "main": "./index.js", + "main": "./js/index.js", + "types": "./js/index.d.ts", "repository": { "type": "git", "url": "https://github.com/peteboere/css-crush.git" @@ -14,19 +15,23 @@ "csscrush": "./bin/csscrush" }, "scripts": { - "lint": "eslint --fix index.js" + "lint": "eslint --fix ./js/index.js", + "types": "npx -p typescript tsc -p jsconfig.json", + "test": "node ./js/tests/test.js" }, "homepage": "http://the-echoplex.net/csscrush", "license": "MIT", "devDependencies": { + "@types/node": "~20.4.9", "eslint": "~8.16.0", - "normalize.css": "7.0.0" + "normalize.css": "7.0.0", + "typescript": "~5.1.6" }, "dependencies": { "glob": "~8.0.3" }, "type": "module", "engines": { - "node": ">=16" + "node": ">=18" } } From 463663f0758744a36ee953ce54729f81d5715d3e Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 10 Aug 2023 13:04:59 +0100 Subject: [PATCH 415/421] Expand workflow. Handle false option for boilerplate. --- .github/workflows/php.yml | 53 +++++++++++++++++++++++++-------------- cli.php | 10 +++++--- composer.json | 3 ++- 3 files changed, 43 insertions(+), 23 deletions(-) diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index edd94f4..5170997 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -3,27 +3,42 @@ name: CI on: [push] jobs: - build: + php-api: runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - - name: Validate composer.json and composer.lock - run: composer validate --strict - - - name: Cache Composer packages - id: composer-cache - uses: actions/cache@v2 - with: - path: vendor - key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} - restore-keys: | - ${{ runner.os }}-php- + strategy: + matrix: + php-versions: ['7.1', '8.2'] - - name: Install dependencies - run: composer install --prefer-dist --no-progress + steps: + - uses: actions/checkout@v2 + - uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-versions }} + tools: composer:v2 + - name: Validate composer.json and composer.lock + run: composer validate --strict + - name: Install dependencies + run: composer install --prefer-dist --no-progress + - name: Lint + run: composer run-script lint + - name: Test + run: composer run-script test + + js-api: + runs-on: ubuntu-latest - - name: Run test suite - run: composer run-script test + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v3 + with: + node-version: 18 + - name: Install dependencies + run: npm install + - name: Lint + run: npm run lint + - name: Check JS + run: npm run types + - name: Test + run: npm run test diff --git a/cli.php b/cli.php index d09d127..9662079 100755 --- a/cli.php +++ b/cli.php @@ -93,7 +93,7 @@ foreach (['boilerplate', 'formatter', 'newlines', 'stat_dump', 'source_map', 'import_path'] as $option) { - if ($args->$option) { + if ($args->$option || $args->$option === false) { $options[$option] = $args->$option; } } @@ -317,7 +317,7 @@ function pick(array &$arr) { foreach ($args as $key) { if (isset($arr[$key])) { - // Optional values return false but we want true is argument is present. + // Optional values return false but we want true if argument is present. return is_bool($arr[$key]) ? true : $arr[$key]; } } @@ -537,8 +537,12 @@ function parse_args() { else { $args->context = $args->input_file ? dirname($args->input_file) : getcwd(); } + if (is_string($args->boilerplate)) { - if (! ($args->boilerplate = realpath($args->boilerplate))) { + if ($args->boilerplate === 'false') { + $args->boilerplate = false; + } + else if (! ($args->boilerplate = realpath($args->boilerplate))) { throw new Exception('Boilerplate file does not exist.', STATUS_ERROR); } } diff --git a/composer.json b/composer.json index b413a5e..8a26477 100644 --- a/composer.json +++ b/composer.json @@ -6,7 +6,8 @@ "homepage": "http://the-echoplex.net/csscrush", "license": "MIT", "scripts": { - "test": "vendor/bin/phpunit tests" + "test": "vendor/bin/phpunit tests", + "lint": "vendor/bin/phpstan" }, "authors": [ { From 706a8a2e03670d3fc910c168900fbf00b689dc2f Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 10 Aug 2023 13:04:59 +0100 Subject: [PATCH 416/421] Expand workflow. Handle false option for boilerplate. --- .github/workflows/php.yml | 53 +++++++++++++++++++++++++-------------- cli.php | 10 +++++--- composer.json | 3 ++- js/tests/test.js | 2 +- 4 files changed, 44 insertions(+), 24 deletions(-) diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index edd94f4..3162173 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -3,27 +3,42 @@ name: CI on: [push] jobs: - build: + php-api: runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - - name: Validate composer.json and composer.lock - run: composer validate --strict - - - name: Cache Composer packages - id: composer-cache - uses: actions/cache@v2 - with: - path: vendor - key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} - restore-keys: | - ${{ runner.os }}-php- + strategy: + matrix: + php-versions: ['8.2'] - - name: Install dependencies - run: composer install --prefer-dist --no-progress + steps: + - uses: actions/checkout@v2 + - uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-versions }} + tools: composer:v2 + - name: Validate composer.json and composer.lock + run: composer validate --strict + - name: Install dependencies + run: composer install --prefer-dist --no-progress + - name: Lint + run: composer run-script lint + - name: Test + run: composer run-script test + + js-api: + runs-on: ubuntu-latest - - name: Run test suite - run: composer run-script test + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v3 + with: + node-version: 18 + - name: Install dependencies + run: npm install + - name: Lint + run: npm run lint + - name: Check JS + run: npm run types + - name: Test + run: npm run test diff --git a/cli.php b/cli.php index d09d127..9662079 100755 --- a/cli.php +++ b/cli.php @@ -93,7 +93,7 @@ foreach (['boilerplate', 'formatter', 'newlines', 'stat_dump', 'source_map', 'import_path'] as $option) { - if ($args->$option) { + if ($args->$option || $args->$option === false) { $options[$option] = $args->$option; } } @@ -317,7 +317,7 @@ function pick(array &$arr) { foreach ($args as $key) { if (isset($arr[$key])) { - // Optional values return false but we want true is argument is present. + // Optional values return false but we want true if argument is present. return is_bool($arr[$key]) ? true : $arr[$key]; } } @@ -537,8 +537,12 @@ function parse_args() { else { $args->context = $args->input_file ? dirname($args->input_file) : getcwd(); } + if (is_string($args->boilerplate)) { - if (! ($args->boilerplate = realpath($args->boilerplate))) { + if ($args->boilerplate === 'false') { + $args->boilerplate = false; + } + else if (! ($args->boilerplate = realpath($args->boilerplate))) { throw new Exception('Boilerplate file does not exist.', STATUS_ERROR); } } diff --git a/composer.json b/composer.json index b413a5e..8a26477 100644 --- a/composer.json +++ b/composer.json @@ -6,7 +6,8 @@ "homepage": "http://the-echoplex.net/csscrush", "license": "MIT", "scripts": { - "test": "vendor/bin/phpunit tests" + "test": "vendor/bin/phpunit tests", + "lint": "vendor/bin/phpstan" }, "authors": [ { diff --git a/js/tests/test.js b/js/tests/test.js index 1b3cf9c..93aff96 100644 --- a/js/tests/test.js +++ b/js/tests/test.js @@ -35,7 +35,7 @@ describe('csscrush.file', () => { }); }); -describe('csscrush.watch', {only: true}, () => { +describe('csscrush.watch', () => { it('should minify CSS text', async () => { From 5dabb8a9cd1cf4295fad68062a70ed7a45e4cf69 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 10 Aug 2023 15:32:35 +0100 Subject: [PATCH 417/421] Delete aliases with < 0.5% browser usage --- .github/workflows/php.yml | 2 +- aliases.ini | 106 ++------------------------------------ js/index.d.ts | 1 + jsconfig.json | 2 +- lib/CssCrush/Util.php | 2 +- 5 files changed, 7 insertions(+), 106 deletions(-) diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 3162173..d07acac 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -9,7 +9,7 @@ jobs: strategy: matrix: - php-versions: ['8.2'] + php-versions: ['7.4', '8.2'] steps: - uses: actions/checkout@v2 diff --git a/aliases.ini b/aliases.ini index 5ebae6f..cf7761f 100644 --- a/aliases.ini +++ b/aliases.ini @@ -1,13 +1,10 @@ ;---------------------------------------------------------------- ; -; Add or delete aliases to suit your needs. -; ; Sources: ; http://developer.mozilla.org/en-US/docs/CSS/CSS_Reference ; http://caniuse.com/#cats=CSS ; ;---------------------------------------------------------------- - ; Property aliases. [properties] @@ -23,6 +20,9 @@ animation-play-state[] = -webkit-animation-play-state animation-timing-function[] = -webkit-animation-timing-function + ; Backdrop filter. + backdrop-filter[] = -webkit-backdrop-filter + ; Backface visibility. backface-visibility[] = -webkit-backface-visibility @@ -32,92 +32,19 @@ ; Box decoration. box-decoration-break[] = -webkit-box-decoration-break - ; Box shadow. - box-shadow[] = -webkit-box-shadow - - ; Box sizing. - box-sizing[] = -webkit-box-sizing - box-sizing[] = -moz-box-sizing - - ; Columns. - columns[] = -webkit-columns - column-count[] = -webkit-column-count - column-fill[] = -webkit-column-fill - column-gap[] = -webkit-column-gap - column-rule[] = -webkit-column-rule - column-rule-style[] = -webkit-column-rule-style - column-rule-width[] = -webkit-column-rule-width - column-rule-style[] = -webkit-column-rule-style - column-rule-color[] = -webkit-column-rule-color - column-span[] = -webkit-column-span - column-width[] = -webkit-column-width - ; Filter. filter[] = -webkit-filter - ; Flexbox (2012). - ; - ; Merges two similar versions of the flexbox spec: - ; - September 2012 (for non IE): http://www.w3.org/TR/2012/CR-css3-flexbox-20120918 - ; - March 2012 (for IE10): http://www.w3.org/TR/2012/WD-css3-flexbox-20120322 - ; - ; The early 2012 spec mostly differs only in syntax to the later one, with the notable - ; exception of not supporting seperate properties for , - ; and . These properties are available in both 2012 implementations via - ; shorthand. - ; - ; Support for the early 2012 syntax implemented in IE10 is achieved here in part with - ; property aliases, and in part with declaration aliases later in this file. - ; - align-content[] = -webkit-align-content - align-items[] = -webkit-align-items - align-self[] = -webkit-align-self - flex[] = -webkit-flex - flex-basis[] = -webkit-flex-basis - flex-direction[] = -webkit-flex-direction - flex-flow[] = -webkit-flex-flow - flex-grow[] = -webkit-flex-grow - flex-shrink[] = -webkit-flex-shrink - flex-wrap[] = -webkit-flex-wrap - justify-content[] = -webkit-justify-content - order[] = -webkit-order - ; Hyphens. hyphens[] = -webkit-hyphens - hyphens[] = -ms-hyphens - - ; Perspective. - perspective[] = -webkit-perspective - perspective-origin[] = -webkit-perspective-origin ; Tab size. tab-size[] = -moz-tab-size tab-size[] = -o-tab-size - ; Text decoration. - text-decoration-color[] = -webkit-text-decoration-color - text-decoration-line[] = -webkit-text-decoration-line - text-decoration-style[] = -webkit-text-decoration-style - - ; Transforms. - transform[] = -webkit-transform - transform[] = -ms-transform - transform-origin[] = -webkit-transform-origin - transform-origin[] = -ms-transform-origin - transform-style[] = -webkit-transform-style - transform-style[] = -ms-transform-style - - ; Transitions. - transition[] = -webkit-transition - transition-delay[] = -webkit-transition-delay - transition-duration[] = -webkit-transition-duration - transition-property[] = -webkit-transition-property - transition-timing-function[] = -webkit-transition-timing-function - ; User select (non standard). user-select[] = -webkit-user-select user-select[] = -moz-user-select - user-select[] = -ms-user-select ;---------------------------------------------------------------- @@ -125,16 +52,6 @@ [declarations] - ; Flexbox (2012). - display:flex[] = display:-webkit-flex - display:inline-flex[] = display:-webkit-inline-flex - - ; Cursor values (non-standard). - cursor:zoom-in[] = cursor:-webkit-zoom-in - cursor:zoom-out[] = cursor:-webkit-zoom-out - cursor:grab[] = cursor:-webkit-grab - cursor:grabbing[] = cursor:-webkit-grabbing - ; Experimental width values. width:max-content[] = width:intrinsic width:max-content[] = width:-webkit-max-content @@ -178,25 +95,8 @@ [functions] - ; Calc. - calc[] = -webkit-calc - - -[functions.gradients] - - ; Gradients. - linear-gradient[] = -webkit-linear-gradient - radial-gradient[] = -webkit-radial-gradient - - ; Repeating gradients. - repeating-linear-gradient[] = -webkit-repeating-linear-gradient - repeating-radial-gradient[] = -webkit-repeating-radial-gradient - ;---------------------------------------------------------------- ; @rule aliases. [at-rules] - - ; Keyframes. - keyframes[] = -webkit-keyframes diff --git a/js/index.d.ts b/js/index.d.ts index 4e0383c..8e7e7f1 100644 --- a/js/index.d.ts +++ b/js/index.d.ts @@ -72,6 +72,7 @@ declare class CSSCrushProcess extends EventEmitter { * @returns {CSSCrushProcess} */ watch(options: CSSCrushProcessOptions): CSSCrushProcess; + kill(): void; #private; } import { EventEmitter } from 'node:events'; diff --git a/jsconfig.json b/jsconfig.json index a58652f..5d104c7 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -1,6 +1,6 @@ { "include": [ - "./js/index.js", + "./js/index.js" ], "exclude": [ "node_modules", diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php index 92a3351..c69a69b 100644 --- a/lib/CssCrush/Util.php +++ b/lib/CssCrush/Util.php @@ -144,7 +144,7 @@ public static function splitDelimList($str, $options = []) if (! $regex && strpos($str, $delim) === false) { // @phpstan-ignore-line variable.undefined return ! $allow_empty_strings && ! strlen($str) ? [] : [$str]; // @phpstan-ignore-line variable.undefined } - $foobar = 1; + if ($match_count = preg_match_all(Regex::$patt->parens, $str, $matches)) { $keys = []; foreach ($matches[0] as $index => &$value) { From 2dc9551e2a8f66982aef55765672c2c8c68a9330 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Thu, 10 Aug 2023 16:10:51 +0100 Subject: [PATCH 418/421] Bump to 4.2.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 119a1c9..0fa5fc5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "csscrush", - "version": "4.1.3", + "version": "4.2.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "csscrush", - "version": "4.1.3", + "version": "4.2.0", "license": "MIT", "dependencies": { "glob": "~8.0.3" diff --git a/package.json b/package.json index 57599ba..a804517 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "csscrush", - "version": "4.1.3", + "version": "4.2.0", "description": "CSS-Crush, CSS preprocessor", "main": "./js/index.js", "types": "./js/index.d.ts", From 88e158b923778d8f64a63cb6341d183647055b22 Mon Sep 17 00:00:00 2001 From: gtbu <30436307+gtbu@users.noreply.github.com> Date: Fri, 9 May 2025 22:00:05 +0200 Subject: [PATCH 419/421] Deprecations for php8.4 (#106) * Undefined array keys php8 * PHP 8.2 Dynamic Properties are deprecated * Update Crush.php Implicitly marking parameter $callback as nullable is deprecated, the explicit nullable type must be used instead * Update Util.php Implicitly marking parameter as nullable is deprecated, the explicit nullable type must be used instead : ? * Update Options.php Implicitly marking parameter as nullable is deprecated, the explicit nullable type must be used instead * Update Functions.php Implicitly marking parameter as nullable is deprecated, the explicit nullable type must be used instead * Update Tokens.php Implicitly marking parameter as nullable is deprecated, the explicit nullable type must be used instead * Update StringObject.php Deprecated: Creation of dynamic property CssCrush\StringObject::$raw is deprecated --- lib/CssCrush/Crush.php | 2 +- lib/CssCrush/Functions.php | 2 +- lib/CssCrush/Options.php | 4 ++-- lib/CssCrush/Process.php | 10 +++++----- lib/CssCrush/StringObject.php | 2 +- lib/CssCrush/Tokens.php | 2 +- lib/CssCrush/Util.php | 4 ++-- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/lib/CssCrush/Crush.php b/lib/CssCrush/Crush.php index 82f1fb1..230d607 100644 --- a/lib/CssCrush/Crush.php +++ b/lib/CssCrush/Crush.php @@ -97,7 +97,7 @@ public static function loadAssets() } } - public static function plugin($name = null, callable $callback = null) + public static function plugin($name = null, callable|null $callback = null) { static $plugins = []; diff --git a/lib/CssCrush/Functions.php b/lib/CssCrush/Functions.php index 74b938a..51909f9 100644 --- a/lib/CssCrush/Functions.php +++ b/lib/CssCrush/Functions.php @@ -53,7 +53,7 @@ public function setPattern($useAll = false) $this->pattern = Functions::makePattern(array_keys($this->register)); } - public function apply($str, \stdClass $context = null) + public function apply($str, ?\stdClass $context = null) { if (strpos($str, '(') === false) { return $str; diff --git a/lib/CssCrush/Options.php b/lib/CssCrush/Options.php index 0081e10..54b87ce 100644 --- a/lib/CssCrush/Options.php +++ b/lib/CssCrush/Options.php @@ -33,7 +33,7 @@ class Options 'newlines' => 'use-platform', ]; - public function __construct(array $options = [], Options $defaults = null) + public function __construct(array $options = [], ?Options $defaults = null) { $options = array_change_key_case($options, CASE_LOWER); @@ -169,7 +169,7 @@ public function get($computed = false) return $computed ? $this->computedOptions : self::filter($this->inputOptions); } - public static function filter(array $optionsArray = null) + public static function filter(?array $optionsArray = null) { return $optionsArray ? array_intersect_key($optionsArray, self::$standardOptions) : self::$standardOptions; } diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 2b344ea..85d6968 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -5,7 +5,7 @@ * */ namespace CssCrush; - +#[\AllowDynamicProperties] class Process { use EventEmitter; @@ -667,8 +667,8 @@ public function captureRules() preg_match($rulePatt, $this->string->raw, $ruleMatch, PREG_UNMATCHED_AS_NULL, $traceOffset); - $selector = trim($ruleMatch['selector']); - $block = trim($ruleMatch['block_content']); + $selector = trim($ruleMatch['selector'] ?? null); + $block = trim($ruleMatch['block_content'] ?? null); $replace = ''; // If rules are nested inside we set their parent property. @@ -688,12 +688,12 @@ public function captureRules() } } else { - $rule = new Rule($selector, $block, $ruleMatch['trace_token']); + $rule = new Rule($selector, $block, $ruleMatch['trace_token'] ?? null); } $replace = $tokens->add($rule, 'r', $rule->label) . $replace; - $this->string->splice($replace, $traceOffset, strlen($ruleMatch[0])); + $this->string->splice($replace, $traceOffset, strlen($ruleMatch[0]) ?? null); } // Flip, since we just captured rules in reverse order. diff --git a/lib/CssCrush/StringObject.php b/lib/CssCrush/StringObject.php index f647cb8..999afc3 100644 --- a/lib/CssCrush/StringObject.php +++ b/lib/CssCrush/StringObject.php @@ -8,7 +8,7 @@ class StringObject { - public $raw; + public string|null $raw = null; public function __construct($str) { diff --git a/lib/CssCrush/Tokens.php b/lib/CssCrush/Tokens.php index 7726955..f40510f 100644 --- a/lib/CssCrush/Tokens.php +++ b/lib/CssCrush/Tokens.php @@ -11,7 +11,7 @@ class Tokens public $store; protected $ids; - public function __construct(array $types = null) + public function __construct(?array $types = null) { $types = $types ?: [ 's', // strings. diff --git a/lib/CssCrush/Util.php b/lib/CssCrush/Util.php index c69a69b..1e3aadf 100644 --- a/lib/CssCrush/Util.php +++ b/lib/CssCrush/Util.php @@ -8,7 +8,7 @@ class Util { - public static function htmlAttributes(array $attributes, array $sort_order = null) + public static function htmlAttributes(array $attributes, ?array $sort_order = null) { // Optionally sort attributes (for better readability). if ($sort_order) { @@ -75,7 +75,7 @@ public static function simplifyPath($path) return $path; } - public static function resolveUserPath($path, callable $recovery = null, $docRoot = null) + public static function resolveUserPath($path, ?callable $recovery = null, $docRoot = null) { // System path. if ($realpath = realpath($path)) { From 7d055b59071d1908cd1bb4d190f2c480508c8047 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Fri, 9 May 2025 21:38:11 +0100 Subject: [PATCH 420/421] Raise PHP version requirement to 8.1 (#107) * Update deps. * Consistent null handling. * Add more versions to test matrix. --- .github/workflows/php.yml | 7 +++++-- composer.json | 8 ++++---- lib/CssCrush/Crush.php | 2 +- lib/CssCrush/Fragment.php | 2 +- lib/CssCrush/Process.php | 8 ++++---- lib/CssCrush/StringObject.php | 2 +- lib/CssCrush/Template.php | 2 +- tests/unit/CssCrush/OptionsTest.php | 2 +- tests/unit/CssCrush/TokensTest.php | 2 +- tests/unit/CssCrush/VersionTest.php | 2 +- 10 files changed, 20 insertions(+), 17 deletions(-) diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index d07acac..5e896c1 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -1,6 +1,9 @@ name: CI -on: [push] +on: + push: + pull_request: + workflow_dispatch: jobs: @@ -9,7 +12,7 @@ jobs: strategy: matrix: - php-versions: ['7.4', '8.2'] + php-versions: ['8.1', '8.4'] steps: - uses: actions/checkout@v2 diff --git a/composer.json b/composer.json index 8a26477..a783e68 100644 --- a/composer.json +++ b/composer.json @@ -20,13 +20,13 @@ } ], "require": { - "php": ">=7.1" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "8.5.*", + "phpunit/phpunit": "9.6.23", "psr/log": "1.0.*@dev", - "twig/twig": "3.7.*", - "phpstan/phpstan": "^1.10" + "phpstan/phpstan": "^1.10", + "twig/twig": "3.11.3" }, "bin": [ "bin/csscrush" diff --git a/lib/CssCrush/Crush.php b/lib/CssCrush/Crush.php index 230d607..0c1348e 100644 --- a/lib/CssCrush/Crush.php +++ b/lib/CssCrush/Crush.php @@ -97,7 +97,7 @@ public static function loadAssets() } } - public static function plugin($name = null, callable|null $callback = null) + public static function plugin($name = null, ?callable $callback = null) { static $plugins = []; diff --git a/lib/CssCrush/Fragment.php b/lib/CssCrush/Fragment.php index 563ee04..334a5ba 100644 --- a/lib/CssCrush/Fragment.php +++ b/lib/CssCrush/Fragment.php @@ -16,7 +16,7 @@ public function __construct($str, $options = []) $this->name = $options['name']; } - public function __invoke(array $args = null, $str = null) + public function __invoke(?array $args = null, $str = null) { $str = parent::__invoke($args); diff --git a/lib/CssCrush/Process.php b/lib/CssCrush/Process.php index 85d6968..06d8791 100644 --- a/lib/CssCrush/Process.php +++ b/lib/CssCrush/Process.php @@ -667,8 +667,8 @@ public function captureRules() preg_match($rulePatt, $this->string->raw, $ruleMatch, PREG_UNMATCHED_AS_NULL, $traceOffset); - $selector = trim($ruleMatch['selector'] ?? null); - $block = trim($ruleMatch['block_content'] ?? null); + $selector = trim($ruleMatch['selector'] ?? ''); + $block = trim($ruleMatch['block_content'] ?? ''); $replace = ''; // If rules are nested inside we set their parent property. @@ -688,12 +688,12 @@ public function captureRules() } } else { - $rule = new Rule($selector, $block, $ruleMatch['trace_token'] ?? null); + $rule = new Rule($selector, $block, $ruleMatch['trace_token'] ?? ''); } $replace = $tokens->add($rule, 'r', $rule->label) . $replace; - $this->string->splice($replace, $traceOffset, strlen($ruleMatch[0]) ?? null); + $this->string->splice($replace, $traceOffset, strlen($ruleMatch[0]) ?? ''); } // Flip, since we just captured rules in reverse order. diff --git a/lib/CssCrush/StringObject.php b/lib/CssCrush/StringObject.php index 999afc3..66a6a58 100644 --- a/lib/CssCrush/StringObject.php +++ b/lib/CssCrush/StringObject.php @@ -8,7 +8,7 @@ class StringObject { - public string|null $raw = null; + public ?string $raw = null; public function __construct($str) { diff --git a/lib/CssCrush/Template.php b/lib/CssCrush/Template.php index 653f558..a459c8b 100644 --- a/lib/CssCrush/Template.php +++ b/lib/CssCrush/Template.php @@ -60,7 +60,7 @@ public function __construct($str) $this->string = $templateFunctions->apply($str); } - public function __invoke(array $args = null, $str = null) + public function __invoke(?array $args = null, $str = null) { $str = isset($str) ? $str : $this->string; diff --git a/tests/unit/CssCrush/OptionsTest.php b/tests/unit/CssCrush/OptionsTest.php index c09655b..b140f78 100644 --- a/tests/unit/CssCrush/OptionsTest.php +++ b/tests/unit/CssCrush/OptionsTest.php @@ -83,7 +83,7 @@ public function testSourceMaps() csscrush_file($this->testFile, ['source_map' => true]); $source_map_contents = file_get_contents("$this->testFile.crush.css.map"); - $this->assertRegExp('~"version": ?3,~', $source_map_contents); + $this->assertMatchesRegularExpression('~"version": ?3,~', $source_map_contents); } public function testAdvancedMinify() diff --git a/tests/unit/CssCrush/TokensTest.php b/tests/unit/CssCrush/TokensTest.php index 2abc3e6..72a1ebc 100644 --- a/tests/unit/CssCrush/TokensTest.php +++ b/tests/unit/CssCrush/TokensTest.php @@ -32,7 +32,7 @@ public function test__construct() public function testCreateLabel() { $type = 's'; - $this->assertRegExp("~^\?{$type}[a-z0-9]+\?$~", $this->tokens->createLabel($type)); + $this->assertMatchesRegularExpression("~^\?{$type}[a-z0-9]+\?$~", $this->tokens->createLabel($type)); } public function testAdd() diff --git a/tests/unit/CssCrush/VersionTest.php b/tests/unit/CssCrush/VersionTest.php index 3f8fd31..aec0fae 100644 --- a/tests/unit/CssCrush/VersionTest.php +++ b/tests/unit/CssCrush/VersionTest.php @@ -36,7 +36,7 @@ public function testProperties() public function testGitDescribe() { if ($version = Version::gitDescribe()) { - $this->assertRegExp('~^ + $this->assertMatchesRegularExpression('~^ v \d+\. \d+\. From 7becadc91eb92aeafe51905d55a08edfa24203e1 Mon Sep 17 00:00:00 2001 From: Pete Boere Date: Tue, 9 Sep 2025 12:34:00 +0100 Subject: [PATCH 421/421] 5.0.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0fa5fc5..0645924 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "csscrush", - "version": "4.2.0", + "version": "5.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "csscrush", - "version": "4.2.0", + "version": "5.0.0", "license": "MIT", "dependencies": { "glob": "~8.0.3" diff --git a/package.json b/package.json index a804517..1b24543 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "csscrush", - "version": "4.2.0", + "version": "5.0.0", "description": "CSS-Crush, CSS preprocessor", "main": "./js/index.js", "types": "./js/index.d.ts",