├── .distignore
├── .github
└── workflows
│ ├── release.yml
│ └── update-assets.yml
├── .gitignore
├── .wordpress-org
├── banner-772x250.png
├── icon-128x128.png
├── icon-256x256.png
├── icon.svg.svg
└── screenshot-1.png
├── README.md
├── admin-color-schemer.php
├── classes
├── color-scheme.php
├── plugin.php
└── version-check.php
├── css
└── admin-color-schemer.css
├── js
└── admin-color-schemer.js
├── lib
└── phpsass
│ ├── .gitignore
│ ├── .travis.yml
│ ├── README.md
│ ├── SassException.php
│ ├── SassFile.php
│ ├── SassLoader.php
│ ├── SassParser.php
│ ├── VERSION
│ ├── compile-apache.php
│ ├── composer.json
│ ├── renderers
│ ├── SassCompactRenderer.php
│ ├── SassCompressedRenderer.php
│ ├── SassExpandedRenderer.php
│ ├── SassNestedRenderer.php
│ └── SassRenderer.php
│ ├── script
│ ├── SassScriptFunction.php
│ ├── SassScriptFunctions.php
│ ├── SassScriptLexer.php
│ ├── SassScriptOperation.php
│ ├── SassScriptParser.php
│ ├── SassScriptParserExceptions.php
│ ├── SassScriptVariable.php
│ └── literals
│ │ ├── SassBoolean.php
│ │ ├── SassColour.php
│ │ ├── SassList.php
│ │ ├── SassLiteral.php
│ │ ├── SassLiteralExceptions.php
│ │ ├── SassNumber.php
│ │ └── SassString.php
│ └── tree
│ ├── SassCharsetNode.php
│ ├── SassCommentNode.php
│ ├── SassContentNode.php
│ ├── SassContext.php
│ ├── SassDebugNode.php
│ ├── SassDirectiveNode.php
│ ├── SassEachNode.php
│ ├── SassElseNode.php
│ ├── SassExtendNode.php
│ ├── SassForNode.php
│ ├── SassFunctionDefinitionNode.php
│ ├── SassIfNode.php
│ ├── SassImportNode.php
│ ├── SassMediaNode.php
│ ├── SassMixinDefinitionNode.php
│ ├── SassMixinNode.php
│ ├── SassNode.php
│ ├── SassNodeExceptions.php
│ ├── SassPropertyNode.php
│ ├── SassReturnNode.php
│ ├── SassRootNode.php
│ ├── SassRuleNode.php
│ ├── SassVariableNode.php
│ ├── SassWarnNode.php
│ └── SassWhileNode.php
└── templates
├── admin-page.php
├── empty-scheme.php
└── updated.php
/.distignore:
--------------------------------------------------------------------------------
1 | /.git
2 | /.github
3 | /.wordpress-org
4 |
5 | .distignore
6 | .gitignore
7 | README.md
8 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Deploy to WordPress.org
2 | on:
3 | release:
4 | types: [published]
5 | jobs:
6 | tag:
7 | name: New release
8 | runs-on: ubuntu-latest
9 | steps:
10 | - name: Checkout code
11 | uses: actions/checkout@v2
12 | - name: Transform Plugin Readme
13 | run: |
14 | cp ./README.md ./readme.txt
15 | sed -i -e 's/^# \(.*\)$/=== \1 ===/' -e 's/ #* ===$/ ===/' -e 's/^## \(.*\)$/== \1 ==/' -e 's/ #* ==$/ ==/' -e 's/^### \(.*\)$/= \1 =/' -e 's/ #* =$/ =/' ./readme.txt
16 | - name: WordPress Plugin Deploy
17 | id: deploy
18 | uses: 10up/action-wordpress-plugin-deploy@stable
19 | with:
20 | generate-zip: true
21 | env:
22 | SVN_USERNAME: ${{ secrets.SVN_USERNAME }}
23 | SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }}
24 | - name: Upload release asset
25 | uses: actions/upload-release-asset@v1
26 | env:
27 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
28 | with:
29 | upload_url: ${{ github.event.release.upload_url }}
30 | asset_path: ${{github.workspace}}/${{ github.event.repository.name }}.zip
31 | asset_name: ${{ github.event.repository.name }}.zip
32 | asset_content_type: application/zip
33 |
--------------------------------------------------------------------------------
/.github/workflows/update-assets.yml:
--------------------------------------------------------------------------------
1 | name: Plugin asset/readme update
2 | on:
3 | push:
4 | branches:
5 | - trunk
6 | jobs:
7 | trunk:
8 | name: Push to trunk
9 | runs-on: ubuntu-latest
10 | steps:
11 | - name: Checkout code
12 | uses: actions/checkout@v2
13 | - name: Transform Plugin Readme
14 | run: |
15 | cp ./README.md ./readme.txt
16 | ls
17 | sed -i -e 's/^# \(.*\)$/=== \1 ===/' -e 's/ #* ===$/ ===/' -e 's/^## \(.*\)$/== \1 ==/' -e 's/ #* ==$/ ==/' -e 's/^### \(.*\)$/= \1 =/' -e 's/ #* =$/ =/' ./readme.txt
18 | - name: WordPress.org plugin asset/readme update
19 | uses: 10up/action-wordpress-plugin-asset-update@stable
20 | env:
21 | SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }}
22 | SVN_USERNAME: ${{ secrets.SVN_USERNAME }}
23 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 |
--------------------------------------------------------------------------------
/.wordpress-org/banner-772x250.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/helen/admin-color-schemer/3b2d2ae562134e4c8c47d627def1baf41dbb1003/.wordpress-org/banner-772x250.png
--------------------------------------------------------------------------------
/.wordpress-org/icon-128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/helen/admin-color-schemer/3b2d2ae562134e4c8c47d627def1baf41dbb1003/.wordpress-org/icon-128x128.png
--------------------------------------------------------------------------------
/.wordpress-org/icon-256x256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/helen/admin-color-schemer/3b2d2ae562134e4c8c47d627def1baf41dbb1003/.wordpress-org/icon-256x256.png
--------------------------------------------------------------------------------
/.wordpress-org/icon.svg.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
--------------------------------------------------------------------------------
/.wordpress-org/screenshot-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/helen/admin-color-schemer/3b2d2ae562134e4c8c47d627def1baf41dbb1003/.wordpress-org/screenshot-1.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Admin Color Schemer #
2 | Contributors: wordpressdotorg, helen, markjaquith
3 | Requires at least: 3.8
4 | License: GPLv2 or later
5 | License URI: http://www.gnu.org/licenses/gpl-2.0.html
6 | Stable tag: 1.1
7 |
8 | Create your own admin color scheme, right in the WordPress admin under the Tools menu.
9 |
10 | ## Description ##
11 |
12 | Create your own admin color scheme, right in the WordPress admin under the Tools menu. Go simple with the four basic colors, or get into the details and customize to your heart's content.
13 |
14 | ### Contributing ###
15 |
16 | Pull requests and issues on [GitHub](https://github.com/helen/admin-color-schemer) welcome.
17 |
18 | ## Installation ##
19 |
20 | Admin Color Schemer is most easily installed automatically via the Plugins tab in your dashboard. Your uploads folder also needs to be writeable in order to save the scheme.
21 |
22 | ## Frequently Asked Questions ##
23 |
24 | ### Why do I have to click a button to preview? ###
25 |
26 | The preview currently operates by generating a complete stylesheet and reloading it. While in some environments this happens near-instantaneously, the time and resources it takes to reflect a change are not ideal for live previews. We do hope to solve this in a later release.
27 |
28 | ### I'm getting an ugly screen asking me for a username and password. ###
29 |
30 | This means that your uploads folder requires credentials in order to save the the scheme files. If you're not sure how to fix that, please try the [support forums](http://wordpress.org/support/). We'll also work on making this prompt more beautiful in the future.
31 |
32 | ### What should I do if I've updated this plugin or WordPress recently and now my color scheme doesn't look right? ###
33 | Some versions of WordPress or this plugin may change how the custom color scheme output is generated, so if you see something funny, first try re-saving your custom color scheme. If it's still broken, then please let us know.
34 |
35 | ## Screenshots ##
36 |
37 | 1. Admin color schemer in action
38 |
39 | ## Changelog ##
40 |
41 | ### 1.1 ###
42 | * Updated phpsass library to fix PHP 7 bug (with many thanks to @KZeni).
43 | * Ensure custom color scheme can be previewed when using the default admin color scheme.
44 | * Avoid a potential PHP notice.
45 |
46 | ### 1.0 ###
47 | * Initial release
48 |
--------------------------------------------------------------------------------
/admin-color-schemer.php:
--------------------------------------------------------------------------------
1 | accessors = array_merge( $this->accessors, array_keys( $admin_schemer->get_colors() ) );
21 |
22 | // set slug
23 | $this->slug = 'admin_color_schemer_' . $this->id;
24 |
25 | if ( is_array( $attr ) ) {
26 | foreach ( $this->accessors as $thing ) {
27 | if ( isset( $attr[$thing] ) && ! empty( $attr[$thing] ) ) {
28 | $this->{$thing} = $attr[$thing];
29 | }
30 | }
31 | } else {
32 | // set defaults
33 | // @todo: make this really set defaults for the items that must have a color - what are those?
34 | $this->name = __( 'Custom', 'admin-color-schemer' );
35 | }
36 | }
37 |
38 | public function __get( $key ) {
39 | if ( in_array( $key, $this->accessors ) ) {
40 | if ( isset( $this->{$key} ) ) {
41 | return $this->sanitize( $this->{$key}, $key, 'out' );
42 | } else {
43 | return false;
44 | }
45 | }
46 | }
47 |
48 | public function __set( $key, $value ) {
49 | if ( in_array( $key, $this->accessors ) ) {
50 | $this->{$key} = $this->sanitize( $value, $key, 'in' );
51 | }
52 | }
53 |
54 | public function __isset( $key ) {
55 | return isset( $this->$key );
56 | }
57 |
58 | private function sanitize( $value, $key, $direction ) {
59 | switch ( $key ) {
60 | case 'id':
61 | $value = absint( $value );
62 | break;
63 | case 'slug':
64 | $value = sanitize_key( $value );
65 | case 'name':
66 | $value = esc_html( $value );
67 | break;
68 | case 'uri':
69 | $value = esc_url_raw( $value );
70 | break;
71 | default:
72 | // everything else should be a hex value
73 | // regex copied from core's sanitize_hex_value()
74 | if ( ! preg_match('|^#([A-Fa-f0-9]{3}){1,2}$|', $value ) ) {
75 | $value = '';
76 | }
77 | break;
78 | }
79 | return $value;
80 | }
81 |
82 | public function to_array() {
83 | $return = array();
84 | foreach ( $this->accessors as $thing ) {
85 | $return[$thing] = $this->{$thing};
86 | }
87 | return $return;
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/classes/version-check.php:
--------------------------------------------------------------------------------
1 | ' );
21 | }
22 |
23 | public function plugins_loaded() {
24 | if ( ! $this->passes() ) {
25 | remove_action( 'init', array( Admin_Color_Schemer_Plugin::get_instance(), 'init' ) );
26 | if ( current_user_can( 'activate_plugins' ) ) {
27 | add_action( 'admin_init', array( $this, 'admin_init' ) );
28 | add_action( 'admin_notices', array( $this, 'admin_notices' ) );
29 | }
30 | }
31 | }
32 |
33 | public function admin_init() {
34 | deactivate_plugins( plugin_basename( dirname( dirname( __FILE__ ) ) . '/admin-color-schemer.php' ) );
35 | }
36 |
37 | public function admin_notices() {
38 | echo '
' . __('Admin Color Schemer requires WordPress 3.8 or higher, and has thus been deactivated. Please update your install and then try again!', 'admin-color-schemer' ) . '
';
39 | if ( isset( $_GET['activate'] ) ) {
40 | unset( $_GET['activate'] );
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/css/admin-color-schemer.css:
--------------------------------------------------------------------------------
1 | .color-schemer-pickers {
2 | margin-top: 1em;
3 | padding: 0 1.5em;
4 | background: #fff !important; /* this is important so a user can't set foreground and background to be the same and essentially lock themselves out - unclear if this good enough or even necessary right now, but just in case. */
5 | }
6 |
7 | .show-advanced {
8 | margin-bottom: 2em;
9 | }
10 |
11 | .schemer-advanced {
12 | margin-top: 0;
13 | }
14 |
--------------------------------------------------------------------------------
/js/admin-color-schemer.js:
--------------------------------------------------------------------------------
1 | (function($){
2 | $('.colorpicker').wpColorPicker();
3 |
4 | $('.show-advanced a').on('click', function(e){
5 | e.preventDefault();
6 | var $this = $(this);
7 |
8 | $this.parent().prev('.schemer-advanced').show();
9 | $this.remove();
10 | });
11 |
12 | $('#preview').on('click', function(e){
13 | e.preventDefault();
14 |
15 | // clear any existing messages
16 | $('h2').next('div').remove();
17 |
18 | $.ajax({
19 | type: 'POST',
20 | dataType: 'json',
21 | url: ajaxurl,
22 | data: $('.color-schemer-pickers').serialize(),
23 | success: function(r) {
24 | if ( typeof r.errors != 'undefined' ) {
25 | $('h2').after( '
' + r.message + '
' );
26 | } else if ( typeof r.uri != 'undefined' ) {
27 | // Default admin color scheme doesn't have a #colors-css link to hot load into so let's make one if we need to
28 | if( ! $('#colors-css').length ) {
29 | $('head').append("");
30 | }
31 |
32 | $('#colors-css').attr('href', r.uri);
33 | $('h2').after( '
' + r.message + '
' );
34 | }
35 | }
36 | })
37 | })
38 | })(jQuery);
39 |
--------------------------------------------------------------------------------
/lib/phpsass/.gitignore:
--------------------------------------------------------------------------------
1 | /sass/sass-cache
2 | .idea
3 | /tests/.sass-cache
4 | vendor
5 | composer.lock
6 | /.project
7 | /.settings
8 | /nbproject
9 |
--------------------------------------------------------------------------------
/lib/phpsass/.travis.yml:
--------------------------------------------------------------------------------
1 | # Configuration file for unit test runner.
2 | language: php
3 | php:
4 | - 5.3
5 | - 5.4
6 | - 5.5
7 | - 5.6
8 | - 7.0
9 | - hhvm
10 | env:
11 | - PHPUNIT_ARGS=--group=sass
12 | matrix:
13 | allow_failures:
14 | - php: hhvm
15 | before_script:
16 | - cd tests
17 | script: phpunit $PHPUNIT_ARGS
18 | notifications:
19 | email: false
20 |
--------------------------------------------------------------------------------
/lib/phpsass/README.md:
--------------------------------------------------------------------------------
1 | #PHPSass [](https://travis-ci.org/richthegeek/phpsass)
2 |
3 | #IMPORTANT NOTICE
4 | I'm not involved with PHP development anymore, and just don't have the time to do much more than approve pull requests for this project. I'm happy to stick around as a hands-off dictator, but if there are others who wish to take over maintainership then please get in touch ([my username]@gmail.com).
5 |
6 | ## About
7 | This is fork of PHamlP primarily for inclusiong as a Drupal pre-processor.
8 | However, there is no Drupal-specific code and it will work for any PHP system.
9 |
10 | This version of the compiler is NOT compatible with other forks of PHamlP, and
11 | the name has been changed to reflect this.
12 |
13 | ## Other info
14 | Origin:
15 |
16 | License:
17 |
18 |
--------------------------------------------------------------------------------
/lib/phpsass/SassException.php:
--------------------------------------------------------------------------------
1 |
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 | *
15 | * @package PHamlP
16 | * @subpackage Sass
17 | */
18 | class SassException extends Exception {
19 |
20 | /**
21 | * Sass Exception.
22 | *
23 | * @param string $message Exception message
24 | * @param mixed $additionalMessageMixed mixed resource for meta data
25 | */
26 | public function __construct($message, $additionalMessageMixed = '') {
27 | if (is_object($additionalMessageMixed)) {
28 | $additionalMessageMixed = ": {$additionalMessageMixed->filename}::{$additionalMessageMixed->line}\nSource: {$additionalMessageMixed->source}";
29 | } else if (is_array($additionalMessageMixed)) {
30 | $additionalMessageMixed = var_export($additionalMessageMixed, TRUE);
31 | } else if (!is_scalar($additionalMessageMixed)) {
32 | $additionalMessageMixed = '';
33 | }
34 | parent::__construct($message . $additionalMessageMixed);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/lib/phpsass/SassFile.php:
--------------------------------------------------------------------------------
1 |
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 | {
20 | const CSS = 'css';
21 | const SASS = 'sass';
22 | const SCSS = 'scss';
23 |
24 | public static $path = FALSE;
25 | public static $parser = FALSE;
26 |
27 | /**
28 | * Returns the parse tree for a file.
29 | * @param string $filename filename to parse
30 | * @param SassParser $parser Sass parser
31 | * @return SassRootNode
32 | */
33 | public static function get_tree($filename, &$parser)
34 | {
35 | $contents = self::get_file_contents($filename);
36 |
37 | $options = array_merge($parser->getOptions(), array('line'=>1));
38 |
39 | # attempt at cross-syntax imports.
40 | $ext = substr($filename, strrpos($filename, '.') + 1);
41 | if ($ext == self::SASS || $ext == self::SCSS) {
42 | $options['syntax'] = $ext;
43 | }
44 |
45 | $dirName = dirname($filename);
46 | $options['load_paths'][] = $dirName;
47 | if (!in_array($dirName, $parser->load_paths)) {
48 | $parser->load_paths[] = dirname($filename);
49 | }
50 |
51 | $sassParser = new SassParser($options);
52 | $tree = $sassParser->parse($contents, FALSE);
53 |
54 | return $tree;
55 | }
56 |
57 | /**
58 | * Get the content of the given file
59 | *
60 | * @param string $filename
61 | *
62 | * @return mixed|string
63 | */
64 | public static function get_file_contents($filename)
65 | {
66 | $content = file_get_contents($filename) . "\n\n "; #add some whitespace to fix bug
67 | # strip // comments at this stage, with allowances for http:// style locations.
68 | $content = preg_replace("/(^|\s)\/\/[^\n]+/", '', $content);
69 | // SassFile::$parser = $parser;
70 | // SassFile::$path = $filename;
71 | return $content;
72 | }
73 |
74 | /**
75 | * Returns the full path to a file to parse.
76 | * The file is looked for recursively under the load_paths directories
77 | * If the filename does not end in .sass or .scss try the current syntax first
78 | * then, if a file is not found, try the other syntax.
79 | * @param string $filename filename to find
80 | * @param SassParser $parser Sass parser
81 | * @param bool $sass_only
82 | * @return array of string path(s) to file(s) or FALSE if no such file
83 | */
84 | public static function get_file($filename, &$parser, $sass_only = TRUE)
85 | {
86 | $ext = substr($filename, strrpos($filename, '.') + 1);
87 | // if the last char isn't *, and it's not (.sass|.scss|.css)
88 | if ($sass_only && substr($filename, -1) != '*' && $ext !== self::SASS && $ext !== self::SCSS && $ext !== self::CSS) {
89 | $sass = self::get_file($filename . '.' . self::SASS, $parser);
90 |
91 | return $sass ? $sass : self::get_file($filename . '.' . self::SCSS, $parser);
92 | }
93 | if (is_file($filename)) {
94 | return array($filename);
95 | }
96 | $paths = $parser->load_paths;
97 | if (is_string($parser->filename) && $path = dirname($parser->filename)) {
98 | $paths[] = $path;
99 | if (!in_array($path, $parser->load_paths)) {
100 | $parser->load_paths[] = $path;
101 | }
102 | }
103 | foreach ($paths as $path) {
104 | $filePath = self::find_file($filename, realpath($path));
105 | if ($filePath !== false) {
106 | if (!is_array($filePath)) {
107 | return array($filePath);
108 | }
109 | return $filePath;
110 | }
111 | }
112 | foreach ($parser->load_path_functions as $function) {
113 | if (is_callable($function) && $paths = call_user_func($function, $filename, $parser)) {
114 | return $paths;
115 | }
116 | }
117 |
118 | return FALSE;
119 | }
120 |
121 | /**
122 | * Looks for the file recursively in the specified directory.
123 | * This will also look for _filename to handle Sass partials.
124 | * Internal cache to find the files quickly
125 | *
126 | * @param string $filename filename to look for
127 | * @param string $dir path to directory to look in and under
128 | *
129 | * @return mixed string: full path to file if found, false if not
130 | */
131 | public static function find_file($filename, $dir) {
132 | // internal cache
133 | static $pathCache = array();
134 | $cacheKey = $filename . '@' . $dir;
135 | if (isset($pathCache[$cacheKey])) {
136 | return $pathCache[$cacheKey];
137 | }
138 |
139 | if (strstr($filename, DIRECTORY_SEPARATOR . '**')) {
140 | $specialDirectory = $dir . DIRECTORY_SEPARATOR . substr($filename, 0, strpos($filename, DIRECTORY_SEPARATOR . '**'));
141 | if (is_dir($specialDirectory)) {
142 | $paths = array();
143 | $files = scandir($specialDirectory);
144 | foreach ($files as $file) {
145 | if ($file === '..') {
146 | continue;
147 | }
148 | if (is_dir($specialDirectory . DIRECTORY_SEPARATOR . $file)) {
149 | if ($file === '.') {
150 | $new_filename = str_replace(DIRECTORY_SEPARATOR . '**', '', $filename);
151 | } else {
152 | $new_filename = str_replace('**', $file, $filename);
153 | }
154 | $path = self::find_file($new_filename, $dir);
155 | if ($path !== FALSE) {
156 | if (!is_array($path)) {
157 | $path = array($path);
158 | }
159 | $paths = array_merge($paths, $path);
160 | }
161 | }
162 | }
163 | // cache and return
164 | $pathCache[$cacheKey] = $paths;
165 | return $paths;
166 | }
167 | }
168 |
169 | if (substr($filename, -2) == DIRECTORY_SEPARATOR . '*') {
170 | $checkDir = $dir . DIRECTORY_SEPARATOR . substr($filename, 0, strlen($filename) - 2);
171 | if (is_dir($checkDir)) {
172 | $dir = $checkDir;
173 | $paths = array();
174 | $files = scandir($dir);
175 | foreach ($files as $file) {
176 | if (($file === '.') || ($file === '..')) {
177 | continue;
178 | }
179 | $ext = substr($file, strrpos($file, '.') + 1);
180 | if (substr($file, -1) != '*' && ($ext == self::SASS || $ext == self::SCSS || $ext == self::CSS)) {
181 | $paths[] = $dir . DIRECTORY_SEPARATOR . $file;
182 | }
183 | }
184 | // cache and return
185 | $pathCache[$cacheKey] = $paths;
186 | return $paths;
187 | }
188 | }
189 |
190 | $partialName = str_replace(basename($filename), ('_' . basename($filename)), $filename);
191 |
192 | foreach (array(
193 | $filename,
194 | $partialName
195 | ) as $file) {
196 | $checkFile = $dir . DIRECTORY_SEPARATOR . $file;
197 | if (is_file($checkFile)) {
198 | // cache and return
199 | $pathCache[$cacheKey] = realpath($checkFile);
200 | return realpath($checkFile);
201 | }
202 | }
203 |
204 | if (is_dir($dir)) {
205 | $dirs = glob($dir . DIRECTORY_SEPARATOR . '*', GLOB_ONLYDIR);
206 | foreach ($dirs as $deepDir) {
207 | $path = self::find_file($filename, $deepDir);
208 | if ($path !== FALSE) {
209 | // cache and return
210 | $pathCache[$cacheKey] = $path;
211 | return $path;
212 | }
213 | }
214 | }
215 | $pathCache[$cacheKey] = FALSE;
216 |
217 | return FALSE;
218 | }
219 |
220 | }
221 |
--------------------------------------------------------------------------------
/lib/phpsass/SassLoader.php:
--------------------------------------------------------------------------------
1 |
6 | */
7 |
8 | /**
9 | * SASS Auto loader
10 | *
11 | * @author Tim Lochmüller
12 | */
13 | class SassLoader {
14 |
15 | /**
16 | * Base dir of the SASS Framework
17 | *
18 | * @var string
19 | */
20 | static private $basePath = NULL;
21 |
22 | /**
23 | * Loader file extension
24 | */
25 | const FILE_EXTENSION = '.php';
26 |
27 | /**
28 | * Load the given class
29 | *
30 | * @param string $className
31 | *
32 | * @todo More smarter Implementation, if the files have a smarter structure
33 | */
34 | static public function load($className) {
35 | $base = self::getBasePath();
36 | $psrFile = self::getPsrFilePath($className);
37 | if (file_exists($base . $psrFile)) {
38 | require_once($base . $psrFile);
39 | } else if (file_exists($base . $className . self::FILE_EXTENSION)) {
40 | require_once($base . $className . self::FILE_EXTENSION);
41 | } else if (file_exists($base . 'tree/' . $className . self::FILE_EXTENSION)) {
42 | require_once($base . 'tree/' . $className . self::FILE_EXTENSION);
43 | } else if (file_exists($base . 'renderers/' . $className . self::FILE_EXTENSION)) {
44 | require_once($base . 'renderers/' . $className . self::FILE_EXTENSION);
45 | } else if (file_exists($base . 'script/' . $className . self::FILE_EXTENSION)) {
46 | require_once($base . 'script/' . $className . self::FILE_EXTENSION);
47 | } else if (file_exists($base . 'script/literals/' . $className . self::FILE_EXTENSION)) {
48 | require_once($base . 'script/literals/' . $className . self::FILE_EXTENSION);
49 | } else {
50 |
51 | }
52 | }
53 |
54 | /**
55 | * Build the filename for a PSR conform className
56 | *
57 | * @param string $className
58 | *
59 | * @return string
60 | */
61 | static private function getPsrFilePath($className) {
62 | $className = ltrim($className, '\\');
63 | $fileName = '';
64 | if ($lastNsPos = strrpos($className, '\\')) {
65 | $namespace = substr($className, 0, $lastNsPos);
66 | $className = substr($className, $lastNsPos + 1);
67 | $fileName = str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR;
68 | }
69 | $fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . self::FILE_EXTENSION;
70 | return $fileName;
71 | }
72 |
73 | /**
74 | * Get the current base path
75 | *
76 | * @return string
77 | */
78 | static private function getBasePath() {
79 | if (self::$basePath === NULL) {
80 | self::$basePath = dirname(__FILE__) . '/';
81 | }
82 | return self::$basePath;
83 | }
84 | }
85 |
86 | spl_autoload_register('SassLoader::load');
--------------------------------------------------------------------------------
/lib/phpsass/VERSION:
--------------------------------------------------------------------------------
1 | Version 201403201700 - Version bump to reflect multiple merged pull requests
2 | Version 201306271500 - Verbump, merge pr #119
3 | Version 201306181400 - Run PHP-CS-Fixer on all files to fix coding style
4 | Version 201306071300 - Version bump to reflect multiple merged pull requests
5 | Version 201304222300 - Version bump to reflect multiple merged pull requests
6 | Version 201211282000 - Version bump to reflect multiple pull requests being merged over time.
7 | Version 201209040040 - Fixed a bunch of issues
8 | Version 201209031740 - Almost up to 3.2.1; Directive interp, splats, new functions, some fixes
9 | Version 201207301030 - Fix to issue 28, extending from included files
10 | Version 201207271000 - Fixed scale-color function (attrib: Steve Jones)
11 | Version 201207262300 - Various issue fixes, better testing framework
12 | Version 201206291700 - Fixed some tests, minor changes to string and import handling
13 | Version 201203071100 - Hotfix to functions, overhauled evaluation method to be less rubbish
14 | Version 201203061130 - Added min/max functions, fix to lists (evaluation instead of just lexing of values)
15 | Version 201203042100 - Fixes to interpolation in selectors, test coverage, and some cleanup
16 | Version 201203021700 - Placeholder selectors! Fixes to [selectors] when nested leaving a space.
17 | Version 201203011100 - Hotfix for unfound function arguments in url() turning up empty from @imported files.
18 | Version 201203010930 - Fix to minor issues and notices (regressions due to changes in prior version)
19 | Version 201203010000 - Proper list support, functions: if, adjust-color, scale-color, change-color, named parameters for functions+mixins, various minor bugfixes
20 | Version 201202100115 - fix to @import parsing of url(x.css)
21 | Version 201202100100 - fix to @each parsing of things like rgba(*,*,*,*) which previously removed braces incorrectly.
22 | Version 201202092300 - fix to @mixins calling @mixins involving variable-name collisions, minor fix to interpolation fail
23 | Version 201202081100 - fix to @media contexts
24 | Version 201202072300 - first version with versions!
25 |
--------------------------------------------------------------------------------
/lib/phpsass/compile-apache.php:
--------------------------------------------------------------------------------
1 | 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:
--------------------------------------------------------------------------------
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 | }
39 |
--------------------------------------------------------------------------------
/lib/phpsass/renderers/SassCompactRenderer.php:
--------------------------------------------------------------------------------
1 |
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 | {
24 | const DEBUG_INFO_RULE = '@media -sass-debug-info';
25 | const DEBUG_INFO_PROPERTY = 'font-family';
26 |
27 | /**
28 | * Renders the brace between the selectors and the properties
29 | * @return string the brace between the selectors and the properties
30 | */
31 | protected function between()
32 | {
33 | return ' { ';
34 | }
35 |
36 | /**
37 | * Renders the brace at the end of the rule
38 | * @return string the brace between the rule and its properties
39 | */
40 | protected function end()
41 | {
42 | return " }\n";
43 | }
44 |
45 | /**
46 | * Renders a comment.
47 | * Comments preceeding a rule are on their own line.
48 | * Comments within a rule are on the same line as the rule.
49 | * @param SassNode $node the node being rendered
50 | * @return string the rendered commnt
51 | */
52 | public function renderComment($node)
53 | {
54 | $nl = ($node->parent instanceof SassRuleNode?'':"\n");
55 |
56 | return "$nl/* " . join("\n * ", $node->children) . " */$nl" ;
57 | }
58 |
59 | /**
60 | * Renders a directive.
61 | * @param SassNode $node the node being rendered
62 | * @param array $properties properties of the directive
63 | * @return string the rendered directive
64 | */
65 | public function renderDirective($node, $properties)
66 | {
67 | return str_replace("\n", '', parent::renderDirective($node, $properties)) .
68 | "\n\n";
69 | }
70 |
71 | /**
72 | * Renders properties.
73 | * @param SassNode $node the node being rendered
74 | * @param array $properties properties to render
75 | * @return string the rendered properties
76 | */
77 | public function renderProperties($node, $properties)
78 | {
79 | return join(' ', $properties);
80 | }
81 |
82 | /**
83 | * Renders a property.
84 | * @param SassNode $node the node being rendered
85 | * @return string the rendered property
86 | */
87 | public function renderProperty($node)
88 | {
89 | $node->important = $node->important ? ' !important' : '';
90 |
91 | return "{$node->name}: {$node->value}{$node->important};";
92 | }
93 |
94 | /**
95 | * Renders a rule.
96 | * @param SassNode $node the node being rendered
97 | * @param array $properties rule properties
98 | * @param string $rules rendered rules
99 | * @return string the rendered rule
100 | */
101 | public function renderRule($node, $properties, $rules)
102 | {
103 | return $this->renderDebug($node) . parent::renderRule($node, $properties,
104 | str_replace("\n\n", "\n", $rules)) . "\n";
105 | }
106 |
107 | /**
108 | * Renders debug information.
109 | * If the node has the debug_info options set true the line number and filename
110 | * are rendered in a format compatible with
111 | * {@link https://addons.mozilla.org/en-US/firefox/addon/firecompass-for-firebug/ FireCompass}.
112 | * Else if the node has the line_numbers option set true the line number and
113 | * filename are rendered in a comment.
114 | * @param SassNode $node the node being rendered
115 | * @return string the debug information
116 | */
117 | protected function renderDebug($node)
118 | {
119 | $indent = $this->getIndent($node);
120 | $debug = '';
121 | if ($node->getDebug_info() || $node->getLine_numbers()) {
122 | $debug .= "$indent/* line {$node->line}, {$node->filename} */\n";
123 | }
124 |
125 | return $debug;
126 | }
127 |
128 | /**
129 | * Renders rule selectors.
130 | * @param SassNode $node the node being rendered
131 | * @return string the rendered selectors
132 | */
133 | protected function renderSelectors($node)
134 | {
135 | $selectors = array();
136 | foreach ($node->selectors as $selector) {
137 | if (!$node->isPlaceholder($selector)) {
138 | $selectors[] = $selector;
139 | }
140 | }
141 |
142 | return join(', ', $selectors);
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/lib/phpsass/renderers/SassCompressedRenderer.php:
--------------------------------------------------------------------------------
1 |
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 | /**
22 | * Renders the brace between the selectors and the properties
23 | * @return string the brace between the selectors and the properties
24 | */
25 | protected function between()
26 | {
27 | return '{';
28 | }
29 |
30 | /**
31 | * Renders the brace at the end of the rule
32 | * @return string the brace between the rule and its properties
33 | */
34 | protected function end()
35 | {
36 | return '}';
37 | }
38 |
39 | /**
40 | * Returns the indent string for the node
41 | * @param SassNode $node the node to return the indent string for
42 | * @return string the indent string for this SassNode
43 | */
44 | protected function getIndent($node)
45 | {
46 | return '';
47 | }
48 |
49 | /**
50 | * Renders a comment.
51 | * @param SassNode $node the node being rendered
52 | * @return string the rendered comment
53 | */
54 | public function renderComment($node)
55 | {
56 | return '';
57 | }
58 |
59 | /**
60 | * Renders a directive.
61 | * @param SassNode $node the node being rendered
62 | * @param array $properties properties of the directive
63 | * @return string the rendered directive
64 | */
65 | public function renderDirective($node, $properties)
66 | {
67 | return $node->directive . $this->between() . $this->renderProperties($node, $properties) . $this->end();
68 | }
69 |
70 | /**
71 | * Renders properties.
72 | * @param SassNode $node the node being rendered
73 | * @param array $properties properties to render
74 | * @return string the rendered properties
75 | */
76 | public function renderProperties($node, $properties)
77 | {
78 | return join('', $properties);
79 | }
80 |
81 | /**
82 | * Renders a property.
83 | * @param SassNode $node the node being rendered
84 | * @return string the rendered property
85 | */
86 | public function renderProperty($node)
87 | {
88 | $node->important = $node->important ? '!important' : '';
89 |
90 | return "{$node->name}:{$node->value}{$node->important};";
91 | }
92 |
93 | /**
94 | * Renders a rule.
95 | * @param SassNode $node the node being rendered
96 | * @param array $properties rule properties
97 | * @param string $rules rendered rules
98 | * @return string the rendered directive
99 | */
100 | public function renderRule($node, $properties, $rules)
101 | {
102 | $selectors = $this->renderSelectors($node);
103 | if ($selectors) {
104 | return (!empty($properties) ? $selectors . $this->between() . $this->renderProperties($node, $properties) . $this->end() : '') . $rules;
105 | }
106 | }
107 |
108 | /**
109 | * Renders the rule's selectors
110 | * @param SassNode $node the node being rendered
111 | * @return string the rendered selectors
112 | */
113 | protected function renderSelectors($node)
114 | {
115 | $selectors = array();
116 | foreach ($node->selectors as $selector) {
117 | if (!$node->isPlaceholder($selector)) {
118 | $selectors[] = $selector;
119 | }
120 | }
121 |
122 | return join(',', $selectors);
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/lib/phpsass/renderers/SassExpandedRenderer.php:
--------------------------------------------------------------------------------
1 |
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 | /**
25 | * Renders the brace between the selectors and the properties
26 | * @return string the brace between the selectors and the properties
27 | */
28 | protected function between()
29 | {
30 | return " {\n" ;
31 | }
32 |
33 | /**
34 | * Renders the brace at the end of the rule
35 | * @return string the brace between the rule and its properties
36 | */
37 | protected function end()
38 | {
39 | return "\n}\n\n";
40 | }
41 |
42 | /**
43 | * Renders a comment.
44 | * @param SassNode $node the node being rendered
45 | * @return string the rendered commnt
46 | */
47 | public function renderComment($node)
48 | {
49 | $indent = $this->getIndent($node);
50 | $lines = explode("\n", $node->value);
51 | foreach ($lines as &$line) {
52 | $line = trim($line);
53 | }
54 |
55 | return "$indent/*\n$indent * ".join("\n$indent * ", $lines)."\n$indent */".(empty($indent)?"\n":'');
56 | }
57 |
58 | /**
59 | * Renders properties.
60 | * @param mixed $node
61 | * @param array $properties properties to render
62 | * @return string the rendered properties
63 | */
64 | public function renderProperties($node, $properties)
65 | {
66 | $indent = $this->getIndent($node).self::INDENT;
67 |
68 | return $indent.join("\n$indent", $properties);
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/lib/phpsass/renderers/SassNestedRenderer.php:
--------------------------------------------------------------------------------
1 |
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 | /**
26 | * Renders the brace at the end of the rule
27 | * @return string the brace between the rule and its properties
28 | */
29 | protected function end()
30 | {
31 | return " }\n";
32 | }
33 |
34 | /**
35 | * Returns the indent string for the node
36 | * @param SassNode $node the node being rendered
37 | * @return string the indent string for this SassNode
38 | */
39 | protected function getIndent($node)
40 | {
41 | return str_repeat(self::INDENT, $node->level);
42 | }
43 |
44 | /**
45 | * Renders a directive.
46 | * @param SassNode $node the node being rendered
47 | * @param array $properties properties of the directive
48 | * @return string the rendered directive
49 | */
50 | public function renderDirective($node, $properties)
51 | {
52 | $directive = $this->getIndent($node) . $node->directive . $this->between() . $this->renderProperties($node, $properties);
53 |
54 | return preg_replace('/(.*})\n$/', '\1', $directive) . $this->end();
55 | }
56 |
57 | /**
58 | * Renders rule selectors.
59 | * @param SassNode $node the node being rendered
60 | * @return string the rendered selectors
61 | */
62 | protected function renderSelectors($node)
63 | {
64 | $selectors = array();
65 | foreach ($node->selectors as $selector) {
66 | if (!$node->isPlaceholder($selector)) {
67 | $selectors[] = $selector;
68 | }
69 | }
70 |
71 | $indent = $this->getIndent($node);
72 |
73 | return $indent.join(",\n$indent", $selectors);
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/lib/phpsass/renderers/SassRenderer.php:
--------------------------------------------------------------------------------
1 |
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 | /**#@+
25 | * Output Styles
26 | */
27 | const STYLE_COMPRESSED = 'compressed';
28 | const STYLE_COMPACT = 'compact';
29 | const STYLE_EXPANDED = 'expanded';
30 | const STYLE_NESTED = 'nested';
31 | /**#@-*/
32 |
33 | const INDENT = ' ';
34 |
35 | /**
36 | * Returns the renderer for the required render style.
37 | * @param string $style render style
38 | * @return SassRenderer
39 | */
40 | public static function getRenderer($style)
41 | {
42 | switch ($style) {
43 | case self::STYLE_COMPACT:
44 | return new SassCompactRenderer();
45 | case self::STYLE_COMPRESSED:
46 | return new SassCompressedRenderer();
47 | case self::STYLE_EXPANDED:
48 | return new SassExpandedRenderer();
49 | case self::STYLE_NESTED:
50 | return new SassNestedRenderer();
51 | } // switch
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/lib/phpsass/script/SassScriptFunction.php:
--------------------------------------------------------------------------------
1 |
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 | /**@#+
22 | * Regexes for matching and extracting functions and arguments
23 | */
24 | const MATCH = '/^(((-\w)|(\w))[-\w:.]*)\(/';
25 | const MATCH_FUNC = '/^((?:(?:-\w)|(?:\w))[-\w:.]*)\((.*)\)/';
26 | const SPLIT_ARGS = '/\s*((?:[\'"].*?["\'])|(?:.+?(?:\(.*\).*?)?))\s*(?:,|$)/';
27 | const NAME = 1;
28 | const ARGS = 2;
29 |
30 | private $name;
31 | private $args;
32 |
33 | public static $context;
34 |
35 | /**
36 | * SassScriptFunction constructor
37 | * @param string $name name of the function
38 | * @param array $args arguments for the function
39 | * @return SassScriptFunction
40 | */
41 | public function __construct($name, $args)
42 | {
43 | $this->name = $name;
44 | $this->args = $args;
45 | }
46 |
47 | private function process_arguments($input)
48 | {
49 | if (is_array($input)) {
50 | $output = array();
51 | foreach ($input as $k => $token) {
52 | $output[$k] = trim($this->process_arguments($token), '\'"');
53 | }
54 |
55 | return $output;
56 | }
57 |
58 | $token = $input;
59 | if ($token === null)
60 | return ' ';
61 |
62 | if (!is_object($token))
63 | return (string) $token;
64 |
65 | if (method_exists($token, 'toString'))
66 | return $token->toString();
67 |
68 | if (method_exists($token, '__toString'))
69 | return $token->__toString();
70 |
71 | if (method_exists($token, 'perform'))
72 | return $token->perform();
73 |
74 | return '';
75 | }
76 |
77 | /**
78 | * Evaluates the function.
79 | * Look for a user defined function first - this allows users to override
80 | * pre-defined functions, then try the pre-defined functions.
81 | *
82 | * @throws Exception
83 | * @return object the value of this Function
84 | */
85 | public function perform()
86 | {
87 | self::$context = new SassContext(SassScriptParser::$context);
88 |
89 | $name = preg_replace('/[^a-z0-9_]/', '_', strtolower($this->name));
90 | $args = $this->process_arguments($this->args);
91 |
92 | foreach ($this->args as $k => $v) {
93 | if (!is_numeric($k)) {
94 | self::$context->setVariable($k, $v);
95 | }
96 | }
97 |
98 | try {
99 | if (SassScriptParser::$context->hasFunction($this->name)) {
100 | $return = SassScriptParser::$context->getFunction($this->name)->execute(SassScriptParser::$context, $this->args);
101 |
102 | return $return;
103 | } elseif (SassScriptParser::$context->hasFunction($name)) {
104 | $return = SassScriptParser::$context->getFunction($name)->execute(SassScriptParser::$context, $this->args);
105 |
106 | return $return;
107 | }
108 | } catch (Exception $e) {
109 | throw $e;
110 | }
111 |
112 | if (isset(SassParser::$functions) && count(SassParser::$functions)) {
113 | foreach (SassParser::$functions as $fn => $callback) {
114 | if (($fn == $name || $fn == $this->name) && is_callable($callback)) {
115 | $result = call_user_func_array($callback, $args);
116 | if (!is_object($result)) {
117 | $lexed = SassScriptLexer::$instance->lex($result, self::$context);
118 | if (count($lexed) === 1) {
119 | return $lexed[0];
120 | }
121 |
122 | return new SassString(implode('', $this->process_arguments($lexed)));
123 | }
124 |
125 | return $result;
126 | }
127 | }
128 | }
129 |
130 | if (method_exists('SassScriptFunctions', $name) || method_exists('SassScriptFunctions', $name = '_' . $name)) {
131 | $sig = self::get_reflection(array('SassScriptFunctions', $name));
132 | list($args) = self::fill_parameters($sig, $this->args, SassScriptParser::$context, $this);
133 |
134 | return call_user_func_array(array('SassScriptFunctions', $name), $args);
135 | }
136 |
137 | foreach ($this->args as $i => $arg) {
138 | if (is_object($arg) && isset($arg->quote)) {
139 | $args[$i] = (string)$arg;
140 | }
141 | if (!is_numeric($i) && SassScriptParser::$context->hasVariable($i)) {
142 | $args[$i] = SassScriptParser::$context->getVariable($i);
143 | }
144 | }
145 |
146 | // CSS function: create a SassString that will emit the function into the CSS
147 | return new SassString($this->name . '(' . join(', ', $args) . ')');
148 | }
149 |
150 | /**
151 | * Imports files in the specified directory.
152 | * @param string $dir path to directory to import
153 | * @return array filenames imported
154 | */
155 | private function import($dir)
156 | {
157 | $files = array();
158 |
159 | foreach (scandir($dir) as $file) {
160 | if (($file === '.') || ($file === '..')) continue;
161 | if (is_file($dir . DIRECTORY_SEPARATOR . $file)) {
162 | $files[] = $file;
163 | require_once($dir . DIRECTORY_SEPARATOR . $file);
164 | }
165 | } // foreach
166 |
167 | return $files;
168 | }
169 |
170 | /**
171 | * Returns a value indicating if a token of this type can be matched at
172 | * the start of the subject string.
173 | * @param string $subject the subject string
174 | * @return mixed match at the start of the string or false if no match
175 | */
176 | public static function isa($subject)
177 | {
178 | if (!preg_match(self::MATCH, $subject, $matches))
179 | return false;
180 |
181 | $match = $matches[0];
182 | $paren = 1;
183 | $strpos = strlen($match);
184 | $strlen = strlen($subject);
185 | $subject_str = (string) $subject;
186 |
187 | while ($paren && $strpos < $strlen) {
188 | $c = $subject_str[$strpos++];
189 |
190 | $match .= $c;
191 | if ($c === '(') {
192 | $paren += 1;
193 | } elseif ($c === ')') {
194 | $paren -= 1;
195 | }
196 | }
197 |
198 | return $match;
199 | }
200 |
201 | public static function extractArgs($string, $include_null = TRUE, $context)
202 | {
203 | $args = array();
204 | $arg = '';
205 | $paren = 0;
206 | $strpos = 0;
207 | $strlen = strlen($string);
208 |
209 | $list = SassList::_build_list($string, ',');
210 | $return = array();
211 |
212 | foreach ($list as $k => $value) {
213 | if (substr($value, -3, 3) == '...' && preg_match(SassVariableNode::MATCH, substr($value, 0, -3) . ':', $match)) {
214 | $list = new SassList($context->getVariable($match[SassVariableNode::NAME]));
215 | if (count($list->value) > 1) {
216 | $return = array_merge($return, $list->value);
217 | continue;
218 | }
219 | }
220 |
221 | if (strpos($value, ':') !== false && preg_match(SassVariableNode::MATCH, $value, $match)) {
222 | $return[$match[SassVariableNode::NAME]] = $match[SassVariableNode::VALUE];
223 | } elseif (substr($value, 0, 1) == '$' && $include_null) {
224 | $return[str_replace('$', '', $value)] = NULL;
225 | } elseif ($include_null || $value !== NULL) {
226 | $return[] = $value;
227 | }
228 | }
229 |
230 | return $return;
231 | }
232 |
233 | public static function get_reflection($method)
234 | {
235 | if (is_array($method)) {
236 | $class = new ReflectionClass($method[0]);
237 | $function = $class->getMethod($method[1]);
238 | } else {
239 | $function = new ReflectionFunction($method);
240 | }
241 |
242 | $return = array();
243 | foreach ($function->getParameters() as $parameter) {
244 | $default = $parameter->isDefaultValueAvailable() ? $parameter->getDefaultValue() : NULL;
245 | if ($default !== NULL) {
246 | $parsed = is_bool($default) ? new SassBoolean($default) : SassScriptParser::$instance->evaluate($default, new SassContext());
247 | $parsed = ($parsed === NULL) ? new SassString($default) : $parsed;
248 | } else {
249 | $parsed = $default;
250 | }
251 | $return[$parameter->getName()] = $parsed; # we evaluate the defaults to get Sass objects.
252 | }
253 |
254 | return $return;
255 | }
256 |
257 | public static function fill_parameters($required, $provided, $context, $source)
258 | {
259 | $context = new SassContext($context);
260 | $_required = array_merge(array(), $required); // need to array_merge?
261 | $fill = $_required;
262 |
263 | foreach ($required as $name=>$default) {
264 | // we require that named variables provide a default.
265 | if (isset($provided[$name]) && $default !== NULL) {
266 | $_required[$name] = $provided[$name];
267 | unset($provided[$name]);
268 | unset($required[$name]);
269 | }
270 | }
271 |
272 | // print_r(array($required, $provided, $_required));
273 | $provided_copy = $provided;
274 |
275 | foreach ($required as $name=>$default) {
276 | if ($default === null && strpos($name, '=') !== FALSE) {
277 | list($name, $default) = explode('=', $name);
278 | $name = trim($name);
279 | $default = trim($default);
280 | }
281 | if (count($provided)) {
282 | $arg = array_shift($provided);
283 | } elseif ($default !== NULL) {
284 | $arg = $default;
285 |
286 | // for mixins with default values that refer to other arguments
287 | // (e.g. border-radius($topright: 0, $bottomright: $topright, $bottomleft: $topright, $topleft: $topright)
288 | if (is_string($default) && $default[0]=='$') {
289 | $referred = trim(trim($default, '$'));
290 | $pos = array_search($referred, array_keys($required));
291 | if ($pos!==false && array_key_exists($pos, $provided_copy)) {
292 | $arg = $provided_copy[$pos];
293 | }
294 | }
295 | } else {
296 | throw new SassMixinNodeException("Function::$name: Required variable ($name) not given.\nFunction defined: " . $source->token->filename . '::' . $source->token->line . "\nFunction used", $source);
297 | }
298 | // splats
299 | if (substr($name, -3, 3) == '...') {
300 | unset ($_required[$name]);
301 | $name = substr($name, 0, -3);
302 | $_required[$name] = new SassList('', ',');
303 | $_required[$name]->value = array_merge(array($arg), $provided);
304 | continue;
305 | } else {
306 | $_required[$name] = $arg;
307 | }
308 | }
309 |
310 | $_required = array_merge($_required, $provided); // any remaining args get tacked onto the end
311 |
312 | foreach ($_required as $key => $value) {
313 | if (!is_object($value)) {
314 | $_required[$key] = SassScriptParser::$instance->evaluate($value, $context);
315 | }
316 | }
317 |
318 | return array($_required, $context);
319 | }
320 | }
321 |
--------------------------------------------------------------------------------
/lib/phpsass/script/SassScriptLexer.php:
--------------------------------------------------------------------------------
1 |
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 | {
31 | const MATCH_WHITESPACE = '/^\s+/';
32 |
33 | /**
34 | * Static holder for last instance of SassScriptLexer
35 | */
36 | public static $instance;
37 |
38 | /**
39 | * @var SassScriptParser the parser object
40 | */
41 | public $parser;
42 |
43 | /**
44 | * SassScriptLexer constructor.
45 | * @return SassScriptLexer
46 | */
47 | public function __construct($parser)
48 | {
49 | $this->parser = $parser;
50 | self::$instance = $this;
51 | }
52 |
53 | /**
54 | * Lex an expression into SassScript tokens.
55 | * @param string $string expression to lex
56 | * @param SassContext $context the context in which the expression is lexed
57 | * @return array tokens
58 | */
59 | public function lex($string, $context)
60 | {
61 | // if it's already lexed, just return it as-is
62 | if (is_object($string)) {
63 | return array($string);
64 | }
65 | if (is_array($string)) {
66 | return $string;
67 | }
68 | $tokens = array();
69 | // whilst the string is not empty, split it into it's tokens.
70 | while ($string !== false) {
71 | if (($match = $this->isWhitespace($string)) !== false) {
72 | $tokens[] = null;
73 | } elseif (($match = SassScriptFunction::isa($string)) !== false) {
74 | preg_match(SassScriptFunction::MATCH_FUNC, $match, $matches);
75 | $args = array();
76 | foreach (SassScriptFunction::extractArgs($matches[SassScriptFunction::ARGS], false, $context) as $key => $expression) {
77 | $args[$key] = $this->parser->evaluate($expression, $context);
78 | }
79 | $tokens[] = new SassScriptFunction($matches[SassScriptFunction::NAME], $args);
80 | } elseif (($match = SassBoolean::isa($string)) !== false) {
81 | $tokens[] = new SassBoolean($match);
82 | } elseif (($match = SassColour::isa($string)) !== false) {
83 | $tokens[] = new SassColour($match);
84 | } elseif (($match = SassNumber::isa($string)) !== false) {
85 | $tokens[] = new SassNumber($match);
86 | } elseif (($match = SassString::isa($string)) !== false) {
87 | $stringed = new SassString($match);
88 | if (
89 | strlen($stringed->quote) == 0 &&
90 | SassList::isa($string) !== false &&
91 | // recursion loop prevented for expressions
92 | // like "transition: -webkit-transform 1s"
93 | !preg_match("/^\-\w+\-\w+$/", $stringed->value)
94 | ) {
95 | $tokens[] = new SassList($string);
96 | } else {
97 | $tokens[] = $stringed;
98 | }
99 | } elseif ($string == '()') {
100 | $match = $string;
101 | $tokens[] = new SassList($match);
102 | } elseif (($match = SassScriptOperation::isa($string)) !== false) {
103 | $tokens[] = new SassScriptOperation($match);
104 | } elseif (($match = SassScriptVariable::isa($string)) !== false) {
105 | $tokens[] = new SassScriptVariable($match);
106 | } else {
107 | $_string = $string;
108 | $match = '';
109 | while (strlen($_string) && !$this->isWhitespace($_string)) {
110 | foreach (SassScriptOperation::$inStrOperators as $operator) {
111 | if (substr($_string, 0, strlen($operator)) == $operator) {
112 | break 2;
113 | }
114 | }
115 | $match .= $_string[0];
116 | $_string = substr($_string, 1);
117 | }
118 | $tokens[] = new SassString($match);
119 | }
120 | $string = substr($string, strlen($match));
121 | if ($string === '') {
122 | $string = false;
123 | }
124 | }
125 |
126 | return $tokens;
127 | }
128 |
129 | /**
130 | * Returns a value indicating if a token of this type can be matched at
131 | * the start of the subject string.
132 | * @param string $subject the subject string
133 | * @return mixed match at the start of the string or false if no match
134 | */
135 | public function isWhitespace($subject)
136 | {
137 | return (preg_match(self::MATCH_WHITESPACE, $subject, $matches) ? $matches[0] : false);
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/lib/phpsass/script/SassScriptOperation.php:
--------------------------------------------------------------------------------
1 |
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 | {
20 | const MATCH = '/^(\(|\)|\+|-|\*|\/|%|<=|>=|<|>|==|!=|=|#{|}|,|and\b|or\b|xor\b|not\b)/';
21 |
22 | /**
23 | * @var array map symbols to tokens.
24 | * A token is function, associativity, precedence, number of operands
25 | */
26 | public static $operators = array(
27 | '*' => array('times', 'l', 8, 2),
28 | '/' => array('div', 'l', 8, 2),
29 | '%' => array('modulo', 'l', 8, 2),
30 | '+' => array('plus', 'l', 7, 2),
31 | '-' => array('minus', 'l', 7, 2),
32 | '<<' => array('shiftl', 'l', 6, 2),
33 | '>>' => array('shiftr', 'l', 6, 2),
34 | '<=' => array('lte', 'l', 5, 2),
35 | '>=' => array('gte', 'l', 5, 2),
36 | '<' => array('lt', 'l', 5, 2),
37 | '>' => array('gt', 'l', 5, 2),
38 | '==' => array('eq', 'l', 4, 2),
39 | '!=' => array('neq', 'l', 4, 2),
40 | 'and' => array('and', 'l', 3, 2),
41 | 'or' => array('or', 'l', 3, 2),
42 | 'xor' => array('xor', 'l', 3, 2),
43 | 'not' => array('not', 'l', 4, 1), # precedence higher than and.
44 | '=' => array('assign', 'l', 2, 2),
45 | ')' => array('rparen', 'l', 10),
46 | '(' => array('lparen', 'l', 10),
47 | ',' => array('comma', 'l', 0, 2),
48 | '#{' => array('begin_interpolation'),
49 | '}' => array('end_interpolation'),
50 | );
51 |
52 | /**
53 | * @var array operators with meaning in uquoted strings;
54 | * selectors, property names and values
55 | */
56 | public static $inStrOperators = array(',', '#{', ')', '(');
57 |
58 | /**
59 | * @var array default operator token.
60 | */
61 | public static $defaultOperator = array('concat', 'l', 0, 2);
62 |
63 | /**
64 | * @var string operator for this operation
65 | */
66 | private $operator;
67 | /**
68 | * @var string associativity of the operator; left or right
69 | */
70 | private $associativity;
71 | /**
72 | * @var integer precedence of the operator
73 | */
74 | private $precedence;
75 | /**
76 | * @var integer number of operands required by the operator
77 | */
78 | private $operandCount = 0;
79 |
80 | /**
81 | * SassScriptOperation constructor
82 | *
83 | * @param mixed string: operator symbol; array: operator token
84 | * @return SassScriptOperation
85 | */
86 | public function __construct($operation)
87 | {
88 | if (is_string($operation)) {
89 | $operation = self::$operators[$operation];
90 | }
91 | $this->operator = $operation[0];
92 | if (isset($operation[1])) {
93 | $this->associativity = $operation[1];
94 | $this->precedence = $operation[2];
95 | $this->operandCount = (isset($operation[3]) ? $operation[3] : 0);
96 | }
97 | }
98 |
99 | /**
100 | * Getter function for properties
101 | * @param string $name name of property
102 | * @return mixed value of the property
103 | * @throws SassScriptOperationException if the property does not exist
104 | */
105 | public function __get($name)
106 | {
107 | if (property_exists($this, $name)) {
108 | return $this->$name;
109 | } else {
110 | throw new SassScriptOperationException('Unknown property: ' . $name, SassScriptParser::$context->node);
111 | }
112 | }
113 |
114 | /**
115 | * Performs this operation.
116 | * @param array $operands operands for the operation. The operands are SassLiterals
117 | * @return SassLiteral the result of the operation
118 | * @throws SassScriptOperationException if the oprand count is incorrect or
119 | * the operation is undefined
120 | */
121 | public function perform($operands)
122 | {
123 | if (count($operands) !== $this->operandCount) {
124 | throw new SassScriptOperationException('Incorrect operand count for ' . get_class($operands[0]) . '; expected ' . $this->operandCount . ', received ' . count($operands), SassScriptParser::$context->node);
125 | }
126 |
127 | if (!count($operands)) {
128 | return $operands;
129 | }
130 |
131 | // fix a bug of unknown origin
132 | foreach ($operands as $i => $op) {
133 | if (!is_object($op)) {
134 | $operands[] = null;
135 | unset ($operands[$i]);
136 | }
137 | }
138 | $operands = array_values($operands);
139 |
140 | if (count($operands) > 1 && $operands[1] === null) {
141 | $operation = 'op_unary_' . $this->operator;
142 | } else {
143 | $operation = 'op_' . $this->operator;
144 | if ($this->associativity == 'l') {
145 | $operands = array_reverse($operands);
146 | }
147 | }
148 |
149 | if (method_exists($operands[0], $operation)) {
150 | $op = clone $operands[0];
151 |
152 | return $op->$operation(!empty($operands[1]) ? $operands[1] : null);
153 | }
154 |
155 | # avoid failures in case of null operands
156 | $count = count($operands);
157 | foreach ($operands as $i => $op) {
158 | if ($op === null) {
159 | $count--;
160 | }
161 | }
162 |
163 | if ($count) {
164 | throw new SassScriptOperationException('Undefined operation "' . $operation . '" for ' . get_class($operands[0]), SassScriptParser::$context->node);
165 | }
166 | }
167 |
168 | /**
169 | * Returns a value indicating if a token of this type can be matched at
170 | * the start of the subject string.
171 | * @param string $subject the subject string
172 | * @return mixed match at the start of the string or false if no match
173 | */
174 | public static function isa($subject)
175 | {
176 | # begins with a "/x", almost always a path without quotes.
177 | if (preg_match('/^\/[^0-9\.\-\s]+/', $subject)) {
178 | return FALSE;
179 | }
180 |
181 | return (preg_match(self::MATCH, $subject, $matches) ? trim($matches[1]) : false);
182 | }
183 |
184 | /**
185 | * Converts the operation back into it's SASS representation
186 | */
187 | public function __toString()
188 | {
189 | foreach (SassScriptOperation::$operators as $char => $operator) {
190 | if ($operator[0] == trim($this->operator)) {
191 | return $char;
192 | }
193 | }
194 | }
195 | }
196 |
--------------------------------------------------------------------------------
/lib/phpsass/script/SassScriptParser.php:
--------------------------------------------------------------------------------
1 |
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 | {
24 | const MATCH_INTERPOLATION = '/(?lexer = new SassScriptLexer($this);
51 | self::$instance = $this;
52 | }
53 |
54 | /**
55 | * Replace interpolated SassScript contained in '#{}' with the parsed value.
56 | * @param string $string the text to interpolate
57 | * @param SassContext $context the context in which the string is interpolated
58 | * @return string the interpolated text
59 | */
60 | public function interpolate($string, $context)
61 | {
62 | for ($i = 0, $n = preg_match_all(self::MATCH_INTERPOLATION, $string, $matches); $i < $n; $i++) {
63 | $var = $this->evaluate($matches[1][$i], $context);
64 |
65 | if ($var instanceOf SassString) {
66 | $var = $var->value;
67 | } else {
68 | $var = $var->toString();
69 | }
70 |
71 | if (preg_match('/^unquote\((["\'])(.*)\1\)$/', $var, $match)) {
72 | $val = $match[2];
73 | } elseif ($var == '""') {
74 | $val = "";
75 | } elseif (preg_match('/^(["\'])(.*)\1$/', $var, $match)) {
76 | $val = $match[2];
77 | } else {
78 | $val = $var;
79 | }
80 | $matches[1][$i] = $val;
81 | }
82 |
83 | return str_replace($matches[0], $matches[1], $string);
84 | }
85 |
86 | /**
87 | * Evaluate a SassScript.
88 | * @param string $expression expression to parse
89 | * @param SassContext $context the context in which the expression is evaluated
90 | * @param integer $environment the environment in which the expression is evaluated
91 | * @return SassLiteral parsed value
92 | */
93 | public function evaluate($expression, $context, $environment = self::DEFAULT_ENV)
94 | {
95 | self::$context = $context;
96 | $operands = array();
97 |
98 | $tokens = $this->parse($expression, $context, $environment);
99 |
100 | while (count($tokens)) {
101 | $token = array_shift($tokens);
102 | if ($token instanceof SassScriptFunction) {
103 | $perform = $token->perform();
104 | array_push($operands, $perform);
105 | } elseif ($token instanceof SassLiteral) {
106 | if ($token instanceof SassString) {
107 | $token = new SassString($this->interpolate($token->toString(), self::$context));
108 | }
109 | array_push($operands, $token);
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 expression to parse
126 | * @param SassContext $context the context in which the expression is parsed
127 | * @param integer $environment 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 | {
132 | $outputQueue = array();
133 | $operatorStack = array();
134 | $parenthesis = 0;
135 |
136 | $tokens = $this->lexer->lex($expression, $context);
137 |
138 | foreach ($tokens as $i=>$token) {
139 | // If two literals/expessions are seperated by whitespace use the concat operator
140 | if (empty($token)) {
141 | if (isset($tokens[$i+1])) {
142 | if ($i > 0 && (!$tokens[$i-1] instanceof SassScriptOperation || $tokens[$i-1]->operator === SassScriptOperation::$operators[')'][0]) &&
143 | (!$tokens[$i+1] instanceof SassScriptOperation || $tokens[$i+1]->operator === SassScriptOperation::$operators['('][0])) {
144 | $token = new SassScriptOperation(SassScriptOperation::$defaultOperator, $context);
145 | } else {
146 | continue;
147 | }
148 | }
149 | } elseif ($token instanceof SassScriptVariable) {
150 | $token = $token->evaluate($context);
151 | $environment = self::DEFAULT_ENV;
152 | }
153 |
154 | // If the token is a number or function add it to the output queue.
155 | if ($token instanceof SassLiteral || $token instanceof SassScriptFunction) {
156 | if ($environment === self::CSS_PROPERTY && $token instanceof SassNumber && !$parenthesis) {
157 | $token->inExpression = false;
158 | }
159 | array_push($outputQueue, $token);
160 | }
161 | // If the token is an operation
162 | elseif ($token instanceof SassScriptOperation) {
163 | // If the token is a left parenthesis push it onto the stack.
164 | if ($token->operator == SassScriptOperation::$operators['('][0]) {
165 | array_push($operatorStack, $token);
166 | $parenthesis++;
167 | }
168 | // If the token is a right parenthesis:
169 | elseif ($token->operator == SassScriptOperation::$operators[')'][0]) {
170 | $parenthesis--;
171 | while ($c = count($operatorStack)) {
172 | // If the token at the top of the stack is a left parenthesis
173 | if ($operatorStack[$c - 1]->operator == SassScriptOperation::$operators['('][0]) {
174 | // Pop the left parenthesis from the stack, but not onto the output queue.
175 | array_pop($operatorStack);
176 | break;
177 | }
178 | // else pop the operator off the stack onto the output queue.
179 | array_push($outputQueue, array_pop($operatorStack));
180 | }
181 | // If the stack runs out without finding a left parenthesis
182 | // there are mismatched parentheses.
183 | if ($c <= 0) {
184 | array_push($outputQueue, new SassString(')'));
185 | break;
186 | }
187 | }
188 | // the token is an operator, o1, so:
189 | else {
190 | // while there is an operator, o2, at the top of the stack
191 | while ($c = count($operatorStack)) {
192 | $operation = $operatorStack[$c - 1];
193 | // if o2 is left parenthesis, or
194 | // the o1 has left associativty and greater precedence than o2, or
195 | // the o1 has right associativity and lower or equal precedence than o2
196 | if (($operation->operator == SassScriptOperation::$operators['('][0]) ||
197 | ($token->associativity == 'l' && $token->precedence > $operation->precedence) ||
198 | ($token->associativity == 'r' && $token->precedence <= $operation->precedence)) {
199 | break; // stop checking operators
200 | }
201 | //pop o2 off the stack and onto the output queue
202 | array_push($outputQueue, array_pop($operatorStack));
203 | }
204 | // push o1 onto the stack
205 | array_push($operatorStack, $token);
206 | }
207 | }
208 | }
209 |
210 | // When there are no more tokens
211 | while ($c = count($operatorStack)) { // While there are operators on the stack:
212 | if ($operatorStack[$c - 1]->operator !== SassScriptOperation::$operators['('][0]) {
213 | array_push($outputQueue, array_pop($operatorStack));
214 | } else {
215 | throw new SassScriptParserException('Unmatched parentheses', $context->node);
216 | }
217 | }
218 |
219 | return $outputQueue;
220 | }
221 |
222 | /**
223 | * Reduces a set down to a singular form
224 | */
225 | public static function makeSingular($operands)
226 | {
227 | if (count($operands) == 1) {
228 | return $operands[0];
229 | }
230 |
231 | $result = null;
232 | foreach ($operands as $i => $operand) {
233 | if (is_object($operand)) {
234 | if (!$result) {
235 | $result = $operand;
236 | continue;
237 | }
238 | if ($result instanceOf SassString) {
239 | $result = $result->op_concat($operand);
240 | } else {
241 | $result = $result->op_plus($operand);
242 | }
243 | } else {
244 | $string = new SassString(' ');
245 | if (!$result) {
246 | $result = $string;
247 | } else {
248 | $result = $result->op_plus($string);
249 | }
250 | }
251 | }
252 |
253 | return $result ? $result : array_shift($operands);
254 | }
255 | }
256 |
--------------------------------------------------------------------------------
/lib/phpsass/script/SassScriptParserExceptions.php:
--------------------------------------------------------------------------------
1 |
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 {}
41 |
--------------------------------------------------------------------------------
/lib/phpsass/script/SassScriptVariable.php:
--------------------------------------------------------------------------------
1 |
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 | /**
20 | * Regex for matching and extracting Variables
21 | */
22 | const MATCH = '/^(?name = substr($value, 1);
36 | }
37 |
38 | /**
39 | * Returns the SassScript object for this variable.
40 | * @param SassContext $context context of the variable
41 | * @return SassLiteral the SassScript object for this variable
42 | */
43 | public function evaluate($context)
44 | {
45 | return $context->getVariable($this->name);
46 | }
47 |
48 | /**
49 | * Returns a value indicating if a token of this type can be matched at
50 | * the start of the subject string.
51 | * @param string $subject the subject string
52 | * @return mixed match at the start of the string or false if no match
53 | */
54 | public static function isa($subject)
55 | {
56 | // we need to do the check as preg_match returns a count of 1 if
57 | // subject == '!important'; the match being an empty match
58 | return (preg_match(self::MATCH, $subject, $matches) ? (empty($matches[0]) ? false : $matches[0]) : false);
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/lib/phpsass/script/literals/SassBoolean.php:
--------------------------------------------------------------------------------
1 |
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 | /**@#+
22 | * Regex for matching and extracting booleans
23 | */
24 | const MATCH = '/^(true|false)\b/';
25 |
26 | /**
27 | * SassBoolean constructor
28 | * @param string $value value of the boolean type
29 | * @throws SassBooleanException
30 | * @return SassBoolean
31 | */
32 | public function __construct($value)
33 | {
34 | if (is_bool($value)) {
35 | $this->value = $value;
36 | } elseif ($value === 'true' || $value === 'false') {
37 | $this->value = ($value === 'true' ? true : false);
38 | } else {
39 | throw new SassBooleanException('Invalid SassBoolean', SassScriptParser::$context->node);
40 | }
41 | }
42 |
43 | /**
44 | * Returns the value of this boolean.
45 | * @return boolean the value of this boolean
46 | */
47 | public function getValue()
48 | {
49 | return $this->value;
50 | }
51 |
52 | /**
53 | * Returns a string representation of the value.
54 | * @return string string representation of the value.
55 | */
56 | public function toString()
57 | {
58 | return $this->getValue() ? 'true' : 'false';
59 | }
60 |
61 | public function length()
62 | {
63 | return 1;
64 | }
65 |
66 | public function nth($i)
67 | {
68 | if ($i == 1 && isset($this->value)) {
69 | return new SassBoolean($this->value);
70 | }
71 |
72 | return new SassBoolean(false);
73 | }
74 |
75 | /**
76 | * Returns a value indicating if a token of this type can be matched at
77 | * the start of the subject string.
78 | * @param string $subject the subject string
79 | * @return mixed match at the start of the string or false if no match
80 | */
81 | public static function isa($subject)
82 | {
83 | return (preg_match(self::MATCH, $subject, $matches) ? $matches[0] : false);
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/lib/phpsass/script/literals/SassList.php:
--------------------------------------------------------------------------------
1 |
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 | public $separator = ' ';
22 |
23 | /**
24 | * SassBoolean constructor
25 | * @param string $value value of the boolean type
26 | * @param string $separator
27 | * @return SassBoolean
28 | */
29 | public function __construct($value, $separator = 'auto')
30 | {
31 | if (is_array($value)) {
32 | $this->value = $value;
33 | $this->separator = ($separator == 'auto' ? ', ' : $separator);
34 | } elseif ($value == '()') {
35 | $this->value = array();
36 | $this->separator = ($separator == 'auto' ? ', ' : $separator);
37 | } elseif (list($list, $separator) = $this->_parse_list($value, $separator, true, SassScriptParser::$context)) {
38 | $this->value = $list;
39 | $this->separator = ($separator == ',' ? ', ' : ' ');
40 | } else {
41 | throw new SassListException('Invalid SassList', SassScriptParser::$context->node);
42 | }
43 | }
44 |
45 | public function nth($i)
46 | {
47 | $i = $i - 1; # SASS uses 1-offset arrays
48 | if (isset($this->value[$i])) {
49 | return $this->value[$i];
50 | }
51 |
52 | return new SassBoolean(false);
53 | }
54 |
55 | public function length()
56 | {
57 | return count($this->value);
58 | }
59 |
60 | public function append($other, $separator = null)
61 | {
62 | if ($separator) {
63 | $this->separator = $separator;
64 | }
65 | if ($other instanceof SassList) {
66 | $this->value = array_merge($this->value, $other->value);
67 | } elseif ($other instanceof SassLiteral) {
68 | $this->value[] = $other;
69 | } else {
70 | throw new SassListException('Appendation can only occur with literals', SassScriptParser::$context->node);
71 | }
72 | }
73 |
74 | // 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.
75 | public function index($value)
76 | {
77 | for ($i = 0; $i < count($this->value); $i++) {
78 | if (trim((string) $value) == trim((string) $this->value[$i])) {
79 | return new SassNumber($i);
80 | }
81 | }
82 |
83 | return new SassBoolean(false);
84 | }
85 |
86 | /**
87 | * Returns the value of this boolean.
88 | * @return boolean the value of this boolean
89 | */
90 | public function getValue()
91 | {
92 | $result = array();
93 | foreach ($this->value as $k => $v) {
94 | if ($v instanceOf SassString) {
95 | $list = $this->_parse_list($v);
96 | if (count($list[0]) > 1) {
97 | if ($list[1] == $this->separator) {
98 | $result = array_merge($result, $list[0]);
99 | } else {
100 | $result[] = $v;
101 | }
102 | } else {
103 | $result[] = $v;
104 | }
105 | } else {
106 | $result[] = $v;
107 | }
108 | }
109 | $this->value = $result;
110 |
111 | return $this->value;
112 | }
113 |
114 | /**
115 | * Returns a string representation of the value.
116 | * @return string string representation of the value.
117 | */
118 | public function toString()
119 | {
120 | $aliases = array(
121 | 'comma' => ',',
122 | 'space' => '',
123 | );
124 | $this->separator = trim($this->separator);
125 | if (isset($aliases[$this->separator])) {
126 | $this->separator = $aliases[$this->separator];
127 | }
128 |
129 | return implode($this->separator . ' ', $this->getValue());
130 | }
131 |
132 | /**
133 | * Returns a value indicating if a token of this type can be matched at
134 | * the start of the subject string.
135 | * @param string $subject the subject string
136 | * @return mixed match at the start of the string or false if no match
137 | */
138 | public static function isa($subject)
139 | {
140 | list($list, $separator) = self::_parse_list($subject, 'auto', false);
141 |
142 | return count($list) > 1 ? $subject : FALSE;
143 | }
144 |
145 | public static function _parse_list($list, $separator = 'auto', $lex = true, $context = null)
146 | {
147 | if ($lex) {
148 | $context = new SassContext($context);
149 | $list = SassScriptParser::$instance->evaluate($list, $context);
150 | $list = $list->toString();
151 | }
152 | if ($separator == 'auto') {
153 | $separator = ',';
154 | $list = $list = self::_build_list($list, ',');
155 | if (count($list) < 2) {
156 | $separator = ' ';
157 | $list = self::_build_list($list, ' ');
158 | }
159 | } else {
160 | $list = self::_build_list($list, $separator);
161 | }
162 |
163 | if ($lex) {
164 | $context = new SassContext($context);
165 | foreach ($list as $k => $v) {
166 | $list[$k] = SassScriptParser::$instance->evaluate($v, $context);
167 | }
168 | }
169 |
170 | return array($list, $separator);
171 | }
172 |
173 | public static function _build_list($list, $separator = ',')
174 | {
175 | if (is_object($list)) {
176 | $list = $list->value;
177 | }
178 |
179 | if (is_array($list)) {
180 | $newlist = array();
181 | foreach ($list as $listlet) {
182 | list($newlist, $separator) = array_merge($newlist, self::_parse_list($listlet, $separator, false));
183 | }
184 | $list = implode(', ', $newlist);
185 | }
186 |
187 | $out = array();
188 | $size = 0;
189 | $braces = 0;
190 | $quotes = false;
191 | $stack = '';
192 | $listCount = strlen($list);
193 | for ($i = 0; $i < $listCount; $i++) {
194 | $char = substr($list, $i, 1);
195 | switch ($char) {
196 | case '"':
197 | case "'":
198 | if (!$quotes) {
199 | $quotes = $char;
200 | } elseif ($quotes && $quotes == $char) {
201 | $quotes = false;
202 | }
203 | $stack .= $char;
204 | break;
205 | case '(':
206 | $braces++;
207 | $stack .= $char;
208 | break;
209 | case ')':
210 | $braces--;
211 | $stack .= $char;
212 | break;
213 | case $separator:
214 | if ($braces === 0 && !$quotes) {
215 | $out[] = $stack;
216 | $stack = '';
217 | $size++;
218 | break;
219 | }
220 | default:
221 | $stack .= $char;
222 | }
223 | }
224 | if (strlen($stack)) {
225 | if (($braces || $quotes) && count($out)) {
226 | $out[count($out) - 1] .= $stack;
227 | } else {
228 | $out[] = $stack;
229 | }
230 | }
231 |
232 | foreach ($out as $k => $v) {
233 | $out[$k] = trim($v, ', ');
234 | }
235 |
236 | return $out;
237 | }
238 | }
239 |
--------------------------------------------------------------------------------
/lib/phpsass/script/literals/SassLiteral.php:
--------------------------------------------------------------------------------
1 |
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 | /**
25 | * @var array maps class names to data types
26 | */
27 | public static $typeOf = array(
28 | 'SassBoolean' => 'bool',
29 | 'SassColour' => 'color',
30 | 'SassNumber' => 'number',
31 | 'SassString' => 'string',
32 | 'SassList' => 'list'
33 | );
34 |
35 | /**
36 | * @var mixed value of the literal type
37 | */
38 | public $value;
39 |
40 | /**
41 | * class constructor
42 | * @param string $value value of the literal type
43 | * @return SassLiteral
44 | */
45 | public function __construct($value = null, $context)
46 | {
47 | $this->value = $value;
48 | $this->context = $context;
49 | }
50 |
51 | /**
52 | * Getter.
53 | * @param string $name name of property to get
54 | * @return mixed return value of getter function
55 | */
56 | public function __get($name)
57 | {
58 | $getter = 'get' . ucfirst($name);
59 | if (method_exists($this, $getter)) {
60 | return $this->$getter();
61 | } else {
62 | throw new SassLiteralException('No getter function for ' . $name, SassScriptParser::$context->node);
63 | }
64 | }
65 |
66 | public function __toString()
67 | {
68 | return $this->toString();
69 | }
70 |
71 | /**
72 | * Returns the boolean representation of the value of this
73 | * @return boolean the boolean representation of the value of this
74 | */
75 | public function toBoolean()
76 | {
77 | return (boolean) $this->value || $this->value === null;
78 | }
79 |
80 | /**
81 | * Returns the type of this
82 | * @return string the type of this
83 | */
84 | public function getTypeOf()
85 | {
86 | return self::$typeOf[get_class($this)];
87 | }
88 |
89 | /**
90 | * Returns the value of this
91 | * @return mixed the value of this
92 | */
93 | public function getValue()
94 | {
95 | throw new SassLiteralException('Child classes must override this method', SassScriptParser::$context->node);
96 | }
97 |
98 | public function getChildren()
99 | {
100 | return array();
101 | }
102 |
103 | /**
104 | * Adds a child object to this.
105 | * @param sassLiteral $sassLiteral the child object
106 | */
107 | public function addChild($sassLiteral)
108 | {
109 | $this->children[] = $sassLiteral;
110 | }
111 |
112 | /**
113 | * SassScript '+' operation.
114 | * @param sassLiteral $other value to add
115 | * @return sassString the string values of this and other with no seperation
116 | */
117 | public function op_plus($other)
118 | {
119 | return new SassString($this->toString().$other->toString());
120 | }
121 |
122 | /**
123 | * SassScript '-' operation.
124 | * @param SassLiteral $other value to subtract
125 | * @return sassString the string values of this and other seperated by '-'
126 | */
127 | public function op_minus($other)
128 | {
129 | return new SassString($this->toString().'-'.$other->toString());
130 | }
131 |
132 | /**
133 | * SassScript '*' operation.
134 | * @param SassLiteral $other value to multiply by
135 | * @return sassString the string values of this and other seperated by '*'
136 | */
137 | public function op_times($other)
138 | {
139 | return new SassString($this->toString().'*'.$other->toString());
140 | }
141 |
142 | /**
143 | * SassScript '/' operation.
144 | * @param SassLiteral $other value to divide by
145 | * @return sassString the string values of this and other seperated by '/'
146 | */
147 | public function op_div($other)
148 | {
149 | return new SassString($this->toString().' / '.$other->toString());
150 | }
151 |
152 | /**
153 | * SassScript '%' operation.
154 | * @param SassLiteral $other value to take the modulus of
155 | * @return SassLiteral result
156 | * @throws Exception if modulo not supported for the data type
157 | */
158 | public function op_modulo($other)
159 | {
160 | throw new SassLiteralException(get_class($this) . ' does not support Modulus', SassScriptParser::$context->node);
161 | }
162 |
163 | /**
164 | * Bitwise AND the value of other and this value
165 | * @param string $other value to bitwise AND with
166 | * @return string result
167 | * @throws Exception if bitwise AND not supported for the data type
168 | */
169 | public function op_bw_and($other)
170 | {
171 | throw new SassLiteralException(get_class($this) . ' does not support Bitwise AND', SassScriptParser::$context->node);
172 | }
173 |
174 | /**
175 | * Bitwise OR the value of other and this value
176 | * @param SassNumber $other value to bitwise OR with
177 | * @return string result
178 | * @throws Exception if bitwise OR not supported for the data type
179 | */
180 | public function op_bw_or($other)
181 | {
182 | throw new SassLiteralException(get_class($this) . ' does not support Bitwise OR', SassScriptParser::$context->node);
183 | }
184 |
185 | /**
186 | * Bitwise XOR the value of other and the value of this
187 | * @param SassNumber $other value to bitwise XOR with
188 | * @return string result
189 | * @throws Exception if bitwise XOR not supported for the data type
190 | */
191 | public function op_bw_xor($other)
192 | {
193 | throw new SassLiteralException(get_class($this) . ' does not support Bitwise XOR', SassScriptParser::$context->node);
194 | }
195 |
196 | /**
197 | * Bitwise NOT the value of other and the value of this
198 | * @return string result
199 | * @throws Exception if bitwise NOT not supported for the data type
200 | */
201 | public function op_bw_not()
202 | {
203 | throw new SassLiteralException(get_class($this) . ' does not support Bitwise NOT', SassScriptParser::$context->node);
204 | }
205 |
206 | /**
207 | * Shifts the value of this left by the number of bits given in value
208 | * @param SassNumber $other amount to shift left by
209 | * @return string result
210 | * @throws Exception if bitwise Shift Left not supported for the data type
211 | */
212 | public function op_shiftl($other)
213 | {
214 | throw new SassLiteralException(get_class($this) . ' does not support Bitwise Shift Left', SassScriptParser::$context->node);
215 | }
216 |
217 | /**
218 | * Shifts the value of this right by the number of bits given in value
219 | * @param SassNumber $other amount to shift right by
220 | * @return string result
221 | * @throws Exception if bitwise Shift Right not supported for the data type
222 | */
223 | public function op_shiftr($other)
224 | {
225 | throw new SassLiteralException(get_class($this) . ' does not support Bitwise Shift Right', SassScriptParser::$context->node);
226 | }
227 |
228 | /**
229 | * The SassScript and operation.
230 | * @param sassLiteral $other the value to and with this
231 | * @return SassLiteral other if this is boolean true, this if false
232 | */
233 | public function op_and($other)
234 | {
235 | return ($this->toBoolean() ? $other : $this);
236 | }
237 |
238 | /**
239 | * The SassScript or operation.
240 | * @param sassLiteral $other the value to or with this
241 | * @return SassLiteral this if this is boolean true, other if false
242 | */
243 | public function op_or($other)
244 | {
245 | return ($this->toBoolean() ? $this : $other);
246 | }
247 |
248 | public function op_assign($other)
249 | {
250 | return $other;
251 | }
252 |
253 | /**
254 | * The SassScript xor operation.
255 | * @param sassLiteral $other the value to xor with this
256 | * @return SassBoolean SassBoolean object with the value true if this or
257 | * other, but not both, are true, false if not
258 | */
259 | public function op_xor($other)
260 | {
261 | return new SassBoolean($this->toBoolean() xor $other->toBoolean());
262 | }
263 |
264 | /**
265 | * The SassScript not operation.
266 | * @return SassBoolean SassBoolean object with the value true if the
267 | * boolean of this is false or false if it is true
268 | */
269 | public function op_not()
270 | {
271 | return new SassBoolean(!$this->toBoolean());
272 | }
273 |
274 | /**
275 | * The SassScript > operation.
276 | * @param sassLiteral $other the value to compare to this
277 | * @return SassBoolean SassBoolean object with the value true if the values
278 | * of this is greater than the value of other, false if it is not
279 | */
280 | public function op_gt($other)
281 | {
282 | return new SassBoolean($this->value > $other->value);
283 | }
284 |
285 | /**
286 | * The SassScript >= operation.
287 | * @param sassLiteral $other the value to compare to this
288 | * @return SassBoolean SassBoolean object with the value true if the values
289 | * of this is greater than or equal to the value of other, false if it is not
290 | */
291 | public function op_gte($other)
292 | {
293 | return new SassBoolean($this->value >= $other->value);
294 | }
295 |
296 | /**
297 | * The SassScript < operation.
298 | * @param sassLiteral $other the value to compare to this
299 | * @return SassBoolean SassBoolean object with the value true if the values
300 | * of this is less than the value of other, false if it is not
301 | */
302 | public function op_lt($other)
303 | {
304 | return new SassBoolean($this->value < $other->value);
305 | }
306 |
307 | /**
308 | * The SassScript <= operation.
309 | * @param sassLiteral $other the value to compare to this
310 | * @return SassBoolean SassBoolean object with the value true if the values
311 | * of this is less than or equal to the value of other, false if it is not
312 | */
313 | public function op_lte($other)
314 | {
315 | return new SassBoolean($this->value <= $other->value);
316 | }
317 |
318 | /**
319 | * The SassScript == operation.
320 | * @param sassLiteral $other the value to compare to this
321 | * @return SassBoolean SassBoolean object with the value true if this and
322 | * other are equal, false if they are not
323 | */
324 | public function op_eq($other)
325 | {
326 | return new SassBoolean($this == $other);
327 | }
328 |
329 | /**
330 | * The SassScript != operation.
331 | * @param sassLiteral $other the value to compare to this
332 | * @return SassBoolean SassBoolean object with the value true if this and
333 | * other are not equal, false if they are
334 | */
335 | public function op_neq($other)
336 | {
337 | return new SassBoolean(!$this->op_eq($other)->toBoolean());
338 | }
339 |
340 | /**
341 | * The SassScript default operation (e.g. $a $b, "foo" "bar").
342 | * @param sassLiteral $other the value to concatenate with a space to this
343 | * @return sassString the string values of this and other seperated by " "
344 | */
345 | public function op_concat($other)
346 | {
347 | return new SassString($this->toString().' '.$other->toString());
348 | }
349 |
350 | /**
351 | * SassScript ',' operation.
352 | * @param sassLiteral $other the value to concatenate with a comma to this
353 | * @return sassString the string values of this and other seperated by ","
354 | */
355 | public function op_comma($other)
356 | {
357 | return new SassString($this->toString().', '.$other->toString());
358 | }
359 |
360 | /**
361 | * Asserts that the literal is the expected type
362 | * @param SassLiteral $other the literal to test
363 | * @param string $type expected type
364 | * @throws SassScriptFunctionException if value is not the expected type
365 | */
366 | public static function assertType($literal, $type)
367 | {
368 | if (!$literal instanceof $type) {
369 | throw new SassScriptFunctionException(($literal instanceof SassLiteral ? get_class($literal) : 'literal') . ' must be a ' . $type, SassScriptParser::$context->node);
370 | }
371 | }
372 |
373 | /**
374 | * Asserts that the value of a literal is within the expected range
375 | * @param SassLiteral $literal the literal to test
376 | * @param float $min the minimum value
377 | * @param float $max the maximum value
378 | * @param string $units the units.
379 | * @throws SassScriptFunctionException if value is not the expected type
380 | */
381 | public static function assertInRange($literal, $min, $max, $units = '')
382 | {
383 | if ($literal->value < $min || $literal->value > $max) {
384 | throw new SassScriptFunctionException($literal->typeOf . ' must be between ' . $min.$units . ' and ' . $max.$units . ' inclusive', SassScriptParser::$context->node);
385 | }
386 | }
387 |
388 | /**
389 | * Returns a string representation of the value.
390 | * @return string string representation of the value.
391 | */
392 | abstract public function toString();
393 |
394 | public function render()
395 | {
396 | return $this->toString();
397 | }
398 |
399 | /**
400 | * Returns a value indicating if a token of this type can be matched at
401 | * the start of the subject string.
402 | * @param string $subject the subject string
403 | * @return mixed match at the start of the string or false if no match
404 | */
405 | public static function isa($subject)
406 | {
407 | throw new SassLiteralException('Child classes must override this method');
408 | }
409 | }
410 |
--------------------------------------------------------------------------------
/lib/phpsass/script/literals/SassLiteralExceptions.php:
--------------------------------------------------------------------------------
1 |
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 | * SassListException class.
37 | * @package PHamlP
38 | * @subpackage Sass.script.literals
39 | */
40 | class SassListException extends SassLiteralException {}
41 |
42 | /**
43 | * SassNumberException class.
44 | * @package PHamlP
45 | * @subpackage Sass.script.literals
46 | */
47 | class SassNumberException extends SassLiteralException {}
48 |
49 | /**
50 | * SassStringException class.
51 | * @package PHamlP
52 | * @subpackage Sass.script.literals
53 | */
54 | class SassStringException extends SassLiteralException {}
55 |
--------------------------------------------------------------------------------
/lib/phpsass/script/literals/SassString.php:
--------------------------------------------------------------------------------
1 |
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 | {
22 | const MATCH = '/^(((["\'])(.*?)(\3))|(-[a-zA-Z-]+[^\s]*?))/i';
23 | const _MATCH = '/^(["\'])(.*?)(\1)?$/'; // Used to match strings such as "Times New Roman",serif
24 | const VALUE = 2;
25 | const QUOTE = 3;
26 |
27 | /**
28 | * @var string string quote type; double or single quotes, or unquoted.
29 | */
30 | public $quote;
31 |
32 | /**
33 | * class constructor
34 | * @param string string
35 | * @return SassString
36 | */
37 | public function __construct($value)
38 | {
39 | preg_match(self::_MATCH, $value, $matches);
40 | if ((isset($matches[self::QUOTE]))) {
41 | $this->quote = $matches[self::QUOTE];
42 | $this->value = $matches[self::VALUE];
43 | } else {
44 | $this->quote = '';
45 | $this->value = $value;
46 | }
47 | }
48 |
49 | /**
50 | * String addition.
51 | * Concatenates this and other.
52 | * The resulting string will be quoted in the same way as this.
53 | * @param sassString string to add to this
54 | * @return sassString the string result
55 | */
56 | public function op_plus($other)
57 | {
58 | $this->value .= $other->value;
59 |
60 | return $this;
61 | }
62 |
63 | /**
64 | * String multiplication.
65 | * this is repeated other times
66 | * @param sassNumber $other the number of times to repeat this
67 | * @return sassString the string result
68 | */
69 | public function op_times($other)
70 | {
71 | if (!($other instanceof SassNumber) || !$other->isUnitless()) {
72 | throw new SassStringException('Value must be a unitless number', SassScriptParser::$context->node);
73 | }
74 | $this->value = str_repeat($this->value, $other->value);
75 |
76 | return $this;
77 | }
78 |
79 | /**
80 | * Equals - works better
81 | */
82 | public function op_eq($other)
83 | {
84 | return new SassBoolean($this->value == $other->value || $this->toString() == $other->toString());
85 | }
86 |
87 | /**
88 | * Evaluates the value as a boolean.
89 | */
90 | public function toBoolean()
91 | {
92 | $value = strtolower(trim($this->value, ' "\''));
93 | if (!$value || in_array($value, array('false', 'null', '0'))) {
94 | return FALSE;
95 | }
96 |
97 | return TRUE;
98 | }
99 |
100 | /**
101 | * Returns the value of this string.
102 | * @return string the string
103 | */
104 | public function getValue()
105 | {
106 | return $this->value;
107 | }
108 |
109 | /**
110 | * Returns a string representation of the value.
111 | * @return string string representation of the value.
112 | */
113 | public function toString()
114 | {
115 | if ($this->quote) {
116 | $value = $this->quote . $this->value . $this->quote;
117 | } else {
118 | $value = strlen(trim($this->value)) ? trim($this->value) : $this->value;
119 | }
120 |
121 | return $value;
122 | }
123 |
124 | public function toVar()
125 | {
126 | return SassScriptParser::$context->getVariable($this->value);
127 | }
128 |
129 | public function getTypeOf()
130 | {
131 | if (SassList::isa($this->toString())) {
132 | return 'list';
133 | }
134 |
135 | return 'string';
136 | }
137 |
138 | /**
139 | * Returns a value indicating if a token of this type can be matched at
140 | * the start of the subject string.
141 | * @param string the subject string
142 | * @return mixed match at the start of the string or false if no match
143 | */
144 | public static function isa($subject)
145 | {
146 | return (preg_match(self::MATCH, $subject, $matches) ? $matches[0] : false);
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/lib/phpsass/tree/SassCharsetNode.php:
--------------------------------------------------------------------------------
1 | source, $matches);
37 |
38 | if (empty($matches)) {
39 | return new SassBoolean('false');
40 | }
41 | }
42 |
43 | /**
44 | * Parse this node.
45 | * Set passed arguments and any optional arguments not passed to their
46 | * defaults, then render the children of the return definition.
47 | * @param SassContext $pcontext the context in which this node is parsed
48 | * @return array the parsed node
49 | */
50 | public function parse($pcontext)
51 | {
52 | return array($this);
53 | }
54 |
55 | public function render() {
56 | // print the original with a semi-colon if needed
57 | return $this->token->source
58 | . (substr($this->token->source, -1, 1) == ';' ? '' : ';')
59 | . "\n";
60 | }
61 |
62 | /**
63 | * Contents a value indicating if the token represents this type of node.
64 | * @param object $token token
65 | * @return boolean true if the token represents this type of node, false if not
66 | */
67 | public static function isa($token)
68 | {
69 | return $token->source[0] === self::IDENTIFIER;
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/lib/phpsass/tree/SassCommentNode.php:
--------------------------------------------------------------------------------
1 |
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 | {
20 | const NODE_IDENTIFIER = '/';
21 | const MATCH = '%^/\*\s*?(.*?)\s*?(\*/)?$%s';
22 | const COMMENT = 1;
23 |
24 | private $value;
25 |
26 | /**
27 | * SassCommentNode constructor.
28 | * @param object $token source token
29 | */
30 | public function __construct($token)
31 | {
32 | parent::__construct($token);
33 | preg_match(self::MATCH, $token->source, $matches);
34 | $this->value = $matches[self::COMMENT];
35 | }
36 |
37 | protected function getValue()
38 | {
39 | return $this->value;
40 | }
41 |
42 | /**
43 | * Parse this node.
44 | * @param mixed $context
45 | * @return array the parsed node - an empty array
46 | */
47 | public function parse($context)
48 | {
49 | return array($this);
50 | }
51 |
52 | /**
53 | * Render this node.
54 | * @return string the rendered node
55 | */
56 | public function render()
57 | {
58 | return $this->renderer->renderComment($this);
59 | }
60 |
61 | /**
62 | * Returns a value indicating if the token represents this type of node.
63 | * @param object $token token
64 | * @return boolean true if the token represents this type of node, false if not
65 | */
66 | public static function isa($token)
67 | {
68 | return $token->source[0] === self::NODE_IDENTIFIER;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/lib/phpsass/tree/SassContentNode.php:
--------------------------------------------------------------------------------
1 | source, $matches);
37 |
38 | if (empty($matches)) {
39 | return new SassBoolean('false');
40 | }
41 | }
42 |
43 | /**
44 | * Parse this node.
45 | * Set passed arguments and any optional arguments not passed to their
46 | * defaults, then render the children of the return definition.
47 | * @param SassContext $pcontext the context in which this node is parsed
48 | * @return array the parsed node
49 | */
50 | public function parse($pcontext)
51 | {
52 | $return = $this;
53 | $context = new SassContext($pcontext);
54 |
55 | $children = array();
56 | foreach ($context->getContent() as $child) {
57 | $child->parent = $this->parent;
58 | $ctx = new SassContext($pcontext->parent);
59 | $ctx->variables = $pcontext->variables;
60 | $children = array_merge($children, $child->parse($ctx));
61 | }
62 |
63 | return $children;
64 | }
65 |
66 | /**
67 | * Contents a value indicating if the token represents this type of node.
68 | * @param object $token token
69 | * @return boolean true if the token represents this type of node, false if not
70 | */
71 | public static function isa($token)
72 | {
73 | return $token->source[0] === self::IDENTIFIER;
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/lib/phpsass/tree/SassContext.php:
--------------------------------------------------------------------------------
1 |
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 | /**
23 | * @var SassContext enclosing context
24 | */
25 | public $parent;
26 |
27 | /**
28 | * @var array mixins defined in this context
29 | */
30 | public $mixins = array();
31 |
32 | /**
33 | * @var array mixins defined in this context
34 | */
35 | public $functions = array();
36 |
37 | /**
38 | * @var array variables defined in this context
39 | */
40 | public $variables = array();
41 |
42 | /**
43 | * @var array tree representing any contextual content.
44 | */
45 | public $content = array();
46 |
47 | /**
48 | * @var SassNode the node being processed
49 | */
50 | public $node;
51 |
52 | /**
53 | * SassContext constructor.
54 | * @param SassContext - the enclosing context
55 | * @return SassContext
56 | */
57 | public function __construct($parent = null)
58 | {
59 | $this->parent = $parent;
60 | }
61 |
62 | /**
63 | *
64 | */
65 | public function getContent()
66 | {
67 | if ($this->content) {
68 | return $this->content;
69 | }
70 | if (!empty($this->parent)) {
71 | return $this->parent->getContent();
72 | }
73 | throw new SassContextException('@content requested but no content passed', $this->node);
74 | }
75 |
76 | /**
77 | * Adds a mixin
78 | * @param string $name name of mixin
79 | * @param mixed $mixin
80 | * @return SassMixinDefinitionNode the mixin
81 | */
82 | public function addMixin($name, $mixin)
83 | {
84 | $this->mixins[$name] = $mixin;
85 |
86 | return $this;
87 | }
88 |
89 | /**
90 | * Returns a mixin
91 | * @param string $name name of mixin to return
92 | * @return SassMixinDefinitionNode the mixin
93 | * @throws SassContextException if mixin not defined in this context
94 | */
95 | public function getMixin($name)
96 | {
97 | if (isset($this->mixins[$name])) {
98 | return $this->mixins[$name];
99 | } elseif (!empty($this->parent)) {
100 | return $this->parent->getMixin($name);
101 | }
102 | throw new SassContextException('Undefined Mixin: ' . $name, $this->node);
103 | }
104 |
105 | /**
106 | * Adds a function
107 | * @param string $name name of function
108 | * @param mixed $function
109 | * @return SassFunctionDefinitionNode the function
110 | */
111 | public function addFunction($name, $function)
112 | {
113 | $this->functions[$name] = $function;
114 | if (!empty($this->parent)) {
115 | $this->parent->addFunction($name, $function);
116 | }
117 |
118 | return $this;
119 | }
120 |
121 | /**
122 | * Returns a function
123 | * @param string $name name of function to return
124 | * @return SassFunctionDefinitionNode the mixin
125 | * @throws SassContextException if function not defined in this context
126 | */
127 | public function getFunction($name)
128 | {
129 | if ($fn = $this->hasFunction($name)) {
130 | return $fn;
131 | }
132 | throw new SassContextException('Undefined Function: ' . $name, $this->node);
133 | }
134 |
135 | /**
136 | * Returns a boolean wether this function exists
137 | * @param string $name name of function to check for
138 | * @return boolean
139 | */
140 | public function hasFunction($name)
141 | {
142 | if (isset($this->functions[$name])) {
143 | return $this->functions[$name];
144 | } elseif (!empty($this->parent)) {
145 | return $this->parent->hasFunction($name);
146 | }
147 |
148 | return FALSE;
149 | }
150 |
151 | /**
152 | * Returns a variable defined in this context
153 | * @param string $name name of variable to return
154 | * @return string the variable
155 | * @throws SassContextException if variable not defined in this context
156 | */
157 | public function getVariable($name)
158 | {
159 | $name = str_replace('-', '_', $name);
160 | if ($this->hasVariable($name)) {
161 | return $this->variables[$name];
162 | } elseif (!empty($this->parent)) {
163 | return $this->parent->getVariable($name);
164 | } else {
165 | // Return false instead of throwing an exception.
166 | // throw new SassContextException('Undefined Variable: ' . $name, $this->node);
167 | return new SassBoolean('false');
168 | }
169 | }
170 |
171 | /**
172 | * Returns a value indicating if the variable exists in this context
173 | * @param string $name name of variable to test
174 | * @return boolean true if the variable exists in this context, false if not
175 | */
176 | public function hasVariable($name)
177 | {
178 | $name = str_replace('-', '_', $name);
179 |
180 | return isset($this->variables[$name]);
181 | }
182 |
183 | /**
184 | * Sets a variable to the given value
185 | * @param string $name name of variable
186 | * @param sassLiteral $value value of variable
187 | * @return SassContext
188 | */
189 | public function setVariable($name, $value)
190 | {
191 | $name = str_replace('-', '_', $name);
192 | $this->variables[$name] = $value;
193 |
194 | return $this;
195 | }
196 |
197 | public function setVariables($vars)
198 | {
199 | foreach ($vars as $key => $value) {
200 | if ($value !== NULL) {
201 | $this->setVariable($key, $value);
202 | }
203 | }
204 | }
205 |
206 | /**
207 | * Makes variables and mixins from this context available in the parent context.
208 | * Note that if there are variables or mixins with the same name in the two
209 | * contexts they will be set to that defined in this context.
210 | */
211 | public function merge()
212 | {
213 | $this->parent->variables = array_merge($this->parent->variables, $this->variables);
214 | $this->parent->mixins = array_merge($this->parent->mixins, $this->mixins);
215 | }
216 | }
217 |
--------------------------------------------------------------------------------
/lib/phpsass/tree/SassDebugNode.php:
--------------------------------------------------------------------------------
1 |
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 | {
20 | const IDENTIFIER = '@';
21 | const MATCH = '/^@(?:debug|warn)\s+(.+?)\s*;?$/';
22 | const MESSAGE = 1;
23 |
24 | /**
25 | * @var string the debug/warning message
26 | */
27 | private $message;
28 | /**
29 | * @var array parameters for the message;
30 | * only used by internal warning messages
31 | */
32 | private $params;
33 | /**
34 | * @var boolean true if this is a warning
35 | */
36 | private $warning;
37 |
38 | /**
39 | * SassDebugNode.
40 | * @param object $token source token
41 | * @param mixed string: an internally generated warning message about the
42 | * source
43 | * boolean: the source token is a @debug or @warn directive containing the
44 | * message; True if this is a @warn directive
45 | * @param array $message parameters for the message
46 | * @return SassDebugNode
47 | */
48 | public function __construct($token, $message = false)
49 | {
50 | parent::__construct($token);
51 | if (is_string($message)) {
52 | $this->message = $message;
53 | $this->warning = true;
54 | } else {
55 | preg_match(self::MATCH, $token->source, $matches);
56 | $this->message = $matches[self::MESSAGE];
57 | $this->warning = $message;
58 | }
59 | }
60 |
61 | /**
62 | * Parse this node.
63 | * This raises an error.
64 | * @return array An empty array
65 | */
66 | public function parse($context)
67 | {
68 | if (!$this->warning) {
69 | $result = $this->evaluate($this->message, $context)->toString();
70 |
71 | $cb = SassParser::$instance->options['callbacks']['debug'];
72 | if ($cb) {
73 | call_user_func($cb, $result, $context);
74 | } else {
75 | set_error_handler(array($this, 'errorHandler'));
76 | trigger_error($result);
77 | restore_error_handler();
78 | }
79 | }
80 |
81 | return array();
82 | }
83 |
84 | /**
85 | * Error handler for degug and warning statements.
86 | * @param int $errno Error number
87 | * @param string $message Message
88 | */
89 | public function errorHandler($errno, $message)
90 | {
91 | echo '