Seditio Source
Root |
./othercms/PHPFusion 9.10.20/includes/classes/PHPFusion/Atom/lib/Less/Tree/Ruleset.php
<?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 || $rule instanceof Less_Tree_NameValue ){
               
$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_Parser::$options['indentation'] , Less_Environment::$tabLevel );
               
$tabSetStr = "\n".str_repeat( Less_Parser::$options['indentation'] , 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 separated selectors
        // The inner list is a list of inheritance separated 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 );
            }
        }
    }
}