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);
             }
         }
     }
 }
Exemple #2
0
 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;
     }
 }
Exemple #3
0
function svg_generator($input, $fn_name)
{
    $process = Crush::$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 $schemas;
    if (!$schemas) {
        $schemas = 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 ($schemas as $type => &$schema) {
            $schema['attrs'] = array_flip(explode(' ', $schema['attrs'])) + array('transform' => true);
        }
    }
    // Non standard attributes.
    static $custom_attrs = array('type' => true, 'data' => true, 'twist' => true, 'diameter' => true, 'corner-radius' => true, 'star-points' => true, 'margin' => true, 'drop-shadow' => true, 'sides' => true, 'text' => true, 'width' => true, 'height' => true);
    // Bail if no args.
    $args = Functions::parseArgs($input);
    if (!isset($args[0])) {
        return '';
    }
    $name = strtolower(array_shift($args));
    // Bail if no SVG registered by this name.
    $svg_defs =& $process->misc->svg_defs;
    if (!isset($svg_defs[$name])) {
        return '';
    }
    // Apply args to template.
    $block = $svg_defs[$name]($args);
    $raw_data = DeclarationList::parse($block, array('keyed' => true, 'lowercase_keys' => true, 'flatten' => true, 'apply_hooks' => true));
    // Resolve the type.
    // Bail if type not recognised.
    $type = isset($raw_data['type']) ? strtolower($raw_data['type']) : 'path';
    if (!isset($schemas[$type])) {
        return '';
    }
    // Create element object for attaching all required rendering data.
    $element = (object) array('tag' => $schemas[$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());
    // Filter off prefixed properties that are for the svg element or @font-face.
    foreach ($raw_data as $property => $value) {
        if (strpos($property, 'svg-') === 0) {
            $element->svg_styles[substr($property, 4)] = $value;
            unset($raw_data[$property]);
        } elseif (strpos($property, 'face-') === 0) {
            $element->face_styles[substr($property, 5)] = $value;
            unset($raw_data[$property]);
        }
    }
    svg_apply_css_funcs($element, $raw_data);
    // Initialize element attributes.
    $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, $schemas[$type]['attrs']);
    // Pre-populate common attributes.
    svg_preprocess($element);
    // Filters.
    svg_apply_filters($element);
    // Apply element type callback.
    call_user_func("CssCrush\\svg_{$type}", $element);
    // Apply optimizations.
    svg_compress($element);
    // Build markup.
    $svg = svg_render($element);
    // Debugging...
    // $code = implode("\n", $svg);
    // $test = '<pre>' . htmlspecialchars($code) . '</pre>';
    // echo $test;
    // Either write to a file.
    if ($fn_name === 'svg' && $process->ioContext === 'file') {
        $flattened_svg = implode("\n", $svg);
        // Create fingerprint for the created file.
        $fingerprint = substr(md5($flattened_svg), 0, 7);
        $generated_filename = "svg-{$name}-{$fingerprint}.svg";
        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_filepath, $flattened_svg, __METHOD__);
        $url = new Url($generated_url);
        $url->noRewrite = true;
    } else {
        $url = new Url('data:image/svg+xml;base64,' . base64_encode(implode('', $svg)));
    }
    // Cache the output URL.
    $label = $process->tokens->add($url);
    $process->misc->svg_cache[$cache_key] = $label;
    return $label;
}
Exemple #4
0
 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;
 }