PageRenderTime 50ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/demo/protected/extensions/less/lib/lessphp/lib/Less/Node/Ruleset.php

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