<?php /* SVN FILE: $Id: SassBoolean.php 49 2010-04-04 10:51:24Z chris.l.yates $ */ /**

* Compass extension SassScript colour stop objects and functions class file.
* @author                      Chris Yates <chris.l.yates@gmail.com>
* @copyright   Copyright (c) 2010 PBM Web Development
* @license                     http://phamlp.googlecode.com/files/license.txt
* @package                     PHamlP
* @subpackage  Sass.extensions.compass.functions
*/

/**

* Compass extension List object.
* @package                     PHamlP
* @subpackage  Sass.extensions.compass.functions
*/

class CompassList extends SassLiteral {

public function __construct($values) {
        $this->value = $values;
}

public function getValues() {
        return $this->value;
}

/**
 * Returns the type of this
 * @return string the type of this
 */
protected function getTypeOf() {
        return 'list';
}

public function toString() {
        $values = array();
        foreach ($this->value as $value) {
                $values[] = $value->toString();
        }
        return join(', ', $values);
}

public static function isa($subject) {}

}

class CompassColourStop extends SassLiteral {

private $colour;
public $stop;

public function __construct($colour, $stop = null) {
        $this->colour = $colour;
        $this->stop = $stop;
}

protected function getColor() {
        return $this->getColour();
}

protected function getColour() {
        return $this->colour;
}

public function toString() {
        $s = $this->colour->toString();
        if (!empty($this->stop)) {
                $s .= ' ';
                if ($this->stop->isUnitless()) {
                        $s .= $this->stop->op_times(new SassNumber('100%'))->toString();
                }
                else {
                        $s .= $this->stop->toString();
                }
        }
        return $s;
}

public static function isa($subject) {}

}

/**

* Compass extension SassScript colour stops functions class.
* A collection of functions for use in SassSCript.
* @package                     PHamlP
* @subpackage  Sass.extensions.compass.functions
*/

class SassExtentionsCompassFunctionsColourStops {

# returns color-stop() calls for use in webkit.
public static function grad_color_stops($colour_list) {
        return self::grad_colour_stops($colour_list);
}

public static function grad_colour_stops($colour_list) {
        SassLiteral::assertType($colour_list, 'CompassList');
        self::normalize_stops($colour_list);
        $v = array_reverse($colour_list->values);
        $max = $v[0]->stop;
        $last_value = null;

        $colourStops = array();

        foreach ($colour_list->values as $pos) {
                # have to convert absolute units to percentages for use in colour stop functions.
                $stop = $pos->stop;
                if ($stop->numeratorUnits === $max->numeratorUnits) {
                        $stop = $stop->op_div($max)->op_times(new SassNumber('100%'));
                }
                # Make sure the colour stops are specified in the right order.
                if ($last_value && $last_value->value > $stop->value) {
                        throw new SassScriptFunctionException('Colour stops must be specified in increasing order', array(), SassScriptParser::$context->node);
                }

                $last_value = $stop;
                $colourStops[] = "colour-stop({$stop->toString()}, {$pos->colour->toString()})";
        }

        return new SassString(join(', ', $colourStops));
}

# returns the end position of the gradient from the colour stop
public static function grad_end_position($colourList, $radial = null) {
        SassLiteral::assertType($colourList, 'CompassList');
        if (is_null($radial)) {
                $radial = new SassBoolean(false);
        }
        else {
                SassLiteral::assertType($radial, 'SassBoolean');
        }
        return self::grad_position($colourList, new SassNumber(sizeof($colourList->values)), new SassNumber(100), $radial);
}

public static function grad_position($colourList, $index, $default, $radial = null) {
        SassLiteral::assertType($colourList, 'CompassList');
        if (is_null($radial)) {
                $radial = new SassBoolean(false);
        }
        else {
                SassLiteral::assertType($radial, 'SassBoolean');
        }
        $stop = $colourList->values[$index->value - 1]->stop;
        if ($stop && $radial->value) {
                $orig_stop = $stop;
                if ($stop->isUnitless()) {
                        if ($stop->value <= 1) {
                                # A unitless number is assumed to be a percentage when it's between 0 and 1
                                $stop = $stop->op_times(new SassNumber('100%'));
                        }
                        else {
                                # Otherwise, a unitless number is assumed to be in pixels
                                $stop = $stop->op_times(new SassNumber('1px'));
                        }
                }

                if ($stop->numeratorUnits === '%' && isset($colourList->values[sizeof($colourList->values)-1]->stop) && $colourList->values[sizeof($colourList->values)-1]->stop->numeratorUnits === 'px')
                        $stop = $stop->op_times($colourList->values[sizeof($colourList->values)-1]->stop)->op_div(new SassNumber('100%'));
                //Compass::Logger.new.record(:warning, "Webkit only supports pixels for the start and end stops for radial gradients. Got: #{orig_stop}") if stop.numerator_units != ["px"];
                return $stop->op_div(new SassNumber('1'.$stop->units));
        }
        elseif ($stop)
                return $stop;
        else
                return $default;
}

# takes the given position and returns a point in percentages
public static function grad_point($position) {
        $position = $position->value;
        if (strpos($position, ' ') !== false) {
                if (preg_match('/(top|bottom|center) (left|right|center)/', $position, $matches)) 
                        $position =  "{$matches[2]} {$matches[1]}";
        }
        else {
                switch ($position) {
                        case 'top':
                        case 'bottom':
                                $position = "left $position";
                                break;
                        case 'left':
                        case 'right':
                                $position .= ' top';
                                break;
                }
        }

        return new SassString(preg_replace(
                array('/top/', '/bottom/', '/left/', '/right/', '/center/'),
                array('0%', '100%', '0%', '100%', '50%'), $position
        ));
}

public static function color_stops() {
        return self::colour_stops(func_get_args());
}

public static function colour_stops() {
        $args = func_get_args();
        $list = array();

        foreach ($args as $arg) {
                if ($arg instanceof SassColour) {
                        $list[] = new CompassColourStop($arg);
                }
                elseif ($arg instanceof SassString) {
                        # We get a string as the result of concatenation
                        # So we have to reparse the expression
                        $colour = $stop = null;
                        if (empty($parser))
                                $parser = new SassScriptParser();
                        $expr = $parser->parse($arg->value, SassScriptParser::$context);

                        $x = array_pop($expr);

                        if ($x instanceof SassColour)
                                $colour = $x;
                        elseif ($x instanceof SassScriptOperation) {
                                if ($x->operator != 'concat')
                                        # This should never happen.
                                        throw new SassScriptFunctionException("Couldn't parse a colour stop from: {value}", array('{value}'=>$arg->value), SassScriptParser::$context->node);
                                $colour = $expr[0];
                                $stop = $expr[1];
                        }
                        else
                                throw new SassScriptFunctionException("Couldn't parse a colour stop from: {value}", array('{value}'=>$arg->value), SassScriptParser::$context->node);
                        $list[] = new CompassColourStop($colour, $stop);
                }
                else
                        throw new SassScriptFunctionException('Not a valid color stop: {arg}', array('{arg}'=>$arg->value), SassScriptParser::$context->node);
        }
        return new CompassList($list);
}

private static function normalize_stops($colourList) {
        $positions = $colourList->values;
        $s = sizeof($positions);

        # fill in the start and end positions, if unspecified
        if (empty($positions[0]->stop))
                $positions[0]->stop = new SassNumber(0);
        if (empty($positions[$s-1]->stop))
                $positions[$s-1]->stop = new SassNumber('100%');

        # fill in empty values
        for ($i = 0; $i<$s; $i++) {
                if (is_null($positions[$i]->stop)) {
                        $num = 2;
                        for ($j = $i+1; $j<$s; $j++) {
                                if (isset($positions[$j]->stop)) {
                                        $positions[$i]->stop = $positions[$i-1]->stop->op_plus($positions[$j]->stop->op_minus($positions[$i-1]->stop))->op_div(new SassNumber($num));
                                        break;
                                }
                                else
                                        $num += 1;
                        }
                }
        }
        # normalize unitless numbers
        foreach ($positions as &$pos) {
                if ($pos->stop->isUnitless()) {
                        $pos->stop = ($pos->stop->value <= 1 ?
                                $pos->stop->op_times(new SassNumber('100%')) :
                                $pos->stop->op_times(new SassNumber('1px'))
                        );
                }
        }
        if ($positions[$s-1]->stop->op_eq(new SassNumber('0px'))->toBoolean() ||
                 $positions[$s-1]->stop->op_eq(new SassNumber('0%'))->toBoolean())
                        throw new SassScriptFunctionException('Colour stops must be specified in increasing order', array(), SassScriptParser::$context->node);
        return null;
}

}