예제 #1
0
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;
}
예제 #2
0
 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;
 }
예제 #3
0
 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;
 }
예제 #4
0
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;
}
예제 #5
0
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)));
}
예제 #6
0
 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);
     });
 }
예제 #7
0
파일: svg.php 프로젝트: MrHidalgo/css-crush
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 . ')';
}
예제 #8
0
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);
}