Viewing file: Ruleset.php (16.05 KB) -rw-rw-rw- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
<?php
/** * Ruleset * * @package Less * @subpackage tree */ class Less_Tree_Ruleset extends Less_Tree{
protected $lookups; public $_variables; public $_rulesets;
public $strictImports;
public $selectors; public $rules; public $root; public $allowImports; public $paths; public $firstRoot; public $type = 'Ruleset'; public $multiMedia; public $allExtends;
public $ruleset_id; public $originalRuleset;
public $first_oelements;
public function SetRulesetIndex(){ $this->ruleset_id = Less_Parser::$next_id++; $this->originalRuleset = $this->ruleset_id;
if( $this->selectors ){ foreach($this->selectors as $sel){ if( $sel->_oelements ){ $this->first_oelements[$sel->_oelements[0]] = true; } } } }
public function __construct($selectors, $rules, $strictImports = null){ $this->selectors = $selectors; $this->rules = $rules; $this->lookups = array(); $this->strictImports = $strictImports; $this->SetRulesetIndex(); }
public function accept( $visitor ){ if( $this->paths ){ $paths_len = count($this->paths); for($i = 0,$paths_len; $i < $paths_len; $i++ ){ $this->paths[$i] = $visitor->visitArray($this->paths[$i]); } }elseif( $this->selectors ){ $this->selectors = $visitor->visitArray($this->selectors); }
if( $this->rules ){ $this->rules = $visitor->visitArray($this->rules); } }
public function compile($env){
$ruleset = $this->PrepareRuleset($env);
// Store the frames around mixin definitions, // so they can be evaluated like closures when the time comes. $rsRuleCnt = count($ruleset->rules); for( $i = 0; $i < $rsRuleCnt; $i++ ){ if( $ruleset->rules[$i] instanceof Less_Tree_Mixin_Definition || $ruleset->rules[$i] instanceof Less_Tree_DetachedRuleset ){ $ruleset->rules[$i] = $ruleset->rules[$i]->compile($env); } }
$mediaBlockCount = 0; if( $env instanceof Less_Environment ){ $mediaBlockCount = count($env->mediaBlocks); }
// Evaluate mixin calls. $this->EvalMixinCalls( $ruleset, $env, $rsRuleCnt );
// Evaluate everything else for( $i=0; $i<$rsRuleCnt; $i++ ){ if(! ($ruleset->rules[$i] instanceof Less_Tree_Mixin_Definition || $ruleset->rules[$i] instanceof Less_Tree_DetachedRuleset) ){ $ruleset->rules[$i] = $ruleset->rules[$i]->compile($env); } }
// Evaluate everything else for( $i=0; $i<$rsRuleCnt; $i++ ){ $rule = $ruleset->rules[$i];
// for rulesets, check if it is a css guard and can be removed if( $rule instanceof Less_Tree_Ruleset && $rule->selectors && count($rule->selectors) === 1 ){
// check if it can be folded in (e.g. & where) if( $rule->selectors[0]->isJustParentSelector() ){ array_splice($ruleset->rules,$i--,1); $rsRuleCnt--;
for($j = 0; $j < count($rule->rules); $j++ ){ $subRule = $rule->rules[$j]; if( !($subRule instanceof Less_Tree_Rule) || !$subRule->variable ){ array_splice($ruleset->rules, ++$i, 0, array($subRule)); $rsRuleCnt++; } }
} } }
// Pop the stack $env->shiftFrame();
if ($mediaBlockCount) { $len = count($env->mediaBlocks); for($i = $mediaBlockCount; $i < $len; $i++ ){ $env->mediaBlocks[$i]->bubbleSelectors($ruleset->selectors); } }
return $ruleset; }
/** * Compile Less_Tree_Mixin_Call objects * * @param Less_Tree_Ruleset $ruleset * @param integer $rsRuleCnt */ private function EvalMixinCalls( $ruleset, $env, &$rsRuleCnt ){ for($i=0; $i < $rsRuleCnt; $i++){ $rule = $ruleset->rules[$i];
if( $rule instanceof Less_Tree_Mixin_Call ){ $rule = $rule->compile($env);
$temp = array(); foreach($rule as $r){ if( ($r instanceof Less_Tree_Rule) && $r->variable ){ // do not pollute the scope if the variable is // already there. consider returning false here // but we need a way to "return" variable from mixins if( !$ruleset->variable($r->name) ){ $temp[] = $r; } }else{ $temp[] = $r; } } $temp_count = count($temp)-1; array_splice($ruleset->rules, $i, 1, $temp); $rsRuleCnt += $temp_count; $i += $temp_count; $ruleset->resetCache();
}elseif( $rule instanceof Less_Tree_RulesetCall ){
$rule = $rule->compile($env); $rules = array(); foreach($rule->rules as $r){ if( ($r instanceof Less_Tree_Rule) && $r->variable ){ continue; } $rules[] = $r; }
array_splice($ruleset->rules, $i, 1, $rules); $temp_count = count($rules); $rsRuleCnt += $temp_count - 1; $i += $temp_count-1; $ruleset->resetCache(); }
} }
/** * Compile the selectors and create a new ruleset object for the compile() method * */ private function PrepareRuleset($env){
$hasOnePassingSelector = false; $selectors = array(); if( $this->selectors ){ Less_Tree_DefaultFunc::error("it is currently only allowed in parametric mixin guards,");
foreach($this->selectors as $s){ $selector = $s->compile($env); $selectors[] = $selector; if( $selector->evaldCondition ){ $hasOnePassingSelector = true; } }
Less_Tree_DefaultFunc::reset(); } else { $hasOnePassingSelector = true; }
if( $this->rules && $hasOnePassingSelector ){ $rules = $this->rules; }else{ $rules = array(); }
$ruleset = new Less_Tree_Ruleset($selectors, $rules, $this->strictImports);
$ruleset->originalRuleset = $this->ruleset_id;
$ruleset->root = $this->root; $ruleset->firstRoot = $this->firstRoot; $ruleset->allowImports = $this->allowImports;
// push the current ruleset to the frames stack $env->unshiftFrame($ruleset);
// Evaluate imports if( $ruleset->root || $ruleset->allowImports || !$ruleset->strictImports ){ $ruleset->evalImports($env); }
return $ruleset; }
function evalImports($env) {
$rules_len = count($this->rules); for($i=0; $i < $rules_len; $i++){ $rule = $this->rules[$i];
if( $rule instanceof Less_Tree_Import ){ $rules = $rule->compile($env); if( is_array($rules) ){ array_splice($this->rules, $i, 1, $rules); $temp_count = count($rules)-1; $i += $temp_count; $rules_len += $temp_count; }else{ array_splice($this->rules, $i, 1, array($rules)); }
$this->resetCache(); } } }
function makeImportant(){
$important_rules = array(); foreach($this->rules as $rule){ if( $rule instanceof Less_Tree_Rule || $rule instanceof Less_Tree_Ruleset ){ $important_rules[] = $rule->makeImportant(); }else{ $important_rules[] = $rule; } }
return new Less_Tree_Ruleset($this->selectors, $important_rules, $this->strictImports ); }
public function matchArgs($args){ return !$args; }
// lets you call a css selector with a guard public function matchCondition( $args, $env ){ $lastSelector = end($this->selectors);
if( !$lastSelector->evaldCondition ){ return false; } if( $lastSelector->condition && !$lastSelector->condition->compile( $env->copyEvalEnv( $env->frames ) ) ){ return false; } return true; }
function resetCache(){ $this->_rulesets = null; $this->_variables = null; $this->lookups = array(); }
public function variables(){ $this->_variables = array(); foreach( $this->rules as $r){ if ($r instanceof Less_Tree_Rule && $r->variable === true) { $this->_variables[$r->name] = $r; } } }
public function variable($name){
if( is_null($this->_variables) ){ $this->variables(); } return isset($this->_variables[$name]) ? $this->_variables[$name] : null; }
public function find( $selector, $self = null ){
$key = implode(' ',$selector->_oelements);
if( !isset($this->lookups[$key]) ){
if( !$self ){ $self = $this->ruleset_id; }
$this->lookups[$key] = array();
$first_oelement = $selector->_oelements[0];
foreach($this->rules as $rule){ if( $rule instanceof Less_Tree_Ruleset && $rule->ruleset_id != $self ){
if( isset($rule->first_oelements[$first_oelement]) ){
foreach( $rule->selectors as $ruleSelector ){ $match = $selector->match($ruleSelector); if( $match ){ if( $selector->elements_len > $match ){ $this->lookups[$key] = array_merge($this->lookups[$key], $rule->find( new Less_Tree_Selector(array_slice($selector->elements, $match)), $self)); } else { $this->lookups[$key][] = $rule; } break; } } } } } }
return $this->lookups[$key]; }
/** * @see Less_Tree::genCSS */ public function genCSS( $output ){
if( !$this->root ){ Less_Environment::$tabLevel++; }
$tabRuleStr = $tabSetStr = ''; if( !Less_Parser::$options['compress'] ){ if( Less_Environment::$tabLevel ){ $tabRuleStr = "\n".str_repeat( ' ' , Less_Environment::$tabLevel ); $tabSetStr = "\n".str_repeat( ' ' , Less_Environment::$tabLevel-1 ); }else{ $tabSetStr = $tabRuleStr = "\n"; } }
$ruleNodes = array(); $rulesetNodes = array(); foreach($this->rules as $rule){
$class = get_class($rule); if( ($class === 'Less_Tree_Media') || ($class === 'Less_Tree_Directive') || ($this->root && $class === 'Less_Tree_Comment') || ($class === 'Less_Tree_Ruleset' && $rule->rules) ){ $rulesetNodes[] = $rule; }else{ $ruleNodes[] = $rule; } }
// If this is the root node, we don't render // a selector, or {}. if( !$this->root ){
/* debugInfo = tree.debugInfo(env, this, tabSetStr);
if (debugInfo) { output.add(debugInfo); output.add(tabSetStr); } */
$paths_len = count($this->paths); for( $i = 0; $i < $paths_len; $i++ ){ $path = $this->paths[$i]; $firstSelector = true;
foreach($path as $p){ $p->genCSS( $output, $firstSelector ); $firstSelector = false; }
if( $i + 1 < $paths_len ){ $output->add( ',' . $tabSetStr ); } }
$output->add( (Less_Parser::$options['compress'] ? '{' : " {") . $tabRuleStr ); }
// Compile rules and rulesets $ruleNodes_len = count($ruleNodes); $rulesetNodes_len = count($rulesetNodes); for( $i = 0; $i < $ruleNodes_len; $i++ ){ $rule = $ruleNodes[$i];
// @page{ directive ends up with root elements inside it, a mix of rules and rulesets // In this instance we do not know whether it is the last property if( $i + 1 === $ruleNodes_len && (!$this->root || $rulesetNodes_len === 0 || $this->firstRoot ) ){ Less_Environment::$lastRule = true; }
$rule->genCSS( $output );
if( !Less_Environment::$lastRule ){ $output->add( $tabRuleStr ); }else{ Less_Environment::$lastRule = false; } }
if( !$this->root ){ $output->add( $tabSetStr . '}' ); Less_Environment::$tabLevel--; }
$firstRuleset = true; $space = ($this->root ? $tabRuleStr : $tabSetStr); for( $i = 0; $i < $rulesetNodes_len; $i++ ){
if( $ruleNodes_len && $firstRuleset ){ $output->add( $space ); }elseif( !$firstRuleset ){ $output->add( $space ); } $firstRuleset = false; $rulesetNodes[$i]->genCSS( $output); }
if( !Less_Parser::$options['compress'] && $this->firstRoot ){ $output->add( "\n" ); }
}
function markReferenced(){ if( !$this->selectors ){ return; } foreach($this->selectors as $selector){ $selector->markReferenced(); } }
public function joinSelectors( $context, $selectors ){ $paths = array(); if( is_array($selectors) ){ foreach($selectors as $selector) { $this->joinSelector( $paths, $context, $selector); } } return $paths; }
public function joinSelector( &$paths, $context, $selector){
$hasParentSelector = false;
foreach($selector->elements as $el) { if( $el->value === '&') { $hasParentSelector = true; } }
if( !$hasParentSelector ){ if( $context ){ foreach($context as $context_el){ $paths[] = array_merge($context_el, array($selector) ); } }else { $paths[] = array($selector); } return; }
// The paths are [[Selector]] // The first list is a list of comma seperated selectors // The inner list is a list of inheritance seperated selectors // e.g. // .a, .b { // .c { // } // } // == [[.a] [.c]] [[.b] [.c]] //
// the elements from the current selector so far $currentElements = array(); // the current list of new selectors to add to the path. // We will build it up. We initiate it with one empty selector as we "multiply" the new selectors // by the parents $newSelectors = array(array());
foreach( $selector->elements as $el){
// non parent reference elements just get added if( $el->value !== '&' ){ $currentElements[] = $el; } else { // the new list of selectors to add $selectorsMultiplied = array();
// merge the current list of non parent selector elements // on to the current list of selectors to add if( $currentElements ){ $this->mergeElementsOnToSelectors( $currentElements, $newSelectors); }
// loop through our current selectors foreach($newSelectors as $sel){
// if we don't have any parent paths, the & might be in a mixin so that it can be used // whether there are parents or not if( !$context ){ // the combinator used on el should now be applied to the next element instead so that // it is not lost if( $sel ){ $sel[0]->elements = array_slice($sel[0]->elements,0); $sel[0]->elements[] = new Less_Tree_Element($el->combinator, '', $el->index, $el->currentFileInfo ); } $selectorsMultiplied[] = $sel; }else {
// and the parent selectors foreach($context as $parentSel){ // We need to put the current selectors // then join the last selector's elements on to the parents selectors
// our new selector path $newSelectorPath = array(); // selectors from the parent after the join $afterParentJoin = array(); $newJoinedSelectorEmpty = true;
//construct the joined selector - if & is the first thing this will be empty, // if not newJoinedSelector will be the last set of elements in the selector if( $sel ){ $newSelectorPath = $sel; $lastSelector = array_pop($newSelectorPath); $newJoinedSelector = $selector->createDerived( array_slice($lastSelector->elements,0) ); $newJoinedSelectorEmpty = false; } else { $newJoinedSelector = $selector->createDerived(array()); }
//put together the parent selectors after the join if ( count($parentSel) > 1) { $afterParentJoin = array_merge($afterParentJoin, array_slice($parentSel,1) ); }
if ( $parentSel ){ $newJoinedSelectorEmpty = false;
// join the elements so far with the first part of the parent $newJoinedSelector->elements[] = new Less_Tree_Element( $el->combinator, $parentSel[0]->elements[0]->value, $el->index, $el->currentFileInfo);
$newJoinedSelector->elements = array_merge( $newJoinedSelector->elements, array_slice($parentSel[0]->elements, 1) ); }
if (!$newJoinedSelectorEmpty) { // now add the joined selector $newSelectorPath[] = $newJoinedSelector; }
// and the rest of the parent $newSelectorPath = array_merge($newSelectorPath, $afterParentJoin);
// add that to our new set of selectors $selectorsMultiplied[] = $newSelectorPath; } } }
// our new selectors has been multiplied, so reset the state $newSelectors = $selectorsMultiplied; $currentElements = array(); } }
// if we have any elements left over (e.g. .a& .b == .b) // add them on to all the current selectors if( $currentElements ){ $this->mergeElementsOnToSelectors($currentElements, $newSelectors); } foreach( $newSelectors as $new_sel){ if( $new_sel ){ $paths[] = $new_sel; } } }
function mergeElementsOnToSelectors( $elements, &$selectors){
if( !$selectors ){ $selectors[] = array( new Less_Tree_Selector($elements) ); return; }
foreach( $selectors as &$sel){
// if the previous thing in sel is a parent this needs to join on to it if( $sel ){ $last = count($sel)-1; $sel[$last] = $sel[$last]->createDerived( array_merge($sel[$last]->elements, $elements) ); }else{ $sel[] = new Less_Tree_Selector( $elements ); } } } }
|