123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280 |
- <?php
- /**
- * toCSS Visitor
- *
- * @package Less
- * @subpackage visitor
- */
- class Less_Visitor_toCSS extends Less_VisitorReplacing {
- private $charset;
- public function __construct() {
- parent::__construct();
- }
- /**
- * @param Less_Tree_Ruleset $root
- */
- public function run( $root ) {
- return $this->visitObj( $root );
- }
- public function visitRule( $ruleNode ) {
- if ( $ruleNode->variable ) {
- return array();
- }
- return $ruleNode;
- }
- public function visitMixinDefinition( $mixinNode ) {
- // mixin definitions do not get eval'd - this means they keep state
- // so we have to clear that state here so it isn't used if toCSS is called twice
- $mixinNode->frames = array();
- return array();
- }
- public function visitExtend() {
- return array();
- }
- public function visitComment( $commentNode ) {
- if ( $commentNode->isSilent() ) {
- return array();
- }
- return $commentNode;
- }
- public function visitMedia( $mediaNode, &$visitDeeper ) {
- $mediaNode->accept( $this );
- $visitDeeper = false;
- if ( !$mediaNode->rules ) {
- return array();
- }
- return $mediaNode;
- }
- public function visitDirective( $directiveNode ) {
- if ( isset( $directiveNode->currentFileInfo['reference'] ) && ( !property_exists( $directiveNode, 'isReferenced' ) || !$directiveNode->isReferenced ) ) {
- return array();
- }
- if ( $directiveNode->name === '@charset' ) {
- // Only output the debug info together with subsequent @charset definitions
- // a comment (or @media statement) before the actual @charset directive would
- // be considered illegal css as it has to be on the first line
- if ( isset( $this->charset ) && $this->charset ) {
- // if( $directiveNode->debugInfo ){
- // $comment = new Less_Tree_Comment('/* ' . str_replace("\n",'',$directiveNode->toCSS())." */\n");
- // $comment->debugInfo = $directiveNode->debugInfo;
- // return $this->visit($comment);
- //}
- return array();
- }
- $this->charset = true;
- }
- return $directiveNode;
- }
- public function checkPropertiesInRoot( $rulesetNode ) {
- if ( !$rulesetNode->firstRoot ) {
- return;
- }
- foreach ( $rulesetNode->rules as $ruleNode ) {
- if ( $ruleNode instanceof Less_Tree_Rule && !$ruleNode->variable ) {
- $msg = "properties must be inside selector blocks, they cannot be in the root. Index ".$ruleNode->index.( $ruleNode->currentFileInfo ? ( ' Filename: '.$ruleNode->currentFileInfo['filename'] ) : null );
- throw new Less_Exception_Compiler( $msg );
- }
- }
- }
- public function visitRuleset( $rulesetNode, &$visitDeeper ) {
- $visitDeeper = false;
- $this->checkPropertiesInRoot( $rulesetNode );
- if ( $rulesetNode->root ) {
- return $this->visitRulesetRoot( $rulesetNode );
- }
- $rulesets = array();
- $rulesetNode->paths = $this->visitRulesetPaths( $rulesetNode );
- // Compile rules and rulesets
- $nodeRuleCnt = $rulesetNode->rules ? count( $rulesetNode->rules ) : 0;
- for ( $i = 0; $i < $nodeRuleCnt; ) {
- $rule = $rulesetNode->rules[$i];
- if ( property_exists( $rule, 'rules' ) ) {
- // visit because we are moving them out from being a child
- $rulesets[] = $this->visitObj( $rule );
- array_splice( $rulesetNode->rules, $i, 1 );
- $nodeRuleCnt--;
- continue;
- }
- $i++;
- }
- // accept the visitor to remove rules and refactor itself
- // then we can decide now whether we want it or not
- if ( $nodeRuleCnt > 0 ) {
- $rulesetNode->accept( $this );
- if ( $rulesetNode->rules ) {
- if ( count( $rulesetNode->rules ) > 1 ) {
- $this->_mergeRules( $rulesetNode->rules );
- $this->_removeDuplicateRules( $rulesetNode->rules );
- }
- // now decide whether we keep the ruleset
- if ( $rulesetNode->paths ) {
- // array_unshift($rulesets, $rulesetNode);
- array_splice( $rulesets, 0, 0, array( $rulesetNode ) );
- }
- }
- }
- if ( count( $rulesets ) === 1 ) {
- return $rulesets[0];
- }
- return $rulesets;
- }
- /**
- * Helper function for visitiRuleset
- *
- * return array|Less_Tree_Ruleset
- */
- private function visitRulesetRoot( $rulesetNode ) {
- $rulesetNode->accept( $this );
- if ( $rulesetNode->firstRoot || $rulesetNode->rules ) {
- return $rulesetNode;
- }
- return array();
- }
- /**
- * Helper function for visitRuleset()
- *
- * @return array
- */
- private function visitRulesetPaths( $rulesetNode ) {
- $paths = array();
- foreach ( $rulesetNode->paths as $p ) {
- if ( $p[0]->elements[0]->combinator === ' ' ) {
- $p[0]->elements[0]->combinator = '';
- }
- foreach ( $p as $pi ) {
- if ( $pi->getIsReferenced() && $pi->getIsOutput() ) {
- $paths[] = $p;
- break;
- }
- }
- }
- return $paths;
- }
- protected function _removeDuplicateRules( &$rules ) {
- // remove duplicates
- $ruleCache = array();
- for ( $i = count( $rules ) - 1; $i >= 0; $i-- ) {
- $rule = $rules[$i];
- if ( $rule instanceof Less_Tree_Rule || $rule instanceof Less_Tree_NameValue ) {
- if ( !isset( $ruleCache[$rule->name] ) ) {
- $ruleCache[$rule->name] = $rule;
- } else {
- $ruleList =& $ruleCache[$rule->name];
- if ( $ruleList instanceof Less_Tree_Rule || $ruleList instanceof Less_Tree_NameValue ) {
- $ruleList = $ruleCache[$rule->name] = array( $ruleCache[$rule->name]->toCSS() );
- }
- $ruleCSS = $rule->toCSS();
- if ( array_search( $ruleCSS, $ruleList ) !== false ) {
- array_splice( $rules, $i, 1 );
- } else {
- $ruleList[] = $ruleCSS;
- }
- }
- }
- }
- }
- protected function _mergeRules( &$rules ) {
- $groups = array();
- // obj($rules);
- $rules_len = count( $rules );
- for ( $i = 0; $i < $rules_len; $i++ ) {
- $rule = $rules[$i];
- if ( ( $rule instanceof Less_Tree_Rule ) && $rule->merge ) {
- $key = $rule->name;
- if ( $rule->important ) {
- $key .= ',!';
- }
- if ( !isset( $groups[$key] ) ) {
- $groups[$key] = array();
- } else {
- array_splice( $rules, $i--, 1 );
- $rules_len--;
- }
- $groups[$key][] = $rule;
- }
- }
- foreach ( $groups as $parts ) {
- if ( count( $parts ) > 1 ) {
- $rule = $parts[0];
- $spacedGroups = array();
- $lastSpacedGroup = array();
- $parts_mapped = array();
- foreach ( $parts as $p ) {
- if ( $p->merge === '+' ) {
- if ( $lastSpacedGroup ) {
- $spacedGroups[] = self::toExpression( $lastSpacedGroup );
- }
- $lastSpacedGroup = array();
- }
- $lastSpacedGroup[] = $p;
- }
- $spacedGroups[] = self::toExpression( $lastSpacedGroup );
- $rule->value = self::toValue( $spacedGroups );
- }
- }
- }
- public static function toExpression( $values ) {
- $mapped = array();
- foreach ( $values as $p ) {
- $mapped[] = $p->value;
- }
- return new Less_Tree_Expression( $mapped );
- }
- public static function toValue( $values ) {
- // return new Less_Tree_Value($values); ??
- $mapped = array();
- foreach ( $values as $p ) {
- $mapped[] = $p;
- }
- return new Less_Tree_Value( $mapped );
- }
- }
|