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 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; } }
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; }
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; }