123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292 |
- <?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);
- }
- }
|