194 lines
5.1 KiB
PHP
194 lines
5.1 KiB
PHP
|
<?php
|
||
|
|
||
|
class Less_Tree_Mixin_Call extends Less_Tree {
|
||
|
|
||
|
public $selector;
|
||
|
public $arguments;
|
||
|
public $index;
|
||
|
public $currentFileInfo;
|
||
|
|
||
|
public $important;
|
||
|
public $type = 'MixinCall';
|
||
|
|
||
|
/**
|
||
|
* less.js: tree.mixin.Call
|
||
|
*
|
||
|
*/
|
||
|
public function __construct( $elements, $args, $index, $currentFileInfo, $important = false ) {
|
||
|
$this->selector = new Less_Tree_Selector( $elements );
|
||
|
$this->arguments = $args;
|
||
|
$this->index = $index;
|
||
|
$this->currentFileInfo = $currentFileInfo;
|
||
|
$this->important = $important;
|
||
|
}
|
||
|
|
||
|
// function accept($visitor){
|
||
|
// $this->selector = $visitor->visit($this->selector);
|
||
|
// $this->arguments = $visitor->visit($this->arguments);
|
||
|
//}
|
||
|
|
||
|
public function compile( $env ) {
|
||
|
$rules = array();
|
||
|
$match = false;
|
||
|
$isOneFound = false;
|
||
|
$candidates = array();
|
||
|
$defaultUsed = false;
|
||
|
$conditionResult = array();
|
||
|
|
||
|
$args = array();
|
||
|
foreach ( $this->arguments as $a ) {
|
||
|
$args[] = array( 'name' => $a['name'], 'value' => $a['value']->compile( $env ) );
|
||
|
}
|
||
|
|
||
|
foreach ( $env->frames as $frame ) {
|
||
|
|
||
|
$mixins = $frame->find( $this->selector );
|
||
|
|
||
|
if ( !$mixins ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$isOneFound = true;
|
||
|
$defNone = 0;
|
||
|
$defTrue = 1;
|
||
|
$defFalse = 2;
|
||
|
|
||
|
// To make `default()` function independent of definition order we have two "subpasses" here.
|
||
|
// At first we evaluate each guard *twice* (with `default() == true` and `default() == false`),
|
||
|
// and build candidate list with corresponding flags. Then, when we know all possible matches,
|
||
|
// we make a final decision.
|
||
|
|
||
|
$mixins_len = count( $mixins );
|
||
|
for ( $m = 0; $m < $mixins_len; $m++ ) {
|
||
|
$mixin = $mixins[$m];
|
||
|
|
||
|
if ( $this->IsRecursive( $env, $mixin ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ( $mixin->matchArgs( $args, $env ) ) {
|
||
|
|
||
|
$candidate = array( 'mixin' => $mixin, 'group' => $defNone );
|
||
|
|
||
|
if ( $mixin instanceof Less_Tree_Ruleset ) {
|
||
|
|
||
|
for ( $f = 0; $f < 2; $f++ ) {
|
||
|
Less_Tree_DefaultFunc::value( $f );
|
||
|
$conditionResult[$f] = $mixin->matchCondition( $args, $env );
|
||
|
}
|
||
|
if ( $conditionResult[0] || $conditionResult[1] ) {
|
||
|
if ( $conditionResult[0] != $conditionResult[1] ) {
|
||
|
$candidate['group'] = $conditionResult[1] ? $defTrue : $defFalse;
|
||
|
}
|
||
|
|
||
|
$candidates[] = $candidate;
|
||
|
}
|
||
|
} else {
|
||
|
$candidates[] = $candidate;
|
||
|
}
|
||
|
|
||
|
$match = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Less_Tree_DefaultFunc::reset();
|
||
|
|
||
|
$count = array( 0, 0, 0 );
|
||
|
for ( $m = 0; $m < count( $candidates ); $m++ ) {
|
||
|
$count[ $candidates[$m]['group'] ]++;
|
||
|
}
|
||
|
|
||
|
if ( $count[$defNone] > 0 ) {
|
||
|
$defaultResult = $defFalse;
|
||
|
} else {
|
||
|
$defaultResult = $defTrue;
|
||
|
if ( ( $count[$defTrue] + $count[$defFalse] ) > 1 ) {
|
||
|
throw new Exception( 'Ambiguous use of `default()` found when matching for `' . $this->format( $args ) . '`' );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$candidates_length = count( $candidates );
|
||
|
$length_1 = ( $candidates_length == 1 );
|
||
|
|
||
|
for ( $m = 0; $m < $candidates_length; $m++ ) {
|
||
|
$candidate = $candidates[$m]['group'];
|
||
|
if ( ( $candidate === $defNone ) || ( $candidate === $defaultResult ) ) {
|
||
|
try{
|
||
|
$mixin = $candidates[$m]['mixin'];
|
||
|
if ( !( $mixin instanceof Less_Tree_Mixin_Definition ) ) {
|
||
|
$mixin = new Less_Tree_Mixin_Definition( '', array(), $mixin->rules, null, false );
|
||
|
$mixin->originalRuleset = $mixins[$m]->originalRuleset;
|
||
|
}
|
||
|
$rules = array_merge( $rules, $mixin->evalCall( $env, $args, $this->important )->rules );
|
||
|
} catch ( Exception $e ) {
|
||
|
// throw new Less_Exception_Compiler($e->getMessage(), $e->index, null, $this->currentFileInfo['filename']);
|
||
|
throw new Less_Exception_Compiler( $e->getMessage(), null, null, $this->currentFileInfo );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( $match ) {
|
||
|
if ( !$this->currentFileInfo || !isset( $this->currentFileInfo['reference'] ) || !$this->currentFileInfo['reference'] ) {
|
||
|
Less_Tree::ReferencedArray( $rules );
|
||
|
}
|
||
|
|
||
|
return $rules;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( $isOneFound ) {
|
||
|
throw new Less_Exception_Compiler( 'No matching definition was found for `'.$this->Format( $args ).'`', null, $this->index, $this->currentFileInfo );
|
||
|
|
||
|
} else {
|
||
|
throw new Less_Exception_Compiler( trim( $this->selector->toCSS() ) . " is undefined in ".$this->currentFileInfo['filename'], null, $this->index );
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Format the args for use in exception messages
|
||
|
*
|
||
|
*/
|
||
|
private function Format( $args ) {
|
||
|
$message = array();
|
||
|
if ( $args ) {
|
||
|
foreach ( $args as $a ) {
|
||
|
$argValue = '';
|
||
|
if ( $a['name'] ) {
|
||
|
$argValue .= $a['name'] . ':';
|
||
|
}
|
||
|
if ( is_object( $a['value'] ) ) {
|
||
|
$argValue .= $a['value']->toCSS();
|
||
|
} else {
|
||
|
$argValue .= '???';
|
||
|
}
|
||
|
$message[] = $argValue;
|
||
|
}
|
||
|
}
|
||
|
return implode( ', ', $message );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Are we in a recursive mixin call?
|
||
|
*
|
||
|
* @return bool
|
||
|
*/
|
||
|
private function IsRecursive( $env, $mixin ) {
|
||
|
foreach ( $env->frames as $recur_frame ) {
|
||
|
if ( !( $mixin instanceof Less_Tree_Mixin_Definition ) ) {
|
||
|
|
||
|
if ( $mixin === $recur_frame ) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if ( isset( $recur_frame->originalRuleset ) && $mixin->ruleset_id === $recur_frame->originalRuleset ) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
}
|