PageRenderTime 405ms CodeModel.GetById 96ms app.highlight 195ms RepoModel.GetById 67ms app.codeStats 1ms

/app/extensions/less/lib/lessphp/lib/Less/Node/Ruleset.php

https://bitbucket.org/Crisu83/webgames
PHP | 253 lines | 202 code | 35 blank | 16 comment | 48 complexity | f6ae6300c5c88bc8422e63a873e0c244 MD5 | raw file
  1<?php
  2
  3namespace Less\Node;
  4
  5class Ruleset
  6{
  7    public $selectors;
  8    public $rules;
  9    protected $lookups;
 10    public $root;
 11    private $_variables;
 12    private $_rulesets;
 13
 14    public function __construct($selectors, $rules)
 15    {
 16        $this->selectors = $selectors;
 17        $this->rules = (array) $rules;
 18        $this->lookups = array();
 19
 20    }
 21
 22    public function compile($env)
 23    {
 24        $ruleset = new Ruleset($this->selectors, $this->rules);
 25        $ruleset->root = $this->root;
 26
 27        // push the current ruleset to the frames stack
 28        $env->unshiftFrame($ruleset);
 29
 30        // Evaluate imports
 31        if ($ruleset->root) {
 32            for($i = 0; $i < count($ruleset->rules); $i++) {
 33                if ($ruleset->rules[$i] instanceof \Less\Node\Import && ! $ruleset->rules[$i]->css) {
 34                    $newRules = $ruleset->rules[$i]->compile($env);
 35                    $ruleset->rules = array_merge(
 36                        array_slice($ruleset->rules, 0, $i),
 37                        (array) $newRules,
 38                        array_slice($ruleset->rules, $i + 1)
 39                    );
 40                }
 41            }
 42        }
 43
 44        // Store the frames around mixin definitions,
 45        // so they can be evaluated like closures when the time comes.
 46        foreach($ruleset->rules as $i => $rule) {
 47            if ($rule instanceof \Less\Node\Mixin\Definition) {
 48                $ruleset->rules[$i]->frames = $env->frames;
 49            }
 50        }
 51
 52        // Evaluate mixin calls.
 53        for($i = 0; $i < count($ruleset->rules); $i++) {
 54            if ($ruleset->rules[$i] instanceof \Less\Node\Mixin\Call) {
 55                $newRules = $ruleset->rules[$i]->compile($env);
 56                $ruleset->rules = array_merge(
 57                    array_slice($ruleset->rules, 0, $i),
 58                    $newRules,
 59                    array_slice($ruleset->rules, $i + 1)
 60                );
 61            }
 62        }
 63
 64        // Evaluate everything else
 65        foreach($ruleset->rules as $i => $rule) {
 66            if (! ($rule instanceof \Less\Node\Mixin\Definition)) {
 67                $ruleset->rules[$i] = is_string($rule) ? $rule : $rule->compile($env);
 68            }
 69        }
 70
 71        // Pop the stack
 72        $env->shiftFrame();
 73
 74        return $ruleset;
 75    }
 76
 77    public function match($args)
 78    {
 79        return ! is_array($args) || count($args) === 0;
 80    }
 81
 82    public function variables()
 83    {
 84        if ( ! $this->_variables) {
 85            $this->_variables = array_reduce($this->rules, function ($hash, $r) {
 86                if ($r instanceof \Less\Node\Rule && $r->variable === true) {
 87                    $hash[$r->name] = $r;
 88                }
 89                return $hash;
 90            });
 91        }
 92
 93        return $this->_variables;
 94    }
 95
 96    public function variable($name)
 97    {
 98        $vars = $this->variables();
 99
100        return isset($vars[$name]) ? $vars[$name] : null;
101    }
102
103    public function rulesets ()
104    {
105        if ($this->_rulesets) {
106            return $this->_rulesets;
107        } else {
108            return $this->_rulesets = array_filter($this->rules, function ($r) {
109                return ($r instanceof \Less\Node\Ruleset) || ($r instanceof \Less\Node\Mixin\Definition);
110            });
111        }
112    }
113
114    public function find ($selector, $self = null, $env = null)
115    {
116        $self = $self ?: $this;
117        $rules = array();
118        $key = $selector->toCSS($env);
119
120        if (array_key_exists($key, $this->lookups)) {
121            return $this->lookups[$key];
122        }
123
124        foreach($this->rulesets() as $rule) {
125            if ($rule !== $self) {
126                foreach($rule->selectors as $ruleSelector) {
127                    if ($selector->match($ruleSelector)) {
128
129                        if (count($selector->elements) > count($ruleSelector->elements)) {
130                            $rules = array_merge($rules, $rule->find( new \Less\Node\Selector(array_slice($selector->elements, 1)), $self, $env));
131                        } else {
132                            $rules[] = $rule;
133                        }
134                        break;
135                    }
136                }
137            }
138        }
139
140        $this->lookups[$key] = $rules;
141
142        return $this->lookups[$key];
143    }
144
145    //
146    // Entry point for code generation
147    //
148    //     `context` holds an array of arrays.
149    //
150    public function toCSS ($context, $env)
151    {
152        $css = array();      // The CSS output
153        $rules = array();    // node.Rule instances
154        $rulesets = array(); // node.Ruleset instances
155        $paths = array();    // Current selectors
156
157        if (! $this->root) {
158            if (count($context) === 0) {
159                $paths = array_map(function ($s) { return array($s); }, $this->selectors);
160            } else {
161                $this->joinSelectors($paths, $context, $this->selectors);
162            }
163        }
164
165        // Compile rules and rulesets
166        foreach($this->rules as $rule) {
167            if (isset($rule->rules) || ($rule instanceof \Less\Node\Directive)) {
168                $rulesets[] = $rule->toCSS($paths, $env);
169            } else if ($rule instanceof \Less\Node\Comment) {
170                if (!$rule->silent) {
171                    if ($this->root) {
172                        $rulesets[] = $rule->toCSS($env);
173                    } else {
174                        $rules[] = $rule->toCSS($env);
175                    }
176                }
177            } else {
178                if (method_exists($rule, 'toCSS') && ( ! isset($rule->variable) ||  ! $rule->variable)) {
179                    $rules[] = $rule->toCSS($env);
180                } else if (isset($rule->value) && $rule->value && ! $rule->variable) {
181                    $rules[] = (string) $rule->value;
182                }
183            }
184        }
185
186        $rulesets = implode('', $rulesets);
187
188        // If this is the root node, we don't render
189        // a selector, or {}.
190        // Otherwise, only output if this ruleset has rules.
191        if ($this->root) {
192            $css[] = implode($env->compress ? '' : "\n", $rules);
193        } else {
194            if (count($rules)) {
195                $selector = array_map(function ($p) use ($env) {
196                    return trim(implode('', array_map(function ($s) use ($env) {
197                        return $s->toCSS($env);
198                    }, $p)));
199                }, $paths);
200
201                $selector = implode($env->compress ? ',' : (count($paths) > 3 ? ",\n" : ', '), $selector);
202
203                $css[] = $selector;
204                $css[] = ($env->compress ? '{' : " {\n  ") .
205                         implode($env->compress ? '' : "\n  ", $rules) .
206                         ($env->compress ? '}' : "\n}\n");
207            }
208        }
209        $css[] = $rulesets;
210
211        return implode('', $css) . ($env->compress ? "\n" : '');
212    }
213
214    public function joinSelectors (&$paths, $context, $selectors)
215    {
216        foreach($selectors as $selector) {
217            $this->joinSelector($paths, $context, $selector);
218        }
219    }
220
221    public function joinSelector (&$paths, $context, $selector)
222    {
223        $before = array();
224        $after = array();
225        $beforeElements = array();
226        $afterElements = array();
227        $hasParentSelector = false;
228
229        foreach($selector->elements as $el) {
230            if (strlen($el->combinator->value) > 0 && $el->combinator->value[0] === '&') {
231                $hasParentSelector = true;
232            }
233            if ($hasParentSelector) {
234                $afterElements[] = $el;
235            } else {
236                $beforeElements[] = $el;
237            }
238        }
239        if (! $hasParentSelector) {
240            $afterElements = $beforeElements;
241            $beforeElements = array();
242        }
243        if (count($beforeElements) > 0) {
244            $before[] = new \Less\Node\Selector($beforeElements);
245        }
246        if (count($afterElements) > 0) {
247            $after[] = new \Less\Node\Selector($afterElements);
248        }
249        foreach($context as $c) {
250            $paths[] = array_merge($before, $c, $after);
251        }
252    }
253}