Version Notes
Missing phpsass library added
Download this release
Release Info
Developer | Laurent Clouet |
Extension | Sass |
Version | 1.1.1 |
Comparing to | |
See all releases |
Code changes from version 1.1.0 to 1.1.1
- app/code/community/Laurent/Sass/etc/config.xml +1 -1
- lib/phpsass/.travis.yml +12 -0
- lib/phpsass/README.md +29 -0
- lib/phpsass/SassException.php +27 -0
- lib/phpsass/SassFile.php +154 -0
- lib/phpsass/SassParser.php +841 -0
- lib/phpsass/VERSION +19 -0
- lib/phpsass/compile-apache.php +42 -0
- lib/phpsass/composer.json +38 -0
- lib/phpsass/renderers/SassCompactRenderer.php +132 -0
- lib/phpsass/renderers/SassCompressedRenderer.php +113 -0
- lib/phpsass/renderers/SassExpandedRenderer.php +62 -0
- lib/phpsass/renderers/SassNestedRenderer.php +68 -0
- lib/phpsass/renderers/SassRenderer.php +51 -0
- lib/phpsass/script/SassScriptFunction.php +290 -0
- lib/phpsass/script/SassScriptFunctions.php +963 -0
- lib/phpsass/script/SassScriptLexer.php +129 -0
- lib/phpsass/script/SassScriptOperation.php +189 -0
- lib/phpsass/script/SassScriptParser.php +259 -0
- lib/phpsass/script/SassScriptParserExceptions.php +40 -0
- lib/phpsass/script/SassScriptVariable.php +57 -0
- lib/phpsass/script/literals/SassBoolean.php +67 -0
- lib/phpsass/script/literals/SassColour.php +918 -0
- lib/phpsass/script/literals/SassList.php +219 -0
- lib/phpsass/script/literals/SassLiteral.php +374 -0
- lib/phpsass/script/literals/SassLiteralExceptions.php +47 -0
- lib/phpsass/script/literals/SassNumber.php +538 -0
- lib/phpsass/script/literals/SassString.php +134 -0
- lib/phpsass/test.css +39 -0
- lib/phpsass/test.php +152 -0
- lib/phpsass/tests/_imported_charset_ibm866.sass +4 -0
- lib/phpsass/tests/_imported_charset_utf8.sass +4 -0
- lib/phpsass/tests/_imported_content.sass +3 -0
- lib/phpsass/tests/_partial.sass +2 -0
- lib/phpsass/tests/alt.css +16 -0
- lib/phpsass/tests/alt.sass +16 -0
- lib/phpsass/tests/alt.scss +21 -0
- lib/phpsass/tests/basic.css +21 -0
- lib/phpsass/tests/basic.sass +22 -0
- lib/phpsass/tests/bork1.sass +2 -0
- lib/phpsass/tests/bork2.sass +2 -0
- lib/phpsass/tests/bork3.sass +2 -0
- lib/phpsass/tests/bork4.sass +2 -0
- lib/phpsass/tests/bork5.sass +3 -0
- lib/phpsass/tests/comments.css +20 -0
- lib/phpsass/tests/comments.sass +20 -0
- lib/phpsass/tests/compact.css +13 -0
- lib/phpsass/tests/compact.sass +15 -0
- lib/phpsass/tests/complex.css +290 -0
- lib/phpsass/tests/complex.sass +303 -0
- lib/phpsass/tests/compressed.css +12 -0
- lib/phpsass/tests/compressed.sass +15 -0
- lib/phpsass/tests/content.css +2 -0
- lib/phpsass/tests/content.scss +3 -0
- lib/phpsass/tests/css3.css +7 -0
- lib/phpsass/tests/css3.scss +8 -0
- lib/phpsass/tests/default.css +4 -0
- lib/phpsass/tests/default.sass +11 -0
- lib/phpsass/tests/default_imported.sass +2 -0
- lib/phpsass/tests/each.css +14 -0
- lib/phpsass/tests/each.scss +17 -0
- lib/phpsass/tests/expanded.css +17 -0
- lib/phpsass/tests/expanded.sass +17 -0
- lib/phpsass/tests/extend.css +13 -0
- lib/phpsass/tests/extend.sass +13 -0
- lib/phpsass/tests/extend_included.scss +4 -0
- lib/phpsass/tests/extend_placeholders.css +48 -0
- lib/phpsass/tests/extend_placeholders.scss +58 -0
- lib/phpsass/tests/filters.css +6 -0
- lib/phpsass/tests/filters.scss +10 -0
- lib/phpsass/tests/functions.css +9 -0
- lib/phpsass/tests/functions.scss +16 -0
- lib/phpsass/tests/holmes.css +227 -0
- lib/phpsass/tests/holmes.sass +262 -0
- lib/phpsass/tests/hsl-functions.css +14 -0
- lib/phpsass/tests/hsl-functions.scss +15 -0
- lib/phpsass/tests/if.css +5 -0
- lib/phpsass/tests/if.sass +11 -0
- lib/phpsass/tests/import.css +62 -0
- lib/phpsass/tests/import.sass +15 -0
- lib/phpsass/tests/import_content.css +2 -0
- lib/phpsass/tests/import_content.sass +4 -0
- lib/phpsass/tests/importee.sass +19 -0
- lib/phpsass/tests/interpolation.css +27 -0
- lib/phpsass/tests/interpolation.scss +54 -0
- lib/phpsass/tests/introspection.css +13 -0
- lib/phpsass/tests/introspection.scss +16 -0
- lib/phpsass/tests/line_numbers.css +37 -0
- lib/phpsass/tests/line_numbers.sass +13 -0
- lib/phpsass/tests/list.css +4 -0
- lib/phpsass/tests/list.scss +8 -0
- lib/phpsass/tests/media.css +7 -0
- lib/phpsass/tests/media.scss +15 -0
- lib/phpsass/tests/misc-functions.css +6 -0
- lib/phpsass/tests/misc-functions.scss +11 -0
- lib/phpsass/tests/misc.css +85 -0
- lib/phpsass/tests/misc.scss +103 -0
- lib/phpsass/tests/mixin-content.css +4 -0
- lib/phpsass/tests/mixin-content.sass +8 -0
- lib/phpsass/tests/mixin-content.scss +12 -0
- lib/phpsass/tests/mixin-ja1.css +8 -0
- lib/phpsass/tests/mixin-ja1.sass +14 -0
- lib/phpsass/tests/mixin-params.css +15 -0
- lib/phpsass/tests/mixin-params.scss +20 -0
- lib/phpsass/tests/mixin_bork.sass +5 -0
- lib/phpsass/tests/mixins.css +87 -0
- lib/phpsass/tests/mixins.sass +84 -0
- lib/phpsass/tests/multiline.css +24 -0
- lib/phpsass/tests/multiline.sass +20 -0
- lib/phpsass/tests/nested-media.css +14 -0
- lib/phpsass/tests/nested-media.scss +22 -0
- lib/phpsass/tests/nested.css +24 -0
- lib/phpsass/tests/nested.sass +28 -0
- lib/phpsass/tests/nested_bork1.sass +2 -0
- lib/phpsass/tests/nested_bork2.sass +2 -0
- lib/phpsass/tests/nested_bork3.sass +2 -0
- lib/phpsass/tests/nested_bork4.sass +2 -0
- lib/phpsass/tests/nested_bork5.sass +2 -0
- lib/phpsass/tests/nested_import.css +18 -0
- lib/phpsass/tests/nested_import.sass +2 -0
- lib/phpsass/tests/nested_media.css +5 -0
- lib/phpsass/tests/nested_media.scss +10 -0
- lib/phpsass/tests/nested_mixin_bork.sass +6 -0
- lib/phpsass/tests/nested_pseudo.css +3 -0
- lib/phpsass/tests/nested_pseudo.scss +7 -0
- lib/phpsass/tests/number.css +9 -0
- lib/phpsass/tests/number.scss +10 -0
- lib/phpsass/tests/opacity.css +7 -0
- lib/phpsass/tests/opacity.scss +8 -0
- lib/phpsass/tests/other-color.css +5 -0
- lib/phpsass/tests/other-color.scss +5 -0
- lib/phpsass/tests/parent_ref.css +39 -0
- lib/phpsass/tests/parent_ref.sass +30 -0
- lib/phpsass/tests/phpSassTest.php +312 -0
- lib/phpsass/tests/phpunit.xml.dist +14 -0
- lib/phpsass/tests/proprietary-selector.css +4 -0
- lib/phpsass/tests/proprietary-selector.scss +8 -0
- lib/phpsass/tests/rgb-functions.css +8 -0
- lib/phpsass/tests/rgb-functions.scss +9 -0
- lib/phpsass/tests/scss_import.css +62 -0
- lib/phpsass/tests/scss_import.scss +15 -0
- lib/phpsass/tests/scss_importee.css +2 -0
- lib/phpsass/tests/scss_importee.scss +1 -0
- lib/phpsass/tests/splats.css +5 -0
- lib/phpsass/tests/splats.scss +19 -0
- lib/phpsass/tests/string.css +3 -0
- lib/phpsass/tests/string.scss +6 -0
- lib/phpsass/tests/subdir/nested_subdir/_nested_partial.sass +2 -0
- lib/phpsass/tests/subdir/nested_subdir/nested_subdir.css +1 -0
- lib/phpsass/tests/subdir/nested_subdir/nested_subdir.sass +3 -0
- lib/phpsass/tests/subdir/subdir.css +3 -0
- lib/phpsass/tests/subdir/subdir.sass +6 -0
- lib/phpsass/tests/units.css +12 -0
- lib/phpsass/tests/units.sass +12 -0
- lib/phpsass/tests/warn.css +1 -0
- lib/phpsass/tests/warn.sass +4 -0
- lib/phpsass/tests/warn_imported.sass +4 -0
- lib/phpsass/tree/SassCommentNode.php +64 -0
- lib/phpsass/tree/SassContentNode.php +62 -0
- lib/phpsass/tree/SassContext.php +198 -0
- lib/phpsass/tree/SassDebugNode.php +90 -0
- lib/phpsass/tree/SassDirectiveNode.php +116 -0
- lib/phpsass/tree/SassEachNode.php +76 -0
- lib/phpsass/tree/SassElseNode.php +28 -0
- lib/phpsass/tree/SassExtendNode.php +49 -0
- lib/phpsass/tree/SassForNode.php +99 -0
- lib/phpsass/tree/SassFunctionDefinitionNode.php +116 -0
- lib/phpsass/tree/SassIfNode.php +95 -0
- lib/phpsass/tree/SassImportNode.php +105 -0
- lib/phpsass/tree/SassMediaNode.php +86 -0
- lib/phpsass/tree/SassMixinDefinitionNode.php +79 -0
- lib/phpsass/tree/SassMixinNode.php +89 -0
- lib/phpsass/tree/SassNode.php +349 -0
- lib/phpsass/tree/SassNodeExceptions.php +132 -0
- lib/phpsass/tree/SassPropertyNode.php +264 -0
- lib/phpsass/tree/SassReturnNode.php +80 -0
- lib/phpsass/tree/SassRootNode.php +112 -0
- lib/phpsass/tree/SassRuleNode.php +378 -0
- lib/phpsass/tree/SassVariableNode.php +91 -0
- lib/phpsass/tree/SassWarnNode.php +81 -0
- lib/phpsass/tree/SassWhileNode.php +64 -0
- package.xml +5 -5
app/code/community/Laurent/Sass/etc/config.xml
CHANGED
@@ -14,7 +14,7 @@
|
|
14 |
<config>
|
15 |
<modules>
|
16 |
<Laurent_Sass>
|
17 |
-
<version>1.1.
|
18 |
</Laurent_Sass>
|
19 |
</modules>
|
20 |
<global>
|
14 |
<config>
|
15 |
<modules>
|
16 |
<Laurent_Sass>
|
17 |
+
<version>1.1.1</version>
|
18 |
</Laurent_Sass>
|
19 |
</modules>
|
20 |
<global>
|
lib/phpsass/.travis.yml
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Configuration file for unit test runner.
|
2 |
+
language: php
|
3 |
+
php:
|
4 |
+
- 5.3
|
5 |
+
- 5.4
|
6 |
+
env:
|
7 |
+
- PHPUNIT_ARGS=--group=sass
|
8 |
+
before_script:
|
9 |
+
- cd tests
|
10 |
+
script: phpunit $PHPUNIT_ARGS
|
11 |
+
notifications:
|
12 |
+
email: false
|
lib/phpsass/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#IMPORTANT NOTICE
|
2 |
+
We are evaluating wether to switch to using namespaces for better PSR compliance. To help with this, can you tweet or email me (richthegeek@gmail.com) with which version of PHP you are using.
|
3 |
+
|
4 |
+
|
5 |
+
#PHPSass [![build status](https://secure.travis-ci.org/richthegeek/phpsass.png)](https://secure.travis-ci.org/#!/richthegeek/phpsass)
|
6 |
+
|
7 |
+
## Try online
|
8 |
+
You can compile and compare SCSS against the ruby compiler at <http://phpsass.com/try>
|
9 |
+
|
10 |
+
## About
|
11 |
+
This is fork of phamlp primarily for inclusiong as a Drupal pre-processor.
|
12 |
+
However, there is no Drupal-specific code and it will work for any PHP system.
|
13 |
+
|
14 |
+
This version of the compiler is NOT compatible with other forks of PHamlP, and
|
15 |
+
the name has been changed to reflect this. Please take a look at the "sassy"
|
16 |
+
module on Drupal.org for information on how to add frameworks and custom
|
17 |
+
functions. Docs will be coming when we aren't doing more important things.
|
18 |
+
|
19 |
+
## Contributors:
|
20 |
+
* Richard Lyon - [@richthegeek](https://github.com/richthegeek)
|
21 |
+
* Sebastian Siemssen - [@thefuhby](https://twitter.com/thefubhy)
|
22 |
+
* Steve Jones - [@darthsteven](https://github.com/darthsteven)
|
23 |
+
* Sam Richard - [@snugug](https://github.com/snugug)
|
24 |
+
|
25 |
+
## Other info
|
26 |
+
Origin: <http://code.google.com/p/phamlp/>
|
27 |
+
|
28 |
+
License: <http://www.opensource.org/licenses/bsd-license.php>
|
29 |
+
|
lib/phpsass/SassException.php
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* SVN FILE: $Id$ */
|
3 |
+
/**
|
4 |
+
* Sass exception.
|
5 |
+
* @author Chris Yates <chris.l.yates@gmail.com>
|
6 |
+
* @copyright Copyright (c) 2010 PBM Web Development
|
7 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
8 |
+
* @package PHamlP
|
9 |
+
* @subpackage Sass
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Sass exception class.
|
14 |
+
* @package PHamlP
|
15 |
+
* @subpackage Sass
|
16 |
+
*/
|
17 |
+
class SassException extends Exception {
|
18 |
+
/**
|
19 |
+
* Sass Exception.
|
20 |
+
* @param string Exception message
|
21 |
+
* @param array parameters to be applied to the message using <code>strtr</code>.
|
22 |
+
* @param object object with source code and meta data
|
23 |
+
*/
|
24 |
+
public function __construct($message, $object) {
|
25 |
+
parent::__construct($message . (is_object($object) ? ": {$object->filename}::{$object->line}\nSource: {$object->source}" : ''));
|
26 |
+
}
|
27 |
+
}
|
lib/phpsass/SassFile.php
ADDED
@@ -0,0 +1,154 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* SVN FILE: $Id$ */
|
3 |
+
/**
|
4 |
+
* SassFile class file.
|
5 |
+
* File handling utilites.
|
6 |
+
* @author Chris Yates <chris.l.yates@gmail.com>
|
7 |
+
* @copyright Copyright (c) 2010 PBM Web Development
|
8 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
9 |
+
* @package PHamlP
|
10 |
+
* @subpackage Sass
|
11 |
+
*/
|
12 |
+
|
13 |
+
/**
|
14 |
+
* SassFile class.
|
15 |
+
* @package PHamlP
|
16 |
+
* @subpackage Sass
|
17 |
+
*/
|
18 |
+
class SassFile {
|
19 |
+
const CSS = 'css';
|
20 |
+
const SASS = 'sass';
|
21 |
+
const SCSS = 'scss';
|
22 |
+
// const SASSC = 'sassc'; # tests for E_NOTICE
|
23 |
+
|
24 |
+
private static $extensions = array(self::SASS, self::SCSS);
|
25 |
+
|
26 |
+
public static $path = FALSE;
|
27 |
+
public static $parser = FALSE;
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Returns the parse tree for a file.
|
31 |
+
* @param string filename to parse
|
32 |
+
* @param SassParser Sass parser
|
33 |
+
* @return SassRootNode
|
34 |
+
*/
|
35 |
+
public static function get_tree($filename, &$parser) {
|
36 |
+
$contents = self::get_file_contents($filename, $parser);
|
37 |
+
|
38 |
+
$options = array_merge($parser->options, array('line'=>1));
|
39 |
+
|
40 |
+
# attempt at cross-syntax imports.
|
41 |
+
$ext = substr($filename, strrpos($filename, '.') + 1);
|
42 |
+
if ($ext == self::SASS || $ext == self::SCSS) {
|
43 |
+
$options['syntax'] = $ext;
|
44 |
+
}
|
45 |
+
|
46 |
+
$dirname = dirname($filename);
|
47 |
+
$options['load_paths'][] = $dirname;
|
48 |
+
if (!in_array($dirname, $parser->load_paths)) {
|
49 |
+
$parser->load_paths[] = dirname($filename);
|
50 |
+
}
|
51 |
+
|
52 |
+
$sassParser = new SassParser($options);
|
53 |
+
$tree = $sassParser->parse($contents, FALSE);
|
54 |
+
return $tree;
|
55 |
+
}
|
56 |
+
|
57 |
+
public static function get_file_contents($filename, $parser) {
|
58 |
+
$contents = file_get_contents($filename) . "\n\n "; #add some whitespace to fix bug
|
59 |
+
# strip // comments at this stage, with allowances for http:// style locations.
|
60 |
+
$contents = preg_replace("/(^|\s)\/\/[^\n]+/", '', $contents);
|
61 |
+
// SassFile::$parser = $parser;
|
62 |
+
// SassFile::$path = $filename;
|
63 |
+
// $contents = preg_replace_callback('/url\(\s*[\'"]?(?![a-z]+:|\/+)([^\'")]+)[\'"]?\s*\)/i', 'SassFile::resolve_paths', $contents);
|
64 |
+
return $contents;
|
65 |
+
}
|
66 |
+
|
67 |
+
public static function resolve_paths($matches) {
|
68 |
+
// Resolve the path into something nicer...
|
69 |
+
return 'url("' . self::resolve_path($matches[1]) . '")';
|
70 |
+
}
|
71 |
+
|
72 |
+
public static function resolve_path($name) {
|
73 |
+
$path = self::$parser->basepath . self::$path;
|
74 |
+
$path = substr($path, 0, strrpos($path, '/')) . '/';
|
75 |
+
$path = $path . $name;
|
76 |
+
$last = '';
|
77 |
+
while ($path != $last) {
|
78 |
+
$last = $path;
|
79 |
+
$path = preg_replace('`(^|/)(?!\.\./)([^/]+)/\.\./`', '$1', $path);
|
80 |
+
}
|
81 |
+
return $path;
|
82 |
+
}
|
83 |
+
|
84 |
+
/**
|
85 |
+
* Returns the full path to a file to parse.
|
86 |
+
* The file is looked for recursively under the load_paths directories
|
87 |
+
* If the filename does not end in .sass or .scss try the current syntax first
|
88 |
+
* then, if a file is not found, try the other syntax.
|
89 |
+
* @param string filename to find
|
90 |
+
* @param SassParser Sass parser
|
91 |
+
* @return array of string path(s) to file(s) or FALSE if no such file
|
92 |
+
*/
|
93 |
+
public static function get_file($filename, &$parser, $sass_only = TRUE) {
|
94 |
+
$ext = substr($filename, strrpos($filename, '.') + 1);
|
95 |
+
// if the last char isn't *, and it's not (.sass|.scss|.css)
|
96 |
+
if ($sass_only && substr($filename, -1) != '*' && $ext !== self::SASS && $ext !== self::SCSS && $ext !== self::CSS) {
|
97 |
+
$sass = self::get_file($filename . '.' . self::SASS, $parser);
|
98 |
+
return $sass ? $sass : self::get_file($filename . '.' . self::SCSS, $parser);
|
99 |
+
}
|
100 |
+
if (file_exists($filename)) {
|
101 |
+
return array($filename);
|
102 |
+
}
|
103 |
+
$paths = $parser->load_paths;
|
104 |
+
if(is_string($parser->filename) && $path = dirname($parser->filename)) {
|
105 |
+
$paths[] = $path;
|
106 |
+
if (!in_array($path, $parser->load_paths)) {
|
107 |
+
$parser->load_paths[] = $path;
|
108 |
+
}
|
109 |
+
}
|
110 |
+
foreach ($paths as $path) {
|
111 |
+
$filepath = self::find_file($filename, realpath($path));
|
112 |
+
if ($filepath !== false) {
|
113 |
+
return array($filepath);
|
114 |
+
}
|
115 |
+
}
|
116 |
+
foreach ($parser->load_path_functions as $function) {
|
117 |
+
if (is_callable($function) && $paths = call_user_func($function, $filename, $parser)) {
|
118 |
+
return $paths;
|
119 |
+
}
|
120 |
+
}
|
121 |
+
return FALSE;
|
122 |
+
}
|
123 |
+
|
124 |
+
/**
|
125 |
+
* Looks for the file recursively in the specified directory.
|
126 |
+
* This will also look for _filename to handle Sass partials.
|
127 |
+
* @param string filename to look for
|
128 |
+
* @param string path to directory to look in and under
|
129 |
+
* @return mixed string: full path to file if found, false if not
|
130 |
+
*/
|
131 |
+
public static function find_file($filename, $dir) {
|
132 |
+
$partialname = dirname($filename).DIRECTORY_SEPARATOR.'_'.basename($filename);
|
133 |
+
|
134 |
+
foreach (array($filename, $partialname) as $file) {
|
135 |
+
if (file_exists($dir . DIRECTORY_SEPARATOR . $file)) {
|
136 |
+
return realpath($dir . DIRECTORY_SEPARATOR . $file);
|
137 |
+
}
|
138 |
+
}
|
139 |
+
|
140 |
+
if (is_dir($dir)) {
|
141 |
+
$files = array_slice(scandir($dir), 2);
|
142 |
+
|
143 |
+
foreach ($files as $file) {
|
144 |
+
if (is_dir($dir . DIRECTORY_SEPARATOR . $file)) {
|
145 |
+
$path = self::find_file($filename, $dir . DIRECTORY_SEPARATOR . $file);
|
146 |
+
if ($path !== false) {
|
147 |
+
return $path;
|
148 |
+
}
|
149 |
+
}
|
150 |
+
}
|
151 |
+
}
|
152 |
+
return false;
|
153 |
+
}
|
154 |
+
}
|
lib/phpsass/SassParser.php
ADDED
@@ -0,0 +1,841 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/* SVN FILE: $Id$ */
|
4 |
+
/**
|
5 |
+
* SassParser class file.
|
6 |
+
* See the {@link http://sass-lang.com/docs Sass documentation}
|
7 |
+
* for details of Sass.
|
8 |
+
*
|
9 |
+
* Credits:
|
10 |
+
* This is a port of Sass to PHP. All the genius comes from the people that
|
11 |
+
* invented and develop Sass; in particular:
|
12 |
+
* + {@link http://hamptoncatlin.com/ Hampton Catlin},
|
13 |
+
* + {@link http://nex-3.com/ Nathan Weizenbaum},
|
14 |
+
* + {@link http://chriseppstein.github.com/ Chris Eppstein}
|
15 |
+
*
|
16 |
+
* The bugs are mine. Please report any found at {@link http://code.google.com/p/phamlp/issues/list}
|
17 |
+
*
|
18 |
+
* @author Chris Yates <chris.l.yates@gmail.com>
|
19 |
+
* @copyright Copyright (c) 2010 PBM Web Development
|
20 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
21 |
+
* @package PHamlP
|
22 |
+
* @subpackage Sass
|
23 |
+
*/
|
24 |
+
|
25 |
+
require_once('SassFile.php');
|
26 |
+
require_once('SassException.php');
|
27 |
+
require_once('tree/SassNode.php');
|
28 |
+
|
29 |
+
/**
|
30 |
+
* SassParser class.
|
31 |
+
* Parses {@link http://sass-lang.com/ .sass and .sccs} files.
|
32 |
+
* @package PHamlP
|
33 |
+
* @subpackage Sass
|
34 |
+
*/
|
35 |
+
class SassParser {
|
36 |
+
/**#@+
|
37 |
+
* Default option values
|
38 |
+
*/
|
39 |
+
const BEGIN_COMMENT = '/';
|
40 |
+
const BEGIN_CSS_COMMENT = '/*';
|
41 |
+
const END_CSS_COMMENT = '*/';
|
42 |
+
const BEGIN_SASS_COMMENT = '//';
|
43 |
+
const BEGIN_INTERPOLATION = '#';
|
44 |
+
const BEGIN_INTERPOLATION_BLOCK = '#{';
|
45 |
+
const BEGIN_BLOCK = '{';
|
46 |
+
const END_BLOCK = '}';
|
47 |
+
const END_STATEMENT = ';';
|
48 |
+
const DOUBLE_QUOTE = '"';
|
49 |
+
const SINGLE_QUOTE = "'";
|
50 |
+
|
51 |
+
/**
|
52 |
+
* Static holder for last instance of a SassParser
|
53 |
+
*/
|
54 |
+
static public $instance;
|
55 |
+
|
56 |
+
/**
|
57 |
+
* @var string the character used for indenting
|
58 |
+
* @see indentChars
|
59 |
+
* @see indentSpaces
|
60 |
+
*/
|
61 |
+
public $indentChar;
|
62 |
+
/**
|
63 |
+
* @var array allowable characters for indenting
|
64 |
+
*/
|
65 |
+
public $indentChars = array(' ', "\t");
|
66 |
+
/**
|
67 |
+
* @var integer number of spaces for indentation.
|
68 |
+
* Used to calculate {@link Level} if {@link indentChar} is space.
|
69 |
+
*/
|
70 |
+
public $indentSpaces = 2;
|
71 |
+
|
72 |
+
/**
|
73 |
+
* @var string source
|
74 |
+
*/
|
75 |
+
public $source;
|
76 |
+
|
77 |
+
/**#@+
|
78 |
+
* Option
|
79 |
+
*/
|
80 |
+
|
81 |
+
public $basepath;
|
82 |
+
|
83 |
+
|
84 |
+
/**
|
85 |
+
* debug_info:
|
86 |
+
* @var boolean When true the line number and file where a selector is defined
|
87 |
+
* is emitted into the compiled CSS in a format that can be understood by the
|
88 |
+
* {@link https://addons.mozilla.org/en-US/firefox/addon/103988/
|
89 |
+
* FireSass Firebug extension}.
|
90 |
+
* Disabled when using the compressed output style.
|
91 |
+
*
|
92 |
+
* Defaults to false.
|
93 |
+
* @see style
|
94 |
+
*/
|
95 |
+
public $debug_info;
|
96 |
+
|
97 |
+
/**
|
98 |
+
* filename:
|
99 |
+
* @var string The filename of the file being rendered.
|
100 |
+
* This is used solely for reporting errors.
|
101 |
+
*/
|
102 |
+
public $filename;
|
103 |
+
|
104 |
+
/**
|
105 |
+
* function:
|
106 |
+
* @var An array of (function_name => callback) items.
|
107 |
+
*/
|
108 |
+
public static $functions;
|
109 |
+
|
110 |
+
/**
|
111 |
+
* line:
|
112 |
+
* @var integer The number of the first line of the Sass template. Used for
|
113 |
+
* reporting line numbers for errors. This is useful to set if the Sass
|
114 |
+
* template is embedded.
|
115 |
+
*
|
116 |
+
* Defaults to 1.
|
117 |
+
*/
|
118 |
+
public $line;
|
119 |
+
|
120 |
+
/**
|
121 |
+
* line_numbers:
|
122 |
+
* @var boolean When true the line number and filename where a selector is
|
123 |
+
* defined is emitted into the compiled CSS as a comment. Useful for debugging
|
124 |
+
* especially when using imports and mixins.
|
125 |
+
* Disabled when using the compressed output style or the debug_info option.
|
126 |
+
*
|
127 |
+
* Defaults to false.
|
128 |
+
* @see debug_info
|
129 |
+
* @see style
|
130 |
+
*/
|
131 |
+
public $line_numbers;
|
132 |
+
|
133 |
+
/**
|
134 |
+
* load_paths:
|
135 |
+
* @var array An array of filesystem paths which should be searched for
|
136 |
+
* Sass templates imported with the @import directive.
|
137 |
+
*
|
138 |
+
* Defaults to './sass-templates'.
|
139 |
+
*/
|
140 |
+
public $load_paths;
|
141 |
+
public $load_path_functions;
|
142 |
+
|
143 |
+
/**
|
144 |
+
* property_syntax:
|
145 |
+
* @var string Forces the document to use one syntax for
|
146 |
+
* properties. If the correct syntax isn't used, an error is thrown.
|
147 |
+
* Value can be:
|
148 |
+
* + new - forces the use of a colon or equals sign after the property name.
|
149 |
+
* For example color: #0f3 or width: $main_width.
|
150 |
+
* + old - forces the use of a colon before the property name.
|
151 |
+
* For example: :color #0f3 or :width = $main_width.
|
152 |
+
*
|
153 |
+
* By default, either syntax is valid.
|
154 |
+
*
|
155 |
+
* Ignored for SCSS files which alaways use the new style.
|
156 |
+
*/
|
157 |
+
public $property_syntax;
|
158 |
+
|
159 |
+
/**
|
160 |
+
* quiet:
|
161 |
+
* @var boolean When set to true, causes warnings to be disabled.
|
162 |
+
* Defaults to false.
|
163 |
+
*/
|
164 |
+
public $quiet;
|
165 |
+
|
166 |
+
/**
|
167 |
+
* callbacks:
|
168 |
+
* @var array listing callbacks for @warn and @debug directives.
|
169 |
+
* Callbacks are executed by call_user_func and thus must conform
|
170 |
+
* to that standard.
|
171 |
+
*/
|
172 |
+
public $callbacks;
|
173 |
+
|
174 |
+
/**
|
175 |
+
* style:
|
176 |
+
* @var string the style of the CSS output.
|
177 |
+
* Value can be:
|
178 |
+
* + nested - Nested is the default Sass style, because it reflects the
|
179 |
+
* structure of the document in much the same way Sass does. Each selector
|
180 |
+
* and rule has its own line with indentation is based on how deeply the rule
|
181 |
+
* is nested. Nested style is very useful when looking at large CSS files as
|
182 |
+
* it allows you to very easily grasp the structure of the file without
|
183 |
+
* actually reading anything.
|
184 |
+
* + expanded - Expanded is the typical human-made CSS style, with each selector
|
185 |
+
* and property taking up one line. Selectors are not indented; properties are
|
186 |
+
* indented within the rules.
|
187 |
+
* + compact - Each CSS rule takes up only one line, with every property defined
|
188 |
+
* on that line. Nested rules are placed with each other while groups of rules
|
189 |
+
* are separated by a blank line.
|
190 |
+
* + compressed - Compressed has no whitespace except that necessary to separate
|
191 |
+
* selectors and properties. It's not meant to be human-readable.
|
192 |
+
*
|
193 |
+
* Defaults to 'nested'.
|
194 |
+
*/
|
195 |
+
public $style;
|
196 |
+
|
197 |
+
/**
|
198 |
+
* syntax:
|
199 |
+
* @var string The syntax of the input file.
|
200 |
+
* 'sass' for the indented syntax and 'scss' for the CSS-extension syntax.
|
201 |
+
*
|
202 |
+
* This is set automatically when parsing a file, else defaults to 'sass'.
|
203 |
+
*/
|
204 |
+
public $syntax;
|
205 |
+
|
206 |
+
/**
|
207 |
+
* debug:
|
208 |
+
* If enabled it causes exceptions to be thrown on errors. This can be
|
209 |
+
* useful for tracking down a bug in your sourcefile but will cause a
|
210 |
+
* site to break if used in production unless the parser in wrapped in
|
211 |
+
* a try/catch structure.
|
212 |
+
*
|
213 |
+
* Defaults to FALSE
|
214 |
+
*/
|
215 |
+
public $debug = FALSE;
|
216 |
+
|
217 |
+
/**
|
218 |
+
* Constructor.
|
219 |
+
* Sets parser options
|
220 |
+
* @param array $options
|
221 |
+
* @return SassParser
|
222 |
+
*/
|
223 |
+
public function __construct($options = array()) {
|
224 |
+
if (!is_array($options)) {
|
225 |
+
if (isset($options['debug']) && $options['debug']) {
|
226 |
+
throw new SassException('Options must be an array');
|
227 |
+
}
|
228 |
+
$options = count((array) $options) ? (array) $options : array();
|
229 |
+
}
|
230 |
+
unset($options['language']);
|
231 |
+
|
232 |
+
$basepath = $_SERVER['PHP_SELF'];
|
233 |
+
$basepath = substr($basepath, 0, strrpos($basepath, '/') + 1);
|
234 |
+
|
235 |
+
$defaultOptions = array(
|
236 |
+
'basepath' => $basepath,
|
237 |
+
'debug_info' => FALSE,
|
238 |
+
'filename' => array('dirname' => '', 'basename' => ''),
|
239 |
+
'functions' => array(),
|
240 |
+
'load_paths' => array(),
|
241 |
+
'load_path_functions' => array(),
|
242 |
+
'line' => 1,
|
243 |
+
'line_numbers' => FALSE,
|
244 |
+
'style' => SassRenderer::STYLE_NESTED,
|
245 |
+
'syntax' => SassFile::SASS,
|
246 |
+
'debug' => FALSE,
|
247 |
+
'quiet' => FALSE,
|
248 |
+
'callbacks' => array(
|
249 |
+
'warn' => FALSE,
|
250 |
+
'debug' => FALSE,
|
251 |
+
),
|
252 |
+
);
|
253 |
+
|
254 |
+
if (isset(self::$instance)) {
|
255 |
+
$defaultOptions['load_paths'] = self::$instance->load_paths;
|
256 |
+
}
|
257 |
+
|
258 |
+
$options = array_merge($defaultOptions, $options);
|
259 |
+
|
260 |
+
// We don't want to allow setting of internal only property syntax value
|
261 |
+
if (isset($options["property_syntax"]) && $options["property_syntax"] == "scss") {
|
262 |
+
unset($options["property_syntax"]);
|
263 |
+
}
|
264 |
+
|
265 |
+
self::$instance = $this;
|
266 |
+
self::$functions = $options['functions'];
|
267 |
+
unset($options['functions']);
|
268 |
+
|
269 |
+
foreach ($options as $name=>$value) {
|
270 |
+
$this->$name = $value;
|
271 |
+
}
|
272 |
+
|
273 |
+
if (!$this->property_syntax && $this->syntax == SassFile::SCSS) {
|
274 |
+
$this->property_syntax = "scss";
|
275 |
+
}
|
276 |
+
|
277 |
+
$GLOBALS['SassParser_debug'] = $this->debug;
|
278 |
+
}
|
279 |
+
|
280 |
+
/**
|
281 |
+
* Getter.
|
282 |
+
* @param string name of property to get
|
283 |
+
* @return mixed return value of getter function
|
284 |
+
*/
|
285 |
+
public function __get($name) {
|
286 |
+
$getter = 'get' . ucfirst($name);
|
287 |
+
if (method_exists($this, $getter)) {
|
288 |
+
return $this->$getter();
|
289 |
+
}
|
290 |
+
if (property_exists($this, $name)) {
|
291 |
+
return $this->$name;
|
292 |
+
}
|
293 |
+
if ($this->debug) {
|
294 |
+
throw new SassException('No getter function for ' . $name);
|
295 |
+
}
|
296 |
+
}
|
297 |
+
|
298 |
+
public function getBasepath() {
|
299 |
+
return $this->basepath;
|
300 |
+
}
|
301 |
+
|
302 |
+
public function getDebug_info() {
|
303 |
+
return $this->debug_info;
|
304 |
+
}
|
305 |
+
|
306 |
+
public function getFilename() {
|
307 |
+
return $this->filename;
|
308 |
+
}
|
309 |
+
|
310 |
+
public function getLine() {
|
311 |
+
return $this->line;
|
312 |
+
}
|
313 |
+
|
314 |
+
public function getSource() {
|
315 |
+
return $this->source;
|
316 |
+
}
|
317 |
+
|
318 |
+
public function getLine_numbers() {
|
319 |
+
return $this->line_numbers;
|
320 |
+
}
|
321 |
+
|
322 |
+
public function getFunctions() {
|
323 |
+
return self::$functions;
|
324 |
+
}
|
325 |
+
|
326 |
+
public function getLoad_paths() {
|
327 |
+
return $this->load_paths;
|
328 |
+
}
|
329 |
+
|
330 |
+
public function getLoad_path_functions() {
|
331 |
+
return $this->load_path_functions;
|
332 |
+
}
|
333 |
+
|
334 |
+
public function getProperty_syntax() {
|
335 |
+
return $this->property_syntax;
|
336 |
+
}
|
337 |
+
|
338 |
+
public function getQuiet() {
|
339 |
+
return $this->quiet;
|
340 |
+
}
|
341 |
+
|
342 |
+
public function getStyle() {
|
343 |
+
return $this->style;
|
344 |
+
}
|
345 |
+
|
346 |
+
public function getSyntax() {
|
347 |
+
return $this->syntax;
|
348 |
+
}
|
349 |
+
|
350 |
+
public function getDebug() {
|
351 |
+
return $this->debug;
|
352 |
+
}
|
353 |
+
|
354 |
+
public function getCallbacks() {
|
355 |
+
return $this->callbacks + array(
|
356 |
+
'warn' => NULL,
|
357 |
+
'debug' => NULL,
|
358 |
+
);
|
359 |
+
}
|
360 |
+
|
361 |
+
public function getOptions() {
|
362 |
+
return array(
|
363 |
+
'callbacks' => $this->callbacks,
|
364 |
+
// 'debug' => $this->debug,
|
365 |
+
'filename' => $this->filename,
|
366 |
+
'functions' => $this->functions,
|
367 |
+
'line' => $this->line,
|
368 |
+
'line_numbers' => $this->line_numbers,
|
369 |
+
'load_path_functions' => $this->load_path_functions,
|
370 |
+
'load_paths' => $this->load_paths,
|
371 |
+
'property_syntax' => ($this->property_syntax == "scss" ? null : $this->property_syntax),
|
372 |
+
'quiet' => $this->quiet,
|
373 |
+
'style' => $this->style,
|
374 |
+
'syntax' => $this->syntax,
|
375 |
+
);
|
376 |
+
}
|
377 |
+
|
378 |
+
/**
|
379 |
+
* Parse a sass file or Sass source code and returns the CSS.
|
380 |
+
* @param string name of source file or Sass source
|
381 |
+
* @return string CSS
|
382 |
+
*/
|
383 |
+
public function toCss($source, $isFile = true) {
|
384 |
+
return $this->parse($source, $isFile)->render();
|
385 |
+
}
|
386 |
+
|
387 |
+
/**
|
388 |
+
* Parse a sass file or Sass source code and
|
389 |
+
* returns the document tree that can then be rendered.
|
390 |
+
* The file will be searched for in the directories specified by the
|
391 |
+
* load_paths option.
|
392 |
+
* @param string name of source file or Sass source
|
393 |
+
* @return SassRootNode Root node of document tree
|
394 |
+
*/
|
395 |
+
public function parse($source, $isFile = true) {
|
396 |
+
# Richard Lyon - 2011-10-25 - ignore unfound files
|
397 |
+
# Richard Lyon - 2011-10-25 - add multiple files to load functions
|
398 |
+
if (!$source) {
|
399 |
+
return $this->toTree($source);
|
400 |
+
}
|
401 |
+
|
402 |
+
if (is_array($source)) {
|
403 |
+
$return = array();
|
404 |
+
foreach ($source as $source_file) {
|
405 |
+
$return = array_merge($return, $this->parse($source_file, TRUE));
|
406 |
+
}
|
407 |
+
return $return;
|
408 |
+
}
|
409 |
+
|
410 |
+
if ($isFile && $files = SassFile::get_file($source, $this)) {
|
411 |
+
$files_source = '';
|
412 |
+
foreach ($files as $file) {
|
413 |
+
$this->filename = $file;
|
414 |
+
$this->syntax = substr(strrchr($file, '.'), 1);
|
415 |
+
if($this->syntax == SassFile::CSS){
|
416 |
+
$this->property_syntax = "css";
|
417 |
+
}elseif (!$this->property_syntax && $this->syntax == SassFile::SCSS) {
|
418 |
+
$this->property_syntax = "scss";
|
419 |
+
}
|
420 |
+
|
421 |
+
if ($this->syntax !== SassFile::SASS && $this->syntax !== SassFile::SCSS && $this->syntax !== SassFile::CSS) {
|
422 |
+
if ($this->debug) {
|
423 |
+
throw new SassException('Invalid {what}', array('{what}' => 'syntax option'));
|
424 |
+
}
|
425 |
+
return FALSE;
|
426 |
+
}
|
427 |
+
$files_source .= SassFile::get_file_contents($this->filename, $this);
|
428 |
+
}
|
429 |
+
return $this->toTree($files_source);
|
430 |
+
}
|
431 |
+
else {
|
432 |
+
return $this->toTree($source);
|
433 |
+
}
|
434 |
+
}
|
435 |
+
|
436 |
+
/**
|
437 |
+
* Parse Sass source into a document tree.
|
438 |
+
* If the tree is already created return that.
|
439 |
+
* @param string Sass source
|
440 |
+
* @return SassRootNode the root of this document tree
|
441 |
+
*/
|
442 |
+
public function toTree($source) {
|
443 |
+
if ($this->syntax === SassFile::SASS) {
|
444 |
+
$source = str_replace(array("\r\n", "\n\r", "\r"), "\n", $source);
|
445 |
+
$this->source = explode("\n", $source);
|
446 |
+
$this->setIndentChar();
|
447 |
+
}
|
448 |
+
else {
|
449 |
+
$this->source = $source;
|
450 |
+
}
|
451 |
+
unset($source);
|
452 |
+
$root = new SassRootNode($this);
|
453 |
+
$this->buildTree($root);
|
454 |
+
return $root;
|
455 |
+
}
|
456 |
+
|
457 |
+
/**
|
458 |
+
* Builds a parse tree under the parent node.
|
459 |
+
* Called recursivly until the source is parsed.
|
460 |
+
* @param SassNode the node
|
461 |
+
*/
|
462 |
+
public function buildTree($parent) {
|
463 |
+
$node = $this->getNode($parent);
|
464 |
+
while (is_object($node) && $node->isChildOf($parent)) {
|
465 |
+
$parent->addChild($node);
|
466 |
+
$node = $this->buildTree($node);
|
467 |
+
}
|
468 |
+
return $node;
|
469 |
+
}
|
470 |
+
|
471 |
+
/**
|
472 |
+
* Creates and returns the next SassNode.
|
473 |
+
* The tpye of SassNode depends on the content of the SassToken.
|
474 |
+
* @return SassNode a SassNode of the appropriate type. Null when no more
|
475 |
+
* source to parse.
|
476 |
+
*/
|
477 |
+
public function getNode($node) {
|
478 |
+
$token = $this->getToken();
|
479 |
+
if (empty($token)) return null;
|
480 |
+
switch (true) {
|
481 |
+
case SassDirectiveNode::isa($token):
|
482 |
+
return $this->parseDirective($token, $node);
|
483 |
+
case SassCommentNode::isa($token):
|
484 |
+
return new SassCommentNode($token);
|
485 |
+
case SassVariableNode::isa($token):
|
486 |
+
return new SassVariableNode($token);
|
487 |
+
case SassPropertyNode::isa(array('token' => $token, 'syntax' => $this->property_syntax)):
|
488 |
+
return new SassPropertyNode($token, $this->property_syntax);
|
489 |
+
case SassFunctionDefinitionNode::isa($token):
|
490 |
+
return new SassFunctionDefinitionNode($token);
|
491 |
+
case SassMixinDefinitionNode::isa($token):
|
492 |
+
if ($this->syntax === SassFile::SCSS) {
|
493 |
+
if ($this->debug) {
|
494 |
+
throw new SassException('Mixin definition shortcut not allowed in SCSS', $this);
|
495 |
+
}
|
496 |
+
return;
|
497 |
+
}
|
498 |
+
else {
|
499 |
+
return new SassMixinDefinitionNode($token);
|
500 |
+
}
|
501 |
+
case SassMixinNode::isa($token):
|
502 |
+
if ($this->syntax === SassFile::SCSS) {
|
503 |
+
if ($this->debug) {
|
504 |
+
throw new SassException('Mixin include shortcut not allowed in SCSS', $this);
|
505 |
+
}
|
506 |
+
return;
|
507 |
+
}
|
508 |
+
else {
|
509 |
+
return new SassMixinNode($token);
|
510 |
+
}
|
511 |
+
default:
|
512 |
+
return new SassRuleNode($token);
|
513 |
+
break;
|
514 |
+
} // switch
|
515 |
+
}
|
516 |
+
|
517 |
+
/**
|
518 |
+
* Returns a token object that contains the next source statement and
|
519 |
+
* meta data about it.
|
520 |
+
* @return object
|
521 |
+
*/
|
522 |
+
public function getToken() {
|
523 |
+
return ($this->syntax === SassFile::SASS ? $this->sass2Token() : $this->scss2Token());
|
524 |
+
}
|
525 |
+
|
526 |
+
/**
|
527 |
+
* Returns an object that contains the next source statement and meta data
|
528 |
+
* about it from SASS source.
|
529 |
+
* Sass statements are passed over. Statements spanning multiple lines, e.g.
|
530 |
+
* CSS comments and selectors, are assembled into a single statement.
|
531 |
+
* @return object Statement token. Null if end of source.
|
532 |
+
*/
|
533 |
+
public function sass2Token() {
|
534 |
+
$statement = ''; // source line being tokenised
|
535 |
+
$token = null;
|
536 |
+
|
537 |
+
while (is_null($token) && !empty($this->source)) {
|
538 |
+
while (empty($statement) && is_array($this->source) && !empty($this->source)) {
|
539 |
+
$source = array_shift($this->source);
|
540 |
+
$statement = trim($source);
|
541 |
+
$this->line++;
|
542 |
+
}
|
543 |
+
|
544 |
+
if (empty($statement)) {
|
545 |
+
break;
|
546 |
+
}
|
547 |
+
|
548 |
+
$level = $this->getLevel($source);
|
549 |
+
|
550 |
+
// Comment statements can span multiple lines
|
551 |
+
if ($statement[0] === self::BEGIN_COMMENT) {
|
552 |
+
// Consume Sass comments
|
553 |
+
if (substr($statement, 0, strlen(self::BEGIN_SASS_COMMENT)) === self::BEGIN_SASS_COMMENT) {
|
554 |
+
unset($statement);
|
555 |
+
while($this->getLevel($this->source[0]) > $level) {
|
556 |
+
array_shift($this->source);
|
557 |
+
$this->line++;
|
558 |
+
}
|
559 |
+
continue;
|
560 |
+
}
|
561 |
+
// Build CSS comments
|
562 |
+
elseif (substr($statement, 0, strlen(self::BEGIN_CSS_COMMENT))
|
563 |
+
=== self::BEGIN_CSS_COMMENT) {
|
564 |
+
while($this->getLevel($this->source[0]) > $level) {
|
565 |
+
$statement .= "\n" . ltrim(array_shift($this->source));
|
566 |
+
$this->line++;
|
567 |
+
}
|
568 |
+
}
|
569 |
+
else {
|
570 |
+
$this->source = $statement;
|
571 |
+
|
572 |
+
if ($this->debug) {
|
573 |
+
throw new SassException('Illegal comment type', $this);
|
574 |
+
}
|
575 |
+
}
|
576 |
+
}
|
577 |
+
// Selector statements can span multiple lines
|
578 |
+
elseif (substr($statement, -1) === SassRuleNode::CONTINUED) {
|
579 |
+
// Build the selector statement
|
580 |
+
while($this->getLevel($this->source[0]) === $level) {
|
581 |
+
$statement .= ltrim(array_shift($this->source));
|
582 |
+
$this->line++;
|
583 |
+
}
|
584 |
+
}
|
585 |
+
|
586 |
+
$token = (object) array(
|
587 |
+
'source' => $statement,
|
588 |
+
'level' => $level,
|
589 |
+
'filename' => $this->filename,
|
590 |
+
'line' => $this->line - 1,
|
591 |
+
);
|
592 |
+
}
|
593 |
+
return $token;
|
594 |
+
}
|
595 |
+
|
596 |
+
/**
|
597 |
+
* Returns the level of the line.
|
598 |
+
* Used for .sass source
|
599 |
+
* @param string the source
|
600 |
+
* @return integer the level of the source
|
601 |
+
* @throws Exception if the source indentation is invalid
|
602 |
+
*/
|
603 |
+
public function getLevel($source) {
|
604 |
+
$indent = strlen($source) - strlen(ltrim($source));
|
605 |
+
$level = $indent/$this->indentSpaces;
|
606 |
+
if (is_float($level)) {
|
607 |
+
$level = (int) ceil($level);
|
608 |
+
}
|
609 |
+
if (!is_int($level) || preg_match("/[^{$this->indentChar}]/", substr($source, 0, $indent))) {
|
610 |
+
$this->source = $source;
|
611 |
+
|
612 |
+
if ($this->debug) {
|
613 |
+
throw new SassException('Invalid indentation', $this);
|
614 |
+
}
|
615 |
+
else {
|
616 |
+
return 0;
|
617 |
+
}
|
618 |
+
}
|
619 |
+
return $level;
|
620 |
+
}
|
621 |
+
|
622 |
+
/**
|
623 |
+
* Returns an object that contains the next source statement and meta data
|
624 |
+
* about it from SCSS source.
|
625 |
+
* @return object Statement token. Null if end of source.
|
626 |
+
*/
|
627 |
+
public function scss2Token() {
|
628 |
+
static $srcpos = 0; // current position in the source stream
|
629 |
+
static $srclen; // the length of the source stream
|
630 |
+
|
631 |
+
$statement = '';
|
632 |
+
$token = null;
|
633 |
+
if (empty($srclen)) {
|
634 |
+
$srclen = strlen($this->source);
|
635 |
+
}
|
636 |
+
while (is_null($token) && $srcpos < strlen($this->source)) {
|
637 |
+
$c = $this->source[$srcpos++];
|
638 |
+
switch ($c) {
|
639 |
+
case self::BEGIN_COMMENT:
|
640 |
+
if (substr($this->source, $srcpos-1, strlen(self::BEGIN_SASS_COMMENT)) === self::BEGIN_SASS_COMMENT) {
|
641 |
+
while ($this->source[$srcpos++] !== "\n") {
|
642 |
+
if ($srcpos >= $srclen)
|
643 |
+
throw new SassException('Unterminated commend', (object) array(
|
644 |
+
'source' => $statement,
|
645 |
+
'filename' => $this->filename,
|
646 |
+
'line' => $this->line,
|
647 |
+
));
|
648 |
+
}
|
649 |
+
$statement .= "\n";
|
650 |
+
}
|
651 |
+
elseif (substr($this->source, $srcpos-1, strlen(self::BEGIN_CSS_COMMENT)) === self::BEGIN_CSS_COMMENT) {
|
652 |
+
if (ltrim($statement)) {
|
653 |
+
if ($this->debug) {
|
654 |
+
throw new SassException('Invalid comment', (object) array(
|
655 |
+
'source' => $statement,
|
656 |
+
'filename' => $this->filename,
|
657 |
+
'line' => $this->line,
|
658 |
+
));
|
659 |
+
}
|
660 |
+
}
|
661 |
+
$statement .= $c.$this->source[$srcpos++];
|
662 |
+
while (substr($this->source, $srcpos, strlen(self::END_CSS_COMMENT)) !== self::END_CSS_COMMENT) {
|
663 |
+
$statement .= $this->source[$srcpos++];
|
664 |
+
}
|
665 |
+
$srcpos += strlen(self::END_CSS_COMMENT);
|
666 |
+
$token = $this->createToken($statement.self::END_CSS_COMMENT);
|
667 |
+
}
|
668 |
+
else {
|
669 |
+
$statement .= $c;
|
670 |
+
}
|
671 |
+
break;
|
672 |
+
case self::DOUBLE_QUOTE:
|
673 |
+
case self::SINGLE_QUOTE:
|
674 |
+
$statement .= $c;
|
675 |
+
while (isset($this->source[$srcpos]) && $this->source[$srcpos] !== $c) {
|
676 |
+
$statement .= $this->source[$srcpos++];
|
677 |
+
}
|
678 |
+
if (isset($this->source[$srcpos+1])) {
|
679 |
+
$statement .= $this->source[$srcpos++];
|
680 |
+
}
|
681 |
+
break;
|
682 |
+
case self::BEGIN_INTERPOLATION:
|
683 |
+
$statement .= $c;
|
684 |
+
if (substr($this->source, $srcpos-1, strlen(self::BEGIN_INTERPOLATION_BLOCK)) === self::BEGIN_INTERPOLATION_BLOCK) {
|
685 |
+
while ($this->source[$srcpos] !== self::END_BLOCK) {
|
686 |
+
$statement .= $this->source[$srcpos++];
|
687 |
+
}
|
688 |
+
$statement .= $this->source[$srcpos++];
|
689 |
+
}
|
690 |
+
break;
|
691 |
+
case self::BEGIN_BLOCK:
|
692 |
+
case self::END_BLOCK:
|
693 |
+
case self::END_STATEMENT:
|
694 |
+
$token = $this->createToken($statement . $c);
|
695 |
+
if (is_null($token)) {
|
696 |
+
$statement = '';
|
697 |
+
}
|
698 |
+
break;
|
699 |
+
default:
|
700 |
+
$statement .= $c;
|
701 |
+
break;
|
702 |
+
}
|
703 |
+
}
|
704 |
+
|
705 |
+
if (is_null($token)) {
|
706 |
+
$srclen = $srcpos = 0;
|
707 |
+
}
|
708 |
+
|
709 |
+
return $token;
|
710 |
+
}
|
711 |
+
|
712 |
+
/**
|
713 |
+
* Returns an object that contains the source statement and meta data about
|
714 |
+
* it.
|
715 |
+
* If the statement is just and end block we update the meta data and return null.
|
716 |
+
* @param string source statement
|
717 |
+
* @return SassToken
|
718 |
+
*/
|
719 |
+
public function createToken($statement) {
|
720 |
+
static $level = 0;
|
721 |
+
|
722 |
+
$this->line += substr_count($statement, "\n");
|
723 |
+
$statement = trim($statement);
|
724 |
+
if (substr($statement, 0, strlen(self::BEGIN_CSS_COMMENT)) !== self::BEGIN_CSS_COMMENT) {
|
725 |
+
$statement = str_replace(array("\n","\r"), '', $statement);
|
726 |
+
}
|
727 |
+
$last = substr($statement, -1);
|
728 |
+
// Trim the statement removing whitespace, end statement (;), begin block ({), and (unless the statement ends in an interpolation block) end block (})
|
729 |
+
$statement = rtrim($statement, ' '.self::BEGIN_BLOCK.self::END_STATEMENT);
|
730 |
+
$statement = (preg_match('/#\{.+?\}$/i', $statement) ? $statement : rtrim($statement, self::END_BLOCK));
|
731 |
+
$token = ($statement ? (object) array(
|
732 |
+
'source' => $statement,
|
733 |
+
'level' => $level,
|
734 |
+
'filename' => $this->filename,
|
735 |
+
'line' => $this->line,
|
736 |
+
) : null);
|
737 |
+
$level += ($last === self::BEGIN_BLOCK ? 1 : ($last === self::END_BLOCK ? -1 : 0));
|
738 |
+
return $token;
|
739 |
+
}
|
740 |
+
|
741 |
+
/**
|
742 |
+
* Parses a directive
|
743 |
+
* @param SassToken token to parse
|
744 |
+
* @param SassNode parent node
|
745 |
+
* @return SassNode a Sass directive node
|
746 |
+
*/
|
747 |
+
public function parseDirective($token, $parent) {
|
748 |
+
switch (SassDirectiveNode::extractDirective($token)) {
|
749 |
+
case '@content':
|
750 |
+
return new SassContentNode($token);
|
751 |
+
break;
|
752 |
+
case '@extend':
|
753 |
+
return new SassExtendNode($token);
|
754 |
+
break;
|
755 |
+
case '@function':
|
756 |
+
return new SassFunctionDefinitionNode($token);
|
757 |
+
break;
|
758 |
+
case '@return':
|
759 |
+
return new SassReturnNode($token);
|
760 |
+
break;
|
761 |
+
case '@media':
|
762 |
+
return new SassMediaNode($token);
|
763 |
+
break;
|
764 |
+
case '@mixin':
|
765 |
+
return new SassMixinDefinitionNode($token);
|
766 |
+
break;
|
767 |
+
case '@include':
|
768 |
+
return new SassMixinNode($token);
|
769 |
+
break;
|
770 |
+
case '@import':
|
771 |
+
if ($this->syntax == SassFile::SASS) {
|
772 |
+
$i = 0;
|
773 |
+
$source = '';
|
774 |
+
while (sizeof($this->source) > $i && empty($source) && isset($this->source[$i + 1])) {
|
775 |
+
$source = $this->source[$i++];
|
776 |
+
}
|
777 |
+
if (!empty($source) && $this->getLevel($source) > $token->level) {
|
778 |
+
if ($this->debug) {
|
779 |
+
throw new SassException('Nesting not allowed beneath @import directive', $token);
|
780 |
+
}
|
781 |
+
}
|
782 |
+
}
|
783 |
+
return new SassImportNode($token, $parent);
|
784 |
+
break;
|
785 |
+
case '@each':
|
786 |
+
return new SassEachNode($token);
|
787 |
+
break;
|
788 |
+
case '@for':
|
789 |
+
return new SassForNode($token);
|
790 |
+
break;
|
791 |
+
case '@if':
|
792 |
+
return new SassIfNode($token);
|
793 |
+
break;
|
794 |
+
case '@else': // handles else and else if directives
|
795 |
+
return new SassElseNode($token);
|
796 |
+
break;
|
797 |
+
case '@do':
|
798 |
+
case '@while':
|
799 |
+
return new SassWhileNode($token);
|
800 |
+
break;
|
801 |
+
case '@warn':
|
802 |
+
return new SassWarnNode($token);
|
803 |
+
break;
|
804 |
+
case '@debug':
|
805 |
+
return new SassDebugNode($token);
|
806 |
+
break;
|
807 |
+
default:
|
808 |
+
return new SassDirectiveNode($token);
|
809 |
+
break;
|
810 |
+
}
|
811 |
+
}
|
812 |
+
|
813 |
+
/**
|
814 |
+
* Determine the indent character and indent spaces.
|
815 |
+
* The first character of the first indented line determines the character.
|
816 |
+
* If this is a space the number of spaces determines the indentSpaces; this
|
817 |
+
* is always 1 if the indent character is a tab.
|
818 |
+
* Only used for .sass files.
|
819 |
+
* @throws SassException if the indent is mixed or
|
820 |
+
* the indent character can not be determined
|
821 |
+
*/
|
822 |
+
public function setIndentChar() {
|
823 |
+
foreach ($this->source as $l=>$source) {
|
824 |
+
if (!empty($source) && in_array($source[0], $this->indentChars)) {
|
825 |
+
$this->indentChar = $source[0];
|
826 |
+
for ($i = 0, $len = strlen($source); $i < $len && $source[$i] == $this->indentChar; $i++);
|
827 |
+
if ($i < $len && in_array($source[$i], $this->indentChars)) {
|
828 |
+
$this->line = ++$l;
|
829 |
+
$this->source = $source;
|
830 |
+
if ($this->debug) {
|
831 |
+
throw new SassException('Mixed indentation not allowed', $this);
|
832 |
+
}
|
833 |
+
}
|
834 |
+
$this->indentSpaces = ($this->indentChar == ' ' ? $i : 1);
|
835 |
+
return;
|
836 |
+
}
|
837 |
+
} // foreach
|
838 |
+
$this->indentChar = ' ';
|
839 |
+
$this->indentSpaces = 2;
|
840 |
+
}
|
841 |
+
}
|
lib/phpsass/VERSION
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Version 201211282000 - Version bump to reflect multiple pull requests being merged over time.
|
2 |
+
Version 201209040040 - Fixed a bunch of issues
|
3 |
+
Version 201209031740 - Almost up to 3.2.1; Directive interp, splats, new functions, some fixes
|
4 |
+
Version 201207301030 - Fix to issue 28, extending from included files
|
5 |
+
Version 201207271000 - Fixed scale-color function (attrib: Steve Jones)
|
6 |
+
Version 201207262300 - Various issue fixes, better testing framework
|
7 |
+
Version 201206291700 - Fixed some tests, minor changes to string and import handling
|
8 |
+
Version 201203071100 - Hotfix to functions, overhauled evaluation method to be less rubbish
|
9 |
+
Version 201203061130 - Added min/max functions, fix to lists (evaluation instead of just lexing of values)
|
10 |
+
Version 201203042100 - Fixes to interpolation in selectors, test coverage, and some cleanup
|
11 |
+
Version 201203021700 - Placeholder selectors! Fixes to [selectors] when nested leaving a space.
|
12 |
+
Version 201203011100 - Hotfix for unfound function arguments in url() turning up empty from @imported files.
|
13 |
+
Version 201203010930 - Fix to minor issues and notices (regressions due to changes in prior version)
|
14 |
+
Version 201203010000 - Proper list support, functions: if, adjust-color, scale-color, change-color, named parameters for functions+mixins, various minor bugfixes
|
15 |
+
Version 201202100115 - fix to @import parsing of url(x.css)
|
16 |
+
Version 201202100100 - fix to @each parsing of things like rgba(*,*,*,*) which previously removed braces incorrectly.
|
17 |
+
Version 201202092300 - fix to @mixins calling @mixins involving variable-name collisions, minor fix to interpolation fail
|
18 |
+
Version 201202081100 - fix to @media contexts
|
19 |
+
Version 201202072300 - first version with versions!
|
lib/phpsass/compile-apache.php
ADDED
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* This file is intended to be used in conjunction with Apache2's mod_actions,
|
5 |
+
* wherein you can have a .htaccess file like so for automatic compilation:
|
6 |
+
* Action compile-sass /git/phpsass/compile-apache.php
|
7 |
+
* AddHandler compile-sass .sass .scss
|
8 |
+
*/
|
9 |
+
|
10 |
+
header('Content-type: text/css');
|
11 |
+
|
12 |
+
require_once './SassParser.php';
|
13 |
+
|
14 |
+
function warn($text, $context) {
|
15 |
+
print "/** WARN: $text, on line {$context->node->token->line} of {$context->node->token->filename} **/\n";
|
16 |
+
}
|
17 |
+
function debug($text, $context) {
|
18 |
+
print "/** DEBUG: $text, on line {$context->node->token->line} of {$context->node->token->filename} **/\n";
|
19 |
+
}
|
20 |
+
|
21 |
+
|
22 |
+
$file = $_SERVER['DOCUMENT_ROOT'] . $_SERVER['PATH_INFO'];
|
23 |
+
$syntax = substr($file, -4, 4);
|
24 |
+
|
25 |
+
$options = array(
|
26 |
+
'style' => 'expanded',
|
27 |
+
'cache' => FALSE,
|
28 |
+
'syntax' => $syntax,
|
29 |
+
'debug' => FALSE,
|
30 |
+
'callbacks' => array(
|
31 |
+
'warn' => 'warn',
|
32 |
+
'debug' => 'debug'
|
33 |
+
),
|
34 |
+
);
|
35 |
+
|
36 |
+
// Execute the compiler.
|
37 |
+
$parser = new SassParser($options);
|
38 |
+
try {
|
39 |
+
print "\n\n" . $parser->toCss($file);
|
40 |
+
} catch (Exception $e) {
|
41 |
+
print $e->getMessage();
|
42 |
+
}
|
lib/phpsass/composer.json
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"name": "richthegeek/phpsass",
|
3 |
+
"description": "PHPSass is a PHP compiler for SASS, a popular CSS pre-processor language.",
|
4 |
+
"license": "BSD-2-Clause",
|
5 |
+
"homepage": "http://phpsass.com",
|
6 |
+
"support": {
|
7 |
+
"issues": "https://github.com/richthegeek/phpsass/issues",
|
8 |
+
"source": "https://github.com/richthegeek/phpsass"
|
9 |
+
},
|
10 |
+
"authors": [
|
11 |
+
{
|
12 |
+
"name": "Richard Lyon",
|
13 |
+
"homepage": "https://github.com/richthegeek"
|
14 |
+
},
|
15 |
+
{
|
16 |
+
"name": "Sebastian Siemssen",
|
17 |
+
"homepage": "https://twitter.com/thefubhy"
|
18 |
+
},
|
19 |
+
{
|
20 |
+
"name": "Steve Jones",
|
21 |
+
"homepage": "https://github.com/darthsteven"
|
22 |
+
},
|
23 |
+
{
|
24 |
+
"name": "Sam Richard",
|
25 |
+
"homepage": "https://github.com/snugug"
|
26 |
+
}
|
27 |
+
],
|
28 |
+
"autoload": {
|
29 |
+
"classmap": [
|
30 |
+
"script/",
|
31 |
+
"renderers/",
|
32 |
+
"tree/",
|
33 |
+
"SassException.php",
|
34 |
+
"SassFile.php",
|
35 |
+
"SassParser.php"
|
36 |
+
]
|
37 |
+
}
|
38 |
+
}
|
lib/phpsass/renderers/SassCompactRenderer.php
ADDED
@@ -0,0 +1,132 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* SVN FILE: $Id$ */
|
3 |
+
/**
|
4 |
+
* SassCompactRenderer class file.
|
5 |
+
* @author Chris Yates <chris.l.yates@gmail.com>
|
6 |
+
* @copyright Copyright (c) 2010 PBM Web Development
|
7 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
8 |
+
* @package PHamlP
|
9 |
+
* @subpackage Sass.renderers
|
10 |
+
*/
|
11 |
+
|
12 |
+
require_once('SassCompressedRenderer.php');
|
13 |
+
|
14 |
+
/**
|
15 |
+
* SassCompactRenderer class.
|
16 |
+
* Each CSS rule takes up only one line, with every property defined on that
|
17 |
+
* line. Nested rules are placed next to each other with no newline, while
|
18 |
+
* groups of rules have newlines between them.
|
19 |
+
* @package PHamlP
|
20 |
+
* @subpackage Sass.renderers
|
21 |
+
*/
|
22 |
+
class SassCompactRenderer extends SassCompressedRenderer {
|
23 |
+
const DEBUG_INFO_RULE = '@media -sass-debug-info';
|
24 |
+
const DEBUG_INFO_PROPERTY = 'font-family';
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Renders the brace between the selectors and the properties
|
28 |
+
* @return string the brace between the selectors and the properties
|
29 |
+
*/
|
30 |
+
protected function between() {
|
31 |
+
return ' { ';
|
32 |
+
}
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Renders the brace at the end of the rule
|
36 |
+
* @return string the brace between the rule and its properties
|
37 |
+
*/
|
38 |
+
protected function end() {
|
39 |
+
return " }\n";
|
40 |
+
}
|
41 |
+
|
42 |
+
/**
|
43 |
+
* Renders a comment.
|
44 |
+
* Comments preceeding a rule are on their own line.
|
45 |
+
* Comments within a rule are on the same line as the rule.
|
46 |
+
* @param SassNode the node being rendered
|
47 |
+
* @return string the rendered commnt
|
48 |
+
*/
|
49 |
+
public function renderComment($node) {
|
50 |
+
$nl = ($node->parent instanceof SassRuleNode?'':"\n");
|
51 |
+
return "$nl/* " . join("\n * ", $node->children) . " */$nl" ;
|
52 |
+
}
|
53 |
+
|
54 |
+
/**
|
55 |
+
* Renders a directive.
|
56 |
+
* @param SassNode the node being rendered
|
57 |
+
* @param array properties of the directive
|
58 |
+
* @return string the rendered directive
|
59 |
+
*/
|
60 |
+
public function renderDirective($node, $properties) {
|
61 |
+
return str_replace("\n", '', parent::renderDirective($node, $properties)) .
|
62 |
+
"\n\n";
|
63 |
+
}
|
64 |
+
|
65 |
+
/**
|
66 |
+
* Renders properties.
|
67 |
+
* @param SassNode the node being rendered
|
68 |
+
* @param array properties to render
|
69 |
+
* @return string the rendered properties
|
70 |
+
*/
|
71 |
+
public function renderProperties($node, $properties) {
|
72 |
+
return join(' ', $properties);
|
73 |
+
}
|
74 |
+
|
75 |
+
/**
|
76 |
+
* Renders a property.
|
77 |
+
* @param SassNode the node being rendered
|
78 |
+
* @return string the rendered property
|
79 |
+
*/
|
80 |
+
public function renderProperty($node) {
|
81 |
+
$node->important = $node->important ? ' !important' : '';
|
82 |
+
return "{$node->name}: {$node->value}{$node->important};";
|
83 |
+
}
|
84 |
+
|
85 |
+
/**
|
86 |
+
* Renders a rule.
|
87 |
+
* @param SassNode the node being rendered
|
88 |
+
* @param array rule properties
|
89 |
+
* @param string rendered rules
|
90 |
+
* @return string the rendered rule
|
91 |
+
*/
|
92 |
+
public function renderRule($node, $properties, $rules) {
|
93 |
+
return $this->renderDebug($node) . parent::renderRule($node, $properties,
|
94 |
+
str_replace("\n\n", "\n", $rules)) . "\n";
|
95 |
+
}
|
96 |
+
|
97 |
+
/**
|
98 |
+
* Renders debug information.
|
99 |
+
* If the node has the debug_info options set true the line number and filename
|
100 |
+
* are rendered in a format compatible with
|
101 |
+
* {@link https://addons.mozilla.org/en-US/firefox/addon/103988/ FireSass}.
|
102 |
+
* Else if the node has the line_numbers option set true the line number and
|
103 |
+
* filename are rendered in a comment.
|
104 |
+
* @param SassNode the node being rendered
|
105 |
+
* @return string the debug information
|
106 |
+
*/
|
107 |
+
protected function renderDebug($node) {
|
108 |
+
$indent = $this->getIndent($node);
|
109 |
+
$debug = '';
|
110 |
+
|
111 |
+
if ($node->debug_info) {
|
112 |
+
|
113 |
+
$debug = $indent . self::DEBUG_INFO_RULE . '{';
|
114 |
+
$debug .= 'filename{' . self::DEBUG_INFO_PROPERTY . ':' . preg_replace('/([^-\w])/', '\\\\\1', "file://{$node->filename}") . ';}';
|
115 |
+
$debug .= 'line{' . self::DEBUG_INFO_PROPERTY . ":'{$node->line}';}";
|
116 |
+
$debug .= "}\n";
|
117 |
+
}
|
118 |
+
elseif ($node->line_numbers) {
|
119 |
+
$debug .= "$indent/* line {$node->line} {$node->filename} */\n";
|
120 |
+
}
|
121 |
+
return $debug;
|
122 |
+
}
|
123 |
+
|
124 |
+
/**
|
125 |
+
* Renders rule selectors.
|
126 |
+
* @param SassNode the node being rendered
|
127 |
+
* @return string the rendered selectors
|
128 |
+
*/
|
129 |
+
protected function renderSelectors($node) {
|
130 |
+
return join(', ', $node->selectors);
|
131 |
+
}
|
132 |
+
}
|
lib/phpsass/renderers/SassCompressedRenderer.php
ADDED
@@ -0,0 +1,113 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* SVN FILE: $Id$ */
|
3 |
+
/**
|
4 |
+
* SassCompressedRenderer class file.
|
5 |
+
* @author Chris Yates <chris.l.yates@gmail.com>
|
6 |
+
* @copyright Copyright (c) 2010 PBM Web Development
|
7 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
8 |
+
* @package PHamlP
|
9 |
+
* @subpackage Sass.renderers
|
10 |
+
*/
|
11 |
+
/**
|
12 |
+
* SassCompressedRenderer class.
|
13 |
+
* Compressed style takes up the minimum amount of space possible, having no
|
14 |
+
* whitespace except that necessary to separate selectors and a newline at the
|
15 |
+
* end of the file. It's not meant to be human-readable
|
16 |
+
* @package PHamlP
|
17 |
+
* @subpackage Sass.renderers
|
18 |
+
*/
|
19 |
+
class SassCompressedRenderer extends SassRenderer {
|
20 |
+
/**
|
21 |
+
* Renders the brace between the selectors and the properties
|
22 |
+
* @return string the brace between the selectors and the properties
|
23 |
+
*/
|
24 |
+
protected function between() {
|
25 |
+
return '{';
|
26 |
+
}
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Renders the brace at the end of the rule
|
30 |
+
* @return string the brace between the rule and its properties
|
31 |
+
*/
|
32 |
+
protected function end() {
|
33 |
+
return '}';
|
34 |
+
}
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Returns the indent string for the node
|
38 |
+
* @param SassNode the node to return the indent string for
|
39 |
+
* @return string the indent string for this SassNode
|
40 |
+
*/
|
41 |
+
protected function getIndent($node) {
|
42 |
+
return '';
|
43 |
+
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Renders a comment.
|
47 |
+
* @param SassNode the node being rendered
|
48 |
+
* @return string the rendered comment
|
49 |
+
*/
|
50 |
+
public function renderComment($node) {
|
51 |
+
return '';
|
52 |
+
}
|
53 |
+
|
54 |
+
/**
|
55 |
+
* Renders a directive.
|
56 |
+
* @param SassNode the node being rendered
|
57 |
+
* @param array properties of the directive
|
58 |
+
* @return string the rendered directive
|
59 |
+
*/
|
60 |
+
public function renderDirective($node, $properties) {
|
61 |
+
return $node->directive . $this->between() . $this->renderProperties($node, $properties) . $this->end();
|
62 |
+
}
|
63 |
+
|
64 |
+
/**
|
65 |
+
* Renders properties.
|
66 |
+
* @param SassNode the node being rendered
|
67 |
+
* @param array properties to render
|
68 |
+
* @return string the rendered properties
|
69 |
+
*/
|
70 |
+
public function renderProperties($node, $properties) {
|
71 |
+
return join('', $properties);
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* Renders a property.
|
76 |
+
* @param SassNode the node being rendered
|
77 |
+
* @return string the rendered property
|
78 |
+
*/
|
79 |
+
public function renderProperty($node) {
|
80 |
+
$node->important = $node->important ? '!important' : '';
|
81 |
+
return "{$node->name}:{$node->value}{$node->important};";
|
82 |
+
}
|
83 |
+
|
84 |
+
/**
|
85 |
+
* Renders a rule.
|
86 |
+
* @param SassNode the node being rendered
|
87 |
+
* @param array rule properties
|
88 |
+
* @param string rendered rules
|
89 |
+
* @return string the rendered directive
|
90 |
+
*/
|
91 |
+
public function renderRule($node, $properties, $rules) {
|
92 |
+
$selectors = $this->renderSelectors($node);
|
93 |
+
if ($selectors) {
|
94 |
+
return (!empty($properties) ? $selectors . $this->between() . $this->renderProperties($node, $properties) . $this->end() : '') . $rules;
|
95 |
+
}
|
96 |
+
}
|
97 |
+
|
98 |
+
/**
|
99 |
+
* Renders the rule's selectors
|
100 |
+
* @param SassNode the node being rendered
|
101 |
+
* @return string the rendered selectors
|
102 |
+
*/
|
103 |
+
protected function renderSelectors($node) {
|
104 |
+
$selectors = array();
|
105 |
+
foreach ($node->selectors as $selector) {
|
106 |
+
if (!$node->isPlaceholder($selector)) {
|
107 |
+
$selectors[] = $selector;
|
108 |
+
}
|
109 |
+
}
|
110 |
+
|
111 |
+
return join(',', $selectors);
|
112 |
+
}
|
113 |
+
}
|
lib/phpsass/renderers/SassExpandedRenderer.php
ADDED
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* SVN FILE: $Id$ */
|
3 |
+
/**
|
4 |
+
* SassExpandedRenderer class file.
|
5 |
+
* @author Chris Yates <chris.l.yates@gmail.com>
|
6 |
+
* @copyright Copyright (c) 2010 PBM Web Development
|
7 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
8 |
+
* @package PHamlP
|
9 |
+
* @subpackage Sass.renderers
|
10 |
+
*/
|
11 |
+
|
12 |
+
require_once('SassCompactRenderer.php');
|
13 |
+
|
14 |
+
/**
|
15 |
+
* SassExpandedRenderer class.
|
16 |
+
* Expanded is the typical human-made CSS style, with each property and rule
|
17 |
+
* taking up one line. Properties are indented within the rules, but the rules
|
18 |
+
* are not indented in any special way.
|
19 |
+
* @package PHamlP
|
20 |
+
* @subpackage Sass.renderers
|
21 |
+
*/
|
22 |
+
class SassExpandedRenderer extends SassCompactRenderer {
|
23 |
+
/**
|
24 |
+
* Renders the brace between the selectors and the properties
|
25 |
+
* @return string the brace between the selectors and the properties
|
26 |
+
*/
|
27 |
+
protected function between() {
|
28 |
+
return " {\n" ;
|
29 |
+
}
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Renders the brace at the end of the rule
|
33 |
+
* @return string the brace between the rule and its properties
|
34 |
+
*/
|
35 |
+
protected function end() {
|
36 |
+
return "\n}\n\n";
|
37 |
+
}
|
38 |
+
|
39 |
+
/**
|
40 |
+
* Renders a comment.
|
41 |
+
* @param SassNode the node being rendered
|
42 |
+
* @return string the rendered commnt
|
43 |
+
*/
|
44 |
+
public function renderComment($node) {
|
45 |
+
$indent = $this->getIndent($node);
|
46 |
+
$lines = explode("\n", $node->value);
|
47 |
+
foreach ($lines as &$line) {
|
48 |
+
$line = trim($line);
|
49 |
+
}
|
50 |
+
return "$indent/*\n$indent * ".join("\n$indent * ", $lines)."\n$indent */".(empty($indent)?"\n":'');
|
51 |
+
}
|
52 |
+
|
53 |
+
/**
|
54 |
+
* Renders properties.
|
55 |
+
* @param array properties to render
|
56 |
+
* @return string the rendered properties
|
57 |
+
*/
|
58 |
+
public function renderProperties($node, $properties) {
|
59 |
+
$indent = $this->getIndent($node).self::INDENT;
|
60 |
+
return $indent.join("\n$indent", $properties);
|
61 |
+
}
|
62 |
+
}
|
lib/phpsass/renderers/SassNestedRenderer.php
ADDED
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* SVN FILE: $Id$ */
|
3 |
+
/**
|
4 |
+
* SassNestedRenderer class file.
|
5 |
+
* @author Chris Yates <chris.l.yates@gmail.com>
|
6 |
+
* @copyright Copyright (c) 2010 PBM Web Development
|
7 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
8 |
+
* @package PHamlP
|
9 |
+
* @subpackage Sass.renderers
|
10 |
+
*/
|
11 |
+
|
12 |
+
require_once('SassExpandedRenderer.php');
|
13 |
+
|
14 |
+
/**
|
15 |
+
* SassNestedRenderer class.
|
16 |
+
* Nested style is the default Sass style, because it reflects the structure of
|
17 |
+
* the document in much the same way Sass does. Each rule is indented based on
|
18 |
+
* how deeply it's nested. Each property has its own line and is indented
|
19 |
+
* within the rule.
|
20 |
+
* @package PHamlP
|
21 |
+
* @subpackage Sass.renderers
|
22 |
+
*/
|
23 |
+
class SassNestedRenderer extends SassExpandedRenderer {
|
24 |
+
/**
|
25 |
+
* Renders the brace at the end of the rule
|
26 |
+
* @return string the brace between the rule and its properties
|
27 |
+
*/
|
28 |
+
protected function end() {
|
29 |
+
return " }\n";
|
30 |
+
}
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Returns the indent string for the node
|
34 |
+
* @param SassNode the node being rendered
|
35 |
+
* @return string the indent string for this SassNode
|
36 |
+
*/
|
37 |
+
protected function getIndent($node) {
|
38 |
+
return str_repeat(self::INDENT, $node->level);
|
39 |
+
}
|
40 |
+
|
41 |
+
/**
|
42 |
+
* Renders a directive.
|
43 |
+
* @param SassNode the node being rendered
|
44 |
+
* @param array properties of the directive
|
45 |
+
* @return string the rendered directive
|
46 |
+
*/
|
47 |
+
public function renderDirective($node, $properties) {
|
48 |
+
$directive = $this->getIndent($node) . $node->directive . $this->between() . $this->renderProperties($node, $properties);
|
49 |
+
return preg_replace('/(.*})\n$/', '\1', $directive) . $this->end();
|
50 |
+
}
|
51 |
+
|
52 |
+
/**
|
53 |
+
* Renders rule selectors.
|
54 |
+
* @param SassNode the node being rendered
|
55 |
+
* @return string the rendered selectors
|
56 |
+
*/
|
57 |
+
protected function renderSelectors($node) {
|
58 |
+
$selectors = array();
|
59 |
+
foreach ($node->selectors as $selector) {
|
60 |
+
if (!$node->isPlaceholder($selector)) {
|
61 |
+
$selectors[] = $selector;
|
62 |
+
}
|
63 |
+
}
|
64 |
+
|
65 |
+
$indent = $this->getIndent($node);
|
66 |
+
return $indent.join(",\n$indent", $selectors);
|
67 |
+
}
|
68 |
+
}
|
lib/phpsass/renderers/SassRenderer.php
ADDED
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* SVN FILE: $Id$ */
|
3 |
+
/**
|
4 |
+
* SassRenderer class file.
|
5 |
+
* @author Chris Yates <chris.l.yates@gmail.com>
|
6 |
+
* @copyright Copyright (c) 2010 PBM Web Development
|
7 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
8 |
+
* @package PHamlP
|
9 |
+
* @subpackage Sass.renderers
|
10 |
+
*/
|
11 |
+
|
12 |
+
require_once('SassCompactRenderer.php');
|
13 |
+
require_once('SassCompressedRenderer.php');
|
14 |
+
require_once('SassExpandedRenderer.php');
|
15 |
+
require_once('SassNestedRenderer.php');
|
16 |
+
|
17 |
+
/**
|
18 |
+
* SassRenderer class.
|
19 |
+
* @package PHamlP
|
20 |
+
* @subpackage Sass.renderers
|
21 |
+
*/
|
22 |
+
class SassRenderer {
|
23 |
+
/**#@+
|
24 |
+
* Output Styles
|
25 |
+
*/
|
26 |
+
const STYLE_COMPRESSED = 'compressed';
|
27 |
+
const STYLE_COMPACT = 'compact';
|
28 |
+
const STYLE_EXPANDED = 'expanded';
|
29 |
+
const STYLE_NESTED = 'nested';
|
30 |
+
/**#@-*/
|
31 |
+
|
32 |
+
const INDENT = ' ';
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Returns the renderer for the required render style.
|
36 |
+
* @param string render style
|
37 |
+
* @return SassRenderer
|
38 |
+
*/
|
39 |
+
public static function getRenderer($style) {
|
40 |
+
switch ($style) {
|
41 |
+
case self::STYLE_COMPACT:
|
42 |
+
return new SassCompactRenderer();
|
43 |
+
case self::STYLE_COMPRESSED:
|
44 |
+
return new SassCompressedRenderer();
|
45 |
+
case self::STYLE_EXPANDED:
|
46 |
+
return new SassExpandedRenderer();
|
47 |
+
case self::STYLE_NESTED:
|
48 |
+
return new SassNestedRenderer();
|
49 |
+
} // switch
|
50 |
+
}
|
51 |
+
}
|
lib/phpsass/script/SassScriptFunction.php
ADDED
@@ -0,0 +1,290 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* SassScriptFunction class file.
|
4 |
+
* @author Chris Yates <chris.l.yates@gmail.com>
|
5 |
+
* @copyright Copyright (c) 2010 PBM Web Development
|
6 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
7 |
+
* @package PHamlP
|
8 |
+
* @subpackage Sass.script
|
9 |
+
*/
|
10 |
+
|
11 |
+
require_once('SassScriptFunctions.php');
|
12 |
+
|
13 |
+
/**
|
14 |
+
* SassScriptFunction class.
|
15 |
+
* Preforms a SassScript function.
|
16 |
+
* @package PHamlP
|
17 |
+
* @subpackage Sass.script
|
18 |
+
*/
|
19 |
+
class SassScriptFunction {
|
20 |
+
/**@#+
|
21 |
+
* Regexes for matching and extracting functions and arguments
|
22 |
+
*/
|
23 |
+
const MATCH = '/^(((-\w)|(\w))[-\w]*)\(/';
|
24 |
+
const MATCH_FUNC = '/^((?:(?:-\w)|(?:\w))[-\w]*)\((.*)\)/';
|
25 |
+
const SPLIT_ARGS = '/\s*((?:[\'"].*?["\'])|(?:.+?(?:\(.*\).*?)?))\s*(?:,|$)/';
|
26 |
+
const NAME = 1;
|
27 |
+
const ARGS = 2;
|
28 |
+
|
29 |
+
private $name;
|
30 |
+
private $args;
|
31 |
+
|
32 |
+
public static $context;
|
33 |
+
|
34 |
+
/**
|
35 |
+
* SassScriptFunction constructor
|
36 |
+
* @param string name of the function
|
37 |
+
* @param array arguments for the function
|
38 |
+
* @return SassScriptFunction
|
39 |
+
*/
|
40 |
+
public function __construct($name, $args) {
|
41 |
+
$this->name = $name;
|
42 |
+
$this->args = $args;
|
43 |
+
}
|
44 |
+
|
45 |
+
private function process_arguments($input) {
|
46 |
+
if (is_array($input)) {
|
47 |
+
$output = array();
|
48 |
+
foreach ($input as $k => $token) {
|
49 |
+
$output[$k] = trim($this->process_arguments($token), '\'"');
|
50 |
+
}
|
51 |
+
return $output;
|
52 |
+
}
|
53 |
+
|
54 |
+
$token = $input;
|
55 |
+
if (is_null($token))
|
56 |
+
return ' ';
|
57 |
+
|
58 |
+
if (!is_object($token))
|
59 |
+
return (string) $token;
|
60 |
+
|
61 |
+
if (method_exists($token, 'toString'))
|
62 |
+
return $token->toString();
|
63 |
+
|
64 |
+
if (method_exists($token, '__toString'))
|
65 |
+
return $token->__toString();
|
66 |
+
|
67 |
+
if (method_exists($token, 'perform'))
|
68 |
+
return $token->perform();
|
69 |
+
|
70 |
+
return '';
|
71 |
+
}
|
72 |
+
|
73 |
+
/**
|
74 |
+
* Evaluates the function.
|
75 |
+
* Look for a user defined function first - this allows users to override
|
76 |
+
* pre-defined functions, then try the pre-defined functions.
|
77 |
+
* @return Function the value of this Function
|
78 |
+
*/
|
79 |
+
public function perform() {
|
80 |
+
self::$context = new SassContext(SassScriptParser::$context);
|
81 |
+
|
82 |
+
$name = preg_replace('/[^a-z0-9_]/', '_', strtolower($this->name));
|
83 |
+
$args = $this->process_arguments($this->args);
|
84 |
+
|
85 |
+
foreach ($this->args as $k => $v) {
|
86 |
+
if (!is_numeric($k)) {
|
87 |
+
self::$context->setVariable($k, $v);
|
88 |
+
}
|
89 |
+
}
|
90 |
+
|
91 |
+
try {
|
92 |
+
if (SassScriptParser::$context->hasFunction($this->name)) {
|
93 |
+
$return = SassScriptParser::$context->getFunction($this->name)->execute(SassScriptParser::$context, $this->args);
|
94 |
+
return $return;
|
95 |
+
}
|
96 |
+
else if (SassScriptParser::$context->hasFunction($name)) {
|
97 |
+
$return = SassScriptParser::$context->getFunction($name)->execute(SassScriptParser::$context, $this->args);
|
98 |
+
return $return;
|
99 |
+
}
|
100 |
+
} catch (Exception $e) {
|
101 |
+
throw $e;
|
102 |
+
}
|
103 |
+
|
104 |
+
if (isset(SassParser::$functions) && count(SassParser::$functions)) {
|
105 |
+
foreach (SassParser::$functions as $fn => $callback) {
|
106 |
+
if (($fn == $name || $fn == $this->name) && is_callable($callback)) {
|
107 |
+
$result = call_user_func_array($callback, $args);
|
108 |
+
if (!is_object($result)) {
|
109 |
+
$lexed = SassScriptLexer::$instance->lex($result, self::$context);
|
110 |
+
if (count($lexed) === 1) {
|
111 |
+
return $lexed[0];
|
112 |
+
}
|
113 |
+
return new SassString(implode('', $this->process_arguments($lexed)));
|
114 |
+
}
|
115 |
+
return $result;
|
116 |
+
}
|
117 |
+
}
|
118 |
+
}
|
119 |
+
|
120 |
+
if (method_exists('SassScriptFunctions', $name) || method_exists('SassScriptFunctions', $name = '_' . $name)) {
|
121 |
+
$sig = self::get_reflection(array('SassScriptFunctions', $name));
|
122 |
+
list($args) = self::fill_parameters($sig, $this->args, SassScriptParser::$context, $this);
|
123 |
+
return call_user_func_array(array('SassScriptFunctions', $name), $args);
|
124 |
+
}
|
125 |
+
|
126 |
+
foreach ($this->args as $i => $arg) {
|
127 |
+
if (is_object($arg) && isset($arg->quote)) {
|
128 |
+
$args[$i] = $arg->toString();
|
129 |
+
}
|
130 |
+
if (!is_numeric($i) && SassScriptParser::$context->hasVariable($i)) {
|
131 |
+
$args[$i] = SassScriptParser::$context->getVariable($i);
|
132 |
+
}
|
133 |
+
}
|
134 |
+
|
135 |
+
|
136 |
+
// CSS function: create a SassString that will emit the function into the CSS
|
137 |
+
return new SassString($this->name . '(' . join(', ', $args) . ')');
|
138 |
+
}
|
139 |
+
|
140 |
+
/**
|
141 |
+
* Imports files in the specified directory.
|
142 |
+
* @param string path to directory to import
|
143 |
+
* @return array filenames imported
|
144 |
+
*/
|
145 |
+
private function import($dir) {
|
146 |
+
$files = array();
|
147 |
+
|
148 |
+
foreach (array_slice(scandir($dir), 2) as $file) {
|
149 |
+
if (is_file($dir . DIRECTORY_SEPARATOR . $file)) {
|
150 |
+
$files[] = $file;
|
151 |
+
require_once($dir . DIRECTORY_SEPARATOR . $file);
|
152 |
+
}
|
153 |
+
} // foreach
|
154 |
+
return $files;
|
155 |
+
}
|
156 |
+
|
157 |
+
/**
|
158 |
+
* Returns a value indicating if a token of this type can be matched at
|
159 |
+
* the start of the subject string.
|
160 |
+
* @param string the subject string
|
161 |
+
* @return mixed match at the start of the string or false if no match
|
162 |
+
*/
|
163 |
+
public static function isa($subject) {
|
164 |
+
if (!preg_match(self::MATCH, $subject, $matches))
|
165 |
+
return false;
|
166 |
+
|
167 |
+
$match = $matches[0];
|
168 |
+
$paren = 1;
|
169 |
+
$strpos = strlen($match);
|
170 |
+
$strlen = strlen($subject);
|
171 |
+
$subject_str = (string) $subject;
|
172 |
+
|
173 |
+
while($paren && $strpos < $strlen) {
|
174 |
+
$c = $subject_str[$strpos++];
|
175 |
+
|
176 |
+
$match .= $c;
|
177 |
+
if ($c === '(') {
|
178 |
+
$paren += 1;
|
179 |
+
}
|
180 |
+
elseif ($c === ')') {
|
181 |
+
$paren -= 1;
|
182 |
+
}
|
183 |
+
}
|
184 |
+
return $match;
|
185 |
+
}
|
186 |
+
|
187 |
+
public static function extractArgs($string, $include_null = TRUE, $context) {
|
188 |
+
$args = array();
|
189 |
+
$arg = '';
|
190 |
+
$paren = 0;
|
191 |
+
$strpos = 0;
|
192 |
+
$strlen = strlen($string);
|
193 |
+
|
194 |
+
$list = SassList::_build_list($string, ',');
|
195 |
+
$return = array();
|
196 |
+
|
197 |
+
foreach ($list as $k => $value) {
|
198 |
+
if (substr($value, -3, 3) == '...' && preg_match(SassVariableNode::MATCH, substr($value, 0, -3) . ':', $match)) {
|
199 |
+
$list = new SassList($context->getVariable($match[SassVariableNode::NAME]));
|
200 |
+
if (count($list->value) > 1) {
|
201 |
+
$return = array_merge($return, $list->value);
|
202 |
+
continue;
|
203 |
+
}
|
204 |
+
}
|
205 |
+
|
206 |
+
if (strpos($value, ':') !== false && preg_match(SassVariableNode::MATCH, $value, $match)) {
|
207 |
+
$return[$match[SassVariableNode::NAME]] = $match[SassVariableNode::VALUE];
|
208 |
+
} else if(substr($value, 0, 1) == '$' && $include_null) {
|
209 |
+
$return[str_replace('$', '', $value)] = NULL;
|
210 |
+
} elseif ($include_null || $value !== NULL) {
|
211 |
+
$return[] = $value;
|
212 |
+
}
|
213 |
+
}
|
214 |
+
|
215 |
+
return $return;
|
216 |
+
}
|
217 |
+
|
218 |
+
public static function get_reflection($method) {
|
219 |
+
if (is_array($method)) {
|
220 |
+
$class = new ReflectionClass($method[0]);
|
221 |
+
$function = $class->getMethod($method[1]);
|
222 |
+
}
|
223 |
+
else {
|
224 |
+
$function = new ReflectionFunction($method);
|
225 |
+
}
|
226 |
+
|
227 |
+
$return = array();
|
228 |
+
foreach ($function->getParameters() as $parameter) {
|
229 |
+
$default = $parameter->isDefaultValueAvailable() ? $parameter->getDefaultValue() : NULL;
|
230 |
+
if ($default !== NULL) {
|
231 |
+
$parsed = is_bool($default) ? new SassBoolean($default) : SassScriptParser::$instance->evaluate($default, new SassContext());
|
232 |
+
$parsed = ($parsed === NULL) ? new SassString($default) : $parsed;
|
233 |
+
} else {
|
234 |
+
$parsed = $default;
|
235 |
+
}
|
236 |
+
$return[$parameter->getName()] = $parsed; # we evaluate the defaults to get Sass objects.
|
237 |
+
}
|
238 |
+
return $return;
|
239 |
+
}
|
240 |
+
|
241 |
+
public static function fill_parameters($required, $provided, $context, $source) {
|
242 |
+
$context = new SassContext($context);
|
243 |
+
$_required = array_merge(array(), $required); // need to array_merge?
|
244 |
+
$fill = $_required;
|
245 |
+
|
246 |
+
|
247 |
+
foreach ($required as $name=>$default) {
|
248 |
+
// we require that named variables provide a default.
|
249 |
+
if (isset($provided[$name]) && $default !== NULL) {
|
250 |
+
$_required[$name] = $provided[$name];
|
251 |
+
unset($provided[$name]);
|
252 |
+
unset($required[$name]);
|
253 |
+
}
|
254 |
+
}
|
255 |
+
|
256 |
+
// print_r(array($required, $provided, $_required));
|
257 |
+
|
258 |
+
foreach ($required as $name=>$default) {
|
259 |
+
if (count($provided)) {
|
260 |
+
$arg = array_shift($provided);
|
261 |
+
}
|
262 |
+
elseif ($default !== NULL) {
|
263 |
+
$arg = $default;
|
264 |
+
}
|
265 |
+
else {
|
266 |
+
throw new SassMixinNodeException("Function::$name: Required variable ($name) not given.\nFunction defined: " . $source->token->filename . '::' . $source->token->line . "\nFunction used", $source);
|
267 |
+
}
|
268 |
+
// splats
|
269 |
+
if (substr($name, -3, 3) == '...') {
|
270 |
+
unset ($_required[$name]);
|
271 |
+
$name = substr($name, 0, -3);
|
272 |
+
$_required[$name] = new SassList('', ',');
|
273 |
+
$_required[$name]->value = array_merge(array($arg), $provided);
|
274 |
+
continue;
|
275 |
+
} else {
|
276 |
+
$_required[$name] = $arg;
|
277 |
+
}
|
278 |
+
}
|
279 |
+
|
280 |
+
$_required = array_merge($_required, $provided); // any remaining args get tacked onto the end
|
281 |
+
|
282 |
+
foreach ($_required as $key => $value) {
|
283 |
+
if (!is_object($value)) {
|
284 |
+
$_required[$key] = SassScriptParser::$instance->evaluate($value, $context);
|
285 |
+
}
|
286 |
+
}
|
287 |
+
|
288 |
+
return array($_required, $context);
|
289 |
+
}
|
290 |
+
}
|
lib/phpsass/script/SassScriptFunctions.php
ADDED
@@ -0,0 +1,963 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* SassScript functions class file.
|
4 |
+
*
|
5 |
+
* Methods in this module are accessible from the SassScript context.
|
6 |
+
* For example, you can write:
|
7 |
+
*
|
8 |
+
* $colour = hsl(120, 100%, 50%)
|
9 |
+
* and it will call SassFunctions::hsl().
|
10 |
+
*
|
11 |
+
* There are a few things to keep in mind when modifying this module.
|
12 |
+
* First of all, the arguments passed are SassLiteral objects.
|
13 |
+
* Literal objects are also expected to be returned.
|
14 |
+
*
|
15 |
+
* Most Literal objects support the SassLiteral->value accessor
|
16 |
+
* for getting their values. Colour objects, though, must be accessed using
|
17 |
+
* SassColour::rgb().
|
18 |
+
*
|
19 |
+
* Second, making functions accessible from Sass introduces the temptation
|
20 |
+
* to do things like database access within stylesheets.
|
21 |
+
* This temptation must be resisted.
|
22 |
+
* Keep in mind that Sass stylesheets are only compiled once and then left as
|
23 |
+
* static CSS files. Any dynamic CSS should be left in <style> tags in the
|
24 |
+
* HTML.
|
25 |
+
*
|
26 |
+
* @author Chris Yates <chris.l.yates@gmail.com>
|
27 |
+
* @copyright Copyright (c) 2010 PBM Web Development
|
28 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
29 |
+
* @package PHamlP
|
30 |
+
* @subpackage Sass.script
|
31 |
+
*/
|
32 |
+
|
33 |
+
/**
|
34 |
+
* SassScript functions class.
|
35 |
+
* A collection of functions for use in SassSCript.
|
36 |
+
* @package PHamlP
|
37 |
+
* @subpackage Sass.script
|
38 |
+
*/
|
39 |
+
class SassScriptFunctions {
|
40 |
+
const DECREASE = false;
|
41 |
+
const INCREASE = true;
|
42 |
+
|
43 |
+
public static $parser = FALSE;
|
44 |
+
public static function option($name) {
|
45 |
+
$options = SassParser::$instance->getOptions();
|
46 |
+
if (isset($options[$name->value])) {
|
47 |
+
return new SassString($options[$name->value]);
|
48 |
+
}
|
49 |
+
return new SassBoolean(false);
|
50 |
+
}
|
51 |
+
|
52 |
+
/*
|
53 |
+
* Colour Creation
|
54 |
+
*/
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Creates a SassColour object from red, green, and blue values.
|
58 |
+
* @param SassNumber the red component.
|
59 |
+
* A number between 0 and 255 inclusive, or between 0% and 100% inclusive
|
60 |
+
* @param SassNumber the green component.
|
61 |
+
* A number between 0 and 255 inclusive, or between 0% and 100% inclusive
|
62 |
+
* @param SassNumber the blue component.
|
63 |
+
* A number between 0 and 255 inclusive, or between 0% and 100% inclusive
|
64 |
+
* @return new SassColour SassColour object
|
65 |
+
* @throws SassScriptFunctionException if red, green, or blue are out of bounds
|
66 |
+
*/
|
67 |
+
public static function rgb($red, $green, $blue) {
|
68 |
+
return self::rgba($red, $green, $blue, new SassNumber(1));
|
69 |
+
}
|
70 |
+
|
71 |
+
/**
|
72 |
+
* Creates a SassColour object from red, green, and blue values and alpha
|
73 |
+
* channel (opacity).
|
74 |
+
* There are two overloads:
|
75 |
+
* * rgba(red, green, blue, alpha)
|
76 |
+
* @param SassNumber the red component.
|
77 |
+
* A number between 0 and 255 inclusive, or between 0% and 100% inclusive
|
78 |
+
* @param SassNumber the green component.
|
79 |
+
* A number between 0 and 255 inclusive, or between 0% and 100% inclusive
|
80 |
+
* @param SassNumber the blue component.
|
81 |
+
* A number between 0 and 255 inclusive, or between 0% and 100% inclusive
|
82 |
+
* @param SassNumber The alpha channel. A number between 0 and 1.
|
83 |
+
*
|
84 |
+
* * rgba(colour, alpha)
|
85 |
+
* @param SassColour a SassColour object
|
86 |
+
* @param SassNumber The alpha channel. A number between 0 and 1.
|
87 |
+
*
|
88 |
+
* @return new SassColour SassColour object
|
89 |
+
* @throws SassScriptFunctionException if any of the red, green, or blue
|
90 |
+
* colour components are out of bounds, or or the colour is not a colour, or
|
91 |
+
* alpha is out of bounds
|
92 |
+
*/
|
93 |
+
public static function rgba() {
|
94 |
+
switch (func_num_args()) {
|
95 |
+
case 2:
|
96 |
+
$colour = func_get_arg(0);
|
97 |
+
$alpha = func_get_arg(1);
|
98 |
+
SassLiteral::assertType($colour, 'SassColour');
|
99 |
+
SassLiteral::assertType($alpha, 'SassNumber');
|
100 |
+
SassLiteral::assertInRange($alpha, 0, 1);
|
101 |
+
return $colour->with(array('alpha' => $alpha->value));
|
102 |
+
break;
|
103 |
+
case 4:
|
104 |
+
$rgba = array();
|
105 |
+
$components = func_get_args();
|
106 |
+
$alpha = array_pop($components);
|
107 |
+
foreach($components as $component) {
|
108 |
+
SassLiteral::assertType($component, 'SassNumber');
|
109 |
+
if ($component->units == '%') {
|
110 |
+
SassLiteral::assertInRange($component, 0, 100, '%');
|
111 |
+
$rgba[] = $component->value * 2.55;
|
112 |
+
}
|
113 |
+
else {
|
114 |
+
SassLiteral::assertInRange($component, 0, 255);
|
115 |
+
$rgba[] = $component->value;
|
116 |
+
}
|
117 |
+
}
|
118 |
+
SassLiteral::assertType($alpha, 'SassNumber');
|
119 |
+
SassLiteral::assertInRange($alpha, 0, 1);
|
120 |
+
$rgba[] = $alpha->value;
|
121 |
+
return new SassColour($rgba);
|
122 |
+
break;
|
123 |
+
default:
|
124 |
+
throw new SassScriptFunctionException('Incorrect argument count for ' . __METHOD__ . '; expected 2 or 4, received ' . func_num_args(), SassScriptParser::$context->node);
|
125 |
+
}
|
126 |
+
}
|
127 |
+
|
128 |
+
/**
|
129 |
+
* Creates a SassColour object from hue, saturation, and lightness.
|
130 |
+
* Uses the algorithm from the
|
131 |
+
* {@link http://www.w3.org/TR/css3-colour/#hsl-colour CSS3 spec}.
|
132 |
+
* @param float The hue of the colour in degrees.
|
133 |
+
* Should be between 0 and 360 inclusive
|
134 |
+
* @param mixed The saturation of the colour as a percentage.
|
135 |
+
* Must be between '0%' and 100%, inclusive
|
136 |
+
* @param mixed The lightness of the colour as a percentage.
|
137 |
+
* Must be between 0% and 100%, inclusive
|
138 |
+
* @return new SassColour The resulting colour
|
139 |
+
* @throws SassScriptFunctionException if saturation or lightness are out of bounds
|
140 |
+
*/
|
141 |
+
public static function hsl($h, $s, $l) {
|
142 |
+
SassLiteral::assertInRange($s, 0, 100, '%');
|
143 |
+
SassLiteral::assertInRange($l, 0, 100, '%');
|
144 |
+
return self::hsla($h, $s, $l, new SassNumber(1));
|
145 |
+
}
|
146 |
+
|
147 |
+
/**
|
148 |
+
* Creates a SassColour object from hue, saturation, lightness and alpha
|
149 |
+
* channel (opacity).
|
150 |
+
* @param SassNumber The hue of the colour in degrees.
|
151 |
+
* Should be between 0 and 360 inclusive
|
152 |
+
* @param SassNumber The saturation of the colour as a percentage.
|
153 |
+
* Must be between 0% and 100% inclusive
|
154 |
+
* @param SassNumber The lightness of the colour as a percentage.
|
155 |
+
* Must be between 0% and 100% inclusive
|
156 |
+
* @param float The alpha channel. A number between 0 and 1.
|
157 |
+
* @return new SassColour The resulting colour
|
158 |
+
* @throws SassScriptFunctionException if saturation, lightness or alpha are
|
159 |
+
* out of bounds
|
160 |
+
*/
|
161 |
+
public static function hsla($h, $s, $l, $a) {
|
162 |
+
SassLiteral::assertType($h, 'SassNumber');
|
163 |
+
SassLiteral::assertType($s, 'SassNumber');
|
164 |
+
SassLiteral::assertType($l, 'SassNumber');
|
165 |
+
SassLiteral::assertType($a, 'SassNumber');
|
166 |
+
SassLiteral::assertInRange($s, 0, 100, '%');
|
167 |
+
SassLiteral::assertInRange($l, 0, 100, '%');
|
168 |
+
SassLiteral::assertInRange($a, 0, 1);
|
169 |
+
return new SassColour(array('hue' => $h, 'saturation' => $s, 'lightness' => $l, 'alpha' => $a));
|
170 |
+
}
|
171 |
+
|
172 |
+
/*
|
173 |
+
* Colour Information
|
174 |
+
*/
|
175 |
+
|
176 |
+
/**
|
177 |
+
* Returns the red component of a colour.
|
178 |
+
* @param SassColour The colour
|
179 |
+
* @return new SassNumber The red component of colour
|
180 |
+
* @throws SassScriptFunctionException If $colour is not a colour
|
181 |
+
*/
|
182 |
+
public static function red($colour) {
|
183 |
+
SassLiteral::assertType($colour, 'SassColour');
|
184 |
+
return new SassNumber($colour->red);
|
185 |
+
}
|
186 |
+
|
187 |
+
/**
|
188 |
+
* Returns the green component of a colour.
|
189 |
+
* @param SassColour The colour
|
190 |
+
* @return new SassNumber The green component of colour
|
191 |
+
* @throws SassScriptFunctionException If $colour is not a colour
|
192 |
+
*/
|
193 |
+
public static function green($colour) {
|
194 |
+
SassLiteral::assertType($colour, 'SassColour');
|
195 |
+
return new SassNumber($colour->green);
|
196 |
+
}
|
197 |
+
|
198 |
+
/**
|
199 |
+
* Returns the blue component of a colour.
|
200 |
+
* @param SassColour The colour
|
201 |
+
* @return new SassNumber The blue component of colour
|
202 |
+
* @throws SassScriptFunctionException If $colour is not a colour
|
203 |
+
*/
|
204 |
+
public static function blue($colour) {
|
205 |
+
SassLiteral::assertType($colour, 'SassColour');
|
206 |
+
return new SassNumber($colour->blue);
|
207 |
+
}
|
208 |
+
|
209 |
+
/**
|
210 |
+
* Returns the hue component of a colour.
|
211 |
+
* @param SassColour The colour
|
212 |
+
* @return new SassNumber The hue component of colour
|
213 |
+
* @throws SassScriptFunctionException If $colour is not a colour
|
214 |
+
*/
|
215 |
+
public static function hue($colour) {
|
216 |
+
SassLiteral::assertType($colour, 'SassColour');
|
217 |
+
return new SassNumber($colour->getHue() . 'deg');
|
218 |
+
}
|
219 |
+
|
220 |
+
/**
|
221 |
+
* Returns the saturation component of a colour.
|
222 |
+
* @param SassColour The colour
|
223 |
+
* @return new SassNumber The saturation component of colour
|
224 |
+
* @throws SassScriptFunctionException If $colour is not a colour
|
225 |
+
*/
|
226 |
+
public static function saturation($colour) {
|
227 |
+
SassLiteral::assertType($colour, 'SassColour');
|
228 |
+
return new SassNumber($colour->getSaturation() . '%');
|
229 |
+
}
|
230 |
+
|
231 |
+
/**
|
232 |
+
* Returns the lightness component of a colour.
|
233 |
+
* @param SassColour The colour
|
234 |
+
* @return new SassNumber The lightness component of colour
|
235 |
+
* @throws SassScriptFunctionException If $colour is not a colour
|
236 |
+
*/
|
237 |
+
public static function lightness($colour) {
|
238 |
+
SassLiteral::assertType($colour, 'SassColour');
|
239 |
+
return new SassNumber($colour->getLightness() . '%');
|
240 |
+
}
|
241 |
+
|
242 |
+
/**
|
243 |
+
* Returns the alpha component (opacity) of a colour.
|
244 |
+
* @param SassColour The colour
|
245 |
+
* @return new SassNumber The alpha component (opacity) of colour
|
246 |
+
* @throws SassScriptFunctionException If $colour is not a colour
|
247 |
+
*
|
248 |
+
* RL modified so that the filter: alpha function doesn't bork
|
249 |
+
*/
|
250 |
+
public static function alpha($colour) {
|
251 |
+
try {
|
252 |
+
SassLiteral::assertType($colour, 'SassColour');
|
253 |
+
}
|
254 |
+
catch (Exception $e) {
|
255 |
+
return new SassString('alpha(100)');
|
256 |
+
}
|
257 |
+
return new SassNumber($colour->alpha);
|
258 |
+
}
|
259 |
+
|
260 |
+
/**
|
261 |
+
* Returns the alpha component (opacity) of a colour.
|
262 |
+
* @param SassColour The colour
|
263 |
+
* @return new SassNumber The alpha component (opacity) of colour
|
264 |
+
* @throws SassScriptFunctionException If $colour is not a colour
|
265 |
+
*/
|
266 |
+
public static function opacity($colour) {
|
267 |
+
SassLiteral::assertType($colour, 'SassColour');
|
268 |
+
return new SassNumber($colour->alpha);
|
269 |
+
}
|
270 |
+
|
271 |
+
/*
|
272 |
+
* Colour Adjustments
|
273 |
+
*/
|
274 |
+
|
275 |
+
/**
|
276 |
+
* Changes the hue of a colour while retaining the lightness and saturation.
|
277 |
+
* @param SassColour The colour to adjust
|
278 |
+
* @param SassNumber The amount to adjust the colour by
|
279 |
+
* @return new SassColour The adjusted colour
|
280 |
+
* @throws SassScriptFunctionException If $colour is not a colour or
|
281 |
+
* $degrees is not a number
|
282 |
+
*/
|
283 |
+
public static function adjust_hue($colour, $degrees) {
|
284 |
+
SassLiteral::assertType($colour, 'SassColour');
|
285 |
+
SassLiteral::assertType($degrees, 'SassNumber');
|
286 |
+
return $colour->with(array('hue' => $colour->getHue(true) + $degrees->value));
|
287 |
+
}
|
288 |
+
|
289 |
+
/**
|
290 |
+
* Makes a colour lighter.
|
291 |
+
* @param SassColour The colour to lighten
|
292 |
+
* @param SassNumber The amount to lighten the colour by
|
293 |
+
* @param SassBoolean Whether the amount is a proportion of the current value
|
294 |
+
* (true) or the total range (false).
|
295 |
+
* The default is false - the amount is a proportion of the total range.
|
296 |
+
* If the colour lightness value is 40% and the amount is 50%,
|
297 |
+
* the resulting colour lightness value is 90% if the amount is a proportion
|
298 |
+
* of the total range, whereas it is 60% if the amount is a proportion of the
|
299 |
+
* current value.
|
300 |
+
* @return new SassColour The lightened colour
|
301 |
+
* @throws SassScriptFunctionException If $colour is not a colour or
|
302 |
+
* $amount is not a number
|
303 |
+
* @see lighten_rel
|
304 |
+
*/
|
305 |
+
public static function lighten($colour, $amount, $ofCurrent = false) {
|
306 |
+
return self::adjust($colour, $amount, $ofCurrent, 'lightness', self::INCREASE, 0, 100, '%');
|
307 |
+
}
|
308 |
+
|
309 |
+
/**
|
310 |
+
* Makes a colour darker.
|
311 |
+
* @param SassColour The colour to darken
|
312 |
+
* @param SassNumber The amount to darken the colour by
|
313 |
+
* @param SassBoolean Whether the amount is a proportion of the current value
|
314 |
+
* (true) or the total range (false).
|
315 |
+
* The default is false - the amount is a proportion of the total range.
|
316 |
+
* If the colour lightness value is 80% and the amount is 50%,
|
317 |
+
* the resulting colour lightness value is 30% if the amount is a proportion
|
318 |
+
* of the total range, whereas it is 40% if the amount is a proportion of the
|
319 |
+
* current value.
|
320 |
+
* @return new SassColour The darkened colour
|
321 |
+
* @throws SassScriptFunctionException If $colour is not a colour or
|
322 |
+
* $amount is not a number
|
323 |
+
* @see adjust
|
324 |
+
*/
|
325 |
+
public static function darken($colour, $amount, $ofCurrent = false) {
|
326 |
+
return self::adjust($colour, $amount, $ofCurrent, 'lightness', self::DECREASE, 0, 100, '%');
|
327 |
+
}
|
328 |
+
|
329 |
+
/**
|
330 |
+
* Makes a colour more saturated.
|
331 |
+
* @param SassColour The colour to saturate
|
332 |
+
* @param SassNumber The amount to saturate the colour by
|
333 |
+
* @param SassBoolean Whether the amount is a proportion of the current value
|
334 |
+
* (true) or the total range (false).
|
335 |
+
* The default is false - the amount is a proportion of the total range.
|
336 |
+
* If the colour saturation value is 40% and the amount is 50%,
|
337 |
+
* the resulting colour saturation value is 90% if the amount is a proportion
|
338 |
+
* of the total range, whereas it is 60% if the amount is a proportion of the
|
339 |
+
* current value.
|
340 |
+
* @return new SassColour The saturated colour
|
341 |
+
* @throws SassScriptFunctionException If $colour is not a colour or
|
342 |
+
* $amount is not a number
|
343 |
+
* @see adjust
|
344 |
+
*/
|
345 |
+
public static function saturate($colour, $amount, $ofCurrent = false) {
|
346 |
+
return self::adjust($colour, $amount, $ofCurrent, 'saturation', self::INCREASE, 0, 100, '%');
|
347 |
+
}
|
348 |
+
|
349 |
+
/**
|
350 |
+
* Makes a colour less saturated.
|
351 |
+
* @param SassColour The colour to desaturate
|
352 |
+
* @param SassNumber The amount to desaturate the colour by
|
353 |
+
* @param SassBoolean Whether the amount is a proportion of the current value
|
354 |
+
* (true) or the total range (false).
|
355 |
+
* The default is false - the amount is a proportion of the total range.
|
356 |
+
* If the colour saturation value is 80% and the amount is 50%,
|
357 |
+
* the resulting colour saturation value is 30% if the amount is a proportion
|
358 |
+
* of the total range, whereas it is 40% if the amount is a proportion of the
|
359 |
+
* current value.
|
360 |
+
* @return new SassColour The desaturateed colour
|
361 |
+
* @throws SassScriptFunctionException If $colour is not a colour or
|
362 |
+
* $amount is not a number
|
363 |
+
* @see adjust
|
364 |
+
*/
|
365 |
+
public static function desaturate($colour, $amount, $ofCurrent = false) {
|
366 |
+
return self::adjust($colour, $amount, $ofCurrent, 'saturation', self::DECREASE, 0, 100, '%');
|
367 |
+
}
|
368 |
+
|
369 |
+
/**
|
370 |
+
* Makes a colour more opaque.
|
371 |
+
* @param SassColour The colour to opacify
|
372 |
+
* @param SassNumber The amount to opacify the colour by
|
373 |
+
* If this is a unitless number between 0 and 1 the adjustment is absolute,
|
374 |
+
* if it is a percentage the adjustment is relative.
|
375 |
+
* If the colour alpha value is 0.4
|
376 |
+
* if the amount is 0.5 the resulting colour alpha value is 0.9,
|
377 |
+
* whereas if the amount is 50% the resulting colour alpha value is 0.6.
|
378 |
+
* @return new SassColour The opacified colour
|
379 |
+
* @throws SassScriptFunctionException If $colour is not a colour or
|
380 |
+
* $amount is not a number
|
381 |
+
* @see opacify_rel
|
382 |
+
*/
|
383 |
+
public static function opacify($colour, $amount, $ofCurrent = false) {
|
384 |
+
$units = self::units($amount);
|
385 |
+
return self::adjust($colour, $amount, $ofCurrent, 'alpha', self::INCREASE, 0, ($units === '%' ? 100 : 1), $units);
|
386 |
+
}
|
387 |
+
|
388 |
+
/**
|
389 |
+
* Makes a colour more transparent.
|
390 |
+
* @param SassColour The colour to transparentize
|
391 |
+
* @param SassNumber The amount to transparentize the colour by.
|
392 |
+
* If this is a unitless number between 0 and 1 the adjustment is absolute,
|
393 |
+
* if it is a percentage the adjustment is relative.
|
394 |
+
* If the colour alpha value is 0.8
|
395 |
+
* if the amount is 0.5 the resulting colour alpha value is 0.3,
|
396 |
+
* whereas if the amount is 50% the resulting colour alpha value is 0.4.
|
397 |
+
* @return new SassColour The transparentized colour
|
398 |
+
* @throws SassScriptFunctionException If $colour is not a colour or
|
399 |
+
* $amount is not a number
|
400 |
+
*/
|
401 |
+
public static function transparentize($colour, $amount, $ofCurrent = false) {
|
402 |
+
$units = self::units($amount);
|
403 |
+
return self::adjust($colour, $amount, $ofCurrent, 'alpha', self::DECREASE, 0, ($units === '%' ? 100 : 1), $units);
|
404 |
+
}
|
405 |
+
|
406 |
+
/**
|
407 |
+
* Makes a colour more opaque.
|
408 |
+
* Alias for {@link opacify}.
|
409 |
+
* @param SassColour The colour to opacify
|
410 |
+
* @param SassNumber The amount to opacify the colour by
|
411 |
+
* @param SassBoolean Whether the amount is a proportion of the current value
|
412 |
+
* (true) or the total range (false).
|
413 |
+
* @return new SassColour The opacified colour
|
414 |
+
* @throws SassScriptFunctionException If $colour is not a colour or
|
415 |
+
* $amount is not a number
|
416 |
+
* @see opacify
|
417 |
+
*/
|
418 |
+
public static function fade_in($colour, $amount, $ofCurrent = false) {
|
419 |
+
return self::opacify($colour, $amount, $ofCurrent);
|
420 |
+
}
|
421 |
+
|
422 |
+
/**
|
423 |
+
* Makes a colour more transparent.
|
424 |
+
* Alias for {@link transparentize}.
|
425 |
+
* @param SassColour The colour to transparentize
|
426 |
+
* @param SassNumber The amount to transparentize the colour by
|
427 |
+
* @param SassBoolean Whether the amount is a proportion of the current value
|
428 |
+
* (true) or the total range (false).
|
429 |
+
* @return new SassColour The transparentized colour
|
430 |
+
* @throws SassScriptFunctionException If $colour is not a colour or
|
431 |
+
* $amount is not a number
|
432 |
+
* @see transparentize
|
433 |
+
*/
|
434 |
+
public static function fade_out($colour, $amount, $ofCurrent = false) {
|
435 |
+
return self::transparentize($colour, $amount, $ofCurrent);
|
436 |
+
}
|
437 |
+
|
438 |
+
/**
|
439 |
+
* Returns the complement of a colour.
|
440 |
+
* Rotates the hue by 180 degrees.
|
441 |
+
* @param SassColour The colour
|
442 |
+
* @return new SassColour The comlemented colour
|
443 |
+
* @uses adjust_hue()
|
444 |
+
*/
|
445 |
+
public static function complement($colour) {
|
446 |
+
// return self::adjust($colour, new SassNumber('180deg'), true, 'hue', self::INCREASE, 0, 360, '');
|
447 |
+
return self::adjust_hue($colour, new SassNumber('180deg'));
|
448 |
+
}
|
449 |
+
|
450 |
+
/**
|
451 |
+
* Greyscale for non-english speakers.
|
452 |
+
* @param SassColour The colour
|
453 |
+
* @return new SassColour The greyscale colour
|
454 |
+
* @see desaturate
|
455 |
+
*/
|
456 |
+
public static function grayscale($colour) {
|
457 |
+
return self::desaturate($colour, new SassNumber(100));
|
458 |
+
}
|
459 |
+
|
460 |
+
/**
|
461 |
+
* Converts a colour to greyscale.
|
462 |
+
* Reduces the saturation to zero.
|
463 |
+
* @param SassColour The colour
|
464 |
+
* @return new SassColour The greyscale colour
|
465 |
+
* @see desaturate
|
466 |
+
*/
|
467 |
+
public static function greyscale($colour) {
|
468 |
+
return self::desaturate($colour, new SassNumber(100));
|
469 |
+
}
|
470 |
+
|
471 |
+
/**
|
472 |
+
* Inverts a colour.
|
473 |
+
* The red, green, and blue values are inverted value = (255 - value)
|
474 |
+
* @param SassColour: the colour
|
475 |
+
* @return new SassColour: the inverted colour
|
476 |
+
*/
|
477 |
+
public static function invert($colour) {
|
478 |
+
SassLiteral::assertType($colour, 'SassColour');
|
479 |
+
return $colour->with(array(
|
480 |
+
'red' => 255 - $colour->getRed(true),
|
481 |
+
'blue' => 255 - $colour->getBlue(true),
|
482 |
+
'green' => 255 - $colour->getGreen(true)
|
483 |
+
));
|
484 |
+
}
|
485 |
+
|
486 |
+
/**
|
487 |
+
* Mixes two colours together.
|
488 |
+
* Takes the average of each of the RGB components, optionally weighted by the
|
489 |
+
* given percentage. The opacity of the colours is also considered when
|
490 |
+
* weighting the components.
|
491 |
+
* The weight specifies the amount of the first colour that should be included
|
492 |
+
* in the returned colour. The default, 50%, means that half the first colour
|
493 |
+
* and half the second colour should be used. 25% means that a quarter of the
|
494 |
+
* first colour and three quarters of the second colour should be used.
|
495 |
+
* For example:
|
496 |
+
* mix(#f00, #00f) => #7f007f
|
497 |
+
* mix(#f00, #00f, 25%) => #3f00bf
|
498 |
+
* mix(rgba(255, 0, 0, 0.5), #00f) => rgba(63, 0, 191, 0.75)
|
499 |
+
*
|
500 |
+
* @param SassColour The first colour
|
501 |
+
* @param SassColour The second colour
|
502 |
+
* @param float Percentage of the first colour to use
|
503 |
+
* @return new SassColour The mixed colour
|
504 |
+
* @throws SassScriptFunctionException If $colour1 or $colour2 is
|
505 |
+
* not a colour
|
506 |
+
*/
|
507 |
+
public static function mix($colour1, $colour2, $weight = '50%') {
|
508 |
+
if (is_object($weight)) {
|
509 |
+
$weight = new SassNumber($weight);
|
510 |
+
}
|
511 |
+
SassLiteral::assertType($colour1, 'SassColour');
|
512 |
+
SassLiteral::assertType($colour2, 'SassColour');
|
513 |
+
SassLiteral::assertType($weight, 'SassNumber');
|
514 |
+
SassLiteral::assertInRange($weight, 0, 100, '%');
|
515 |
+
/*
|
516 |
+
* This algorithm factors in both the user-provided weight
|
517 |
+
* and the difference between the alpha values of the two colours
|
518 |
+
* to decide how to perform the weighted average of the two RGB values.
|
519 |
+
*
|
520 |
+
* It works by first normalizing both parameters to be within [-1, 1],
|
521 |
+
* where 1 indicates "only use colour1", -1 indicates "only use colour 0",
|
522 |
+
* and all values in between indicated a proportionately weighted average.
|
523 |
+
*
|
524 |
+
* Once we have the normalized variables w and a,
|
525 |
+
* we apply the formula (w + a)/(1 + w*a)
|
526 |
+
* to get the combined weight (in [-1, 1]) of colour1.
|
527 |
+
* This formula has two especially nice properties:
|
528 |
+
*
|
529 |
+
* * When either w or a are -1 or 1, the combined weight is also that number
|
530 |
+
* (cases where w * a == -1 are undefined, and handled as a special case).
|
531 |
+
*
|
532 |
+
* * When a is 0, the combined weight is w, and vice versa
|
533 |
+
*
|
534 |
+
* Finally, the weight of colour1 is renormalized to be within [0, 1]
|
535 |
+
* and the weight of colour2 is given by 1 minus the weight of colour1.
|
536 |
+
*/
|
537 |
+
|
538 |
+
$p = $weight->value/100;
|
539 |
+
$w = $p * 2 - 1;
|
540 |
+
$a = $colour1->alpha - $colour2->alpha;
|
541 |
+
|
542 |
+
|
543 |
+
$w1 = ((($w * $a == -1) ? $w : ($w + $a)/(1 + $w * $a)) + 1) / 2;
|
544 |
+
$w2 = 1 - $w1;
|
545 |
+
|
546 |
+
$rgb1 = $colour1->getRgb();
|
547 |
+
$rgb2 = $colour2->getRgb();
|
548 |
+
$rgba = array();
|
549 |
+
foreach ($rgb1 as $key=>$value) {
|
550 |
+
$rgba[$key] = floor(($value * $w1) + ($rgb2[$key] * $w2));
|
551 |
+
} // foreach
|
552 |
+
$rgba[] = floor($colour1->alpha * $p + $colour2->alpha * (1 - $p));
|
553 |
+
|
554 |
+
return new SassColour($rgba);
|
555 |
+
}
|
556 |
+
|
557 |
+
/**
|
558 |
+
* Adjusts one or more property of the color by the value requested.
|
559 |
+
* @param SassColour the colour to adjust
|
560 |
+
* @param SassNumber (red, green, blue, hue, saturation, lightness, alpha) - the amount(s) to adjust by
|
561 |
+
* @return SassColour
|
562 |
+
*/
|
563 |
+
public static function adjust_color($color, $red = 0, $green = 0, $blue = 0, $hue = 0, $saturation = 0, $lightness = 0, $alpha = 0) {
|
564 |
+
foreach (array('red', 'green', 'blue', 'hue', 'saturation', 'lightness', 'alpha') as $property) {
|
565 |
+
$obj = $$property;
|
566 |
+
$color = self::adjust($color, $$property, FALSE, $property, self::INCREASE, 0, 255);
|
567 |
+
}
|
568 |
+
|
569 |
+
return $color;
|
570 |
+
}
|
571 |
+
|
572 |
+
/**
|
573 |
+
* Scales one or more property of the color by the percentage requested.
|
574 |
+
* @param SassColour the colour to adjust
|
575 |
+
* @param SassNumber (red, green, blue, saturation, lightness, alpha) - the amount(s) to scale by
|
576 |
+
* @return SassColour
|
577 |
+
*/
|
578 |
+
public static function scale_color($color, $red = 0, $green = 0, $blue = 0, $saturation = 0, $lightness = 0, $alpha = 0) {
|
579 |
+
$maxes = array(
|
580 |
+
'red' => 255,
|
581 |
+
'green' => 255,
|
582 |
+
'blue' => 255,
|
583 |
+
'saturation' => 100,
|
584 |
+
'lightness' => 100,
|
585 |
+
'alpha' => 1,
|
586 |
+
);
|
587 |
+
$color->rgb2hsl();
|
588 |
+
foreach ($maxes as $property => $max) {
|
589 |
+
$obj = $$property;
|
590 |
+
$scale = 0.01 * $obj->value;
|
591 |
+
$diff = $scale > 0 ? $max - $color->$property : $color->$property;
|
592 |
+
$color->$property = $color->$property + $diff * $scale;
|
593 |
+
}
|
594 |
+
$color->hsl2rgb();
|
595 |
+
return $color;
|
596 |
+
}
|
597 |
+
|
598 |
+
/**
|
599 |
+
* Changes one or more properties of the color to the requested value
|
600 |
+
* @param SassColour - the color to change
|
601 |
+
* @param SassNumber (red, green, blue, hue, saturation, lightness, alpha) - the amounts to scale by
|
602 |
+
* @return SassColour
|
603 |
+
*/
|
604 |
+
public static function change_color($color, $red = false, $green = false, $blue = false, $hue = false, $saturation = false, $lightness = false, $alpha = false) {
|
605 |
+
$attrs = array();
|
606 |
+
foreach (array('red', 'green', 'blue', 'hue', 'saturation', 'lightness', 'alpha') as $i => $property) {
|
607 |
+
$obj = $$property;
|
608 |
+
if ($obj instanceof SassNumber) {
|
609 |
+
$attrs[$property] = $obj->value;
|
610 |
+
}
|
611 |
+
}
|
612 |
+
return $color->with($attrs);
|
613 |
+
}
|
614 |
+
|
615 |
+
/**
|
616 |
+
* Adjusts the colour
|
617 |
+
* @param SassColour the colour to adjust
|
618 |
+
* @param SassNumber the amount to adust by
|
619 |
+
* @param boolean whether the amount is a proportion of the current value or
|
620 |
+
* the total range
|
621 |
+
* @param string the attribute to adjust
|
622 |
+
* @param boolean whether to decrease (false) or increase (true) the value of the attribute
|
623 |
+
* @param float minimum value the amount can be
|
624 |
+
* @param float maximum value the amount can bemixed
|
625 |
+
* @param string amount units
|
626 |
+
*/
|
627 |
+
public static function adjust($colour, $amount, $ofCurrent, $att, $op, $min, $max, $units='') {
|
628 |
+
SassLiteral::assertType($colour, 'SassColour');
|
629 |
+
SassLiteral::assertType($amount, 'SassNumber');
|
630 |
+
// SassLiteral::assertInRange($amount, $min, $max, $units);
|
631 |
+
if (!is_bool($ofCurrent)) {
|
632 |
+
SassLiteral::assertType($ofCurrent, 'SassBoolean');
|
633 |
+
$ofCurrent = $ofCurrent->value;
|
634 |
+
}
|
635 |
+
$colour = clone $colour; # clone here to stop it altering original value
|
636 |
+
|
637 |
+
$amount = $amount->value * (($att === 'alpha' && $ofCurrent && $units === '') ? 100 : 1);
|
638 |
+
|
639 |
+
if ($att == 'red' || $att == 'blue' || $att == 'green') {
|
640 |
+
$colour->hsl2rgb();
|
641 |
+
$colour->$att = $ofCurrent ? $colour->$att * (1 + ($amount * ($op === self::INCREASE ? 1 : -1))/100) : $colour->$att + ($amount * ($op === self::INCREASE ? 1 : -1));
|
642 |
+
$colour->rgb2hsl();
|
643 |
+
}
|
644 |
+
else {
|
645 |
+
$colour->rgb2hsl();
|
646 |
+
$colour->$att = $ofCurrent ? $colour->$att * (1 + ($amount * ($op === self::INCREASE ? 1 : -1))/100) : $colour->$att + ($amount * ($op === self::INCREASE ? 1 : -1));
|
647 |
+
$colour->$att = max($min, min($max, $colour->$att));
|
648 |
+
$colour->hsl2rgb();
|
649 |
+
}
|
650 |
+
return $colour;
|
651 |
+
}
|
652 |
+
|
653 |
+
/**
|
654 |
+
* returns an IE hex string for a color with an alpha channel
|
655 |
+
* suitable for passing to IE filters.
|
656 |
+
*/
|
657 |
+
public static function ie_hex_str($color) {
|
658 |
+
if (!($color instanceof SassColour)) {
|
659 |
+
$color = new SassColour($color);
|
660 |
+
}
|
661 |
+
$alpha = str_replace(',','.',round($color->alpha * 255));
|
662 |
+
$alpha_str = str_pad(dechex($alpha), 2, '0', STR_PAD_LEFT);
|
663 |
+
$col = $color->asHex(FALSE);
|
664 |
+
return new SassString(strtoupper('#' . $alpha_str . $col));
|
665 |
+
}
|
666 |
+
|
667 |
+
|
668 |
+
/*
|
669 |
+
* Number Functions
|
670 |
+
*/
|
671 |
+
|
672 |
+
/**
|
673 |
+
* Finds the absolute value of a number.
|
674 |
+
* For example:
|
675 |
+
* abs(10px) => 10px
|
676 |
+
* abs(-10px) => 10px
|
677 |
+
*
|
678 |
+
* @param SassNumber The number to round
|
679 |
+
* @return SassNumber The absolute value of the number
|
680 |
+
* @throws SassScriptFunctionException If $number is not a number
|
681 |
+
*/
|
682 |
+
public static function abs($number) {
|
683 |
+
SassLiteral::assertType($number, 'SassNumber');
|
684 |
+
return new SassNumber(abs($number->value).$number->units);
|
685 |
+
}
|
686 |
+
|
687 |
+
/**
|
688 |
+
* Rounds a number up to the nearest whole number.
|
689 |
+
* For example:
|
690 |
+
* ceil(10.4px) => 11px
|
691 |
+
* ceil(10.6px) => 11px
|
692 |
+
*
|
693 |
+
* @param SassNumber The number to round
|
694 |
+
* @return new SassNumber The rounded number
|
695 |
+
* @throws SassScriptFunctionException If $number is not a number
|
696 |
+
*/
|
697 |
+
public static function ceil($number) {
|
698 |
+
SassLiteral::assertType($number, 'SassNumber');
|
699 |
+
return new SassNumber(ceil($number->value).$number->units);
|
700 |
+
}
|
701 |
+
|
702 |
+
/**
|
703 |
+
* Rounds down to the nearest whole number.
|
704 |
+
* For example:
|
705 |
+
* floor(10.4px) => 10px
|
706 |
+
* floor(10.6px) => 10px
|
707 |
+
*
|
708 |
+
* @param SassNumber The number to round
|
709 |
+
* @return new SassNumber The rounded number
|
710 |
+
* @throws SassScriptFunctionException If $value is not a number
|
711 |
+
*/
|
712 |
+
public static function floor($number) {
|
713 |
+
SassLiteral::assertType($number, 'SassNumber');
|
714 |
+
return new SassNumber(floor($number->value).$number->units);
|
715 |
+
}
|
716 |
+
|
717 |
+
/**
|
718 |
+
* Rounds a number to the nearest whole number.
|
719 |
+
* For example:
|
720 |
+
* round(10.4px) => 10px
|
721 |
+
* round(10.6px) => 11px
|
722 |
+
*
|
723 |
+
* @param SassNumber The number to round
|
724 |
+
* @return new SassNumber The rounded number
|
725 |
+
* @throws SassScriptFunctionException If $number is not a number
|
726 |
+
*/
|
727 |
+
public static function round($number) {
|
728 |
+
SassLiteral::assertType($number, 'SassNumber');
|
729 |
+
return new SassNumber(str_replace(',','.',round($number->value)).$number->units);
|
730 |
+
}
|
731 |
+
|
732 |
+
/**
|
733 |
+
* Returns true if two numbers are similar enough to be added, subtracted,
|
734 |
+
* or compared.
|
735 |
+
* @param SassNumber The first number to test
|
736 |
+
* @param SassNumber The second number to test
|
737 |
+
* @return new SassBoolean True if the numbers are similar
|
738 |
+
* @throws SassScriptFunctionException If $number1 or $number2 is not
|
739 |
+
* a number
|
740 |
+
*/
|
741 |
+
public static function comparable($number1, $number2) {
|
742 |
+
SassLiteral::assertType($number1, 'SassNumber');
|
743 |
+
SassLiteral::assertType($number2, 'SassNumber');
|
744 |
+
return new SassBoolean($number1->isComparableTo($number2));
|
745 |
+
}
|
746 |
+
|
747 |
+
/**
|
748 |
+
* Converts a decimal number to a percentage.
|
749 |
+
* For example:
|
750 |
+
* percentage(100px / 50px) => 200%
|
751 |
+
*
|
752 |
+
* @param SassNumber The decimal number to convert to a percentage
|
753 |
+
* @return new SassNumber The number as a percentage
|
754 |
+
* @throws SassScriptFunctionException If $number isn't a unitless number
|
755 |
+
*/
|
756 |
+
public static function percentage($number) {
|
757 |
+
$number->value *= 100;
|
758 |
+
$number->units = '%';
|
759 |
+
return $number;
|
760 |
+
}
|
761 |
+
|
762 |
+
public static function max() {
|
763 |
+
$max = func_get_arg(0);
|
764 |
+
foreach (func_get_args() as $var) {
|
765 |
+
if ($var instanceOf SassNumber && $var->op_gt($max)->value) {
|
766 |
+
$max = $var;
|
767 |
+
}
|
768 |
+
}
|
769 |
+
return $max;
|
770 |
+
}
|
771 |
+
|
772 |
+
public static function min() {
|
773 |
+
$min = func_get_arg(0);
|
774 |
+
foreach (func_get_args() as $var) {
|
775 |
+
if ($var instanceOf SassNumber && $var->op_lt($min)->value) {
|
776 |
+
$min = $var;
|
777 |
+
}
|
778 |
+
}
|
779 |
+
return $min;
|
780 |
+
}
|
781 |
+
|
782 |
+
|
783 |
+
/**
|
784 |
+
* Inspects the unit of the number, returning it as a quoted string.
|
785 |
+
* Alias for units.
|
786 |
+
* @param SassNumber The number to inspect
|
787 |
+
* @return new SassString The units of the number
|
788 |
+
* @throws SassScriptFunctionException If $number is not a number
|
789 |
+
* @see units
|
790 |
+
*/
|
791 |
+
public static function unit($number) {
|
792 |
+
return self::units($number);
|
793 |
+
}
|
794 |
+
|
795 |
+
/**
|
796 |
+
* Inspects the units of the number, returning it as a quoted string.
|
797 |
+
* @param SassNumber The number to inspect
|
798 |
+
* @return new SassString The units of the number
|
799 |
+
* @throws SassScriptFunctionException If $number is not a number
|
800 |
+
*/
|
801 |
+
public static function units($number) {
|
802 |
+
SassLiteral::assertType($number, 'SassNumber');
|
803 |
+
return new SassString($number->units);
|
804 |
+
}
|
805 |
+
|
806 |
+
/**
|
807 |
+
* Inspects the unit of the number, returning a boolean indicating if it is
|
808 |
+
* unitless.
|
809 |
+
* @param SassNumber The number to inspect
|
810 |
+
* @return new SassBoolean True if the number is unitless, false if it has units.
|
811 |
+
* @throws SassScriptFunctionException If $number is not a number
|
812 |
+
*/
|
813 |
+
public static function unitless($number) {
|
814 |
+
SassLiteral::assertType($number, 'SassNumber');
|
815 |
+
return new SassBoolean($number->isUnitless());
|
816 |
+
}
|
817 |
+
|
818 |
+
/*
|
819 |
+
* String Functions
|
820 |
+
*/
|
821 |
+
|
822 |
+
/**
|
823 |
+
* Add quotes to a string if the string isn't quoted,
|
824 |
+
* or returns the same string if it is.
|
825 |
+
* @param string String to quote
|
826 |
+
* @return new SassString Quoted string
|
827 |
+
* @throws SassScriptFunctionException If $string is not a string
|
828 |
+
* @see unquote
|
829 |
+
*/
|
830 |
+
public static function quote($string) {
|
831 |
+
SassLiteral::assertType($string, 'SassString');
|
832 |
+
return new SassString('"'.$string->value.'"');
|
833 |
+
}
|
834 |
+
|
835 |
+
/**
|
836 |
+
* Removes quotes from a string if the string is quoted, or returns the same
|
837 |
+
* string if it's not.
|
838 |
+
* @param string String to unquote
|
839 |
+
* @return new SassString Unuoted string
|
840 |
+
* @throws SassScriptFunctionException If $string is not a string
|
841 |
+
* @see quote
|
842 |
+
*/
|
843 |
+
public static function unquote($string) {
|
844 |
+
if ($string instanceof SassString) {
|
845 |
+
return new SassString($string->value);
|
846 |
+
}
|
847 |
+
return $string;
|
848 |
+
}
|
849 |
+
|
850 |
+
/**
|
851 |
+
* Returns the variable whose name is the string.
|
852 |
+
* @param string String to unquote
|
853 |
+
* @return
|
854 |
+
* @throws SassScriptFunctionException If $string is not a string
|
855 |
+
*/
|
856 |
+
public static function get_var($string) {
|
857 |
+
SassLiteral::assertType($string, 'SassString');
|
858 |
+
return new SassString($string->toVar());
|
859 |
+
}
|
860 |
+
|
861 |
+
/**
|
862 |
+
* List Functions - taken mostly from Compass
|
863 |
+
*/
|
864 |
+
|
865 |
+
/**
|
866 |
+
* Returns the length of the $list
|
867 |
+
* @param SassList - the list to count
|
868 |
+
* @return SassNumber
|
869 |
+
*/
|
870 |
+
public static function length($list) {
|
871 |
+
if ($list instanceOf SassString) {
|
872 |
+
$list = new SassList($list->toString());
|
873 |
+
}
|
874 |
+
return new SassNumber($list->length());
|
875 |
+
}
|
876 |
+
|
877 |
+
/**
|
878 |
+
* Returns the nth value ofthe $list
|
879 |
+
* @param SassList - the list to get from
|
880 |
+
* @param SassNumber - the value to get
|
881 |
+
* @return anything
|
882 |
+
*/
|
883 |
+
public static function nth($list, $n) {
|
884 |
+
SassLiteral::assertType($n, 'SassNumber');
|
885 |
+
|
886 |
+
if ($list instanceof SassString) {
|
887 |
+
$list = new SassList($list->toString());
|
888 |
+
}
|
889 |
+
|
890 |
+
return $list->nth($n->value);
|
891 |
+
}
|
892 |
+
|
893 |
+
public static function join($one, $two, $sep = ', ') {
|
894 |
+
return self::append($one, $two, $sep);
|
895 |
+
}
|
896 |
+
|
897 |
+
public static function append($list, $val, $sep = ', ') {
|
898 |
+
if ($list instanceOf SassString) {
|
899 |
+
$list = new SassList($list->toString());
|
900 |
+
}
|
901 |
+
$list->append($val, $sep);
|
902 |
+
return $list;
|
903 |
+
}
|
904 |
+
|
905 |
+
public static function index($list, $value) {
|
906 |
+
if (!($list instanceOf SassList)) {
|
907 |
+
$list = new SassList($list->toString());
|
908 |
+
}
|
909 |
+
return $list->index($value);
|
910 |
+
}
|
911 |
+
|
912 |
+
|
913 |
+
// New function zip allows several lists to be combined into one list of lists. For example: zip(1px 1px 3px, solid dashed solid, red green blue) becomes 1px solid red, 1px dashed green, 3px solid blue
|
914 |
+
function zip() {
|
915 |
+
$result = new SassList('', ',');
|
916 |
+
foreach (func_get_args() as $i => $arg) {
|
917 |
+
$list = new SassList($arg);
|
918 |
+
foreach ($list->value as $j => $val) {
|
919 |
+
$result->value += array($j => new SassList('', 'space'));
|
920 |
+
$result->value[$j]->value[] = (string) $val;
|
921 |
+
}
|
922 |
+
}
|
923 |
+
return $result;
|
924 |
+
}
|
925 |
+
|
926 |
+
/*
|
927 |
+
* Misc. Functions
|
928 |
+
*/
|
929 |
+
|
930 |
+
/**
|
931 |
+
* An inline "if-else" statement.
|
932 |
+
* @param SassBoolean condition - values are loosely-evaulated by PHP, so
|
933 |
+
* 'false' includes null, false, 0, ''
|
934 |
+
* @param anything - returns if Condition is true
|
935 |
+
* @param anything - returns if Condition is false
|
936 |
+
*/
|
937 |
+
public static function _if($condition, $if_true, $if_false) {
|
938 |
+
return ($condition->value ? $if_true : $if_false);
|
939 |
+
}
|
940 |
+
|
941 |
+
/**
|
942 |
+
* Inspects the type of the argument, returning it as an unquoted string.
|
943 |
+
* @param SassLiteral The object to inspect
|
944 |
+
* @return new SassString The type of object
|
945 |
+
* @throws SassScriptFunctionException If $obj is not an instance of a
|
946 |
+
* SassLiteral
|
947 |
+
*/
|
948 |
+
public static function type_of($obj) {
|
949 |
+
SassLiteral::assertType($obj, 'SassLiteral');
|
950 |
+
return new SassString($obj->typeOf);
|
951 |
+
}
|
952 |
+
|
953 |
+
/**
|
954 |
+
* Ensures the value is within the given range, clipping it if needed.
|
955 |
+
* @param float the value to test
|
956 |
+
* @param float the minimum value
|
957 |
+
* @param float the maximum value
|
958 |
+
* @return the value clipped to the range
|
959 |
+
*/
|
960 |
+
public static function inRange($value, $min, $max) {
|
961 |
+
return ($value < $min ? $min : ($value > $max ? $max : $value));
|
962 |
+
}
|
963 |
+
}
|
lib/phpsass/script/SassScriptLexer.php
ADDED
@@ -0,0 +1,129 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* SVN FILE: $Id$ */
|
3 |
+
/**
|
4 |
+
* SassScriptLexer class file.
|
5 |
+
* @author Chris Yates <chris.l.yates@gmail.com>
|
6 |
+
* @copyright Copyright (c) 2010 PBM Web Development
|
7 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
8 |
+
* @package PHamlP
|
9 |
+
* @subpackage Sass.script
|
10 |
+
*/
|
11 |
+
|
12 |
+
require_once('literals/SassBoolean.php');
|
13 |
+
require_once('literals/SassColour.php');
|
14 |
+
require_once('literals/SassNumber.php');
|
15 |
+
require_once('literals/SassString.php');
|
16 |
+
require_once('literals/SassList.php');
|
17 |
+
require_once('SassScriptFunction.php');
|
18 |
+
require_once('SassScriptOperation.php');
|
19 |
+
require_once('SassScriptVariable.php');
|
20 |
+
|
21 |
+
/**
|
22 |
+
* SassScriptLexer class.
|
23 |
+
* Lexes SassSCript into tokens for the parser.
|
24 |
+
*
|
25 |
+
* Implements a {@link http://en.wikipedia.org/wiki/Shunting-yard_algorithm Shunting-yard algorithm} to provide {@link http://en.wikipedia.org/wiki/Reverse_Polish_notation Reverse Polish notation} output.
|
26 |
+
* @package PHamlP
|
27 |
+
* @subpackage Sass.script
|
28 |
+
*/
|
29 |
+
class SassScriptLexer {
|
30 |
+
const MATCH_WHITESPACE = '/^\s+/';
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Static holder for last instance of SassScriptLexer
|
34 |
+
*/
|
35 |
+
static public $instance;
|
36 |
+
|
37 |
+
/**
|
38 |
+
* @var SassScriptParser the parser object
|
39 |
+
*/
|
40 |
+
public $parser;
|
41 |
+
|
42 |
+
/**
|
43 |
+
* SassScriptLexer constructor.
|
44 |
+
* @return SassScriptLexer
|
45 |
+
*/
|
46 |
+
public function __construct($parser) {
|
47 |
+
$this->parser = $parser;
|
48 |
+
self::$instance = $this;
|
49 |
+
}
|
50 |
+
|
51 |
+
/**
|
52 |
+
* Lex an expression into SassScript tokens.
|
53 |
+
* @param string expression to lex
|
54 |
+
* @param SassContext the context in which the expression is lexed
|
55 |
+
* @return array tokens
|
56 |
+
*/
|
57 |
+
public function lex($string, $context) {
|
58 |
+
// if it's already lexed, just return it as-is
|
59 |
+
if (is_object($string)) {
|
60 |
+
return array($string);
|
61 |
+
}
|
62 |
+
if (is_array($string)) {
|
63 |
+
return $string;
|
64 |
+
}
|
65 |
+
// whilst the string is not empty, split it into it's tokens.
|
66 |
+
while ($string !== false) {
|
67 |
+
if (($match = $this->isWhitespace($string)) !== false) {
|
68 |
+
$tokens[] = null;
|
69 |
+
}
|
70 |
+
elseif (($match = SassScriptFunction::isa($string)) !== false) {
|
71 |
+
preg_match(SassScriptFunction::MATCH_FUNC, $match, $matches);
|
72 |
+
$args = array();
|
73 |
+
foreach (SassScriptFunction::extractArgs($matches[SassScriptFunction::ARGS], false, $context) as $key => $expression) {
|
74 |
+
$args[$key] = $this->parser->evaluate($expression, $context);
|
75 |
+
}
|
76 |
+
$tokens[] = new SassScriptFunction($matches[SassScriptFunction::NAME], $args);
|
77 |
+
}
|
78 |
+
elseif (($match = SassBoolean::isa($string)) !== false) {
|
79 |
+
$tokens[] = new SassBoolean($match);
|
80 |
+
}
|
81 |
+
elseif (($match = SassColour::isa($string)) !== false) {
|
82 |
+
$tokens[] = new SassColour($match);
|
83 |
+
}
|
84 |
+
elseif (($match = SassNumber::isa($string)) !== false) {
|
85 |
+
$tokens[] = new SassNumber($match);
|
86 |
+
}
|
87 |
+
elseif (($match = SassString::isa($string)) !== false) {
|
88 |
+
$stringed = new SassString($match);
|
89 |
+
if (strlen($stringed->quote) == 0 && SassList::isa($string) !== false) {
|
90 |
+
$tokens[] = new SassList($string);
|
91 |
+
} else {
|
92 |
+
$tokens[] = $stringed;
|
93 |
+
}
|
94 |
+
}
|
95 |
+
elseif (($match = SassScriptOperation::isa($string)) !== false) {
|
96 |
+
$tokens[] = new SassScriptOperation($match);
|
97 |
+
}
|
98 |
+
elseif (($match = SassScriptVariable::isa($string)) !== false) {
|
99 |
+
$tokens[] = new SassScriptVariable($match);
|
100 |
+
}
|
101 |
+
else {
|
102 |
+
$_string = $string;
|
103 |
+
$match = '';
|
104 |
+
while (strlen($_string) && !$this->isWhitespace($_string)) {
|
105 |
+
foreach (SassScriptOperation::$inStrOperators as $operator) {
|
106 |
+
if (substr($_string, 0, strlen($operator)) == $operator) {
|
107 |
+
break 2;
|
108 |
+
}
|
109 |
+
}
|
110 |
+
$match .= $_string[0];
|
111 |
+
$_string = substr($_string, 1);
|
112 |
+
}
|
113 |
+
$tokens[] = new SassString($match);
|
114 |
+
}
|
115 |
+
$string = substr($string, strlen($match));
|
116 |
+
}
|
117 |
+
return $tokens;
|
118 |
+
}
|
119 |
+
|
120 |
+
/**
|
121 |
+
* Returns a value indicating if a token of this type can be matched at
|
122 |
+
* the start of the subject string.
|
123 |
+
* @param string the subject string
|
124 |
+
* @return mixed match at the start of the string or false if no match
|
125 |
+
*/
|
126 |
+
public function isWhitespace($subject) {
|
127 |
+
return (preg_match(self::MATCH_WHITESPACE, $subject, $matches) ? $matches[0] : false);
|
128 |
+
}
|
129 |
+
}
|
lib/phpsass/script/SassScriptOperation.php
ADDED
@@ -0,0 +1,189 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* SVN FILE: $Id$ */
|
3 |
+
/**
|
4 |
+
* SassScriptOperation class file.
|
5 |
+
* @author Chris Yates <chris.l.yates@gmail.com>
|
6 |
+
* @copyright Copyright (c) 2010 PBM Web Development
|
7 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
8 |
+
* @package PHamlP
|
9 |
+
* @subpackage Sass.script
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* SassScriptOperation class.
|
14 |
+
* The operation to perform.
|
15 |
+
* @package PHamlP
|
16 |
+
* @subpackage Sass.script
|
17 |
+
*/
|
18 |
+
class SassScriptOperation {
|
19 |
+
const MATCH = '/^(\(|\)|\+|-|\*|\/|%|<=|>=|<|>|==|!=|=|#{|}|,|and\b|or\b|xor\b|not\b)/';
|
20 |
+
|
21 |
+
/**
|
22 |
+
* @var array map symbols to tokens.
|
23 |
+
* A token is function, associativity, precedence, number of operands
|
24 |
+
*/
|
25 |
+
public static $operators = array(
|
26 |
+
'*' => array('times', 'l', 8, 2),
|
27 |
+
'/' => array('div', 'l', 8, 2),
|
28 |
+
'%' => array('modulo', 'l', 8, 2),
|
29 |
+
'+' => array('plus', 'l', 7, 2),
|
30 |
+
'-' => array('minus', 'l', 7, 2),
|
31 |
+
'<<' => array('shiftl', 'l', 6, 2),
|
32 |
+
'>>' => array('shiftr', 'l', 6, 2),
|
33 |
+
'<=' => array('lte', 'l', 5, 2),
|
34 |
+
'>=' => array('gte', 'l', 5, 2),
|
35 |
+
'<' => array('lt', 'l', 5, 2),
|
36 |
+
'>' => array('gt', 'l', 5, 2),
|
37 |
+
'==' => array('eq', 'l', 4, 2),
|
38 |
+
'!=' => array('neq', 'l', 4, 2),
|
39 |
+
'and' => array('and', 'l', 3, 2),
|
40 |
+
'or' => array('or', 'l', 3, 2),
|
41 |
+
'xor' => array('xor', 'l', 3, 2),
|
42 |
+
'not' => array('not', 'l', 4, 1), # precedence higher than and.
|
43 |
+
'=' => array('assign', 'l', 2, 2),
|
44 |
+
')' => array('rparen', 'l', 10),
|
45 |
+
'(' => array('lparen', 'l', 10),
|
46 |
+
',' => array('comma', 'l', 0, 2),
|
47 |
+
'#{' => array('begin_interpolation'),
|
48 |
+
'}' => array('end_interpolation'),
|
49 |
+
);
|
50 |
+
|
51 |
+
/**
|
52 |
+
* @var array operators with meaning in uquoted strings;
|
53 |
+
* selectors, property names and values
|
54 |
+
*/
|
55 |
+
public static $inStrOperators = array(',', '#{');
|
56 |
+
|
57 |
+
/**
|
58 |
+
* @var array default operator token.
|
59 |
+
*/
|
60 |
+
public static $defaultOperator = array('concat', 'l', 0, 2);
|
61 |
+
|
62 |
+
/**
|
63 |
+
* @var string operator for this operation
|
64 |
+
*/
|
65 |
+
private $operator;
|
66 |
+
/**
|
67 |
+
* @var string associativity of the operator; left or right
|
68 |
+
*/
|
69 |
+
private $associativity;
|
70 |
+
/**
|
71 |
+
* @var integer precedence of the operator
|
72 |
+
*/
|
73 |
+
private $precedence;
|
74 |
+
/**
|
75 |
+
* @var integer number of operands required by the operator
|
76 |
+
*/
|
77 |
+
private $operandCount = 0;
|
78 |
+
|
79 |
+
/**
|
80 |
+
* SassScriptOperation constructor
|
81 |
+
*
|
82 |
+
* @param mixed string: operator symbol; array: operator token
|
83 |
+
* @return SassScriptOperation
|
84 |
+
*/
|
85 |
+
public function __construct($operation) {
|
86 |
+
if (is_string($operation)) {
|
87 |
+
$operation = self::$operators[$operation];
|
88 |
+
}
|
89 |
+
$this->operator = $operation[0];
|
90 |
+
if (isset($operation[1])) {
|
91 |
+
$this->associativity = $operation[1];
|
92 |
+
$this->precedence = $operation[2];
|
93 |
+
$this->operandCount = (isset($operation[3]) ? $operation[3] : 0);
|
94 |
+
}
|
95 |
+
}
|
96 |
+
|
97 |
+
/**
|
98 |
+
* Getter function for properties
|
99 |
+
* @param string name of property
|
100 |
+
* @return mixed value of the property
|
101 |
+
* @throws SassScriptOperationException if the property does not exist
|
102 |
+
*/
|
103 |
+
public function __get($name) {
|
104 |
+
if (property_exists($this, $name)) {
|
105 |
+
return $this->$name;
|
106 |
+
}
|
107 |
+
else {
|
108 |
+
throw new SassScriptOperationException('Unknown property: ' . $name, SassScriptParser::$context->node);
|
109 |
+
}
|
110 |
+
}
|
111 |
+
|
112 |
+
/**
|
113 |
+
* Performs this operation.
|
114 |
+
* @param array operands for the operation. The operands are SassLiterals
|
115 |
+
* @return SassLiteral the result of the operation
|
116 |
+
* @throws SassScriptOperationException if the oprand count is incorrect or
|
117 |
+
* the operation is undefined
|
118 |
+
*/
|
119 |
+
public function perform($operands) {
|
120 |
+
if (count($operands) !== $this->operandCount) {
|
121 |
+
throw new SassScriptOperationException('Incorrect operand count for ' . get_class($operands[0]) . '; expected ' . $this->operandCount . ', received ' . count($operands), SassScriptParser::$context->node);
|
122 |
+
}
|
123 |
+
|
124 |
+
if (!count($operands)) {
|
125 |
+
return $operands;
|
126 |
+
}
|
127 |
+
|
128 |
+
// fix a bug of unknown origin
|
129 |
+
foreach ($operands as $i => $op) {
|
130 |
+
if (!is_object($op)) {
|
131 |
+
$operands[] = null;
|
132 |
+
unset ($operands[$i]);
|
133 |
+
}
|
134 |
+
}
|
135 |
+
$operands = array_values($operands);
|
136 |
+
|
137 |
+
if (count($operands) > 1 && is_null($operands[1])) {
|
138 |
+
$operation = 'op_unary_' . $this->operator;
|
139 |
+
}
|
140 |
+
else {
|
141 |
+
$operation = 'op_' . $this->operator;
|
142 |
+
if ($this->associativity == 'l') {
|
143 |
+
$operands = array_reverse($operands);
|
144 |
+
}
|
145 |
+
}
|
146 |
+
|
147 |
+
if (method_exists($operands[0], $operation)) {
|
148 |
+
$op = clone $operands[0];
|
149 |
+
return $op->$operation(!empty($operands[1]) ? $operands[1] : null);
|
150 |
+
}
|
151 |
+
|
152 |
+
# avoid failures in case of null operands
|
153 |
+
$count = count($operands);
|
154 |
+
foreach ($operands as $i => $op) {
|
155 |
+
if (is_null($op)) {
|
156 |
+
$count--;
|
157 |
+
}
|
158 |
+
}
|
159 |
+
|
160 |
+
if ($count) {
|
161 |
+
throw new SassScriptOperationException('Undefined operation "' . $operation . '" for ' . get_class($operands[0]), SassScriptParser::$context->node);
|
162 |
+
}
|
163 |
+
}
|
164 |
+
|
165 |
+
/**
|
166 |
+
* Returns a value indicating if a token of this type can be matched at
|
167 |
+
* the start of the subject string.
|
168 |
+
* @param string the subject string
|
169 |
+
* @return mixed match at the start of the string or false if no match
|
170 |
+
*/
|
171 |
+
public static function isa($subject) {
|
172 |
+
# begins with a "/x", almost always a path without quotes.
|
173 |
+
if (preg_match('/^\/[^0-9\.\-\s]+/', $subject)) {
|
174 |
+
return FALSE;
|
175 |
+
}
|
176 |
+
return (preg_match(self::MATCH, $subject, $matches) ? trim($matches[1]) : false);
|
177 |
+
}
|
178 |
+
|
179 |
+
/**
|
180 |
+
* Converts the operation back into it's SASS representation
|
181 |
+
*/
|
182 |
+
public function __toString() {
|
183 |
+
foreach(SassScriptOperation::$operators as $char => $operator) {
|
184 |
+
if ($operator[0] == trim($this->operator)) {
|
185 |
+
return $char;
|
186 |
+
}
|
187 |
+
}
|
188 |
+
}
|
189 |
+
}
|
lib/phpsass/script/SassScriptParser.php
ADDED
@@ -0,0 +1,259 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* SVN FILE: $Id$ */
|
3 |
+
/**
|
4 |
+
* SassScriptParser class file.
|
5 |
+
* @author Chris Yates <chris.l.yates@gmail.com>
|
6 |
+
* @copyright Copyright (c) 2010 PBM Web Development
|
7 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
8 |
+
* @package PHamlP
|
9 |
+
* @subpackage Sass.script
|
10 |
+
*/
|
11 |
+
|
12 |
+
require_once('SassScriptLexer.php');
|
13 |
+
require_once('SassScriptParserExceptions.php');
|
14 |
+
|
15 |
+
/**
|
16 |
+
* SassScriptParser class.
|
17 |
+
* Parses SassScript. SassScript is lexed into {@link http://en.wikipedia.org/wiki/Reverse_Polish_notation Reverse Polish notation} by the SassScriptLexer and
|
18 |
+
* the calculated result returned.
|
19 |
+
* @package PHamlP
|
20 |
+
* @subpackage Sass.script
|
21 |
+
*/
|
22 |
+
class SassScriptParser {
|
23 |
+
const MATCH_INTERPOLATION = '/(?<!\\\\)#\{(.*?)\}/';
|
24 |
+
const DEFAULT_ENV = 0;
|
25 |
+
const CSS_RULE = 1;
|
26 |
+
const CSS_PROPERTY = 2;
|
27 |
+
|
28 |
+
/**
|
29 |
+
* @var SassContext Used for error reporting
|
30 |
+
*/
|
31 |
+
public static $context;
|
32 |
+
|
33 |
+
/**
|
34 |
+
* @var SassScriptLexer the lexer object
|
35 |
+
*/
|
36 |
+
public $lexer;
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Hold a copy of a parser available to the general public.
|
40 |
+
*/
|
41 |
+
public static $instance;
|
42 |
+
|
43 |
+
/**
|
44 |
+
* SassScriptParser constructor.
|
45 |
+
* @return SassScriptParser
|
46 |
+
*/
|
47 |
+
public function __construct() {
|
48 |
+
$this->lexer = new SassScriptLexer($this);
|
49 |
+
self::$instance = $this;
|
50 |
+
}
|
51 |
+
|
52 |
+
/**
|
53 |
+
* Replace interpolated SassScript contained in '#{}' with the parsed value.
|
54 |
+
* @param string the text to interpolate
|
55 |
+
* @param SassContext the context in which the string is interpolated
|
56 |
+
* @return string the interpolated text
|
57 |
+
*/
|
58 |
+
public function interpolate($string, $context) {
|
59 |
+
for ($i = 0, $n = preg_match_all(self::MATCH_INTERPOLATION, $string, $matches); $i < $n; $i++) {
|
60 |
+
$var = $this->evaluate($matches[1][$i], $context);
|
61 |
+
|
62 |
+
if ($var instanceOf SassString) {
|
63 |
+
$var = $var->value;
|
64 |
+
} else {
|
65 |
+
$var = $var->toString();
|
66 |
+
}
|
67 |
+
|
68 |
+
if(preg_match('/^unquote\((["\'])(.*)\1\)$/', $var, $match)){
|
69 |
+
$val = $match[2];
|
70 |
+
}
|
71 |
+
else if($var == '""'){
|
72 |
+
$val = "";
|
73 |
+
}
|
74 |
+
else if(preg_match('/^(["\'])(.*)\1$/', $var, $match)){
|
75 |
+
$val = $match[2];
|
76 |
+
}
|
77 |
+
else {
|
78 |
+
$val = $var;
|
79 |
+
}
|
80 |
+
$matches[1][$i] = $val;
|
81 |
+
}
|
82 |
+
return str_replace($matches[0], $matches[1], $string);
|
83 |
+
}
|
84 |
+
|
85 |
+
/**
|
86 |
+
* Evaluate a SassScript.
|
87 |
+
* @param string expression to parse
|
88 |
+
* @param SassContext the context in which the expression is evaluated
|
89 |
+
* @param integer the environment in which the expression is evaluated
|
90 |
+
* @return SassLiteral parsed value
|
91 |
+
*/
|
92 |
+
public function evaluate($expression, $context, $environment = self::DEFAULT_ENV) {
|
93 |
+
self::$context = $context;
|
94 |
+
$operands = array();
|
95 |
+
|
96 |
+
$tokens = $this->parse($expression, $context, $environment);
|
97 |
+
|
98 |
+
while (count($tokens)) {
|
99 |
+
$token = array_shift($tokens);
|
100 |
+
if ($token instanceof SassScriptFunction) {
|
101 |
+
$perform = $token->perform();
|
102 |
+
array_push($operands, $perform);
|
103 |
+
}
|
104 |
+
elseif ($token instanceof SassLiteral) {
|
105 |
+
if ($token instanceof SassString) {
|
106 |
+
$token = new SassString($this->interpolate($token->toString(), self::$context));
|
107 |
+
}
|
108 |
+
array_push($operands, $token);
|
109 |
+
}
|
110 |
+
else {
|
111 |
+
$args = array();
|
112 |
+
for ($i = 0, $c = $token->operandCount; $i < $c; $i++) {
|
113 |
+
$args[] = array_pop($operands);
|
114 |
+
}
|
115 |
+
array_push($operands, $token->perform($args));
|
116 |
+
}
|
117 |
+
}
|
118 |
+
|
119 |
+
return self::makeSingular($operands);
|
120 |
+
}
|
121 |
+
|
122 |
+
/**
|
123 |
+
* Parse SassScript to a set of tokens in RPN
|
124 |
+
* using the Shunting Yard Algorithm.
|
125 |
+
* @param string expression to parse
|
126 |
+
* @param SassContext the context in which the expression is parsed
|
127 |
+
* @param integer the environment in which the expression is parsed
|
128 |
+
* @return array tokens in RPN
|
129 |
+
*/
|
130 |
+
public function parse($expression, $context, $environment=self::DEFAULT_ENV) {
|
131 |
+
$outputQueue = array();
|
132 |
+
$operatorStack = array();
|
133 |
+
$parenthesis = 0;
|
134 |
+
|
135 |
+
$tokens = $this->lexer->lex($expression, $context);
|
136 |
+
|
137 |
+
foreach($tokens as $i=>$token) {
|
138 |
+
// If two literals/expessions are seperated by whitespace use the concat operator
|
139 |
+
if (empty($token)) {
|
140 |
+
if (isset($tokens[$i+1])) {
|
141 |
+
if ($i > 0 && (!$tokens[$i-1] instanceof SassScriptOperation || $tokens[$i-1]->operator === SassScriptOperation::$operators[')'][0]) &&
|
142 |
+
(!$tokens[$i+1] instanceof SassScriptOperation || $tokens[$i+1]->operator === SassScriptOperation::$operators['('][0])) {
|
143 |
+
$token = new SassScriptOperation(SassScriptOperation::$defaultOperator, $context);
|
144 |
+
}
|
145 |
+
else {
|
146 |
+
continue;
|
147 |
+
}
|
148 |
+
}
|
149 |
+
}
|
150 |
+
elseif ($token instanceof SassScriptVariable) {
|
151 |
+
$token = $token->evaluate($context);
|
152 |
+
$environment = self::DEFAULT_ENV;
|
153 |
+
}
|
154 |
+
|
155 |
+
// If the token is a number or function add it to the output queue.
|
156 |
+
if ($token instanceof SassLiteral || $token instanceof SassScriptFunction) {
|
157 |
+
if ($environment === self::CSS_PROPERTY && $token instanceof SassNumber && !$parenthesis) {
|
158 |
+
$token->inExpression = false;
|
159 |
+
}
|
160 |
+
array_push($outputQueue, $token);
|
161 |
+
}
|
162 |
+
// If the token is an operation
|
163 |
+
elseif ($token instanceof SassScriptOperation) {
|
164 |
+
// If the token is a left parenthesis push it onto the stack.
|
165 |
+
if ($token->operator == SassScriptOperation::$operators['('][0]) {
|
166 |
+
array_push($operatorStack, $token);
|
167 |
+
$parenthesis++;
|
168 |
+
}
|
169 |
+
// If the token is a right parenthesis:
|
170 |
+
elseif ($token->operator == SassScriptOperation::$operators[')'][0]) {
|
171 |
+
$parenthesis--;
|
172 |
+
while ($c = count($operatorStack)) {
|
173 |
+
// If the token at the top of the stack is a left parenthesis
|
174 |
+
if ($operatorStack[$c - 1]->operator == SassScriptOperation::$operators['('][0]) {
|
175 |
+
// Pop the left parenthesis from the stack, but not onto the output queue.
|
176 |
+
array_pop($operatorStack);
|
177 |
+
break;
|
178 |
+
}
|
179 |
+
// else pop the operator off the stack onto the output queue.
|
180 |
+
array_push($outputQueue, array_pop($operatorStack));
|
181 |
+
}
|
182 |
+
// If the stack runs out without finding a left parenthesis
|
183 |
+
// there are mismatched parentheses.
|
184 |
+
if ($c <= 0) {
|
185 |
+
array_push($outputQueue, new SassString(')'));
|
186 |
+
break;
|
187 |
+
throw new SassScriptParserException('Unmatched parentheses', $context->node);
|
188 |
+
}
|
189 |
+
}
|
190 |
+
// the token is an operator, o1, so:
|
191 |
+
else {
|
192 |
+
// while there is an operator, o2, at the top of the stack
|
193 |
+
while ($c = count($operatorStack)) {
|
194 |
+
$operation = $operatorStack[$c - 1];
|
195 |
+
// if o2 is left parenthesis, or
|
196 |
+
// the o1 has left associativty and greater precedence than o2, or
|
197 |
+
// the o1 has right associativity and lower or equal precedence than o2
|
198 |
+
if (($operation->operator == SassScriptOperation::$operators['('][0]) ||
|
199 |
+
($token->associativity == 'l' && $token->precedence > $operation->precedence) ||
|
200 |
+
($token->associativity == 'r' && $token->precedence <= $operation->precedence)) {
|
201 |
+
break; // stop checking operators
|
202 |
+
}
|
203 |
+
//pop o2 off the stack and onto the output queue
|
204 |
+
array_push($outputQueue, array_pop($operatorStack));
|
205 |
+
}
|
206 |
+
// push o1 onto the stack
|
207 |
+
array_push($operatorStack, $token);
|
208 |
+
}
|
209 |
+
}
|
210 |
+
}
|
211 |
+
|
212 |
+
// When there are no more tokens
|
213 |
+
while ($c = count($operatorStack)) { // While there are operators on the stack:
|
214 |
+
if ($operatorStack[$c - 1]->operator !== SassScriptOperation::$operators['('][0]) {
|
215 |
+
array_push($outputQueue, array_pop($operatorStack));
|
216 |
+
}
|
217 |
+
else {
|
218 |
+
throw new SassScriptParserException('Unmatched parentheses', $context->node);
|
219 |
+
}
|
220 |
+
}
|
221 |
+
|
222 |
+
return $outputQueue;
|
223 |
+
}
|
224 |
+
|
225 |
+
/**
|
226 |
+
* Reduces a set down to a singular form
|
227 |
+
*/
|
228 |
+
public static function makeSingular($operands) {
|
229 |
+
if (count($operands) == 1) {
|
230 |
+
return $operands[0];
|
231 |
+
}
|
232 |
+
|
233 |
+
$result = null;
|
234 |
+
foreach ($operands as $i => $operand) {
|
235 |
+
if (is_object($operand)) {
|
236 |
+
if (!$result) {
|
237 |
+
$result = $operand;
|
238 |
+
continue;
|
239 |
+
}
|
240 |
+
if ($result instanceOf SassString) {
|
241 |
+
$result = $result->op_concat($operand);
|
242 |
+
}
|
243 |
+
else {
|
244 |
+
$result = $result->op_plus($operand);
|
245 |
+
}
|
246 |
+
}
|
247 |
+
else {
|
248 |
+
$string = new SassString(' ');
|
249 |
+
if (!$result) {
|
250 |
+
$result = $string;
|
251 |
+
} else {
|
252 |
+
$result = $result->op_plus($string);
|
253 |
+
}
|
254 |
+
}
|
255 |
+
}
|
256 |
+
|
257 |
+
return $result ? $result : array_shift($operands);
|
258 |
+
}
|
259 |
+
}
|
lib/phpsass/script/SassScriptParserExceptions.php
ADDED
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* SVN FILE: $Id$ */
|
3 |
+
/**
|
4 |
+
* SassScript Parser exception class file.
|
5 |
+
* @author Chris Yates <chris.l.yates@gmail.com>
|
6 |
+
* @copyright Copyright (c) 2010 PBM Web Development
|
7 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
8 |
+
* @package PHamlP
|
9 |
+
* @subpackage Sass.script
|
10 |
+
*/
|
11 |
+
|
12 |
+
require_once(dirname(__FILE__).'/../SassException.php');
|
13 |
+
|
14 |
+
/**
|
15 |
+
* SassScriptParserException class.
|
16 |
+
* @package PHamlP
|
17 |
+
* @subpackage Sass.script
|
18 |
+
*/
|
19 |
+
class SassScriptParserException extends SassException {}
|
20 |
+
|
21 |
+
/**
|
22 |
+
* SassScriptLexerException class.
|
23 |
+
* @package PHamlP
|
24 |
+
* @subpackage Sass.script
|
25 |
+
*/
|
26 |
+
class SassScriptLexerException extends SassScriptParserException {}
|
27 |
+
|
28 |
+
/**
|
29 |
+
* SassScriptOperationException class.
|
30 |
+
* @package PHamlP
|
31 |
+
* @subpackage Sass.script
|
32 |
+
*/
|
33 |
+
class SassScriptOperationException extends SassScriptParserException {}
|
34 |
+
|
35 |
+
/**
|
36 |
+
* SassScriptFunctionException class.
|
37 |
+
* @package PHamlP
|
38 |
+
* @subpackage Sass.script
|
39 |
+
*/
|
40 |
+
class SassScriptFunctionException extends SassScriptParserException {}
|
lib/phpsass/script/SassScriptVariable.php
ADDED
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* SVN FILE: $Id: SassVariable.php 49 2010-04-04 10:51:24Z chris.l.yates $ */
|
3 |
+
/**
|
4 |
+
* SassVariable class file.
|
5 |
+
* @author Chris Yates <chris.l.yates@gmail.com>
|
6 |
+
* @copyright Copyright (c) 2010 PBM Web Development
|
7 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
8 |
+
* @package PHamlP
|
9 |
+
* @subpackage Sass.script.literals
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* SassVariable class.
|
14 |
+
* @package PHamlP
|
15 |
+
* @subpackage Sass.script.literals
|
16 |
+
*/
|
17 |
+
class SassScriptVariable {
|
18 |
+
/**
|
19 |
+
* Regex for matching and extracting Variables
|
20 |
+
*/
|
21 |
+
const MATCH = '/^(?<!\\\\)(?(?!!important\b)[!\$]([\w-]+))/';
|
22 |
+
|
23 |
+
/**
|
24 |
+
* @var string name of variable
|
25 |
+
*/
|
26 |
+
private $name;
|
27 |
+
|
28 |
+
/**
|
29 |
+
* SassVariable constructor
|
30 |
+
* @param string value of the Variable type
|
31 |
+
* @return SassVariable
|
32 |
+
*/
|
33 |
+
public function __construct($value) {
|
34 |
+
$this->name = substr($value, 1);
|
35 |
+
}
|
36 |
+
|
37 |
+
/**
|
38 |
+
* Returns the SassScript object for this variable.
|
39 |
+
* @param SassContext context of the variable
|
40 |
+
* @return SassLiteral the SassScript object for this variable
|
41 |
+
*/
|
42 |
+
public function evaluate($context) {
|
43 |
+
return $context->getVariable($this->name);
|
44 |
+
}
|
45 |
+
|
46 |
+
/**
|
47 |
+
* Returns a value indicating if a token of this type can be matched at
|
48 |
+
* the start of the subject string.
|
49 |
+
* @param string the subject string
|
50 |
+
* @return mixed match at the start of the string or false if no match
|
51 |
+
*/
|
52 |
+
public static function isa($subject) {
|
53 |
+
// we need to do the check as preg_match returns a count of 1 if
|
54 |
+
// subject == '!important'; the match being an empty match
|
55 |
+
return (preg_match(self::MATCH, $subject, $matches) ? (empty($matches[0]) ? false : $matches[0]) : false);
|
56 |
+
}
|
57 |
+
}
|
lib/phpsass/script/literals/SassBoolean.php
ADDED
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* SVN FILE: $Id$ */
|
3 |
+
/**
|
4 |
+
* SassBoolean class file.
|
5 |
+
* @author Chris Yates <chris.l.yates@gmail.com>
|
6 |
+
* @copyright Copyright (c) 2010 PBM Web Development
|
7 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
8 |
+
* @package PHamlP
|
9 |
+
* @subpackage Sass.script.literals
|
10 |
+
*/
|
11 |
+
|
12 |
+
require_once('SassLiteral.php');
|
13 |
+
|
14 |
+
/**
|
15 |
+
* SassBoolean class.
|
16 |
+
* @package PHamlP
|
17 |
+
* @subpackage Sass.script.literals
|
18 |
+
*/
|
19 |
+
class SassBoolean extends SassLiteral {
|
20 |
+
/**@#+
|
21 |
+
* Regex for matching and extracting booleans
|
22 |
+
*/
|
23 |
+
const MATCH = '/^(true|false)\b/';
|
24 |
+
|
25 |
+
/**
|
26 |
+
* SassBoolean constructor
|
27 |
+
* @param string value of the boolean type
|
28 |
+
* @return SassBoolean
|
29 |
+
*/
|
30 |
+
public function __construct($value) {
|
31 |
+
if (is_bool($value)) {
|
32 |
+
$this->value = $value;
|
33 |
+
}
|
34 |
+
elseif ($value === 'true' || $value === 'false') {
|
35 |
+
$this->value = ($value === 'true' ? true : false);
|
36 |
+
}
|
37 |
+
else {
|
38 |
+
throw new SassBooleanException('Invalid SassBoolean', SassScriptParser::$context->node);
|
39 |
+
}
|
40 |
+
}
|
41 |
+
|
42 |
+
/**
|
43 |
+
* Returns the value of this boolean.
|
44 |
+
* @return boolean the value of this boolean
|
45 |
+
*/
|
46 |
+
public function getValue() {
|
47 |
+
return $this->value;
|
48 |
+
}
|
49 |
+
|
50 |
+
/**
|
51 |
+
* Returns a string representation of the value.
|
52 |
+
* @return string string representation of the value.
|
53 |
+
*/
|
54 |
+
public function toString() {
|
55 |
+
return $this->getValue() ? 'true' : 'false';
|
56 |
+
}
|
57 |
+
|
58 |
+
/**
|
59 |
+
* Returns a value indicating if a token of this type can be matched at
|
60 |
+
* the start of the subject string.
|
61 |
+
* @param string the subject string
|
62 |
+
* @return mixed match at the start of the string or false if no match
|
63 |
+
*/
|
64 |
+
public static function isa($subject) {
|
65 |
+
return (preg_match(self::MATCH, $subject, $matches) ? $matches[0] : false);
|
66 |
+
}
|
67 |
+
}
|
lib/phpsass/script/literals/SassColour.php
ADDED
@@ -0,0 +1,918 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* SVN FILE: $Id$ */
|
3 |
+
/**
|
4 |
+
* SassColour class file.
|
5 |
+
* @author Chris Yates <chris.l.yates@gmail.com>
|
6 |
+
* @copyright Copyright (c) 2010 PBM Web Development
|
7 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
8 |
+
* @package PHamlP
|
9 |
+
* @subpackage Sass.script.literals
|
10 |
+
*/
|
11 |
+
|
12 |
+
require_once('SassLiteral.php');
|
13 |
+
|
14 |
+
/**
|
15 |
+
* SassColour class.
|
16 |
+
* A SassScript object representing a CSS colour.
|
17 |
+
*
|
18 |
+
* A colour may be represented internally as RGBA, HSLA, or both. It is
|
19 |
+
* originally represented as whatever its input is; if it’s created with RGB
|
20 |
+
* values, it’s represented as RGBA, and if it’s created with HSL values, it’s
|
21 |
+
* represented as HSLA. Once a property is accessed that requires the other
|
22 |
+
* representation – for example, SassColour::red for an HSL color – that
|
23 |
+
* component is calculated and cached.
|
24 |
+
*
|
25 |
+
* The alpha channel of a color is independent of its RGB or HSL representation.
|
26 |
+
* It’s always stored, as 1 if nothing else is specified. If only the alpha
|
27 |
+
* channel is modified using SassColour::with(), the cached RGB and HSL values
|
28 |
+
* are retained.
|
29 |
+
*
|
30 |
+
* Colour operations are all piecewise, e.g. when adding two colours each
|
31 |
+
* component is added independantly; Rr = R1 + R2, Gr = G1 + G2, Br = B1 + B2.
|
32 |
+
*
|
33 |
+
* Colours are returned as a named colour if possible or #rrggbb.
|
34 |
+
*
|
35 |
+
* @package PHamlP
|
36 |
+
* @subpackage Sass.script.literals
|
37 |
+
*/
|
38 |
+
class SassColour extends SassLiteral {
|
39 |
+
/**@#+
|
40 |
+
* Regexes for matching and extracting colours
|
41 |
+
*/
|
42 |
+
const MATCH = '/^((#([\da-f]{6}|[\da-f]{3}))|transparent|{CSS_COLOURS})/';
|
43 |
+
const EXTRACT_3 = '/#([\da-f])([\da-f])([\da-f])$/';
|
44 |
+
const EXTRACT_6 = '/#([\da-f]{2})([\da-f]{2})([\da-f]{2})/';
|
45 |
+
const TRANSPARENT = 'transparent';
|
46 |
+
/**@#-*/
|
47 |
+
|
48 |
+
/**@#-*/
|
49 |
+
static public $svgColours = array(
|
50 |
+
'aliceblue' => '#f0f8ff',
|
51 |
+
'antiquewhite' => '#faebd7',
|
52 |
+
'aqua' => '#00ffff',
|
53 |
+
'aquamarine' => '#7fffd4',
|
54 |
+
'azure' => '#f0ffff',
|
55 |
+
'beige' => '#f5f5dc',
|
56 |
+
'bisque' => '#ffe4c4',
|
57 |
+
'black' => '#000000',
|
58 |
+
'blanchedalmond' => '#ffebcd',
|
59 |
+
'blue' => '#0000ff',
|
60 |
+
'blueviolet' => '#8a2be2',
|
61 |
+
'brown' => '#a52a2a',
|
62 |
+
'burlywood' => '#deb887',
|
63 |
+
'cadetblue' => '#5f9ea0',
|
64 |
+
'chartreuse' => '#7fff00',
|
65 |
+
'chocolate' => '#d2691e',
|
66 |
+
'coral' => '#ff7f50',
|
67 |
+
'cornflowerblue' => '#6495ed',
|
68 |
+
'cornsilk' => '#fff8dc',
|
69 |
+
'crimson' => '#dc143c',
|
70 |
+
'cyan' => '#00ffff',
|
71 |
+
'darkblue' => '#00008b',
|
72 |
+
'darkcyan' => '#008b8b',
|
73 |
+
'darkgoldenrod' => '#b8860b',
|
74 |
+
'darkgreen' => '#006400',
|
75 |
+
'darkkhaki' => '#bdb76b',
|
76 |
+
'darkmagenta' => '#8b008b',
|
77 |
+
'darkolivegreen' => '#556b2f',
|
78 |
+
'darkorange' => '#ff8c00',
|
79 |
+
'darkorchid' => '#9932cc',
|
80 |
+
'darkred' => '#8b0000',
|
81 |
+
'darksalmon' => '#e9967a',
|
82 |
+
'darkseagreen' => '#8fbc8f',
|
83 |
+
'darkslateblue' => '#483d8b',
|
84 |
+
'darkslategray' => '#2f4f4f',
|
85 |
+
'darkslategrey' => '#2f4f4f',
|
86 |
+
'darkturquoise' => '#00ced1',
|
87 |
+
'darkviolet' => '#9400d3',
|
88 |
+
'deeppink' => '#ff1493',
|
89 |
+
'deepskyblue' => '#00bfff',
|
90 |
+
'dimgray' => '#696969',
|
91 |
+
'dimgrey' => '#696969',
|
92 |
+
'dodgerblue' => '#1e90ff',
|
93 |
+
'firebrick' => '#b22222',
|
94 |
+
'floralwhite' => '#fffaf0',
|
95 |
+
'forestgreen' => '#228b22',
|
96 |
+
'fuchsia' => '#ff00ff',
|
97 |
+
'gainsboro' => '#dcdcdc',
|
98 |
+
'ghostwhite' => '#f8f8ff',
|
99 |
+
'gold' => '#ffd700',
|
100 |
+
'goldenrod' => '#daa520',
|
101 |
+
'gray' => '#808080',
|
102 |
+
'green' => '#008000',
|
103 |
+
'greenyellow' => '#adff2f',
|
104 |
+
'grey' => '#808080',
|
105 |
+
'honeydew' => '#f0fff0',
|
106 |
+
'hotpink' => '#ff69b4',
|
107 |
+
'indianred' => '#cd5c5c',
|
108 |
+
'indigo' => '#4b0082',
|
109 |
+
'ivory' => '#fffff0',
|
110 |
+
'khaki' => '#f0e68c',
|
111 |
+
'lavender' => '#e6e6fa',
|
112 |
+
'lavenderblush' => '#fff0f5',
|
113 |
+
'lawngreen' => '#7cfc00',
|
114 |
+
'lemonchiffon' => '#fffacd',
|
115 |
+
'lightblue' => '#add8e6',
|
116 |
+
'lightcoral' => '#f08080',
|
117 |
+
'lightcyan' => '#e0ffff',
|
118 |
+
'lightgoldenrodyellow' => '#fafad2',
|
119 |
+
'lightgray' => '#d3d3d3',
|
120 |
+
'lightgreen' => '#90ee90',
|
121 |
+
'lightgrey' => '#d3d3d3',
|
122 |
+
'lightpink' => '#ffb6c1',
|
123 |
+
'lightsalmon' => '#ffa07a',
|
124 |
+
'lightseagreen' => '#20b2aa',
|
125 |
+
'lightskyblue' => '#87cefa',
|
126 |
+
'lightslategray' => '#778899',
|
127 |
+
'lightslategrey' => '#778899',
|
128 |
+
'lightsteelblue' => '#b0c4de',
|
129 |
+
'lightyellow' => '#ffffe0',
|
130 |
+
'lime' => '#00ff00',
|
131 |
+
'limegreen' => '#32cd32',
|
132 |
+
'linen' => '#faf0e6',
|
133 |
+
'magenta' => '#ff00ff',
|
134 |
+
'maroon' => '#800000',
|
135 |
+
'mediumaquamarine' => '#66cdaa',
|
136 |
+
'mediumblue' => '#0000cd',
|
137 |
+
'mediumorchid' => '#ba55d3',
|
138 |
+
'mediumpurple' => '#9370db',
|
139 |
+
'mediumseagreen' => '#3cb371',
|
140 |
+
'mediumslateblue' => '#7b68ee',
|
141 |
+
'mediumspringgreen' => '#00fa9a',
|
142 |
+
'mediumturquoise' => '#48d1cc',
|
143 |
+
'mediumvioletred' => '#c71585',
|
144 |
+
'midnightblue' => '#191970',
|
145 |
+
'mintcream' => '#f5fffa',
|
146 |
+
'mistyrose' => '#ffe4e1',
|
147 |
+
'moccasin' => '#ffe4b5',
|
148 |
+
'navajowhite' => '#ffdead',
|
149 |
+
'navy' => '#000080',
|
150 |
+
'oldlace' => '#fdf5e6',
|
151 |
+
'olive' => '#808000',
|
152 |
+
'olivedrab' => '#6b8e23',
|
153 |
+
'orange' => '#ffa500',
|
154 |
+
'orangered' => '#ff4500',
|
155 |
+
'orchid' => '#da70d6',
|
156 |
+
'palegoldenrod' => '#eee8aa',
|
157 |
+
'palegreen' => '#98fb98',
|
158 |
+
'paleturquoise' => '#afeeee',
|
159 |
+
'palevioletred' => '#db7093',
|
160 |
+
'papayawhip' => '#ffefd5',
|
161 |
+
'peachpuff' => '#ffdab9',
|
162 |
+
'peru' => '#cd853f',
|
163 |
+
'pink' => '#ffc0cb',
|
164 |
+
'plum' => '#dda0dd',
|
165 |
+
'powderblue' => '#b0e0e6',
|
166 |
+
'purple' => '#800080',
|
167 |
+
'red' => '#ff0000',
|
168 |
+
'rosybrown' => '#bc8f8f',
|
169 |
+
'royalblue' => '#4169e1',
|
170 |
+
'saddlebrown' => '#8b4513',
|
171 |
+
'salmon' => '#fa8072',
|
172 |
+
'sandybrown' => '#f4a460',
|
173 |
+
'seagreen' => '#2e8b57',
|
174 |
+
'seashell' => '#fff5ee',
|
175 |
+
'sienna' => '#a0522d',
|
176 |
+
'silver' => '#c0c0c0',
|
177 |
+
'skyblue' => '#87ceeb',
|
178 |
+
'slateblue' => '#6a5acd',
|
179 |
+
'slategray' => '#708090',
|
180 |
+
'slategrey' => '#708090',
|
181 |
+
'snow' => '#fffafa',
|
182 |
+
'springgreen' => '#00ff7f',
|
183 |
+
'steelblue' => '#4682b4',
|
184 |
+
'tan' => '#d2b48c',
|
185 |
+
'teal' => '#008080',
|
186 |
+
'thistle' => '#d8bfd8',
|
187 |
+
'tomato' => '#ff6347',
|
188 |
+
'turquoise' => '#40e0d0',
|
189 |
+
'violet' => '#ee82ee',
|
190 |
+
'wheat' => '#f5deb3',
|
191 |
+
'white' => '#ffffff',
|
192 |
+
'whitesmoke' => '#f5f5f5',
|
193 |
+
'yellow' => '#ffff00',
|
194 |
+
'yellowgreen' => '#9acd32'
|
195 |
+
);
|
196 |
+
|
197 |
+
/**
|
198 |
+
* @var array reverse array (value => name) of named SVG1.0 colours
|
199 |
+
*/
|
200 |
+
static public $_svgColours;
|
201 |
+
|
202 |
+
/**
|
203 |
+
* @var array reverse array (value => name) of named HTML4 colours
|
204 |
+
*/
|
205 |
+
static public $_html4Colours = array(
|
206 |
+
'#000000' => 'black',
|
207 |
+
'#000080' => 'navy',
|
208 |
+
'#0000ff' => 'blue',
|
209 |
+
'#008000' => 'green',
|
210 |
+
'#008080' => 'teal',
|
211 |
+
'#00ff00' => 'lime',
|
212 |
+
'#00ffff' => 'aqua',
|
213 |
+
'#800000' => 'maroon',
|
214 |
+
'#800080' => 'purple',
|
215 |
+
'#808000' => 'olive',
|
216 |
+
'#808080' => 'gray',
|
217 |
+
'#c0c0c0' => 'silver',
|
218 |
+
'#ff0000' => 'red',
|
219 |
+
'#ff00ff' => 'fuchsia',
|
220 |
+
'#ffff00' => 'yellow',
|
221 |
+
'#ffffff' => 'white',
|
222 |
+
);
|
223 |
+
|
224 |
+
static public $regex;
|
225 |
+
|
226 |
+
/**@#+
|
227 |
+
* RGB colour components
|
228 |
+
*/
|
229 |
+
/**
|
230 |
+
* @var array RGB colour components. Used to check for RGB attributes.
|
231 |
+
*/
|
232 |
+
static public $rgb = array('red', 'green', 'blue');
|
233 |
+
/**
|
234 |
+
* @var integer red component. 0 - 255
|
235 |
+
*/
|
236 |
+
public $red;
|
237 |
+
/**
|
238 |
+
* @var integer green component. 0 - 255
|
239 |
+
*/
|
240 |
+
public $green;
|
241 |
+
/**
|
242 |
+
* @var integer blue component. 0 - 255
|
243 |
+
*/
|
244 |
+
public $blue;
|
245 |
+
/**@#-*/
|
246 |
+
/**@#+
|
247 |
+
* HSL colour components
|
248 |
+
*/
|
249 |
+
/**
|
250 |
+
* @var array HSL colour components. Used to check for HSL attributes.
|
251 |
+
*/
|
252 |
+
static public $hsl = array('hue', 'saturation', 'lightness');
|
253 |
+
/**
|
254 |
+
* @var float hue component. 0 - 360
|
255 |
+
*/
|
256 |
+
public $hue;
|
257 |
+
/**
|
258 |
+
* @var float saturation component. 0 - 100
|
259 |
+
*/
|
260 |
+
public $saturation;
|
261 |
+
/**
|
262 |
+
* @var float lightness component. 0 - 100
|
263 |
+
*/
|
264 |
+
public $lightness;
|
265 |
+
/**@#-*/
|
266 |
+
/**
|
267 |
+
* @var float alpha component. 0 - 1
|
268 |
+
*/
|
269 |
+
public $alpha = 1;
|
270 |
+
|
271 |
+
/**
|
272 |
+
* Constructs an RGB or HSL color object, optionally with an alpha channel.
|
273 |
+
* RGB values must be between 0 and 255. Saturation and lightness values must
|
274 |
+
* be between 0 and 100. The alpha value must be between 0 and 1.
|
275 |
+
* The colour can be specified as:
|
276 |
+
* + a string that is an SVG colour or of the form #rgb or #rrggbb
|
277 |
+
* + an array with either 'red', 'green', and 'blue' keys, and optionally
|
278 |
+
* an alpha key.
|
279 |
+
* + an array with 'hue', 'saturation', and 'lightness' keys, and optionally
|
280 |
+
* an alpha key.
|
281 |
+
* + an array of red, green, and blue values, and optionally an alpha value.
|
282 |
+
* @param mixed the colour
|
283 |
+
* @return SassColour
|
284 |
+
*/
|
285 |
+
public function __construct($colour) {
|
286 |
+
if (is_string($colour)) {
|
287 |
+
$colour = strtolower($colour);
|
288 |
+
if ($colour === self::TRANSPARENT) {
|
289 |
+
$this->red = 0;
|
290 |
+
$this->green = 0;
|
291 |
+
$this->blue = 0;
|
292 |
+
$this->alpha = 0;
|
293 |
+
}
|
294 |
+
else {
|
295 |
+
if (array_key_exists($colour, self::$svgColours)) {
|
296 |
+
$colour = self::$svgColours[$colour];
|
297 |
+
}
|
298 |
+
if (preg_match(self::EXTRACT_3, $colour, $matches)) {
|
299 |
+
for ($i = 1; $i < 4; $i++) {
|
300 |
+
$matches[$i] = str_repeat($matches[$i], 2);
|
301 |
+
}
|
302 |
+
}
|
303 |
+
else {
|
304 |
+
preg_match(self::EXTRACT_6, $colour, $matches);
|
305 |
+
}
|
306 |
+
|
307 |
+
if (empty($matches)) {
|
308 |
+
throw new SassColourException('Invalid SassColour string', SassScriptParser::$context->node);
|
309 |
+
}
|
310 |
+
$this->red = intval($matches[1], 16);
|
311 |
+
$this->green = intval($matches[2], 16);
|
312 |
+
$this->blue = intval($matches[3], 16);
|
313 |
+
$this->alpha = 1;
|
314 |
+
}
|
315 |
+
}
|
316 |
+
elseif (is_array($colour)) {
|
317 |
+
$scheme = $this->assertValid($colour);
|
318 |
+
if ($scheme == 'rgb') {
|
319 |
+
$this->red = $colour['red'];
|
320 |
+
$this->green = $colour['green'];
|
321 |
+
$this->blue = $colour['blue'];
|
322 |
+
$this->alpha = (isset($colour['alpha']) ? $colour['alpha'] : 1);
|
323 |
+
}
|
324 |
+
elseif ($scheme == 'hsl') {
|
325 |
+
$this->hue = $colour['hue'];
|
326 |
+
$this->saturation = $colour['saturation'];
|
327 |
+
$this->lightness = $colour['lightness'];
|
328 |
+
$this->alpha = (isset($colour['alpha']) ? $colour['alpha'] : 1);
|
329 |
+
}
|
330 |
+
else {
|
331 |
+
$this->red = $colour[0];
|
332 |
+
$this->green = $colour[1];
|
333 |
+
$this->blue = $colour[2];
|
334 |
+
$this->alpha = (isset($colour[3]) ? $colour[3] : 1);
|
335 |
+
}
|
336 |
+
}
|
337 |
+
else {
|
338 |
+
throw new SassColourException('Colour must be a array', SassScriptParser::$context->node);
|
339 |
+
}
|
340 |
+
}
|
341 |
+
|
342 |
+
/**
|
343 |
+
* Colour addition
|
344 |
+
* @param mixed SassColour|SassNumber value to add
|
345 |
+
* @return sassColour the colour result
|
346 |
+
*/
|
347 |
+
public function op_plus($other) {
|
348 |
+
if ($other instanceof SassNumber) {
|
349 |
+
if (!$other->isUnitless()) {
|
350 |
+
return new SassString($this->toString() . $other->value);
|
351 |
+
throw new SassColourException('Number must be a unitless number', SassScriptParser::$context->node);
|
352 |
+
}
|
353 |
+
$this->red = $this->getRed() + $other->value;
|
354 |
+
$this->green = $this->getGreen() + $other->value;
|
355 |
+
$this->blue = $this->getBlue() + $other->value;
|
356 |
+
}
|
357 |
+
elseif (!$other instanceof SassColour) {
|
358 |
+
return new SassString($this->toString() . $other->value);
|
359 |
+
throw new SassColourException('Argument must be a SassColour or SassNumber', SassScriptParser::$context->node);
|
360 |
+
}
|
361 |
+
else {
|
362 |
+
$this->red = $this->getRed() + $other->getRed();
|
363 |
+
$this->green = $this->getGreen() + $other->getGreen();
|
364 |
+
$this->blue = $this->getBlue() + $other->getBlue();
|
365 |
+
}
|
366 |
+
return $this;
|
367 |
+
}
|
368 |
+
|
369 |
+
/**
|
370 |
+
* Colour subraction
|
371 |
+
* @param mixed value (SassColour or SassNumber) to subtract
|
372 |
+
* @return sassColour the colour result
|
373 |
+
*/
|
374 |
+
public function op_minus($other) {
|
375 |
+
if ($other instanceof SassNumber) {
|
376 |
+
if (!$other->isUnitless()) {
|
377 |
+
throw new SassColourException('Number must be a unitless number', SassScriptParser::$context->node);
|
378 |
+
}
|
379 |
+
$this->red = $this->getRed() - $other->value;
|
380 |
+
$this->green = $this->getGreen() - $other->value;
|
381 |
+
$this->blue = $this->getBlue() - $other->value;
|
382 |
+
}
|
383 |
+
elseif (!$other instanceof SassColour) {
|
384 |
+
throw new SassColourException('Argument must be a SassColour or SassNumber', SassScriptParser::$context->node);
|
385 |
+
}
|
386 |
+
else {
|
387 |
+
$this->red = $this->getRed() - $other->getRed();
|
388 |
+
$this->green = $this->getGreen() - $other->getGreen();
|
389 |
+
$this->blue = $this->getBlue() - $other->getBlue();
|
390 |
+
}
|
391 |
+
return $this;
|
392 |
+
}
|
393 |
+
|
394 |
+
/**
|
395 |
+
* Colour multiplication
|
396 |
+
* @param mixed SassColour|SassNumber value to multiply by
|
397 |
+
* @return sassColour the colour result
|
398 |
+
*/
|
399 |
+
public function op_times($other) {
|
400 |
+
if ($other instanceof SassNumber) {
|
401 |
+
if (!$other->isUnitless()) {
|
402 |
+
throw new SassColourException('Number must be a unitless number', SassScriptParser::$context->node);
|
403 |
+
}
|
404 |
+
$this->red = $this->getRed() * $other->value;
|
405 |
+
$this->green = $this->getGreen() * $other->value;
|
406 |
+
$this->blue = $this->getBlue() * $other->value;
|
407 |
+
}
|
408 |
+
elseif (!$other instanceof SassColour) {
|
409 |
+
throw new SassColourException('Argument must be a SassColour or SassNumber', SassScriptParser::$context->node);
|
410 |
+
}
|
411 |
+
else {
|
412 |
+
$this->red = $this->getRed() * $other->getRed();
|
413 |
+
$this->green = $this->getGreen() * $other->getGreen();
|
414 |
+
$this->blue = $this->getBlue() * $other->getBlue();
|
415 |
+
}
|
416 |
+
return $this;
|
417 |
+
}
|
418 |
+
|
419 |
+
/**
|
420 |
+
* Colour division
|
421 |
+
* @param mixed value (SassColour or SassNumber) to divide by
|
422 |
+
* @return sassColour the colour result
|
423 |
+
*/
|
424 |
+
public function op_div($other) {
|
425 |
+
if ($other instanceof SassNumber) {
|
426 |
+
if (!$other->isUnitless()) {
|
427 |
+
throw new SassColourException('Number must be a unitless number', SassScriptParser::$context->node);
|
428 |
+
}
|
429 |
+
$this->red = $this->getRed() / $other->value;
|
430 |
+
$this->green = $this->getGreen() / $other->value;
|
431 |
+
$this->blue = $this->getBlue() / $other->value;
|
432 |
+
}
|
433 |
+
elseif (!$other instanceof SassColour) {
|
434 |
+
throw new SassColourException('Argument must be a SassColour or SassNumber', SassScriptParser::$context->node);
|
435 |
+
}
|
436 |
+
else {
|
437 |
+
$this->red = $this->getRed() / $other->getRed();
|
438 |
+
$this->green = $this->getGreen() / $other->getGreen();
|
439 |
+
$this->blue = $this->getBlue() / $other->getBlue();
|
440 |
+
}
|
441 |
+
return $this;
|
442 |
+
}
|
443 |
+
|
444 |
+
/**
|
445 |
+
* Colour modulus
|
446 |
+
* @param mixed value (SassColour or SassNumber) to divide by
|
447 |
+
* @return sassColour the colour result
|
448 |
+
*/
|
449 |
+
public function op_modulo($other) {
|
450 |
+
if ($other instanceof SassNumber) {
|
451 |
+
if (!$other->isUnitless()) {
|
452 |
+
throw new SassColourException('Number must be a unitless number', SassScriptParser::$context->node);
|
453 |
+
}
|
454 |
+
$this->red = fmod($this->getRed(), $other->value);
|
455 |
+
$this->green = fmod($this->getGreen(), $other->value);
|
456 |
+
$this->blue = fmod($this->getBlue(), $other->value);
|
457 |
+
}
|
458 |
+
elseif (!$other instanceof SassColour) {
|
459 |
+
throw new SassColourException('Argument must be a SassColour or SassNumber', SassScriptParser::$context->node);
|
460 |
+
}
|
461 |
+
else {
|
462 |
+
$this->red = fmod($this->getRed(), $other->getRed());
|
463 |
+
$this->green = fmod($this->getGreen(), $other->getGreen());
|
464 |
+
$this->blue = fmod($this->getBlue(), $other->getBlue());
|
465 |
+
}
|
466 |
+
return $this;
|
467 |
+
}
|
468 |
+
|
469 |
+
/**
|
470 |
+
* Colour bitwise AND
|
471 |
+
* @param mixed value (SassColour or SassNumber) to bitwise AND with
|
472 |
+
* @return sassColour the colour result
|
473 |
+
*/
|
474 |
+
public function op_bw_and($other) {
|
475 |
+
if ($other instanceof SassNumber) {
|
476 |
+
if (!$other->isUnitless()) {
|
477 |
+
throw new SassColourException('Number must be a unitless number', SassScriptParser::$context->node);
|
478 |
+
}
|
479 |
+
$this->red = $this->getRed() & $other->value;
|
480 |
+
$this->green = $this->getGreen() & $other->value;
|
481 |
+
$this->blue = $this->getBlue() & $other->value;
|
482 |
+
}
|
483 |
+
elseif (!$other instanceof SassColour) {
|
484 |
+
throw new SassColourException('Argument must be a SassColour or SassNumber', SassScriptParser::$context->node);
|
485 |
+
}
|
486 |
+
else {
|
487 |
+
$this->red = $this->getRed() & $other->getRed();
|
488 |
+
$this->green = $this->getGreen() & $other->getGreen();
|
489 |
+
$this->blue = $this->getBlue() & $other->getBlue();
|
490 |
+
}
|
491 |
+
return $this;
|
492 |
+
}
|
493 |
+
|
494 |
+
/**
|
495 |
+
* Colour bitwise OR
|
496 |
+
* @param mixed value (SassColour or SassNumber) to bitwise OR with
|
497 |
+
* @return sassColour the colour result
|
498 |
+
*/
|
499 |
+
public function op_bw_or($other) {
|
500 |
+
if ($other instanceof SassNumber) {
|
501 |
+
if (!$other->isUnitless()) {
|
502 |
+
throw new SassColourException('Number must be a unitless number', SassScriptParser::$context->node);
|
503 |
+
}
|
504 |
+
$this->red = $this->getRed() | $other->value;
|
505 |
+
$this->green = $this->getGreen() | $other->value;
|
506 |
+
$this->blue = $this->getBlue() | $other->value;
|
507 |
+
}
|
508 |
+
elseif (!$other instanceof SassColour) {
|
509 |
+
throw new SassColourException('Argument must be a SassColour or SassNumber', SassScriptParser::$context->node);
|
510 |
+
}
|
511 |
+
else {
|
512 |
+
$this->red = $this->getRed() | $other->getRed();
|
513 |
+
$this->green = $this->getGreen() | $other->getGreen();
|
514 |
+
$this->blue = $this->getBlue() | $other->getBlue();
|
515 |
+
}
|
516 |
+
return $this;
|
517 |
+
}
|
518 |
+
|
519 |
+
/**
|
520 |
+
* Colour bitwise XOR
|
521 |
+
* @param mixed value (SassColour or SassNumber) to bitwise XOR with
|
522 |
+
* @return sassColour the colour result
|
523 |
+
*/
|
524 |
+
public function op_bw_xor($other) {
|
525 |
+
if ($other instanceof SassNumber) {
|
526 |
+
if (!$other->isUnitless()) {
|
527 |
+
throw new SassColourException('Number must be a unitless number', SassScriptParser::$context->node);
|
528 |
+
}
|
529 |
+
$this->red = $this->getRed() ^ $other->value;
|
530 |
+
$this->green = $this->getGreen() ^ $other->value;
|
531 |
+
$this->blue = $this->getBlue() ^ $other->value;
|
532 |
+
}
|
533 |
+
elseif (!$other instanceof SassColour) {
|
534 |
+
throw new SassColourException('Argument must be a SassColour or SassNumber', SassScriptParser::$context->node);
|
535 |
+
}
|
536 |
+
else {
|
537 |
+
$this->red = $this->getRed() ^ $other->getRed();
|
538 |
+
$this->green = $this->getGreen() ^ $other->getGreen();
|
539 |
+
$this->blue = $this->getBlue() ^ $other->getBlue();
|
540 |
+
}
|
541 |
+
return $this;
|
542 |
+
}
|
543 |
+
|
544 |
+
/**
|
545 |
+
* Colour bitwise NOT
|
546 |
+
* @return sassColour the colour result
|
547 |
+
*/
|
548 |
+
public function op_not() {
|
549 |
+
$this->red = ~$this->getRed();
|
550 |
+
$this->green = ~$this->getGreen();
|
551 |
+
$this->blue = ~$this->getBlue();
|
552 |
+
return $this;
|
553 |
+
}
|
554 |
+
|
555 |
+
/**
|
556 |
+
* Colour bitwise Shift Left
|
557 |
+
* @param sassNumber amount to shift left by
|
558 |
+
* @return sassColour the colour result
|
559 |
+
*/
|
560 |
+
public function op_shiftl($other) {
|
561 |
+
if (!$other instanceof SassNumber ||!$other->isUnitless()) {
|
562 |
+
throw new SassColourException('Number must be a unitless number', SassScriptParser::$context->node);
|
563 |
+
}
|
564 |
+
$this->red = $this->getRed() << $other->value;
|
565 |
+
$this->green = $this->getGreen() << $other->value;
|
566 |
+
$this->blue = $this->getBlue() << $other->value;
|
567 |
+
return $this;
|
568 |
+
}
|
569 |
+
|
570 |
+
/**
|
571 |
+
* Colour bitwise Shift Right
|
572 |
+
* @param sassNumber amount to shift right by
|
573 |
+
* @return sassColour the colour result
|
574 |
+
*/
|
575 |
+
public function op_shiftr($other) {
|
576 |
+
if (!$other instanceof SassNumber || !$other->isUnitless()) {
|
577 |
+
throw new SassColourException('Number must be a unitless number', SassScriptParser::$context->node);
|
578 |
+
}
|
579 |
+
$this->red = $this->getRed() >> $other->value;
|
580 |
+
$this->green = $this->getGreen() >> $other->value;
|
581 |
+
$this->blue = $this->getBlue() >> $other->value;
|
582 |
+
return $this;
|
583 |
+
}
|
584 |
+
|
585 |
+
/**
|
586 |
+
* Returns a copy of this colour with one or more channels changed.
|
587 |
+
* RGB or HSL attributes may be changed, but not both at once.
|
588 |
+
* @param array attributes to change
|
589 |
+
*/
|
590 |
+
public function with($attributes) {
|
591 |
+
if ($this->assertValid($attributes, false) === 'hsl') {
|
592 |
+
$colour = array_merge(array(
|
593 |
+
'hue' => $this->getHue(),
|
594 |
+
'saturation' => $this->getSaturation(),
|
595 |
+
'lightness' => $this->getLightness(),
|
596 |
+
'alpha' => $this->alpha
|
597 |
+
), $attributes);
|
598 |
+
}
|
599 |
+
else {
|
600 |
+
$colour = array_merge(array(
|
601 |
+
'red' => $this->getRed(),
|
602 |
+
'green' => $this->getGreen(),
|
603 |
+
'blue' => $this->getBlue(),
|
604 |
+
'alpha' => $this->alpha
|
605 |
+
), $attributes);
|
606 |
+
}
|
607 |
+
|
608 |
+
|
609 |
+
$colour = new SassColour($colour);
|
610 |
+
$colour->getRed(); # will get RGB and HSL
|
611 |
+
return $colour;
|
612 |
+
}
|
613 |
+
|
614 |
+
/**
|
615 |
+
* Returns the alpha component (opacity) of this colour.
|
616 |
+
* @return float the alpha component (opacity) of this colour.
|
617 |
+
*/
|
618 |
+
public function getAlpha($value = false) {
|
619 |
+
if ($value && isset($this->alpha->$value)) {
|
620 |
+
return $this->alpha->value;
|
621 |
+
}
|
622 |
+
return $this->alpha;
|
623 |
+
}
|
624 |
+
|
625 |
+
/**
|
626 |
+
* Returns the hue of this colour.
|
627 |
+
* @return float the hue of this colour.
|
628 |
+
*/
|
629 |
+
public function getHue($value = false) {
|
630 |
+
if (is_null($this->hue)) {
|
631 |
+
$this->rgb2hsl();
|
632 |
+
}
|
633 |
+
if ($value && isset($this->hue->value)) {
|
634 |
+
return $this->hue->value;
|
635 |
+
}
|
636 |
+
return $this->hue;
|
637 |
+
}
|
638 |
+
|
639 |
+
/**
|
640 |
+
* Returns the saturation of this colour.
|
641 |
+
* @return float the saturation of this colour.
|
642 |
+
*/
|
643 |
+
public function getSaturation($value = false) {
|
644 |
+
if (is_null($this->saturation)) {
|
645 |
+
$this->rgb2hsl();
|
646 |
+
}
|
647 |
+
if ($value && isset($this->saturation->value)) {
|
648 |
+
return $this->saturation->value;
|
649 |
+
}
|
650 |
+
return $this->saturation;
|
651 |
+
}
|
652 |
+
|
653 |
+
/**
|
654 |
+
* Returns the lightness of this colour.
|
655 |
+
* @return float the lightness of this colour.
|
656 |
+
*/
|
657 |
+
public function getLightness($value = false) {
|
658 |
+
if (is_null($this->lightness)) {
|
659 |
+
$this->rgb2hsl();
|
660 |
+
}
|
661 |
+
if ($value && isset($this->lightness->value)) {
|
662 |
+
return $this->lightness->value;
|
663 |
+
}
|
664 |
+
return $this->lightness;
|
665 |
+
}
|
666 |
+
|
667 |
+
/**
|
668 |
+
* Returns the blue component of this colour.
|
669 |
+
* @return integer the blue component of this colour.
|
670 |
+
*/
|
671 |
+
public function getBlue($value = false) {
|
672 |
+
if (is_null($this->blue)) {
|
673 |
+
$this->hsl2rgb();
|
674 |
+
}
|
675 |
+
if ($value && isset($this->blue->value)) {
|
676 |
+
return $this->blue->value;
|
677 |
+
}
|
678 |
+
return max(0, min(255, str_replace(',','.',round($this->blue))));
|
679 |
+
}
|
680 |
+
|
681 |
+
/**
|
682 |
+
* Returns the green component of this colour.
|
683 |
+
* @return integer the green component of this colour.
|
684 |
+
*/
|
685 |
+
public function getGreen($value = false) {
|
686 |
+
if (is_null($this->green)) {
|
687 |
+
$this->hsl2rgb();
|
688 |
+
}
|
689 |
+
if ($value && isset($this->green->value)) {
|
690 |
+
return $this->green->value;
|
691 |
+
}
|
692 |
+
return max(0, min(255, str_replace(',','.',round($this->green))));
|
693 |
+
}
|
694 |
+
|
695 |
+
/**
|
696 |
+
* Returns the red component of this colour.
|
697 |
+
* @return integer the red component of this colour.
|
698 |
+
*/
|
699 |
+
public function getRed($value = false) {
|
700 |
+
if (is_null($this->red)) {
|
701 |
+
$this->hsl2rgb();
|
702 |
+
}
|
703 |
+
if ($value && isset($this->red->value)) {
|
704 |
+
return $this->red->value;
|
705 |
+
}
|
706 |
+
return max(0, min(255, str_replace(',','.',round($this->red))));
|
707 |
+
}
|
708 |
+
|
709 |
+
/**
|
710 |
+
* Returns an array with the RGB components of this colour.
|
711 |
+
* @return array the RGB components of this colour
|
712 |
+
*/
|
713 |
+
public function getRgb() {
|
714 |
+
return array($this->red, $this->green, $this->blue);
|
715 |
+
}
|
716 |
+
|
717 |
+
/**
|
718 |
+
* Returns an array with the RGB and alpha components of this colour.
|
719 |
+
* @return array the RGB and alpha components of this colour
|
720 |
+
*/
|
721 |
+
public function getRgba() {
|
722 |
+
return array($this->getRed(), $this->getGreen(), $this->getBlue(), $this->alpha);
|
723 |
+
}
|
724 |
+
|
725 |
+
/**
|
726 |
+
* Returns an array with the HSL components of this colour.
|
727 |
+
* @return array the HSL components of this colour
|
728 |
+
*/
|
729 |
+
public function getHsl() {
|
730 |
+
return array($this->getHue(), $this->getSaturation(), $this->getLightness());
|
731 |
+
}
|
732 |
+
|
733 |
+
/**
|
734 |
+
* Returns an array with the HSL and alpha components of this colour.
|
735 |
+
* @return array the HSL and alpha components of this colour
|
736 |
+
*/
|
737 |
+
public function getHsla() {
|
738 |
+
return array($this->getHue(), $this->getSaturation(), $this->getLightness(), $this->alpha);
|
739 |
+
}
|
740 |
+
|
741 |
+
/**
|
742 |
+
* Returns the value of this colour.
|
743 |
+
* @return array the colour
|
744 |
+
* @deprecated
|
745 |
+
*/
|
746 |
+
public function getValue() {
|
747 |
+
return $this->rgb;
|
748 |
+
}
|
749 |
+
|
750 |
+
/**
|
751 |
+
* Returns whether this colour object is translucent; that is, whether the alpha channel is non-1.
|
752 |
+
* @return boolean true if this colour is translucent, false if not
|
753 |
+
*/
|
754 |
+
public function isTranslucent() {
|
755 |
+
return $this->alpha < 1;
|
756 |
+
}
|
757 |
+
|
758 |
+
/**
|
759 |
+
* Converts the colour to a string.
|
760 |
+
* @param boolean whether to use CSS3 SVG1.0 colour names
|
761 |
+
* @return string the colour as a named colour, rgba(r,g,g,a) or #rrggbb
|
762 |
+
*/
|
763 |
+
public function toString($css3 = true) {
|
764 |
+
$rgba = $this->getRgba();
|
765 |
+
|
766 |
+
foreach ($rgba as $k => $v) {
|
767 |
+
if (is_object($v)) {
|
768 |
+
$rgba[$k] = $v->value;
|
769 |
+
}
|
770 |
+
}
|
771 |
+
|
772 |
+
if ($rgba[3] == 0) {
|
773 |
+
return 'transparent';
|
774 |
+
}
|
775 |
+
elseif ($rgba[3] < 1) {
|
776 |
+
$rgba[3] = str_replace(',','.',round($rgba[3], 2));
|
777 |
+
return sprintf('rgba(%d, %d, %d, %s)', $rgba[0], $rgba[1], $rgba[2], $rgba[3]);
|
778 |
+
}
|
779 |
+
else {
|
780 |
+
$colour = sprintf('#%02x%02x%02x', str_replace(',','.',round($rgba[0])), str_replace(',','.',round($rgba[1])), str_replace(',','.',round($rgba[2])));
|
781 |
+
}
|
782 |
+
|
783 |
+
if ($css3) {
|
784 |
+
if (empty(self::$_svgColours)) {
|
785 |
+
self::$_svgColours = array_flip(self::$svgColours);
|
786 |
+
}
|
787 |
+
return (array_key_exists($colour, self::$_svgColours) ? self::$_svgColours[$colour] : $colour);
|
788 |
+
}
|
789 |
+
else {
|
790 |
+
return (array_key_exists($colour, self::$_html4Colours) ? self::$_html4Colours[$colour] : $colour);
|
791 |
+
}
|
792 |
+
}
|
793 |
+
|
794 |
+
|
795 |
+
public function asHex($inc_hash = TRUE) {
|
796 |
+
return sprintf(($inc_hash ? '#' : '') . '%02x%02x%02x', str_replace(',','.',round($this->red)), str_replace(',','.',round($this->green)), str_replace(',','.',round($this->blue)));
|
797 |
+
}
|
798 |
+
|
799 |
+
/**
|
800 |
+
* Converts from HSL to RGB colourspace
|
801 |
+
* Algorithm from the CSS3 spec: {@link http://www.w3.org/TR/css3-color/#hsl-color}
|
802 |
+
* @uses hue2rgb()
|
803 |
+
*/
|
804 |
+
public function hsl2rgb() {
|
805 |
+
$h = $this->getHue(true) / 360;
|
806 |
+
$s = $this->getSaturation(true) / 100;
|
807 |
+
$l = $this->getLightness(true) / 100;
|
808 |
+
|
809 |
+
$q = $l < 0.5 ? $l * (1 + $s)
|
810 |
+
: $l + $s - $l * $s;
|
811 |
+
$p = 2 * $l - $q;
|
812 |
+
|
813 |
+
$this->red = $this->hue2rgb($p, $q, $h + 1/3);
|
814 |
+
$this->green = $this->hue2rgb($p, $q, $h);
|
815 |
+
$this->blue = $this->hue2rgb($p, $q, $h - 1/3);
|
816 |
+
}
|
817 |
+
|
818 |
+
/**
|
819 |
+
* Converts from hue to RGB colourspace
|
820 |
+
*/
|
821 |
+
public function hue2rgb($p, $q, $t) {
|
822 |
+
if ($t < 0)
|
823 |
+
$t += 1;
|
824 |
+
if ($t > 1)
|
825 |
+
$t -= 1;
|
826 |
+
|
827 |
+
if ($t < 1/6)
|
828 |
+
$p = $p + ($q - $p) * 6 * $t;
|
829 |
+
else if ($t < 1/2)
|
830 |
+
$p = $q;
|
831 |
+
else if ($t < 2/3)
|
832 |
+
$p = $p + ($q - $p) * (2/3 - $t) * 6;
|
833 |
+
|
834 |
+
return str_replace(',','.',round($p * 255));
|
835 |
+
}
|
836 |
+
|
837 |
+
/**
|
838 |
+
* Converts from RGB to HSL colourspace
|
839 |
+
* Algorithm adapted from {@link http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript}
|
840 |
+
*/
|
841 |
+
public function rgb2hsl() {
|
842 |
+
list($r, $g, $b) = array($this->red / 255, $this->green / 255, $this->blue / 255);
|
843 |
+
|
844 |
+
$max = max($r, $g, $b);
|
845 |
+
$min = min($r, $g, $b);
|
846 |
+
$d = $max - $min;
|
847 |
+
|
848 |
+
if ($max == $min) {
|
849 |
+
$h = 0;
|
850 |
+
$s = 0;
|
851 |
+
}
|
852 |
+
else if ($max == $r)
|
853 |
+
$h = 60 * ($g - $b) / $d;
|
854 |
+
else if ($max == $g)
|
855 |
+
$h = 60 * ($b - $r) / $d + 120;
|
856 |
+
else if ($max == $b)
|
857 |
+
$h = 60 * ($r - $g) / $d + 240;
|
858 |
+
|
859 |
+
$l = ($max + $min) / 2;
|
860 |
+
|
861 |
+
if ($l > 0.5)
|
862 |
+
$s = (2 - $max - $min) != 0 ? $d / (2 - $max - $min) : 0;
|
863 |
+
else
|
864 |
+
$s = ($max + $min) != 0 ? $d / ($max + $min) : 0;
|
865 |
+
|
866 |
+
while ($h > 360) $h -= 360;
|
867 |
+
while ($h < 0) $h += 360;
|
868 |
+
|
869 |
+
$this->hue = $h;
|
870 |
+
$this->saturation = $s * 100;
|
871 |
+
$this->lightness = $l * 100;
|
872 |
+
}
|
873 |
+
|
874 |
+
/**
|
875 |
+
* Asserts that the colour space is valid.
|
876 |
+
* Returns the name of the colour space: 'rgb' if red, green, or blue keys given;
|
877 |
+
* 'hsl' if hue, saturation or lightness keys given; null if a non-associative array
|
878 |
+
* @param array the colour to test
|
879 |
+
* @param boolean whether all colour space keys must be given
|
880 |
+
* @return string name of the colour space
|
881 |
+
* @throws SassColourException if mixed colour space keys given or not all
|
882 |
+
* keys for a colour space are required but not given (contructor)
|
883 |
+
*/
|
884 |
+
public function assertValid($colour, $all = true) {
|
885 |
+
if (array_key_exists('red', $colour) || array_key_exists('green', $colour) || array_key_exists('blue', $colour)) {
|
886 |
+
if (array_key_exists('hue', $colour) || array_key_exists('saturation', $colour) || array_key_exists('lightness', $colour)) {
|
887 |
+
throw new SassColourException('SassColour can not have HSL and RGB keys specified', SassScriptParser::$context->node);
|
888 |
+
}
|
889 |
+
if ($all && (!array_key_exists('red', $colour) || !array_key_exists('green', $colour) || !array_key_exists('blue', $colour))) {
|
890 |
+
throw new SassColourException('SassColour must have all RGB keys specified', SassScriptParser::$context->node);
|
891 |
+
}
|
892 |
+
return 'rgb';
|
893 |
+
}
|
894 |
+
elseif (array_key_exists('hue', $colour) || array_key_exists('saturation', $colour) || array_key_exists('lightness', $colour)) {
|
895 |
+
if ($all && (!array_key_exists('hue', $colour) || !array_key_exists('saturation', $colour) || !array_key_exists('lightness', $colour))) {
|
896 |
+
throw new SassColourException('SassColour must have all HSL keys specified', SassScriptParser::$context->node);
|
897 |
+
}
|
898 |
+
return 'hsl';
|
899 |
+
}
|
900 |
+
elseif ($all && sizeof($colour) < 3) {
|
901 |
+
throw new SassColourException('SassColour array must have at least 3 elements', SassScriptParser::$context->node);
|
902 |
+
}
|
903 |
+
}
|
904 |
+
|
905 |
+
/**
|
906 |
+
* Returns a value indicating if a token of this type can be matched at
|
907 |
+
* the start of the subject string.
|
908 |
+
* @param string the subject string
|
909 |
+
* @return mixed match at the start of the string or false if no match
|
910 |
+
*/
|
911 |
+
static public function isa($subject) {
|
912 |
+
if (empty(self::$regex)) {
|
913 |
+
self::$regex = str_replace('{CSS_COLOURS}', join('|', array_reverse(array_keys(self::$svgColours))), self::MATCH);
|
914 |
+
}
|
915 |
+
return (preg_match(self::$regex, strtolower($subject), $matches) ?
|
916 |
+
$matches[0] : false);
|
917 |
+
}
|
918 |
+
}
|
lib/phpsass/script/literals/SassList.php
ADDED
@@ -0,0 +1,219 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* SVN FILE: $Id$ */
|
3 |
+
/**
|
4 |
+
* SassBoolean class file.
|
5 |
+
* @author Chris Yates <chris.l.yates@gmail.com>
|
6 |
+
* @copyright Copyright (c) 2010 PBM Web Development
|
7 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
8 |
+
* @package PHamlP
|
9 |
+
* @subpackage Sass.script.literals
|
10 |
+
*/
|
11 |
+
|
12 |
+
require_once('SassLiteral.php');
|
13 |
+
|
14 |
+
/**
|
15 |
+
* SassBoolean class.
|
16 |
+
* @package PHamlP
|
17 |
+
* @subpackage Sass.script.literals
|
18 |
+
*/
|
19 |
+
class SassList extends SassLiteral {
|
20 |
+
|
21 |
+
var $separator = ' ';
|
22 |
+
|
23 |
+
/**
|
24 |
+
* SassBoolean constructor
|
25 |
+
* @param string value of the boolean type
|
26 |
+
* @return SassBoolean
|
27 |
+
*/
|
28 |
+
public function __construct($value, $separator = 'auto') {
|
29 |
+
if (is_array($value)) {
|
30 |
+
$this->value = $value;
|
31 |
+
$this->separator = ($separator == 'auto' ? ', ' : $separator);
|
32 |
+
}
|
33 |
+
else if (list($list, $separator) = $this->_parse_list($value, $separator, true, SassScriptParser::$context)) {
|
34 |
+
$this->value = $list;
|
35 |
+
$this->separator = ($separator == ',' ? ', ' : ' ');
|
36 |
+
}
|
37 |
+
else {
|
38 |
+
throw new SassListException('Invalid SassList', SassScriptParser::$context->node);
|
39 |
+
}
|
40 |
+
}
|
41 |
+
|
42 |
+
function nth($i) {
|
43 |
+
$i = $i - 1; # SASS uses 1-offset arrays
|
44 |
+
if (isset($this->value[$i])) {
|
45 |
+
return $this->value[$i];
|
46 |
+
}
|
47 |
+
return new SassBoolean(false);
|
48 |
+
}
|
49 |
+
|
50 |
+
function length() {
|
51 |
+
return count($this->value);
|
52 |
+
}
|
53 |
+
|
54 |
+
function append($other, $separator = null) {
|
55 |
+
if ($separator) {
|
56 |
+
$this->separator = $separator;
|
57 |
+
}
|
58 |
+
if ($other instanceof SassList) {
|
59 |
+
$this->value = array_merge($this->value, $other->value);
|
60 |
+
}
|
61 |
+
else if ($other instanceof SassLiteral) {
|
62 |
+
$this->value[] = $other;
|
63 |
+
}
|
64 |
+
else {
|
65 |
+
throw new SassListException('Appendation can only occur with literals', SassScriptParser::$context->node);
|
66 |
+
}
|
67 |
+
}
|
68 |
+
|
69 |
+
// New function index returns the list index of a value within a list. For example: index(1px solid red, solid) returns 2. When the value is not found false is returned.
|
70 |
+
function index($value) {
|
71 |
+
for ($i = 0; $i < count($this->value); $i++) {
|
72 |
+
if (trim((string) $value) == trim((string) $this->value[$i])) {
|
73 |
+
return new SassNumber($i);
|
74 |
+
}
|
75 |
+
}
|
76 |
+
return new SassBoolean(false);
|
77 |
+
}
|
78 |
+
|
79 |
+
/**
|
80 |
+
* Returns the value of this boolean.
|
81 |
+
* @return boolean the value of this boolean
|
82 |
+
*/
|
83 |
+
public function getValue() {
|
84 |
+
$result = array();
|
85 |
+
foreach ($this->value as $k => $v) {
|
86 |
+
if ($v instanceOf SassString) {
|
87 |
+
$list = $this->_parse_list($v);
|
88 |
+
if (count($list[0]) > 1) {
|
89 |
+
if ($list[1] == $this->separator) {
|
90 |
+
$result = array_merge($result, $list[0]);
|
91 |
+
} else {
|
92 |
+
$result[] = $v;
|
93 |
+
}
|
94 |
+
} else {
|
95 |
+
$result[] = $v;
|
96 |
+
}
|
97 |
+
} else {
|
98 |
+
$result[] = $v;
|
99 |
+
}
|
100 |
+
}
|
101 |
+
$this->value = $result;
|
102 |
+
return $this->value;
|
103 |
+
}
|
104 |
+
|
105 |
+
/**
|
106 |
+
* Returns a string representation of the value.
|
107 |
+
* @return string string representation of the value.
|
108 |
+
*/
|
109 |
+
public function toString() {
|
110 |
+
$aliases = array(
|
111 |
+
'comma' => ',',
|
112 |
+
'space' => '',
|
113 |
+
);
|
114 |
+
$this->separator = trim($this->separator);
|
115 |
+
if (isset($aliases[$this->separator])) {
|
116 |
+
$this->separator = $aliases[$this->separator];
|
117 |
+
}
|
118 |
+
return implode($this->separator . ' ', $this->getValue());
|
119 |
+
}
|
120 |
+
|
121 |
+
/**
|
122 |
+
* Returns a value indicating if a token of this type can be matched at
|
123 |
+
* the start of the subject string.
|
124 |
+
* @param string the subject string
|
125 |
+
* @return mixed match at the start of the string or false if no match
|
126 |
+
*/
|
127 |
+
public static function isa($subject) {
|
128 |
+
list($list, $separator) = self::_parse_list($subject, 'auto', false);
|
129 |
+
return count($list) > 1 ? $subject : FALSE;
|
130 |
+
}
|
131 |
+
|
132 |
+
public static function _parse_list($list, $separator = 'auto', $lex = true, $context = null) {
|
133 |
+
if ($separator == 'auto') {
|
134 |
+
$separator = ',';
|
135 |
+
$list = $list = self::_build_list($list, ',');
|
136 |
+
if (count($list) < 2) {
|
137 |
+
$separator = ' ';
|
138 |
+
$list = self::_build_list($list, ' ');
|
139 |
+
}
|
140 |
+
}
|
141 |
+
else {
|
142 |
+
$list = self::_build_list($list, $separator);
|
143 |
+
}
|
144 |
+
|
145 |
+
if ($lex) {
|
146 |
+
$context = new SassContext($context);
|
147 |
+
foreach ($list as $k => $v) {
|
148 |
+
$list[$k] = SassScriptParser::$instance->evaluate($v, $context);
|
149 |
+
}
|
150 |
+
}
|
151 |
+
return array($list, $separator);
|
152 |
+
}
|
153 |
+
|
154 |
+
public static function _build_list($list, $separator = ',') {
|
155 |
+
if (is_object($list)) {
|
156 |
+
$list = $list->value;
|
157 |
+
}
|
158 |
+
|
159 |
+
if (is_array($list)) {
|
160 |
+
$newlist = array();
|
161 |
+
foreach ($list as $listlet) {
|
162 |
+
list($newlist, $separator) = array_merge($newlist, self::_parse_list($listlet, $separator, false));
|
163 |
+
}
|
164 |
+
$list = implode(', ', $newlist);
|
165 |
+
}
|
166 |
+
|
167 |
+
$out = array();
|
168 |
+
$size = 0;
|
169 |
+
$braces = 0;
|
170 |
+
$quotes = false;
|
171 |
+
$stack = '';
|
172 |
+
for($i = 0; $i < strlen($list); $i++) {
|
173 |
+
$char = substr($list, $i, 1);
|
174 |
+
switch ($char) {
|
175 |
+
case '"':
|
176 |
+
case "'":
|
177 |
+
if (!$quotes) {
|
178 |
+
$quotes = $char;
|
179 |
+
}
|
180 |
+
else if ($quotes && $quotes == $char) {
|
181 |
+
$quotes = false;
|
182 |
+
}
|
183 |
+
$stack .= $char;
|
184 |
+
break;
|
185 |
+
case '(':
|
186 |
+
$braces++;
|
187 |
+
$stack .= $char;
|
188 |
+
break;
|
189 |
+
case ')':
|
190 |
+
$braces--;
|
191 |
+
$stack .= $char;
|
192 |
+
break;
|
193 |
+
case $separator:
|
194 |
+
if ($braces === 0 && !$quotes) {
|
195 |
+
$out[] = $stack;
|
196 |
+
$stack = '';
|
197 |
+
$size++;
|
198 |
+
break;
|
199 |
+
}
|
200 |
+
default:
|
201 |
+
$stack .= $char;
|
202 |
+
}
|
203 |
+
}
|
204 |
+
if (strlen($stack)) {
|
205 |
+
if (($braces || $quotes) && count($out)) {
|
206 |
+
$out[count($out) - 1] .= $stack;
|
207 |
+
} else {
|
208 |
+
$out[] = $stack;
|
209 |
+
}
|
210 |
+
}
|
211 |
+
|
212 |
+
foreach ($out as $k => $v) {
|
213 |
+
$v = trim($v, ', ');
|
214 |
+
$out[$k] = $v;
|
215 |
+
}
|
216 |
+
|
217 |
+
return $out;
|
218 |
+
}
|
219 |
+
}
|
lib/phpsass/script/literals/SassLiteral.php
ADDED
@@ -0,0 +1,374 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* SVN FILE: $Id$ */
|
3 |
+
/**
|
4 |
+
* SassLiteral class file.
|
5 |
+
* @author Chris Yates <chris.l.yates@gmail.com>
|
6 |
+
* @copyright Copyright (c) 2010 PBM Web Development
|
7 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
8 |
+
* @package PHamlP
|
9 |
+
* @subpackage Sass.script.literals
|
10 |
+
*/
|
11 |
+
|
12 |
+
require_once('SassLiteralExceptions.php');
|
13 |
+
|
14 |
+
/**
|
15 |
+
* SassLiteral class.
|
16 |
+
* Base class for all Sass literals.
|
17 |
+
* Sass data types are extended from this class and these override the operation
|
18 |
+
* methods to provide the appropriate semantics.
|
19 |
+
* @package PHamlP
|
20 |
+
* @subpackage Sass.script.literals
|
21 |
+
*/
|
22 |
+
abstract class SassLiteral {
|
23 |
+
/**
|
24 |
+
* @var array maps class names to data types
|
25 |
+
*/
|
26 |
+
static public $typeOf = array(
|
27 |
+
'SassBoolean' => 'bool',
|
28 |
+
'SassColour' => 'color',
|
29 |
+
'SassNumber' => 'number',
|
30 |
+
'SassString' => 'string',
|
31 |
+
'SassList' => 'list'
|
32 |
+
);
|
33 |
+
|
34 |
+
/**
|
35 |
+
* @var mixed value of the literal type
|
36 |
+
*/
|
37 |
+
public $value;
|
38 |
+
|
39 |
+
/**
|
40 |
+
* class constructor
|
41 |
+
* @param string value of the literal type
|
42 |
+
* @return SassLiteral
|
43 |
+
*/
|
44 |
+
public function __construct($value = null, $context) {
|
45 |
+
$this->value = $value;
|
46 |
+
$this->context = $context;
|
47 |
+
}
|
48 |
+
|
49 |
+
/**
|
50 |
+
* Getter.
|
51 |
+
* @param string name of property to get
|
52 |
+
* @return mixed return value of getter function
|
53 |
+
*/
|
54 |
+
public function __get($name) {
|
55 |
+
$getter = 'get' . ucfirst($name);
|
56 |
+
if (method_exists($this, $getter)) {
|
57 |
+
return $this->$getter();
|
58 |
+
}
|
59 |
+
else {
|
60 |
+
throw new SassLiteralException('No getter function for ' . $name, SassScriptParser::$context->node);
|
61 |
+
}
|
62 |
+
}
|
63 |
+
|
64 |
+
public function __toString() {
|
65 |
+
return $this->toString();
|
66 |
+
}
|
67 |
+
|
68 |
+
/**
|
69 |
+
* Returns the boolean representation of the value of this
|
70 |
+
* @return boolean the boolean representation of the value of this
|
71 |
+
*/
|
72 |
+
public function toBoolean() {
|
73 |
+
return (boolean)$this->value || $this->value === null;
|
74 |
+
}
|
75 |
+
|
76 |
+
/**
|
77 |
+
* Returns the type of this
|
78 |
+
* @return string the type of this
|
79 |
+
*/
|
80 |
+
public function getTypeOf() {
|
81 |
+
return self::$typeOf[get_class($this)];
|
82 |
+
}
|
83 |
+
|
84 |
+
/**
|
85 |
+
* Returns the value of this
|
86 |
+
* @return mixed the value of this
|
87 |
+
*/
|
88 |
+
public function getValue() {
|
89 |
+
throw new SassLiteralException('Child classes must override this method', SassScriptParser::$context->node);
|
90 |
+
}
|
91 |
+
|
92 |
+
public function getChildren() {
|
93 |
+
return array();
|
94 |
+
}
|
95 |
+
|
96 |
+
/**
|
97 |
+
* Adds a child object to this.
|
98 |
+
* @param sassLiteral the child object
|
99 |
+
*/
|
100 |
+
public function addChild($sassLiteral) {
|
101 |
+
$this->children[] = $sassLiteral;
|
102 |
+
}
|
103 |
+
|
104 |
+
/**
|
105 |
+
* SassScript '+' operation.
|
106 |
+
* @param sassLiteral value to add
|
107 |
+
* @return sassString the string values of this and other with no seperation
|
108 |
+
*/
|
109 |
+
public function op_plus($other) {
|
110 |
+
return new SassString($this->toString().$other->toString());
|
111 |
+
}
|
112 |
+
|
113 |
+
/**
|
114 |
+
* SassScript '-' operation.
|
115 |
+
* @param SassLiteral value to subtract
|
116 |
+
* @return sassString the string values of this and other seperated by '-'
|
117 |
+
*/
|
118 |
+
public function op_minus($other) {
|
119 |
+
return new SassString($this->toString().'-'.$other->toString());
|
120 |
+
}
|
121 |
+
|
122 |
+
/**
|
123 |
+
* SassScript '*' operation.
|
124 |
+
* @param SassLiteral value to multiply by
|
125 |
+
* @return sassString the string values of this and other seperated by '*'
|
126 |
+
*/
|
127 |
+
public function op_times($other) {
|
128 |
+
return new SassString($this->toString().'*'.$other->toString());
|
129 |
+
}
|
130 |
+
|
131 |
+
/**
|
132 |
+
* SassScript '/' operation.
|
133 |
+
* @param SassLiteral value to divide by
|
134 |
+
* @return sassString the string values of this and other seperated by '/'
|
135 |
+
*/
|
136 |
+
public function op_div($other) {
|
137 |
+
return new SassString($this->toString().' / '.$other->toString());
|
138 |
+
}
|
139 |
+
|
140 |
+
/**
|
141 |
+
* SassScript '%' operation.
|
142 |
+
* @param SassLiteral value to take the modulus of
|
143 |
+
* @return SassLiteral result
|
144 |
+
* @throws Exception if modulo not supported for the data type
|
145 |
+
*/
|
146 |
+
public function op_modulo($other) {
|
147 |
+
throw new SassLiteralException(get_class($this) . ' does not support Modulus', SassScriptParser::$context->node);
|
148 |
+
}
|
149 |
+
|
150 |
+
/**
|
151 |
+
* Bitwise AND the value of other and this value
|
152 |
+
* @param string value to bitwise AND with
|
153 |
+
* @return string result
|
154 |
+
* @throws Exception if bitwise AND not supported for the data type
|
155 |
+
*/
|
156 |
+
public function op_bw_and($other) {
|
157 |
+
throw new SassLiteralException(get_class($this) . ' does not support Bitwise AND', SassScriptParser::$context->node);
|
158 |
+
}
|
159 |
+
|
160 |
+
/**
|
161 |
+
* Bitwise OR the value of other and this value
|
162 |
+
* @param SassNumber value to bitwise OR with
|
163 |
+
* @return string result
|
164 |
+
* @throws Exception if bitwise OR not supported for the data type
|
165 |
+
*/
|
166 |
+
public function op_bw_or($other) {
|
167 |
+
throw new SassLiteralException(get_class($this) . ' does not support Bitwise OR', SassScriptParser::$context->node);
|
168 |
+
}
|
169 |
+
|
170 |
+
/**
|
171 |
+
* Bitwise XOR the value of other and the value of this
|
172 |
+
* @param SassNumber value to bitwise XOR with
|
173 |
+
* @return string result
|
174 |
+
* @throws Exception if bitwise XOR not supported for the data type
|
175 |
+
*/
|
176 |
+
public function op_bw_xor($other) {
|
177 |
+
throw new SassLiteralException(get_class($this) . ' does not support Bitwise XOR', SassScriptParser::$context->node);
|
178 |
+
}
|
179 |
+
|
180 |
+
/**
|
181 |
+
* Bitwise NOT the value of other and the value of this
|
182 |
+
* @param SassNumber value to bitwise NOT with
|
183 |
+
* @return string result
|
184 |
+
* @throws Exception if bitwise NOT not supported for the data type
|
185 |
+
*/
|
186 |
+
public function op_bw_not() {
|
187 |
+
throw new SassLiteralException(get_class($this) . ' does not support Bitwise NOT', SassScriptParser::$context->node);
|
188 |
+
}
|
189 |
+
|
190 |
+
/**
|
191 |
+
* Shifts the value of this left by the number of bits given in value
|
192 |
+
* @param SassNumber amount to shift left by
|
193 |
+
* @return string result
|
194 |
+
* @throws Exception if bitwise Shift Left not supported for the data type
|
195 |
+
*/
|
196 |
+
public function op_shiftl($other) {
|
197 |
+
throw new SassLiteralException(get_class($this) . ' does not support Bitwise Shift Left', SassScriptParser::$context->node);
|
198 |
+
}
|
199 |
+
|
200 |
+
/**
|
201 |
+
* Shifts the value of this right by the number of bits given in value
|
202 |
+
* @param SassNumber amount to shift right by
|
203 |
+
* @return string result
|
204 |
+
* @throws Exception if bitwise Shift Right not supported for the data type
|
205 |
+
*/
|
206 |
+
public function op_shiftr($other) {
|
207 |
+
throw new SassLiteralException(get_class($this) . ' does not support Bitwise Shift Right', SassScriptParser::$context->node);
|
208 |
+
}
|
209 |
+
|
210 |
+
/**
|
211 |
+
* The SassScript and operation.
|
212 |
+
* @param sassLiteral the value to and with this
|
213 |
+
* @return SassLiteral other if this is boolean true, this if false
|
214 |
+
*/
|
215 |
+
public function op_and($other) {
|
216 |
+
return ($this->toBoolean() ? $other : $this);
|
217 |
+
}
|
218 |
+
|
219 |
+
/**
|
220 |
+
* The SassScript or operation.
|
221 |
+
* @param sassLiteral the value to or with this
|
222 |
+
* @return SassLiteral this if this is boolean true, other if false
|
223 |
+
*/
|
224 |
+
public function op_or($other) {
|
225 |
+
return ($this->toBoolean() ? $this : $other);
|
226 |
+
}
|
227 |
+
|
228 |
+
public function op_assign($other) {
|
229 |
+
return $other;
|
230 |
+
}
|
231 |
+
|
232 |
+
/**
|
233 |
+
* The SassScript xor operation.
|
234 |
+
* @param sassLiteral the value to xor with this
|
235 |
+
* @return SassBoolean SassBoolean object with the value true if this or
|
236 |
+
* other, but not both, are true, false if not
|
237 |
+
*/
|
238 |
+
public function op_xor($other) {
|
239 |
+
return new SassBoolean($this->toBoolean() xor $other->toBoolean());
|
240 |
+
}
|
241 |
+
|
242 |
+
/**
|
243 |
+
* The SassScript not operation.
|
244 |
+
* @return SassBoolean SassBoolean object with the value true if the
|
245 |
+
* boolean of this is false or false if it is true
|
246 |
+
*/
|
247 |
+
public function op_not() {
|
248 |
+
return new SassBoolean(!$this->toBoolean());
|
249 |
+
}
|
250 |
+
|
251 |
+
/**
|
252 |
+
* The SassScript > operation.
|
253 |
+
* @param sassLiteral the value to compare to this
|
254 |
+
* @return SassBoolean SassBoolean object with the value true if the values
|
255 |
+
* of this is greater than the value of other, false if it is not
|
256 |
+
*/
|
257 |
+
public function op_gt($other) {
|
258 |
+
return new SassBoolean($this->value > $other->value);
|
259 |
+
}
|
260 |
+
|
261 |
+
/**
|
262 |
+
* The SassScript >= operation.
|
263 |
+
* @param sassLiteral the value to compare to this
|
264 |
+
* @return SassBoolean SassBoolean object with the value true if the values
|
265 |
+
* of this is greater than or equal to the value of other, false if it is not
|
266 |
+
*/
|
267 |
+
public function op_gte($other) {
|
268 |
+
return new SassBoolean($this->value >= $other->value);
|
269 |
+
}
|
270 |
+
|
271 |
+
/**
|
272 |
+
* The SassScript < operation.
|
273 |
+
* @param sassLiteral the value to compare to this
|
274 |
+
* @return SassBoolean SassBoolean object with the value true if the values
|
275 |
+
* of this is less than the value of other, false if it is not
|
276 |
+
*/
|
277 |
+
public function op_lt($other) {
|
278 |
+
return new SassBoolean($this->value < $other->value);
|
279 |
+
}
|
280 |
+
|
281 |
+
/**
|
282 |
+
* The SassScript <= operation.
|
283 |
+
* @param sassLiteral the value to compare to this
|
284 |
+
* @return SassBoolean SassBoolean object with the value true if the values
|
285 |
+
* of this is less than or equal to the value of other, false if it is not
|
286 |
+
*/
|
287 |
+
public function op_lte($other) {
|
288 |
+
return new SassBoolean($this->value <= $other->value);
|
289 |
+
}
|
290 |
+
|
291 |
+
/**
|
292 |
+
* The SassScript == operation.
|
293 |
+
* @param sassLiteral the value to compare to this
|
294 |
+
* @return SassBoolean SassBoolean object with the value true if this and
|
295 |
+
* other are equal, false if they are not
|
296 |
+
*/
|
297 |
+
public function op_eq($other) {
|
298 |
+
return new SassBoolean($this == $other);
|
299 |
+
}
|
300 |
+
|
301 |
+
/**
|
302 |
+
* The SassScript != operation.
|
303 |
+
* @param sassLiteral the value to compare to this
|
304 |
+
* @return SassBoolean SassBoolean object with the value true if this and
|
305 |
+
* other are not equal, false if they are
|
306 |
+
*/
|
307 |
+
public function op_neq($other) {
|
308 |
+
return new SassBoolean(!$this->op_eq($other)->toBoolean());
|
309 |
+
}
|
310 |
+
|
311 |
+
/**
|
312 |
+
* The SassScript default operation (e.g. $a $b, "foo" "bar").
|
313 |
+
* @param sassLiteral the value to concatenate with a space to this
|
314 |
+
* @return sassString the string values of this and other seperated by " "
|
315 |
+
*/
|
316 |
+
public function op_concat($other) {
|
317 |
+
return new SassString($this->toString().' '.$other->toString());
|
318 |
+
}
|
319 |
+
|
320 |
+
/**
|
321 |
+
* SassScript ',' operation.
|
322 |
+
* @param sassLiteral the value to concatenate with a comma to this
|
323 |
+
* @return sassString the string values of this and other seperated by ","
|
324 |
+
*/
|
325 |
+
public function op_comma($other) {
|
326 |
+
return new SassString($this->toString().', '.$other->toString());
|
327 |
+
}
|
328 |
+
|
329 |
+
/**
|
330 |
+
* Asserts that the literal is the expected type
|
331 |
+
* @param SassLiteral the literal to test
|
332 |
+
* @param string expected type
|
333 |
+
* @throws SassScriptFunctionException if value is not the expected type
|
334 |
+
*/
|
335 |
+
public static function assertType($literal, $type) {
|
336 |
+
if (!$literal instanceof $type) {
|
337 |
+
throw new SassScriptFunctionException(($literal instanceof SassLiteral ? get_class($literal) : 'literal') . ' must be a ' . $type, SassScriptParser::$context->node);
|
338 |
+
}
|
339 |
+
}
|
340 |
+
|
341 |
+
/**
|
342 |
+
* Asserts that the value of a literal is within the expected range
|
343 |
+
* @param SassLiteral the literal to test
|
344 |
+
* @param float the minimum value
|
345 |
+
* @param float the maximum value
|
346 |
+
* @param string the units.
|
347 |
+
* @throws SassScriptFunctionException if value is not the expected type
|
348 |
+
*/
|
349 |
+
public static function assertInRange($literal, $min, $max, $units = '') {
|
350 |
+
if ($literal->value < $min || $literal->value > $max) {
|
351 |
+
throw new SassScriptFunctionException($literal->typeOf . ' must be between ' . $min.$units . ' and ' . $max.$units . ' inclusive', SassScriptParser::$context->node);
|
352 |
+
}
|
353 |
+
}
|
354 |
+
|
355 |
+
/**
|
356 |
+
* Returns a string representation of the value.
|
357 |
+
* @return string string representation of the value.
|
358 |
+
*/
|
359 |
+
abstract public function toString();
|
360 |
+
|
361 |
+
public function render() {
|
362 |
+
return $this->toString();
|
363 |
+
}
|
364 |
+
|
365 |
+
/**
|
366 |
+
* Returns a value indicating if a token of this type can be matched at
|
367 |
+
* the start of the subject string.
|
368 |
+
* @param string the subject string
|
369 |
+
* @return mixed match at the start of the string or false if no match
|
370 |
+
*/
|
371 |
+
public static function isa($subject){
|
372 |
+
throw new SassLiteralException('Child classes must override this method');
|
373 |
+
}
|
374 |
+
}
|
lib/phpsass/script/literals/SassLiteralExceptions.php
ADDED
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* SVN FILE: $Id$ */
|
3 |
+
/**
|
4 |
+
* Sass literal exception classes.
|
5 |
+
* @author Chris Yates <chris.l.yates@gmail.com>
|
6 |
+
* @copyright Copyright (c) 2010 PBM Web Development
|
7 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
8 |
+
* @package PHamlP
|
9 |
+
* @subpackage Sass.script.literals
|
10 |
+
*/
|
11 |
+
|
12 |
+
require_once(dirname(__FILE__).'/../SassScriptParserExceptions.php');
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Sass literal exception.
|
16 |
+
* @package PHamlP
|
17 |
+
* @subpackage Sass.script.literals
|
18 |
+
*/
|
19 |
+
class SassLiteralException extends SassScriptParserException {}
|
20 |
+
|
21 |
+
/**
|
22 |
+
* SassBooleanException class.
|
23 |
+
* @package PHamlP
|
24 |
+
* @subpackage Sass.script.literals
|
25 |
+
*/
|
26 |
+
class SassBooleanException extends SassLiteralException {}
|
27 |
+
|
28 |
+
/**
|
29 |
+
* SassColourException class.
|
30 |
+
* @package PHamlP
|
31 |
+
* @subpackage Sass.script.literals
|
32 |
+
*/
|
33 |
+
class SassColourException extends SassLiteralException {}
|
34 |
+
|
35 |
+
/**
|
36 |
+
* SassNumberException class.
|
37 |
+
* @package PHamlP
|
38 |
+
* @subpackage Sass.script.literals
|
39 |
+
*/
|
40 |
+
class SassNumberException extends SassLiteralException {}
|
41 |
+
|
42 |
+
/**
|
43 |
+
* SassStringException class.
|
44 |
+
* @package PHamlP
|
45 |
+
* @subpackage Sass.script.literals
|
46 |
+
*/
|
47 |
+
class SassStringException extends SassLiteralException {}
|
lib/phpsass/script/literals/SassNumber.php
ADDED
@@ -0,0 +1,538 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* SVN FILE: $Id$ */
|
3 |
+
/**
|
4 |
+
* SassNumber class file.
|
5 |
+
* @author Chris Yates <chris.l.yates@gmail.com>
|
6 |
+
* @copyright Copyright (c) 2010 PBM Web Development
|
7 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
8 |
+
* @package PHamlP
|
9 |
+
* @subpackage Sass.script.literals
|
10 |
+
*/
|
11 |
+
|
12 |
+
require_once('SassLiteral.php');
|
13 |
+
|
14 |
+
/**
|
15 |
+
* SassNumber class.
|
16 |
+
* Provides operations and type testing for Sass numbers.
|
17 |
+
* Units are of the passed value are converted the those of the class value
|
18 |
+
* if it has units. e.g. 2cm + 20mm = 4cm while 2 + 20mm = 22mm.
|
19 |
+
* @package PHamlP
|
20 |
+
* @subpackage Sass.script.literals
|
21 |
+
*/
|
22 |
+
class SassNumber extends SassLiteral {
|
23 |
+
/**
|
24 |
+
* Regx for matching and extracting numbers
|
25 |
+
*/
|
26 |
+
const MATCH = '/^((?:-)?(?:\d*\.)?\d+)(([a-z%]+)(\s*[\*\/]\s*[a-z%]+)*)?/i';
|
27 |
+
// const MATCH = '/^(?!\d+px\/)((?:-)?(?:\d.)?\d+)(([a-z%]+)(\s[*\/]\s[a-z%]+))?/i';
|
28 |
+
const VALUE = 1;
|
29 |
+
const UNITS = 2;
|
30 |
+
/**
|
31 |
+
* The number of decimal digits to round to.
|
32 |
+
* If the units are pixels the result is always
|
33 |
+
* rounded down to the nearest integer.
|
34 |
+
*/
|
35 |
+
const PRECISION = 3;
|
36 |
+
|
37 |
+
/**
|
38 |
+
* @var array Conversion factors for units using inches as the base unit
|
39 |
+
* (only because pt and pc are expressed as fraction of an inch, so makes the
|
40 |
+
* numbers easy to understand).
|
41 |
+
* Conversions are based on the following
|
42 |
+
* in: inches — 1 inch = 2.54 centimeters
|
43 |
+
* cm: centimeters
|
44 |
+
* mm: millimeters
|
45 |
+
* pc: picas — 1 pica = 12 points
|
46 |
+
* pt: points — 1 point = 1/72nd of an inch
|
47 |
+
*/
|
48 |
+
static private $unitConversion = array(
|
49 |
+
'in' => 1,
|
50 |
+
'cm' => 2.54,
|
51 |
+
'mm' => 25.4,
|
52 |
+
'pc' => 6,
|
53 |
+
'pt' => 72,
|
54 |
+
'px' => 96
|
55 |
+
);
|
56 |
+
static private $validUnits = array(
|
57 |
+
'in', 'cm', 'mm', 'pc', 'pt', 'em', 'rem', 'ex', 'px', '%', 's', 'deg'
|
58 |
+
);
|
59 |
+
|
60 |
+
/**
|
61 |
+
* @var array numerator units of this number
|
62 |
+
*/
|
63 |
+
private $numeratorUnits = array();
|
64 |
+
|
65 |
+
/**
|
66 |
+
* @var array denominator units of this number
|
67 |
+
*/
|
68 |
+
private $denominatorUnits = array();
|
69 |
+
|
70 |
+
/**
|
71 |
+
* @var boolean whether this number is in an expression or a literal number
|
72 |
+
* Used to determine whether division should take place
|
73 |
+
*/
|
74 |
+
public $inExpression = true;
|
75 |
+
|
76 |
+
/**
|
77 |
+
* class constructor.
|
78 |
+
* Sets the value and units of the number.
|
79 |
+
* @param string number
|
80 |
+
* @return SassNumber
|
81 |
+
*/
|
82 |
+
public function __construct($value) {
|
83 |
+
preg_match(self::MATCH, $value, $matches);
|
84 |
+
|
85 |
+
$matches += array(null,null,'','');
|
86 |
+
|
87 |
+
$this->value = $matches[self::VALUE];
|
88 |
+
if (!empty($matches[self::UNITS])) {
|
89 |
+
$units = explode('/', $matches[self::UNITS]);
|
90 |
+
$numeratorUnits = $denominatorUnits = array();
|
91 |
+
|
92 |
+
foreach (explode('*', $units[0]) as $unit) {
|
93 |
+
$numeratorUnits[] = trim($unit);
|
94 |
+
}
|
95 |
+
if (isset($units[1])) {
|
96 |
+
foreach (explode('*', $units[1]) as $unit) {
|
97 |
+
$denominatorUnits[] = trim($unit);
|
98 |
+
}
|
99 |
+
}
|
100 |
+
$units = $this->removeCommonUnits($numeratorUnits, $denominatorUnits);
|
101 |
+
$this->numeratorUnits = $units[0];
|
102 |
+
$this->denominatorUnits = $units[1];
|
103 |
+
}
|
104 |
+
}
|
105 |
+
|
106 |
+
/**
|
107 |
+
* Adds the value of other to the value of this
|
108 |
+
* @param mixed SassNumber|SassColour: value to add
|
109 |
+
* @return mixed SassNumber if other is a SassNumber or
|
110 |
+
* SassColour if it is a SassColour
|
111 |
+
*/
|
112 |
+
public function op_plus($other) {
|
113 |
+
if ($other instanceof SassColour) {
|
114 |
+
return $other->op_plus($this);
|
115 |
+
}
|
116 |
+
else if ($other instanceOf SassString) {
|
117 |
+
$other = clone $other;
|
118 |
+
$other->value = $this->value . $other->value;
|
119 |
+
return $other;
|
120 |
+
}
|
121 |
+
elseif (!$other instanceof SassNumber) {
|
122 |
+
throw new SassNumberException('Number must be a number', SassScriptParser::$context->node);
|
123 |
+
}
|
124 |
+
else {
|
125 |
+
$other = $this->convert($other);
|
126 |
+
return new SassNumber(($this->value + $other->value).$this->units);
|
127 |
+
}
|
128 |
+
}
|
129 |
+
|
130 |
+
/**
|
131 |
+
* Unary + operator
|
132 |
+
* @return SassNumber the value of this number
|
133 |
+
*/
|
134 |
+
public function op_unary_plus() {
|
135 |
+
return $this;
|
136 |
+
}
|
137 |
+
|
138 |
+
/**
|
139 |
+
* Subtracts the value of other from this value
|
140 |
+
* @param mixed SassNumber|SassColour: value to subtract
|
141 |
+
* @return mixed SassNumber if other is a SassNumber or
|
142 |
+
* SassColour if it is a SassColour
|
143 |
+
*/
|
144 |
+
public function op_minus($other) {
|
145 |
+
if ($other instanceof SassColour) {
|
146 |
+
return $other->op_minus($this);
|
147 |
+
}
|
148 |
+
elseif (!$other instanceof SassNumber) {
|
149 |
+
throw new SassNumberException('Number must be a number', SassScriptParser::$context->node);
|
150 |
+
}
|
151 |
+
else {
|
152 |
+
$other = $this->convert($other);
|
153 |
+
return new SassNumber(($this->value - $other->value) . $this->units);
|
154 |
+
}
|
155 |
+
}
|
156 |
+
|
157 |
+
/**
|
158 |
+
* Unary - operator
|
159 |
+
* @return SassNumber the negative value of this number
|
160 |
+
*/
|
161 |
+
public function op_unary_minus() {
|
162 |
+
return new SassNumber(($this->value * -1) . $this->units);
|
163 |
+
}
|
164 |
+
|
165 |
+
public function op_unary_concat() {
|
166 |
+
return $this;
|
167 |
+
}
|
168 |
+
|
169 |
+
/**
|
170 |
+
* Multiplies this value by the value of other
|
171 |
+
* @param mixed SassNumber|SassColour: value to multiply by
|
172 |
+
* @return mixed SassNumber if other is a SassNumber or
|
173 |
+
* SassColour if it is a SassColour
|
174 |
+
*/
|
175 |
+
public function op_times($other) {
|
176 |
+
if ($other instanceof SassColour) {
|
177 |
+
return $other->op_times($this);
|
178 |
+
}
|
179 |
+
elseif (!$other instanceof SassNumber) {
|
180 |
+
throw new SassNumberException('Number must be a number', SassScriptParser::$context->node);
|
181 |
+
}
|
182 |
+
else {
|
183 |
+
return new SassNumber(($this->value * $other->value).$this->unitString(
|
184 |
+
array_merge($this->numeratorUnits, $other->numeratorUnits),
|
185 |
+
array_merge($this->denominatorUnits, $other->denominatorUnits)
|
186 |
+
));
|
187 |
+
}
|
188 |
+
}
|
189 |
+
|
190 |
+
/**
|
191 |
+
* Divides this value by the value of other
|
192 |
+
* @param mixed SassNumber|SassColour: value to divide by
|
193 |
+
* @return mixed SassNumber if other is a SassNumber or
|
194 |
+
* SassColour if it is a SassColour
|
195 |
+
*/
|
196 |
+
public function op_div($other) {
|
197 |
+
if ($other instanceof SassColour) {
|
198 |
+
return $other->op_div($this);
|
199 |
+
}
|
200 |
+
elseif (!$other instanceof SassNumber) {
|
201 |
+
throw new SassNumberException('Number must be a number', SassScriptParser::$context->node);
|
202 |
+
}
|
203 |
+
elseif ($this->inExpression || $other->inExpression) {
|
204 |
+
return new SassNumber(($this->value / $other->value).$this->unitString(
|
205 |
+
array_merge($this->numeratorUnits, $other->denominatorUnits),
|
206 |
+
array_merge($this->denominatorUnits, $other->numeratorUnits)
|
207 |
+
));
|
208 |
+
}
|
209 |
+
else {
|
210 |
+
return new SassNumber(($this->value / $other->value).$this->unitString(
|
211 |
+
array_merge($this->numeratorUnits, $other->denominatorUnits),
|
212 |
+
$this->denominatorUnits
|
213 |
+
));
|
214 |
+
}
|
215 |
+
}
|
216 |
+
|
217 |
+
/**
|
218 |
+
* The SassScript == operation.
|
219 |
+
* @return SassBoolean SassBoolean object with the value true if the values
|
220 |
+
* of this and other are equal, false if they are not
|
221 |
+
*/
|
222 |
+
public function op_eq($other) {
|
223 |
+
if (!$other instanceof SassNumber) {
|
224 |
+
return new SassBoolean(false);
|
225 |
+
}
|
226 |
+
try {
|
227 |
+
return new SassBoolean($this->value == $this->convert($other)->value);
|
228 |
+
}
|
229 |
+
catch (Exception $e) {
|
230 |
+
return new SassBoolean(false);
|
231 |
+
}
|
232 |
+
}
|
233 |
+
|
234 |
+
/**
|
235 |
+
* The SassScript > operation.
|
236 |
+
* @param sassLiteral the value to compare to this
|
237 |
+
* @return SassBoolean SassBoolean object with the value true if the values
|
238 |
+
* of this is greater than the value of other, false if it is not
|
239 |
+
*/
|
240 |
+
public function op_gt($other) {
|
241 |
+
if (!$other instanceof SassNumber) {
|
242 |
+
throw new SassNumberException('Number must be a number', SassScriptParser::$context->node);
|
243 |
+
}
|
244 |
+
return new SassBoolean($this->value > $this->convert($other)->value);
|
245 |
+
}
|
246 |
+
|
247 |
+
/**
|
248 |
+
* The SassScript >= operation.
|
249 |
+
* @param sassLiteral the value to compare to this
|
250 |
+
* @return SassBoolean SassBoolean object with the value true if the values
|
251 |
+
* of this is greater than or equal to the value of other, false if it is not
|
252 |
+
*/
|
253 |
+
public function op_gte($other) {
|
254 |
+
if (!$other instanceof SassNumber) {
|
255 |
+
throw new SassNumberException('Number must be a number', SassScriptParser::$context->node);
|
256 |
+
}
|
257 |
+
return new SassBoolean($this->value >= $this->convert($other)->value);
|
258 |
+
}
|
259 |
+
|
260 |
+
/**
|
261 |
+
* The SassScript < operation.
|
262 |
+
* @param sassLiteral the value to compare to this
|
263 |
+
* @return SassBoolean SassBoolean object with the value true if the values
|
264 |
+
* of this is less than the value of other, false if it is not
|
265 |
+
*/
|
266 |
+
public function op_lt($other) {
|
267 |
+
if (!$other instanceof SassNumber) {
|
268 |
+
throw new SassNumberException('Number must be a number', SassScriptParser::$context->node);
|
269 |
+
}
|
270 |
+
return new SassBoolean($this->value < $this->convert($other)->value);
|
271 |
+
}
|
272 |
+
|
273 |
+
/**
|
274 |
+
* The SassScript <= operation.
|
275 |
+
* @param sassLiteral the value to compare to this
|
276 |
+
* @return SassBoolean SassBoolean object with the value true if the values
|
277 |
+
* of this is less than or equal to the value of other, false if it is not
|
278 |
+
*/
|
279 |
+
public function op_lte($other) {
|
280 |
+
if (!$other instanceof SassNumber) {
|
281 |
+
throw new SassNumberException('Number must be a number', SassScriptParser::$context->node);
|
282 |
+
}
|
283 |
+
return new SassBoolean($this->value <= $this->convert($other)->value);
|
284 |
+
}
|
285 |
+
|
286 |
+
/**
|
287 |
+
* Takes the modulus (remainder) of this value divided by the value of other
|
288 |
+
* @param string value to divide by
|
289 |
+
* @return mixed SassNumber if other is a SassNumber or
|
290 |
+
* SassColour if it is a SassColour
|
291 |
+
*/
|
292 |
+
public function op_modulo($other) {
|
293 |
+
if (!$other instanceof SassNumber || !$other->isUnitless()) {
|
294 |
+
throw new SassNumberException('Number must be a unitless number', SassScriptParser::$context->node);
|
295 |
+
}
|
296 |
+
$this->value %= $this->convert($other)->value;
|
297 |
+
return $this;
|
298 |
+
}
|
299 |
+
|
300 |
+
/**
|
301 |
+
* Converts values and units.
|
302 |
+
* If this is a unitless numeber it will take the units of other; if not
|
303 |
+
* other is coerced to the units of this.
|
304 |
+
* @param SassNumber the other number
|
305 |
+
* @return SassNumber the other number with its value and units coerced if neccessary
|
306 |
+
* @throws SassNumberException if the units are incompatible
|
307 |
+
*/
|
308 |
+
private function convert($other) {
|
309 |
+
if ($this->isUnitless()) {
|
310 |
+
$this->numeratorUnits = $other->numeratorUnits;
|
311 |
+
$this->denominatorUnits = $other->denominatorUnits;
|
312 |
+
}
|
313 |
+
else {
|
314 |
+
$other = $other->coerce($this->numeratorUnits, $this->denominatorUnits);
|
315 |
+
}
|
316 |
+
return $other;
|
317 |
+
}
|
318 |
+
|
319 |
+
/**
|
320 |
+
* Returns the value of this number converted to other units.
|
321 |
+
* The conversion takes into account the relationship between e.g. mm and cm,
|
322 |
+
* as well as between e.g. in and cm.
|
323 |
+
*
|
324 |
+
* If this number is unitless, it will simply return itself with the given units.
|
325 |
+
* @param array $numeratorUnits
|
326 |
+
* @param array $denominatorUnits
|
327 |
+
* @return SassNumber
|
328 |
+
*/
|
329 |
+
public function coerce($numeratorUnits, $denominatorUnits) {
|
330 |
+
return new SassNumber(($this->isUnitless() ?
|
331 |
+
$this->value : $this->value *
|
332 |
+
$this->coercionFactor($this->numeratorUnits, $numeratorUnits) /
|
333 |
+
$this->coercionFactor($this->denominatorUnits, $denominatorUnits)
|
334 |
+
) . join(' * ', $numeratorUnits) . (!empty($denominatorUnits) ? ' / ' . join(' * ', $denominatorUnits) : ''));
|
335 |
+
}
|
336 |
+
|
337 |
+
/**
|
338 |
+
* Calculates the corecion factor to apply to the value
|
339 |
+
* @param array units being converted from
|
340 |
+
* @param array units being converted to
|
341 |
+
* @return float the coercion factor to apply
|
342 |
+
*/
|
343 |
+
private function coercionFactor($fromUnits, $toUnits) {
|
344 |
+
$units = $this->removeCommonUnits($fromUnits, $toUnits);
|
345 |
+
$fromUnits = $units[0];
|
346 |
+
$toUnits = $units[1];
|
347 |
+
|
348 |
+
while (count($fromUnits) > count($toUnits)) {
|
349 |
+
$toUnits[] = 'in';
|
350 |
+
}
|
351 |
+
|
352 |
+
if (sizeof($fromUnits) !== sizeof($toUnits) || !$this->areConvertable(array_merge($fromUnits, $toUnits))) {
|
353 |
+
throw new SassNumberException("Incompatible units: '" . join(' * ', $fromUnits) . "' and '" . join(' * ', $toUnits) . "'", SassScriptParser::$context->node);
|
354 |
+
}
|
355 |
+
|
356 |
+
$coercionFactor = 1;
|
357 |
+
foreach ($fromUnits as $i=>$from) {
|
358 |
+
if (array_key_exists($i, $toUnits) && array_key_exists($toUnits[$i], self::$unitConversion)) {
|
359 |
+
$coercionFactor *= self::$unitConversion[$toUnits[$i]] / self::$unitConversion[$from];
|
360 |
+
}
|
361 |
+
else {
|
362 |
+
throw new SassNumberException("Incompatible units: '" . join(' * ', $fromUnits) . "' and '" . join(' * ', $toUnits) . "'", SassScriptParser::$context->node);
|
363 |
+
}
|
364 |
+
}
|
365 |
+
return $coercionFactor;
|
366 |
+
}
|
367 |
+
|
368 |
+
/**
|
369 |
+
* Returns a value indicating if all the units are capable of being converted
|
370 |
+
* @param array units to test
|
371 |
+
* @return boolean true if all units can be converted, false if not
|
372 |
+
*/
|
373 |
+
private function areConvertable($units) {
|
374 |
+
$convertable = array_keys(self::$unitConversion);
|
375 |
+
foreach ($units as $unit) {
|
376 |
+
if (!in_array($unit, $convertable)) {
|
377 |
+
return false;
|
378 |
+
}
|
379 |
+
}
|
380 |
+
return true;
|
381 |
+
}
|
382 |
+
|
383 |
+
/**
|
384 |
+
* Removes common units from each set.
|
385 |
+
* We don't use array_diff because we want (for eaxmple) mm*mm/mm*cm to
|
386 |
+
* end up as mm/cm.
|
387 |
+
* @param array first set of units
|
388 |
+
* @param array second set of units
|
389 |
+
* @return array both sets of units with common units removed
|
390 |
+
*/
|
391 |
+
private function removeCommonUnits($u1, $u2) {
|
392 |
+
$_u1 = array();
|
393 |
+
while (!empty($u1)) {
|
394 |
+
$u = array_shift($u1);
|
395 |
+
$i = array_search($u, $u2);
|
396 |
+
if ($i !== false) {
|
397 |
+
unset($u2[$i]);
|
398 |
+
}
|
399 |
+
else {
|
400 |
+
$_u1[] = $u;
|
401 |
+
}
|
402 |
+
}
|
403 |
+
return (array($_u1, $u2));
|
404 |
+
}
|
405 |
+
|
406 |
+
/**
|
407 |
+
* Returns a value indicating if this number is unitless.
|
408 |
+
* @return boolean true if this number is unitless, false if not
|
409 |
+
*/
|
410 |
+
public function isUnitless() {
|
411 |
+
return empty($this->numeratorUnits) && empty($this->denominatorUnits);
|
412 |
+
}
|
413 |
+
|
414 |
+
/**
|
415 |
+
* Returns a value indicating if this number has units.
|
416 |
+
* @return boolean true if this number has, false if not
|
417 |
+
*/
|
418 |
+
public function hasUnits() {
|
419 |
+
return !$this->isUnitless();
|
420 |
+
}
|
421 |
+
|
422 |
+
/**
|
423 |
+
* Returns a value indicating if this number has units that can be represented
|
424 |
+
* in CSS.
|
425 |
+
* @return boolean true if this number has units that can be represented in
|
426 |
+
* CSS, false if not
|
427 |
+
*/
|
428 |
+
public function hasLegalUnits() {
|
429 |
+
return (empty($this->numeratorUnits) || count($this->numeratorUnits) === 1) && empty($this->denominatorUnits);
|
430 |
+
}
|
431 |
+
|
432 |
+
/**
|
433 |
+
* Returns a string representation of the units.
|
434 |
+
* @return string the units
|
435 |
+
*/
|
436 |
+
public function unitString($numeratorUnits, $denominatorUnits) {
|
437 |
+
foreach ($numeratorUnits as $i => $unit) {
|
438 |
+
if (!in_array($unit, self::$validUnits)) {
|
439 |
+
unset($numeratorUnits[$i]);
|
440 |
+
}
|
441 |
+
}
|
442 |
+
foreach ($denominatorUnits as $i => $unit) {
|
443 |
+
if (!in_array($unit, self::$validUnits)) {
|
444 |
+
unset($denominatorUnits[$i]);
|
445 |
+
}
|
446 |
+
}
|
447 |
+
return join(' * ', $numeratorUnits) . (!empty($denominatorUnits) ? ' / ' . join(' * ', $denominatorUnits) : '');
|
448 |
+
}
|
449 |
+
|
450 |
+
/**
|
451 |
+
* Returns the units of this number.
|
452 |
+
* @return string the units of this number
|
453 |
+
*/
|
454 |
+
public function getUnits() {
|
455 |
+
return $this->unitString($this->numeratorUnits, $this->denominatorUnits);
|
456 |
+
}
|
457 |
+
|
458 |
+
/**
|
459 |
+
* Returns the denominator units of this number.
|
460 |
+
* @return string the denominator units of this number
|
461 |
+
*/
|
462 |
+
public function getDenominatorUnits() {
|
463 |
+
return join(' * ', $this->denominatorUnits);
|
464 |
+
}
|
465 |
+
|
466 |
+
/**
|
467 |
+
* Returns the numerator units of this number.
|
468 |
+
* @return string the numerator units of this number
|
469 |
+
*/
|
470 |
+
public function getNumeratorUnits() {
|
471 |
+
return join(' * ', $this->numeratorUnits);
|
472 |
+
}
|
473 |
+
|
474 |
+
/**
|
475 |
+
* Returns a value indicating if this number can be compared to other.
|
476 |
+
* @return boolean true if this number can be compared to other, false if not
|
477 |
+
*/
|
478 |
+
public function isComparableTo($other) {
|
479 |
+
try {
|
480 |
+
$this->op_plus($other);
|
481 |
+
return true;
|
482 |
+
}
|
483 |
+
catch (Exception $e) {
|
484 |
+
return false;
|
485 |
+
}
|
486 |
+
}
|
487 |
+
|
488 |
+
/**
|
489 |
+
* Returns a value indicating if this number is an integer.
|
490 |
+
* @return boolean true if this number is an integer, false if not
|
491 |
+
*/
|
492 |
+
public function isInt() {
|
493 |
+
return $this->value % 1 === 0;
|
494 |
+
}
|
495 |
+
|
496 |
+
/**
|
497 |
+
* Returns the value of this number.
|
498 |
+
* @return float the value of this number.
|
499 |
+
*/
|
500 |
+
public function getValue() {
|
501 |
+
return $this->value;
|
502 |
+
}
|
503 |
+
|
504 |
+
/**
|
505 |
+
* Returns the integer value.
|
506 |
+
* @return integer the integer value.
|
507 |
+
* @throws SassNumberException if the number is not an integer
|
508 |
+
*/
|
509 |
+
public function toInt() {
|
510 |
+
if (!$this->isInt()) {
|
511 |
+
throw new SassNumberException('Not an integer: ' . $this->value, SassScriptParser::$context->node);
|
512 |
+
}
|
513 |
+
return intval($this->value);
|
514 |
+
}
|
515 |
+
|
516 |
+
/**
|
517 |
+
* Converts the number to a string with it's units if any.
|
518 |
+
* If the units are px the result is rounded down to the nearest integer,
|
519 |
+
* otherwise the result is rounded to the specified precision.
|
520 |
+
* @return string number as a string with it's units if any
|
521 |
+
*/
|
522 |
+
public function toString() {
|
523 |
+
if (!isset($this->units)) {
|
524 |
+
$this->units = $this->getUnits();
|
525 |
+
}
|
526 |
+
return ($this->units == 'px' ? floor($this->value) : str_replace(',','.',round($this->value, self::PRECISION))) . $this->units;
|
527 |
+
}
|
528 |
+
|
529 |
+
/**
|
530 |
+
* Returns a value indicating if a token of this type can be matched at
|
531 |
+
* the start of the subject string.
|
532 |
+
* @param string the subject string
|
533 |
+
* @return mixed match at the start of the string or false if no match
|
534 |
+
*/
|
535 |
+
public static function isa($subject) {
|
536 |
+
return (preg_match(self::MATCH, $subject, $matches) ? $matches[0] : false);
|
537 |
+
}
|
538 |
+
}
|
lib/phpsass/script/literals/SassString.php
ADDED
@@ -0,0 +1,134 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* SVN FILE: $Id$ */
|
3 |
+
/**
|
4 |
+
* SassString class file.
|
5 |
+
* @author Chris Yates <chris.l.yates@gmail.com>
|
6 |
+
* @copyright Copyright (c) 2010 PBM Web Development
|
7 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
8 |
+
* @package PHamlP
|
9 |
+
* @subpackage Sass.script.literals
|
10 |
+
*/
|
11 |
+
|
12 |
+
require_once('SassLiteral.php');
|
13 |
+
|
14 |
+
/**
|
15 |
+
* SassString class.
|
16 |
+
* Provides operations and type testing for Sass strings.
|
17 |
+
* @package PHamlP
|
18 |
+
* @subpackage Sass.script.literals
|
19 |
+
*/
|
20 |
+
class SassString extends SassLiteral {
|
21 |
+
const MATCH = '/^(((["\'])(.*?)(\3))|(-[a-zA-Z-]+[^\s]*?))/i';
|
22 |
+
const _MATCH = '/^(["\'])(.*?)(\1)?$/'; // Used to match strings such as "Times New Roman",serif
|
23 |
+
const VALUE = 2;
|
24 |
+
const QUOTE = 3;
|
25 |
+
|
26 |
+
/**
|
27 |
+
* @var string string quote type; double or single quotes, or unquoted.
|
28 |
+
*/
|
29 |
+
public $quote;
|
30 |
+
|
31 |
+
/**
|
32 |
+
* class constructor
|
33 |
+
* @param string string
|
34 |
+
* @return SassString
|
35 |
+
*/
|
36 |
+
public function __construct($value) {
|
37 |
+
preg_match(self::_MATCH, $value, $matches);
|
38 |
+
if ((isset($matches[self::QUOTE]))) {
|
39 |
+
$this->quote = $matches[self::QUOTE];
|
40 |
+
$this->value = $matches[self::VALUE];
|
41 |
+
}
|
42 |
+
else {
|
43 |
+
$this->quote = '';
|
44 |
+
$this->value = $value;
|
45 |
+
}
|
46 |
+
}
|
47 |
+
|
48 |
+
/**
|
49 |
+
* String addition.
|
50 |
+
* Concatenates this and other.
|
51 |
+
* The resulting string will be quoted in the same way as this.
|
52 |
+
* @param sassString string to add to this
|
53 |
+
* @return sassString the string result
|
54 |
+
*/
|
55 |
+
public function op_plus($other) {
|
56 |
+
$this->value .= $other->value;
|
57 |
+
return $this;
|
58 |
+
}
|
59 |
+
|
60 |
+
/**
|
61 |
+
* String multiplication.
|
62 |
+
* this is repeated other times
|
63 |
+
* @param sassNumber the number of times to repeat this
|
64 |
+
* @return sassString the string result
|
65 |
+
*/
|
66 |
+
public function op_times($other) {
|
67 |
+
if (!($other instanceof SassNumber) || !$other->isUnitless()) {
|
68 |
+
throw new SassStringException('Value must be a unitless number', SassScriptParser::$context->node);
|
69 |
+
}
|
70 |
+
$this->value = str_repeat($this->value, $other->value);
|
71 |
+
return $this;
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* Equals - works better
|
76 |
+
*/
|
77 |
+
public function op_eq($other) {
|
78 |
+
return new SassBoolean($this->value == $other->value || $this->toString() == $other->toString());
|
79 |
+
}
|
80 |
+
|
81 |
+
/**
|
82 |
+
* Evaluates the value as a boolean.
|
83 |
+
*/
|
84 |
+
public function toBoolean() {
|
85 |
+
$value = strtolower(trim($this->value, ' "\''));
|
86 |
+
if (!$value || in_array($value, array('false', 'null', '0'))) {
|
87 |
+
return FALSE;
|
88 |
+
}
|
89 |
+
return TRUE;
|
90 |
+
}
|
91 |
+
|
92 |
+
/**
|
93 |
+
* Returns the value of this string.
|
94 |
+
* @return string the string
|
95 |
+
*/
|
96 |
+
public function getValue() {
|
97 |
+
return $this->value;
|
98 |
+
}
|
99 |
+
|
100 |
+
/**
|
101 |
+
* Returns a string representation of the value.
|
102 |
+
* @return string string representation of the value.
|
103 |
+
*/
|
104 |
+
public function toString() {
|
105 |
+
if ($this->quote) {
|
106 |
+
$value = $this->quote . $this->value . $this->quote;
|
107 |
+
}
|
108 |
+
else {
|
109 |
+
$value = strlen(trim($this->value)) ? trim($this->value) : $this->value;
|
110 |
+
}
|
111 |
+
return $value;
|
112 |
+
}
|
113 |
+
|
114 |
+
public function toVar() {
|
115 |
+
return SassScriptParser::$context->getVariable($this->value);
|
116 |
+
}
|
117 |
+
|
118 |
+
public function getTypeOf() {
|
119 |
+
if (SassList::isa($this->toString())) {
|
120 |
+
return 'list';
|
121 |
+
}
|
122 |
+
return 'string';
|
123 |
+
}
|
124 |
+
|
125 |
+
/**
|
126 |
+
* Returns a value indicating if a token of this type can be matched at
|
127 |
+
* the start of the subject string.
|
128 |
+
* @param string the subject string
|
129 |
+
* @return mixed match at the start of the string or false if no match
|
130 |
+
*/
|
131 |
+
public static function isa($subject) {
|
132 |
+
return (preg_match(self::MATCH, $subject, $matches) ? $matches[0] : false);
|
133 |
+
}
|
134 |
+
}
|
lib/phpsass/test.css
ADDED
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
body {
|
2 |
+
margin: 41px 0 0;
|
3 |
+
font-family: Lato, Calibri, "Trebuchet MS", Verdana, sans-serif;
|
4 |
+
}
|
5 |
+
|
6 |
+
body::before {
|
7 |
+
content: 'PHPSass Test Suite';
|
8 |
+
position: fixed;
|
9 |
+
top: 0;
|
10 |
+
left: 0;
|
11 |
+
right: 0;
|
12 |
+
padding: 5px 10px;
|
13 |
+
background: #222;
|
14 |
+
color: #fff;
|
15 |
+
font-size: 18pt;
|
16 |
+
}
|
17 |
+
|
18 |
+
p {
|
19 |
+
font-size: 12pt;
|
20 |
+
padding: 5px 10px;
|
21 |
+
margin: 0 0 1px;
|
22 |
+
background: #eee;
|
23 |
+
border-left: 10px solid #eee;
|
24 |
+
}
|
25 |
+
|
26 |
+
.pass {
|
27 |
+
border-color: #0c0;
|
28 |
+
color: #888;
|
29 |
+
font-size: 8pt;
|
30 |
+
}
|
31 |
+
|
32 |
+
.fail {
|
33 |
+
border-color:#c00;
|
34 |
+
font-weight: bold;
|
35 |
+
}
|
36 |
+
|
37 |
+
.warn, .debug {
|
38 |
+
border-color: #27a;
|
39 |
+
}
|
lib/phpsass/test.php
ADDED
@@ -0,0 +1,152 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<html>
|
2 |
+
<head>
|
3 |
+
<title>PHamlP Test Suite</title>
|
4 |
+
<link rel="stylesheet" type="text/css" href="test.css">
|
5 |
+
</head>
|
6 |
+
<body>
|
7 |
+
<?php
|
8 |
+
|
9 |
+
/**
|
10 |
+
* This file is horrible and not Drupal at all. Forgive me, I did not have time to write concise code.
|
11 |
+
*/
|
12 |
+
|
13 |
+
/* Testing for Sassy.
|
14 |
+
* Looks in tests* and compiles any .sass/.scss files
|
15 |
+
* and compares to them to their twin .css files by
|
16 |
+
* filename.
|
17 |
+
*
|
18 |
+
* That is, if we have three files:
|
19 |
+
* test.scss
|
20 |
+
* test.sass
|
21 |
+
* test.css
|
22 |
+
*
|
23 |
+
* The tester will compile test.scss and test.sass seperately
|
24 |
+
* and compare their outputs both to each other and to test.css
|
25 |
+
*
|
26 |
+
* Testing is eased by stripping out all whitespace, which may
|
27 |
+
* introduce bugs of their own.
|
28 |
+
*/
|
29 |
+
include 'SassParser.php';
|
30 |
+
|
31 |
+
$test_dir = './tests';
|
32 |
+
|
33 |
+
$files = find_files($test_dir);
|
34 |
+
|
35 |
+
$i = 0;
|
36 |
+
|
37 |
+
foreach ($files['by_name'] as $name => $test) {
|
38 |
+
if (isset($_GET['name']) && $name != $_GET['name']) {
|
39 |
+
continue;
|
40 |
+
}
|
41 |
+
if (isset($_GET['skip']) && $name && preg_match('/(^|,)(' . preg_quote($name) . ')(,|$)/', $_GET['skip'])) {
|
42 |
+
continue;
|
43 |
+
}
|
44 |
+
if (count($test) > 1) {
|
45 |
+
$result = test_files($test, $test_dir);
|
46 |
+
|
47 |
+
if ($result === TRUE) {
|
48 |
+
print "\n\t<p class='pass'><em>PASS</em> $name</p>";
|
49 |
+
}
|
50 |
+
else {
|
51 |
+
print "\n\t<p class='fail'><em>FAIL</em> $name</p>";
|
52 |
+
print "<pre>$result</pre>";
|
53 |
+
}
|
54 |
+
flush();
|
55 |
+
|
56 |
+
if ($i++ == 100) {
|
57 |
+
die;
|
58 |
+
}
|
59 |
+
}
|
60 |
+
}
|
61 |
+
|
62 |
+
function test_files($files, $dir = '.') {
|
63 |
+
sort($files);
|
64 |
+
foreach ($files as $i => $file) {
|
65 |
+
$name = explode('.', $file);
|
66 |
+
$ext = array_pop($name);
|
67 |
+
|
68 |
+
$fn = 'parse_' . $ext;
|
69 |
+
if (function_exists($fn)) {
|
70 |
+
try {
|
71 |
+
$result = $fn($dir . '/' . $file);
|
72 |
+
} catch (Exception $e) {
|
73 |
+
$result = $e->__toString();
|
74 |
+
}
|
75 |
+
file_put_contents('/tmp/scss_test_' . $i, trim($result) . "\n");
|
76 |
+
}
|
77 |
+
}
|
78 |
+
|
79 |
+
$diff = exec('diff -ibwB /tmp/scss_test_0 /tmp/scss_test_1', $out);
|
80 |
+
if (count($out)) {
|
81 |
+
if (isset($_GET['full'])) {
|
82 |
+
$out[] = "\n\n\n" . $result;
|
83 |
+
}
|
84 |
+
return implode("\n", $out);
|
85 |
+
} else {
|
86 |
+
return TRUE;
|
87 |
+
}
|
88 |
+
}
|
89 |
+
|
90 |
+
|
91 |
+
function parse_scss($file) {
|
92 |
+
return __parse($file, 'scss');
|
93 |
+
}
|
94 |
+
function parse_sass($file) {
|
95 |
+
return __parse($file, 'sass');
|
96 |
+
}
|
97 |
+
function parse_css($file) {
|
98 |
+
return file_get_contents($file);
|
99 |
+
}
|
100 |
+
|
101 |
+
function __parse($file, $syntax, $style = 'nested') {
|
102 |
+
$options = array(
|
103 |
+
'style' => $style,
|
104 |
+
'cache' => FALSE,
|
105 |
+
'syntax' => $syntax,
|
106 |
+
'debug' => FALSE,
|
107 |
+
'callbacks' => array(
|
108 |
+
'warn' => 'cb_warn',
|
109 |
+
'debug' => 'cb_debug',
|
110 |
+
),
|
111 |
+
);
|
112 |
+
// Execute the compiler.
|
113 |
+
$parser = new SassParser($options);
|
114 |
+
return $parser->toCss($file);
|
115 |
+
}
|
116 |
+
|
117 |
+
function cb_warn($message, $context) {
|
118 |
+
print "<p class='warn'>WARN : ";
|
119 |
+
print_r($message);
|
120 |
+
print "</p>";
|
121 |
+
}
|
122 |
+
function cb_debug($message) {
|
123 |
+
print "<p class='debug'>DEBUG : ";
|
124 |
+
print_r($message);
|
125 |
+
print "</p>";
|
126 |
+
}
|
127 |
+
|
128 |
+
function find_files($dir) {
|
129 |
+
$op = opendir($dir);
|
130 |
+
$return = array('by_type' => array(), 'by_name' => array());
|
131 |
+
if ($op) {
|
132 |
+
while (false !== ($file = readdir($op))) {
|
133 |
+
if (substr($file, 0, 1) == '.') {
|
134 |
+
continue;
|
135 |
+
}
|
136 |
+
$name = explode('.', $file);
|
137 |
+
$ext = array_pop($name);
|
138 |
+
$return['by_type'][$ext] = $file;
|
139 |
+
$name = implode('.', $name);
|
140 |
+
if (!isset($return['by_name'][$name])) {
|
141 |
+
$return['by_name'][$name] = array();
|
142 |
+
}
|
143 |
+
$return['by_name'][$name][] = $name . '.' . $ext;
|
144 |
+
}
|
145 |
+
}
|
146 |
+
asort($return['by_name']);
|
147 |
+
asort($return['by_type']);
|
148 |
+
return $return;
|
149 |
+
}
|
150 |
+
?>
|
151 |
+
</body>
|
152 |
+
</html>
|
lib/phpsass/tests/_imported_charset_ibm866.sass
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
1 |
+
@charset "IBM866"
|
2 |
+
|
3 |
+
.bar
|
4 |
+
a: �
|
lib/phpsass/tests/_imported_charset_utf8.sass
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
1 |
+
@charset "UTF-8"
|
2 |
+
|
3 |
+
.bar
|
4 |
+
a: щ
|
lib/phpsass/tests/_imported_content.sass
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
1 |
+
@mixin foo
|
2 |
+
a
|
3 |
+
@content
|
lib/phpsass/tests/_partial.sass
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
1 |
+
#foo
|
2 |
+
:background-color #baf
|
lib/phpsass/tests/alt.css
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
h1 {
|
2 |
+
float: left;
|
3 |
+
width: 274px;
|
4 |
+
height: 75px;
|
5 |
+
margin: 0;
|
6 |
+
background-repeat: no-repeat;
|
7 |
+
background-image: none; }
|
8 |
+
h1 a:hover,
|
9 |
+
h1 a:visited {
|
10 |
+
color: green; }
|
11 |
+
h1 b:hover {
|
12 |
+
color: red;
|
13 |
+
background-color: green; }
|
14 |
+
h1 const {
|
15 |
+
nosp: 3;
|
16 |
+
sp: 3; }
|
lib/phpsass/tests/alt.sass
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
h1
|
2 |
+
float: left
|
3 |
+
width: 274px
|
4 |
+
height: 75px
|
5 |
+
margin: 0
|
6 |
+
background:
|
7 |
+
repeat: no-repeat
|
8 |
+
image: none
|
9 |
+
a:hover, a:visited
|
10 |
+
color: green
|
11 |
+
b:hover
|
12 |
+
color: red
|
13 |
+
background-color: green
|
14 |
+
const
|
15 |
+
nosp: 6 / 2
|
16 |
+
sp : 1 + 2
|
lib/phpsass/tests/alt.scss
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
h1 {
|
2 |
+
float: left;
|
3 |
+
width: 274px;
|
4 |
+
height: 75px;
|
5 |
+
margin: 0;
|
6 |
+
background: {
|
7 |
+
repeat: no-repeat;
|
8 |
+
image: none;
|
9 |
+
}
|
10 |
+
a:hover, a:visited {
|
11 |
+
color: green;
|
12 |
+
}
|
13 |
+
b:hover {
|
14 |
+
color: red;
|
15 |
+
background-color: green;
|
16 |
+
}
|
17 |
+
const {
|
18 |
+
nosp: 6 / 2;
|
19 |
+
sp : 1 + 2;
|
20 |
+
}
|
21 |
+
}
|
lib/phpsass/tests/basic.css
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
body {
|
2 |
+
font: Arial;
|
3 |
+
background: blue; }
|
4 |
+
|
5 |
+
#page {
|
6 |
+
width: 700px;
|
7 |
+
height: 100;
|
8 |
+
line-height: 100%; }
|
9 |
+
#page #header {
|
10 |
+
height: 300px; }
|
11 |
+
#page #header h1 {
|
12 |
+
font-size: 50px;
|
13 |
+
color: blue; }
|
14 |
+
|
15 |
+
#content.user.show #container.top #column.left {
|
16 |
+
width: 100px; }
|
17 |
+
#content.user.show #container.top #column.right {
|
18 |
+
width: 600px; }
|
19 |
+
#content.user.show #container.bottom {
|
20 |
+
background: brown; }
|
21 |
+
|
lib/phpsass/tests/basic.sass
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
body
|
2 |
+
font: Arial
|
3 |
+
background: blue
|
4 |
+
|
5 |
+
#page
|
6 |
+
:width 700px
|
7 |
+
:height 100
|
8 |
+
:line-height 100%
|
9 |
+
#header
|
10 |
+
:height 300px
|
11 |
+
h1
|
12 |
+
:font-size 50px
|
13 |
+
:color blue
|
14 |
+
|
15 |
+
#content.user.show
|
16 |
+
#container.top
|
17 |
+
#column.left
|
18 |
+
:width 100px
|
19 |
+
#column.right
|
20 |
+
:width 600px
|
21 |
+
#container.bottom
|
22 |
+
:background brown
|
lib/phpsass/tests/bork1.sass
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
1 |
+
bork
|
2 |
+
:bork $bork
|
lib/phpsass/tests/bork2.sass
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
1 |
+
bork
|
2 |
+
:bork: bork;
|
lib/phpsass/tests/bork3.sass
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
1 |
+
bork
|
2 |
+
bork:
|
lib/phpsass/tests/bork4.sass
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
1 |
+
|
2 |
+
bork: blah
|
lib/phpsass/tests/bork5.sass
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
1 |
+
foo
|
2 |
+
@function bar($a)
|
3 |
+
@return $a
|
lib/phpsass/tests/comments.css
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
body {
|
2 |
+
font-family: sans-serif;
|
3 |
+
color: #111111;
|
4 |
+
font-size: 75%;
|
5 |
+
line-height: 1.2;
|
6 |
+
padding: 0;
|
7 |
+
margin: 0; }
|
8 |
+
|
9 |
+
.invisible,
|
10 |
+
.element-invisible {
|
11 |
+
border: 0;
|
12 |
+
clip: rect(1px 1px 1px 1px);
|
13 |
+
clip: rect(1px, 1px, 1px, 1px);
|
14 |
+
height: 1px;
|
15 |
+
margin: -1px;
|
16 |
+
overflow: hidden;
|
17 |
+
padding: 0;
|
18 |
+
position: absolute !important;
|
19 |
+
width: 1px; }
|
20 |
+
|
lib/phpsass/tests/comments.sass
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
body
|
2 |
+
font-family: sans-serif
|
3 |
+
color: #111
|
4 |
+
font-size: 75% // 12px
|
5 |
+
line-height: 1.2
|
6 |
+
padding: 0
|
7 |
+
// Addresses margins handled incorrectly in IE6/7.
|
8 |
+
margin: 0
|
9 |
+
|
10 |
+
.invisible,
|
11 |
+
.element-invisible
|
12 |
+
border: 0
|
13 |
+
clip: rect(1px 1px 1px 1px) // IE6, IE7
|
14 |
+
clip: rect(1px, 1px, 1px, 1px)
|
15 |
+
height: 1px
|
16 |
+
margin: -1px
|
17 |
+
overflow: hidden
|
18 |
+
padding: 0
|
19 |
+
position: absolute !important
|
20 |
+
width: 1px
|
lib/phpsass/tests/compact.css
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#main {
|
2 |
+
width: 15em;
|
3 |
+
color: blue; }
|
4 |
+
#main p {
|
5 |
+
border-style: dotted;
|
6 |
+
border-width: 2px; }
|
7 |
+
#main .cool {
|
8 |
+
width: 100px; }
|
9 |
+
|
10 |
+
#left {
|
11 |
+
font-size: 2em;
|
12 |
+
font-weight: bold;
|
13 |
+
float: left; }
|
lib/phpsass/tests/compact.sass
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#main
|
2 |
+
:width 15em
|
3 |
+
:color #0000ff
|
4 |
+
p
|
5 |
+
:border
|
6 |
+
:style dotted
|
7 |
+
:width 2px
|
8 |
+
.cool
|
9 |
+
:width 100px
|
10 |
+
|
11 |
+
#left
|
12 |
+
:font
|
13 |
+
:size 2em
|
14 |
+
:weight bold
|
15 |
+
:float left
|
lib/phpsass/tests/complex.css
ADDED
@@ -0,0 +1,290 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
body {
|
2 |
+
margin: 0;
|
3 |
+
font: 0.85em "Lucida Grande", "Trebuchet MS", Verdana, sans-serif;
|
4 |
+
color: white;
|
5 |
+
background: url(/images/global_bg.gif); }
|
6 |
+
|
7 |
+
#page {
|
8 |
+
width: 900px;
|
9 |
+
margin: 0 auto;
|
10 |
+
background: #440008;
|
11 |
+
border-top-width: 5px;
|
12 |
+
border-top-style: solid;
|
13 |
+
border-top-color: #ff8500; }
|
14 |
+
|
15 |
+
#header {
|
16 |
+
height: 75px;
|
17 |
+
padding: 0; }
|
18 |
+
#header h1 {
|
19 |
+
float: left;
|
20 |
+
width: 274px;
|
21 |
+
height: 75px;
|
22 |
+
margin: 0;
|
23 |
+
background-image: url(/images/global_logo.gif);
|
24 |
+
/*
|
25 |
+
* Crazy nested comment
|
26 |
+
*/
|
27 |
+
background-repeat: no-repeat;
|
28 |
+
text-indent: -9999px; }
|
29 |
+
#header .status {
|
30 |
+
float: right;
|
31 |
+
padding-top: 0.5em;
|
32 |
+
padding-left: 0.5em;
|
33 |
+
padding-right: 0.5em;
|
34 |
+
padding-bottom: 0; }
|
35 |
+
#header .status p {
|
36 |
+
float: left;
|
37 |
+
margin-top: 0;
|
38 |
+
margin-right: 0.5em;
|
39 |
+
margin-bottom: 0;
|
40 |
+
margin-left: 0; }
|
41 |
+
#header .status ul {
|
42 |
+
float: left;
|
43 |
+
margin: 0;
|
44 |
+
padding: 0; }
|
45 |
+
#header .status li {
|
46 |
+
list-style-type: none;
|
47 |
+
display: inline;
|
48 |
+
margin: 0 5px; }
|
49 |
+
#header .status a:link,
|
50 |
+
#header .status a:visited {
|
51 |
+
color: #ff8500;
|
52 |
+
text-decoration: none; }
|
53 |
+
#header .status a:hover {
|
54 |
+
text-decoration: underline; }
|
55 |
+
#header .search {
|
56 |
+
float: right;
|
57 |
+
clear: right;
|
58 |
+
margin: 12px 0 0 0; }
|
59 |
+
#header .search form {
|
60 |
+
margin: 0; }
|
61 |
+
#header .search input {
|
62 |
+
margin: 0 3px 0 0;
|
63 |
+
padding: 2px;
|
64 |
+
border: none; }
|
65 |
+
|
66 |
+
#menu {
|
67 |
+
clear: both;
|
68 |
+
text-align: right;
|
69 |
+
height: 20px;
|
70 |
+
border-bottom: 5px solid #006b95;
|
71 |
+
background: #00a4e4; }
|
72 |
+
#menu .contests ul {
|
73 |
+
margin: 0 5px 0 0;
|
74 |
+
padding: 0; }
|
75 |
+
#menu .contests ul li {
|
76 |
+
list-style-type: none;
|
77 |
+
margin: 0 5px;
|
78 |
+
padding: 5px 5px 0 5px;
|
79 |
+
display: inline;
|
80 |
+
font-size: 1.1em;
|
81 |
+
color: white;
|
82 |
+
background: #00a4e4; }
|
83 |
+
#menu .contests a:link,
|
84 |
+
#menu .contests a:visited {
|
85 |
+
color: white;
|
86 |
+
text-decoration: none;
|
87 |
+
font-weight: bold; }
|
88 |
+
#menu .contests a:hover {
|
89 |
+
text-decoration: underline; }
|
90 |
+
|
91 |
+
#content {
|
92 |
+
clear: both; }
|
93 |
+
#content .container {
|
94 |
+
clear: both; }
|
95 |
+
#content .container .column {
|
96 |
+
float: left; }
|
97 |
+
#content .container .column .right {
|
98 |
+
float: right; }
|
99 |
+
#content a:link,
|
100 |
+
#content a:visited {
|
101 |
+
color: #93d700;
|
102 |
+
text-decoration: none; }
|
103 |
+
#content a:hover {
|
104 |
+
text-decoration: underline; }
|
105 |
+
|
106 |
+
#content p,
|
107 |
+
#content div {
|
108 |
+
width: 40em; }
|
109 |
+
#content p li,
|
110 |
+
#content p dt,
|
111 |
+
#content p dd,
|
112 |
+
#content div li,
|
113 |
+
#content div dt,
|
114 |
+
#content div dd {
|
115 |
+
color: #ddffdd;
|
116 |
+
background-color: #4792bb; }
|
117 |
+
#content .container.video .column.left {
|
118 |
+
width: 200px; }
|
119 |
+
#content .container.video .column.left .box {
|
120 |
+
margin-top: 10px; }
|
121 |
+
#content .container.video .column.left .box p {
|
122 |
+
margin: 0 1em auto 1em; }
|
123 |
+
#content .container.video .column.left .box.participants img {
|
124 |
+
float: left;
|
125 |
+
margin: 0 1em auto 1em;
|
126 |
+
border: 1px solid #6e000d;
|
127 |
+
border-style: solid; }
|
128 |
+
#content .container.video .column.left .box.participants h2 {
|
129 |
+
margin: 0 0 10px 0;
|
130 |
+
padding: 0.5em;
|
131 |
+
/*
|
132 |
+
* The background image is a gif!
|
133 |
+
*/
|
134 |
+
background: #6e000d url(/images/hdr_participant.gif) 2px 2px no-repeat;
|
135 |
+
/*
|
136 |
+
* Okay check this out
|
137 |
+
* Multiline comments
|
138 |
+
* Wow dude
|
139 |
+
* I mean seriously, WOW
|
140 |
+
*/
|
141 |
+
text-indent: -9999px;
|
142 |
+
border-top-width: 5px;
|
143 |
+
border-top-style: solid;
|
144 |
+
border-top-color: #a20013;
|
145 |
+
border-right-width: 1px;
|
146 |
+
border-right-style: dotted; }
|
147 |
+
#content .container.video .column.middle {
|
148 |
+
width: 500px; }
|
149 |
+
#content .container.video .column.right {
|
150 |
+
width: 200px; }
|
151 |
+
#content .container.video .column.right .box {
|
152 |
+
margin-top: 0; }
|
153 |
+
#content .container.video .column.right .box p {
|
154 |
+
margin: 0 1em auto 1em; }
|
155 |
+
#content .container.video .column p {
|
156 |
+
margin-top: 0; }
|
157 |
+
|
158 |
+
#content.contests .container.information .column.right .box {
|
159 |
+
margin: 1em 0; }
|
160 |
+
#content.contests .container.information .column.right .box.videos .thumbnail img {
|
161 |
+
width: 200px;
|
162 |
+
height: 150px;
|
163 |
+
margin-bottom: 5px; }
|
164 |
+
#content.contests .container.information .column.right .box.videos a:link,
|
165 |
+
#content.contests .container.information .column.right .box.videos a:visited {
|
166 |
+
color: #93d700;
|
167 |
+
text-decoration: none; }
|
168 |
+
#content.contests .container.information .column.right .box.videos a:hover {
|
169 |
+
text-decoration: underline; }
|
170 |
+
#content.contests .container.information .column.right .box.votes a {
|
171 |
+
display: block;
|
172 |
+
width: 200px;
|
173 |
+
height: 60px;
|
174 |
+
margin: 15px 0;
|
175 |
+
background: url(/images/btn_votenow.gif) no-repeat;
|
176 |
+
text-indent: -9999px;
|
177 |
+
outline: none;
|
178 |
+
border: none; }
|
179 |
+
#content.contests .container.information .column.right .box.votes h2 {
|
180 |
+
margin: 52px 0 10px 0;
|
181 |
+
padding: 0.5em;
|
182 |
+
background: #6e000d url(/images/hdr_videostats.gif) 2px 2px no-repeat;
|
183 |
+
text-indent: -9999px;
|
184 |
+
border-top: 5px solid #a20013; }
|
185 |
+
|
186 |
+
#content.contests .container.video .box.videos h2 {
|
187 |
+
margin: 0;
|
188 |
+
padding: 0.5em;
|
189 |
+
background: #6e000d url(/images/hdr_newestclips.gif) 2px 2px no-repeat;
|
190 |
+
text-indent: -9999px;
|
191 |
+
border-top: 5px solid #a20013; }
|
192 |
+
#content.contests .container.video .box.videos table {
|
193 |
+
width: 100; }
|
194 |
+
#content.contests .container.video .box.videos table td {
|
195 |
+
padding: 1em;
|
196 |
+
width: 25;
|
197 |
+
vertical-align: top; }
|
198 |
+
#content.contests .container.video .box.videos table td p {
|
199 |
+
margin: 0 0 5px 0; }
|
200 |
+
#content.contests .container.video .box.videos table td a:link,
|
201 |
+
#content.contests .container.video .box.videos table td a:visited {
|
202 |
+
color: #93d700;
|
203 |
+
text-decoration: none; }
|
204 |
+
#content.contests .container.video .box.videos table td a:hover {
|
205 |
+
text-decoration: underline; }
|
206 |
+
#content.contests .container.video .box.videos .thumbnail {
|
207 |
+
float: left; }
|
208 |
+
#content.contests .container.video .box.videos .thumbnail img {
|
209 |
+
width: 80px;
|
210 |
+
height: 60px;
|
211 |
+
margin: 0 10px 0 0;
|
212 |
+
border: 1px solid #6e000d; }
|
213 |
+
|
214 |
+
#content .container.comments .column {
|
215 |
+
margin-top: 15px; }
|
216 |
+
#content .container.comments .column.left {
|
217 |
+
width: 600px; }
|
218 |
+
#content .container.comments .column.left .box ol {
|
219 |
+
margin: 0;
|
220 |
+
padding: 0; }
|
221 |
+
#content .container.comments .column.left .box li {
|
222 |
+
list-style-type: none;
|
223 |
+
padding: 10px;
|
224 |
+
margin: 0 0 1em 0;
|
225 |
+
background: #6e000d;
|
226 |
+
border-top: 5px solid #a20013; }
|
227 |
+
#content .container.comments .column.left .box li div {
|
228 |
+
margin-bottom: 1em; }
|
229 |
+
#content .container.comments .column.left .box li ul {
|
230 |
+
text-align: right; }
|
231 |
+
#content .container.comments .column.left .box li ul li {
|
232 |
+
display: inline;
|
233 |
+
border: none;
|
234 |
+
padding: 0; }
|
235 |
+
#content .container.comments .column.right {
|
236 |
+
width: 290px;
|
237 |
+
padding-left: 10px; }
|
238 |
+
#content .container.comments .column.right h2 {
|
239 |
+
margin: 0;
|
240 |
+
padding: 0.5em;
|
241 |
+
background: #6e000d url(/images/hdr_addcomment.gif) 2px 2px no-repeat;
|
242 |
+
text-indent: -9999px;
|
243 |
+
border-top: 5px solid #a20013; }
|
244 |
+
#content .container.comments .column.right .box textarea {
|
245 |
+
width: 290px;
|
246 |
+
height: 100px;
|
247 |
+
border: none; }
|
248 |
+
|
249 |
+
#footer {
|
250 |
+
margin-top: 10px;
|
251 |
+
padding: 1.2em 1.5em;
|
252 |
+
background: #ff8500; }
|
253 |
+
#footer ul {
|
254 |
+
margin: 0;
|
255 |
+
padding: 0;
|
256 |
+
list-style-type: none; }
|
257 |
+
#footer ul li {
|
258 |
+
display: inline;
|
259 |
+
margin: 0 0.5em;
|
260 |
+
color: #440008; }
|
261 |
+
#footer ul.links {
|
262 |
+
float: left; }
|
263 |
+
#footer ul.links a:link,
|
264 |
+
#footer ul.links a:visited {
|
265 |
+
color: #440008;
|
266 |
+
text-decoration: none; }
|
267 |
+
#footer ul.links a:hover {
|
268 |
+
text-decoration: underline; }
|
269 |
+
#footer ul.copyright {
|
270 |
+
float: right; }
|
271 |
+
|
272 |
+
.clear {
|
273 |
+
clear: both; }
|
274 |
+
|
275 |
+
.centered {
|
276 |
+
text-align: center; }
|
277 |
+
|
278 |
+
img {
|
279 |
+
border: none; }
|
280 |
+
|
281 |
+
button.short {
|
282 |
+
width: 60px;
|
283 |
+
height: 22px;
|
284 |
+
padding: 0 0 2px 0;
|
285 |
+
color: white;
|
286 |
+
border: none;
|
287 |
+
background: url(/images/btn_short.gif) no-repeat; }
|
288 |
+
|
289 |
+
table {
|
290 |
+
border-collapse: collapse; }
|
lib/phpsass/tests/complex.sass
ADDED
@@ -0,0 +1,303 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
body
|
2 |
+
:margin 0
|
3 |
+
:font 0.85em "Lucida Grande", "Trebuchet MS", Verdana, sans-serif
|
4 |
+
:color #fff
|
5 |
+
$path: /images/global_bg.gif
|
6 |
+
:background url($path)
|
7 |
+
|
8 |
+
#page
|
9 |
+
:width 900px
|
10 |
+
:margin 0 auto
|
11 |
+
:background #440008
|
12 |
+
:border-top
|
13 |
+
:width 5px
|
14 |
+
:style solid
|
15 |
+
:color #ff8500
|
16 |
+
|
17 |
+
#header
|
18 |
+
:height 75px
|
19 |
+
:padding 0
|
20 |
+
h1
|
21 |
+
:float left
|
22 |
+
:width 274px
|
23 |
+
:height 75px
|
24 |
+
:margin 0
|
25 |
+
:background
|
26 |
+
:image url(/images/global_logo.gif)
|
27 |
+
/* Crazy nested comment
|
28 |
+
:repeat no-repeat
|
29 |
+
:text-indent -9999px
|
30 |
+
.status
|
31 |
+
:float right
|
32 |
+
:padding
|
33 |
+
:top .5em
|
34 |
+
:left .5em
|
35 |
+
:right .5em
|
36 |
+
:bottom 0
|
37 |
+
p
|
38 |
+
:float left
|
39 |
+
:margin
|
40 |
+
:top 0
|
41 |
+
:right 0.5em
|
42 |
+
:bottom 0
|
43 |
+
:left 0
|
44 |
+
ul
|
45 |
+
:float left
|
46 |
+
:margin 0
|
47 |
+
:padding 0
|
48 |
+
li
|
49 |
+
:list-style-type none
|
50 |
+
:display inline
|
51 |
+
:margin 0 5px
|
52 |
+
a:link, a:visited
|
53 |
+
:color #ff8500
|
54 |
+
:text-decoration none
|
55 |
+
a:hover
|
56 |
+
:text-decoration underline
|
57 |
+
.search
|
58 |
+
:float right
|
59 |
+
:clear right
|
60 |
+
:margin 12px 0 0 0
|
61 |
+
form
|
62 |
+
:margin 0
|
63 |
+
input
|
64 |
+
:margin 0 3px 0 0
|
65 |
+
:padding 2px
|
66 |
+
:border none
|
67 |
+
|
68 |
+
#menu
|
69 |
+
:clear both
|
70 |
+
:text-align right
|
71 |
+
:height 20px
|
72 |
+
:border-bottom 5px solid #006b95
|
73 |
+
:background #00a4e4
|
74 |
+
.contests
|
75 |
+
ul
|
76 |
+
:margin 0 5px 0 0
|
77 |
+
:padding 0
|
78 |
+
li
|
79 |
+
:list-style-type none
|
80 |
+
:margin 0 5px
|
81 |
+
:padding 5px 5px 0 5px
|
82 |
+
:display inline
|
83 |
+
:font-size 1.1em
|
84 |
+
// This comment is properly indented
|
85 |
+
:color #fff
|
86 |
+
:background #00a4e4
|
87 |
+
a:link, a:visited
|
88 |
+
:color #fff
|
89 |
+
:text-decoration none
|
90 |
+
:font-weight bold
|
91 |
+
a:hover
|
92 |
+
:text-decoration underline
|
93 |
+
|
94 |
+
//General content information
|
95 |
+
#content
|
96 |
+
:clear both
|
97 |
+
.container
|
98 |
+
:clear both
|
99 |
+
.column
|
100 |
+
:float left
|
101 |
+
.right
|
102 |
+
:float right
|
103 |
+
a:link, a:visited
|
104 |
+
:color #93d700
|
105 |
+
:text-decoration none
|
106 |
+
a:hover
|
107 |
+
:text-decoration underline
|
108 |
+
|
109 |
+
#content
|
110 |
+
p, div
|
111 |
+
:width 40em
|
112 |
+
li, dt, dd
|
113 |
+
:color #ddffdd
|
114 |
+
:background-color #4792bb
|
115 |
+
.container.video
|
116 |
+
.column.left
|
117 |
+
:width 200px
|
118 |
+
.box
|
119 |
+
:margin-top 10px
|
120 |
+
p
|
121 |
+
:margin 0 1em auto 1em
|
122 |
+
.box.participants
|
123 |
+
img
|
124 |
+
:float left
|
125 |
+
:margin 0 1em auto 1em
|
126 |
+
:border 1px solid #6e000d
|
127 |
+
:style solid
|
128 |
+
h2
|
129 |
+
:margin 0 0 10px 0
|
130 |
+
:padding 0.5em
|
131 |
+
/* The background image is a gif!
|
132 |
+
:background #6e000d url(/images/hdr_participant.gif) 2px 2px no-repeat
|
133 |
+
/* Okay check this out
|
134 |
+
Multiline comments
|
135 |
+
Wow dude
|
136 |
+
I mean seriously, WOW
|
137 |
+
:text-indent -9999px
|
138 |
+
// And also...
|
139 |
+
Multiline comments that don't output!
|
140 |
+
Snazzy, no?
|
141 |
+
:border
|
142 |
+
:top
|
143 |
+
:width 5px
|
144 |
+
:style solid
|
145 |
+
:color #a20013
|
146 |
+
:right
|
147 |
+
:width 1px
|
148 |
+
:style dotted
|
149 |
+
.column.middle
|
150 |
+
:width 500px
|
151 |
+
.column.right
|
152 |
+
:width 200px
|
153 |
+
.box
|
154 |
+
:margin-top 0
|
155 |
+
p
|
156 |
+
:margin 0 1em auto 1em
|
157 |
+
.column
|
158 |
+
p
|
159 |
+
:margin-top 0
|
160 |
+
|
161 |
+
#content.contests
|
162 |
+
.container.information
|
163 |
+
.column.right
|
164 |
+
.box
|
165 |
+
:margin 1em 0
|
166 |
+
.box.videos
|
167 |
+
.thumbnail img
|
168 |
+
:width 200px
|
169 |
+
:height 150px
|
170 |
+
:margin-bottom 5px
|
171 |
+
a:link, a:visited
|
172 |
+
:color #93d700
|
173 |
+
:text-decoration none
|
174 |
+
a:hover
|
175 |
+
:text-decoration underline
|
176 |
+
.box.votes
|
177 |
+
a
|
178 |
+
:display block
|
179 |
+
:width 200px
|
180 |
+
:height 60px
|
181 |
+
:margin 15px 0
|
182 |
+
:background url(/images/btn_votenow.gif) no-repeat
|
183 |
+
:text-indent -9999px
|
184 |
+
:outline none
|
185 |
+
:border none
|
186 |
+
h2
|
187 |
+
:margin 52px 0 10px 0
|
188 |
+
:padding 0.5em
|
189 |
+
:background #6e000d url(/images/hdr_videostats.gif) 2px 2px no-repeat
|
190 |
+
:text-indent -9999px
|
191 |
+
:border-top 5px solid #a20013
|
192 |
+
|
193 |
+
#content.contests
|
194 |
+
.container.video
|
195 |
+
.box.videos
|
196 |
+
h2
|
197 |
+
:margin 0
|
198 |
+
:padding 0.5em
|
199 |
+
:background #6e000d url(/images/hdr_newestclips.gif) 2px 2px no-repeat
|
200 |
+
:text-indent -9999px
|
201 |
+
:border-top 5px solid #a20013
|
202 |
+
table
|
203 |
+
:width 100
|
204 |
+
td
|
205 |
+
:padding 1em
|
206 |
+
:width 25
|
207 |
+
:vertical-align top
|
208 |
+
p
|
209 |
+
:margin 0 0 5px 0
|
210 |
+
a:link, a:visited
|
211 |
+
:color #93d700
|
212 |
+
:text-decoration none
|
213 |
+
a:hover
|
214 |
+
:text-decoration underline
|
215 |
+
.thumbnail
|
216 |
+
:float left
|
217 |
+
img
|
218 |
+
:width 80px
|
219 |
+
:height 60px
|
220 |
+
:margin 0 10px 0 0
|
221 |
+
:border 1px solid #6e000d
|
222 |
+
|
223 |
+
#content
|
224 |
+
.container.comments
|
225 |
+
.column
|
226 |
+
:margin-top 15px
|
227 |
+
.column.left
|
228 |
+
:width 600px
|
229 |
+
.box
|
230 |
+
ol
|
231 |
+
:margin 0
|
232 |
+
:padding 0
|
233 |
+
li
|
234 |
+
:list-style-type none
|
235 |
+
:padding 10px
|
236 |
+
:margin 0 0 1em 0
|
237 |
+
:background #6e000d
|
238 |
+
:border-top 5px solid #a20013
|
239 |
+
div
|
240 |
+
:margin-bottom 1em
|
241 |
+
ul
|
242 |
+
:text-align right
|
243 |
+
li
|
244 |
+
:display inline
|
245 |
+
:border none
|
246 |
+
:padding 0
|
247 |
+
.column.right
|
248 |
+
:width 290px
|
249 |
+
:padding-left 10px
|
250 |
+
h2
|
251 |
+
:margin 0
|
252 |
+
:padding 0.5em
|
253 |
+
:background #6e000d url(/images/hdr_addcomment.gif) 2px 2px no-repeat
|
254 |
+
:text-indent -9999px
|
255 |
+
:border-top 5px solid #a20013
|
256 |
+
.box
|
257 |
+
textarea
|
258 |
+
:width 290px
|
259 |
+
:height 100px
|
260 |
+
:border none
|
261 |
+
|
262 |
+
#footer
|
263 |
+
:margin-top 10px
|
264 |
+
:padding 1.2em 1.5em
|
265 |
+
:background #ff8500
|
266 |
+
ul
|
267 |
+
:margin 0
|
268 |
+
:padding 0
|
269 |
+
:list-style-type none
|
270 |
+
li
|
271 |
+
:display inline
|
272 |
+
:margin 0 0.5em
|
273 |
+
:color #440008
|
274 |
+
ul.links
|
275 |
+
:float left
|
276 |
+
a:link, a:visited
|
277 |
+
:color #440008
|
278 |
+
:text-decoration none
|
279 |
+
a:hover
|
280 |
+
:text-decoration underline
|
281 |
+
ul.copyright
|
282 |
+
:float right
|
283 |
+
|
284 |
+
|
285 |
+
.clear
|
286 |
+
:clear both
|
287 |
+
|
288 |
+
.centered
|
289 |
+
:text-align center
|
290 |
+
|
291 |
+
img
|
292 |
+
:border none
|
293 |
+
|
294 |
+
button.short
|
295 |
+
:width 60px
|
296 |
+
:height 22px
|
297 |
+
:padding 0 0 2px 0
|
298 |
+
:color #fff
|
299 |
+
:border none
|
300 |
+
:background url(/images/btn_short.gif) no-repeat
|
301 |
+
|
302 |
+
table
|
303 |
+
:border-collapse collapse
|
lib/phpsass/tests/compressed.css
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#main {
|
2 |
+
width:15em;
|
3 |
+
color:blue; }
|
4 |
+
#main p {
|
5 |
+
border-style:dotted;
|
6 |
+
border-width:2px; }
|
7 |
+
#main .cool {
|
8 |
+
width:100px; }
|
9 |
+
#left {
|
10 |
+
font-size:2em;
|
11 |
+
font-weight:bold;
|
12 |
+
float:left; }
|
lib/phpsass/tests/compressed.sass
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#main
|
2 |
+
:width 15em
|
3 |
+
:color #0000ff
|
4 |
+
p
|
5 |
+
:border
|
6 |
+
:style dotted
|
7 |
+
:width 2px
|
8 |
+
.cool
|
9 |
+
:width 100px
|
10 |
+
|
11 |
+
#left
|
12 |
+
:font
|
13 |
+
:size 2em
|
14 |
+
:weight bold
|
15 |
+
:float left
|
lib/phpsass/tests/content.css
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
1 |
+
div:before {
|
2 |
+
content: "- "; }
|
lib/phpsass/tests/content.scss
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
1 |
+
div:before {
|
2 |
+
content: "- ";
|
3 |
+
}
|
lib/phpsass/tests/css3.css
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
h1 {
|
2 |
+
opacity: 0.3;
|
3 |
+
color: red;
|
4 |
+
-moz-transition: opacity 1s ease 1s;
|
5 |
+
-webkit-transition: opacity 1s ease 1s;
|
6 |
+
-o-transition: opacity 1s ease 1s;
|
7 |
+
transition: opacity 1s ease 1s; }
|
lib/phpsass/tests/css3.scss
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
h1 {
|
2 |
+
opacity: 0.3;
|
3 |
+
color:red;
|
4 |
+
-moz-transition: opacity 1s ease 1s;
|
5 |
+
-webkit-transition: opacity 1s ease 1s;
|
6 |
+
-o-transition: opacity 1s ease 1s;
|
7 |
+
transition: opacity 1s ease 1s;
|
8 |
+
}
|
lib/phpsass/tests/default.css
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
1 |
+
body {
|
2 |
+
background: blue;
|
3 |
+
color: blue;
|
4 |
+
border-color: blue; }
|
lib/phpsass/tests/default.sass
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
$var1: blue
|
2 |
+
$var1: red !default
|
3 |
+
|
4 |
+
$var2: blue
|
5 |
+
@import default_imported.sass
|
6 |
+
$var3: red !default
|
7 |
+
|
8 |
+
body
|
9 |
+
background: $var1
|
10 |
+
color: $var2
|
11 |
+
border-color: $var3
|
lib/phpsass/tests/default_imported.sass
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
1 |
+
$var2: red !default
|
2 |
+
$var3: blue
|
lib/phpsass/tests/each.css
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
a {
|
2 |
+
b: 1px;
|
3 |
+
b: 2px;
|
4 |
+
b: 3px;
|
5 |
+
b: 4px; }
|
6 |
+
|
7 |
+
c {
|
8 |
+
d: foo;
|
9 |
+
d: bar;
|
10 |
+
d: baz;
|
11 |
+
d: bang; }
|
12 |
+
|
13 |
+
e {
|
14 |
+
f: blue; }
|
lib/phpsass/tests/each.scss
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
a {
|
2 |
+
@each $number in 1px 2px 3px 4px {
|
3 |
+
b: $number;
|
4 |
+
}
|
5 |
+
}
|
6 |
+
|
7 |
+
c {
|
8 |
+
@each $str in foo, bar, baz, bang {
|
9 |
+
d: $str;
|
10 |
+
}
|
11 |
+
}
|
12 |
+
|
13 |
+
e {
|
14 |
+
@each $single in blue {
|
15 |
+
f: $single;
|
16 |
+
}
|
17 |
+
}
|
lib/phpsass/tests/expanded.css
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#main {
|
2 |
+
width: 15em;
|
3 |
+
color: blue; }
|
4 |
+
#main p {
|
5 |
+
border-style: dotted;
|
6 |
+
/*
|
7 |
+
* Nested comment
|
8 |
+
* More nested stuff
|
9 |
+
*/
|
10 |
+
border-width: 2px; }
|
11 |
+
#main .cool {
|
12 |
+
width: 100px; }
|
13 |
+
|
14 |
+
#left {
|
15 |
+
font-size: 2em;
|
16 |
+
font-weight: bold;
|
17 |
+
float: left; }
|
lib/phpsass/tests/expanded.sass
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#main
|
2 |
+
:width 15em
|
3 |
+
:color #0000ff
|
4 |
+
p
|
5 |
+
:border
|
6 |
+
:style dotted
|
7 |
+
/* Nested comment
|
8 |
+
More nested stuff
|
9 |
+
:width 2px
|
10 |
+
.cool
|
11 |
+
:width 100px
|
12 |
+
|
13 |
+
#left
|
14 |
+
:font
|
15 |
+
:size 2em
|
16 |
+
:weight bold
|
17 |
+
:float left
|
lib/phpsass/tests/extend.css
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.error,
|
2 |
+
.seriousError,
|
3 |
+
.hoverlink {
|
4 |
+
border: 1px red;
|
5 |
+
background-color: #ffdddd; }
|
6 |
+
|
7 |
+
.seriousError,
|
8 |
+
.hoverlink {
|
9 |
+
border-width: 3px; }
|
10 |
+
|
11 |
+
.comment a:hover,
|
12 |
+
.comment .hoverlink {
|
13 |
+
text-decoration: underline; }
|
lib/phpsass/tests/extend.sass
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
@import extend_included.scss;
|
2 |
+
|
3 |
+
.seriousError
|
4 |
+
@extend .error
|
5 |
+
border-width: 3px
|
6 |
+
|
7 |
+
|
8 |
+
.hoverlink
|
9 |
+
@extend a:hover
|
10 |
+
@extend .seriousError
|
11 |
+
|
12 |
+
.comment a:hover
|
13 |
+
text-decoration: underline
|
lib/phpsass/tests/extend_included.scss
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
1 |
+
.error {
|
2 |
+
border: 1px #f00;
|
3 |
+
background-color: #fdd;
|
4 |
+
}
|
lib/phpsass/tests/extend_placeholders.css
ADDED
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*
|
2 |
+
* test basic placeholder usage, with multiple extenders
|
3 |
+
*/
|
4 |
+
.bar,
|
5 |
+
.baz {
|
6 |
+
color: blue; }
|
7 |
+
|
8 |
+
/*
|
9 |
+
* test unused placeholder
|
10 |
+
*/
|
11 |
+
/*
|
12 |
+
* test placeholder descendant selector
|
13 |
+
*/
|
14 |
+
#context .bar a {
|
15 |
+
color: blue; }
|
16 |
+
|
17 |
+
/*
|
18 |
+
* test placeholder-descendant with multiple extenders
|
19 |
+
*/
|
20 |
+
.bar .baz {
|
21 |
+
color: blue; }
|
22 |
+
/*
|
23 |
+
* test placeholder selector as modifier
|
24 |
+
*/
|
25 |
+
a.bar.baz {
|
26 |
+
color: blue; }
|
27 |
+
|
28 |
+
/*
|
29 |
+
* the div should not be rendered as it results in adiv (invalid)
|
30 |
+
*/
|
31 |
+
|
32 |
+
/*
|
33 |
+
* test placeholder selector modifier again
|
34 |
+
*/
|
35 |
+
.hello.goodbye {
|
36 |
+
color: blue; }
|
37 |
+
|
38 |
+
/*
|
39 |
+
* test placeholder interpolation
|
40 |
+
*/
|
41 |
+
.inter {
|
42 |
+
color: blue; }
|
43 |
+
|
44 |
+
/*
|
45 |
+
* test media in a plaecholder
|
46 |
+
*/
|
47 |
+
.media {
|
48 |
+
c: d; }
|
lib/phpsass/tests/extend_placeholders.scss
ADDED
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/* test basic placeholder usage, with multiple extenders */
|
2 |
+
%foo { color: blue; }
|
3 |
+
.bar { @extend %foo; }
|
4 |
+
.baz { @extend %foo; }
|
5 |
+
|
6 |
+
/* test unused placeholder */
|
7 |
+
%bar { color: red; }
|
8 |
+
|
9 |
+
/* test placeholder descendant selector */
|
10 |
+
#context %context a {
|
11 |
+
color: blue;
|
12 |
+
}
|
13 |
+
.bar {
|
14 |
+
@extend %context;
|
15 |
+
}
|
16 |
+
|
17 |
+
/* test placeholder-descendant with multiple extenders */
|
18 |
+
#three %three, .bar .baz {
|
19 |
+
color: blue;
|
20 |
+
}
|
21 |
+
|
22 |
+
/* test placeholder selector as modifier */
|
23 |
+
a%four.baz {
|
24 |
+
color: blue;
|
25 |
+
}
|
26 |
+
.bar { @extend %four; }
|
27 |
+
/* the div should not be rendered as it results in adiv (invalid) */
|
28 |
+
div { @extend %four; }
|
29 |
+
|
30 |
+
/* test placeholder selector modifier again */
|
31 |
+
.hello%hello {
|
32 |
+
color: blue;
|
33 |
+
}
|
34 |
+
|
35 |
+
.goodbye {
|
36 |
+
@extend %hello;
|
37 |
+
}
|
38 |
+
|
39 |
+
/* test placeholder interpolation */
|
40 |
+
$in: 'inter';
|
41 |
+
%#{$in} {
|
42 |
+
color: blue;
|
43 |
+
}
|
44 |
+
.inter {
|
45 |
+
@extend %inter;
|
46 |
+
}
|
47 |
+
|
48 |
+
/* test media in a plaecholder */
|
49 |
+
%medias {
|
50 |
+
bar {
|
51 |
+
@media screen {
|
52 |
+
a: b;
|
53 |
+
}
|
54 |
+
}
|
55 |
+
}
|
56 |
+
.media {
|
57 |
+
c: d;
|
58 |
+
}
|
lib/phpsass/tests/filters.css
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
test {
|
2 |
+
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#feffff', endColorstr='#ccffffff',GradientType=0 );
|
3 |
+
foo: bar; }
|
4 |
+
|
5 |
+
other {
|
6 |
+
hello: goodbye; }
|
lib/phpsass/tests/filters.scss
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// https://github.com/richthegeek/phpsass/issues/31
|
2 |
+
|
3 |
+
test {
|
4 |
+
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#feffff', endColorstr='#ccffffff',GradientType=0 );
|
5 |
+
foo: bar;
|
6 |
+
}
|
7 |
+
|
8 |
+
other {
|
9 |
+
hello: goodbye;
|
10 |
+
}
|
lib/phpsass/tests/functions.css
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
foo,
|
2 |
+
bar,
|
3 |
+
foo + bar {
|
4 |
+
content: 'one two' true;
|
5 |
+
content: 'foo bar' true;
|
6 |
+
content: 'foo two' true;
|
7 |
+
content: 'one foo' true;
|
8 |
+
content: bar foo true;
|
9 |
+
adjust-color: #0000f0; }
|
lib/phpsass/tests/functions.scss
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
@function test() {
|
2 |
+
@return 'foo, bar, foo + bar';
|
3 |
+
}
|
4 |
+
|
5 |
+
@function x($one: 'one', $two: 'two') {
|
6 |
+
@return $one + ' ' + $two;
|
7 |
+
}
|
8 |
+
|
9 |
+
#{test()} {
|
10 |
+
content: x() if(x() == 'one two', true, false);
|
11 |
+
content: x('foo', 'bar') if(x('foo', 'bar') == 'foo bar', true, false);
|
12 |
+
content: x('foo') if(x('foo') == 'foo two', true, false);
|
13 |
+
content: x($two: 'foo') if(x($two: 'foo') == 'one foo', true, false);
|
14 |
+
content: x($two: 'foo', bar) if(x($two: 'foo', bar) == bar foo, true, false);
|
15 |
+
adjust-color: adjust-color(#00F, $blue: -15);
|
16 |
+
}
|
lib/phpsass/tests/holmes.css
ADDED
@@ -0,0 +1,227 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
body.holmes-debug a:not([href]),
|
2 |
+
body.holmes-debug a[href=""] {
|
3 |
+
outline: 2px solid red; }
|
4 |
+
body.holmes-debug a[href="#"],
|
5 |
+
body.holmes-debug a[href^="javascript"],
|
6 |
+
body.holmes-debug a:not([title]),
|
7 |
+
body.holmes-debug a[title=""] {
|
8 |
+
outline: 2px solid #ffdd00; }
|
9 |
+
body.holmes-debug img:not([alt]),
|
10 |
+
body.holmes-debug img[alt=""] {
|
11 |
+
outline: 2px solid red; }
|
12 |
+
body.holmes-debug label:not([for]),
|
13 |
+
body.holmes-debug label[for=""],
|
14 |
+
body.holmes-debug input:not([name]),
|
15 |
+
body.holmes-debug input[name=""],
|
16 |
+
body.holmes-debug select:not([name]),
|
17 |
+
body.holmes-debug select[name=""],
|
18 |
+
body.holmes-debug textarea:not([name]),
|
19 |
+
body.holmes-debug textarea[name=""],
|
20 |
+
body.holmes-debug abbr:not([title]),
|
21 |
+
body.holmes-debug abbr[title=""],
|
22 |
+
body.holmes-debug [class=""],
|
23 |
+
body.holmes-debug [id=""],
|
24 |
+
body.holmes-debug table:not([summary]),
|
25 |
+
body.holmes-debug table[summary=""] {
|
26 |
+
outline: 2px solid red; }
|
27 |
+
body.holmes-debug [style],
|
28 |
+
body.holmes-debug [onclick],
|
29 |
+
body.holmes-debug [onblur],
|
30 |
+
body.holmes-debug [onfocus],
|
31 |
+
body.holmes-debug [onselect],
|
32 |
+
body.holmes-debug [onload],
|
33 |
+
body.holmes-debug [onunload] {
|
34 |
+
outline: 2px solid #ffdd00; }
|
35 |
+
body.holmes-debug applet,
|
36 |
+
body.holmes-debug acronym,
|
37 |
+
body.holmes-debug center,
|
38 |
+
body.holmes-debug dir,
|
39 |
+
body.holmes-debug font,
|
40 |
+
body.holmes-debug strike,
|
41 |
+
body.holmes-debug u,
|
42 |
+
body.holmes-debug big,
|
43 |
+
body.holmes-debug tt,
|
44 |
+
body.holmes-debug marquee,
|
45 |
+
body.holmes-debug plaintext,
|
46 |
+
body.holmes-debug xmp {
|
47 |
+
border: 2px solid #a9a9a9; }
|
48 |
+
body.holmes-debug[bgproperties],
|
49 |
+
body.holmes-debug[topmargin],
|
50 |
+
body.holmes-debug[rightmargin],
|
51 |
+
body.holmes-debug[bottommargin],
|
52 |
+
body.holmes-debug[leftmargin] {
|
53 |
+
outline: 2px solid #a9a9a9; }
|
54 |
+
body.holmes-debug *[bordercolor],
|
55 |
+
body.holmes-debug *[bordercolordark],
|
56 |
+
body.holmes-debug *[bordercolorlight],
|
57 |
+
body.holmes-debug table[frame] {
|
58 |
+
border: 2px solid #a9a9a9; }
|
59 |
+
body.holmes-debug div:empty,
|
60 |
+
body.holmes-debug span:empty,
|
61 |
+
body.holmes-debug li:empty,
|
62 |
+
body.holmes-debug p:empty,
|
63 |
+
body.holmes-debug td:empty,
|
64 |
+
body.holmes-debug th:empty {
|
65 |
+
border: 2px solid #ff1100; }
|
66 |
+
body.holmes-debug a[href="#"]:hover::after,
|
67 |
+
body.holmes-debug a[href^="javascript"]:hover::after,
|
68 |
+
body.holmes-debug a:not([title]):hover::after,
|
69 |
+
body.holmes-debug a[title=""]:hover::after,
|
70 |
+
body.holmes-debug div:empty:hover::after,
|
71 |
+
body.holmes-debug span:empty:hover::after,
|
72 |
+
body.holmes-debug li:empty:hover::after,
|
73 |
+
body.holmes-debug p:empty:hover::after,
|
74 |
+
body.holmes-debug td:empty:hover::after,
|
75 |
+
body.holmes-debug th:empty:hover::after,
|
76 |
+
body.holmes-debug [style]:hover::after {
|
77 |
+
border-radius: 0.5em;
|
78 |
+
display: block;
|
79 |
+
padding: 1em;
|
80 |
+
margin: 1em;
|
81 |
+
background: #ffdd00;
|
82 |
+
position: fixed;
|
83 |
+
color: black;
|
84 |
+
left: 0;
|
85 |
+
top: 0;
|
86 |
+
z-index: 9999; }
|
87 |
+
body.holmes-debug a:not([href]):hover::after,
|
88 |
+
body.holmes-debug a[href=""]:hover::after,
|
89 |
+
body.holmes-debug img:not([alt]):hover:after,
|
90 |
+
body.holmes-debug img[alt=""]:hover::after,
|
91 |
+
body.holmes-debug label:not([for]):hover::after,
|
92 |
+
body.holmes-debug label[for=""]:hover::after,
|
93 |
+
body.holmes-debug input:not([name]):hover::after,
|
94 |
+
body.holmes-debug input[name=""]:hover::after,
|
95 |
+
body.holmes-debug select:not([name]):hover::after,
|
96 |
+
body.holmes-debug select[name=""]:hover::after,
|
97 |
+
body.holmes-debug textarea:not([name]):hover::after,
|
98 |
+
body.holmes-debug textarea[name=""]:hover::after,
|
99 |
+
body.holmes-debug abbr:not([title]):hover::after,
|
100 |
+
body.holmes-debug abbr[title=""]:hover::after,
|
101 |
+
body.holmes-debug [class=""]:hover::after,
|
102 |
+
body.holmes-debug [id=""]:hover::after,
|
103 |
+
body.holmes-debug table:not([summary]):hover::after,
|
104 |
+
body.holmes-debug table[summary=""]:hover::after {
|
105 |
+
border-radius: 0.5em;
|
106 |
+
display: block;
|
107 |
+
padding: 1em;
|
108 |
+
margin: 1em;
|
109 |
+
background: red;
|
110 |
+
position: fixed;
|
111 |
+
color: black;
|
112 |
+
left: 0;
|
113 |
+
top: 0;
|
114 |
+
z-index: 9999;
|
115 |
+
text-decoration: none; }
|
116 |
+
body.holmes-debug applet:hover::after,
|
117 |
+
body.holmes-debug acronym:hover::after,
|
118 |
+
body.holmes-debug center:hover::after,
|
119 |
+
body.holmes-debug dir:hover::after,
|
120 |
+
body.holmes-debug font:hover::after,
|
121 |
+
body.holmes-debug strike:hover::after,
|
122 |
+
body.holmes-debug big:hover::after,
|
123 |
+
body.holmes-debug tt:hover::after,
|
124 |
+
body.holmes-debug marquee:hover::after,
|
125 |
+
body.holmes-debug plaintext:hover::after,
|
126 |
+
body.holmes-debug xmp:hover::after,
|
127 |
+
body.holmes-debug *[bordercolor]:hover::after,
|
128 |
+
body.holmes-debug *[bordercolordark]:hover::after,
|
129 |
+
body.holmes-debug *[bordercolorlight]:hover::after,
|
130 |
+
body.holmes-debug table[frame]:hover::after {
|
131 |
+
border-radius: 0.5em;
|
132 |
+
display: block;
|
133 |
+
padding: 1em;
|
134 |
+
margin: 1em;
|
135 |
+
background: red;
|
136 |
+
position: fixed;
|
137 |
+
background: #a9a9a9;
|
138 |
+
color: black;
|
139 |
+
left: 0;
|
140 |
+
top: 0;
|
141 |
+
z-index: 9999; }
|
142 |
+
body.holmes-debug[bgproperties]:hover::after,
|
143 |
+
body.holmes-debug[topmargin]:hover::after,
|
144 |
+
body.holmes-debug[rightmargin]:hover::after,
|
145 |
+
body.holmes-debug[bottommargin]:hover::after,
|
146 |
+
body.holmes-debug[leftmargin]:hover::after {
|
147 |
+
border-radius: 0.5em;
|
148 |
+
display: block;
|
149 |
+
padding: 1em;
|
150 |
+
margin: 1em;
|
151 |
+
background: red;
|
152 |
+
position: fixed;
|
153 |
+
background: #a9a9a9;
|
154 |
+
color: black;
|
155 |
+
bottom: 0;
|
156 |
+
right: 0;
|
157 |
+
z-index: 9999; }
|
158 |
+
body.holmes-debug td:empty:hover::after,
|
159 |
+
body.holmes-debug th:empty:hover::after {
|
160 |
+
top: 4em; }
|
161 |
+
body.holmes-debug a:not([href]):hover::after,
|
162 |
+
body.holmes-debug a[href=""]:hover::after {
|
163 |
+
content: "Missing href attribute"; }
|
164 |
+
body.holmes-debug a[href="#"]:hover:after {
|
165 |
+
content: "href='#'"; }
|
166 |
+
body.holmes-debug a[href^="javascript"]:hover:after {
|
167 |
+
content: "href has JavaScript"; }
|
168 |
+
body.holmes-debug a:not([title]):hover::after,
|
169 |
+
body.holmes-debug a[title=""]:hover::after {
|
170 |
+
content: "Missing title attribute"; }
|
171 |
+
body.holmes-debug img:not([alt]):hover::after,
|
172 |
+
body.holmes-debug img[alt=""]:hover::after {
|
173 |
+
content: "Missing alt attribute"; }
|
174 |
+
body.holmes-debug label:not([for]):hover::after,
|
175 |
+
body.holmes-debug label[for=""]:hover::after {
|
176 |
+
content: "Missing for attribute"; }
|
177 |
+
body.holmes-debug input:not([name]):hover::after,
|
178 |
+
body.holmes-debug input[name=""]:hover::after,
|
179 |
+
body.holmes-debug select:not([name]):hover::after,
|
180 |
+
body.holmes-debug select[name=""]:hover::after,
|
181 |
+
body.holmes-debug textarea:not([name]):hover::after,
|
182 |
+
body.holmes-debug textarea[name=""]:hover::after {
|
183 |
+
content: "Missing name attribute"; }
|
184 |
+
body.holmes-debug abbr:not([title]):hover::after,
|
185 |
+
body.holmes-debug abbr[title=""]:hover::after {
|
186 |
+
content: "Missing title attribute"; }
|
187 |
+
body.holmes-debug [class=""]:hover::after {
|
188 |
+
content: "Blank class attribute"; }
|
189 |
+
body.holmes-debug [id=""]:hover::after {
|
190 |
+
content: "Blank id attribute"; }
|
191 |
+
body.holmes-debug table:not([summary]):hover::after,
|
192 |
+
body.holmes-debug table[summary=""]:hover::after {
|
193 |
+
content: "Missing summary attribute"; }
|
194 |
+
body.holmes-debug [style]:hover::after {
|
195 |
+
content: "Element has inline styles"; }
|
196 |
+
body.holmes-debug applet:hover::after,
|
197 |
+
body.holmes-debug acronym:hover::after,
|
198 |
+
body.holmes-debug center:hover::after,
|
199 |
+
body.holmes-debug dir:hover::after,
|
200 |
+
body.holmes-debug font:hover::after,
|
201 |
+
body.holmes-debug strike:hover::after,
|
202 |
+
body.holmes-debug big:hover::after,
|
203 |
+
body.holmes-debug tt:hover::after,
|
204 |
+
body.holmes-debug marquee:hover::after,
|
205 |
+
body.holmes-debug plaintext:hover::after,
|
206 |
+
body.holmes-debug xmp:hover::after {
|
207 |
+
content: "Deprecated or Non-W3C element"; }
|
208 |
+
body.holmes-debug[bgproperties]:hover::after {
|
209 |
+
content: "Deprecated or Non-W3C body attribute bgproperties"; }
|
210 |
+
body.holmes-debug[topmargin]:hover::after,
|
211 |
+
body.holmes-debug[rightmargin]:hover::after,
|
212 |
+
body.holmes-debug[bottommargin]:hover::after,
|
213 |
+
body.holmes-debug[leftmargin]:hover::after {
|
214 |
+
content: "Deprecated or Non-W3C body attribute *margin"; }
|
215 |
+
body.holmes-debug *[bordercolor]:hover::after,
|
216 |
+
body.holmes-debug *[bordercolordark]:hover::after,
|
217 |
+
body.holmes-debug *[bordercolorlight]:hover::after {
|
218 |
+
content: "Deprecated or Non-W3C body attribute bordercolor"; }
|
219 |
+
body.holmes-debug table[frame]:hover::after {
|
220 |
+
content: "Deprecated or Non-W3C attribute frame"; }
|
221 |
+
body.holmes-debug div:empty:hover::after,
|
222 |
+
body.holmes-debug span:empty:hover::after,
|
223 |
+
body.holmes-debug li:empty:hover::after,
|
224 |
+
body.holmes-debug p:empty:hover::after,
|
225 |
+
body.holmes-debug td:empty:hover::after,
|
226 |
+
body.holmes-debug th:empty:hover::after {
|
227 |
+
content: "Empty element!"; }
|
lib/phpsass/tests/holmes.sass
ADDED
@@ -0,0 +1,262 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
body.holmes-debug
|
2 |
+
|
3 |
+
a:not([href]),
|
4 |
+
a[href=""]
|
5 |
+
outline: 2px solid red
|
6 |
+
|
7 |
+
a[href="#"],
|
8 |
+
a[href^="javascript"],
|
9 |
+
a:not([title]),
|
10 |
+
a[title=""]
|
11 |
+
outline: 2px solid #fd0
|
12 |
+
|
13 |
+
img:not([alt]),
|
14 |
+
img[alt=""]
|
15 |
+
outline: 2px solid red
|
16 |
+
|
17 |
+
label:not([for]),
|
18 |
+
label[for=""],
|
19 |
+
input:not([name]),
|
20 |
+
input[name=""],
|
21 |
+
select:not([name]),
|
22 |
+
select[name=""],
|
23 |
+
textarea:not([name]),
|
24 |
+
textarea[name=""],
|
25 |
+
abbr:not([title]),
|
26 |
+
abbr[title=""],
|
27 |
+
[class=""],
|
28 |
+
[id=""],
|
29 |
+
table:not([summary]),
|
30 |
+
table[summary=""]
|
31 |
+
outline: 2px solid red
|
32 |
+
|
33 |
+
[style],
|
34 |
+
[onclick],
|
35 |
+
[onblur],
|
36 |
+
[onfocus],
|
37 |
+
[onselect],
|
38 |
+
[onload],
|
39 |
+
[onunload]
|
40 |
+
outline: 2px solid #fd0
|
41 |
+
|
42 |
+
applet,
|
43 |
+
acronym,
|
44 |
+
center,
|
45 |
+
dir,
|
46 |
+
font,
|
47 |
+
strike,
|
48 |
+
u,
|
49 |
+
big,
|
50 |
+
tt,
|
51 |
+
marquee,
|
52 |
+
plaintext,
|
53 |
+
xmp
|
54 |
+
border: 2px solid #a9a9a9
|
55 |
+
|
56 |
+
[bgproperties],
|
57 |
+
[topmargin],
|
58 |
+
[rightmargin],
|
59 |
+
[bottommargin],
|
60 |
+
[leftmargin]
|
61 |
+
outline: 2px solid #a9a9a9
|
62 |
+
|
63 |
+
*[bordercolor],
|
64 |
+
*[bordercolordark],
|
65 |
+
*[bordercolorlight],
|
66 |
+
table[frame]
|
67 |
+
border: 2px solid #a9a9a9
|
68 |
+
|
69 |
+
div:empty,
|
70 |
+
span:empty,
|
71 |
+
li:empty,
|
72 |
+
p:empty,
|
73 |
+
td:empty,
|
74 |
+
th:empty
|
75 |
+
border: 2px solid #f10
|
76 |
+
|
77 |
+
a[href="#"]:hover::after,
|
78 |
+
a[href^="javascript"]:hover::after,
|
79 |
+
a:not([title]):hover::after,
|
80 |
+
a[title=""]:hover::after,
|
81 |
+
div:empty:hover::after,
|
82 |
+
span:empty:hover::after,
|
83 |
+
li:empty:hover::after,
|
84 |
+
p:empty:hover::after,
|
85 |
+
td:empty:hover::after,
|
86 |
+
th:empty:hover::after,
|
87 |
+
[style]:hover::after
|
88 |
+
border-radius: 0.5em
|
89 |
+
display: block
|
90 |
+
padding: 1em
|
91 |
+
margin: 1em
|
92 |
+
background: #fd0
|
93 |
+
position: fixed
|
94 |
+
color: #000
|
95 |
+
left: 0
|
96 |
+
top: 0
|
97 |
+
z-index: 9999
|
98 |
+
|
99 |
+
a:not([href]):hover::after,
|
100 |
+
a[href=""]:hover::after,
|
101 |
+
img:not([alt]):hover:after,
|
102 |
+
img[alt=""]:hover::after,
|
103 |
+
label:not([for]):hover::after,
|
104 |
+
label[for=""]:hover::after,
|
105 |
+
input:not([name]):hover::after,
|
106 |
+
input[name=""]:hover::after,
|
107 |
+
select:not([name]):hover::after,
|
108 |
+
select[name=""]:hover::after,
|
109 |
+
textarea:not([name]):hover::after,
|
110 |
+
textarea[name=""]:hover::after,
|
111 |
+
abbr:not([title]):hover::after,
|
112 |
+
abbr[title=""]:hover::after,
|
113 |
+
[class=""]:hover::after,
|
114 |
+
[id=""]:hover::after,
|
115 |
+
table:not([summary]):hover::after,
|
116 |
+
table[summary=""]:hover::after
|
117 |
+
border-radius: 0.5em
|
118 |
+
display: block
|
119 |
+
padding: 1em
|
120 |
+
margin: 1em
|
121 |
+
background: red
|
122 |
+
position: fixed
|
123 |
+
color: #000
|
124 |
+
left: 0
|
125 |
+
top: 0
|
126 |
+
z-index: 9999
|
127 |
+
text-decoration: none
|
128 |
+
|
129 |
+
applet:hover::after,
|
130 |
+
acronym:hover::after,
|
131 |
+
center:hover::after,
|
132 |
+
dir:hover::after,
|
133 |
+
font:hover::after,
|
134 |
+
strike:hover::after,
|
135 |
+
big:hover::after,
|
136 |
+
tt:hover::after,
|
137 |
+
marquee:hover::after,
|
138 |
+
plaintext:hover::after,
|
139 |
+
xmp:hover::after,
|
140 |
+
*[bordercolor]:hover::after,
|
141 |
+
*[bordercolordark]:hover::after,
|
142 |
+
*[bordercolorlight]:hover::after,
|
143 |
+
table[frame]:hover::after
|
144 |
+
border-radius: 0.5em
|
145 |
+
display: block
|
146 |
+
padding: 1em
|
147 |
+
margin: 1em
|
148 |
+
background: red
|
149 |
+
position: fixed
|
150 |
+
background: #a9a9a9
|
151 |
+
color: #000
|
152 |
+
left: 0
|
153 |
+
top: 0
|
154 |
+
z-index: 9999
|
155 |
+
|
156 |
+
&[bgproperties]:hover::after,
|
157 |
+
&[topmargin]:hover::after,
|
158 |
+
&[rightmargin]:hover::after,
|
159 |
+
&[bottommargin]:hover::after,
|
160 |
+
&[leftmargin]:hover::after
|
161 |
+
border-radius: 0.5em
|
162 |
+
display: block
|
163 |
+
padding: 1em
|
164 |
+
margin: 1em
|
165 |
+
background: red
|
166 |
+
position: fixed
|
167 |
+
background: #a9a9a9
|
168 |
+
color: #000
|
169 |
+
bottom: 0
|
170 |
+
right: 0
|
171 |
+
z-index: 9999
|
172 |
+
|
173 |
+
td:empty:hover::after,
|
174 |
+
th:empty:hover::after
|
175 |
+
top: 4em
|
176 |
+
|
177 |
+
a:not([href]):hover::after,
|
178 |
+
a[href=""]:hover::after
|
179 |
+
content: "Missing href attribute"
|
180 |
+
|
181 |
+
a[href="#"]:hover:after
|
182 |
+
content: "href='#'"
|
183 |
+
|
184 |
+
a[href^="javascript"]:hover:after
|
185 |
+
content: "href has JavaScript"
|
186 |
+
|
187 |
+
a:not([title]):hover::after,
|
188 |
+
a[title=""]:hover::after
|
189 |
+
content: "Missing title attribute"
|
190 |
+
|
191 |
+
|
192 |
+
img:not([alt]):hover::after,
|
193 |
+
img[alt=""]:hover::after
|
194 |
+
content: "Missing alt attribute"
|
195 |
+
|
196 |
+
label:not([for]):hover::after,
|
197 |
+
label[for=""]:hover::after
|
198 |
+
content: "Missing for attribute"
|
199 |
+
|
200 |
+
input:not([name]):hover::after,
|
201 |
+
input[name=""]:hover::after,
|
202 |
+
select:not([name]):hover::after,
|
203 |
+
select[name=""]:hover::after,
|
204 |
+
textarea:not([name]):hover::after,
|
205 |
+
textarea[name=""]:hover::after
|
206 |
+
content: "Missing name attribute"
|
207 |
+
|
208 |
+
abbr:not([title]):hover::after,
|
209 |
+
abbr[title=""]:hover::after
|
210 |
+
content: "Missing title attribute"
|
211 |
+
|
212 |
+
[class=""]:hover::after
|
213 |
+
content: "Blank class attribute"
|
214 |
+
|
215 |
+
[id=""]:hover::after
|
216 |
+
content: "Blank id attribute"
|
217 |
+
|
218 |
+
table:not([summary]):hover::after,
|
219 |
+
table[summary=""]:hover::after
|
220 |
+
content: "Missing summary attribute"
|
221 |
+
|
222 |
+
[style]:hover::after
|
223 |
+
content: "Element has inline styles"
|
224 |
+
|
225 |
+
applet:hover::after,
|
226 |
+
acronym:hover::after,
|
227 |
+
center:hover::after,
|
228 |
+
dir:hover::after,
|
229 |
+
font:hover::after,
|
230 |
+
strike:hover::after,
|
231 |
+
big:hover::after,
|
232 |
+
tt:hover::after,
|
233 |
+
marquee:hover::after,
|
234 |
+
plaintext:hover::after,
|
235 |
+
xmp:hover::after
|
236 |
+
content: "Deprecated or Non-W3C element"
|
237 |
+
|
238 |
+
&[bgproperties]:hover::after
|
239 |
+
content: "Deprecated or Non-W3C body attribute bgproperties"
|
240 |
+
|
241 |
+
&[topmargin]:hover::after,
|
242 |
+
&[rightmargin]:hover::after,
|
243 |
+
&[bottommargin]:hover::after,
|
244 |
+
&[leftmargin]:hover::after
|
245 |
+
content: "Deprecated or Non-W3C body attribute *margin"
|
246 |
+
|
247 |
+
*[bordercolor]:hover::after,
|
248 |
+
*[bordercolordark]:hover::after,
|
249 |
+
*[bordercolorlight]:hover::after
|
250 |
+
content: "Deprecated or Non-W3C body attribute bordercolor"
|
251 |
+
|
252 |
+
table[frame]:hover::after
|
253 |
+
content: "Deprecated or Non-W3C attribute frame"
|
254 |
+
|
255 |
+
div:empty:hover::after,
|
256 |
+
span:empty:hover::after,
|
257 |
+
li:empty:hover::after,
|
258 |
+
p:empty:hover::after,
|
259 |
+
td:empty:hover::after,
|
260 |
+
th:empty:hover::after
|
261 |
+
content: "Empty element!"
|
262 |
+
|
lib/phpsass/tests/hsl-functions.css
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#hsl {
|
2 |
+
background: #010101;
|
3 |
+
background: rgba(1, 1, 1, 0.5);
|
4 |
+
background: 32.975deg;
|
5 |
+
background: 100%;
|
6 |
+
background: 47.451%;
|
7 |
+
background: #f28500;
|
8 |
+
background: #f58600;
|
9 |
+
background: #ef8400;
|
10 |
+
background: #f28500;
|
11 |
+
background: #f18501;
|
12 |
+
background: #797979;
|
13 |
+
background: #006df2;
|
14 |
+
background: #0d7aff; }
|
lib/phpsass/tests/hsl-functions.scss
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#hsl {
|
2 |
+
background: hsl(.09, 1, .47); // #010101
|
3 |
+
background: hsla(.09, 1, .47, .5); // rgba(1, 1, 1, 0.5)
|
4 |
+
background: hue(#f28500); // 32.975deg
|
5 |
+
background: saturation(#f28500); // 100%
|
6 |
+
background: lightness(#f28500); // 47.451%
|
7 |
+
background: adjust-hue(#f28500, .01); // #f28500
|
8 |
+
background: lighten(#f28500, .5); // #f58600
|
9 |
+
background: darken(#f28500, .5); // #ef8400
|
10 |
+
background: saturate(#f28500, .5); // #f28500
|
11 |
+
background: desaturate(#f28500, .5); // #f18501
|
12 |
+
background: grayscale(#f28500); // #797979
|
13 |
+
background: complement(#f28500); // #006df2
|
14 |
+
background: invert(#f28500); // #0d7aff
|
15 |
+
}
|
lib/phpsass/tests/if.css
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
a {
|
2 |
+
branch: if; }
|
3 |
+
|
4 |
+
b {
|
5 |
+
branch: else; }
|
lib/phpsass/tests/if.sass
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
a
|
2 |
+
@if true
|
3 |
+
branch: if
|
4 |
+
@else
|
5 |
+
branch: else
|
6 |
+
|
7 |
+
b
|
8 |
+
@if false
|
9 |
+
branch: if
|
10 |
+
@else
|
11 |
+
branch: else
|
lib/phpsass/tests/import.css
ADDED
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
imported {
|
2 |
+
otherconst: hello;
|
3 |
+
myconst: goodbye;
|
4 |
+
pre-mixin: here; }
|
5 |
+
|
6 |
+
body {
|
7 |
+
font: Arial;
|
8 |
+
background: blue; }
|
9 |
+
|
10 |
+
#page {
|
11 |
+
width: 700px;
|
12 |
+
height: 100;
|
13 |
+
line-height: 100%; }
|
14 |
+
#page #header {
|
15 |
+
height: 300px; }
|
16 |
+
#page #header h1 {
|
17 |
+
font-size: 50px;
|
18 |
+
color: blue; }
|
19 |
+
|
20 |
+
#content.user.show #container.top #column.left {
|
21 |
+
width: 100px; }
|
22 |
+
#content.user.show #container.top #column.right {
|
23 |
+
width: 600px; }
|
24 |
+
#content.user.show #container.bottom {
|
25 |
+
background: brown; }
|
26 |
+
|
27 |
+
midrule {
|
28 |
+
inthe: middle; }
|
29 |
+
|
30 |
+
scss {
|
31 |
+
imported: yes; }
|
32 |
+
|
33 |
+
body {
|
34 |
+
font: Arial;
|
35 |
+
background: blue; }
|
36 |
+
|
37 |
+
#page {
|
38 |
+
width: 700px;
|
39 |
+
height: 100;
|
40 |
+
line-height: 100%; }
|
41 |
+
#page #header {
|
42 |
+
height: 300px; }
|
43 |
+
#page #header h1 {
|
44 |
+
font-size: 50px;
|
45 |
+
color: blue; }
|
46 |
+
|
47 |
+
#content.user.show #container.top #column.left {
|
48 |
+
width: 100px; }
|
49 |
+
#content.user.show #container.top #column.right {
|
50 |
+
width: 600px; }
|
51 |
+
#content.user.show #container.bottom {
|
52 |
+
background: brown; }
|
53 |
+
|
54 |
+
@import url('basic.css');
|
55 |
+
@import url('../results/complex.css');
|
56 |
+
#foo {
|
57 |
+
background-color: #bbaaff; }
|
58 |
+
|
59 |
+
nonimported {
|
60 |
+
myconst: hello;
|
61 |
+
otherconst: goodbye;
|
62 |
+
post-mixin: here; }
|
lib/phpsass/tests/import.sass
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
$preconst: hello
|
2 |
+
|
3 |
+
=premixin
|
4 |
+
pre-mixin: here
|
5 |
+
|
6 |
+
@import importee.sass, scss_importee
|
7 |
+
@import "basic.sass"
|
8 |
+
@import basic.css
|
9 |
+
@import ../results/complex.css
|
10 |
+
@import partial.sass
|
11 |
+
|
12 |
+
nonimported
|
13 |
+
:myconst $preconst
|
14 |
+
:otherconst $postconst
|
15 |
+
+postmixin
|
lib/phpsass/tests/import_content.css
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
1 |
+
a {
|
2 |
+
b: c; }
|
lib/phpsass/tests/import_content.sass
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
1 |
+
@import imported_content
|
2 |
+
|
3 |
+
@include foo
|
4 |
+
b: c
|
lib/phpsass/tests/importee.sass
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
$postconst: goodbye
|
2 |
+
|
3 |
+
=postmixin
|
4 |
+
post-mixin: here
|
5 |
+
|
6 |
+
imported
|
7 |
+
:otherconst $preconst
|
8 |
+
:myconst $postconst
|
9 |
+
+premixin
|
10 |
+
|
11 |
+
@import basic
|
12 |
+
|
13 |
+
midrule
|
14 |
+
:inthe middle
|
15 |
+
|
16 |
+
=crazymixin
|
17 |
+
foo: bar
|
18 |
+
baz
|
19 |
+
blat: bang
|
lib/phpsass/tests/interpolation.css
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
foo,
|
2 |
+
bar {
|
3 |
+
o: k; }
|
4 |
+
|
5 |
+
outer foo,
|
6 |
+
outer bar {
|
7 |
+
o: k; }
|
8 |
+
|
9 |
+
body[content="oh, look"] foo,
|
10 |
+
body[content="oh, look"] bar {
|
11 |
+
o: k; }
|
12 |
+
|
13 |
+
.foo-bar {
|
14 |
+
o: k; }
|
15 |
+
|
16 |
+
.bar {
|
17 |
+
o: k; }
|
18 |
+
|
19 |
+
foo2bar {
|
20 |
+
o: k; }
|
21 |
+
|
22 |
+
nex3_is_weird {
|
23 |
+
flabnabbit: flabnabbit('1foo'); }
|
24 |
+
|
25 |
+
@media screen and (-webkit-min-device-pixel-ratio: 1.5) {
|
26 |
+
webkit-only {
|
27 |
+
color: green; } }
|
lib/phpsass/tests/interpolation.scss
ADDED
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
$foo: 'foo', bar;
|
2 |
+
|
3 |
+
// selector interpolation
|
4 |
+
#{$foo} {
|
5 |
+
o: k;
|
6 |
+
}
|
7 |
+
|
8 |
+
// nested selector interpolation
|
9 |
+
outer {
|
10 |
+
#{$foo} {
|
11 |
+
o: k;
|
12 |
+
}
|
13 |
+
}
|
14 |
+
|
15 |
+
// nested again, but with complexity (commas in the body content)
|
16 |
+
body[content="oh, look"] {
|
17 |
+
#{$foo} {
|
18 |
+
o: k;
|
19 |
+
}
|
20 |
+
}
|
21 |
+
|
22 |
+
// interpolation quoted
|
23 |
+
.foo-#{"bar"} {
|
24 |
+
o: k
|
25 |
+
}
|
26 |
+
|
27 |
+
// placeholder interpolation
|
28 |
+
$bar: bar;
|
29 |
+
%#{$bar} {
|
30 |
+
o: k
|
31 |
+
}
|
32 |
+
.bar {
|
33 |
+
@extend %bar;
|
34 |
+
}
|
35 |
+
|
36 |
+
// string interpolation
|
37 |
+
foo#{1 + 1}bar {
|
38 |
+
o: k
|
39 |
+
}
|
40 |
+
|
41 |
+
// interpolation in a function call
|
42 |
+
nex3_is_weird {
|
43 |
+
flabnabbit: flabnabbit('#{1 + 'foo'}');
|
44 |
+
}
|
45 |
+
|
46 |
+
$media: screen;
|
47 |
+
$feature: -webkit-min-device-pixel-ratio;
|
48 |
+
$value: 1.5;
|
49 |
+
|
50 |
+
@media #{$media} and ($feature: $value) {
|
51 |
+
webkit-only {
|
52 |
+
color: green;
|
53 |
+
}
|
54 |
+
}
|
lib/phpsass/tests/introspection.css
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#introspection {
|
2 |
+
append: 'apple', 'banana', 'pear', 2px, 2, 2em, 4px, 'hello';
|
3 |
+
content: list;
|
4 |
+
content: string;
|
5 |
+
content: number;
|
6 |
+
content: number;
|
7 |
+
content: px;
|
8 |
+
content: ;
|
9 |
+
content: false;
|
10 |
+
content: true;
|
11 |
+
content: true;
|
12 |
+
content: false;
|
13 |
+
content: true; }
|
lib/phpsass/tests/introspection.scss
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#introspection {
|
2 |
+
$one: 'hello';
|
3 |
+
$array: 'apple', 'banana', 'pear', 2px, 2, 2em, 4px;
|
4 |
+
append: append($array, $one);
|
5 |
+
content: type-of($array);
|
6 |
+
content: type-of(nth($array, 1));
|
7 |
+
content: type-of(nth($array, 4));
|
8 |
+
content: type-of(nth($array, 5));
|
9 |
+
content: unit(nth($array, 4));
|
10 |
+
content: unit(nth($array, 5));
|
11 |
+
content: unitless(nth($array, 4));
|
12 |
+
content: unitless(nth($array, 5));
|
13 |
+
content: comparable(nth($array, 4), nth($array, 5));
|
14 |
+
content: comparable(nth($array, 4), nth($array, 6));
|
15 |
+
content: comparable(nth($array, 4), nth($array, 7));
|
16 |
+
}
|
lib/phpsass/tests/line_numbers.css
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
foo {
|
2 |
+
bar: baz; }
|
3 |
+
|
4 |
+
imported {
|
5 |
+
otherconst: 12;
|
6 |
+
myconst: goodbye; }
|
7 |
+
imported squggle {
|
8 |
+
blat: bang; }
|
9 |
+
|
10 |
+
body {
|
11 |
+
font: Arial;
|
12 |
+
background: blue; }
|
13 |
+
|
14 |
+
#page {
|
15 |
+
width: 700px;
|
16 |
+
height: 100;
|
17 |
+
line-height: 100%; }
|
18 |
+
#page #header {
|
19 |
+
height: 300px; }
|
20 |
+
#page #header h1 {
|
21 |
+
font-size: 50px;
|
22 |
+
color: blue; }
|
23 |
+
|
24 |
+
#content.user.show #container.top #column.left {
|
25 |
+
width: 100px; }
|
26 |
+
#content.user.show #container.top #column.right {
|
27 |
+
width: 600px; }
|
28 |
+
#content.user.show #container.bottom {
|
29 |
+
background: brown; }
|
30 |
+
|
31 |
+
midrule {
|
32 |
+
inthe: middle; }
|
33 |
+
|
34 |
+
umph {
|
35 |
+
foo: bar; }
|
36 |
+
umph baz {
|
37 |
+
blat: bang; }
|
lib/phpsass/tests/line_numbers.sass
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
foo
|
2 |
+
bar: baz
|
3 |
+
|
4 |
+
=premixin
|
5 |
+
squggle
|
6 |
+
blat: bang
|
7 |
+
|
8 |
+
$preconst: 12
|
9 |
+
|
10 |
+
@import importee
|
11 |
+
|
12 |
+
umph
|
13 |
+
+crazymixin
|
lib/phpsass/tests/list.css
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
1 |
+
#list {
|
2 |
+
font-size: 3;
|
3 |
+
content: 'banana';
|
4 |
+
content: 'pear'; }
|
lib/phpsass/tests/list.scss
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#list {
|
2 |
+
$test-list: 'apple', 'banana', 'orange';
|
3 |
+
$list-join: 'pear';
|
4 |
+
font-size: length($test-list);
|
5 |
+
content: nth($test-list, 2);
|
6 |
+
$test-list: join($test-list, $list-join);
|
7 |
+
content: nth($test-list, 4);
|
8 |
+
}
|
lib/phpsass/tests/media.css
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.sidebar {
|
2 |
+
width: 300px; }
|
3 |
+
@media screen and (max-width: 1024px) {
|
4 |
+
.sidebar {
|
5 |
+
width: 500 px; } }
|
6 |
+
@media all and (max-width: 800px) {
|
7 |
+
color: blue; }
|
lib/phpsass/tests/media.scss
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
$fiver: 500px;
|
2 |
+
.sidebar {
|
3 |
+
width: 300px;
|
4 |
+
@media screen and (max-width: 1024px) {
|
5 |
+
width: $fiver;
|
6 |
+
}
|
7 |
+
}
|
8 |
+
|
9 |
+
@mixin test-mixin() {
|
10 |
+
color: blue;
|
11 |
+
}
|
12 |
+
|
13 |
+
@media all and (max-width: 800px) {
|
14 |
+
@include test-mixin;
|
15 |
+
}
|
lib/phpsass/tests/misc-functions.css
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#misc-function {
|
2 |
+
font-size: 12px; }
|
3 |
+
|
4 |
+
.min-max {
|
5 |
+
min: -42;
|
6 |
+
max: 42; }
|
lib/phpsass/tests/misc-functions.scss
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#misc-function {
|
2 |
+
$bool: true;
|
3 |
+
|
4 |
+
font-size: if($bool, 12px, 10px);
|
5 |
+
}
|
6 |
+
|
7 |
+
.min-max {
|
8 |
+
$list: 34, 42, 29, 3, -42;
|
9 |
+
min: min($list...);
|
10 |
+
max: max($list...);
|
11 |
+
}
|
lib/phpsass/tests/misc.css
ADDED
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*
|
2 |
+
* Source: http://code.google.com/p/phamlp/issues/detail?id=117;
|
3 |
+
*/
|
4 |
+
@import url("http://fonts.googleapis.com/css?family=Actor");
|
5 |
+
|
6 |
+
/*
|
7 |
+
* Source: http://code.google.com/p/phamlp/issues/detail?id=116;
|
8 |
+
*/
|
9 |
+
@import url('somefile.css');
|
10 |
+
|
11 |
+
/*
|
12 |
+
* Source: http://code.google.com/p/phamlp/issues/detail?id=101;
|
13 |
+
*/
|
14 |
+
.nav-boolean ol li input:checked + label {
|
15 |
+
color: red; }
|
16 |
+
|
17 |
+
/*
|
18 |
+
* Source: http://code.google.com/p/phamlp/issues/detail?id=100;
|
19 |
+
*/
|
20 |
+
.zero {
|
21 |
+
margin: 0 0;
|
22 |
+
padding: 0px; }
|
23 |
+
|
24 |
+
/*
|
25 |
+
* Source: http://code.google.com/p/phamlp/issues/detail?id=99;
|
26 |
+
*/
|
27 |
+
/*
|
28 |
+
* Variables
|
29 |
+
*/
|
30 |
+
.content-navigation {
|
31 |
+
border-color: #3bbfce;
|
32 |
+
color: #2ca2af; }
|
33 |
+
|
34 |
+
.border {
|
35 |
+
padding: 16px;
|
36 |
+
margin: 8px;
|
37 |
+
border-color: #3bbfce; }
|
38 |
+
|
39 |
+
/*
|
40 |
+
* Nesting
|
41 |
+
*/
|
42 |
+
table.hl {
|
43 |
+
margin: 2em 0; }
|
44 |
+
table.hl td.ln {
|
45 |
+
text-align: right; }
|
46 |
+
|
47 |
+
li {
|
48 |
+
font-family: serif;
|
49 |
+
font-weight: bold;
|
50 |
+
font-size: 1.2em; }
|
51 |
+
|
52 |
+
/*
|
53 |
+
* Mixins
|
54 |
+
*/
|
55 |
+
#data {
|
56 |
+
float: left;
|
57 |
+
margin-left: 10px; }
|
58 |
+
#data th {
|
59 |
+
text-align: center;
|
60 |
+
font-weight: bold; }
|
61 |
+
#data td,
|
62 |
+
#data th {
|
63 |
+
padding: 2px; }
|
64 |
+
|
65 |
+
/*
|
66 |
+
* Selector Inheritance
|
67 |
+
*/
|
68 |
+
.error,
|
69 |
+
.madError {
|
70 |
+
border: 1px red;
|
71 |
+
background: #ffdddd; }
|
72 |
+
|
73 |
+
.error.intrusion,
|
74 |
+
.madError.intrusion {
|
75 |
+
font-size: 1.3em;
|
76 |
+
font-weight: bold; }
|
77 |
+
|
78 |
+
.madError {
|
79 |
+
border-width: 3px; }
|
80 |
+
|
81 |
+
git20 {
|
82 |
+
*margin-left: 0.991%; }
|
83 |
+
|
84 |
+
git30 {
|
85 |
+
width: 30px !important;}
|
lib/phpsass/tests/misc.scss
ADDED
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/* Source: http://code.google.com/p/phamlp/issues/detail?id=117; */
|
2 |
+
@import url("http://fonts.googleapis.com/css?family=Actor");
|
3 |
+
|
4 |
+
/* Source: http://code.google.com/p/phamlp/issues/detail?id=116; */
|
5 |
+
@import "somefile.css";
|
6 |
+
|
7 |
+
/* Source: http://code.google.com/p/phamlp/issues/detail?id=101; */
|
8 |
+
@mixin nav-boolean {
|
9 |
+
ol {
|
10 |
+
li {
|
11 |
+
input:checked + label { color: red; }
|
12 |
+
}
|
13 |
+
}
|
14 |
+
}
|
15 |
+
|
16 |
+
.nav-boolean { @include nav-boolean; }
|
17 |
+
|
18 |
+
/* Source: http://code.google.com/p/phamlp/issues/detail?id=100; */
|
19 |
+
.zero {
|
20 |
+
margin: 0 0;
|
21 |
+
padding: 0px;
|
22 |
+
}
|
23 |
+
|
24 |
+
/* Source: http://code.google.com/p/phamlp/issues/detail?id=99; */
|
25 |
+
/* Variables */
|
26 |
+
|
27 |
+
$blue: #3bbfce;
|
28 |
+
$margin: 16px;
|
29 |
+
|
30 |
+
.content-navigation {
|
31 |
+
border-color: $blue;
|
32 |
+
color:
|
33 |
+
darken($blue, 9%);
|
34 |
+
}
|
35 |
+
|
36 |
+
.border {
|
37 |
+
padding: $margin;
|
38 |
+
margin: $margin / 2;
|
39 |
+
border-color: $blue;
|
40 |
+
}
|
41 |
+
|
42 |
+
/* Nesting */
|
43 |
+
|
44 |
+
table.hl {
|
45 |
+
margin: 2em 0;
|
46 |
+
td.ln {
|
47 |
+
text-align: right;
|
48 |
+
}
|
49 |
+
}
|
50 |
+
|
51 |
+
li {
|
52 |
+
font: {
|
53 |
+
family: serif;
|
54 |
+
weight: bold;
|
55 |
+
size: 1.2em;
|
56 |
+
}
|
57 |
+
}
|
58 |
+
|
59 |
+
/* Mixins */
|
60 |
+
|
61 |
+
@mixin table-base {
|
62 |
+
th {
|
63 |
+
text-align: center;
|
64 |
+
font-weight: bold;
|
65 |
+
}
|
66 |
+
td, th {padding: 2px}
|
67 |
+
}
|
68 |
+
|
69 |
+
@mixin left($dist) {
|
70 |
+
float: left;
|
71 |
+
margin-left: $dist;
|
72 |
+
}
|
73 |
+
|
74 |
+
#data {
|
75 |
+
@include left(10px);
|
76 |
+
@include table-base;
|
77 |
+
}
|
78 |
+
|
79 |
+
/* Selector Inheritance */
|
80 |
+
|
81 |
+
.error {
|
82 |
+
border: 1px red;
|
83 |
+
background: #ffdddd;
|
84 |
+
}
|
85 |
+
.error.intrusion {
|
86 |
+
font-size: 1.3em;
|
87 |
+
font-weight: bold;
|
88 |
+
}
|
89 |
+
|
90 |
+
.madError {
|
91 |
+
@extend .error;
|
92 |
+
border-width: 3px;
|
93 |
+
}
|
94 |
+
|
95 |
+
git20 {
|
96 |
+
$fluidGridGutterWidth: 1%;
|
97 |
+
$gridRowWidth: 60px;
|
98 |
+
*margin-left: $fluidGridGutterWidth - (0.5 / $gridRowWidth * 100px * 1%);
|
99 |
+
}
|
100 |
+
|
101 |
+
git30 {
|
102 |
+
width: 30px!important;
|
103 |
+
}
|
lib/phpsass/tests/mixin-content.css
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
1 |
+
#logo {
|
2 |
+
background-image: url("/images/logo.png"); }
|
3 |
+
* html #logo {
|
4 |
+
background-image: url("/images/logo.gif"); }
|
lib/phpsass/tests/mixin-content.sass
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
=ie6
|
2 |
+
* html &
|
3 |
+
@content
|
4 |
+
|
5 |
+
#logo
|
6 |
+
background-image: url("/images/logo.png")
|
7 |
+
+ie6
|
8 |
+
background-image: url("/images/logo.gif")
|
lib/phpsass/tests/mixin-content.scss
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
@mixin ie6 {
|
2 |
+
* html & {
|
3 |
+
@content
|
4 |
+
}
|
5 |
+
}
|
6 |
+
|
7 |
+
#logo {
|
8 |
+
background-image: url("/images/logo.png");
|
9 |
+
@include ie6 {
|
10 |
+
background-image: url("/images/logo.gif");
|
11 |
+
}
|
12 |
+
}
|
lib/phpsass/tests/mixin-ja1.css
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.messages h2 {
|
2 |
+
display: -moz-inline-box;
|
3 |
+
-moz-box-orient: vertical;
|
4 |
+
display: inline-block;
|
5 |
+
vertical-align: middle;
|
6 |
+
*vertical-align: auto; }
|
7 |
+
.messages h2 {
|
8 |
+
*display: inline; }
|
lib/phpsass/tests/mixin-ja1.sass
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
$legacy-support-for-ie: true
|
2 |
+
=inline-block
|
3 |
+
@if $legacy-support-for-ie
|
4 |
+
&
|
5 |
+
*display: inline
|
6 |
+
display: -moz-inline-box
|
7 |
+
-moz-box-orient: vertical
|
8 |
+
display: inline-block
|
9 |
+
vertical-align: middle
|
10 |
+
@if $legacy-support-for-ie
|
11 |
+
*vertical-align: auto
|
12 |
+
|
13 |
+
.messages h2
|
14 |
+
+inline-block
|
lib/phpsass/tests/mixin-params.css
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
default {
|
2 |
+
one: 'foo';
|
3 |
+
two: 'bar'; }
|
4 |
+
|
5 |
+
one {
|
6 |
+
one: 'one';
|
7 |
+
two: 'bar'; }
|
8 |
+
|
9 |
+
two {
|
10 |
+
one: 'foo';
|
11 |
+
two: 'two'; }
|
12 |
+
|
13 |
+
both {
|
14 |
+
one: 'one';
|
15 |
+
two: 'two'; }
|
lib/phpsass/tests/mixin-params.scss
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
@mixin test($one: 'foo', $two: 'bar') {
|
2 |
+
one: $one;
|
3 |
+
two: $two;
|
4 |
+
}
|
5 |
+
|
6 |
+
default {
|
7 |
+
@include test();
|
8 |
+
}
|
9 |
+
|
10 |
+
one {
|
11 |
+
@include test('one');
|
12 |
+
}
|
13 |
+
|
14 |
+
two {
|
15 |
+
@include test($two: 'two');
|
16 |
+
}
|
17 |
+
|
18 |
+
both {
|
19 |
+
@include test('one', 'two');
|
20 |
+
}
|
lib/phpsass/tests/mixin_bork.sass
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
=outer-mixin
|
2 |
+
+error-mixin
|
3 |
+
|
4 |
+
foo
|
5 |
+
+outer-mixin
|
lib/phpsass/tests/mixins.css
ADDED
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*
|
2 |
+
* test that variable context evaluates correctly
|
3 |
+
*/
|
4 |
+
test {
|
5 |
+
one: two; }
|
6 |
+
#main {
|
7 |
+
width: 15em;
|
8 |
+
color: blue; }
|
9 |
+
#main p {
|
10 |
+
border-top-width: 2px;
|
11 |
+
border-top-color: #ffcc00;
|
12 |
+
border-left-width: 1px;
|
13 |
+
border-left-color: black;
|
14 |
+
-moz-border-radius: 10px;
|
15 |
+
border-style: dotted;
|
16 |
+
border-width: 2px; }
|
17 |
+
#main .cool {
|
18 |
+
width: 100px; }
|
19 |
+
|
20 |
+
#left {
|
21 |
+
border-top-width: 2px;
|
22 |
+
border-top-color: #ffcc00;
|
23 |
+
border-left-width: 1px;
|
24 |
+
border-left-color: black;
|
25 |
+
-moz-border-radius: 10px;
|
26 |
+
font-size: 2em;
|
27 |
+
font-weight: bold;
|
28 |
+
float: left; }
|
29 |
+
|
30 |
+
#right {
|
31 |
+
border-top-width: 2px;
|
32 |
+
border-top-color: #ffcc00;
|
33 |
+
border-left-width: 1px;
|
34 |
+
border-left-color: black;
|
35 |
+
-moz-border-radius: 10px;
|
36 |
+
color: red;
|
37 |
+
font-size: 20px;
|
38 |
+
float: right; }
|
39 |
+
|
40 |
+
.bordered {
|
41 |
+
border-top-width: 2px;
|
42 |
+
border-top-color: #ffcc00;
|
43 |
+
border-left-width: 1px;
|
44 |
+
border-left-color: black;
|
45 |
+
-moz-border-radius: 10px; }
|
46 |
+
|
47 |
+
.complex {
|
48 |
+
color: red;
|
49 |
+
font-size: 20px;
|
50 |
+
text-decoration: none; }
|
51 |
+
.complex:after {
|
52 |
+
content: ".";
|
53 |
+
display: block;
|
54 |
+
height: 0;
|
55 |
+
clear: both;
|
56 |
+
visibility: hidden; }
|
57 |
+
* html .complex {
|
58 |
+
height: 1px;
|
59 |
+
color: red;
|
60 |
+
font-size: 20px; }
|
61 |
+
|
62 |
+
.more-complex {
|
63 |
+
color: red;
|
64 |
+
font-size: 20px;
|
65 |
+
text-decoration: none;
|
66 |
+
display: inline;
|
67 |
+
-webkit-nonsense-top-right: 1px;
|
68 |
+
-webkit-nonsense-bottom-left: 1px; }
|
69 |
+
.more-complex:after {
|
70 |
+
content: ".";
|
71 |
+
display: block;
|
72 |
+
height: 0;
|
73 |
+
clear: both;
|
74 |
+
visibility: hidden; }
|
75 |
+
* html .more-complex {
|
76 |
+
height: 1px;
|
77 |
+
color: red;
|
78 |
+
font-size: 20px; }
|
79 |
+
.more-complex a:hover {
|
80 |
+
text-decoration: underline;
|
81 |
+
color: red;
|
82 |
+
font-size: 20px;
|
83 |
+
border-top-width: 2px;
|
84 |
+
border-top-color: #ffcc00;
|
85 |
+
border-left-width: 1px;
|
86 |
+
border-left-color: black;
|
87 |
+
-moz-border-radius: 10px; }
|
lib/phpsass/tests/mixins.sass
ADDED
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
$yellow: #fc0
|
2 |
+
|
3 |
+
/* test that variable context evaluates correctly */
|
4 |
+
=foo($var, $val)
|
5 |
+
:#{$var} $val
|
6 |
+
=bar($var)
|
7 |
+
+foo('one', $var)
|
8 |
+
test
|
9 |
+
+bar(two)
|
10 |
+
|
11 |
+
=bordered
|
12 |
+
:border
|
13 |
+
:top
|
14 |
+
:width 2px
|
15 |
+
:color $yellow
|
16 |
+
:left
|
17 |
+
:width 1px
|
18 |
+
:color #000
|
19 |
+
-moz-border-radius: 10px
|
20 |
+
|
21 |
+
=header-font
|
22 |
+
:color #f00
|
23 |
+
:font
|
24 |
+
:size 20px
|
25 |
+
|
26 |
+
=compound
|
27 |
+
+header-font
|
28 |
+
+bordered
|
29 |
+
|
30 |
+
=complex
|
31 |
+
+header-font
|
32 |
+
text:
|
33 |
+
decoration: none
|
34 |
+
&:after
|
35 |
+
content: "."
|
36 |
+
display: block
|
37 |
+
height: 0
|
38 |
+
clear: both
|
39 |
+
visibility: hidden
|
40 |
+
* html &
|
41 |
+
height: 1px
|
42 |
+
+header-font
|
43 |
+
=deep
|
44 |
+
a:hover
|
45 |
+
:text-decoration underline
|
46 |
+
+compound
|
47 |
+
|
48 |
+
|
49 |
+
#main
|
50 |
+
:width 15em
|
51 |
+
:color #0000ff
|
52 |
+
p
|
53 |
+
+bordered
|
54 |
+
:border
|
55 |
+
:style dotted
|
56 |
+
:width 2px
|
57 |
+
.cool
|
58 |
+
:width 100px
|
59 |
+
|
60 |
+
#left
|
61 |
+
+bordered
|
62 |
+
:font
|
63 |
+
:size 2em
|
64 |
+
:weight bold
|
65 |
+
:float left
|
66 |
+
|
67 |
+
#right
|
68 |
+
+bordered
|
69 |
+
+header-font
|
70 |
+
:float right
|
71 |
+
|
72 |
+
.bordered
|
73 |
+
+bordered
|
74 |
+
|
75 |
+
.complex
|
76 |
+
+complex
|
77 |
+
|
78 |
+
.more-complex
|
79 |
+
+complex
|
80 |
+
+deep
|
81 |
+
display: inline
|
82 |
+
-webkit-nonsense:
|
83 |
+
top-right: 1px
|
84 |
+
bottom-left: 1px
|
lib/phpsass/tests/multiline.css
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#main,
|
2 |
+
#header {
|
3 |
+
height: 50px; }
|
4 |
+
#main div,
|
5 |
+
#header div {
|
6 |
+
width: 100px; }
|
7 |
+
#main div a span,
|
8 |
+
#main div em span,
|
9 |
+
#header div a span,
|
10 |
+
#header div em span {
|
11 |
+
color: pink; }
|
12 |
+
|
13 |
+
#one div.nested,
|
14 |
+
#one span.nested,
|
15 |
+
#one p.nested,
|
16 |
+
#two div.nested,
|
17 |
+
#two span.nested,
|
18 |
+
#two p.nested,
|
19 |
+
#three div.nested,
|
20 |
+
#three span.nested,
|
21 |
+
#three p.nested {
|
22 |
+
font-weight: bold;
|
23 |
+
border-color: red;
|
24 |
+
display: block; }
|
lib/phpsass/tests/multiline.sass
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#main,
|
2 |
+
#header
|
3 |
+
height: 50px
|
4 |
+
div
|
5 |
+
width: 100px
|
6 |
+
a,
|
7 |
+
em
|
8 |
+
span
|
9 |
+
color: pink
|
10 |
+
|
11 |
+
#one,
|
12 |
+
#two,
|
13 |
+
#three
|
14 |
+
div.nested,
|
15 |
+
span.nested,
|
16 |
+
p.nested
|
17 |
+
:font
|
18 |
+
:weight bold
|
19 |
+
:border-color red
|
20 |
+
:display block
|
lib/phpsass/tests/nested-media.css
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.sidebar {
|
2 |
+
width: 300px; }
|
3 |
+
@media screen and (max-width: 1024px) {
|
4 |
+
.sidebar {
|
5 |
+
width: 500px; }
|
6 |
+
.sidebar .subclass {
|
7 |
+
height: 100px; }
|
8 |
+
.sidebar .subclass .subsubclass {
|
9 |
+
height: 200px; } }
|
10 |
+
@media screen and (max-width:1024px) {
|
11 |
+
.subclass {
|
12 |
+
height: 100px; }
|
13 |
+
.subclass .subsubclass {
|
14 |
+
height: 200px; } }
|
lib/phpsass/tests/nested-media.scss
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
$fiver: 500px;
|
2 |
+
.sidebar {
|
3 |
+
width: 300px;
|
4 |
+
@media screen and (max-width: 1024px) {
|
5 |
+
width: $fiver;
|
6 |
+
.subclass {
|
7 |
+
height: 100px;
|
8 |
+
.subsubclass {
|
9 |
+
height: 200px;
|
10 |
+
}
|
11 |
+
}
|
12 |
+
}
|
13 |
+
}
|
14 |
+
|
15 |
+
@media screen and (max-width: 1024px) {
|
16 |
+
.subclass {
|
17 |
+
height: 100px;
|
18 |
+
.subsubclass {
|
19 |
+
height: 200px;
|
20 |
+
}
|
21 |
+
}
|
22 |
+
}
|
lib/phpsass/tests/nested.css
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#main {
|
2 |
+
width: 15em;
|
3 |
+
color: blue; }
|
4 |
+
#main p {
|
5 |
+
border-style: dotted;
|
6 |
+
border-width: 2px; }
|
7 |
+
#main .cool {
|
8 |
+
width: 100px; }
|
9 |
+
|
10 |
+
#left {
|
11 |
+
font-size: 2em;
|
12 |
+
font-weight: bold;
|
13 |
+
float: left; }
|
14 |
+
|
15 |
+
#right .header {
|
16 |
+
border-style: solid; }
|
17 |
+
#right .body {
|
18 |
+
border-style: dotted; }
|
19 |
+
#right .footer {
|
20 |
+
border-style: dashed; }
|
21 |
+
|
22 |
+
|
23 |
+
body[bgproperties] {
|
24 |
+
outline: 2px solid blue; }
|
lib/phpsass/tests/nested.sass
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#main
|
2 |
+
:width 15em
|
3 |
+
:color #0000ff
|
4 |
+
p
|
5 |
+
:border
|
6 |
+
:style dotted
|
7 |
+
:width 2px
|
8 |
+
.cool
|
9 |
+
:width 100px
|
10 |
+
|
11 |
+
#left
|
12 |
+
:font
|
13 |
+
:size 2em
|
14 |
+
:weight bold
|
15 |
+
:float left
|
16 |
+
|
17 |
+
#right
|
18 |
+
.header
|
19 |
+
:border-style solid
|
20 |
+
.body
|
21 |
+
:border-style dotted
|
22 |
+
.footer
|
23 |
+
:border-style dashed
|
24 |
+
|
25 |
+
|
26 |
+
body
|
27 |
+
[bgproperties]
|
28 |
+
outline: 2px solid blue
|
lib/phpsass/tests/nested_bork1.sass
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
1 |
+
|
2 |
+
@import bork1
|
lib/phpsass/tests/nested_bork2.sass
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
1 |
+
|
2 |
+
@import bork2
|
lib/phpsass/tests/nested_bork3.sass
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
1 |
+
|
2 |
+
@import bork3
|
lib/phpsass/tests/nested_bork4.sass
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
1 |
+
|
2 |
+
@import bork4
|
lib/phpsass/tests/nested_bork5.sass
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
1 |
+
|
2 |
+
@import bork5
|
lib/phpsass/tests/nested_import.css
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.foo body {
|
2 |
+
font: Arial;
|
3 |
+
background: blue; }
|
4 |
+
.foo #page {
|
5 |
+
width: 700px;
|
6 |
+
height: 100;
|
7 |
+
line-height: 100%; }
|
8 |
+
.foo #page #header {
|
9 |
+
height: 300px; }
|
10 |
+
.foo #page #header h1 {
|
11 |
+
font-size: 50px;
|
12 |
+
color: blue; }
|
13 |
+
.foo #content.user.show #container.top #column.left {
|
14 |
+
width: 100px; }
|
15 |
+
.foo #content.user.show #container.top #column.right {
|
16 |
+
width: 600px; }
|
17 |
+
.foo #content.user.show #container.bottom {
|
18 |
+
background: brown; }
|
lib/phpsass/tests/nested_import.sass
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
1 |
+
.foo
|
2 |
+
@import basic
|
lib/phpsass/tests/nested_media.css
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
@media only screen and (max-width: 480px) {
|
2 |
+
a b {
|
3 |
+
one: two; }
|
4 |
+
a b c {
|
5 |
+
three: four; } }
|
lib/phpsass/tests/nested_media.scss
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
@media only screen and (max-width: 480px) {
|
2 |
+
a {
|
3 |
+
b {
|
4 |
+
one: two;
|
5 |
+
c {
|
6 |
+
three: four;
|
7 |
+
}
|
8 |
+
}
|
9 |
+
}
|
10 |
+
}
|
lib/phpsass/tests/nested_mixin_bork.sass
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
|
3 |
+
=error-mixin
|
4 |
+
width: 1px * 1em
|
5 |
+
|
6 |
+
@import mixin_bork
|
lib/phpsass/tests/nested_pseudo.css
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
1 |
+
div :first-child,
|
2 |
+
div h1 + * {
|
3 |
+
border: 0; }
|
lib/phpsass/tests/nested_pseudo.scss
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// Covers issue #13
|
2 |
+
div {
|
3 |
+
:first-child,
|
4 |
+
h1 + * {
|
5 |
+
border: 0;
|
6 |
+
}
|
7 |
+
}
|
lib/phpsass/tests/number.css
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#number {
|
2 |
+
font-size: 50%;
|
3 |
+
font-size: 1;
|
4 |
+
font-size: 0;
|
5 |
+
font-size: 1;
|
6 |
+
font-size: 1;
|
7 |
+
font-size: 0;
|
8 |
+
font-size: 0.5;
|
9 |
+
font-size: 0.5; }
|
lib/phpsass/tests/number.scss
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#number {
|
2 |
+
font-size: percentage(.5);
|
3 |
+
font-size: round(.5);
|
4 |
+
font-size: round(.4);
|
5 |
+
font-size: round(.6);
|
6 |
+
font-size: ceil(.5);
|
7 |
+
font-size: floor(.5);
|
8 |
+
font-size: abs(.5);
|
9 |
+
font-size: abs(-.5);
|
10 |
+
}
|
lib/phpsass/tests/opacity.css
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#opacity {
|
2 |
+
background: 0.5;
|
3 |
+
background: 0.5;
|
4 |
+
background: rgba(242, 133, 0, 0.7);
|
5 |
+
background: rgba(242, 133, 0, 0.7);
|
6 |
+
background: rgba(242, 133, 0, 0.5);
|
7 |
+
background: rgba(242, 133, 0, 0.5); }
|
lib/phpsass/tests/opacity.scss
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#opacity {
|
2 |
+
background: alpha(rgba(#f28500, .5));
|
3 |
+
background: opacity(rgba(#f28500, .5));
|
4 |
+
background: opacify(rgba(#f28500, .2), .5);
|
5 |
+
background: fade-in(rgba(#f28500, .2), .5);
|
6 |
+
background: transparentize(#f28500, .5);
|
7 |
+
background: fade-out(#f28500, .5);
|
8 |
+
}
|
lib/phpsass/tests/other-color.css
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#other-color {
|
2 |
+
background: #f28500;
|
3 |
+
background: #ff920e;
|
4 |
+
background: rgba(242, 133, 0, 0.5); }
|
5 |
+
|
lib/phpsass/tests/other-color.scss
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#other-color {
|
2 |
+
background: adjust-color(#f28500, $green: .1, $red: .2, $blue: .3, $alpha: .4);
|
3 |
+
background: scale-color(#f28500, $lightness: 10%);
|
4 |
+
background: change-color(#f28500, $alpha: .5);
|
5 |
+
}
|
lib/phpsass/tests/parent_ref.css
ADDED
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
a {
|
2 |
+
color: black; }
|
3 |
+
a:hover {
|
4 |
+
color: red; }
|
5 |
+
|
6 |
+
p,
|
7 |
+
div {
|
8 |
+
width: 100em; }
|
9 |
+
p foo,
|
10 |
+
div foo {
|
11 |
+
width: 10em; }
|
12 |
+
p bar,
|
13 |
+
div bar,
|
14 |
+
p:hover,
|
15 |
+
div:hover {
|
16 |
+
height: 20em; }
|
17 |
+
|
18 |
+
#cool {
|
19 |
+
border-style: solid;
|
20 |
+
border-width: 2em; }
|
21 |
+
.ie7 #cool,
|
22 |
+
.ie6 #cool {
|
23 |
+
content: string("Totally not cool."); }
|
24 |
+
.firefox #cool {
|
25 |
+
content: string("Quite cool."); }
|
26 |
+
|
27 |
+
.wow,
|
28 |
+
.snazzy {
|
29 |
+
font-family: fantasy; }
|
30 |
+
.wow:hover,
|
31 |
+
.snazzy:hover,
|
32 |
+
.wow:visited,
|
33 |
+
.snazzy:visited {
|
34 |
+
font-weight: bold; }
|
35 |
+
|
36 |
+
input[type="search"] {
|
37 |
+
-webkit-appearance: textfield; }
|
38 |
+
input[type="search"]::-webkit-search-decoration {
|
39 |
+
-webkit-appearance: none; }
|
lib/phpsass/tests/parent_ref.sass
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
a
|
2 |
+
:color #000
|
3 |
+
&:hover
|
4 |
+
:color #f00
|
5 |
+
|
6 |
+
p, div
|
7 |
+
:width 100em
|
8 |
+
& foo
|
9 |
+
:width 10em
|
10 |
+
&:hover, bar
|
11 |
+
:height 20em
|
12 |
+
|
13 |
+
#cool
|
14 |
+
:border
|
15 |
+
:style solid
|
16 |
+
:width 2em
|
17 |
+
.ie7 &, .ie6 &
|
18 |
+
:content string("Totally not cool.")
|
19 |
+
.firefox &
|
20 |
+
:content string("Quite cool.")
|
21 |
+
|
22 |
+
.wow, .snazzy
|
23 |
+
:font-family fantasy
|
24 |
+
&:hover, &:visited
|
25 |
+
:font-weight bold
|
26 |
+
|
27 |
+
input[type="search"]
|
28 |
+
-webkit-appearance: textfield
|
29 |
+
&::-webkit-search-decoration
|
30 |
+
-webkit-appearance: none
|
lib/phpsass/tests/phpSassTest.php
ADDED
@@ -0,0 +1,312 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* PHP Sass tests.
|
5 |
+
* @group sass
|
6 |
+
*/
|
7 |
+
class PHPSass_TestCase extends PHPUnit_Framework_TestCase {
|
8 |
+
|
9 |
+
/**
|
10 |
+
* This is the path to a directory of SASS, SCSS and CSS files used in tests.
|
11 |
+
*/
|
12 |
+
var $css_tests_path;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* This is the location of the PHPSass library being used.
|
16 |
+
*/
|
17 |
+
var $phpsass_library_path;
|
18 |
+
|
19 |
+
protected function setUp() {
|
20 |
+
parent::setUp();
|
21 |
+
|
22 |
+
$this->requirePHPSassLibrary();
|
23 |
+
$this->css_tests_path = dirname(__FILE__);
|
24 |
+
}
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Require the PHPSass Library.
|
28 |
+
*
|
29 |
+
* We try to include it from the local site if it's around, otherwise we try a
|
30 |
+
* few known locations, and then failing all of that we fall back to
|
31 |
+
* downloading it from the web.
|
32 |
+
*/
|
33 |
+
protected function requirePHPSassLibrary() {
|
34 |
+
|
35 |
+
// Allow people to specify the library before we are called.
|
36 |
+
if (isset($this->phpsass_library_path)) {
|
37 |
+
|
38 |
+
}
|
39 |
+
// Try to use libraries first.
|
40 |
+
elseif (($library_path = dirname(__FILE__) . '/..') && file_exists($library_path . '/SassParser.php')) {
|
41 |
+
$this->phpsass_library_path = $library_path;
|
42 |
+
}
|
43 |
+
|
44 |
+
if (isset($this->phpsass_library_path)) {
|
45 |
+
require_once($this->phpsass_library_path . '/SassParser.php');
|
46 |
+
}
|
47 |
+
else {
|
48 |
+
throw new Exception('Could not find PHPSass compiler.');
|
49 |
+
}
|
50 |
+
}
|
51 |
+
|
52 |
+
protected function runSassTest($input, $output = FALSE, $settings = array()) {
|
53 |
+
$name = $input;
|
54 |
+
|
55 |
+
$path = $this->css_tests_path;
|
56 |
+
$output = $path . '/' . ($output ? $output : preg_replace('/\..+$/', '.css', $input));
|
57 |
+
$input = $path . '/' . $input;
|
58 |
+
|
59 |
+
if (!file_exists($input)) {
|
60 |
+
return $this->fail('Input file not found - ' . $input);
|
61 |
+
}
|
62 |
+
if (!file_exists($output)) {
|
63 |
+
return $this->fail('Comparison file not found - ' . $output);
|
64 |
+
}
|
65 |
+
|
66 |
+
$syntax = explode('.', $input);
|
67 |
+
$syntax = array_pop($syntax);
|
68 |
+
$settings = $settings + array(
|
69 |
+
'style' => 'nested',
|
70 |
+
'cache' => FALSE,
|
71 |
+
'syntax' => $syntax,
|
72 |
+
'debug' => FALSE,
|
73 |
+
'debug_info' => FALSE,
|
74 |
+
'callbacks' => array(
|
75 |
+
'debug' => array($this, 'sassParserDebug'),
|
76 |
+
'warn' => array($this, 'sassParserWarning'),
|
77 |
+
),
|
78 |
+
);
|
79 |
+
$parser = new SassParser($settings);
|
80 |
+
$result = $parser->toCss($input);
|
81 |
+
|
82 |
+
$compare = file_get_contents($output);
|
83 |
+
if ($compare === FALSE) {
|
84 |
+
$this->fail('Unable to load comparison file - ' . $compare);
|
85 |
+
}
|
86 |
+
|
87 |
+
$_result = $this->trimResult($result);
|
88 |
+
$_compare = $this->trimResult($compare);
|
89 |
+
|
90 |
+
$this->assertEquals($_result, $_compare, 'Result for ' . $name . ' did not match comparison file');
|
91 |
+
}
|
92 |
+
|
93 |
+
/**
|
94 |
+
* Logging callback for PHPSass debug messages.
|
95 |
+
*/
|
96 |
+
public function sassParserDebug($message, $context) {
|
97 |
+
|
98 |
+
}
|
99 |
+
|
100 |
+
/**
|
101 |
+
* Logging callback for PHPSass warning messages.
|
102 |
+
*/
|
103 |
+
public function sassParserWarning($message, $context) {
|
104 |
+
|
105 |
+
}
|
106 |
+
|
107 |
+
protected function trimResult(&$input) {
|
108 |
+
$trim = preg_replace('/[\s;]+/', '', $input);
|
109 |
+
$trim = preg_replace('/\/\*.+?\*\//m', '', $trim);
|
110 |
+
return $trim;
|
111 |
+
}
|
112 |
+
|
113 |
+
public function testAlt() {
|
114 |
+
$this->runSassTest('alt.sass');
|
115 |
+
$this->runSassTest('alt.scss');
|
116 |
+
}
|
117 |
+
|
118 |
+
public function testBasic() {
|
119 |
+
$this->runSassTest('basic.sass');
|
120 |
+
}
|
121 |
+
|
122 |
+
|
123 |
+
public function testComments() {
|
124 |
+
$this->runSassTest('comments.sass');
|
125 |
+
}
|
126 |
+
|
127 |
+
public function testCompact() {
|
128 |
+
$this->runSassTest('compact.sass');
|
129 |
+
}
|
130 |
+
|
131 |
+
public function testComplex() {
|
132 |
+
$this->runSassTest('complex.sass');
|
133 |
+
}
|
134 |
+
|
135 |
+
public function testCompressed() {
|
136 |
+
$this->runSassTest('compressed.sass');
|
137 |
+
}
|
138 |
+
|
139 |
+
public function testContent() {
|
140 |
+
$this->runSassTest('content.scss');
|
141 |
+
}
|
142 |
+
|
143 |
+
public function testCss3() {
|
144 |
+
$this->runSassTest('css3.scss');
|
145 |
+
}
|
146 |
+
|
147 |
+
public function testDefault() {
|
148 |
+
$this->runSassTest('default.sass');
|
149 |
+
}
|
150 |
+
|
151 |
+
public function testEach() {
|
152 |
+
$this->runSassTest('each.scss');
|
153 |
+
}
|
154 |
+
|
155 |
+
public function testExpanded() {
|
156 |
+
$this->runSassTest('expanded.sass');
|
157 |
+
}
|
158 |
+
|
159 |
+
public function testExtend() {
|
160 |
+
$this->runSassTest('extend.sass');
|
161 |
+
}
|
162 |
+
|
163 |
+
public function testExtendPlaceholders() {
|
164 |
+
$this->runSassTest('extend_placeholders.scss');
|
165 |
+
}
|
166 |
+
|
167 |
+
public function testFilters() {
|
168 |
+
$this->runSassTest('filters.scss');
|
169 |
+
}
|
170 |
+
|
171 |
+
public function testFunctions() {
|
172 |
+
$this->runSassTest('functions.scss');
|
173 |
+
}
|
174 |
+
|
175 |
+
public function testHolmes() {
|
176 |
+
$this->runSassTest('holmes.sass');
|
177 |
+
}
|
178 |
+
|
179 |
+
public function testHSLFunction() {
|
180 |
+
$this->runSassTest('hsl-functions.scss');
|
181 |
+
}
|
182 |
+
|
183 |
+
public function testIf() {
|
184 |
+
$this->runSassTest('if.sass');
|
185 |
+
}
|
186 |
+
|
187 |
+
public function testImportedContent() {
|
188 |
+
$this->runSassTest('import_content.sass');
|
189 |
+
}
|
190 |
+
|
191 |
+
public function testInterpolation() {
|
192 |
+
$this->runSassTest('interpolation.scss');
|
193 |
+
}
|
194 |
+
|
195 |
+
public function testIntrospection() {
|
196 |
+
$this->runSassTest('introspection.scss');
|
197 |
+
}
|
198 |
+
|
199 |
+
public function testImport() {
|
200 |
+
$this->runSassTest('import.sass');
|
201 |
+
}
|
202 |
+
|
203 |
+
public function testLineNumbers() {
|
204 |
+
$this->runSassTest('line_numbers.sass');
|
205 |
+
}
|
206 |
+
|
207 |
+
public function testList() {
|
208 |
+
$this->runSassTest('list.scss');
|
209 |
+
}
|
210 |
+
|
211 |
+
public function testMedia() {
|
212 |
+
$this->runSassTest('media.scss');
|
213 |
+
}
|
214 |
+
|
215 |
+
public function testMiscFunctions() {
|
216 |
+
$this->runSassTest('misc-functions.scss');
|
217 |
+
}
|
218 |
+
|
219 |
+
public function testMisc() {
|
220 |
+
$this->runSassTest('misc.scss');
|
221 |
+
}
|
222 |
+
|
223 |
+
public function testMixinContent() {
|
224 |
+
$this->runSassTest('mixin-content.sass');
|
225 |
+
$this->runSassTest('mixin-content.scss');
|
226 |
+
}
|
227 |
+
|
228 |
+
public function testMixinJa1() {
|
229 |
+
$this->runSassTest('mixin-ja1.sass');
|
230 |
+
}
|
231 |
+
|
232 |
+
public function testMixinParams() {
|
233 |
+
$this->runSassTest('mixin-params.scss');
|
234 |
+
}
|
235 |
+
|
236 |
+
public function testMixins() {
|
237 |
+
$this->runSassTest('mixins.sass');
|
238 |
+
}
|
239 |
+
|
240 |
+
public function testMultiline() {
|
241 |
+
$this->runSassTest('multiline.sass');
|
242 |
+
}
|
243 |
+
|
244 |
+
public function testNestedImport() {
|
245 |
+
$this->runSassTest('nested_import.sass');
|
246 |
+
}
|
247 |
+
|
248 |
+
public function testNested() {
|
249 |
+
$this->runSassTest('nested.sass');
|
250 |
+
}
|
251 |
+
|
252 |
+
public function testNestedMedia() {
|
253 |
+
$this->runSassTest('nested_media.scss');
|
254 |
+
}
|
255 |
+
|
256 |
+
public function testNestedPseudo() {
|
257 |
+
$this->runSassTest('nested_pseudo.scss');
|
258 |
+
}
|
259 |
+
|
260 |
+
public function testNumber() {
|
261 |
+
$this->runSassTest('number.scss');
|
262 |
+
}
|
263 |
+
|
264 |
+
public function testOpacity() {
|
265 |
+
$this->runSassTest('opacity.scss');
|
266 |
+
}
|
267 |
+
|
268 |
+
public function testOtherColor() {
|
269 |
+
$this->runSassTest('other-color.scss');
|
270 |
+
}
|
271 |
+
|
272 |
+
public function testParentRef() {
|
273 |
+
$this->runSassTest('parent_ref.sass');
|
274 |
+
}
|
275 |
+
|
276 |
+
public function testProprietarySelector() {
|
277 |
+
$this->runSassTest('proprietary-selector.scss');
|
278 |
+
}
|
279 |
+
|
280 |
+
public function testRGBFunctions() {
|
281 |
+
$this->runSassTest('rgb-functions.scss');
|
282 |
+
}
|
283 |
+
|
284 |
+
public function testScssImportee() {
|
285 |
+
$this->runSassTest('scss_importee.scss');
|
286 |
+
}
|
287 |
+
|
288 |
+
public function testScssImport() {
|
289 |
+
$this->runSassTest('scss_import.scss');
|
290 |
+
}
|
291 |
+
|
292 |
+
public function testSplats() {
|
293 |
+
$this->runSassTest('splats.scss');
|
294 |
+
}
|
295 |
+
|
296 |
+
public function testString() {
|
297 |
+
$this->runSassTest('string.scss');
|
298 |
+
}
|
299 |
+
|
300 |
+
public function testUnits() {
|
301 |
+
$this->runSassTest('units.sass');
|
302 |
+
}
|
303 |
+
|
304 |
+
public function testWarnImported() {
|
305 |
+
$this->markTestIncomplete('This test has not been implemented yet.');
|
306 |
+
//$this->runSassTest('warn_imported.sass');
|
307 |
+
}
|
308 |
+
|
309 |
+
public function testWarn() {
|
310 |
+
$this->runSassTest('warn.sass');
|
311 |
+
}
|
312 |
+
}
|
lib/phpsass/tests/phpunit.xml.dist
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!-- Copy and rename to phpunit.xml. Customize as needed. -->
|
2 |
+
<phpunit backupGlobals="false"
|
3 |
+
backupStaticAttributes="false"
|
4 |
+
syntaxCheck="false">
|
5 |
+
<testsuites>
|
6 |
+
<testsuite name="PHPSass Test Suite">
|
7 |
+
<directory>.</directory>
|
8 |
+
</testsuite>
|
9 |
+
</testsuites>
|
10 |
+
|
11 |
+
<php>
|
12 |
+
<includePath>.</includePath>
|
13 |
+
</php>
|
14 |
+
</phpunit>
|
lib/phpsass/tests/proprietary-selector.css
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
1 |
+
input:-moz-placeholder {
|
2 |
+
color: #cccccc; }
|
3 |
+
input::-webkit-input-placeholder {
|
4 |
+
color: #cccccc; }
|
lib/phpsass/tests/proprietary-selector.scss
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
input {
|
2 |
+
&:-moz-placeholder {
|
3 |
+
color: #ccc;
|
4 |
+
}
|
5 |
+
&::-webkit-input-placeholder {
|
6 |
+
color: #ccc;
|
7 |
+
}
|
8 |
+
}
|
lib/phpsass/tests/rgb-functions.css
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#rgb {
|
2 |
+
background-color: #fff36c;
|
3 |
+
background-color: rgba(255, 243, 108, 0.5);
|
4 |
+
background-color: rgba(255, 250, 43, 0.5);
|
5 |
+
background-color: 255;
|
6 |
+
background-color: 250;
|
7 |
+
background-color: 43;
|
8 |
+
background-color: #fff64b; }
|
lib/phpsass/tests/rgb-functions.scss
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#rgb {
|
2 |
+
background-color: rgb(255, 243, 108);
|
3 |
+
background-color: rgba(255, 243, 108, .5);
|
4 |
+
background-color: rgba(#fffa2b, .5);
|
5 |
+
background-color: red(#fffa2b);
|
6 |
+
background-color: green(#fffa2b);
|
7 |
+
background-color: blue(#fffa2b);
|
8 |
+
background-color: mix(#fffa2b, rgb(255, 243, 108));
|
9 |
+
}
|
lib/phpsass/tests/scss_import.css
ADDED
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
imported {
|
2 |
+
otherconst: hello;
|
3 |
+
myconst: goodbye;
|
4 |
+
pre-mixin: here; }
|
5 |
+
|
6 |
+
body {
|
7 |
+
font: Arial;
|
8 |
+
background: blue; }
|
9 |
+
|
10 |
+
#page {
|
11 |
+
width: 700px;
|
12 |
+
height: 100;
|
13 |
+
line-height: 100%; }
|
14 |
+
#page #header {
|
15 |
+
height: 300px; }
|
16 |
+
#page #header h1 {
|
17 |
+
font-size: 50px;
|
18 |
+
color: blue; }
|
19 |
+
|
20 |
+
#content.user.show #container.top #column.left {
|
21 |
+
width: 100px; }
|
22 |
+
#content.user.show #container.top #column.right {
|
23 |
+
width: 600px; }
|
24 |
+
#content.user.show #container.bottom {
|
25 |
+
background: brown; }
|
26 |
+
|
27 |
+
midrule {
|
28 |
+
inthe: middle; }
|
29 |
+
|
30 |
+
scss {
|
31 |
+
imported: yes; }
|
32 |
+
|
33 |
+
body {
|
34 |
+
font: Arial;
|
35 |
+
background: blue; }
|
36 |
+
|
37 |
+
#page {
|
38 |
+
width: 700px;
|
39 |
+
height: 100;
|
40 |
+
line-height: 100%; }
|
41 |
+
#page #header {
|
42 |
+
height: 300px; }
|
43 |
+
#page #header h1 {
|
44 |
+
font-size: 50px;
|
45 |
+
color: blue; }
|
46 |
+
|
47 |
+
#content.user.show #container.top #column.left {
|
48 |
+
width: 100px; }
|
49 |
+
#content.user.show #container.top #column.right {
|
50 |
+
width: 600px; }
|
51 |
+
#content.user.show #container.bottom {
|
52 |
+
background: brown; }
|
53 |
+
|
54 |
+
@import url('basic.css');
|
55 |
+
@import url('../results/complex.css');
|
56 |
+
#foo {
|
57 |
+
background-color: #bbaaff; }
|
58 |
+
|
59 |
+
nonimported {
|
60 |
+
myconst: hello;
|
61 |
+
otherconst: goodbye;
|
62 |
+
post-mixin: here; }
|
lib/phpsass/tests/scss_import.scss
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
$preconst: hello;
|
2 |
+
|
3 |
+
@mixin premixin {pre-mixin: here}
|
4 |
+
|
5 |
+
@import "importee.sass";
|
6 |
+
@import "scss_importee";
|
7 |
+
@import "basic.sass";
|
8 |
+
@import "basic.css";
|
9 |
+
@import "../results/complex.css";
|
10 |
+
@import "partial.sass";
|
11 |
+
|
12 |
+
nonimported {
|
13 |
+
myconst: $preconst;
|
14 |
+
otherconst: $postconst;
|
15 |
+
@include postmixin; }
|
lib/phpsass/tests/scss_importee.css
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
1 |
+
scss {
|
2 |
+
imported: yes; }
|
lib/phpsass/tests/scss_importee.scss
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
scss {imported: yes}
|
lib/phpsass/tests/splats.css
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
fn {
|
2 |
+
three: 1, 2, 3;
|
3 |
+
four: 1, 2, 3, 4; }
|
4 |
+
box {
|
5 |
+
box-shadow: 0 0 0 black, inset 5px 30px 10px red; }
|
lib/phpsass/tests/splats.scss
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
@function test($in...) {
|
2 |
+
@return $in;
|
3 |
+
}
|
4 |
+
|
5 |
+
fn {
|
6 |
+
three: test(1,2,3);
|
7 |
+
|
8 |
+
$foo: 1, 2, 3, 4;
|
9 |
+
four: test($foo...);
|
10 |
+
}
|
11 |
+
|
12 |
+
@mixin box-shadow($shadows...) {
|
13 |
+
box-shadow: $shadows;
|
14 |
+
}
|
15 |
+
|
16 |
+
box {
|
17 |
+
$shadows: 0 0 0 #000, inset 5px 30px 10px red;
|
18 |
+
@include box-shadow($shadows...);
|
19 |
+
}
|
lib/phpsass/tests/string.css
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
1 |
+
#string {
|
2 |
+
content: banana;
|
3 |
+
content: "pineapple"; }
|
lib/phpsass/tests/string.scss
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#string {
|
2 |
+
$banana: "banana";
|
3 |
+
$pineapple: pineapple;
|
4 |
+
content: unquote($banana);
|
5 |
+
content: quote($pineapple);
|
6 |
+
}
|
lib/phpsass/tests/subdir/nested_subdir/_nested_partial.sass
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
1 |
+
#nested
|
2 |
+
:relative true
|
lib/phpsass/tests/subdir/nested_subdir/nested_subdir.css
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
#pi { width: 314px; }
|
lib/phpsass/tests/subdir/nested_subdir/nested_subdir.sass
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
1 |
+
#pi
|
2 |
+
:width 314px
|
3 |
+
|
lib/phpsass/tests/subdir/subdir.css
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
1 |
+
#nested { relative: true; }
|
2 |
+
|
3 |
+
#subdir { font-size: 20px; font-weight: bold; }
|
lib/phpsass/tests/subdir/subdir.sass
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
@import nested_subdir/nested_partial.sass
|
2 |
+
|
3 |
+
#subdir
|
4 |
+
:font
|
5 |
+
:size 20px
|
6 |
+
:weight bold
|
lib/phpsass/tests/units.css
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
b {
|
2 |
+
foo: 5px;
|
3 |
+
bar: 24px;
|
4 |
+
baz: 66.667%;
|
5 |
+
many-units: 32em;
|
6 |
+
mm: 15mm;
|
7 |
+
pc: 2pc;
|
8 |
+
pt: -72pt;
|
9 |
+
inches: 2in;
|
10 |
+
more-inches: 3.5in;
|
11 |
+
mixed: 6px;
|
12 |
+
rem: 37rem; }
|
lib/phpsass/tests/units.sass
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
b
|
2 |
+
:foo 0.5 * 10px
|
3 |
+
:bar 10zzz * 12px / 5zzz
|
4 |
+
:baz percentage(12.0px / 18px)
|
5 |
+
:many-units 10.0zzz / 3yyy * 12px / 5zzz * 3yyy / 3px * 4em
|
6 |
+
:mm 5mm + 1cm
|
7 |
+
:pc 1pc + 12pt
|
8 |
+
:pt 72pt - 2in
|
9 |
+
:inches 1in + 2.54cm
|
10 |
+
:more-inches 1in + ((72pt * 2in) + (36pt * 1in)) / 2.54cm
|
11 |
+
:mixed (1 + (1em * 6px / 3in)) * 4in / 2em
|
12 |
+
:rem 34rem + 3rem
|
lib/phpsass/tests/warn.css
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
/* @warn: In the main file *//* @warn: Imported *//* @warn: In an imported mixin */
|
lib/phpsass/tests/warn.sass
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
1 |
+
@debug 1em + 2em
|
2 |
+
@warn "In the main file"
|
3 |
+
@import warn_imported.sass
|
4 |
+
+emits-a-warning
|
lib/phpsass/tests/warn_imported.sass
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
1 |
+
@warn "Imported"
|
2 |
+
|
3 |
+
=emits-a-warning
|
4 |
+
@warn "In an imported mixin"
|
lib/phpsass/tree/SassCommentNode.php
ADDED
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* SVN FILE: $Id$ */
|
3 |
+
/**
|
4 |
+
* SassCommentNode class file.
|
5 |
+
* @author Chris Yates <chris.l.yates@gmail.com>
|
6 |
+
* @copyright Copyright (c) 2010 PBM Web Development
|
7 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
8 |
+
* @package PHamlP
|
9 |
+
* @subpackage Sass.tree
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* SassCommentNode class.
|
14 |
+
* Represents a CSS comment.
|
15 |
+
* @package PHamlP
|
16 |
+
* @subpackage Sass.tree
|
17 |
+
*/
|
18 |
+
class SassCommentNode extends SassNode {
|
19 |
+
const NODE_IDENTIFIER = '/';
|
20 |
+
const MATCH = '%^/\*\s*?(.*?)\s*?(\*/)?$%s';
|
21 |
+
const COMMENT = 1;
|
22 |
+
|
23 |
+
private $value;
|
24 |
+
|
25 |
+
/**
|
26 |
+
* SassCommentNode constructor.
|
27 |
+
* @param object source token
|
28 |
+
* @return CommentNode
|
29 |
+
*/
|
30 |
+
public function __construct($token) {
|
31 |
+
parent::__construct($token);
|
32 |
+
preg_match(self::MATCH, $token->source, $matches);
|
33 |
+
$this->value = $matches[self::COMMENT];
|
34 |
+
}
|
35 |
+
|
36 |
+
protected function getValue() {
|
37 |
+
return $this->value;
|
38 |
+
}
|
39 |
+
|
40 |
+
/**
|
41 |
+
* Parse this node.
|
42 |
+
* @return array the parsed node - an empty array
|
43 |
+
*/
|
44 |
+
public function parse($context) {
|
45 |
+
return array($this);
|
46 |
+
}
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Render this node.
|
50 |
+
* @return string the rendered node
|
51 |
+
*/
|
52 |
+
public function render() {
|
53 |
+
return $this->renderer->renderComment($this);
|
54 |
+
}
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Returns a value indicating if the token represents this type of node.
|
58 |
+
* @param object token
|
59 |
+
* @return boolean true if the token represents this type of node, false if not
|
60 |
+
*/
|
61 |
+
public static function isa($token) {
|
62 |
+
return $token->source[0] === self::NODE_IDENTIFIER;
|
63 |
+
}
|
64 |
+
}
|
lib/phpsass/tree/SassContentNode.php
ADDED
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* SVN FILE: $Id$ */
|
3 |
+
/**
|
4 |
+
* SassContentNode class file.
|
5 |
+
* @author Richard Lyon
|
6 |
+
* @copyright Copyright (c) 2010 PBM Web Development
|
7 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
8 |
+
* @package PHamlP
|
9 |
+
* @subpackage Sass.tree
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* SassContentNode class.
|
14 |
+
* Represents a Content.
|
15 |
+
* @package PHamlP
|
16 |
+
* @subpackage Sass.tree
|
17 |
+
*/
|
18 |
+
class SassContentNode extends SassNode {
|
19 |
+
const MATCH = '/^(@content)(.*)$/i';
|
20 |
+
const IDENTIFIER = 1;
|
21 |
+
|
22 |
+
/**
|
23 |
+
* @var statement to execute and return
|
24 |
+
*/
|
25 |
+
private $statement;
|
26 |
+
|
27 |
+
/**
|
28 |
+
* SassContentNode constructor.
|
29 |
+
* @param object source token
|
30 |
+
* @return SassContentNode
|
31 |
+
*/
|
32 |
+
public function __construct($token) {
|
33 |
+
parent::__construct($token);
|
34 |
+
preg_match(self::MATCH, $token->source, $matches);
|
35 |
+
|
36 |
+
if (empty($matches)) {
|
37 |
+
return new SassBoolean('false');
|
38 |
+
}
|
39 |
+
}
|
40 |
+
|
41 |
+
/**
|
42 |
+
* Parse this node.
|
43 |
+
* Set passed arguments and any optional arguments not passed to their
|
44 |
+
* defaults, then render the children of the return definition.
|
45 |
+
* @param SassContext the context in which this node is parsed
|
46 |
+
* @return array the parsed node
|
47 |
+
*/
|
48 |
+
public function parse($pcontext) {
|
49 |
+
$return = $this;
|
50 |
+
$context = new SassContext($pcontext);
|
51 |
+
return ($context->getContent());
|
52 |
+
}
|
53 |
+
|
54 |
+
/**
|
55 |
+
* Contents a value indicating if the token represents this type of node.
|
56 |
+
* @param object token
|
57 |
+
* @return boolean true if the token represents this type of node, false if not
|
58 |
+
*/
|
59 |
+
public static function isa($token) {
|
60 |
+
return $token->source[0] === self::NODE_IDENTIFIER;
|
61 |
+
}
|
62 |
+
}
|
lib/phpsass/tree/SassContext.php
ADDED
@@ -0,0 +1,198 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* SVN FILE: $Id$ */
|
3 |
+
/**
|
4 |
+
* SassContext class file.
|
5 |
+
* @author Chris Yates <chris.l.yates@gmail.com>
|
6 |
+
* @copyright Copyright (c) 2010 PBM Web Development
|
7 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
8 |
+
* @package PHamlP
|
9 |
+
* @subpackage Sass.tree
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* SassContext class.
|
14 |
+
* Defines the context that the parser is operating in and so allows variables
|
15 |
+
* to be scoped.
|
16 |
+
* A new context is created for Mixins and imported files.
|
17 |
+
* @package PHamlP
|
18 |
+
* @subpackage Sass.tree
|
19 |
+
*/
|
20 |
+
class SassContext {
|
21 |
+
/**
|
22 |
+
* @var SassContext enclosing context
|
23 |
+
*/
|
24 |
+
public $parent;
|
25 |
+
|
26 |
+
/**
|
27 |
+
* @var array mixins defined in this context
|
28 |
+
*/
|
29 |
+
public $mixins = array();
|
30 |
+
|
31 |
+
/**
|
32 |
+
* @var array mixins defined in this context
|
33 |
+
*/
|
34 |
+
public $functions = array();
|
35 |
+
|
36 |
+
/**
|
37 |
+
* @var array variables defined in this context
|
38 |
+
*/
|
39 |
+
public $variables = array();
|
40 |
+
|
41 |
+
/**
|
42 |
+
* @var tree representing any contextual content.
|
43 |
+
*/
|
44 |
+
public $content = array();
|
45 |
+
|
46 |
+
/**
|
47 |
+
* @var SassNode the node being processed
|
48 |
+
*/
|
49 |
+
public $node;
|
50 |
+
|
51 |
+
/**
|
52 |
+
* SassContext constructor.
|
53 |
+
* @param SassContext - the enclosing context
|
54 |
+
* @return SassContext
|
55 |
+
*/
|
56 |
+
public function __construct($parent = null) {
|
57 |
+
$this->parent = $parent;
|
58 |
+
}
|
59 |
+
|
60 |
+
/**
|
61 |
+
*
|
62 |
+
*/
|
63 |
+
public function getContent() {
|
64 |
+
if ($this->content) {
|
65 |
+
return $this->content;
|
66 |
+
}
|
67 |
+
if (!empty($this->parent)) {
|
68 |
+
return $this->parent->getContent();
|
69 |
+
}
|
70 |
+
throw new SassContextException('@content requested but no content passed', $this->node);
|
71 |
+
}
|
72 |
+
|
73 |
+
/**
|
74 |
+
* Adds a mixin
|
75 |
+
* @param string name of mixin
|
76 |
+
* @return SassMixinDefinitionNode the mixin
|
77 |
+
*/
|
78 |
+
public function addMixin($name, $mixin) {
|
79 |
+
$this->mixins[$name] = $mixin;
|
80 |
+
return $this;
|
81 |
+
}
|
82 |
+
|
83 |
+
/**
|
84 |
+
* Returns a mixin
|
85 |
+
* @param string name of mixin to return
|
86 |
+
* @return SassMixinDefinitionNode the mixin
|
87 |
+
* @throws SassContextException if mixin not defined in this context
|
88 |
+
*/
|
89 |
+
public function getMixin($name) {
|
90 |
+
if (isset($this->mixins[$name])) {
|
91 |
+
return $this->mixins[$name];
|
92 |
+
}
|
93 |
+
elseif (!empty($this->parent)) {
|
94 |
+
return $this->parent->getMixin($name);
|
95 |
+
}
|
96 |
+
throw new SassContextException('Undefined Mixin: ' . $name, $this->node);
|
97 |
+
}
|
98 |
+
|
99 |
+
/**
|
100 |
+
* Adds a function
|
101 |
+
* @param string name of function
|
102 |
+
* @return SassFunctionDefinitionNode the function
|
103 |
+
*/
|
104 |
+
public function addFunction($name, $function) {
|
105 |
+
$this->functions[$name] = $function;
|
106 |
+
if (!empty($this->parent)) {
|
107 |
+
$this->parent->addFunction($name);
|
108 |
+
}
|
109 |
+
return $this;
|
110 |
+
}
|
111 |
+
|
112 |
+
/**
|
113 |
+
* Returns a function
|
114 |
+
* @param string name of function to return
|
115 |
+
* @return SassFunctionDefinitionNode the mixin
|
116 |
+
* @throws SassContextException if function not defined in this context
|
117 |
+
*/
|
118 |
+
public function getFunction($name) {
|
119 |
+
if ($fn = $this->hasFunction($name)) {
|
120 |
+
return $fn;
|
121 |
+
}
|
122 |
+
throw new SassContextException('Undefined Function: ' . $name, $this->node);
|
123 |
+
}
|
124 |
+
|
125 |
+
/**
|
126 |
+
* Returns a boolean wether this function exists
|
127 |
+
* @param string name of function to check for
|
128 |
+
* @return boolean
|
129 |
+
*/
|
130 |
+
public function hasFunction($name) {
|
131 |
+
if (isset($this->functions[$name])) {
|
132 |
+
return $this->functions[$name];
|
133 |
+
} else if (!empty($this->parent)) {
|
134 |
+
return $this->parent->hasFunction($name);
|
135 |
+
}
|
136 |
+
return FALSE;
|
137 |
+
}
|
138 |
+
|
139 |
+
/**
|
140 |
+
* Returns a variable defined in this context
|
141 |
+
* @param string name of variable to return
|
142 |
+
* @return string the variable
|
143 |
+
* @throws SassContextException if variable not defined in this context
|
144 |
+
*/
|
145 |
+
public function getVariable($name) {
|
146 |
+
$name = str_replace('-', '_', $name);
|
147 |
+
if ($this->hasVariable($name)) {
|
148 |
+
return $this->variables[$name];
|
149 |
+
}
|
150 |
+
elseif (!empty($this->parent)) {
|
151 |
+
return $this->parent->getVariable($name);
|
152 |
+
}
|
153 |
+
else {
|
154 |
+
// Return false instead of throwing an exception.
|
155 |
+
// throw new SassContextException('Undefined Variable: ' . $name, $this->node);
|
156 |
+
return new SassBoolean('false');
|
157 |
+
}
|
158 |
+
}
|
159 |
+
|
160 |
+
/**
|
161 |
+
* Returns a value indicating if the variable exists in this context
|
162 |
+
* @param string name of variable to test
|
163 |
+
* @return boolean true if the variable exists in this context, false if not
|
164 |
+
*/
|
165 |
+
public function hasVariable($name) {
|
166 |
+
$name = str_replace('-', '_', $name);
|
167 |
+
return isset($this->variables[$name]);
|
168 |
+
}
|
169 |
+
|
170 |
+
/**
|
171 |
+
* Sets a variable to the given value
|
172 |
+
* @param string name of variable
|
173 |
+
* @param sassLiteral value of variable
|
174 |
+
*/
|
175 |
+
public function setVariable($name, $value) {
|
176 |
+
$name = str_replace('-', '_', $name);
|
177 |
+
$this->variables[$name] = $value;
|
178 |
+
return $this;
|
179 |
+
}
|
180 |
+
|
181 |
+
public function setVariables($vars) {
|
182 |
+
foreach ($vars as $key => $value) {
|
183 |
+
if ($value !== NULL) {
|
184 |
+
$this->setVariable($key, $value);
|
185 |
+
}
|
186 |
+
}
|
187 |
+
}
|
188 |
+
|
189 |
+
/**
|
190 |
+
* Makes variables and mixins from this context available in the parent context.
|
191 |
+
* Note that if there are variables or mixins with the same name in the two
|
192 |
+
* contexts they will be set to that defined in this context.
|
193 |
+
*/
|
194 |
+
public function merge() {
|
195 |
+
$this->parent->variables = array_merge($this->parent->variables, $this->variables);
|
196 |
+
$this->parent->mixins = array_merge($this->parent->mixins, $this->mixins);
|
197 |
+
}
|
198 |
+
}
|
lib/phpsass/tree/SassDebugNode.php
ADDED
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* SVN FILE: $Id: SassDebugNode.php 49 2010-04-04 10:51:24Z chris.l.yates $ */
|
3 |
+
/**
|
4 |
+
* SassDebugNode class file.
|
5 |
+
* @author Chris Yates <chris.l.yates@gmail.com>
|
6 |
+
* @copyright Copyright (c) 2010 PBM Web Development
|
7 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
8 |
+
* @package PHamlP
|
9 |
+
* @subpackage Sass.tree
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* SassDebugNode class.
|
14 |
+
* Represents a Sass @debug or @warn directive.
|
15 |
+
* @package PHamlP
|
16 |
+
* @subpackage Sass.tree
|
17 |
+
*/
|
18 |
+
class SassDebugNode extends SassNode {
|
19 |
+
const IDENTIFIER = '@';
|
20 |
+
const MATCH = '/^@(?:debug|warn)\s+(.+?)\s*;?$/';
|
21 |
+
const MESSAGE = 1;
|
22 |
+
|
23 |
+
/**
|
24 |
+
* @var string the debug/warning message
|
25 |
+
*/
|
26 |
+
private $message;
|
27 |
+
/**
|
28 |
+
* @var array parameters for the message;
|
29 |
+
* only used by internal warning messages
|
30 |
+
*/
|
31 |
+
private $params;
|
32 |
+
/**
|
33 |
+
* @var boolean true if this is a warning
|
34 |
+
*/
|
35 |
+
private $warning;
|
36 |
+
|
37 |
+
/**
|
38 |
+
* SassDebugNode.
|
39 |
+
* @param object source token
|
40 |
+
* @param mixed string: an internally generated warning message about the
|
41 |
+
* source
|
42 |
+
* boolean: the source token is a @debug or @warn directive containing the
|
43 |
+
* message; True if this is a @warn directive
|
44 |
+
* @param array parameters for the message
|
45 |
+
* @return SassDebugNode
|
46 |
+
*/
|
47 |
+
public function __construct($token, $message = false) {
|
48 |
+
parent::__construct($token);
|
49 |
+
if (is_string($message)) {
|
50 |
+
$this->message = $message;
|
51 |
+
$this->warning = true;
|
52 |
+
}
|
53 |
+
else {
|
54 |
+
preg_match(self::MATCH, $token->source, $matches);
|
55 |
+
$this->message = $matches[self::MESSAGE];
|
56 |
+
$this->warning = $message;
|
57 |
+
}
|
58 |
+
}
|
59 |
+
|
60 |
+
/**
|
61 |
+
* Parse this node.
|
62 |
+
* This raises an error.
|
63 |
+
* @return array An empty array
|
64 |
+
*/
|
65 |
+
public function parse($context) {
|
66 |
+
if (!$this->warning) {
|
67 |
+
$result = $this->evaluate($this->message, $context)->toString();
|
68 |
+
|
69 |
+
$cb = SassParser::$instance->options['callbacks']['debug'];
|
70 |
+
if ($cb) {
|
71 |
+
call_user_func($cb, $result, $context);
|
72 |
+
} else {
|
73 |
+
set_error_handler(array($this, 'errorHandler'));
|
74 |
+
trigger_error($result);
|
75 |
+
restore_error_handler();
|
76 |
+
}
|
77 |
+
}
|
78 |
+
|
79 |
+
return array();
|
80 |
+
}
|
81 |
+
|
82 |
+
/**
|
83 |
+
* Error handler for degug and warning statements.
|
84 |
+
* @param int Error number
|
85 |
+
* @param string Message
|
86 |
+
*/
|
87 |
+
public function errorHandler($errno, $message) {
|
88 |
+
echo '<div style="background-color:#ce4dd6;border-bottom:1px dashed #88338d;color:white;font:10pt verdana;margin:0;padding:0.5em 2%;width:96%;"><p style="height:auto;margin:0.25em 0;padding:0;width:100%;"><span style="font-weight:bold;">SASS '.($this->warning ? 'WARNING' : 'DEBUG').":</span> $message</p><p style=\"margin:0.1em;padding:0;padding-left:0.5em;width:100%;\">{$this->filename}::{$this->line}</p><p style=\"margin:0.1em;padding:0;padding-left:0.5em;width:100%;\">Source: {$this->source}</p></div>";
|
89 |
+
}
|
90 |
+
}
|
lib/phpsass/tree/SassDirectiveNode.php
ADDED
@@ -0,0 +1,116 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* SVN FILE: $Id$ */
|
3 |
+
/**
|
4 |
+
* SassDirectiveNode class file.
|
5 |
+
* @author Chris Yates <chris.l.yates@gmail.com>
|
6 |
+
* @copyright Copyright (c) 2010 PBM Web Development
|
7 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
8 |
+
* @package PHamlP
|
9 |
+
* @subpackage Sass.tree
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* SassDirectiveNode class.
|
14 |
+
* Represents a CSS directive.
|
15 |
+
* @package PHamlP
|
16 |
+
* @subpackage Sass.tree
|
17 |
+
*/
|
18 |
+
class SassDirectiveNode extends SassNode {
|
19 |
+
const NODE_IDENTIFIER = '@';
|
20 |
+
const MATCH = '/^(@[\w-]+)/';
|
21 |
+
|
22 |
+
const INTERPOLATION_MATCH = '/\$([\w-]+)/';
|
23 |
+
|
24 |
+
/**
|
25 |
+
* SassDirectiveNode.
|
26 |
+
* @param object source token
|
27 |
+
* @return SassDirectiveNode
|
28 |
+
*/
|
29 |
+
public function __construct($token) {
|
30 |
+
parent::__construct($token);
|
31 |
+
}
|
32 |
+
|
33 |
+
protected function getDirective() {
|
34 |
+
return $this->token->source;
|
35 |
+
preg_match('/^(@[\w-]+)(?:\s*(\w+))*/', $this->token->source, $matches);
|
36 |
+
array_shift($matches);
|
37 |
+
$parts = implode(' ', $matches);
|
38 |
+
return strtolower($parts);
|
39 |
+
}
|
40 |
+
|
41 |
+
/**
|
42 |
+
* Parse this node.
|
43 |
+
* @param SassContext the context in which this node is parsed
|
44 |
+
* @return array the parsed node
|
45 |
+
*/
|
46 |
+
public function parse($context) {
|
47 |
+
$this->token->source = self::interpolate_nonstrict($this->token->source, $context);
|
48 |
+
|
49 |
+
$this->children = $this->parseChildren($context);
|
50 |
+
return array($this);
|
51 |
+
}
|
52 |
+
|
53 |
+
/**
|
54 |
+
* Render this node.
|
55 |
+
* @return string the rendered node
|
56 |
+
*/
|
57 |
+
public function render() {
|
58 |
+
$properties = array();
|
59 |
+
foreach ($this->children as $child) {
|
60 |
+
$properties[] = $child->render();
|
61 |
+
} // foreach
|
62 |
+
|
63 |
+
return $this->renderer->renderDirective($this, $properties);
|
64 |
+
}
|
65 |
+
|
66 |
+
/**
|
67 |
+
* Returns a value indicating if the token represents this type of node.
|
68 |
+
* @param object token
|
69 |
+
* @return boolean true if the token represents this type of node, false if not
|
70 |
+
*/
|
71 |
+
public static function isa($token) {
|
72 |
+
return $token->source[0] === self::NODE_IDENTIFIER;
|
73 |
+
}
|
74 |
+
|
75 |
+
/**
|
76 |
+
* Returns the directive
|
77 |
+
* @param object token
|
78 |
+
* @return string the directive
|
79 |
+
*/
|
80 |
+
public static function extractDirective($token) {
|
81 |
+
|
82 |
+
preg_match(self::MATCH, $token->source, $matches);
|
83 |
+
return strtolower($matches[1]);
|
84 |
+
}
|
85 |
+
|
86 |
+
public static function interpolate_nonstrict($string, $context) {
|
87 |
+
for ($i = 0, $n = preg_match_all(self::INTERPOLATION_MATCH, $string, $matches); $i < $n; $i++) {
|
88 |
+
$var = SassScriptParser::$instance->evaluate($matches[0][$i], $context);
|
89 |
+
|
90 |
+
if ($var instanceOf SassString) {
|
91 |
+
$var = $var->value;
|
92 |
+
} else {
|
93 |
+
$var = $var->toString();
|
94 |
+
}
|
95 |
+
|
96 |
+
if(preg_match('/^unquote\((["\'])(.*)\1\)$/', $var, $match)){
|
97 |
+
$val = $match[2];
|
98 |
+
}
|
99 |
+
else if($var == '""'){
|
100 |
+
$val = "";
|
101 |
+
}
|
102 |
+
else if(preg_match('/^(["\'])(.*)\1$/', $var, $match)){
|
103 |
+
$val = $match[2];
|
104 |
+
}
|
105 |
+
else {
|
106 |
+
$val = $var;
|
107 |
+
}
|
108 |
+
$matches[1][$i] = $val;
|
109 |
+
}
|
110 |
+
$matches[0][] = '#{';
|
111 |
+
$matches[0][] = '}';
|
112 |
+
$matches[1][] = '';
|
113 |
+
$matches[1][] = '';
|
114 |
+
return str_replace($matches[0], $matches[1], $string);
|
115 |
+
}
|
116 |
+
}
|
lib/phpsass/tree/SassEachNode.php
ADDED
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* SVN FILE: $Id$ */
|
3 |
+
/**
|
4 |
+
* SassEachNode class file.
|
5 |
+
* The syntax is:
|
6 |
+
* <pre>@each <var> in <list><pre>.
|
7 |
+
*
|
8 |
+
* <list> is comma+space separated
|
9 |
+
* <var> is available to the rest of the script following evaluation
|
10 |
+
* and has the value that terminated the loop.
|
11 |
+
*
|
12 |
+
* @author Pavol (Lopo) Hluchy <lopo@losys.eu>
|
13 |
+
* @copyright Copyright (c) 2011 Lopo
|
14 |
+
* @license http://www.gnu.org/licenses/gpl.html GNU General Public License Version 3
|
15 |
+
* @package PHamlP
|
16 |
+
* @subpackage Sass.tree
|
17 |
+
*/
|
18 |
+
|
19 |
+
/**
|
20 |
+
* SassEachNode class.
|
21 |
+
* Represents a Sass @each loop.
|
22 |
+
* @package PHamlP
|
23 |
+
* @subpackage Sass.tree
|
24 |
+
*/
|
25 |
+
class SassEachNode extends SassNode {
|
26 |
+
const MATCH = '/@each\s+[!\$](.+?)in\s+(.+)$/i';
|
27 |
+
|
28 |
+
const VARIABLE = 1;
|
29 |
+
const IN = 2;
|
30 |
+
|
31 |
+
/**
|
32 |
+
* @var string variable name for the loop
|
33 |
+
*/
|
34 |
+
private $variable;
|
35 |
+
/**
|
36 |
+
* @var string expression that provides the loop values
|
37 |
+
*/
|
38 |
+
private $in;
|
39 |
+
|
40 |
+
/**
|
41 |
+
* SassEachNode constructor.
|
42 |
+
* @param object source token
|
43 |
+
* @return SassEachNode
|
44 |
+
*/
|
45 |
+
public function __construct($token) {
|
46 |
+
parent::__construct($token);
|
47 |
+
if (!preg_match(self::MATCH, $token->source, $matches)) {
|
48 |
+
throw new SassEachNodeException('Invalid @each directive', $this);
|
49 |
+
}
|
50 |
+
else {
|
51 |
+
$this->variable = trim($matches[self::VARIABLE]);
|
52 |
+
$this->in = $matches[self::IN];
|
53 |
+
}
|
54 |
+
}
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Parse this node.
|
58 |
+
* @param SassContext the context in which this node is parsed
|
59 |
+
* @return array parsed child nodes
|
60 |
+
*/
|
61 |
+
public function parse($context) {
|
62 |
+
$children = array();
|
63 |
+
|
64 |
+
if ($this->variable && $this->in) {
|
65 |
+
$context = new SassContext($context);
|
66 |
+
|
67 |
+
list($in, $sep) = SassList::_parse_list($this->in, 'auto', true, $context);
|
68 |
+
foreach ($in as $var) {
|
69 |
+
$context->setVariable($this->variable, $var);
|
70 |
+
$children = array_merge($children, $this->parseChildren($context));
|
71 |
+
}
|
72 |
+
}
|
73 |
+
$context->merge();
|
74 |
+
return $children;
|
75 |
+
}
|
76 |
+
}
|
lib/phpsass/tree/SassElseNode.php
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* SVN FILE: $Id: SassIfNode.php 49 2010-04-04 10:51:24Z chris.l.yates $ */
|
3 |
+
/**
|
4 |
+
* SassElseNode class file.
|
5 |
+
* @author Chris Yates <chris.l.yates@gmail.com>
|
6 |
+
* @copyright Copyright (c) 2010 PBM Web Development
|
7 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
8 |
+
* @package PHamlP
|
9 |
+
* @subpackage Sass.tree
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* SassElseNode class.
|
14 |
+
* Represents Sass Else If and Else statements.
|
15 |
+
* Else If and Else statement nodes are chained below the If statement node.
|
16 |
+
* @package PHamlP
|
17 |
+
* @subpackage Sass.tree
|
18 |
+
*/
|
19 |
+
class SassElseNode extends SassIfNode {
|
20 |
+
/**
|
21 |
+
* SassElseNode constructor.
|
22 |
+
* @param object source token
|
23 |
+
* @return SassElseNode
|
24 |
+
*/
|
25 |
+
public function __construct($token) {
|
26 |
+
parent::__construct($token, false);
|
27 |
+
}
|
28 |
+
}
|
lib/phpsass/tree/SassExtendNode.php
ADDED
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* SVN FILE: $Id: SassExtendNode.php 49 2010-04-04 10:51:24Z chris.l.yates $ */
|
3 |
+
/**
|
4 |
+
* SassExtendNode class file.
|
5 |
+
* @author Chris Yates <chris.l.yates@gmail.com>
|
6 |
+
* @copyright Copyright (c) 2010 PBM Web Development
|
7 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
8 |
+
* @package PHamlP
|
9 |
+
* @subpackage Sass.tree
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* SassExtendNode class.
|
14 |
+
* Represents a Sass @debug or @warn directive.
|
15 |
+
* @package PHamlP
|
16 |
+
* @subpackage Sass.tree
|
17 |
+
*/
|
18 |
+
class SassExtendNode extends SassNode {
|
19 |
+
const IDENTIFIER = '@';
|
20 |
+
const MATCH = '/^@extend\s+(.+)/i';
|
21 |
+
const VALUE = 1;
|
22 |
+
|
23 |
+
/**
|
24 |
+
* @var string the directive
|
25 |
+
*/
|
26 |
+
private $value;
|
27 |
+
|
28 |
+
/**
|
29 |
+
* SassExtendNode.
|
30 |
+
* @param object source token
|
31 |
+
* @return SassExtendNode
|
32 |
+
*/
|
33 |
+
public function __construct($token) {
|
34 |
+
parent::__construct($token);
|
35 |
+
preg_match(self::MATCH, $token->source, $matches);
|
36 |
+
$this->value = $matches[self::VALUE];
|
37 |
+
}
|
38 |
+
|
39 |
+
/**
|
40 |
+
* Parse this node.
|
41 |
+
* @return array An empty array
|
42 |
+
*/
|
43 |
+
public function parse($context) {
|
44 |
+
# resolve selectors in relation to variables
|
45 |
+
# allows extend inside nested loops.
|
46 |
+
$this->root->extend($this->value, $this->parent->resolveSelectors($context));
|
47 |
+
return array();
|
48 |
+
}
|
49 |
+
}
|
lib/phpsass/tree/SassForNode.php
ADDED
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* SVN FILE: $Id$ */
|
3 |
+
/**
|
4 |
+
* SassForNode class file.
|
5 |
+
* This is an enhanced version of the standard SassScript @for loop that adds
|
6 |
+
* an optional step clause. Step must evaluate to a positive integer.
|
7 |
+
* The syntax is:
|
8 |
+
* <pre>@for <var> from <start> to|through <end>[ step <step>]</pre>.
|
9 |
+
*
|
10 |
+
* <start> can be less or greater than <end>.
|
11 |
+
* If the step clause is ommitted the <step> = 1.
|
12 |
+
* <var> is available to the rest of the script following evaluation
|
13 |
+
* and has the value that terminated the loop.
|
14 |
+
*
|
15 |
+
* @author Chris Yates <chris.l.yates@gmail.com>
|
16 |
+
* @copyright Copyright (c) 2010 PBM Web Development
|
17 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
18 |
+
* @package PHamlP
|
19 |
+
* @subpackage Sass.tree
|
20 |
+
*/
|
21 |
+
|
22 |
+
/**
|
23 |
+
* SassForNode class.
|
24 |
+
* Represents a Sass @for loop.
|
25 |
+
* @package PHamlP
|
26 |
+
* @subpackage Sass.tree
|
27 |
+
*/
|
28 |
+
class SassForNode extends SassNode {
|
29 |
+
const MATCH = '/@for\s+[!\$](\w+)\s+from\s+(.+?)\s+(through|to)\s+(.+?)(?:\s+step\s+(.+))?$/i';
|
30 |
+
|
31 |
+
const VARIABLE = 1;
|
32 |
+
const FROM = 2;
|
33 |
+
const INCLUSIVE = 3;
|
34 |
+
const TO = 4;
|
35 |
+
const STEP = 5;
|
36 |
+
const IS_INCLUSIVE = 'through';
|
37 |
+
|
38 |
+
/**
|
39 |
+
* @var string variable name for the loop
|
40 |
+
*/
|
41 |
+
private $variable;
|
42 |
+
/**
|
43 |
+
* @var string expression that provides the loop start value
|
44 |
+
*/
|
45 |
+
private $from;
|
46 |
+
/**
|
47 |
+
* @var string expression that provides the loop end value
|
48 |
+
*/
|
49 |
+
private $to;
|
50 |
+
/**
|
51 |
+
* @var boolean whether the loop end value is inclusive
|
52 |
+
*/
|
53 |
+
private $inclusive;
|
54 |
+
/**
|
55 |
+
* @var string expression that provides the amount by which the loop variable
|
56 |
+
* changes on each iteration
|
57 |
+
*/
|
58 |
+
private $step;
|
59 |
+
|
60 |
+
/**
|
61 |
+
* SassForNode constructor.
|
62 |
+
* @param object source token
|
63 |
+
* @return SassForNode
|
64 |
+
*/
|
65 |
+
public function __construct($token) {
|
66 |
+
parent::__construct($token);
|
67 |
+
if (!preg_match(self::MATCH, $token->source, $matches)) {
|
68 |
+
throw new SassForNodeException('Invalid @for directive', $this);
|
69 |
+
}
|
70 |
+
$this->variable = $matches[self::VARIABLE];
|
71 |
+
$this->from = $matches[self::FROM];
|
72 |
+
$this->to = $matches[self::TO];
|
73 |
+
$this->inclusive = ($matches[self::INCLUSIVE] === SassForNode::IS_INCLUSIVE);
|
74 |
+
$this->step = (empty($matches[self::STEP]) ? 1 : $matches[self::STEP]);
|
75 |
+
}
|
76 |
+
|
77 |
+
/**
|
78 |
+
* Parse this node.
|
79 |
+
* @param SassContext the context in which this node is parsed
|
80 |
+
* @return array parsed child nodes
|
81 |
+
*/
|
82 |
+
public function parse($context) {
|
83 |
+
$children = array();
|
84 |
+
$from = (float)$this->evaluate($this->from, $context)->value;
|
85 |
+
$to = (float)$this->evaluate($this->to, $context)->value;
|
86 |
+
$step = (float)$this->evaluate($this->step, $context)->value * ($to > $from ? 1 : -1);
|
87 |
+
|
88 |
+
if ($this->inclusive) {
|
89 |
+
$to += ($from < $to ? 1 : -1);
|
90 |
+
}
|
91 |
+
|
92 |
+
$context = new SassContext($context);
|
93 |
+
for ($i = $from; ($from < $to ? $i < $to : $i > $to); $i = $i + $step) {
|
94 |
+
$context->setVariable($this->variable, new SassNumber($i));
|
95 |
+
$children = array_merge($children, $this->parseChildren($context));
|
96 |
+
}
|
97 |
+
return $children;
|
98 |
+
}
|
99 |
+
}
|
lib/phpsass/tree/SassFunctionDefinitionNode.php
ADDED
@@ -0,0 +1,116 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* SVN FILE: $Id$ */
|
3 |
+
/**
|
4 |
+
* SassFunctionDefinitionNode class file.
|
5 |
+
* @author Chris Yates <chris.l.yates@gmail.com>
|
6 |
+
* @copyright Copyright (c) 2010 PBM Web Development
|
7 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
8 |
+
* @package PHamlP
|
9 |
+
* @subpackage Sass.tree
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* SassFunctionDefinitionNode class.
|
14 |
+
* Represents a Function definition.
|
15 |
+
* @package PHamlP
|
16 |
+
* @subpackage Sass.tree
|
17 |
+
*/
|
18 |
+
class SassFunctionDefinitionNode extends SassNode {
|
19 |
+
const NODE_IDENTIFIER = FALSE;
|
20 |
+
const MATCH = '/^@function\s+([_-\w]+)\s*(?:\((.*?)\))?\s*$/im';
|
21 |
+
const IDENTIFIER = 1;
|
22 |
+
const NAME = 1;
|
23 |
+
const ARGUMENTS = 2;
|
24 |
+
|
25 |
+
/**
|
26 |
+
* @var string name of the function
|
27 |
+
*/
|
28 |
+
private $name;
|
29 |
+
/**
|
30 |
+
* @var array arguments for the function as name=>value pairs were value is the
|
31 |
+
* default value or null for required arguments
|
32 |
+
*/
|
33 |
+
private $args = array();
|
34 |
+
|
35 |
+
public $parent;
|
36 |
+
|
37 |
+
/**
|
38 |
+
* SassFunctionDefinitionNode constructor.
|
39 |
+
* @param object source token
|
40 |
+
* @return SassFunctionDefinitionNode
|
41 |
+
*/
|
42 |
+
public function __construct($token) {
|
43 |
+
// if ($token->level !== 0) {
|
44 |
+
// throw new SassFunctionDefinitionNodeException('Functions can only be defined at root level', $token);
|
45 |
+
// }
|
46 |
+
parent::__construct($token);
|
47 |
+
preg_match(self::MATCH, $token->source, $matches);
|
48 |
+
if (empty($matches)) {
|
49 |
+
throw new SassFunctionDefinitionNodeException('Invalid Function', $token);
|
50 |
+
}
|
51 |
+
$this->name = $matches[self::NAME];
|
52 |
+
$this->name = preg_replace('/[^a-z0-9_]/', '_', strtolower($this->name));
|
53 |
+
if (isset($matches[self::ARGUMENTS])) {
|
54 |
+
if (strlen(trim($matches[self::ARGUMENTS]))) {
|
55 |
+
foreach (explode(',', $matches[self::ARGUMENTS]) as $arg) {
|
56 |
+
$arg = explode(($matches[self::IDENTIFIER] === self::NODE_IDENTIFIER ? '=' : ':'), trim($arg));
|
57 |
+
$this->args[substr(trim($arg[0]), 1)] = (count($arg) == 2 ? trim($arg[1]) : null);
|
58 |
+
} // foreach
|
59 |
+
}
|
60 |
+
}
|
61 |
+
}
|
62 |
+
|
63 |
+
/**
|
64 |
+
* Parse this node.
|
65 |
+
* Add this function to the current context.
|
66 |
+
* @param SassContext the context in which this node is parsed
|
67 |
+
* @return array the parsed node - an empty array
|
68 |
+
*/
|
69 |
+
public function parse($context) {
|
70 |
+
$context->addFunction($this->name, $this);
|
71 |
+
return array();
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* Returns the arguments with default values for this function
|
76 |
+
* @return array the arguments with default values for this function
|
77 |
+
*/
|
78 |
+
public function getArgs() {
|
79 |
+
return $this->args;
|
80 |
+
}
|
81 |
+
|
82 |
+
/**
|
83 |
+
* Returns a value indicating if the token represents this type of node.
|
84 |
+
* @param object token
|
85 |
+
* @return boolean true if the token represents this type of node, false if not
|
86 |
+
*/
|
87 |
+
public static function isa($token) {
|
88 |
+
return $token->source[0] === self::NODE_IDENTIFIER;
|
89 |
+
}
|
90 |
+
|
91 |
+
/**
|
92 |
+
* Evalutes the function in the given context, with the provided arguments
|
93 |
+
* @param SassContext - the parent context
|
94 |
+
* @param array - the list of provided variables
|
95 |
+
* @throws SassReturn - if the @return is fired then this is thrown to break early
|
96 |
+
* @return SassBoolean(false) - if no @return was fired, return false
|
97 |
+
*/
|
98 |
+
public function execute($pcontext, $provided) {
|
99 |
+
list($arguments, $context) = SassScriptFunction::fill_parameters($this->args, $provided, $pcontext, $this);
|
100 |
+
$context->setVariables($arguments);
|
101 |
+
|
102 |
+
$parser = $this->parent->parser;
|
103 |
+
|
104 |
+
$children = array();
|
105 |
+
try {
|
106 |
+
foreach ($this->children as $child) {
|
107 |
+
$child->parent = $this;
|
108 |
+
$children = array_merge($children, $child->parse($context));
|
109 |
+
}
|
110 |
+
} catch (SassReturn $e) {
|
111 |
+
return $e->value;
|
112 |
+
}
|
113 |
+
|
114 |
+
return new SassBoolean('false');
|
115 |
+
}
|
116 |
+
}
|
lib/phpsass/tree/SassIfNode.php
ADDED
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* SVN FILE: $Id$ */
|
3 |
+
/**
|
4 |
+
* SassIfNode class file.
|
5 |
+
* @author Chris Yates <chris.l.yates@gmail.com>
|
6 |
+
* @copyright Copyright (c) 2010 PBM Web Development
|
7 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
8 |
+
* @package PHamlP
|
9 |
+
* @subpackage Sass.tree
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* SassIfNode class.
|
14 |
+
* Represents Sass If, Else If and Else statements.
|
15 |
+
* Else If and Else statement nodes are chained below the If statement node.
|
16 |
+
* @package PHamlP
|
17 |
+
* @subpackage Sass.tree
|
18 |
+
*/
|
19 |
+
class SassIfNode extends SassNode {
|
20 |
+
const MATCH_IF = '/^@if\s+(.+)$/i';
|
21 |
+
const MATCH_ELSE = '/@else(\s+if\s+(.+))?/i';
|
22 |
+
const IF_EXPRESSION = 1;
|
23 |
+
const ELSE_IF = 1;
|
24 |
+
const ELSE_EXPRESSION = 2;
|
25 |
+
/**
|
26 |
+
* @var SassIfNode the next else node.
|
27 |
+
*/
|
28 |
+
private $else;
|
29 |
+
/**
|
30 |
+
* @var string expression to evaluate
|
31 |
+
*/
|
32 |
+
private $expression;
|
33 |
+
|
34 |
+
/**
|
35 |
+
* SassIfNode constructor.
|
36 |
+
* @param object source token
|
37 |
+
* @param boolean true for an "if" node, false for an "else if | else" node
|
38 |
+
* @return SassIfNode
|
39 |
+
*/
|
40 |
+
public function __construct($token, $if=true) {
|
41 |
+
parent::__construct($token);
|
42 |
+
if ($if) {
|
43 |
+
preg_match(self::MATCH_IF, $token->source, $matches);
|
44 |
+
$this->expression = $matches[SassIfNode::IF_EXPRESSION];
|
45 |
+
}
|
46 |
+
else {
|
47 |
+
preg_match(self::MATCH_ELSE, $token->source, $matches);
|
48 |
+
$this->expression = (sizeof($matches)==1 ? null : $matches[SassIfNode::ELSE_EXPRESSION]);
|
49 |
+
}
|
50 |
+
}
|
51 |
+
|
52 |
+
/**
|
53 |
+
* Adds an "else" statement to this node.
|
54 |
+
* @param SassIfNode "else" statement node to add
|
55 |
+
* @return SassIfNode this node
|
56 |
+
*/
|
57 |
+
public function addElse($node) {
|
58 |
+
if (is_null($this->else)) {
|
59 |
+
$node->parent = $this;
|
60 |
+
$node->root = $this->root;
|
61 |
+
$this->else = $node;
|
62 |
+
}
|
63 |
+
else {
|
64 |
+
$this->else->addElse($node);
|
65 |
+
}
|
66 |
+
return $this;
|
67 |
+
}
|
68 |
+
|
69 |
+
/**
|
70 |
+
* Parse this node.
|
71 |
+
* @param SassContext the context in which this node is parsed
|
72 |
+
* @return array parsed child nodes
|
73 |
+
*/
|
74 |
+
public function parse($context) {
|
75 |
+
if ($this->isElse() || $this->evaluate($this->expression, $context)->toBoolean()) {
|
76 |
+
$children = $this->parseChildren($context);
|
77 |
+
}
|
78 |
+
elseif (!empty($this->else)) {
|
79 |
+
$children = $this->else->parse($context);
|
80 |
+
}
|
81 |
+
else {
|
82 |
+
$children = array();
|
83 |
+
}
|
84 |
+
return $children;
|
85 |
+
}
|
86 |
+
|
87 |
+
/**
|
88 |
+
* Returns a value indicating if this node is an "else" node.
|
89 |
+
* @return true if this node is an "else" node, false if this node is an "if"
|
90 |
+
* or "else if" node
|
91 |
+
*/
|
92 |
+
private function isElse() {
|
93 |
+
return ($this->expression=='');
|
94 |
+
}
|
95 |
+
}
|
lib/phpsass/tree/SassImportNode.php
ADDED
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* SVN FILE: $Id$ */
|
3 |
+
/**
|
4 |
+
* SassImportNode class file.
|
5 |
+
* @author Chris Yates <chris.l.yates@gmail.com>
|
6 |
+
* @copyright Copyright (c) 2010 PBM Web Development
|
7 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
8 |
+
* @package PHamlP
|
9 |
+
* @subpackage Sass.tree
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* SassImportNode class.
|
14 |
+
* Represents a CSS Import.
|
15 |
+
* @package PHamlP
|
16 |
+
* @subpackage Sass.tree
|
17 |
+
*/
|
18 |
+
class SassImportNode extends SassNode {
|
19 |
+
const IDENTIFIER = '@';
|
20 |
+
const MATCH = '/^@import\s+(.+)/i';
|
21 |
+
const MATCH_CSS = '/^((url)\((.+)\)|.+" \w+|http|.+\.css$)/im';
|
22 |
+
const FILES = 1;
|
23 |
+
|
24 |
+
/**
|
25 |
+
* @var array files to import
|
26 |
+
*/
|
27 |
+
private $files = array();
|
28 |
+
|
29 |
+
/**
|
30 |
+
* SassImportNode.
|
31 |
+
* @param object source token
|
32 |
+
* @return SassImportNode
|
33 |
+
*/
|
34 |
+
public function __construct($token, $parent) {
|
35 |
+
parent::__construct($token);
|
36 |
+
$this->parent = $parent;
|
37 |
+
preg_match(self::MATCH, $token->source, $matches);
|
38 |
+
|
39 |
+
foreach (SassList::_build_list($matches[self::FILES]) as $file) {
|
40 |
+
$this->files[] = trim($file, '"\'; ');
|
41 |
+
}
|
42 |
+
}
|
43 |
+
|
44 |
+
/**
|
45 |
+
* Parse this node.
|
46 |
+
* If the node is a CSS import return the CSS import rule.
|
47 |
+
* Else returns the rendered tree for the file.
|
48 |
+
* @param SassContext the context in which this node is parsed
|
49 |
+
* @return array the parsed node
|
50 |
+
*/
|
51 |
+
public function parse($context) {
|
52 |
+
$imported = array();
|
53 |
+
foreach ($this->files as $file) {
|
54 |
+
if (preg_match(self::MATCH_CSS, $file, $matches)) {
|
55 |
+
if (isset($matches[2]) && $matches[2] == 'url') {
|
56 |
+
$file = $matches[1];
|
57 |
+
} else {
|
58 |
+
$file = "url('$file')";
|
59 |
+
}
|
60 |
+
return array(new SassString("@import $file;"), new SassString("\n"));
|
61 |
+
}
|
62 |
+
$file = trim($file, '\'"');
|
63 |
+
$files = SassFile::get_file($file, $this->parser);
|
64 |
+
$tree = array();
|
65 |
+
if ($files) {
|
66 |
+
if ($this->token->level > 0) {
|
67 |
+
$tree = $this->parent;
|
68 |
+
while (get_class($tree) != 'SassRuleNode' && get_class($tree) != 'SassRootNode' && isset($tree->parent)) {
|
69 |
+
$tree = $tree->parent;
|
70 |
+
}
|
71 |
+
$tree = clone $tree;
|
72 |
+
$tree->children = array();
|
73 |
+
} else {
|
74 |
+
$tree = new SassRootNode($this->parser);
|
75 |
+
$tree->extend_parent = $this->parent;
|
76 |
+
}
|
77 |
+
|
78 |
+
foreach ($files as $subfile) {
|
79 |
+
if (preg_match(self::MATCH_CSS, $subfile)) {
|
80 |
+
$tree->addChild(new SassString("@import url('$subfile');"));
|
81 |
+
}
|
82 |
+
else {
|
83 |
+
$this->parser->filename = $subfile;
|
84 |
+
$subtree = SassFile::get_tree($subfile, $this->parser);
|
85 |
+
foreach($subtree->getChildren() as $child) {
|
86 |
+
$tree->addChild($child);
|
87 |
+
}
|
88 |
+
}
|
89 |
+
}
|
90 |
+
}
|
91 |
+
if (!empty($tree)) {
|
92 |
+
# parent may be either SassRootNode (returns an object) or SassRuleNode (returns an array of nodes)
|
93 |
+
# so we parse then try get the children.
|
94 |
+
$parsed = $tree->parse($context);
|
95 |
+
if (!is_array($parsed) && isset($parsed->children)) {
|
96 |
+
$parsed = $parsed->children;
|
97 |
+
}
|
98 |
+
if (is_array($parsed)) {
|
99 |
+
$imported = array_merge($imported, $parsed);
|
100 |
+
}
|
101 |
+
}
|
102 |
+
}
|
103 |
+
return $imported;
|
104 |
+
}
|
105 |
+
}
|
lib/phpsass/tree/SassMediaNode.php
ADDED
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* SVN FILE: $Id: SassMediaNode.php 49 2010-04-04 10:51:24Z chris.l.yates $ */
|
3 |
+
/**
|
4 |
+
* SassMediaNode class file.
|
5 |
+
* @author Richard Lyon
|
6 |
+
* @copyright none
|
7 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
8 |
+
* @package PHamlP
|
9 |
+
* @subpackage Sass.tree
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* SassMediaNode class.
|
14 |
+
* Represents a CSS @media directive
|
15 |
+
* @package PHamlP
|
16 |
+
* @subpackage Sass.tree
|
17 |
+
*/
|
18 |
+
class SassMediaNode extends SassNode {
|
19 |
+
const IDENTIFIER = '@';
|
20 |
+
const MATCH = '/^@(media)\s+(.+?)\s*;?$/';
|
21 |
+
const MEDIA = 1;
|
22 |
+
|
23 |
+
|
24 |
+
public $token;
|
25 |
+
|
26 |
+
/**
|
27 |
+
* @var string
|
28 |
+
*/
|
29 |
+
private $media;
|
30 |
+
/**
|
31 |
+
* @var array parameters for the message;
|
32 |
+
* only used by internal warning messages
|
33 |
+
*/
|
34 |
+
private $params;
|
35 |
+
/**
|
36 |
+
* @var boolean true if this is a warning
|
37 |
+
*/
|
38 |
+
private $warning;
|
39 |
+
|
40 |
+
/**
|
41 |
+
* SassMediaNode.
|
42 |
+
* @param object source token
|
43 |
+
* @param mixed string: an internally generated warning message about the
|
44 |
+
* source
|
45 |
+
* boolean: the source token is a @Media or @warn directive containing the
|
46 |
+
* message; True if this is a @warn directive
|
47 |
+
* @param array parameters for the message
|
48 |
+
* @return SassMediaNode
|
49 |
+
*/
|
50 |
+
public function __construct($token) {
|
51 |
+
parent::__construct($token);
|
52 |
+
|
53 |
+
preg_match(self::MATCH, $token->source, $matches);
|
54 |
+
$this->token = $token;
|
55 |
+
$this->media = $matches[self::MEDIA];
|
56 |
+
}
|
57 |
+
|
58 |
+
/**
|
59 |
+
* Parse this node.
|
60 |
+
* This raises an error.
|
61 |
+
* @return array An empty array
|
62 |
+
*/
|
63 |
+
public function parse($context) {
|
64 |
+
$this->token->source = SassDirectiveNode::interpolate_nonstrict($this->token->source, $context);
|
65 |
+
|
66 |
+
$node = new SassRuleNode($this->token, $context);
|
67 |
+
$node->root = $this->parent->root;
|
68 |
+
|
69 |
+
$rule = clone $this->parent;
|
70 |
+
$rule->root = $node->root;
|
71 |
+
$rule->children = $this->children;
|
72 |
+
|
73 |
+
$try = $rule->parse($context);
|
74 |
+
if (is_array($try)) {
|
75 |
+
$rule->children = $try;
|
76 |
+
}
|
77 |
+
// Tests were failing with this, but I'm not sure if we cover every case.
|
78 |
+
//else if (is_object($try) && method_exists($try, 'render')) {
|
79 |
+
// $rule = $try;
|
80 |
+
//}
|
81 |
+
|
82 |
+
$node->children = array(new SassString($rule->render($context)));
|
83 |
+
|
84 |
+
return array($node);
|
85 |
+
}
|
86 |
+
}
|
lib/phpsass/tree/SassMixinDefinitionNode.php
ADDED
@@ -0,0 +1,79 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* SVN FILE: $Id$ */
|
3 |
+
/**
|
4 |
+
* SassMixinDefinitionNode class file.
|
5 |
+
* @author Chris Yates <chris.l.yates@gmail.com>
|
6 |
+
* @copyright Copyright (c) 2010 PBM Web Development
|
7 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
8 |
+
* @package PHamlP
|
9 |
+
* @subpackage Sass.tree
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* SassMixinDefinitionNode class.
|
14 |
+
* Represents a Mixin definition.
|
15 |
+
* @package PHamlP
|
16 |
+
* @subpackage Sass.tree
|
17 |
+
*/
|
18 |
+
class SassMixinDefinitionNode extends SassNode {
|
19 |
+
const NODE_IDENTIFIER = '=';
|
20 |
+
const MATCH = '/^(=|@mixin\s+)([-\w]+)\s*(?:\((.*?)\))?\s*$/im';
|
21 |
+
const IDENTIFIER = 1;
|
22 |
+
const NAME = 2;
|
23 |
+
const ARGUMENTS = 3;
|
24 |
+
|
25 |
+
/**
|
26 |
+
* @var string name of the mixin
|
27 |
+
*/
|
28 |
+
private $name;
|
29 |
+
/**
|
30 |
+
* @var array arguments for the mixin as name=>value pairs were value is the
|
31 |
+
* default value or null for required arguments
|
32 |
+
*/
|
33 |
+
private $args = array();
|
34 |
+
|
35 |
+
/**
|
36 |
+
* SassMixinDefinitionNode constructor.
|
37 |
+
* @param object source token
|
38 |
+
* @return SassMixinDefinitionNode
|
39 |
+
*/
|
40 |
+
public function __construct($token) {
|
41 |
+
preg_match(self::MATCH, $token->source, $matches);
|
42 |
+
parent::__construct($token);
|
43 |
+
if (empty($matches)) {
|
44 |
+
throw new SassMixinDefinitionNodeException('Invalid Mixin', $this);
|
45 |
+
}
|
46 |
+
$this->name = $matches[self::NAME];
|
47 |
+
if (isset($matches[self::ARGUMENTS])) {
|
48 |
+
$this->args = SassScriptFunction::extractArgs($matches[self::ARGUMENTS], true, new SassContext);
|
49 |
+
}
|
50 |
+
}
|
51 |
+
|
52 |
+
/**
|
53 |
+
* Parse this node.
|
54 |
+
* Add this mixin to the current context.
|
55 |
+
* @param SassContext the context in which this node is parsed
|
56 |
+
* @return array the parsed node - an empty array
|
57 |
+
*/
|
58 |
+
public function parse($context) {
|
59 |
+
$context->addMixin($this->name, $this);
|
60 |
+
return array();
|
61 |
+
}
|
62 |
+
|
63 |
+
/**
|
64 |
+
* Returns the arguments with default values for this mixin
|
65 |
+
* @return array the arguments with default values for this mixin
|
66 |
+
*/
|
67 |
+
public function getArgs() {
|
68 |
+
return $this->args;
|
69 |
+
}
|
70 |
+
|
71 |
+
/**
|
72 |
+
* Returns a value indicating if the token represents this type of node.
|
73 |
+
* @param object token
|
74 |
+
* @return boolean true if the token represents this type of node, false if not
|
75 |
+
*/
|
76 |
+
public static function isa($token) {
|
77 |
+
return $token->source[0] === self::NODE_IDENTIFIER;
|
78 |
+
}
|
79 |
+
}
|
lib/phpsass/tree/SassMixinNode.php
ADDED
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* SVN FILE: $Id$ */
|
3 |
+
/**
|
4 |
+
* SassMixinNode class file.
|
5 |
+
* @author Chris Yates <chris.l.yates@gmail.com>
|
6 |
+
* @copyright Copyright (c) 2010 PBM Web Development
|
7 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
8 |
+
* @package PHamlP
|
9 |
+
* @subpackage Sass.tree
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* SassMixinNode class.
|
14 |
+
* Represents a Mixin.
|
15 |
+
* @package PHamlP
|
16 |
+
* @subpackage Sass.tree
|
17 |
+
*/
|
18 |
+
class SassMixinNode extends SassNode {
|
19 |
+
const NODE_IDENTIFIER = '+';
|
20 |
+
const MATCH = '/^(\+|@include\s+)([a-z0-9_-]+)\s*(?:\((.*?)\))?\s*$/i';
|
21 |
+
const IDENTIFIER = 1;
|
22 |
+
const NAME = 2;
|
23 |
+
const ARGS = 3;
|
24 |
+
|
25 |
+
/**
|
26 |
+
* @var string name of the mixin
|
27 |
+
*/
|
28 |
+
private $name;
|
29 |
+
/**
|
30 |
+
* @var array arguments for the mixin
|
31 |
+
*/
|
32 |
+
private $args = '';
|
33 |
+
|
34 |
+
/**
|
35 |
+
* SassMixinDefinitionNode constructor.
|
36 |
+
* @param object source token
|
37 |
+
* @return SassMixinNode
|
38 |
+
*/
|
39 |
+
public function __construct($token) {
|
40 |
+
parent::__construct($token);
|
41 |
+
preg_match(self::MATCH, $token->source, $matches);
|
42 |
+
|
43 |
+
if (!isset($matches[self::NAME])) {
|
44 |
+
throw new SassMixinNodeException('Invalid mixin invocation: ($token->source)', $this);
|
45 |
+
}
|
46 |
+
$this->name = $matches[self::NAME];
|
47 |
+
if (isset($matches[self::ARGS]) && strlen($matches[self::ARGS])) {
|
48 |
+
$this->args = $matches[self::ARGS];
|
49 |
+
}
|
50 |
+
}
|
51 |
+
|
52 |
+
/**
|
53 |
+
* Parse this node.
|
54 |
+
* Set passed arguments and any optional arguments not passed to their
|
55 |
+
* defaults, then render the children of the mixin definition.
|
56 |
+
* @param SassContext the context in which this node is parsed
|
57 |
+
* @return array the parsed node
|
58 |
+
*/
|
59 |
+
public function parse($pcontext) {
|
60 |
+
$mixin = $pcontext->getMixin($this->name);
|
61 |
+
$context = new SassContext($pcontext);
|
62 |
+
$context->content = $this->children;
|
63 |
+
$argc = count($this->args);
|
64 |
+
$count = 0;
|
65 |
+
|
66 |
+
$args = SassScriptFunction::extractArgs($this->args, false, $context);
|
67 |
+
|
68 |
+
list($arguments) = SassScriptFunction::fill_parameters($mixin->args, $args, $context, $this);
|
69 |
+
$context->setVariables($arguments);
|
70 |
+
|
71 |
+
$children = array();
|
72 |
+
foreach ($mixin->children as $child) {
|
73 |
+
$child->parent = $this;
|
74 |
+
$children = array_merge($children, $child->parse($context));
|
75 |
+
}
|
76 |
+
|
77 |
+
// $context->merge();
|
78 |
+
return $children;
|
79 |
+
}
|
80 |
+
|
81 |
+
/**
|
82 |
+
* Returns a value indicating if the token represents this type of node.
|
83 |
+
* @param object token
|
84 |
+
* @return boolean true if the token represents this type of node, false if not
|
85 |
+
*/
|
86 |
+
public static function isa($token) {
|
87 |
+
return $token->source[0] === self::NODE_IDENTIFIER;
|
88 |
+
}
|
89 |
+
}
|
lib/phpsass/tree/SassNode.php
ADDED
@@ -0,0 +1,349 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* SVN FILE: $Id$ */
|
3 |
+
/**
|
4 |
+
* SassNode class file.
|
5 |
+
* @author Chris Yates <chris.l.yates@gmail.com>
|
6 |
+
* @copyright Copyright (c) 2010 PBM Web Development
|
7 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
8 |
+
* @package PHamlP
|
9 |
+
* @subpackage Sass.tree
|
10 |
+
*/
|
11 |
+
|
12 |
+
require_once('SassContext.php');
|
13 |
+
require_once('SassCommentNode.php');
|
14 |
+
require_once('SassDebugNode.php');
|
15 |
+
require_once('SassDirectiveNode.php');
|
16 |
+
require_once('SassImportNode.php');
|
17 |
+
require_once('SassMixinNode.php');
|
18 |
+
require_once('SassMixinDefinitionNode.php');
|
19 |
+
require_once('SassPropertyNode.php');
|
20 |
+
require_once('SassRootNode.php');
|
21 |
+
require_once('SassRuleNode.php');
|
22 |
+
require_once('SassVariableNode.php');
|
23 |
+
require_once('SassExtendNode.php');
|
24 |
+
require_once('SassEachNode.php');
|
25 |
+
require_once('SassForNode.php');
|
26 |
+
require_once('SassIfNode.php');
|
27 |
+
require_once('SassElseNode.php');
|
28 |
+
require_once('SassWhileNode.php');
|
29 |
+
require_once('SassNodeExceptions.php');
|
30 |
+
|
31 |
+
require_once('SassFunctionDefinitionNode.php');
|
32 |
+
require_once('SassReturnNode.php');
|
33 |
+
require_once('SassContentNode.php');
|
34 |
+
require_once('SassWarnNode.php');
|
35 |
+
require_once('SassMediaNode.php');
|
36 |
+
|
37 |
+
/**
|
38 |
+
* SassNode class.
|
39 |
+
* Base class for all Sass nodes.
|
40 |
+
* @package PHamlP
|
41 |
+
* @subpackage Sass.tree
|
42 |
+
*/
|
43 |
+
class SassNode {
|
44 |
+
/**
|
45 |
+
* @var SassNode parent of this node
|
46 |
+
*/
|
47 |
+
public $parent;
|
48 |
+
/**
|
49 |
+
* @var SassNode root node
|
50 |
+
*/
|
51 |
+
public $root;
|
52 |
+
/**
|
53 |
+
* @var array children of this node
|
54 |
+
*/
|
55 |
+
public $children = array();
|
56 |
+
/**
|
57 |
+
* @var object source token
|
58 |
+
*/
|
59 |
+
public $token;
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Constructor.
|
63 |
+
* @param object source token
|
64 |
+
* @return SassNode
|
65 |
+
*/
|
66 |
+
public function __construct($token) {
|
67 |
+
$this->token = $token;
|
68 |
+
}
|
69 |
+
|
70 |
+
/**
|
71 |
+
* Getter.
|
72 |
+
* @param string name of property to get
|
73 |
+
* @return mixed return value of getter function
|
74 |
+
*/
|
75 |
+
public function __get($name) {
|
76 |
+
$getter = 'get' . ucfirst($name);
|
77 |
+
if (method_exists($this, $getter)) {
|
78 |
+
return $this->$getter();
|
79 |
+
}
|
80 |
+
throw new SassNodeException('No getter function for ' . $name, $this);
|
81 |
+
}
|
82 |
+
|
83 |
+
/**
|
84 |
+
* Setter.
|
85 |
+
* @param string name of property to set
|
86 |
+
* @return mixed value of property
|
87 |
+
* @return SassNode this node
|
88 |
+
*/
|
89 |
+
public function __set($name, $value) {
|
90 |
+
$setter = 'set' . ucfirst($name);
|
91 |
+
if (method_exists($this, $setter)) {
|
92 |
+
$this->$setter($value);
|
93 |
+
return $this;
|
94 |
+
}
|
95 |
+
throw new SassNodeException('No setter function for ' . $name, $this);
|
96 |
+
}
|
97 |
+
|
98 |
+
/**
|
99 |
+
* Resets children when cloned
|
100 |
+
* @see parse
|
101 |
+
*/
|
102 |
+
public function __clone() {
|
103 |
+
$this->children = array();
|
104 |
+
}
|
105 |
+
|
106 |
+
/**
|
107 |
+
* Return a value indicating if this node has a parent
|
108 |
+
* @return array the node's parent
|
109 |
+
*/
|
110 |
+
public function hasParent() {
|
111 |
+
return !empty($this->parent);
|
112 |
+
}
|
113 |
+
|
114 |
+
/**
|
115 |
+
* Returns the node's parent
|
116 |
+
* @return array the node's parent
|
117 |
+
*/
|
118 |
+
public function getParent() {
|
119 |
+
return $this->parent;
|
120 |
+
}
|
121 |
+
|
122 |
+
/**
|
123 |
+
* Adds a child to this node.
|
124 |
+
* @return SassNode the child to add
|
125 |
+
*/
|
126 |
+
public function addChild($child) {
|
127 |
+
if ($child instanceof SassElseNode) {
|
128 |
+
if (!$this->lastChild instanceof SassIfNode) {
|
129 |
+
throw new SassException('@else(if) directive must come after @(else)if', $child);
|
130 |
+
}
|
131 |
+
$this->lastChild->addElse($child);
|
132 |
+
}
|
133 |
+
else {
|
134 |
+
$this->children[] = $child;
|
135 |
+
$child->parent = $this;
|
136 |
+
$child->root = $this->root;
|
137 |
+
}
|
138 |
+
// The child will have children if a debug node has been added
|
139 |
+
foreach ($child->children as $grandchild) {
|
140 |
+
$grandchild->root = $this->root;
|
141 |
+
}
|
142 |
+
}
|
143 |
+
|
144 |
+
/**
|
145 |
+
* Returns a value indicating if this node has children
|
146 |
+
* @return boolean true if the node has children, false if not
|
147 |
+
*/
|
148 |
+
public function hasChildren() {
|
149 |
+
return !empty($this->children);
|
150 |
+
}
|
151 |
+
|
152 |
+
/**
|
153 |
+
* Returns the node's children
|
154 |
+
* @return array the node's children
|
155 |
+
*/
|
156 |
+
public function getChildren() {
|
157 |
+
return $this->children;
|
158 |
+
}
|
159 |
+
|
160 |
+
/**
|
161 |
+
* Returns a value indicating if this node is a child of the passed node.
|
162 |
+
* This just checks the levels of the nodes. If this node is at a greater
|
163 |
+
* level than the passed node if is a child of it.
|
164 |
+
* @return boolean true if the node is a child of the passed node, false if not
|
165 |
+
*/
|
166 |
+
public function isChildOf($node) {
|
167 |
+
return $this->level > $node->level;
|
168 |
+
}
|
169 |
+
|
170 |
+
/**
|
171 |
+
* Returns the last child node of this node.
|
172 |
+
* @return SassNode the last child node of this node
|
173 |
+
*/
|
174 |
+
public function getLastChild() {
|
175 |
+
return $this->children[count($this->children) - 1];
|
176 |
+
}
|
177 |
+
|
178 |
+
/**
|
179 |
+
* Returns the level of this node.
|
180 |
+
* @return integer the level of this node
|
181 |
+
*/
|
182 |
+
public function getLevel() {
|
183 |
+
return $this->token->level;
|
184 |
+
}
|
185 |
+
|
186 |
+
/**
|
187 |
+
* Returns the source for this node
|
188 |
+
* @return string the source for this node
|
189 |
+
*/
|
190 |
+
public function getSource() {
|
191 |
+
return $this->token->source;
|
192 |
+
}
|
193 |
+
|
194 |
+
/**
|
195 |
+
* Returns the debug_info option setting for this node
|
196 |
+
* @return boolean the debug_info option setting for this node
|
197 |
+
*/
|
198 |
+
public function getDebug_info() {
|
199 |
+
return $this->parser->debug_info;
|
200 |
+
}
|
201 |
+
|
202 |
+
/**
|
203 |
+
* Returns the line number for this node
|
204 |
+
* @return string the line number for this node
|
205 |
+
*/
|
206 |
+
public function getLine() {
|
207 |
+
return $this->token->line;
|
208 |
+
}
|
209 |
+
|
210 |
+
/**
|
211 |
+
* Returns the line_numbers option setting for this node
|
212 |
+
* @return boolean the line_numbers option setting for this node
|
213 |
+
*/
|
214 |
+
public function getLine_numbers() {
|
215 |
+
return $this->parser->line_numbers;
|
216 |
+
}
|
217 |
+
|
218 |
+
/**
|
219 |
+
* Returns the filename for this node
|
220 |
+
* @return string the filename for this node
|
221 |
+
*/
|
222 |
+
public function getFilename() {
|
223 |
+
return $this->token->filename;
|
224 |
+
}
|
225 |
+
|
226 |
+
/**
|
227 |
+
* Returns the Sass parser.
|
228 |
+
* @return SassParser the Sass parser
|
229 |
+
*/
|
230 |
+
public function getParser() {
|
231 |
+
return $this->root->parser;
|
232 |
+
}
|
233 |
+
|
234 |
+
/**
|
235 |
+
* Returns the property syntax being used.
|
236 |
+
* @return string the property syntax being used
|
237 |
+
*/
|
238 |
+
public function getPropertySyntax() {
|
239 |
+
return $this->root->parser->propertySyntax;
|
240 |
+
}
|
241 |
+
|
242 |
+
/**
|
243 |
+
* Returns the SassScript parser.
|
244 |
+
* @return SassScriptParser the SassScript parser
|
245 |
+
*/
|
246 |
+
public function getScript() {
|
247 |
+
return $this->root->script;
|
248 |
+
}
|
249 |
+
|
250 |
+
/**
|
251 |
+
* Returns the renderer.
|
252 |
+
* @return SassRenderer the renderer
|
253 |
+
*/
|
254 |
+
public function getRenderer() {
|
255 |
+
return $this->root->renderer;
|
256 |
+
}
|
257 |
+
|
258 |
+
/**
|
259 |
+
* Returns the render style of the document tree.
|
260 |
+
* @return string the render style of the document tree
|
261 |
+
*/
|
262 |
+
public function getStyle() {
|
263 |
+
return $this->root->parser->style;
|
264 |
+
}
|
265 |
+
|
266 |
+
/**
|
267 |
+
* Returns a value indicating whether this node is in a directive
|
268 |
+
* @param boolean true if the node is in a directive, false if not
|
269 |
+
*/
|
270 |
+
public function inDirective() {
|
271 |
+
return $this->parent instanceof SassDirectiveNode ||
|
272 |
+
$this->parent instanceof SassDirectiveNode;
|
273 |
+
}
|
274 |
+
|
275 |
+
/**
|
276 |
+
* Returns a value indicating whether this node is in a SassScript directive
|
277 |
+
* @param boolean true if this node is in a SassScript directive, false if not
|
278 |
+
*/
|
279 |
+
public function inSassScriptDirective() {
|
280 |
+
return $this->parent instanceof SassEachNode ||
|
281 |
+
$this->parent->parent instanceof SassEachNode ||
|
282 |
+
$this->parent instanceof SassForNode ||
|
283 |
+
$this->parent->parent instanceof SassForNode ||
|
284 |
+
$this->parent instanceof SassIfNode ||
|
285 |
+
$this->parent->parent instanceof SassIfNode ||
|
286 |
+
$this->parent instanceof SassWhileNode ||
|
287 |
+
$this->parent->parent instanceof SassWhileNode;
|
288 |
+
}
|
289 |
+
|
290 |
+
/**
|
291 |
+
* Evaluates a SassScript expression.
|
292 |
+
* @param string expression to evaluate
|
293 |
+
* @param SassContext the context in which the expression is evaluated
|
294 |
+
* @return SassLiteral value of parsed expression
|
295 |
+
*/
|
296 |
+
public function evaluate($expression, $context, $x=null) {
|
297 |
+
$context->node = $this;
|
298 |
+
return $this->script->evaluate($expression, $context, $x);
|
299 |
+
}
|
300 |
+
|
301 |
+
/**
|
302 |
+
* Replace interpolated SassScript contained in '#{}' with the parsed value.
|
303 |
+
* @param string the text to interpolate
|
304 |
+
* @param SassContext the context in which the string is interpolated
|
305 |
+
* @return string the interpolated text
|
306 |
+
*/
|
307 |
+
public function interpolate($expression, $context) {
|
308 |
+
$context->node = $this;
|
309 |
+
return $this->script->interpolate($expression, $context);
|
310 |
+
}
|
311 |
+
|
312 |
+
/**
|
313 |
+
* Adds a warning to the node.
|
314 |
+
* @param string warning message
|
315 |
+
* @param array line
|
316 |
+
*/
|
317 |
+
public function addWarning($message) {
|
318 |
+
$warning = new SassDebugNode($this->token, $message);
|
319 |
+
$this->addChild($warning);
|
320 |
+
}
|
321 |
+
|
322 |
+
/**
|
323 |
+
* Parse the children of the node.
|
324 |
+
* @param SassContext the context in which the children are parsed
|
325 |
+
* @return array the parsed child nodes
|
326 |
+
*/
|
327 |
+
public function parseChildren($context) {
|
328 |
+
$children = array();
|
329 |
+
foreach ($this->children as $child) {
|
330 |
+
# child could be a SassLiteral /or/ SassNode
|
331 |
+
if (method_exists($child, 'parse')) {
|
332 |
+
$kid = $child->parse($context);
|
333 |
+
} else {
|
334 |
+
$kid = array($child);
|
335 |
+
}
|
336 |
+
$children = array_merge($children, $kid);
|
337 |
+
}
|
338 |
+
return $children;
|
339 |
+
}
|
340 |
+
|
341 |
+
/**
|
342 |
+
* Returns a value indicating if the token represents this type of node.
|
343 |
+
* @param object token
|
344 |
+
* @return boolean true if the token represents this type of node, false if not
|
345 |
+
*/
|
346 |
+
public static function isa($token) {
|
347 |
+
throw new SassNodeException('Child classes must override this method');
|
348 |
+
}
|
349 |
+
}
|
lib/phpsass/tree/SassNodeExceptions.php
ADDED
@@ -0,0 +1,132 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* SVN FILE: $Id$ */
|
3 |
+
/**
|
4 |
+
* SassNode exception classes.
|
5 |
+
* @author Chris Yates <chris.l.yates@gmail.com>
|
6 |
+
* @copyright Copyright (c) 2010 PBM Web Development
|
7 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
8 |
+
* @package PHamlP
|
9 |
+
* @subpackage Sass.tree
|
10 |
+
*/
|
11 |
+
|
12 |
+
require_once(dirname(__FILE__).'/../SassException.php');
|
13 |
+
|
14 |
+
/**
|
15 |
+
* SassNodeException class.
|
16 |
+
* @package PHamlP
|
17 |
+
* @subpackage Sass.tree
|
18 |
+
*/
|
19 |
+
class SassNodeException extends SassException {}
|
20 |
+
|
21 |
+
/**
|
22 |
+
* SassContextException class.
|
23 |
+
* @package PHamlP
|
24 |
+
* @subpackage Sass.tree
|
25 |
+
*/
|
26 |
+
class SassContextException extends SassNodeException {}
|
27 |
+
|
28 |
+
/**
|
29 |
+
* SassCommentNodeException class.
|
30 |
+
* @package PHamlP
|
31 |
+
* @subpackage Sass.tree
|
32 |
+
*/
|
33 |
+
class SassCommentNodeException extends SassNodeException {}
|
34 |
+
|
35 |
+
/**
|
36 |
+
* SassDebugNodeException class.
|
37 |
+
* @package PHamlP
|
38 |
+
* @subpackage Sass.tree
|
39 |
+
*/
|
40 |
+
class SassDebugNodeException extends SassNodeException {}
|
41 |
+
|
42 |
+
/**
|
43 |
+
* SassDirectiveNodeException class.
|
44 |
+
* @package PHamlP
|
45 |
+
* @subpackage Sass.tree
|
46 |
+
*/
|
47 |
+
class SassDirectiveNodeException extends SassNodeException {}
|
48 |
+
|
49 |
+
/**
|
50 |
+
* SassEachNodeException class.
|
51 |
+
* @package PHamlP
|
52 |
+
* @subpackage Sass.tree
|
53 |
+
*/
|
54 |
+
class SassEachNodeException extends SassNodeException {}
|
55 |
+
|
56 |
+
/**
|
57 |
+
* SassExtendNodeException class.
|
58 |
+
* @package PHamlP
|
59 |
+
* @subpackage Sass.tree
|
60 |
+
*/
|
61 |
+
class SassExtendNodeException extends SassNodeException {}
|
62 |
+
|
63 |
+
/**
|
64 |
+
* SassForNodeException class.
|
65 |
+
* @package PHamlP
|
66 |
+
* @subpackage Sass.tree
|
67 |
+
*/
|
68 |
+
class SassForNodeException extends SassNodeException {}
|
69 |
+
|
70 |
+
/**
|
71 |
+
* SassFunctionDefinitionNodeException class.
|
72 |
+
* @package PHamlP
|
73 |
+
* @subpackage Sass.tree
|
74 |
+
*/
|
75 |
+
class SassFunctionDefinitionNodeException extends SassNodeException {}
|
76 |
+
|
77 |
+
|
78 |
+
/**
|
79 |
+
* SassIfNodeException class.
|
80 |
+
* @package PHamlP
|
81 |
+
* @subpackage Sass.tree
|
82 |
+
*/
|
83 |
+
class SassIfNodeException extends SassNodeException {}
|
84 |
+
|
85 |
+
/**
|
86 |
+
* SassImportNodeException class.
|
87 |
+
* @package PHamlP
|
88 |
+
* @subpackage Sass.tree
|
89 |
+
*/
|
90 |
+
class SassImportNodeException extends SassNodeException {}
|
91 |
+
|
92 |
+
/**
|
93 |
+
* SassMixinDefinitionNodeException class.
|
94 |
+
* @package PHamlP
|
95 |
+
* @subpackage Sass.tree
|
96 |
+
*/
|
97 |
+
class SassMixinDefinitionNodeException extends SassNodeException {}
|
98 |
+
|
99 |
+
/**
|
100 |
+
* SassMixinNodeException class.
|
101 |
+
* @package PHamlP
|
102 |
+
* @subpackage Sass.tree
|
103 |
+
*/
|
104 |
+
class SassMixinNodeException extends SassNodeException {}
|
105 |
+
|
106 |
+
/**
|
107 |
+
* SassPropertyNodeException class.
|
108 |
+
* @package PHamlP
|
109 |
+
* @subpackage Sass.tree
|
110 |
+
*/
|
111 |
+
class SassPropertyNodeException extends SassNodeException {}
|
112 |
+
|
113 |
+
/**
|
114 |
+
* SassRuleNodeException class.
|
115 |
+
* @package PHamlP
|
116 |
+
* @subpackage Sass.tree
|
117 |
+
*/
|
118 |
+
class SassRuleNodeException extends SassNodeException {}
|
119 |
+
|
120 |
+
/**
|
121 |
+
* SassVariableNodeException class.
|
122 |
+
* @package PHamlP
|
123 |
+
* @subpackage Sass.tree
|
124 |
+
*/
|
125 |
+
class SassVariableNodeException extends SassNodeException {}
|
126 |
+
|
127 |
+
/**
|
128 |
+
* SassWhileNodeException class.
|
129 |
+
* @package PHamlP
|
130 |
+
* @subpackage Sass.tree
|
131 |
+
*/
|
132 |
+
class SassWhileNodeException extends SassNodeException {}
|
lib/phpsass/tree/SassPropertyNode.php
ADDED
@@ -0,0 +1,264 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* SVN FILE: $Id: SassPropertyNode.php 118 2010-09-21 09:45:11Z chris.l.yates@gmail.com $ */
|
3 |
+
/**
|
4 |
+
* SassPropertyNode class file.
|
5 |
+
* @author Chris Yates <chris.l.yates@gmail.com>
|
6 |
+
* @copyright Copyright (c) 2010 PBM Web Development
|
7 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
8 |
+
* @package PHamlP
|
9 |
+
* @subpackage Sass.tree
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* SassPropertyNode class.
|
14 |
+
* Represents a CSS property.
|
15 |
+
* @package PHamlP
|
16 |
+
* @subpackage Sass.tree
|
17 |
+
*/
|
18 |
+
class SassPropertyNode extends SassNode {
|
19 |
+
const MATCH_PROPERTY_SCSS = '/^([^\s=:"(\\\\:)]*)\s*(?:(= )|:)([^\:].*?)?(\s*!important.*)?$/';
|
20 |
+
const MATCH_PROPERTY_NEW = '/^([^\s=:"]+)\s*(?:(= )|:)([^\:].*?)?(\s*!important.*)?$/';
|
21 |
+
const MATCH_PROPERTY_OLD = '/^:([^\s=:]+)(?:\s*(=)\s*|\s+|$)(.*)(\s*!important.*)?/';
|
22 |
+
const MATCH_PSUEDO_SELECTOR = '/^:*\w[-\w]+\(?/i';
|
23 |
+
const MATCH_INTERPOLATION = '/^#\{(.*?)\}/i';
|
24 |
+
const MATCH_PROPRIETARY_SELECTOR = '/^:?-(moz|webkit|o|ms)-/';
|
25 |
+
const NAME = 1;
|
26 |
+
const SCRIPT = 2;
|
27 |
+
const VALUE = 3;
|
28 |
+
const IS_SCRIPT = '= ';
|
29 |
+
|
30 |
+
public static $psuedoSelectors = array(
|
31 |
+
'root',
|
32 |
+
'nth-child(',
|
33 |
+
'nth-last-child(',
|
34 |
+
'nth-of-type(',
|
35 |
+
'nth-last-of-type(',
|
36 |
+
'first-child',
|
37 |
+
'last-child',
|
38 |
+
'first-of-type',
|
39 |
+
'last-of-type',
|
40 |
+
'only-child',
|
41 |
+
'only-of-type',
|
42 |
+
'empty',
|
43 |
+
'link',
|
44 |
+
'visited',
|
45 |
+
'active',
|
46 |
+
'hover',
|
47 |
+
'focus',
|
48 |
+
'target',
|
49 |
+
'lang(',
|
50 |
+
'enabled',
|
51 |
+
'disabled',
|
52 |
+
'checked',
|
53 |
+
':first-line',
|
54 |
+
':first-letter',
|
55 |
+
':before',
|
56 |
+
':after',
|
57 |
+
// CSS 2.1
|
58 |
+
'first-line',
|
59 |
+
'first-letter',
|
60 |
+
'before',
|
61 |
+
'after',
|
62 |
+
// CSS 3
|
63 |
+
'not(',
|
64 |
+
);
|
65 |
+
|
66 |
+
/**
|
67 |
+
* @var string property name
|
68 |
+
*/
|
69 |
+
public $name;
|
70 |
+
/**
|
71 |
+
* @var string property value or expression to evaluate
|
72 |
+
*/
|
73 |
+
public $value;
|
74 |
+
|
75 |
+
|
76 |
+
/**
|
77 |
+
* @var boolean, wether the property is important
|
78 |
+
*/
|
79 |
+
public $important;
|
80 |
+
|
81 |
+
/**
|
82 |
+
* SassPropertyNode constructor.
|
83 |
+
* @param object source token
|
84 |
+
* @param string property syntax
|
85 |
+
* @return SassPropertyNode
|
86 |
+
*/
|
87 |
+
public function __construct($token, $syntax = 'new') {
|
88 |
+
parent::__construct($token);
|
89 |
+
$matches = self::match($token, $syntax);
|
90 |
+
$this->name = @$matches[self::NAME];
|
91 |
+
if (!isset($matches[self::VALUE])) {
|
92 |
+
$this->value = '';
|
93 |
+
}
|
94 |
+
else {
|
95 |
+
$this->value = $matches[self::VALUE];
|
96 |
+
if ($matches[self::SCRIPT] === self::IS_SCRIPT) {
|
97 |
+
$this->addWarning('Setting CSS properties with "=" is deprecated; use "{name}: {value};"',
|
98 |
+
array('{name}'=>$this->name, '{value}'=>$this->value)
|
99 |
+
);
|
100 |
+
}
|
101 |
+
}
|
102 |
+
$this->important = trim(array_pop($matches)) == '!important';
|
103 |
+
}
|
104 |
+
|
105 |
+
/**
|
106 |
+
* Parse this node.
|
107 |
+
* If the node is a property namespace return all parsed child nodes. If not
|
108 |
+
* return the parsed version of this node.
|
109 |
+
* @param SassContext the context in which this node is parsed
|
110 |
+
* @return array the parsed node
|
111 |
+
*/
|
112 |
+
public function parse($context) {
|
113 |
+
$return = array();
|
114 |
+
if ($this->value !== "") {
|
115 |
+
$node = clone $this;
|
116 |
+
$node->name = ($this->inNamespace() ? "{$this->namespace}-" : '') . $this->interpolate($this->name, $context);
|
117 |
+
|
118 |
+
$result = $this->evaluate($this->interpolate($this->value, $context), $context, SassScriptParser::CSS_PROPERTY);
|
119 |
+
|
120 |
+
$node->value = $result && is_object($result) ? $result->toString() : $this->value;
|
121 |
+
$return[] = $node;
|
122 |
+
}
|
123 |
+
if ($this->children) {
|
124 |
+
$return = array_merge($return, $this->parseChildren($context));
|
125 |
+
}
|
126 |
+
return $return;
|
127 |
+
}
|
128 |
+
|
129 |
+
/**
|
130 |
+
* Render this node.
|
131 |
+
* @return string the rendered node
|
132 |
+
*/
|
133 |
+
public function render() {
|
134 |
+
return $this->renderer->renderProperty($this);
|
135 |
+
}
|
136 |
+
|
137 |
+
/**
|
138 |
+
* Returns a value indicating if this node is in a namespace
|
139 |
+
* @return boolean true if this node is in a property namespace, false if not
|
140 |
+
*/
|
141 |
+
public function inNamespace() {
|
142 |
+
$parent = $this->parent;
|
143 |
+
do {
|
144 |
+
if ($parent instanceof SassPropertyNode) {
|
145 |
+
return true;
|
146 |
+
}
|
147 |
+
$parent = $parent->parent;
|
148 |
+
} while (is_object($parent));
|
149 |
+
return false;
|
150 |
+
}
|
151 |
+
|
152 |
+
/**
|
153 |
+
* Returns the namespace for this node
|
154 |
+
* @return string the namespace for this node
|
155 |
+
*/
|
156 |
+
public function getNamespace() {
|
157 |
+
$namespace = array();
|
158 |
+
$parent = $this->parent;
|
159 |
+
do {
|
160 |
+
if ($parent instanceof SassPropertyNode) {
|
161 |
+
$namespace[] = $parent->name;
|
162 |
+
}
|
163 |
+
$parent = $parent->parent;
|
164 |
+
} while (is_object($parent));
|
165 |
+
return join('-', array_reverse($namespace));
|
166 |
+
}
|
167 |
+
|
168 |
+
/**
|
169 |
+
* Returns the name of this property.
|
170 |
+
* If the property is in a namespace the namespace is prepended
|
171 |
+
* @return string the name of this property
|
172 |
+
*/
|
173 |
+
public function getName() {
|
174 |
+
return $this->name;
|
175 |
+
}
|
176 |
+
|
177 |
+
/**
|
178 |
+
* Returns the parsed value of this property.
|
179 |
+
* @return string the parsed value of this property
|
180 |
+
*/
|
181 |
+
public function getValue() {
|
182 |
+
return $this->value;
|
183 |
+
}
|
184 |
+
|
185 |
+
/**
|
186 |
+
* Returns a value indicating if the token represents this type of node.
|
187 |
+
* @param object token
|
188 |
+
* @param string the property syntax being used
|
189 |
+
* @return boolean true if the token represents this type of node, false if not
|
190 |
+
*/
|
191 |
+
public static function isa($token) {
|
192 |
+
if(!is_array($token)) {
|
193 |
+
$syntax = 'old';
|
194 |
+
}
|
195 |
+
else {
|
196 |
+
$syntax = $token['syntax'];
|
197 |
+
$token = $token['token'];
|
198 |
+
}
|
199 |
+
|
200 |
+
$matches = self::match($token, $syntax);
|
201 |
+
|
202 |
+
if (!empty($matches)) {
|
203 |
+
if (isset($matches[self::VALUE]) && self::isPseudoSelector($matches[self::VALUE])) {
|
204 |
+
return false;
|
205 |
+
}
|
206 |
+
if ($token->level === 0) {
|
207 |
+
# RL - if it's on the first level it's probably a false positive, not an error.
|
208 |
+
# even if it is a genuine error, no need to kill the compiler about it.
|
209 |
+
return false;
|
210 |
+
// throw new SassPropertyNodeException('Properties can not be assigned at root level', $token);
|
211 |
+
}
|
212 |
+
else {
|
213 |
+
return true;
|
214 |
+
}
|
215 |
+
}
|
216 |
+
else {
|
217 |
+
return false;
|
218 |
+
}
|
219 |
+
}
|
220 |
+
|
221 |
+
/**
|
222 |
+
* Returns the matches for this type of node.
|
223 |
+
* @param array the line to match
|
224 |
+
* @param string the property syntax being used
|
225 |
+
* @return array matches
|
226 |
+
*/
|
227 |
+
public static function match($token, $syntax) {
|
228 |
+
switch ($syntax) {
|
229 |
+
case 'scss':
|
230 |
+
preg_match(self::MATCH_PROPERTY_SCSS, $token->source, $matches);
|
231 |
+
break;
|
232 |
+
case 'new':
|
233 |
+
preg_match(self::MATCH_PROPERTY_NEW, $token->source, $matches);
|
234 |
+
break;
|
235 |
+
case 'old':
|
236 |
+
preg_match(self::MATCH_PROPERTY_OLD, $token->source, $matches);
|
237 |
+
break;
|
238 |
+
default:
|
239 |
+
if (preg_match(self::MATCH_PROPERTY_NEW, $token->source, $matches) == 0) {
|
240 |
+
preg_match(self::MATCH_PROPERTY_OLD, $token->source, $matches);
|
241 |
+
}
|
242 |
+
break;
|
243 |
+
}
|
244 |
+
return $matches;
|
245 |
+
}
|
246 |
+
|
247 |
+
/**
|
248 |
+
* Returns a value indicating if the string starts with a pseudo selector.
|
249 |
+
* This is used to reject pseudo selectors as property values as, for example,
|
250 |
+
* "a:hover" and "text-decoration:underline" look the same to the property
|
251 |
+
* match regex.
|
252 |
+
* It will also match interpolation to allow for constructs such as
|
253 |
+
* content:#{$pos}
|
254 |
+
* @see isa()
|
255 |
+
* @param string the string to test
|
256 |
+
* @return bool true if the string starts with a pseudo selector, false if not
|
257 |
+
*/
|
258 |
+
public static function isPseudoSelector($string) {
|
259 |
+
preg_match(self::MATCH_PSUEDO_SELECTOR, $string, $matches);
|
260 |
+
return (isset($matches[0]) && in_array($matches[0], self::$psuedoSelectors)) ||
|
261 |
+
preg_match(self::MATCH_INTERPOLATION, $string) ||
|
262 |
+
preg_match(self::MATCH_PROPRIETARY_SELECTOR, $string);
|
263 |
+
}
|
264 |
+
}
|
lib/phpsass/tree/SassReturnNode.php
ADDED
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* SVN FILE: $Id$ */
|
3 |
+
/**
|
4 |
+
* SassReturnNode class file.
|
5 |
+
* @author Chris Yates <chris.l.yates@gmail.com>
|
6 |
+
* @copyright Copyright (c) 2010 PBM Web Development
|
7 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
8 |
+
* @package PHamlP
|
9 |
+
* @subpackage Sass.tree
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* SassReturnNode class.
|
14 |
+
* Represents a Return.
|
15 |
+
* @package PHamlP
|
16 |
+
* @subpackage Sass.tree
|
17 |
+
*/
|
18 |
+
class SassReturnNode extends SassNode {
|
19 |
+
const NODE_IDENTIFIER = '+';
|
20 |
+
const MATCH = '/^(@return\s+)(.*)$/i';
|
21 |
+
const IDENTIFIER = 1;
|
22 |
+
const STATEMENT = 2;
|
23 |
+
|
24 |
+
/**
|
25 |
+
* @var statement to execute and return
|
26 |
+
*/
|
27 |
+
private $statement;
|
28 |
+
|
29 |
+
/**
|
30 |
+
* SassReturnNode constructor.
|
31 |
+
* @param object source token
|
32 |
+
* @return SassReturnNode
|
33 |
+
*/
|
34 |
+
public function __construct($token) {
|
35 |
+
parent::__construct($token);
|
36 |
+
preg_match(self::MATCH, $token->source, $matches);
|
37 |
+
|
38 |
+
if (empty($matches)) {
|
39 |
+
return new SassBoolean('false');
|
40 |
+
}
|
41 |
+
|
42 |
+
$this->statement = $matches[self::STATEMENT];
|
43 |
+
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Parse this node.
|
47 |
+
* Set passed arguments and any optional arguments not passed to their
|
48 |
+
* defaults, then render the children of the return definition.
|
49 |
+
* @param SassContext the context in which this node is parsed
|
50 |
+
* @return array the parsed node
|
51 |
+
*/
|
52 |
+
public function parse($pcontext) {
|
53 |
+
$return = $this;
|
54 |
+
$context = new SassContext($pcontext);
|
55 |
+
$statement = $this->statement;
|
56 |
+
|
57 |
+
$parent = $this->parent->parent->parser;
|
58 |
+
$script = $this->parent->parent->script;
|
59 |
+
$lexer = $script->lexer;
|
60 |
+
|
61 |
+
$result = $script->evaluate($statement, $context);
|
62 |
+
|
63 |
+
throw new SassReturn($result);
|
64 |
+
}
|
65 |
+
|
66 |
+
/**
|
67 |
+
* Returns a value indicating if the token represents this type of node.
|
68 |
+
* @param object token
|
69 |
+
* @return boolean true if the token represents this type of node, false if not
|
70 |
+
*/
|
71 |
+
public static function isa($token) {
|
72 |
+
return $token->source[0] === self::NODE_IDENTIFIER;
|
73 |
+
}
|
74 |
+
}
|
75 |
+
|
76 |
+
class SassReturn extends Exception {
|
77 |
+
function __construct($value) {
|
78 |
+
$this->value = $value;
|
79 |
+
}
|
80 |
+
}
|
lib/phpsass/tree/SassRootNode.php
ADDED
@@ -0,0 +1,112 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* SVN FILE: $Id$ */
|
3 |
+
/**
|
4 |
+
* SassRootNode class file.
|
5 |
+
* @author Chris Yates <chris.l.yates@gmail.com>
|
6 |
+
* @copyright Copyright (c) 2010 PBM Web Development
|
7 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
8 |
+
* @package PHamlP
|
9 |
+
* @subpackage Sass.tree
|
10 |
+
*/
|
11 |
+
|
12 |
+
require_once(dirname(__FILE__).'/../script/SassScriptParser.php');
|
13 |
+
require_once(dirname(__FILE__).'/../renderers/SassRenderer.php');
|
14 |
+
|
15 |
+
/**
|
16 |
+
* SassRootNode class.
|
17 |
+
* Also the root node of a document.
|
18 |
+
* @package PHamlP
|
19 |
+
* @subpackage Sass.tree
|
20 |
+
*/
|
21 |
+
class SassRootNode extends SassNode {
|
22 |
+
/**
|
23 |
+
* @var SassScriptParser SassScript parser
|
24 |
+
*/
|
25 |
+
public $script;
|
26 |
+
/**
|
27 |
+
* @var SassRenderer the renderer for this node
|
28 |
+
*/
|
29 |
+
public $renderer;
|
30 |
+
/**
|
31 |
+
* @var SassParser
|
32 |
+
*/
|
33 |
+
public $parser;
|
34 |
+
/**
|
35 |
+
* @var array extenders for this tree in the form extendee=>extender
|
36 |
+
*/
|
37 |
+
public $extenders = array();
|
38 |
+
|
39 |
+
/**
|
40 |
+
* Extend_parent - for resolving extends across imported files.
|
41 |
+
*/
|
42 |
+
public $extend_parent = null;
|
43 |
+
|
44 |
+
/**
|
45 |
+
* Root SassNode constructor.
|
46 |
+
* @param SassParser Sass parser
|
47 |
+
* @return SassNode
|
48 |
+
*/
|
49 |
+
public function __construct($parser) {
|
50 |
+
parent::__construct((object) array(
|
51 |
+
'source' => '',
|
52 |
+
'level' => -1,
|
53 |
+
'filename' => $parser->filename,
|
54 |
+
'line' => 0,
|
55 |
+
));
|
56 |
+
$this->parser = $parser;
|
57 |
+
$this->script = new SassScriptParser();
|
58 |
+
$this->renderer = SassRenderer::getRenderer($parser->style);
|
59 |
+
$this->root = $this;
|
60 |
+
}
|
61 |
+
|
62 |
+
/**
|
63 |
+
* Parses this node and its children into the render tree.
|
64 |
+
* Dynamic nodes are evaluated, files imported, etc.
|
65 |
+
* Only static nodes for rendering are in the resulting tree.
|
66 |
+
* @param SassContext the context in which this node is parsed
|
67 |
+
* @return SassNode root node of the render tree
|
68 |
+
*/
|
69 |
+
public function parse($context) {
|
70 |
+
$node = clone $this;
|
71 |
+
$node->children = $this->parseChildren($context);
|
72 |
+
return $node;
|
73 |
+
}
|
74 |
+
|
75 |
+
/**
|
76 |
+
* Render this node.
|
77 |
+
* @return string the rendered node
|
78 |
+
*/
|
79 |
+
public function render($context = null) {
|
80 |
+
$context = new SassContext($context);
|
81 |
+
$node = $this->parse($context);
|
82 |
+
$output = '';
|
83 |
+
foreach ($node->children as $child) {
|
84 |
+
$output .= $child->render();
|
85 |
+
} // foreach
|
86 |
+
return $output;
|
87 |
+
}
|
88 |
+
|
89 |
+
public function extend($extendee, $selectors) {
|
90 |
+
if ($this->extend_parent && method_exists($this->extend_parent, 'extend')) {
|
91 |
+
return $this->extend_parent->extend($extendee, $selectors);
|
92 |
+
}
|
93 |
+
$this->extenders[$extendee] = (isset($this->extenders[$extendee])
|
94 |
+
? array_merge($this->extenders[$extendee], $selectors) : $selectors);
|
95 |
+
}
|
96 |
+
|
97 |
+
public function getExtenders() {
|
98 |
+
if ($this->extend_parent && method_exists($this->extend_parent, 'getExtenders')) {
|
99 |
+
return $this->extend_parent->getExtenders();
|
100 |
+
}
|
101 |
+
return $this->extenders;
|
102 |
+
}
|
103 |
+
|
104 |
+
/**
|
105 |
+
* Returns a value indicating if the line represents this type of node.
|
106 |
+
* Child classes must override this method.
|
107 |
+
* @throws SassNodeException if not overriden
|
108 |
+
*/
|
109 |
+
public static function isa($line) {
|
110 |
+
throw new SassNodeException('Child classes must override this method');
|
111 |
+
}
|
112 |
+
}
|
lib/phpsass/tree/SassRuleNode.php
ADDED
@@ -0,0 +1,378 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* SVN FILE: $Id$ */
|
3 |
+
/**
|
4 |
+
* SassRuleNode class file.
|
5 |
+
* @author Chris Yates <chris.l.yates@gmail.com>
|
6 |
+
* @copyright Copyright (c) 2010 PBM Web Development
|
7 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
8 |
+
* @package PHamlP
|
9 |
+
* @subpackage Sass.tree
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* SassRuleNode class.
|
14 |
+
* Represents a CSS rule.
|
15 |
+
* @package PHamlP
|
16 |
+
* @subpackage Sass.tree
|
17 |
+
*/
|
18 |
+
class SassRuleNode extends SassNode {
|
19 |
+
const MATCH = '/^(.+?)(?:\s*\{)?$/';
|
20 |
+
const SELECTOR = 1;
|
21 |
+
const CONTINUED = ',';
|
22 |
+
|
23 |
+
/**
|
24 |
+
* @const string that is replaced with the parent node selector
|
25 |
+
*/
|
26 |
+
const PARENT_REFERENCE = '&';
|
27 |
+
|
28 |
+
/**
|
29 |
+
* @var array selector(s)
|
30 |
+
*/
|
31 |
+
private $selectors = array();
|
32 |
+
|
33 |
+
/**
|
34 |
+
* @var array parent selectors
|
35 |
+
*/
|
36 |
+
private $parentSelectors = array();
|
37 |
+
|
38 |
+
/**
|
39 |
+
* @var array resolved selectors
|
40 |
+
*/
|
41 |
+
private $resolvedSelectors = array();
|
42 |
+
|
43 |
+
/**
|
44 |
+
* @var boolean whether the node expects more selectors
|
45 |
+
*/
|
46 |
+
private $isContinued;
|
47 |
+
|
48 |
+
/**
|
49 |
+
* SassRuleNode constructor.
|
50 |
+
* @param object source token
|
51 |
+
* @param string rule selector
|
52 |
+
* @return SassRuleNode
|
53 |
+
*/
|
54 |
+
public function __construct($token) {
|
55 |
+
parent::__construct($token);
|
56 |
+
preg_match(self::MATCH, $token->source, $matches);
|
57 |
+
$this->addSelectors($matches[SassRuleNode::SELECTOR]);
|
58 |
+
}
|
59 |
+
|
60 |
+
/**
|
61 |
+
* Adds selector(s) to the rule.
|
62 |
+
* If the selectors are to continue for the rule the selector must end in a comma
|
63 |
+
* @param string selector
|
64 |
+
*/
|
65 |
+
public function addSelectors($selectors, $explode = true) {
|
66 |
+
$this->isContinued = substr($selectors, -1) === self::CONTINUED;
|
67 |
+
$this->selectors = array_merge($this->selectors, $explode ? $this->explode($selectors) : $selectors);
|
68 |
+
}
|
69 |
+
|
70 |
+
/**
|
71 |
+
* Returns a value indicating if the selectors for this rule are to be continued.
|
72 |
+
* @param boolean true if the selectors for this rule are to be continued,
|
73 |
+
* false if not
|
74 |
+
*/
|
75 |
+
public function getIsContinued() {
|
76 |
+
return $this->isContinued;
|
77 |
+
}
|
78 |
+
|
79 |
+
/**
|
80 |
+
* Parse this node and its children into static nodes.
|
81 |
+
* @param SassContext the context in which this node is parsed
|
82 |
+
* @return array the parsed node and its children
|
83 |
+
*/
|
84 |
+
public function parse($context) {
|
85 |
+
$node = clone $this;
|
86 |
+
$node->selectors = $this->resolveSelectors($context);
|
87 |
+
$node->children = $this->parseChildren($context);
|
88 |
+
return array($node);
|
89 |
+
}
|
90 |
+
|
91 |
+
/**
|
92 |
+
* Render this node and its children to CSS.
|
93 |
+
* @return string the rendered node
|
94 |
+
*/
|
95 |
+
public function render() {
|
96 |
+
$this->extend();
|
97 |
+
$rules = '';
|
98 |
+
$properties = array();
|
99 |
+
|
100 |
+
foreach ($this->children as $child) {
|
101 |
+
$child->parent = $this;
|
102 |
+
if ($child instanceof SassRuleNode) {
|
103 |
+
$rules .= $child->render();
|
104 |
+
}
|
105 |
+
else {
|
106 |
+
$properties[] = $child->render();
|
107 |
+
}
|
108 |
+
}
|
109 |
+
|
110 |
+
return $this->renderer->renderRule($this, $properties, $rules);
|
111 |
+
}
|
112 |
+
|
113 |
+
/**
|
114 |
+
* Extend this nodes selectors
|
115 |
+
* $extendee is the subject of the @extend directive
|
116 |
+
* $extender is the selector that contains the @extend directive
|
117 |
+
* $selector a selector or selector sequence that is to be extended
|
118 |
+
*/
|
119 |
+
public function extend() {
|
120 |
+
foreach ($this->root->getExtenders() as $extendee => $extenders) {
|
121 |
+
if ($this->isPsuedo($extendee)) {
|
122 |
+
$extendee = explode(':', $extendee);
|
123 |
+
$pattern = preg_quote($extendee[0]).'((\.[-\w]+)*):'.preg_quote($extendee[1]);
|
124 |
+
}
|
125 |
+
else {
|
126 |
+
$pattern = preg_quote($extendee);
|
127 |
+
}
|
128 |
+
|
129 |
+
foreach (preg_grep('/'.$pattern.'/', $this->selectors) as $selector) {
|
130 |
+
foreach ($extenders as $extender) {
|
131 |
+
# first if establishes that we are using a placeholder and the extendee begins with a tag
|
132 |
+
if ($extendee{0} == '%' && $selector{0} != '%' && preg_match('/(^| )[a-zA-Z][^%]*' . preg_quote($extendee) . '([^a-z0-9_-]|$)/', $selector)) {
|
133 |
+
# the second if establishes that the extender is a tag rather than a class/id
|
134 |
+
$zero = ord(strtolower(substr($extender, 0, 1))); // cheaper than regex
|
135 |
+
if ($zero >= 97 && $zero <= 122) {
|
136 |
+
continue;
|
137 |
+
}
|
138 |
+
}
|
139 |
+
if (is_array($extendee)) {
|
140 |
+
$this->selectors[] = preg_replace('/(.*?)'.$pattern.'([^a-zA-Z0-9_-]|$)/', '$1' . $extender . '$2', $selector);
|
141 |
+
}
|
142 |
+
elseif ($this->isSequence($extender) || $this->isSequence($selector)) {
|
143 |
+
$this->selectors = array_merge($this->selectors, $this->mergeSequence($extender, $extendee, $selector));
|
144 |
+
}
|
145 |
+
else {
|
146 |
+
$this->selectors[] = str_replace($extendee, $extender, $selector);
|
147 |
+
}
|
148 |
+
}
|
149 |
+
}
|
150 |
+
$this->selectors = array_unique($this->selectors);
|
151 |
+
}
|
152 |
+
}
|
153 |
+
|
154 |
+
/**
|
155 |
+
* Tests whether the selector is a psuedo selector
|
156 |
+
* @param string selector to test
|
157 |
+
* @return boolean true if the selector is a psuedo selector, false if not
|
158 |
+
*/
|
159 |
+
private function isPsuedo($selector) {
|
160 |
+
return strpos($selector, ':') !== false;
|
161 |
+
}
|
162 |
+
|
163 |
+
/**
|
164 |
+
* Tests whether the selector is a sequence selector
|
165 |
+
* @param string selector to test
|
166 |
+
* @return boolean true if the selector is a sequence selector, false if not
|
167 |
+
*/
|
168 |
+
private function isSequence($selector) {
|
169 |
+
return strpos($selector, ' ') !== false;
|
170 |
+
}
|
171 |
+
|
172 |
+
public function isPlaceholder($selector) {
|
173 |
+
return strpos($selector, '%') !== false;
|
174 |
+
}
|
175 |
+
|
176 |
+
/**
|
177 |
+
* Merges selector sequences
|
178 |
+
* @param string the extender selector
|
179 |
+
* @param string selector to extend
|
180 |
+
* @return array the merged sequences
|
181 |
+
*/
|
182 |
+
private function mergeSequence($extender, $extendee, $selector) {
|
183 |
+
// if it's a placeholder, be lazy. Needs tests.
|
184 |
+
if ($extendee[0] == '%') {
|
185 |
+
// need to stop things like a%foo accepting div { @extend %foo }
|
186 |
+
return array(str_replace($extendee, $extender, $selector));
|
187 |
+
}
|
188 |
+
|
189 |
+
$extender = explode(' ', $extender);
|
190 |
+
$end = array_pop($extender);
|
191 |
+
$selector = explode(' ', $selector);
|
192 |
+
array_pop($selector);
|
193 |
+
|
194 |
+
$common = array();
|
195 |
+
if (count($extender) && count($selector)) {
|
196 |
+
while(trim($extender[0]) === trim($selector[0])) {
|
197 |
+
$common[] = array_shift($selector);
|
198 |
+
array_shift($extender);
|
199 |
+
if (!count($extender)) {
|
200 |
+
break;
|
201 |
+
}
|
202 |
+
}
|
203 |
+
}
|
204 |
+
|
205 |
+
$beginning = (!empty($common) ? join(' ', $common) . ' ' : '');
|
206 |
+
|
207 |
+
# Richard Lyon - 2011-10-25 - removes duplicates by uniquing and trimming.
|
208 |
+
# regex removes whitespace from start and and end of string as well as removing
|
209 |
+
# whitespace following whitespace. slightly quicker than a trim and simpler replace
|
210 |
+
return array_unique(array(
|
211 |
+
preg_replace('/(^\s+|(\s)\s+|\s+$)/', '$2', $beginning.join(' ', $selector).' '.join(' ', $extender). ' ' . $end),
|
212 |
+
preg_replace('/(^\s+|(\s)\s+|\s+$)/', '$2', $beginning.join(' ', $extender).' '.join(' ', $selector). ' ' . $end)
|
213 |
+
));
|
214 |
+
}
|
215 |
+
|
216 |
+
/**
|
217 |
+
* Returns the selectors
|
218 |
+
* @return array selectors
|
219 |
+
*/
|
220 |
+
public function getSelectors() {
|
221 |
+
return $this->selectors;
|
222 |
+
}
|
223 |
+
|
224 |
+
/**
|
225 |
+
* Resolves selectors.
|
226 |
+
* Interpolates SassScript in selectors and resolves any parent references or
|
227 |
+
* appends the parent selectors.
|
228 |
+
* @param SassContext the context in which this node is parsed
|
229 |
+
*
|
230 |
+
* Change: 7/Dec/11 - change to make selector ordering conform to Ruby compiler.
|
231 |
+
*/
|
232 |
+
public function resolveSelectors($context) {
|
233 |
+
$resolvedSelectors = $normalSelectors = array();
|
234 |
+
$this->parentSelectors = $this->getParentSelectors($context);
|
235 |
+
|
236 |
+
foreach ($this->selectors as $key=>$selector) {
|
237 |
+
$selector = $this->interpolate($selector, $context);
|
238 |
+
$selectors = SassList::_build_list($selector);
|
239 |
+
|
240 |
+
foreach ($selectors as $selector) {
|
241 |
+
$selector = trim($selector, ' \'"'); // strip whitespace and quotes, just-in-case.
|
242 |
+
if ($this->hasParentReference($selector)) {
|
243 |
+
$resolvedSelectors = array_merge($resolvedSelectors, $this->resolveParentReferences($selector, $context));
|
244 |
+
}
|
245 |
+
else {
|
246 |
+
$normalSelectors[] = $selector;
|
247 |
+
}
|
248 |
+
}
|
249 |
+
} // foreach
|
250 |
+
|
251 |
+
// merge with parent selectors
|
252 |
+
if ($this->parentSelectors) {
|
253 |
+
$return = array();
|
254 |
+
foreach ($this->parentSelectors as $parent) {
|
255 |
+
foreach ($normalSelectors as $selector) {
|
256 |
+
$spacer = (substr($selector, 0, 1) == '[') ? '' : ' ';
|
257 |
+
|
258 |
+
$return[] = $parent . $spacer . $selector;
|
259 |
+
}
|
260 |
+
}
|
261 |
+
$normalSelectors = $return;
|
262 |
+
}
|
263 |
+
|
264 |
+
return array_merge($normalSelectors, $resolvedSelectors);
|
265 |
+
}
|
266 |
+
|
267 |
+
/**
|
268 |
+
* Returns the parent selector(s) for this node.
|
269 |
+
* This in an empty array if there is no parent selector.
|
270 |
+
* @return array the parent selector for this node
|
271 |
+
*/
|
272 |
+
protected function getParentSelectors($context) {
|
273 |
+
$ancestor = $this->parent;
|
274 |
+
while (!$ancestor instanceof SassRuleNode && $ancestor->hasParent()) {
|
275 |
+
$ancestor = $ancestor->parent;
|
276 |
+
}
|
277 |
+
|
278 |
+
if ($ancestor instanceof SassRuleNode) {
|
279 |
+
return $ancestor->resolveSelectors($context);
|
280 |
+
}
|
281 |
+
return array();
|
282 |
+
}
|
283 |
+
|
284 |
+
/**
|
285 |
+
* Returns the position of the first parent reference in the selector.
|
286 |
+
* If there is no parent reference in the selector this function returns
|
287 |
+
* boolean FALSE.
|
288 |
+
* Note that the return value may be non-Boolean that evaluates to FALSE,
|
289 |
+
* i.e. 0. The return value should be tested using the === operator.
|
290 |
+
* @param string selector to test
|
291 |
+
* @return mixed integer: position of the the first parent reference,
|
292 |
+
* boolean: false if there is no parent reference.
|
293 |
+
*/
|
294 |
+
private function parentReferencePos($selector) {
|
295 |
+
$inString = '';
|
296 |
+
for ($i = 0, $l = strlen($selector); $i < $l; $i++) {
|
297 |
+
$c = $selector[$i];
|
298 |
+
if ($c === self::PARENT_REFERENCE && empty($inString)) {
|
299 |
+
return $i;
|
300 |
+
}
|
301 |
+
elseif (empty($inString) && ($c === '"' || $c === "'")) {
|
302 |
+
$inString = $c;
|
303 |
+
}
|
304 |
+
elseif ($c === $inString) {
|
305 |
+
$inString = '';
|
306 |
+
}
|
307 |
+
}
|
308 |
+
return false;
|
309 |
+
}
|
310 |
+
|
311 |
+
/**
|
312 |
+
* Determines if there is a parent reference in the selector
|
313 |
+
* @param string selector
|
314 |
+
* @return boolean true if there is a parent reference in the selector
|
315 |
+
*/
|
316 |
+
private function hasParentReference($selector) {
|
317 |
+
return $this->parentReferencePos($selector) !== false;
|
318 |
+
}
|
319 |
+
|
320 |
+
/**
|
321 |
+
* Resolves parent references in the selector
|
322 |
+
* @param string selector
|
323 |
+
* @return string selector with parent references resolved
|
324 |
+
*/
|
325 |
+
private function resolveParentReferences($selector, $context) {
|
326 |
+
$resolvedReferences = array();
|
327 |
+
if (!count($this->parentSelectors)) {
|
328 |
+
throw new SassRuleNodeException('Can not use parent selector (' . self::PARENT_REFERENCE . ') when no parent selectors', $this);
|
329 |
+
}
|
330 |
+
foreach ($this->getParentSelectors($context) as $parentSelector) {
|
331 |
+
$resolvedReferences[] = str_replace(self::PARENT_REFERENCE, $parentSelector, $selector);
|
332 |
+
}
|
333 |
+
return $resolvedReferences;
|
334 |
+
}
|
335 |
+
|
336 |
+
/**
|
337 |
+
* Explodes a string of selectors into an array.
|
338 |
+
* We can't use PHP::explode as this will potentially explode attribute
|
339 |
+
* matches in the selector, e.g. div[title="some,value"] and interpolations.
|
340 |
+
* @param string selectors
|
341 |
+
* @return array selectors
|
342 |
+
*/
|
343 |
+
private function explode($string) {
|
344 |
+
$selectors = array();
|
345 |
+
$inString = false;
|
346 |
+
$interpolate = false;
|
347 |
+
$selector = '';
|
348 |
+
|
349 |
+
for ($i = 0, $l = strlen($string); $i < $l; $i++) {
|
350 |
+
$c = $string[$i];
|
351 |
+
if ($c === self::CONTINUED && !$inString && !$interpolate) {
|
352 |
+
$selectors[] = trim($selector);
|
353 |
+
$selector = '';
|
354 |
+
}
|
355 |
+
else {
|
356 |
+
$selector .= $c;
|
357 |
+
if ($c === '"' || $c === "'") {
|
358 |
+
do {
|
359 |
+
$_c = $string[++$i];
|
360 |
+
$selector .= $_c;
|
361 |
+
} while ($_c !== $c && isset($string[$i+1]));
|
362 |
+
}
|
363 |
+
elseif ($c === '#' && $string[$i+1] === '{') {
|
364 |
+
do {
|
365 |
+
$c = $string[++$i];
|
366 |
+
$selector .= $c;
|
367 |
+
} while ($c !== '}');
|
368 |
+
}
|
369 |
+
}
|
370 |
+
}
|
371 |
+
|
372 |
+
if (!empty($selector)) {
|
373 |
+
$selectors[] = trim($selector);
|
374 |
+
}
|
375 |
+
|
376 |
+
return $selectors;
|
377 |
+
}
|
378 |
+
}
|
lib/phpsass/tree/SassVariableNode.php
ADDED
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* SVN FILE: $Id$ */
|
3 |
+
/**
|
4 |
+
* SassVariableNode class file.
|
5 |
+
* @author Chris Yates <chris.l.yates@gmail.com>
|
6 |
+
* @copyright Copyright (c) 2010 PBM Web Development
|
7 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
8 |
+
* @package PHamlP
|
9 |
+
* @subpackage Sass.tree
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* SassVariableNode class.
|
14 |
+
* Represents a variable.
|
15 |
+
* @package PHamlP
|
16 |
+
* @subpackage Sass.tree
|
17 |
+
*/
|
18 |
+
class SassVariableNode extends SassNode {
|
19 |
+
const MATCH = '/^([!$])([\w-]+)\s*:?\s*((\|\|)?=)?\s*(.+?)\s*(!default)?;?$/i';
|
20 |
+
const IDENTIFIER = 1;
|
21 |
+
const NAME = 2;
|
22 |
+
const SASS_ASSIGNMENT = 3;
|
23 |
+
const SASS_DEFAULT = 4;
|
24 |
+
const VALUE = 5;
|
25 |
+
const SCSS_DEFAULT = 6;
|
26 |
+
const SASS_IDENTIFIER = '!';
|
27 |
+
const SCSS_IDENTIFIER = '$';
|
28 |
+
|
29 |
+
/**
|
30 |
+
* @var string name of the variable
|
31 |
+
*/
|
32 |
+
private $name;
|
33 |
+
/**
|
34 |
+
* @var string value of the variable or expression to evaluate
|
35 |
+
*/
|
36 |
+
private $value;
|
37 |
+
/**
|
38 |
+
* @var boolean whether the variable is optionally assigned
|
39 |
+
*/
|
40 |
+
private $isDefault;
|
41 |
+
|
42 |
+
/**
|
43 |
+
* SassVariableNode constructor.
|
44 |
+
* @param object source token
|
45 |
+
* @return SassVariableNode
|
46 |
+
*/
|
47 |
+
public function __construct($token) {
|
48 |
+
parent::__construct($token);
|
49 |
+
preg_match(self::MATCH, $token->source, $matches);
|
50 |
+
if (empty($matches[self::NAME]) || ($matches[self::VALUE] === '')) {
|
51 |
+
throw new SassVariableNodeException('Invalid variable definition; name and expression required', $this);
|
52 |
+
}
|
53 |
+
|
54 |
+
$this->name = $matches[self::NAME];
|
55 |
+
$this->value = $matches[self::VALUE];
|
56 |
+
$this->isDefault = (!empty($matches[self::SASS_DEFAULT]) || !empty($matches[self::SCSS_DEFAULT]));
|
57 |
+
|
58 |
+
// Warn about deprecated features
|
59 |
+
if ($matches[self::IDENTIFIER] === self::SASS_IDENTIFIER) {
|
60 |
+
$this->addWarning('Variables prefixed with "!" is deprecated; use "' . $this->name . '"');
|
61 |
+
}
|
62 |
+
if (!empty($matches[SassVariableNode::SASS_ASSIGNMENT])) {
|
63 |
+
$this->addWarning('Setting variables with "' . (!empty($matches[SassVariableNode::SASS_DEFAULT])?'||':'') . '=" is deprecated; use "$' . $this->name . ': ' . $this->value . (!empty($matches[SassVariableNode::SASS_DEFAULT]) ? ' !default' : ''));
|
64 |
+
}
|
65 |
+
}
|
66 |
+
|
67 |
+
/**
|
68 |
+
* Parse this node.
|
69 |
+
* Sets the variable in the current context.
|
70 |
+
* @param SassContext the context in which this node is parsed
|
71 |
+
* @return array the parsed node - an empty array
|
72 |
+
*/
|
73 |
+
public function parse($context) {
|
74 |
+
if (!$this->isDefault || !$context->hasVariable($this->name)) {
|
75 |
+
$context->setVariable(
|
76 |
+
$this->name, $this->evaluate($this->value, $context)
|
77 |
+
);
|
78 |
+
}
|
79 |
+
$this->parseChildren($context); // Parse any warnings
|
80 |
+
return array();
|
81 |
+
}
|
82 |
+
|
83 |
+
/**
|
84 |
+
* Returns a value indicating if the token represents this type of node.
|
85 |
+
* @param object token
|
86 |
+
* @return boolean true if the token represents this type of node, false if not
|
87 |
+
*/
|
88 |
+
public static function isa($token) {
|
89 |
+
return $token->source[0] === self::SASS_IDENTIFIER || $token->source[0] === self::SCSS_IDENTIFIER;
|
90 |
+
}
|
91 |
+
}
|
lib/phpsass/tree/SassWarnNode.php
ADDED
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* SVN FILE: $Id$ */
|
3 |
+
/**
|
4 |
+
* SassReturnNode class file.
|
5 |
+
* @author Richard Lyon <richthegeek@gmail.com>
|
6 |
+
* @copyright none
|
7 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
8 |
+
* @package PHamlP
|
9 |
+
* @subpackage Sass.tree
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* SassReturnNode class.
|
14 |
+
* Represents a Return.
|
15 |
+
* @package PHamlP
|
16 |
+
* @subpackage Sass.tree
|
17 |
+
*/
|
18 |
+
class SassWarnNode extends SassNode {
|
19 |
+
const NODE_IDENTIFIER = '+';
|
20 |
+
const MATCH = '/^(@warn\s+)(["\']?)(.*?)(["\']?)$/i';
|
21 |
+
const IDENTIFIER = 1;
|
22 |
+
const STATEMENT = 3;
|
23 |
+
|
24 |
+
/**
|
25 |
+
* @var statement to execute and return
|
26 |
+
*/
|
27 |
+
private $statement;
|
28 |
+
|
29 |
+
/**
|
30 |
+
* SassReturnNode constructor.
|
31 |
+
* @param object source token
|
32 |
+
* @return SassReturnNode
|
33 |
+
*/
|
34 |
+
public function __construct($token) {
|
35 |
+
parent::__construct($token);
|
36 |
+
preg_match(self::MATCH, $token->source, $matches);
|
37 |
+
|
38 |
+
if (empty($matches)) {
|
39 |
+
return new SassBoolean('false');
|
40 |
+
}
|
41 |
+
|
42 |
+
$this->statement = $matches[self::STATEMENT];
|
43 |
+
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Parse this node.
|
47 |
+
* Set passed arguments and any optional arguments not passed to their
|
48 |
+
* defaults, then render the children of the return definition.
|
49 |
+
* @param SassContext the context in which this node is parsed
|
50 |
+
* @return array the parsed node
|
51 |
+
*/
|
52 |
+
public function parse($pcontext) {
|
53 |
+
$context = new SassContext($pcontext);
|
54 |
+
$statement = $this->statement;
|
55 |
+
|
56 |
+
try {
|
57 |
+
$statement = $this->evaluate($this->statement, $context)->toString();
|
58 |
+
}
|
59 |
+
catch (Exception $e) {}
|
60 |
+
|
61 |
+
if (SassParser::$instance->options['callbacks']['warn']) {
|
62 |
+
call_user_func(SassParser::$instance->options['callbacks']['warn'], $statement, $context);
|
63 |
+
}
|
64 |
+
|
65 |
+
if (SassParser::$instance->getQuiet()) {
|
66 |
+
return array(new SassString(''));
|
67 |
+
}
|
68 |
+
else {
|
69 |
+
return array(new SassString('/* @warn: ' . str_replace('*/', '', $statement) . ' */'));
|
70 |
+
}
|
71 |
+
}
|
72 |
+
|
73 |
+
/**
|
74 |
+
* Returns a value indicating if the token represents this type of node.
|
75 |
+
* @param object token
|
76 |
+
* @return boolean true if the token represents this type of node, false if not
|
77 |
+
*/
|
78 |
+
public static function isa($token) {
|
79 |
+
return $token->source[0] === self::NODE_IDENTIFIER;
|
80 |
+
}
|
81 |
+
}
|
lib/phpsass/tree/SassWhileNode.php
ADDED
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* SVN FILE: $Id$ */
|
3 |
+
/**
|
4 |
+
* SassWhileNode class file.
|
5 |
+
* @author Chris Yates <chris.l.yates@gmail.com>
|
6 |
+
* @copyright Copyright (c) 2010 PBM Web Development
|
7 |
+
* @license http://phamlp.googlecode.com/files/license.txt
|
8 |
+
* @package PHamlP
|
9 |
+
* @subpackage Sass.tree
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* SassWhileNode class.
|
14 |
+
* Represents a Sass @while loop and a Sass @do loop.
|
15 |
+
* @package PHamlP
|
16 |
+
* @subpackage Sass.tree
|
17 |
+
*/
|
18 |
+
class SassWhileNode extends SassNode {
|
19 |
+
const MATCH = '/^@(do|while)\s+(.+)$/i';
|
20 |
+
const LOOP = 1;
|
21 |
+
const EXPRESSION = 2;
|
22 |
+
const IS_DO = 'do';
|
23 |
+
/**
|
24 |
+
* @var boolean whether this is a do/while.
|
25 |
+
* A do/while loop is guarenteed to run at least once.
|
26 |
+
*/
|
27 |
+
private $isDo;
|
28 |
+
/**
|
29 |
+
* @var string expression to evaluate
|
30 |
+
*/
|
31 |
+
private $expression;
|
32 |
+
|
33 |
+
/**
|
34 |
+
* SassWhileNode constructor.
|
35 |
+
* @param object source token
|
36 |
+
* @return SassWhileNode
|
37 |
+
*/
|
38 |
+
public function __construct($token) {
|
39 |
+
parent::__construct($token);
|
40 |
+
preg_match(self::MATCH, $token->source, $matches);
|
41 |
+
$this->expression = $matches[self::EXPRESSION];
|
42 |
+
$this->isDo = ($matches[self::LOOP] === SassWhileNode::IS_DO);
|
43 |
+
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Parse this node.
|
47 |
+
* @param SassContext the context in which this node is parsed
|
48 |
+
* @return array the parsed child nodes
|
49 |
+
*/
|
50 |
+
public function parse($context) {
|
51 |
+
$children = array();
|
52 |
+
if ($this->isDo) {
|
53 |
+
do {
|
54 |
+
$children = array_merge($children, $this->parseChildren($context));
|
55 |
+
} while ($this->evaluate($this->expression, $context)->toBoolean());
|
56 |
+
}
|
57 |
+
else {
|
58 |
+
while ($this->evaluate($this->expression, $context)->toBoolean()) {
|
59 |
+
$children = array_merge($children, $this->parseChildren($context));
|
60 |
+
}
|
61 |
+
}
|
62 |
+
return $children;
|
63 |
+
}
|
64 |
+
}
|
package.xml
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
<?xml version="1.0"?>
|
2 |
<package>
|
3 |
<name>Sass</name>
|
4 |
-
<version>1.1.
|
5 |
<stability>stable</stability>
|
6 |
<license uri="http://opensource.org/licenses/osl-3.0.php">Open Software License (OSL 3.0)</license>
|
7 |
<channel>community</channel>
|
@@ -12,11 +12,11 @@
|
|
12 |
You just need to create you sass file and add it to your layout like default css file.
|
13 |

|
14 |
This extension will then convert it to css file using phpSass or sass in command line if available</description>
|
15 |
-
<notes>
|
16 |
<authors><author><name>Laurent Clouet</name><user>laurent35240</user><email>laurent35240@gmail.com</email></author></authors>
|
17 |
-
<date>2013-01-
|
18 |
-
<time>
|
19 |
-
<contents><target name="magecommunity"><dir name="Laurent"><dir name="Sass"><dir name="Helper"><file name="Data.php" hash="63d744a096e8ceace1050e0eef4948ad"/></dir><dir name="Model"><dir name="Config"><file name="Style.php" hash="413571124dcf58d950200499a144420d"/></dir><dir name="Design"><file name="Package.php" hash="eb5db9b03fa47a7132575e79d8c47eaf"/></dir></dir><dir name="etc"><file name="config.xml" hash="8569cd53b25f033280f757cb49bbba90"/><file name="system.xml" hash="a2db1af0d9bae017b46805a80aedb442"/></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="Laurent_Sass.xml" hash="4ad004de851457681610553228973c30"/></dir></target><target name="magelocale"><dir name="en_US"><file name="Laurent_Sass.csv" hash="3a789fbb68da4b549e3efd3dab932b8e"/></dir><dir name="fr_FR"><file name="Laurent_Sass.csv" hash="355464f69094f63c11eb0f0b68d2386e"/></dir></target></contents>
|
20 |
<compatible/>
|
21 |
<dependencies><required><php><min>5.2.0</min><max>6.0.0</max></php></required></dependencies>
|
22 |
</package>
|
1 |
<?xml version="1.0"?>
|
2 |
<package>
|
3 |
<name>Sass</name>
|
4 |
+
<version>1.1.1</version>
|
5 |
<stability>stable</stability>
|
6 |
<license uri="http://opensource.org/licenses/osl-3.0.php">Open Software License (OSL 3.0)</license>
|
7 |
<channel>community</channel>
|
12 |
You just need to create you sass file and add it to your layout like default css file.
|
13 |

|
14 |
This extension will then convert it to css file using phpSass or sass in command line if available</description>
|
15 |
+
<notes>Missing phpsass library added</notes>
|
16 |
<authors><author><name>Laurent Clouet</name><user>laurent35240</user><email>laurent35240@gmail.com</email></author></authors>
|
17 |
+
<date>2013-01-22</date>
|
18 |
+
<time>18:27:19</time>
|
19 |
+
<contents><target name="magecommunity"><dir name="Laurent"><dir name="Sass"><dir name="Helper"><file name="Data.php" hash="63d744a096e8ceace1050e0eef4948ad"/></dir><dir name="Model"><dir name="Config"><file name="Style.php" hash="413571124dcf58d950200499a144420d"/></dir><dir name="Design"><file name="Package.php" hash="eb5db9b03fa47a7132575e79d8c47eaf"/></dir></dir><dir name="etc"><file name="config.xml" hash="6446c038b468af0ffe9543a2d8bb7705"/><file name="system.xml" hash="a2db1af0d9bae017b46805a80aedb442"/></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="Laurent_Sass.xml" hash="4ad004de851457681610553228973c30"/></dir></target><target name="magelocale"><dir name="en_US"><file name="Laurent_Sass.csv" hash="3a789fbb68da4b549e3efd3dab932b8e"/></dir><dir name="fr_FR"><file name="Laurent_Sass.csv" hash="355464f69094f63c11eb0f0b68d2386e"/></dir></target><target name="magelib"><dir name="phpsass"><file name="README.md" hash="1448ba255f7f18d69f044b90dbb03481"/><file name="SassException.php" hash="ae7482e7d574a36156671b4575d8c0b9"/><file name="SassFile.php" hash="930a4e6148fbb54df3ffe14c8d971b2a"/><file name="SassParser.php" hash="2820c4ddc7fbfe94aa4630642afd08a7"/><file name="VERSION" hash="e3574197344e94aa46d58c97fe5d7fc8"/><file name="compile-apache.php" hash="7a95ffbe3baf88cd27260645e51d326d"/><file name="composer.json" hash="fb5dad84d70d71d3ad3ea36e06238c21"/><dir name="renderers"><file name="SassCompactRenderer.php" hash="d06cc6830ab426eacb6758b6b7e60db8"/><file name="SassCompressedRenderer.php" hash="ebbb33ce5f0c205fe96f80d452cdc75e"/><file name="SassExpandedRenderer.php" hash="f7d4dfa1a46748a50f212ab1b4c39cf9"/><file name="SassNestedRenderer.php" hash="ae57a3b8da86ae7c8560f71fc7cf69c4"/><file name="SassRenderer.php" hash="2f9059ce82c1d73f3a386ba005457bde"/></dir><dir name="script"><file name="SassScriptFunction.php" hash="d3a694d2329862c89bc26d454e84b711"/><file name="SassScriptFunctions.php" hash="99b290ce879e50b9414de4dd6187c1b0"/><file name="SassScriptLexer.php" hash="1bc50096c5b44ce9b847da4311ba9b51"/><file name="SassScriptOperation.php" hash="22fcd4a2995da8e265ec81d8dde22b90"/><file name="SassScriptParser.php" hash="3d99f5d850767c11be70189ff33bb4e2"/><file name="SassScriptParserExceptions.php" hash="7adc8cdc2f8a343213de10fa9c89a9fb"/><file name="SassScriptVariable.php" hash="49a4b8e3d3d5d358e3361684d6717439"/><dir name="literals"><file name="SassBoolean.php" hash="9848178e8a10415670fab8a7b3bf65bd"/><file name="SassColour.php" hash="e26779ced2b6ce74de884ea0d6d14ecf"/><file name="SassList.php" hash="78340c3a7326306bd703fec94b3aa5d3"/><file name="SassLiteral.php" hash="d4403c33ca7bac07035832e009df5df8"/><file name="SassLiteralExceptions.php" hash="37841fc5e291f83b053b46dff8fabb1d"/><file name="SassNumber.php" hash="912ca47e7c4348c19309ac7f32c8ade1"/><file name="SassString.php" hash="4403e9ba0973e3fd04acf2eba5657496"/></dir></dir><file name="test.css" hash="1655ffa4e91349bbe84d9e9f87b5ae39"/><file name="test.php" hash="eab045b07750bcfbbdc6f899b31d507d"/><dir name="tests"><file name="_imported_charset_ibm866.sass" hash="92b1d97a137e4219bb7796d7751aa847"/><file name="_imported_charset_utf8.sass" hash="51489125c07d0e8f2b055295d40d2549"/><file name="_imported_content.sass" hash="aafd4b9ba2de82e4f977e04934f204d7"/><file name="_partial.sass" hash="529fb954d7e0f6b42c484c869e60fbcf"/><file name="alt.css" hash="d48efc67765cb14b1c532b36b036a064"/><file name="alt.sass" hash="66be58fb872cd96be90fb95425a0aadf"/><file name="alt.scss" hash="ec1074aaba9b4a1ef3933d091c547c63"/><file name="basic.css" hash="22c341ec6e41dba84606032a40ea21c3"/><file name="basic.sass" hash="450faf5ca8e4bacefd01a74509ae135c"/><file name="bork1.sass" hash="0608533ebc4c4a16ddff507f818a709e"/><file name="bork2.sass" hash="036c6e0bcebc36908f9093d1b536ceb1"/><file name="bork3.sass" hash="ad06e4551ead8422ac443ae0cad7994b"/><file name="bork4.sass" hash="0bd4b0aed58208f5abf4418eb9672ef9"/><file name="bork5.sass" hash="e467427a1e928d1d72ef8db10482e262"/><file name="comments.css" hash="0ca95a61ccc49717e465173fb0c2e6c6"/><file name="comments.sass" hash="41bb5d2d2e14e3dd16c8b0296608a00e"/><file name="compact.css" hash="96105d0147b6881651b723eabe303f65"/><file name="compact.sass" hash="d51cfe7db78fe0d81793c7e24cb32e46"/><file name="complex.css" hash="3ffea6ccf80b24cc136bb102b3b41a67"/><file name="complex.sass" hash="b1426382fb915456b5efc0877ae7072d"/><file name="compressed.css" hash="1975f89cc758e39302d317578fdfdedb"/><file name="compressed.sass" hash="d51cfe7db78fe0d81793c7e24cb32e46"/><file name="content.css" hash="0c0892c48d74761fee54e09ccba6e662"/><file name="content.scss" hash="e624a12355435c147c0772f9fd1fe065"/><file name="css3.css" hash="8b2a0df29de7f2158baa3e97e76d91f2"/><file name="css3.scss" hash="819f570dae1e0c8c77007a03a2642158"/><file name="default.css" hash="8296f416c47ae4b66fa6bdf3259b1015"/><file name="default.sass" hash="90e19da7fd8144eeab4d93e5d3adb3bd"/><file name="default_imported.sass" hash="dbea23754dce7bc6c0f7ff69863c05c5"/><file name="each.css" hash="3f84fdb19433d6c2fb0e63c60c06d072"/><file name="each.scss" hash="9661d8222338cfdd6ba8ff771f1c881c"/><file name="expanded.css" hash="88ee2a16c4d88965bcbcf573a2d7c0fa"/><file name="expanded.sass" hash="66dbccbf13e56c23c9b12401ef4edb2d"/><file name="extend.css" hash="c4e9ba7d8748202d2c1351d5b69e3353"/><file name="extend.sass" hash="f34bd79d09364600631ea4889232ad64"/><file name="extend_included.scss" hash="be61d8166e45b16c08e49087985ed1fb"/><file name="extend_placeholders.css" hash="369b772056769bece364221f3c069a8e"/><file name="extend_placeholders.scss" hash="4af5f1ee5888e7cd6cf6c6de45dbfc17"/><file name="filters.css" hash="f37ab5406ea00310513ce2e114cf6f07"/><file name="filters.scss" hash="ea989dd11a1d2a147b72d748b3a9c8c9"/><file name="functions.css" hash="0a039fdec50d756df0ca5430fdc25a0a"/><file name="functions.scss" hash="c9669b01b11b3abe4d82f701180ac2b0"/><file name="holmes.css" hash="07772c914ace61121cba53beb6ad8ca6"/><file name="holmes.sass" hash="e2199b90032b5f14a4dfc5233a915223"/><file name="hsl-functions.css" hash="b90a44e5919a286a8a62040f211b3580"/><file name="hsl-functions.scss" hash="fa055c7d603b5436613ecac898351cae"/><file name="if.css" hash="82391ad41348b41fc209cca5412ed740"/><file name="if.sass" hash="c3a27a421eb7e3d463918e189195b573"/><file name="import.css" hash="3c8abb5481ee0c0002916b49681af2a5"/><file name="import.sass" hash="4d43389aa785393a47d57734724dad2f"/><file name="import_content.css" hash="4b78a566882b2768336f603d54f73fa4"/><file name="import_content.sass" hash="2db9b7a5b9a581e9f44256b6c8dbedd1"/><file name="importee.sass" hash="606678a5d48f7653f6d8e2e327109310"/><file name="interpolation.css" hash="ef048c83424f05a9c0882007ba0f4958"/><file name="interpolation.scss" hash="ce66433664f7f37277a8d4726a39556c"/><file name="introspection.css" hash="0d9f0e64e263388ec0e87eadef41193e"/><file name="introspection.scss" hash="9d57f03789f7a947ce62d6ab0320f886"/><file name="line_numbers.css" hash="802e2285df194c362503f849647a1380"/><file name="line_numbers.sass" hash="4770a812ab401d2bd35b352408623a17"/><file name="list.css" hash="4dd913d85cf2a28cb12d8af0f622ce03"/><file name="list.scss" hash="24f96a097c84d0da64cb3e753e2439ee"/><file name="media.css" hash="409f2c0c468598dc19067376a796828f"/><file name="media.scss" hash="1fadbcd19113ff5855d1137be0693799"/><file name="misc-functions.css" hash="7da15f26cab9b6df784ce1e37e9a30f1"/><file name="misc-functions.scss" hash="b133df9bb8cf346a3e6d3b3612c1de64"/><file name="misc.css" hash="a05a0a6b49ddbfdd803e92c5abe97594"/><file name="misc.scss" hash="36b58121127544b6a8179ae8a0c90a35"/><file name="mixin-content.css" hash="e78896e8791b036b3ad422c01dfab80a"/><file name="mixin-content.sass" hash="d6d50676938cfcc4233af025391e84c4"/><file name="mixin-content.scss" hash="6cbc7c1eaa8978aba39f8bac785cac5e"/><file name="mixin-ja1.css" hash="c551c8ffd0723e1c9e5795708d5dbda5"/><file name="mixin-ja1.sass" hash="90d3f2653ccbccbc00a9009bd31f8071"/><file name="mixin-params.css" hash="d93b61ece294cd6ade82ab84508c3f78"/><file name="mixin-params.scss" hash="c6e703eda1488825f02b46e7c31aa529"/><file name="mixin_bork.sass" hash="259fd0cca5a94583c0d406646b3217eb"/><file name="mixins.css" hash="76ea6005844a4339745b1afe66274c74"/><file name="mixins.sass" hash="55fb1a104a59d6e5411987447af96b55"/><file name="multiline.css" hash="4a3d9bfe75a3b98c52ed95f3d785b079"/><file name="multiline.sass" hash="2f4ff6cfefa20eece2830170e29ea25e"/><file name="nested-media.css" hash="4fc2c8503b918fe9add9fcb230215fe2"/><file name="nested-media.scss" hash="2ee9901f1413f1f0839cfdc9b2ea00af"/><file name="nested.css" hash="26bd112a486652342cbfd55060552576"/><file name="nested.sass" hash="0bdc5d64efc02766544553f968bb82fd"/><file name="nested_bork1.sass" hash="56fa9072b6cc553909bfbd18f571a11d"/><file name="nested_bork2.sass" hash="999ae47bdbc6190972f0f52958870130"/><file name="nested_bork3.sass" hash="ccb998041d90cfe54a39f01afc4c4cc0"/><file name="nested_bork4.sass" hash="eed6453ea8564978c35d60f26a9bbb25"/><file name="nested_bork5.sass" hash="d6de98aee6d80aaecc1aa97311136003"/><file name="nested_import.css" hash="de8bc2a3cd89496c22ff564e65c2b046"/><file name="nested_import.sass" hash="b4493203dae52b5757ca91e17dd42e42"/><file name="nested_media.css" hash="d910a2026928133ce7235e8e1143a425"/><file name="nested_media.scss" hash="6e2dceddb572a0caa6f3959e00d332f4"/><file name="nested_mixin_bork.sass" hash="d9f57da9545e6c9a54d89bebe2f750b9"/><file name="nested_pseudo.css" hash="435b969153bc5030ceb8c251d241d358"/><file name="nested_pseudo.scss" hash="145d425a42541236d8952ea9f5e68ae7"/><file name="number.css" hash="000ca28603c30a49647d53bc5aabed94"/><file name="number.scss" hash="c585efaf419263809a1c91b76d8a0799"/><file name="opacity.css" hash="f54a7fbecddad66df7a6dae52bea4c21"/><file name="opacity.scss" hash="3a20a779e1becfcfaba90758d907c05e"/><file name="other-color.css" hash="45d9e1061d0e71b1c55362c2974b7bdf"/><file name="other-color.scss" hash="e2b8b86016461cbcdae6365ee14b0a5d"/><file name="parent_ref.css" hash="cbecced10861e9c060fa05187f94ee5f"/><file name="parent_ref.sass" hash="b5a3fe6fd3384b0853211e3515427796"/><file name="phpSassTest.php" hash="50a02a22610ec53faf16a899a53287ec"/><file name="phpunit.xml.dist" hash="3f11a2a97eb3d9b8bb48f60915daefda"/><file name="proprietary-selector.css" hash="6da7ab22184ed10ec679065835fc0f5f"/><file name="proprietary-selector.scss" hash="a7fa95fad2ae9aa0b93d8b49101eaf5f"/><file name="rgb-functions.css" hash="24177c2bc7b35526bf06334ea4174e27"/><file name="rgb-functions.scss" hash="7ad824885f782f15738ef67bbf00ae2a"/><file name="scss_import.css" hash="9eeec339e1ce59cbf3f2d29efab1382b"/><file name="scss_import.scss" hash="7ae409e52a06ad89582fe89063cf6bdd"/><file name="scss_importee.css" hash="7cad5194c7f7bb18e50f1ca093597033"/><file name="scss_importee.scss" hash="20c55fb516c7b137eadc6462f74cfa94"/><file name="splats.css" hash="3d11bc1d58858e4967ed498a3d75d008"/><file name="splats.scss" hash="4fab47d7391ff72a2e8d6de13c5e1b80"/><file name="string.css" hash="9f1482fe5b4964e96d24c00765c2503d"/><file name="string.scss" hash="f89f2aad48738bb1fa33a67f733e2e90"/><dir name="subdir"><dir name="nested_subdir"><file name="_nested_partial.sass" hash="24985866e9414ee4ef69621b738833ee"/><file name="nested_subdir.css" hash="759df6872a88c4174a39c32fe7d95a04"/><file name="nested_subdir.sass" hash="9e5e623f6765e28d5bec3bb2d3944eac"/></dir><file name="subdir.css" hash="ddd9dd41c2b72164a7ba634e359c2872"/><file name="subdir.sass" hash="acde24bd965d15ae3f19bf74b1f1c2a8"/></dir><file name="units.css" hash="ebc7390d227439bf2694a3ce29c54a6d"/><file name="units.sass" hash="040de247b027e6368c7edbc57d6e331f"/><file name="warn.css" hash="31c098880201803dc33c8785ecf3de26"/><file name="warn.sass" hash="47c7543a6a374523514669e108249f41"/><file name="warn_imported.sass" hash="2b7455acd686bf2511fce0b710874b01"/></dir><dir name="tree"><file name="SassCommentNode.php" hash="642f1a815d0e7dfc1106a518e8d87b31"/><file name="SassContentNode.php" hash="789f74473f6af06d065ee95ae1d07a22"/><file name="SassContext.php" hash="20760243da06e897f9c21d7ba1672fd4"/><file name="SassDebugNode.php" hash="9acd25b3581aa8d18313f3ac3d9e6e9a"/><file name="SassDirectiveNode.php" hash="039a39f8662e2131b2c0018a80337fde"/><file name="SassEachNode.php" hash="63aa2d6a2ade5a9a966f45edc70fabec"/><file name="SassElseNode.php" hash="6a6426a9d34047ac81eed7add893a179"/><file name="SassExtendNode.php" hash="02252a9b29559a959348fb87bb586d0e"/><file name="SassForNode.php" hash="0322a55c38b6f7fa0d4382f7e3972f4d"/><file name="SassFunctionDefinitionNode.php" hash="97b6440a715ce30922f1d3fafb7cfc3a"/><file name="SassIfNode.php" hash="af42d33a38ed91a5f5181b2458a2fa60"/><file name="SassImportNode.php" hash="a0b2c5b200cff2262e66327458054878"/><file name="SassMediaNode.php" hash="48fb1aacbdcb0817ab5c8ee84ac5dcdf"/><file name="SassMixinDefinitionNode.php" hash="40763d3f0db2bcccd24e26262b2cc244"/><file name="SassMixinNode.php" hash="058bf4b74bcd7e99be00ff7890081881"/><file name="SassNode.php" hash="cc0144e874a2495fd791807e90398419"/><file name="SassNodeExceptions.php" hash="c8acde883ff1f08c56aaea36d3878c69"/><file name="SassPropertyNode.php" hash="388d56d83ab2e05909e8d04d849d786c"/><file name="SassReturnNode.php" hash="c951be13c91fad525b69fc09764e45ee"/><file name="SassRootNode.php" hash="423c2c19d8ad01638b42bf81b19a88fc"/><file name="SassRuleNode.php" hash="0828b0d29742e5e62f006a18961caa48"/><file name="SassVariableNode.php" hash="e4ab9cc2baab946a5b3cfc561baaf5cf"/><file name="SassWarnNode.php" hash="ba0da120f75efd07c57d040adf96a95d"/><file name="SassWhileNode.php" hash="b2c1328c15a28b4e37fed4d998d60dc9"/></dir><file name=".travis.yml" hash="d0ac6ce1cf8465183941fa3ac326a54e"/></dir></target></contents>
|
20 |
<compatible/>
|
21 |
<dependencies><required><php><min>5.2.0</min><max>6.0.0</max></php></required></dependencies>
|
22 |
</package>
|