diff options
Diffstat (limited to 'lib/less.php/Functions.php')
-rw-r--r-- | lib/less.php/Functions.php | 1188 |
1 files changed, 1188 insertions, 0 deletions
diff --git a/lib/less.php/Functions.php b/lib/less.php/Functions.php new file mode 100644 index 0000000..a61e5ac --- /dev/null +++ b/lib/less.php/Functions.php @@ -0,0 +1,1188 @@ +<?php + +/** + * Builtin functions + * + * @package Less + * @subpackage function + * @see http://lesscss.org/functions/ + */ +class Less_Functions{ + + public $env; + public $currentFileInfo; + + function __construct($env, $currentFileInfo = null ){ + $this->env = $env; + $this->currentFileInfo = $currentFileInfo; + } + + /** + * @param string $op + */ + public static function operate( $op, $a, $b ){ + switch ($op) { + case '+': return $a + $b; + case '-': return $a - $b; + case '*': return $a * $b; + case '/': return $a / $b; + } + } + + public static function clamp($val, $max = 1){ + return min( max($val, 0), $max); + } + + public static function fround( $value ){ + + if( $value === 0 ){ + return $value; + } + + if( Less_Parser::$options['numPrecision'] ){ + $p = pow(10, Less_Parser::$options['numPrecision']); + return round( $value * $p) / $p; + } + return $value; + } + + public static function number($n){ + + if ($n instanceof Less_Tree_Dimension) { + return floatval( $n->unit->is('%') ? $n->value / 100 : $n->value); + } else if (is_numeric($n)) { + return $n; + } else { + throw new Less_Exception_Compiler("color functions take numbers as parameters"); + } + } + + public static function scaled($n, $size = 255 ){ + if( $n instanceof Less_Tree_Dimension && $n->unit->is('%') ){ + return (float)$n->value * $size / 100; + } else { + return Less_Functions::number($n); + } + } + + public function rgb ($r = null, $g = null, $b = null){ + if (is_null($r) || is_null($g) || is_null($b)) { + throw new Less_Exception_Compiler("rgb expects three parameters"); + } + return $this->rgba($r, $g, $b, 1.0); + } + + public function rgba($r = null, $g = null, $b = null, $a = null){ + $rgb = array($r, $g, $b); + $rgb = array_map(array('Less_Functions','scaled'),$rgb); + + $a = self::number($a); + return new Less_Tree_Color($rgb, $a); + } + + public function hsl($h, $s, $l){ + return $this->hsla($h, $s, $l, 1.0); + } + + public function hsla($h, $s, $l, $a){ + + $h = fmod(self::number($h), 360) / 360; // Classic % operator will change float to int + $s = self::clamp(self::number($s)); + $l = self::clamp(self::number($l)); + $a = self::clamp(self::number($a)); + + $m2 = $l <= 0.5 ? $l * ($s + 1) : $l + $s - $l * $s; + + $m1 = $l * 2 - $m2; + + return $this->rgba( self::hsla_hue($h + 1/3, $m1, $m2) * 255, + self::hsla_hue($h, $m1, $m2) * 255, + self::hsla_hue($h - 1/3, $m1, $m2) * 255, + $a); + } + + /** + * @param double $h + */ + public function hsla_hue($h, $m1, $m2){ + $h = $h < 0 ? $h + 1 : ($h > 1 ? $h - 1 : $h); + if ($h * 6 < 1) return $m1 + ($m2 - $m1) * $h * 6; + else if ($h * 2 < 1) return $m2; + else if ($h * 3 < 2) return $m1 + ($m2 - $m1) * (2/3 - $h) * 6; + else return $m1; + } + + public function hsv($h, $s, $v) { + return $this->hsva($h, $s, $v, 1.0); + } + + /** + * @param double $a + */ + public function hsva($h, $s, $v, $a) { + $h = ((Less_Functions::number($h) % 360) / 360 ) * 360; + $s = Less_Functions::number($s); + $v = Less_Functions::number($v); + $a = Less_Functions::number($a); + + $i = floor(($h / 60) % 6); + $f = ($h / 60) - $i; + + $vs = array( $v, + $v * (1 - $s), + $v * (1 - $f * $s), + $v * (1 - (1 - $f) * $s)); + + $perm = array(array(0, 3, 1), + array(2, 0, 1), + array(1, 0, 3), + array(1, 2, 0), + array(3, 1, 0), + array(0, 1, 2)); + + return $this->rgba($vs[$perm[$i][0]] * 255, + $vs[$perm[$i][1]] * 255, + $vs[$perm[$i][2]] * 255, + $a); + } + + public function hue($color = null){ + if (!$color instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to hue must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + $c = $color->toHSL(); + return new Less_Tree_Dimension(Less_Parser::round($c['h'])); + } + + public function saturation($color = null){ + if (!$color instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to saturation must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + $c = $color->toHSL(); + return new Less_Tree_Dimension(Less_Parser::round($c['s'] * 100), '%'); + } + + public function lightness($color = null){ + if (!$color instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to lightness must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + $c = $color->toHSL(); + return new Less_Tree_Dimension(Less_Parser::round($c['l'] * 100), '%'); + } + + public function hsvhue( $color = null ){ + if (!$color instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to hsvhue must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + $hsv = $color->toHSV(); + return new Less_Tree_Dimension( Less_Parser::round($hsv['h']) ); + } + + + public function hsvsaturation( $color = null ){ + if (!$color instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to hsvsaturation must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + $hsv = $color->toHSV(); + return new Less_Tree_Dimension( Less_Parser::round($hsv['s'] * 100), '%' ); + } + + public function hsvvalue( $color = null ){ + if (!$color instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to hsvvalue must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + $hsv = $color->toHSV(); + return new Less_Tree_Dimension( Less_Parser::round($hsv['v'] * 100), '%' ); + } + + public function red($color = null) { + if (!$color instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to red must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + return new Less_Tree_Dimension( $color->rgb[0] ); + } + + public function green($color = null) { + if (!$color instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to green must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + return new Less_Tree_Dimension( $color->rgb[1] ); + } + + public function blue($color = null) { + if (!$color instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to blue must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + return new Less_Tree_Dimension( $color->rgb[2] ); + } + + public function alpha($color = null){ + if (!$color instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to alpha must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + $c = $color->toHSL(); + return new Less_Tree_Dimension($c['a']); + } + + public function luma ($color = null) { + if (!$color instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to luma must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + return new Less_Tree_Dimension(Less_Parser::round( $color->luma() * $color->alpha * 100), '%'); + } + + public function luminance( $color = null ){ + if (!$color instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to luminance must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + $luminance = + (0.2126 * $color->rgb[0] / 255) + + (0.7152 * $color->rgb[1] / 255) + + (0.0722 * $color->rgb[2] / 255); + + return new Less_Tree_Dimension(Less_Parser::round( $luminance * $color->alpha * 100), '%'); + } + + public function saturate($color = null, $amount = null){ + // filter: saturate(3.2); + // should be kept as is, so check for color + if ($color instanceof Less_Tree_Dimension) { + return null; + } + + if (!$color instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to saturate must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + if (!$amount instanceof Less_Tree_Dimension) { + throw new Less_Exception_Compiler('The second argument to saturate must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + $hsl = $color->toHSL(); + + $hsl['s'] += $amount->value / 100; + $hsl['s'] = self::clamp($hsl['s']); + + return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']); + } + + /** + * @param Less_Tree_Dimension $amount + */ + public function desaturate($color = null, $amount = null){ + if (!$color instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to desaturate must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + if (!$amount instanceof Less_Tree_Dimension) { + throw new Less_Exception_Compiler('The second argument to desaturate must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + $hsl = $color->toHSL(); + + $hsl['s'] -= $amount->value / 100; + $hsl['s'] = self::clamp($hsl['s']); + + return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']); + } + + + + public function lighten($color = null, $amount=null){ + if (!$color instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to lighten must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + if (!$amount instanceof Less_Tree_Dimension) { + throw new Less_Exception_Compiler('The second argument to lighten must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + $hsl = $color->toHSL(); + + $hsl['l'] += $amount->value / 100; + $hsl['l'] = self::clamp($hsl['l']); + + return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']); + } + + public function darken($color = null, $amount = null){ + if (!$color instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to darken must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + if (!$amount instanceof Less_Tree_Dimension) { + throw new Less_Exception_Compiler('The second argument to darken must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + $hsl = $color->toHSL(); + $hsl['l'] -= $amount->value / 100; + $hsl['l'] = self::clamp($hsl['l']); + + return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']); + } + + public function fadein($color = null, $amount = null){ + if (!$color instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to fadein must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + if (!$amount instanceof Less_Tree_Dimension) { + throw new Less_Exception_Compiler('The second argument to fadein must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + $hsl = $color->toHSL(); + $hsl['a'] += $amount->value / 100; + $hsl['a'] = self::clamp($hsl['a']); + return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']); + } + + public function fadeout($color = null, $amount = null){ + if (!$color instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to fadeout must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + if (!$amount instanceof Less_Tree_Dimension) { + throw new Less_Exception_Compiler('The second argument to fadeout must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + $hsl = $color->toHSL(); + $hsl['a'] -= $amount->value / 100; + $hsl['a'] = self::clamp($hsl['a']); + return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']); + } + + public function fade($color = null, $amount = null){ + if (!$color instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to fade must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + if (!$amount instanceof Less_Tree_Dimension) { + throw new Less_Exception_Compiler('The second argument to fade must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + $hsl = $color->toHSL(); + + $hsl['a'] = $amount->value / 100; + $hsl['a'] = self::clamp($hsl['a']); + return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']); + } + + + + public function spin($color = null, $amount = null){ + if (!$color instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to spin must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + if (!$amount instanceof Less_Tree_Dimension) { + throw new Less_Exception_Compiler('The second argument to spin must be a number' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + $hsl = $color->toHSL(); + $hue = fmod($hsl['h'] + $amount->value, 360); + + $hsl['h'] = $hue < 0 ? 360 + $hue : $hue; + + return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']); + } + + // + // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein + // http://sass-lang.com + // + + /** + * @param Less_Tree_Color $color1 + */ + public function mix($color1 = null, $color2 = null, $weight = null){ + if (!$color1 instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to mix must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + if (!$color2 instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The second argument to mix must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + if (!$weight) { + $weight = new Less_Tree_Dimension('50', '%'); + } + if (!$weight instanceof Less_Tree_Dimension) { + throw new Less_Exception_Compiler('The third argument to contrast must be a percentage' . ($weight instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + $p = $weight->value / 100.0; + $w = $p * 2 - 1; + $hsl1 = $color1->toHSL(); + $hsl2 = $color2->toHSL(); + $a = $hsl1['a'] - $hsl2['a']; + + $w1 = (((($w * $a) == -1) ? $w : ($w + $a) / (1 + $w * $a)) + 1) / 2; + $w2 = 1 - $w1; + + $rgb = array($color1->rgb[0] * $w1 + $color2->rgb[0] * $w2, + $color1->rgb[1] * $w1 + $color2->rgb[1] * $w2, + $color1->rgb[2] * $w1 + $color2->rgb[2] * $w2); + + $alpha = $color1->alpha * $p + $color2->alpha * (1 - $p); + + return new Less_Tree_Color($rgb, $alpha); + } + + public function greyscale($color){ + return $this->desaturate($color, new Less_Tree_Dimension(100,'%')); + } + + + public function contrast( $color, $dark = null, $light = null, $threshold = null){ + // filter: contrast(3.2); + // should be kept as is, so check for color + if (!$color instanceof Less_Tree_Color) { + return null; + } + if( !$light ){ + $light = $this->rgba(255, 255, 255, 1.0); + } + if( !$dark ){ + $dark = $this->rgba(0, 0, 0, 1.0); + } + + if (!$dark instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The second argument to contrast must be a color' . ($dark instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + if (!$light instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The third argument to contrast must be a color' . ($light instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + //Figure out which is actually light and dark! + if( $dark->luma() > $light->luma() ){ + $t = $light; + $light = $dark; + $dark = $t; + } + if( !$threshold ){ + $threshold = 0.43; + } else { + $threshold = Less_Functions::number($threshold); + } + + if( $color->luma() < $threshold ){ + return $light; + } else { + return $dark; + } + } + + public function e ($str){ + if( is_string($str) ){ + return new Less_Tree_Anonymous($str); + } + return new Less_Tree_Anonymous($str instanceof Less_Tree_JavaScript ? $str->expression : $str->value); + } + + public function escape ($str){ + + $revert = array('%21'=>'!', '%2A'=>'*', '%27'=>"'",'%3F'=>'?','%26'=>'&','%2C'=>',','%2F'=>'/','%40'=>'@','%2B'=>'+','%24'=>'$'); + + return new Less_Tree_Anonymous(strtr(rawurlencode($str->value), $revert)); + } + + + /** + * todo: This function will need some additional work to make it work the same as less.js + * + */ + public function replace( $string, $pattern, $replacement, $flags = null ){ + $result = $string->value; + + $expr = '/'.str_replace('/','\\/',$pattern->value).'/'; + if( $flags && $flags->value){ + $expr .= self::replace_flags($flags->value); + } + + $result = preg_replace($expr,$replacement->value,$result); + + + if( property_exists($string,'quote') ){ + return new Less_Tree_Quoted( $string->quote, $result, $string->escaped); + } + return new Less_Tree_Quoted( '', $result ); + } + + public static function replace_flags($flags){ + $flags = str_split($flags,1); + $new_flags = ''; + + foreach($flags as $flag){ + switch($flag){ + case 'e': + case 'g': + break; + + default: + $new_flags .= $flag; + break; + } + } + + return $new_flags; + } + + public function _percent(){ + $string = func_get_arg(0); + + $args = func_get_args(); + array_shift($args); + $result = $string->value; + + foreach($args as $arg){ + if( preg_match('/%[sda]/i',$result, $token) ){ + $token = $token[0]; + $value = stristr($token, 's') ? $arg->value : $arg->toCSS(); + $value = preg_match('/[A-Z]$/', $token) ? urlencode($value) : $value; + $result = preg_replace('/%[sda]/i',$value, $result, 1); + } + } + $result = str_replace('%%', '%', $result); + + return new Less_Tree_Quoted( $string->quote , $result, $string->escaped); + } + + public function unit( $val, $unit = null) { + if( !($val instanceof Less_Tree_Dimension) ){ + throw new Less_Exception_Compiler('The first argument to unit must be a number' . ($val instanceof Less_Tree_Operation ? '. Have you forgotten parenthesis?' : '.') ); + } + + if( $unit ){ + if( $unit instanceof Less_Tree_Keyword ){ + $unit = $unit->value; + } else { + $unit = $unit->toCSS(); + } + } else { + $unit = ""; + } + return new Less_Tree_Dimension($val->value, $unit ); + } + + public function convert($val, $unit){ + return $val->convertTo($unit->value); + } + + public function round($n, $f = false) { + + $fraction = 0; + if( $f !== false ){ + $fraction = $f->value; + } + + return $this->_math('Less_Parser::round',null, $n, $fraction); + } + + public function pi(){ + return new Less_Tree_Dimension(M_PI); + } + + public function mod($a, $b) { + return new Less_Tree_Dimension( $a->value % $b->value, $a->unit); + } + + + + public function pow($x, $y) { + if( is_numeric($x) && is_numeric($y) ){ + $x = new Less_Tree_Dimension($x); + $y = new Less_Tree_Dimension($y); + }elseif( !($x instanceof Less_Tree_Dimension) || !($y instanceof Less_Tree_Dimension) ){ + throw new Less_Exception_Compiler('Arguments must be numbers'); + } + + return new Less_Tree_Dimension( pow($x->value, $y->value), $x->unit ); + } + + // var mathFunctions = [{name:"ce ... + public function ceil( $n ){ return $this->_math('ceil', null, $n); } + public function floor( $n ){ return $this->_math('floor', null, $n); } + public function sqrt( $n ){ return $this->_math('sqrt', null, $n); } + public function abs( $n ){ return $this->_math('abs', null, $n); } + + public function tan( $n ){ return $this->_math('tan', '', $n); } + public function sin( $n ){ return $this->_math('sin', '', $n); } + public function cos( $n ){ return $this->_math('cos', '', $n); } + + public function atan( $n ){ return $this->_math('atan', 'rad', $n); } + public function asin( $n ){ return $this->_math('asin', 'rad', $n); } + public function acos( $n ){ return $this->_math('acos', 'rad', $n); } + + private function _math() { + $args = func_get_args(); + $fn = array_shift($args); + $unit = array_shift($args); + + if ($args[0] instanceof Less_Tree_Dimension) { + + if( $unit === null ){ + $unit = $args[0]->unit; + }else{ + $args[0] = $args[0]->unify(); + } + $args[0] = (float)$args[0]->value; + return new Less_Tree_Dimension( call_user_func_array($fn, $args), $unit); + } else if (is_numeric($args[0])) { + return call_user_func_array($fn,$args); + } else { + throw new Less_Exception_Compiler("math functions take numbers as parameters"); + } + } + + /** + * @param boolean $isMin + */ + private function _minmax( $isMin, $args ){ + + $arg_count = count($args); + + if( $arg_count < 1 ){ + throw new Less_Exception_Compiler( 'one or more arguments required'); + } + + $j = null; + $unitClone = null; + $unitStatic = null; + + + $order = array(); // elems only contains original argument values. + $values = array(); // key is the unit.toString() for unified tree.Dimension values, + // value is the index into the order array. + + + for( $i = 0; $i < $arg_count; $i++ ){ + $current = $args[$i]; + if( !($current instanceof Less_Tree_Dimension) ){ + if( is_array($args[$i]->value) ){ + $args[] = $args[$i]->value; + } + continue; + } + + if( $current->unit->toString() === '' && !$unitClone ){ + $temp = new Less_Tree_Dimension($current->value, $unitClone); + $currentUnified = $temp->unify(); + }else{ + $currentUnified = $current->unify(); + } + + if( $currentUnified->unit->toString() === "" && !$unitStatic ){ + $unit = $unitStatic; + }else{ + $unit = $currentUnified->unit->toString(); + } + + if( $unit !== '' && !$unitStatic || $unit !== '' && $order[0]->unify()->unit->toString() === "" ){ + $unitStatic = $unit; + } + + if( $unit != '' && !$unitClone ){ + $unitClone = $current->unit->toString(); + } + + if( isset($values['']) && $unit !== '' && $unit === $unitStatic ){ + $j = $values['']; + }elseif( isset($values[$unit]) ){ + $j = $values[$unit]; + }else{ + + if( $unitStatic && $unit !== $unitStatic ){ + throw new Less_Exception_Compiler( 'incompatible types'); + } + $values[$unit] = count($order); + $order[] = $current; + continue; + } + + + if( $order[$j]->unit->toString() === "" && $unitClone ){ + $temp = new Less_Tree_Dimension( $order[$j]->value, $unitClone); + $referenceUnified = $temp->unify(); + }else{ + $referenceUnified = $order[$j]->unify(); + } + if( ($isMin && $currentUnified->value < $referenceUnified->value) || (!$isMin && $currentUnified->value > $referenceUnified->value) ){ + $order[$j] = $current; + } + } + + if( count($order) == 1 ){ + return $order[0]; + } + $args = array(); + foreach($order as $a){ + $args[] = $a->toCSS($this->env); + } + return new Less_Tree_Anonymous( ($isMin?'min(':'max(') . implode(Less_Environment::$_outputMap[','],$args).')'); + } + + public function min(){ + $args = func_get_args(); + return $this->_minmax( true, $args ); + } + + public function max(){ + $args = func_get_args(); + return $this->_minmax( false, $args ); + } + + public function getunit($n){ + return new Less_Tree_Anonymous($n->unit); + } + + public function argb($color) { + if (!$color instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to argb must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + return new Less_Tree_Anonymous($color->toARGB()); + } + + public function percentage($n) { + return new Less_Tree_Dimension($n->value * 100, '%'); + } + + public function color($n) { + + if( $n instanceof Less_Tree_Quoted ){ + $colorCandidate = $n->value; + $returnColor = Less_Tree_Color::fromKeyword($colorCandidate); + if( $returnColor ){ + return $returnColor; + } + if( preg_match('/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/',$colorCandidate) ){ + return new Less_Tree_Color(substr($colorCandidate, 1)); + } + throw new Less_Exception_Compiler("argument must be a color keyword or 3/6 digit hex e.g. #FFF"); + } else { + throw new Less_Exception_Compiler("argument must be a string"); + } + } + + + public function iscolor($n) { + return $this->_isa($n, 'Less_Tree_Color'); + } + + public function isnumber($n) { + return $this->_isa($n, 'Less_Tree_Dimension'); + } + + public function isstring($n) { + return $this->_isa($n, 'Less_Tree_Quoted'); + } + + public function iskeyword($n) { + return $this->_isa($n, 'Less_Tree_Keyword'); + } + + public function isurl($n) { + return $this->_isa($n, 'Less_Tree_Url'); + } + + public function ispixel($n) { + return $this->isunit($n, 'px'); + } + + public function ispercentage($n) { + return $this->isunit($n, '%'); + } + + public function isem($n) { + return $this->isunit($n, 'em'); + } + + /** + * @param string $unit + */ + public function isunit( $n, $unit ){ + + if( is_object($unit) && property_exists($unit,'value') ){ + $unit = $unit->value; + } + + return ($n instanceof Less_Tree_Dimension) && $n->unit->is($unit) ? new Less_Tree_Keyword('true') : new Less_Tree_Keyword('false'); + } + + /** + * @param string $type + */ + private function _isa($n, $type) { + return is_a($n, $type) ? new Less_Tree_Keyword('true') : new Less_Tree_Keyword('false'); + } + + public function tint($color, $amount = null) { + return $this->mix( $this->rgb(255,255,255), $color, $amount); + } + + public function shade($color, $amount = null) { + return $this->mix($this->rgb(0, 0, 0), $color, $amount); + } + + public function extract($values, $index ){ + $index = (int)$index->value - 1; // (1-based index) + // handle non-array values as an array of length 1 + // return 'undefined' if index is invalid + if( property_exists($values,'value') && is_array($values->value) ){ + if( isset($values->value[$index]) ){ + return $values->value[$index]; + } + return null; + + }elseif( (int)$index === 0 ){ + return $values; + } + + return null; + } + + public function length($values){ + $n = (property_exists($values,'value') && is_array($values->value)) ? count($values->value) : 1; + return new Less_Tree_Dimension($n); + } + + public function datauri($mimetypeNode, $filePathNode = null ) { + + $filePath = ( $filePathNode ? $filePathNode->value : null ); + $mimetype = $mimetypeNode->value; + + $args = 2; + if( !$filePath ){ + $filePath = $mimetype; + $args = 1; + } + + $filePath = str_replace('\\','/',$filePath); + if( Less_Environment::isPathRelative($filePath) ){ + + if( Less_Parser::$options['relativeUrls'] ){ + $temp = $this->currentFileInfo['currentDirectory']; + } else { + $temp = $this->currentFileInfo['entryPath']; + } + + if( !empty($temp) ){ + $filePath = Less_Environment::normalizePath(rtrim($temp,'/').'/'.$filePath); + } + + } + + + // detect the mimetype if not given + if( $args < 2 ){ + + /* incomplete + $mime = require('mime'); + mimetype = mime.lookup(path); + + // use base 64 unless it's an ASCII or UTF-8 format + var charset = mime.charsets.lookup(mimetype); + useBase64 = ['US-ASCII', 'UTF-8'].indexOf(charset) < 0; + if (useBase64) mimetype += ';base64'; + */ + + $mimetype = Less_Mime::lookup($filePath); + + $charset = Less_Mime::charsets_lookup($mimetype); + $useBase64 = !in_array($charset,array('US-ASCII', 'UTF-8')); + if( $useBase64 ){ $mimetype .= ';base64'; } + + }else{ + $useBase64 = preg_match('/;base64$/',$mimetype); + } + + + if( file_exists($filePath) ){ + $buf = @file_get_contents($filePath); + }else{ + $buf = false; + } + + + // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded + // and the --ieCompat flag is enabled, return a normal url() instead. + $DATA_URI_MAX_KB = 32; + $fileSizeInKB = round( strlen($buf) / 1024 ); + if( $fileSizeInKB >= $DATA_URI_MAX_KB ){ + $url = new Less_Tree_Url( ($filePathNode ? $filePathNode : $mimetypeNode), $this->currentFileInfo); + return $url->compile($this); + } + + if( $buf ){ + $buf = $useBase64 ? base64_encode($buf) : rawurlencode($buf); + $filePath = '"data:' . $mimetype . ',' . $buf . '"'; + } + + return new Less_Tree_Url( new Less_Tree_Anonymous($filePath) ); + } + + //svg-gradient + public function svggradient( $direction ){ + + $throw_message = 'svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]'; + $arguments = func_get_args(); + + if( count($arguments) < 3 ){ + throw new Less_Exception_Compiler( $throw_message ); + } + + $stops = array_slice($arguments,1); + $gradientType = 'linear'; + $rectangleDimension = 'x="0" y="0" width="1" height="1"'; + $useBase64 = true; + $directionValue = $direction->toCSS(); + + + switch( $directionValue ){ + case "to bottom": + $gradientDirectionSvg = 'x1="0%" y1="0%" x2="0%" y2="100%"'; + break; + case "to right": + $gradientDirectionSvg = 'x1="0%" y1="0%" x2="100%" y2="0%"'; + break; + case "to bottom right": + $gradientDirectionSvg = 'x1="0%" y1="0%" x2="100%" y2="100%"'; + break; + case "to top right": + $gradientDirectionSvg = 'x1="0%" y1="100%" x2="100%" y2="0%"'; + break; + case "ellipse": + case "ellipse at center": + $gradientType = "radial"; + $gradientDirectionSvg = 'cx="50%" cy="50%" r="75%"'; + $rectangleDimension = 'x="-50" y="-50" width="101" height="101"'; + break; + default: + throw new Less_Exception_Compiler( "svg-gradient direction must be 'to bottom', 'to right', 'to bottom right', 'to top right' or 'ellipse at center'" ); + } + + $returner = '<?xml version="1.0" ?>' . + '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="100%" viewBox="0 0 1 1" preserveAspectRatio="none">' . + '<' . $gradientType . 'Gradient id="gradient" gradientUnits="userSpaceOnUse" ' . $gradientDirectionSvg . '>'; + + for( $i = 0; $i < count($stops); $i++ ){ + if( is_object($stops[$i]) && property_exists($stops[$i],'value') ){ + $color = $stops[$i]->value[0]; + $position = $stops[$i]->value[1]; + }else{ + $color = $stops[$i]; + $position = null; + } + + if( !($color instanceof Less_Tree_Color) || (!(($i === 0 || $i+1 === count($stops)) && $position === null) && !($position instanceof Less_Tree_Dimension)) ){ + throw new Less_Exception_Compiler( $throw_message ); + } + if( $position ){ + $positionValue = $position->toCSS(); + }elseif( $i === 0 ){ + $positionValue = '0%'; + }else{ + $positionValue = '100%'; + } + $alpha = $color->alpha; + $returner .= '<stop offset="' . $positionValue . '" stop-color="' . $color->toRGB() . '"' . ($alpha < 1 ? ' stop-opacity="' . $alpha . '"' : '') . '/>'; + } + + $returner .= '</' . $gradientType . 'Gradient><rect ' . $rectangleDimension . ' fill="url(#gradient)" /></svg>'; + + + if( $useBase64 ){ + $returner = "'data:image/svg+xml;base64,".base64_encode($returner)."'"; + }else{ + $returner = "'data:image/svg+xml,".$returner."'"; + } + + return new Less_Tree_URL( new Less_Tree_Anonymous( $returner ) ); + } + + + /** + * Php version of javascript's `encodeURIComponent` function + * + * @param string $string The string to encode + * @return string The encoded string + */ + public static function encodeURIComponent($string){ + $revert = array('%21' => '!', '%2A' => '*', '%27' => "'", '%28' => '(', '%29' => ')'); + return strtr(rawurlencode($string), $revert); + } + + + // Color Blending + // ref: http://www.w3.org/TR/compositing-1 + + public function colorBlend( $mode, $color1, $color2 ){ + $ab = $color1->alpha; // backdrop + $as = $color2->alpha; // source + $r = array(); // result + + $ar = $as + $ab * (1 - $as); + for( $i = 0; $i < 3; $i++ ){ + $cb = $color1->rgb[$i] / 255; + $cs = $color2->rgb[$i] / 255; + $cr = call_user_func( $mode, $cb, $cs ); + if( $ar ){ + $cr = ($as * $cs + $ab * ($cb - $as * ($cb + $cs - $cr))) / $ar; + } + $r[$i] = $cr * 255; + } + + return new Less_Tree_Color($r, $ar); + } + + public function multiply($color1 = null, $color2 = null ){ + if (!$color1 instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to multiply must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + if (!$color2 instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The second argument to multiply must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + return $this->colorBlend( array($this,'colorBlendMultiply'), $color1, $color2 ); + } + + private function colorBlendMultiply($cb, $cs){ + return $cb * $cs; + } + + public function screen($color1 = null, $color2 = null ){ + if (!$color1 instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to screen must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + if (!$color2 instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The second argument to screen must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + return $this->colorBlend( array($this,'colorBlendScreen'), $color1, $color2 ); + } + + private function colorBlendScreen( $cb, $cs){ + return $cb + $cs - $cb * $cs; + } + + public function overlay($color1 = null, $color2 = null){ + if (!$color1 instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to overlay must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + if (!$color2 instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The second argument to overlay must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + return $this->colorBlend( array($this,'colorBlendOverlay'), $color1, $color2 ); + } + + private function colorBlendOverlay($cb, $cs ){ + $cb *= 2; + return ($cb <= 1) + ? $this->colorBlendMultiply($cb, $cs) + : $this->colorBlendScreen($cb - 1, $cs); + } + + public function softlight($color1 = null, $color2 = null){ + if (!$color1 instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to softlight must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + if (!$color2 instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The second argument to softlight must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + return $this->colorBlend( array($this,'colorBlendSoftlight'), $color1, $color2 ); + } + + private function colorBlendSoftlight($cb, $cs ){ + $d = 1; + $e = $cb; + if( $cs > 0.5 ){ + $e = 1; + $d = ($cb > 0.25) ? sqrt($cb) + : ((16 * $cb - 12) * $cb + 4) * $cb; + } + return $cb - (1 - 2 * $cs) * $e * ($d - $cb); + } + + public function hardlight($color1 = null, $color2 = null){ + if (!$color1 instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to hardlight must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + if (!$color2 instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The second argument to hardlight must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + return $this->colorBlend( array($this,'colorBlendHardlight'), $color1, $color2 ); + } + + private function colorBlendHardlight( $cb, $cs ){ + return $this->colorBlendOverlay($cs, $cb); + } + + public function difference($color1 = null, $color2 = null) { + if (!$color1 instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to difference must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + if (!$color2 instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The second argument to difference must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + return $this->colorBlend( array($this,'colorBlendDifference'), $color1, $color2 ); + } + + private function colorBlendDifference( $cb, $cs ){ + return abs($cb - $cs); + } + + public function exclusion( $color1 = null, $color2 = null ){ + if (!$color1 instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to exclusion must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + if (!$color2 instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The second argument to exclusion must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + return $this->colorBlend( array($this,'colorBlendExclusion'), $color1, $color2 ); + } + + private function colorBlendExclusion( $cb, $cs ){ + return $cb + $cs - 2 * $cb * $cs; + } + + public function average($color1 = null, $color2 = null){ + if (!$color1 instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to average must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + if (!$color2 instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The second argument to average must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + return $this->colorBlend( array($this,'colorBlendAverage'), $color1, $color2 ); + } + + // non-w3c functions: + public function colorBlendAverage($cb, $cs ){ + return ($cb + $cs) / 2; + } + + public function negation($color1 = null, $color2 = null ){ + if (!$color1 instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to negation must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + if (!$color2 instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The second argument to negation must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + return $this->colorBlend( array($this,'colorBlendNegation'), $color1, $color2 ); + } + + public function colorBlendNegation($cb, $cs){ + return 1 - abs($cb + $cs - 1); + } + + // ~ End of Color Blending + +} |