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('~(?<func>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; }
public function __invoke($args) { $handler = $this->handler; $tokens = Crush::$process->tokens; $splat_arg_patt = Regex::make('~#\\((?<fallback>{{ ident }})?\\)~'); switch ($this->type) { case 'alias': return $handler($args); case 'callback': $template = new Template($handler($args)); return $template($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); } }
function svg_capture($process) { $process->string->pregReplaceCallback(Regex::make('~@svg\\s+(?<name>{{ ident }})\\s*{{ block }}~iS'), function ($m) { Crush::$process->misc->svg_defs[strtolower($m['name'])] = new Template($m['block_content']); return ''; }); }
public function expand() { static $grouping_patt, $expand, $expandSelector; if (!$grouping_patt) { $grouping_patt = Regex::make('~\\:any{{ parens }}~iS'); $expand = function ($selector_string) use($grouping_patt) { if (preg_match($grouping_patt, $selector_string, $m, PREG_OFFSET_CAPTURE)) { 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(); // 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; } return false; }; $expandSelector = function ($selector_string) use($expand) { if ($running_stack = $expand($selector_string)) { $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; } } $running_stack = $loop_stack; } while ($loop_stack); return $flattened_stack; } return array($selector_string => true); }; } $expanded_set = array(); 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); $expanded_set[$new->readableValue] = $new; } } else { $expanded_set[$original_selector->readableValue] = $original_selector; } } $this->store = $expanded_set; }
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); } } }
/** * 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); } }
function color_capture($process) { $captured_keywords = $process->string->captureDirectives('color', array('singles' => true)); if ($captured_keywords) { $native_keywords = Color::getKeywords(); $custom_keywords = array(); $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 }}(?<color_keyword>' . implode('|', $custom_keywords) . '){{ RB }}~iS'); } } }
public function captureUrls($str, $add_padding = false) { $count = preg_match_all(Regex::make('~@import \\s+ (?<import>{{s_token}}) | {{LB}} (?<func>url|data-uri) {{parens}}~ixS'), $str, $m, PREG_OFFSET_CAPTURE); while ($count--) { list($full_text, $full_offset) = $m[0][$count]; list($import_text, $import_offset) = $m['import'][$count]; // @import directive. if ($import_offset !== -1) { $label = $this->add(new Url(trim($import_text))); $str = str_replace($import_text, $add_padding ? str_pad($label, strlen($import_text)) : $label, $str); } 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); $str = substr_replace($str, $add_padding ? Tokens::pad($label, $full_text) : $label, $full_offset, strlen($full_text)); } } return $str; }
function ease(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 = Regex::make("~{{ LB }}{$property}{{ RB }}~i"); $find[] = $patt; $replace[] = $value; } } if (!array_intersect_key($rule->declarations->canonicalProperties, $easing_properties)) { return; } foreach ($rule->declarations->filter(array('skip' => false)) as $declaration) { if (isset($easing_properties[$declaration->canonicalProperty])) { $declaration->value = preg_replace($find, $replace, $declaration->value); } } }
public static function call($message, $context = null) { $process = Crush::$process; $mixable = null; $message = trim($message); // Test for mixin or abstract rule. e.g: // named-mixin( 50px, rgba(0,0,0,0), left 100% ) // abstract-rule if (preg_match(Regex::make('~^(?<name>{{ident}}) {{parens}}?~xS'), $message, $message_match)) { $name = $message_match['name']; if (isset($process->mixins[$name])) { $mixable = $process->mixins[$name]; } elseif (isset($process->references[$name])) { $mixable = $process->references[$name]; } } // If no mixin or abstract rule matched, look for matching selector if (!$mixable) { $selector_test = Selector::makeReadable($message); if (isset($process->references[$selector_test])) { $mixable = $process->references[$selector_test]; } } // Avoid infinite recursion. if (!$mixable || $mixable === $context) { return false; } elseif ($mixable instanceof Mixin) { $args = array(); $raw_args = isset($message_match['parens_content']) ? trim($message_match['parens_content']) : null; if ($raw_args) { $args = Util::splitDelimList($raw_args); } return DeclarationList::parse($mixable->template->__invoke($args), array('flatten' => true, 'context' => $mixable)); } elseif ($mixable instanceof Rule) { return $mixable->declarations->store; } }
public static function colorSplit($str) { if ($test = 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); } // Strip all whitespace. $color = preg_replace('~\\s+~', '', $color); // Extract alpha component if one is matched. $opacity = 1; if (preg_match(Regex::make('~^(rgb|hsl)a\\(({{number}}%?,{{number}}%?,{{number}}%?),({{number}})\\)$~i'), $color, $m)) { $opacity = floatval($m[3]); $color = "{$m['1']}({$m['2']})"; } // Return color value and alpha component seperated. return array($color, $opacity); }
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); } } if ($idents) { $idents = '{{ LB }}-?(?<function>' . implode('|', $idents) . ')'; } if ($nonIdents) { $nonIdents = '(?<simple_function>' . implode('|', $nonIdents) . ')'; } if ($idents && $nonIdents) { $patt = "(?:{$idents}|{$nonIdents})"; } elseif ($idents) { $patt = $idents; } elseif ($nonIdents) { $patt = $nonIdents; } return Regex::make("~{$patt}\\(~iS"); }
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 = array('font' => true, 'font-size' => true, 'line-height' => true); $modes = array('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 the default base em-size, to my knowledge always 16px. $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; $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)) { $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. // 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); } }
protected function minifyColors() { static $keywords_patt, $functions_patt; $minified_keywords = Color::getMinifyableKeywords(); if (!$keywords_patt) { $keywords_patt = '~(?<![\\w-\\.#])(' . implode('|', array_keys($minified_keywords)) . ')(?![\\w-\\.#\\]])~iS'; $functions_patt = Regex::make('~{{ LB }}(rgb|hsl)\\(([^\\)]{5,})\\)~iS'); } $this->string->pregReplaceCallback($keywords_patt, function ($m) use($minified_keywords) { return $minified_keywords[strtolower($m[0])]; }); $this->string->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); }); }
protected function addMarkers(&$str) { $process = $this->process; $currentFileIndex = count($process->sources) - 1; static $patt; if (!$patt) { $patt = Regex::make('~ (?:^|(?<=[;{}])) (?<before> (?: \\s | {{c_token}} )* ) (?<selector> (?: # Some @-rules are treated like standard rule blocks. @(?: (?i)page|abstract|font-face(?-i) ) {{RB}} [^{]* | [^@;{}]+ ) ) \\{ ~xS'); } $count = preg_match_all($patt, $str, $matches, PREG_OFFSET_CAPTURE); while ($count--) { $selectorOffset = $matches['selector'][$count][1]; $line = 0; $before = substr($str, 0, $selectorOffset); if ($selectorOffset) { $line = substr_count($before, "\n"); } $pointData = array($currentFileIndex, $line); // Source maps require column index too. if ($process->generateMap) { $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(',', $pointData), 't'), $selectorOffset, 0); } }
public function aliasFunctions($vendor_context = null) { $function_aliases =& Crush::$process->aliases['functions']; $function_alias_groups =& Crush::$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); } } 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(Regex::make("~{{ LB }}{$fn_name}(?=\\()~iS"), $fn_alias, $copy->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 captureDirectives($directive, $parse_options = array()) { if (is_array($directive)) { $directive = '(?:' . implode('|', $directive) . ')'; } $parse_options += array('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+(?<name>{{ ident }})\\s+(?<value>[^;]+)\\s*;)~S'); } else { $patt = Regex::make('~@(?i)' . $directive . '(?-i)\\s*{{ block }}~S'); } $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; }
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("~(?<function>{$flatList})\\(~iS"); }
public static function init() { self::$patt = $patt = new \stdClass(); self::$classes = $classes = new \stdClass(); // 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->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->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 = '(?<![\\w-])'; // Left ident boundry. $classes->RB = '(?![\\w-])'; // Right ident boundry. // Recursive block matching. $classes->block = '(?<block>\\{\\s*(?<block_content>(?:(?>[^{}]+)|(?&block))*)\\})'; $classes->parens = '(?<parens>\\(\\s*(?<parens_content>(?:(?>[^()]+)|(?&parens))*)\\))'; // 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) { $patt->{$name} = '~' . $class . '~S'; } // Rooted classes. $patt->rooted_ident = '~^' . $classes->ident . '$~'; $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->mixin = Regex::make('~@mixin \\s+ (?<name>{{ident}}) \\s* {{block}}~ixS'); $patt->fragmentCapture = Regex::make('~@fragment \\s+ (?<name>{{ident}}) \\s* {{block}}~ixS'); $patt->fragmentInvoke = Regex::make('~@fragment \\s+ (?<name>{{ident}}) {{parens}}? \\s* ;~ixS'); $patt->abstract = Regex::make('~^@abstract \\s+ (?<name>{{ident}})~ixS'); // Functions. $patt->functionTest = Regex::make('~{{ LB }} (?<func_name>{{ ident }}) \\(~xS'); $patt->thisFunction = Functions::makePattern(array('this')); // Strings and comments. $patt->string = '~(\'|")(?:\\\\\\1|[^\\1])*?\\1~xS'; $patt->commentAndString = '~ # Quoted string (to EOF if unmatched). (\'|")(?:\\\\\\1|[^\\1])*?(?:\\1|$) | # Block comment (to EOF if unmatched). /\\*(?:[^*]*\\*+(?:[^/*][^*]*\\*+)*/|.*) ~xsS'; // Misc. $patt->vendorPrefix = '~^-([a-z]+)-([a-z-]+)~iS'; $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('~^ \\? (?<type>[a-zA-Z]) {{token_id}} \\? $~xS'); }
public static function parseAliasesFile($file) { if (!($tree = Util::parseIni($file, true))) { return false; } $regex = Regex::$patt; // Some alias groups need further parsing to unpack useful information into the tree. foreach ($tree as $section => $items) { if ($section === 'declarations') { $store = array(); foreach ($items as $prop_val => $aliases) { list($prop, $value) = array_map('trim', explode(':', $prop_val)); foreach ($aliases as &$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; } $tree['declarations'] = $store; } 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. $tree['functions'][$func_name] = $group; foreach ($aliases as $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}(?=\\()~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'); return $tree; }
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%'), '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 = "<radialGradient id=\"{$gradient_id}\" gradientUnits=\"userSpaceOnUse\""; $gradient .= " cx=\"{$position[0]}\" cy=\"{$position[1]}\" r=\"100%\">"; $gradient .= $color_stops; $gradient .= '</radialGradient>'; return array($gradient_id => $gradient); }