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 static function expandAliases($str) { $process = Crush::$process; if (!$process->selectorAliases || !preg_match($process->selectorAliasesPatt, $str)) { return $str; } while (preg_match_all($process->selectorAliasesPatt, $str, $m, PREG_OFFSET_CAPTURE | PREG_SET_ORDER)) { $alias_call = end($m); $alias_name = strtolower($alias_call[1][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($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; } } $str = substr_replace($str, $process->selectorAliases[$alias_name]($args), $start, $length); } return $str; }
public function __invoke(array $args = null, $str = null) { $str = parent::__invoke($args); // Flatten all fragment calls within the template string. while (preg_match(Regex::$patt->fragmentInvoke, $str, $m, PREG_OFFSET_CAPTURE)) { $name = strtolower($m['name'][0]); $fragment = isset(Crush::$process->fragments[$name]) ? Crush::$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'][1])) { $args = Functions::parseArgs($m['parens_content'][0]); } $replacement = $fragment($args); } $str = substr_replace($str, $replacement, $start, $length); } return $str; }
function fn__query($input, $context) { $args = Functions::parseArgs($input); // Context property is required. if (!count($args) || !isset($context->property)) { return ''; } list($target, $property, $fallback) = $args + array(null, $context->property, null); if (strtolower($property) === 'default') { $property = $context->property; } if (!preg_match(Regex::$patt->rooted_ident, $target)) { $target = Selector::makeReadable($target); } $targetRule = null; $references =& Crush::$process->references; 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 = ''; if ($targetRule) { $targetRule->declarations->process(); $targetRule->declarations->expandData('queryData', $property); if (isset($targetRule->declarations->queryData[$property])) { $result = $targetRule->declarations->queryData[$property]; } } if ($result === '' && isset($fallback)) { $result = $fallback; } return $result; }
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 xmlns=\"http://www.w3.org/2000/svg\" width=\"{$dimensions[0]}\" height=\"{$dimensions[1]}\">"; $svg .= '<defs>'; $svg .= "<filter id=\"f\" x=\"0\" y=\"0\" width=\"100%\" height=\"100%\">"; $svg .= "<feTurbulence type=\"{$type}\" baseFrequency=\"{$frequency}\" numOctaves=\"{$octaves}\" result=\"f1\"/>"; $component_adjustments = array(); if ($sharpen === 'sharpen') { // It's more posterizing than sharpening, but it hits a sweet spot. $component_adjustments[] = "<feFuncR type=\"discrete\" tableValues=\"0 .5 1 1\"/>"; $component_adjustments[] = "<feFuncG type=\"discrete\" tableValues=\"0 .5 1\"/>"; // Some unpredictable results with this: // $component_adjustments[] = "<feFuncB type=\"discrete\" tableValues=\"0\"/>"; } if ($opacity != '1') { $component_adjustments[] = "<feFuncA type=\"table\" tableValues=\"0 {$opacity}\"/>"; } if ($component_adjustments) { $svg .= "<feComponentTransfer>"; $svg .= implode('', $component_adjustments); $svg .= "</feComponentTransfer>"; } if ($color_filter) { $svg .= "<feColorMatrix type=\"{$color_filter[0]}\" values=\"{$color_filter[1]}\"/>"; } if ($blend_mode !== 'normal') { $svg .= "<feBlend mode=\"{$blend_mode}\" in=\"SourceGraphic\"/>"; } $svg .= '</filter>'; $svg .= '</defs>'; $svg .= "<rect x=\"0\" y=\"0\" width=\"100%\" height=\"100%\" fill=\"{$fill_color}\"/>"; $svg .= "<rect x=\"0\" y=\"0\" width=\"100%\" height=\"100%\" fill=\"{$fill_color}\" filter=\"url(#f)\"/>"; $svg .= '</svg>'; return Crush::$process->tokens->add(new Url('data:image/svg+xml;base64,' . base64_encode($svg))); }
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); }); }
function svg_fn_pattern($input, $element) { $pid = 'p' . ++$GLOBALS['CSSCRUSH_SVG_UID']; // Get args in order with defaults. list($url, $transform_list, $width, $height, $x, $y) = Functions::parseArgs($input) + array('', '', 0, 0, 0, 0); $url = Crush::$process->tokens->get($url); if (!$url) { return ''; } // If $width or $height is not specified get image dimensions the slow way. if (!$width || !$height) { $file = $url->getAbsolutePath(); 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 = "<pattern id=\"{$pid}\" patternUnits=\"userSpaceOnUse\" width=\"{$width}\" height=\"{$height}\"{$transform_list}>"; $generated_pattern .= "<image xlink:href=\"{$url->value}\" x=\"{$x}\" y=\"{$y}\" width=\"{$width}\" height=\"{$height}\"/>"; $generated_pattern .= '</pattern>'; $element->fills['patterns'][] = $generated_pattern; $element->svg_attrs['xmlns:xlink'] = "http://www.w3.org/1999/xlink"; return 'url(#' . $pid . ')'; }
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); }