SassScriptFunctions.php in Sassy 7
File
phamlp/sass/script/SassScriptFunctions.phpView source
<?php
/**
* SassScript functions class file.
*
* Methods in this module are accessible from the SassScript context.
* For example, you can write:
*
* $colour = hsl(120, 100%, 50%)
* and it will call SassFunctions::hsl().
*
* There are a few things to keep in mind when modifying this module.
* First of all, the arguments passed are SassLiteral objects.
* Literal objects are also expected to be returned.
*
* Most Literal objects support the SassLiteral->value accessor
* for getting their values. Colour objects, though, must be accessed using
* SassColour::rgb().
*
* Second, making functions accessible from Sass introduces the temptation
* to do things like database access within stylesheets.
* This temptation must be resisted.
* Keep in mind that Sass stylesheets are only compiled once and then left as
* static CSS files. Any dynamic CSS should be left in <style> tags in the
* HTML.
*
* @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.script
*/
/**
* SassScript functions class.
* A collection of functions for use in SassSCript.
* @package PHamlP
* @subpackage Sass.script
*/
class SassScriptFunctions {
const DECREASE = false;
const INCREASE = true;
/*
* Colour Creation
*/
/**
* Creates a SassColour object from red, green, and blue values.
* @param SassNumber the red component.
* A number between 0 and 255 inclusive, or between 0% and 100% inclusive
* @param SassNumber the green component.
* A number between 0 and 255 inclusive, or between 0% and 100% inclusive
* @param SassNumber the blue component.
* A number between 0 and 255 inclusive, or between 0% and 100% inclusive
* @return new SassColour SassColour object
* @throws SassScriptFunctionException if red, green, or blue are out of bounds
*/
public static function rgb($red, $green, $blue) {
return self::rgba($red, $green, $blue, new SassNumber(1));
}
/**
* Creates a SassColour object from red, green, and blue values and alpha
* channel (opacity).
* There are two overloads:
* * rgba(red, green, blue, alpha)
* @param SassNumber the red component.
* A number between 0 and 255 inclusive, or between 0% and 100% inclusive
* @param SassNumber the green component.
* A number between 0 and 255 inclusive, or between 0% and 100% inclusive
* @param SassNumber the blue component.
* A number between 0 and 255 inclusive, or between 0% and 100% inclusive
* @param SassNumber The alpha channel. A number between 0 and 1.
*
* * rgba(colour, alpha)
* @param SassColour a SassColour object
* @param SassNumber The alpha channel. A number between 0 and 1.
*
* @return new SassColour SassColour object
* @throws SassScriptFunctionException if any of the red, green, or blue
* colour components are out of bounds, or or the colour is not a colour, or
* alpha is out of bounds
*/
public static function rgba() {
switch (func_num_args()) {
case 2:
$colour = func_get_arg(0);
$alpha = func_get_arg(1);
SassLiteral::assertType($colour, 'SassColour');
SassLiteral::assertType($alpha, 'SassNumber');
SassLiteral::assertInRange($alpha, 0, 1);
return $colour
->with(array(
'alpha' => $alpha->value,
));
break;
case 4:
$rgba = array();
$components = func_get_args();
$alpha = array_pop($components);
foreach ($components as $component) {
SassLiteral::assertType($component, 'SassNumber');
if ($component->units == '%') {
SassLiteral::assertInRange($component, 0, 100, '%');
$rgba[] = $component->value * 2.55;
}
else {
SassLiteral::assertInRange($component, 0, 255);
$rgba[] = $component->value;
}
}
SassLiteral::assertType($alpha, 'SassNumber');
SassLiteral::assertInRange($alpha, 0, 1);
$rgba[] = $alpha->value;
return new SassColour($rgba);
break;
default:
throw new SassScriptFunctionException('Incorrect argument count for {method}; expected {expected}, received {received}', array(
'{method}' => __METHOD__,
'{expected}' => '2 or 4',
'{received}' => func_num_args(),
), SassScriptParser::$context->node);
}
}
/**
* Creates a SassColour object from hue, saturation, and lightness.
* Uses the algorithm from the
* {@link http://www.w3.org/TR/css3-colour/#hsl-colour CSS3 spec}.
* @param float The hue of the colour in degrees.
* Should be between 0 and 360 inclusive
* @param mixed The saturation of the colour as a percentage.
* Must be between '0%' and 100%, inclusive
* @param mixed The lightness of the colour as a percentage.
* Must be between 0% and 100%, inclusive
* @return new SassColour The resulting colour
* @throws SassScriptFunctionException if saturation or lightness are out of bounds
*/
public static function hsl($h, $s, $l) {
return self::hsla($h, $s, $l, new SassNumber(1));
}
/**
* Creates a SassColour object from hue, saturation, lightness and alpha
* channel (opacity).
* @param SassNumber The hue of the colour in degrees.
* Should be between 0 and 360 inclusive
* @param SassNumber The saturation of the colour as a percentage.
* Must be between 0% and 100% inclusive
* @param SassNumber The lightness of the colour as a percentage.
* Must be between 0% and 100% inclusive
* @param float The alpha channel. A number between 0 and 1.
* @return new SassColour The resulting colour
* @throws SassScriptFunctionException if saturation, lightness or alpha are
* out of bounds
*/
public static function hsla($h, $s, $l, $a) {
SassLiteral::assertType($h, 'SassNumber');
SassLiteral::assertType($s, 'SassNumber');
SassLiteral::assertType($l, 'SassNumber');
SassLiteral::assertType($a, 'SassNumber');
SassLiteral::assertInRange($s, 0, 100, '%');
SassLiteral::assertInRange($l, 0, 100, '%');
SassLiteral::assertInRange($a, 0, 1);
return new SassColour(array(
'hue' => $h,
'saturation' => $s,
'lightness' => $l,
'alpha' => $a,
));
}
/*
* Colour Information
*/
/**
* Returns the red component of a colour.
* @param SassColour The colour
* @return new SassNumber The red component of colour
* @throws SassScriptFunctionException If $colour is not a colour
*/
public static function red($colour) {
SassLiteral::assertType($colour, 'SassColour');
return new SassNumber($colour->red);
}
/**
* Returns the green component of a colour.
* @param SassColour The colour
* @return new SassNumber The green component of colour
* @throws SassScriptFunctionException If $colour is not a colour
*/
public static function green($colour) {
SassLiteral::assertType($colour, 'SassColour');
return new SassNumber($colour->green);
}
/**
* Returns the blue component of a colour.
* @param SassColour The colour
* @return new SassNumber The blue component of colour
* @throws SassScriptFunctionException If $colour is not a colour
*/
public static function blue($colour) {
SassLiteral::assertType($colour, 'SassColour');
return new SassNumber($colour->blue);
}
/**
* Returns the hue component of a colour.
* @param SassColour The colour
* @return new SassNumber The hue component of colour
* @throws SassScriptFunctionException If $colour is not a colour
*/
public static function hue($colour) {
SassLiteral::assertType($colour, 'SassColour');
return new SassNumber($colour->hue);
}
/**
* Returns the saturation component of a colour.
* @param SassColour The colour
* @return new SassNumber The saturation component of colour
* @throws SassScriptFunctionException If $colour is not a colour
*/
public static function saturation($colour) {
SassLiteral::assertType($colour, 'SassColour');
return new SassNumber($colour->saturation);
}
/**
* Returns the lightness component of a colour.
* @param SassColour The colour
* @return new SassNumber The lightness component of colour
* @throws SassScriptFunctionException If $colour is not a colour
*/
public static function lightness($colour) {
SassLiteral::assertType($colour, 'SassColour');
return new SassNumber($colour->lightness);
}
/**
* Returns the alpha component (opacity) of a colour.
* @param SassColour The colour
* @return new SassNumber The alpha component (opacity) of colour
* @throws SassScriptFunctionException If $colour is not a colour
*/
public static function alpha($colour) {
SassLiteral::assertType($colour, 'SassColour');
return new SassNumber($colour->alpha);
}
/**
* Returns the alpha component (opacity) of a colour.
* @param SassColour The colour
* @return new SassNumber The alpha component (opacity) of colour
* @throws SassScriptFunctionException If $colour is not a colour
*/
public static function opacity($colour) {
SassLiteral::assertType($colour, 'SassColour');
return new SassNumber($colour->alpha);
}
/*
* Colour Adjustments
*/
/**
* Changes the hue of a colour while retaining the lightness and saturation.
* @param SassColour The colour to adjust
* @param SassNumber The amount to adjust the colour by
* @return new SassColour The adjusted colour
* @throws SassScriptFunctionException If $colour is not a colour or
* $degrees is not a number
*/
public static function adjust_hue($colour, $degrees) {
SassLiteral::assertType($colour, 'SassColour');
SassLiteral::assertType($degrees, 'SassNumber');
return $colour
->with(array(
'hue' => $colour->hue + $degrees->value,
));
}
/**
* Makes a colour lighter.
* @param SassColour The colour to lighten
* @param SassNumber The amount to lighten the colour by
* @param SassBoolean Whether the amount is a proportion of the current value
* (true) or the total range (false).
* The default is false - the amount is a proportion of the total range.
* If the colour lightness value is 40% and the amount is 50%,
* the resulting colour lightness value is 90% if the amount is a proportion
* of the total range, whereas it is 60% if the amount is a proportion of the
* current value.
* @return new SassColour The lightened colour
* @throws SassScriptFunctionException If $colour is not a colour or
* $amount is not a number
* @see lighten_rel
*/
public static function lighten($colour, $amount, $ofCurrent = false) {
return self::adjust($colour, $amount, $ofCurrent, 'lightness', self::INCREASE, 0, 100, '%');
}
/**
* Makes a colour darker.
* @param SassColour The colour to darken
* @param SassNumber The amount to darken the colour by
* @param SassBoolean Whether the amount is a proportion of the current value
* (true) or the total range (false).
* The default is false - the amount is a proportion of the total range.
* If the colour lightness value is 80% and the amount is 50%,
* the resulting colour lightness value is 30% if the amount is a proportion
* of the total range, whereas it is 40% if the amount is a proportion of the
* current value.
* @return new SassColour The darkened colour
* @throws SassScriptFunctionException If $colour is not a colour or
* $amount is not a number
* @see adjust
*/
public static function darken($colour, $amount, $ofCurrent = false) {
return self::adjust($colour, $amount, $ofCurrent, 'lightness', self::DECREASE, 0, 100, '%');
}
/**
* Makes a colour more saturated.
* @param SassColour The colour to saturate
* @param SassNumber The amount to saturate the colour by
* @param SassBoolean Whether the amount is a proportion of the current value
* (true) or the total range (false).
* The default is false - the amount is a proportion of the total range.
* If the colour saturation value is 40% and the amount is 50%,
* the resulting colour saturation value is 90% if the amount is a proportion
* of the total range, whereas it is 60% if the amount is a proportion of the
* current value.
* @return new SassColour The saturated colour
* @throws SassScriptFunctionException If $colour is not a colour or
* $amount is not a number
* @see adjust
*/
public static function saturate($colour, $amount, $ofCurrent = false) {
return self::adjust($colour, $amount, $ofCurrent, 'saturation', self::INCREASE, 0, 100, '%');
}
/**
* Makes a colour less saturated.
* @param SassColour The colour to desaturate
* @param SassNumber The amount to desaturate the colour by
* @param SassBoolean Whether the amount is a proportion of the current value
* (true) or the total range (false).
* The default is false - the amount is a proportion of the total range.
* If the colour saturation value is 80% and the amount is 50%,
* the resulting colour saturation value is 30% if the amount is a proportion
* of the total range, whereas it is 40% if the amount is a proportion of the
* current value.
* @return new SassColour The desaturateed colour
* @throws SassScriptFunctionException If $colour is not a colour or
* $amount is not a number
* @see adjust
*/
public static function desaturate($colour, $amount, $ofCurrent = false) {
return self::adjust($colour, $amount, $ofCurrent, 'saturation', self::DECREASE, 0, 100, '%');
}
/**
* Makes a colour more opaque.
* @param SassColour The colour to opacify
* @param SassNumber The amount to opacify the colour by
* If this is a unitless number between 0 and 1 the adjustment is absolute,
* if it is a percentage the adjustment is relative.
* If the colour alpha value is 0.4
* if the amount is 0.5 the resulting colour alpha value is 0.9,
* whereas if the amount is 50% the resulting colour alpha value is 0.6.
* @return new SassColour The opacified colour
* @throws SassScriptFunctionException If $colour is not a colour or
* $amount is not a number
* @see opacify_rel
*/
public static function opacify($colour, $amount, $ofCurrent = false) {
$units = self::units($amount);
return self::adjust($colour, $amount, $ofCurrent, 'alpha', self::INCREASE, 0, $units === '%' ? 100 : 1, $units);
}
/**
* Makes a colour more transparent.
* @param SassColour The colour to transparentize
* @param SassNumber The amount to transparentize the colour by.
* If this is a unitless number between 0 and 1 the adjustment is absolute,
* if it is a percentage the adjustment is relative.
* If the colour alpha value is 0.8
* if the amount is 0.5 the resulting colour alpha value is 0.3,
* whereas if the amount is 50% the resulting colour alpha value is 0.4.
* @return new SassColour The transparentized colour
* @throws SassScriptFunctionException If $colour is not a colour or
* $amount is not a number
*/
public static function transparentize($colour, $amount, $ofCurrent = false) {
$units = self::units($amount);
return self::adjust($colour, $amount, $ofCurrent, 'alpha', self::DECREASE, 0, $units === '%' ? 100 : 1, $units);
}
/**
* Makes a colour more opaque.
* Alias for {@link opacify}.
* @param SassColour The colour to opacify
* @param SassNumber The amount to opacify the colour by
* @param SassBoolean Whether the amount is a proportion of the current value
* (true) or the total range (false).
* @return new SassColour The opacified colour
* @throws SassScriptFunctionException If $colour is not a colour or
* $amount is not a number
* @see opacify
*/
public static function fade_in($colour, $amount, $ofCurrent = false) {
return self::opacify($colour, $amount, $ofCurrent);
}
/**
* Makes a colour more transparent.
* Alias for {@link transparentize}.
* @param SassColour The colour to transparentize
* @param SassNumber The amount to transparentize the colour by
* @param SassBoolean Whether the amount is a proportion of the current value
* (true) or the total range (false).
* @return new SassColour The transparentized colour
* @throws SassScriptFunctionException If $colour is not a colour or
* $amount is not a number
* @see transparentize
*/
public static function fade_out($colour, $amount, $ofCurrent = false) {
return self::transparentize($colour, $amount, $ofCurrent);
}
/**
* Returns the complement of a colour.
* Rotates the hue by 180 degrees.
* @param SassColour The colour
* @return new SassColour The comlemented colour
* @uses adjust_hue()
*/
public static function complement($colour) {
return self::adjust_hue($colour, new SassNumber('180deg'));
}
/**
* Greyscale for non-english speakers.
* @param SassColour The colour
* @return new SassColour The greyscale colour
* @see desaturate
*/
public static function grayscale($colour) {
return self::desaturate($colour, new SassNumber(100));
}
/**
* Converts a colour to greyscale.
* Reduces the saturation to zero.
* @param SassColour The colour
* @return new SassColour The greyscale colour
* @see desaturate
*/
public static function greyscale($colour) {
return self::desaturate($colour, new SassNumber(100));
}
/**
* Mixes two colours together.
* Takes the average of each of the RGB components, optionally weighted by the
* given percentage. The opacity of the colours is also considered when
* weighting the components.
* The weight specifies the amount of the first colour that should be included
* in the returned colour. The default, 50%, means that half the first colour
* and half the second colour should be used. 25% means that a quarter of the
* first colour and three quarters of the second colour should be used.
* For example:
* mix(#f00, #00f) => #7f007f
* mix(#f00, #00f, 25%) => #3f00bf
* mix(rgba(255, 0, 0, 0.5), #00f) => rgba(63, 0, 191, 0.75)
*
* @param SassColour The first colour
* @param SassColour The second colour
* @param float Percentage of the first colour to use
* @return new SassColour The mixed colour
* @throws SassScriptFunctionException If $colour1 or $colour2 is
* not a colour
*/
public static function mix($colour1, $colour2, $weight = null) {
if (is_null($weight)) {
$weight = new SassNumber('50%');
}
SassLiteral::assertType($colour1, 'SassColour');
SassLiteral::assertType($colour2, 'SassColour');
SassLiteral::assertType($weight, 'SassNumber');
SassLiteral::assertInRange($weight, 0, 100, '%');
/*
* This algorithm factors in both the user-provided weight
* and the difference between the alpha values of the two colours
* to decide how to perform the weighted average of the two RGB values.
*
* It works by first normalizing both parameters to be within [-1, 1],
* where 1 indicates "only use colour1", -1 indicates "only use colour 0",
* and all values in between indicated a proportionately weighted average.
*
* Once we have the normalized variables w and a,
* we apply the formula (w + a)/(1 + w*a)
* to get the combined weight (in [-1, 1]) of colour1.
* This formula has two especially nice properties:
*
* * When either w or a are -1 or 1, the combined weight is also that number
* (cases where w * a == -1 are undefined, and handled as a special case).
*
* * When a is 0, the combined weight is w, and vice versa
*
* Finally, the weight of colour1 is renormalized to be within [0, 1]
* and the weight of colour2 is given by 1 minus the weight of colour1.
*/
$p = $weight->value / 100;
$w = $p * 2 - 1;
$a = $colour1->alpha - $colour2->alpha;
$w1 = (($w * $a == -1 ? $w : ($w + $a) / (1 + $w * $a)) + 1) / 2;
$w2 = 1 - $w1;
$rgb1 = $colour1
->rgb();
$rgb2 = $colour2
->rgb();
$rgba = array();
foreach ($rgb1 as $key => $value) {
$rgba[$key] = $value * $w1 + $rgb2[$key] * $w2;
}
// foreach
$rgba[] = $colour1->alpha * $p + $colour2->alpha * (1 - $p);
return new SassColour($rgba);
}
/**
* Adjusts the colour
* @param SassColour the colour to adjust
* @param SassNumber the amount to adust by
* @param boolean whether the amount is a proportion of the current value or
* the total range
* @param string the attribute to adjust
* @param boolean whether to decrease (false) or increase (true) the value of the attribute
* @param float minimum value the amount can be
* @param float maximum value the amount can bemixed
* @param string amount units
*/
private static function adjust($colour, $amount, $ofCurrent, $attribute, $op, $min, $max, $units = '') {
SassLiteral::assertType($colour, 'SassColour');
SassLiteral::assertType($amount, 'SassNumber');
SassLiteral::assertInRange($amount, $min, $max, $units);
if (!is_bool($ofCurrent)) {
SassLiteral::assertType($ofCurrent, 'SassBoolean');
$ofCurrent = $ofCurrent->value;
}
$amount = $amount->value * ($attribute === 'alpha' && $ofCurrent && $units === '' ? 100 : 1);
return $colour
->with(array(
$attribute => self::inRange($ofCurrent ? $colour->{$attribute} * (1 + $amount * ($op === self::INCREASE ? 1 : -1) / 100) : $colour->{$attribute} + $amount * ($op === self::INCREASE ? 1 : -1), $min, $max),
));
}
/*
* Number Functions
*/
/**
* Finds the absolute value of a number.
* For example:
* abs(10px) => 10px
* abs(-10px) => 10px
*
* @param SassNumber The number to round
* @return SassNumber The absolute value of the number
* @throws SassScriptFunctionException If $number is not a number
*/
public static function abs($number) {
SassLiteral::assertType($number, 'SassNumber');
return new SassNumber(abs($number->value) . $number->units);
}
/**
* Rounds a number up to the nearest whole number.
* For example:
* ceil(10.4px) => 11px
* ceil(10.6px) => 11px
*
* @param SassNumber The number to round
* @return new SassNumber The rounded number
* @throws SassScriptFunctionException If $number is not a number
*/
public static function ceil($number) {
SassLiteral::assertType($number, 'SassNumber');
return new SassNumber(ceil($number->value) . $number->units);
}
/**
* Rounds down to the nearest whole number.
* For example:
* floor(10.4px) => 10px
* floor(10.6px) => 10px
*
* @param SassNumber The number to round
* @return new SassNumber The rounded number
* @throws SassScriptFunctionException If $value is not a number
*/
public static function floor($number) {
SassLiteral::assertType($number, 'SassNumber');
return new SassNumber(floor($number->value) . $number->units);
}
/**
* Rounds a number to the nearest whole number.
* For example:
* round(10.4px) => 10px
* round(10.6px) => 11px
*
* @param SassNumber The number to round
* @return new SassNumber The rounded number
* @throws SassScriptFunctionException If $number is not a number
*/
public static function round($number) {
SassLiteral::assertType($number, 'SassNumber');
return new SassNumber(round($number->value) . $number->units);
}
/**
* Returns true if two numbers are similar enough to be added, subtracted,
* or compared.
* @param SassNumber The first number to test
* @param SassNumber The second number to test
* @return new SassBoolean True if the numbers are similar
* @throws SassScriptFunctionException If $number1 or $number2 is not
* a number
*/
public static function comparable($number1, $number2) {
SassLiteral::assertType($number1, 'SassNumber');
SassLiteral::assertType($number2, 'SassNumber');
return new SassBoolean($number1
->isComparableTo($number2));
}
/**
* Converts a decimal number to a percentage.
* For example:
* percentage(100px / 50px) => 200%
*
* @param SassNumber The decimal number to convert to a percentage
* @return new SassNumber The number as a percentage
* @throws SassScriptFunctionException If $number isn't a unitless number
*/
public static function percentage($number) {
if (!$number instanceof SassNumber || $number
->hasUnits()) {
throw new SassScriptFunctionException('{what} must be a {type}', array(
'{what}' => 'number',
'{type}' => 'unitless SassNumber',
), SassScriptParser::$context->node);
}
$number->value *= 100;
$number->units = '%';
return $number;
}
/**
* Inspects the unit of the number, returning it as a quoted string.
* Alias for units.
* @param SassNumber The number to inspect
* @return new SassString The units of the number
* @throws SassScriptFunctionException If $number is not a number
* @see units
*/
public static function unit($number) {
return self::units($number);
}
/**
* Inspects the units of the number, returning it as a quoted string.
* @param SassNumber The number to inspect
* @return new SassString The units of the number
* @throws SassScriptFunctionException If $number is not a number
*/
public static function units($number) {
SassLiteral::assertType($number, 'SassNumber');
return new SassString($number->units);
}
/**
* Inspects the unit of the number, returning a boolean indicating if it is
* unitless.
* @param SassNumber The number to inspect
* @return new SassBoolean True if the number is unitless, false if it has units.
* @throws SassScriptFunctionException If $number is not a number
*/
public static function unitless() {
SassLiteral::assertType($number, 'SassNumber');
return new SassBoolean($number
->isUnitless());
}
/*
* String Functions
*/
/**
* Add quotes to a string if the string isn't quoted,
* or returns the same string if it is.
* @param string String to quote
* @return new SassString Quoted string
* @throws SassScriptFunctionException If $string is not a string
* @see unquote
*/
public static function quote($string) {
SassLiteral::assertType($string, 'SassString');
return new SassString('"' . $string->value . '"');
}
/**
* Removes quotes from a string if the string is quoted, or returns the same
* string if it's not.
* @param string String to unquote
* @return new SassString Unuoted string
* @throws SassScriptFunctionException If $string is not a string
* @see quote
*/
public static function unquote($string) {
SassLiteral::assertType($string, 'SassString');
return new SassString($string->value);
}
/**
* Returns the variable whose name is the string.
* @param string String to unquote
* @return
* @throws SassScriptFunctionException If $string is not a string
*/
public static function get_var($string) {
SassLiteral::assertType($string, 'SassString');
return new SassString($string
->toVar());
}
/*
* Misc. Functions
*/
/**
* Inspects the type of the argument, returning it as an unquoted string.
* @param SassLiteral The object to inspect
* @return new SassString The type of object
* @throws SassScriptFunctionException If $obj is not an instance of a
* SassLiteral
*/
public static function type_of($obj) {
SassLiteral::assertType($obj, SassLiteral);
return new SassString($obj->typeOf);
}
/**
* Ensures the value is within the given range, clipping it if needed.
* @param float the value to test
* @param float the minimum value
* @param float the maximum value
* @return the value clipped to the range
*/
private static function inRange($value, $min, $max) {
return $value < $min ? $min : ($value > $max ? $max : $value);
}
}
Classes
Name | Description |
---|---|
SassScriptFunctions | SassScript functions class. A collection of functions for use in SassSCript. @package PHamlP @subpackage Sass.script |