WP-LESS - Version 1.9.2

Version Description

= 1.6.0 =

Warning: this release has some breaking changes. If you the old selector expression syntax (eg. Bootstrap 2.3) then its recommended to use 0.3.9.

  • Add support for ; as argument delimiter
  • Add support for passing arguments by name to mixin
  • Remove old selector expression syntax ("hello")
  • Remove ability to skip arguments by repeating delimiter
  • Add built in functions: sin, cos, tan, asin, acos, atan, pow, pi, mod, sqrt, extract
  • Fix bug where @arguments was not getting values from ...
  • Selector interpolation works inside of brackets in selector
  • Fix bug when resolving mixin that has same name as enclosing class
  • Duplicate properties are now removed from output
Download this release

Release Info

Developer oncletom
Plugin Icon wp plugin WP-LESS
Version 1.9.2
Comparing to
See all releases

Code changes from version 1.4.2 to 1.9.2

Files changed (81) hide show
  1. .svnignore +5 -0
  2. CONTRIBUTORS.md +13 -0
  3. bootstrap-for-theme.php +20 -12
  4. bootstrap.php +11 -5
  5. composer.json +18 -0
  6. doc/API.md +119 -0
  7. doc/Advanced-Usage.md +111 -0
  8. doc/Common-Usage.md +92 -0
  9. doc/Embedding-within-a-WordPress-Theme.md +99 -0
  10. doc/README.md +38 -0
  11. doc/api/filters.txt +0 -0
  12. doc/api/hooks.txt +0 -0
  13. doc/theme/footer.php +0 -2
  14. doc/theme/functions.php +0 -6
  15. doc/theme/header.php +0 -9
  16. doc/theme/index.php +0 -7
  17. doc/theme/screen.less +0 -20
  18. doc/theme/style.css +0 -11
  19. doc/usage.txt +0 -0
  20. lib/Compiler.class.php +93 -55
  21. lib/Configuration.class.php +94 -73
  22. lib/Garbagecollector.class.php +121 -0
  23. lib/Loader.class.php +49 -0
  24. lib/Plugin.class.php +441 -226
  25. lib/Stylesheet.class.php +30 -31
  26. lib/vendor/lessphp/.gitignore +0 -4
  27. lib/vendor/lessphp/LICENSE +0 -660
  28. lib/vendor/lessphp/README.md +0 -64
  29. lib/vendor/lessphp/docs/docs.md +0 -798
  30. lib/vendor/lessphp/lessc.inc.php +0 -2239
  31. lib/vendor/lessphp/lessify +0 -23
  32. lib/vendor/lessphp/lessify.inc.php +0 -447
  33. lib/vendor/lessphp/plessc +0 -191
  34. lib/vendor/lessphp/tests/README +0 -23
  35. lib/vendor/lessphp/tests/inputs/accessors.less.disable +0 -36
  36. lib/vendor/lessphp/tests/inputs/attributes.less +0 -41
  37. lib/vendor/lessphp/tests/inputs/builtins.less +0 -33
  38. lib/vendor/lessphp/tests/inputs/colors.less +0 -98
  39. lib/vendor/lessphp/tests/inputs/compile_on_mixin.less +0 -39
  40. lib/vendor/lessphp/tests/inputs/escape.less +0 -19
  41. lib/vendor/lessphp/tests/inputs/font_family.less +0 -28
  42. lib/vendor/lessphp/tests/inputs/hacks.less +0 -6
  43. lib/vendor/lessphp/tests/inputs/import.less +0 -21
  44. lib/vendor/lessphp/tests/inputs/keyframes.less +0 -48
  45. lib/vendor/lessphp/tests/inputs/math.less +0 -81
  46. lib/vendor/lessphp/tests/inputs/media.less +0 -27
  47. lib/vendor/lessphp/tests/inputs/misc.less +0 -71
  48. lib/vendor/lessphp/tests/inputs/mixin_functions.less +0 -39
  49. lib/vendor/lessphp/tests/inputs/mixin_merging.less.disable +0 -100
  50. lib/vendor/lessphp/tests/inputs/mixins.less +0 -122
  51. lib/vendor/lessphp/tests/inputs/nested.less +0 -60
  52. lib/vendor/lessphp/tests/inputs/out.html +0 -0
  53. lib/vendor/lessphp/tests/inputs/scopes.less +0 -40
  54. lib/vendor/lessphp/tests/inputs/site_demos.less +0 -120
  55. lib/vendor/lessphp/tests/inputs/test-imports/file1.less +0 -16
  56. lib/vendor/lessphp/tests/inputs/test-imports/file2.less +0 -6
  57. lib/vendor/lessphp/tests/inputs/variables.less +0 -38
  58. lib/vendor/lessphp/tests/outputs/accessors.css +0 -14
  59. lib/vendor/lessphp/tests/outputs/attributes.css +0 -35
  60. lib/vendor/lessphp/tests/outputs/builtins.css +0 -17
  61. lib/vendor/lessphp/tests/outputs/colors.css +0 -57
  62. lib/vendor/lessphp/tests/outputs/compile_on_mixin.css +0 -11
  63. lib/vendor/lessphp/tests/outputs/escape.css +0 -13
  64. lib/vendor/lessphp/tests/outputs/font_family.css +0 -17
  65. lib/vendor/lessphp/tests/outputs/hacks.css +0 -1
  66. lib/vendor/lessphp/tests/outputs/import.css +0 -14
  67. lib/vendor/lessphp/tests/outputs/keyframes.css +0 -36
  68. lib/vendor/lessphp/tests/outputs/math.css +0 -34
  69. lib/vendor/lessphp/tests/outputs/media.css +0 -22
  70. lib/vendor/lessphp/tests/outputs/misc.css +0 -27
  71. lib/vendor/lessphp/tests/outputs/mixin_functions.css +0 -14
  72. lib/vendor/lessphp/tests/outputs/mixin_merging.css +0 -42
  73. lib/vendor/lessphp/tests/outputs/mixins.css +0 -47
  74. lib/vendor/lessphp/tests/outputs/nested.css +0 -16
  75. lib/vendor/lessphp/tests/outputs/nesting.css +0 -6
  76. lib/vendor/lessphp/tests/outputs/scopes.css +0 -7
  77. lib/vendor/lessphp/tests/outputs/site_demos.css +0 -54
  78. lib/vendor/lessphp/tests/outputs/variables.css +0 -18
  79. lib/vendor/lessphp/tests/test.php +0 -139
  80. lib/vendor/plugin-toolkit/BaseConfiguration.class.php +1 -1
  81. readme.txt +116 -56
.svnignore ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ *.idea
2
+ .git
3
+ *.lock
4
+ tests
5
+ test
CONTRIBUTORS.md ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # `wp-less` Contributors
2
+
3
+ These people have contributed code to improve the quality and features of the WordPress plugin.
4
+ They are listed in an alphabetical order.
5
+
6
+ * [Drew Geraets](https://github.com/drewgeraets)
7
+ * SSL URL issue
8
+ * [Mark Fabrizio](https://github.com/fabrizim)
9
+ * Caching and Garbage collection issues
10
+ * [Rix](https://github.com/RixTox)
11
+ * WordPress compatibility issue
12
+ * [Sebastian Schmid](https://github.com/sebastianschmid)
13
+ * URL resolving on old PHP version
bootstrap-for-theme.php CHANGED
@@ -2,32 +2,32 @@
2
  /*
3
  * This file tends to be included in any development.
4
  * In a sentence, in every case where you don't want to use WP-LESS as a standalone.
5
- *
6
  * Once included, it's up to you to use the available toolkit for your needs.
7
- *
8
  * = How to use? =
9
- *
10
  * 1. In your theme, include the `wp-less` anywhere you want. (eg: `wp-content/themes/yourtheme/lib/wp-less`)
11
  * 2. Include the required files in your functions.php file. (eg: `require dirname(__FILE__).'/lib/wp-less/bootstreap-theme.php`)
12
  * 3. The `$WPLessPlugin` is available for your
13
- *
14
  * In case you need to access the $WPLessPlugin variable outside the include scope, simply do that:
15
  * `$WPLessPlugin = WPLessPlugin::getInstance();`
16
- *
17
  * And to apply automatic building on page display:
18
  * `add_action('wp_print_styles', array($WPLessPlugin, 'processStylesheets'));`
19
  * Or apply all hooks with:
20
  * `$WPLessPlugin->dispatch();`
21
- *
22
  * You can rebuild all stylesheets at any time with:
23
  * `$WPLessPlugin->processStylesheets();`
24
- *
25
  * Or a specific stylesheet:
26
  * `wp_enqueue_style('my_css', 'path/to/my/style.css');`
27
  * `$WPLessPlugin->processStylesheet('my_css');`
28
- *
29
  * = Filters and hooks aren't enough =
30
- *
31
  * Build your own flavour and manage it the way you want. Simply extends WPLessPlugin and/or WPLessConfiguration.
32
  * Dig in the code to see what to configure. I tried to make things customizable without extending classes!
33
  */
@@ -36,8 +36,16 @@
36
  * This will be effective only if the plugin is not activated.
37
  * You can then redistribute your theme with this loader fearlessly.
38
  */
39
- if (!class_exists('WPLessPlugin'))
40
  {
41
- require dirname(__FILE__).'/lib/Plugin.class.php';
42
- $WPLessPlugin = WPPluginToolkitPlugin::create('WPLess', __FILE__, 'WPLessPlugin');
 
 
 
 
 
 
 
 
43
  }
2
  /*
3
  * This file tends to be included in any development.
4
  * In a sentence, in every case where you don't want to use WP-LESS as a standalone.
5
+ *
6
  * Once included, it's up to you to use the available toolkit for your needs.
7
+ *
8
  * = How to use? =
9
+ *
10
  * 1. In your theme, include the `wp-less` anywhere you want. (eg: `wp-content/themes/yourtheme/lib/wp-less`)
11
  * 2. Include the required files in your functions.php file. (eg: `require dirname(__FILE__).'/lib/wp-less/bootstreap-theme.php`)
12
  * 3. The `$WPLessPlugin` is available for your
13
+ *
14
  * In case you need to access the $WPLessPlugin variable outside the include scope, simply do that:
15
  * `$WPLessPlugin = WPLessPlugin::getInstance();`
16
+ *
17
  * And to apply automatic building on page display:
18
  * `add_action('wp_print_styles', array($WPLessPlugin, 'processStylesheets'));`
19
  * Or apply all hooks with:
20
  * `$WPLessPlugin->dispatch();`
21
+ *
22
  * You can rebuild all stylesheets at any time with:
23
  * `$WPLessPlugin->processStylesheets();`
24
+ *
25
  * Or a specific stylesheet:
26
  * `wp_enqueue_style('my_css', 'path/to/my/style.css');`
27
  * `$WPLessPlugin->processStylesheet('my_css');`
28
+ *
29
  * = Filters and hooks aren't enough =
30
+ *
31
  * Build your own flavour and manage it the way you want. Simply extends WPLessPlugin and/or WPLessConfiguration.
32
  * Dig in the code to see what to configure. I tried to make things customizable without extending classes!
33
  */
36
  * This will be effective only if the plugin is not activated.
37
  * You can then redistribute your theme with this loader fearlessly.
38
  */
39
+ if (!class_exists('WPLessPluginLoader'))
40
  {
41
+ require dirname(__FILE__).'/lib/Loader.class.php';
42
+ $WPLessPlugin = WPLessPluginLoader::load(function($WPLessPlugin) {
43
+
44
+ //READY and WORKING
45
+ //add_action('after_setup_theme', array($WPLessPlugin, 'install'));
46
+
47
+ // NOT WORKING
48
+ //@see http://core.trac.wordpress.org/ticket/14955
49
+ //add_action('uninstall_theme', array($WPLessPlugin, 'uninstall'));
50
+ });
51
  }
bootstrap.php CHANGED
@@ -3,15 +3,21 @@
3
  Plugin Name: WP LESS
4
  Description: LESS extends CSS with variables, mixins, operations and nested rules. This plugin magically parse all your <code>*.less</code> files queued with <code>wp_enqueue_style</code> in WordPress.
5
  Author: Oncle Tom
6
- Version: 1.4.2
7
- Author URI: http://case.oncle-tom.net/
8
  Plugin URI: http://wordpress.org/extend/plugins/wp-less/
9
 
10
  This plugin is released under version 3 of the GPL:
11
  http://www.opensource.org/licenses/gpl-3.0.html
12
  */
13
 
14
- require dirname(__FILE__).'/lib/Plugin.class.php';
15
- $WPLessPlugin = WPPluginToolkitPlugin::create('WPLess', __FILE__);
 
 
 
 
16
 
17
- $WPLessPlugin->dispatch();
 
 
3
  Plugin Name: WP LESS
4
  Description: LESS extends CSS with variables, mixins, operations and nested rules. This plugin magically parse all your <code>*.less</code> files queued with <code>wp_enqueue_style</code> in WordPress.
5
  Author: Oncle Tom
6
+ Version: 1.9.0
7
+ Author URI: https://oncletom.io/
8
  Plugin URI: http://wordpress.org/extend/plugins/wp-less/
9
 
10
  This plugin is released under version 3 of the GPL:
11
  http://www.opensource.org/licenses/gpl-3.0.html
12
  */
13
 
14
+ if (!class_exists('WPLessPluginLoader'))
15
+ {
16
+ require dirname(__FILE__).'/lib/Loader.class.php';
17
+ $WPLessPlugin = WPLessPluginLoader::load(function($WPLessPlugin) {
18
+ register_activation_hook(__FILE__, array($WPLessPlugin, 'install'));
19
+ register_deactivation_hook(__FILE__, array($WPLessPlugin, 'uninstall'));
20
 
21
+ $WPLessPlugin->dispatch();
22
+ });
23
+ }
composer.json ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "oncletom/wp-less",
3
+ "require": {
4
+ "oyejorge/less.php": "~1.7",
5
+ "leafo/lessphp": "~0.5",
6
+ "wikimedia/less.php" : "~3.1"
7
+ },
8
+ "autoload": {
9
+ "classmap": ["lib/"]
10
+ },
11
+ "license": "GPL-2.0",
12
+ "authors": [
13
+ {
14
+ "name": "oncletom",
15
+ "email": "hi@oncletom.io"
16
+ }
17
+ ]
18
+ }
doc/API.md ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # API
2
+
3
+ ## Processing Workflow
4
+
5
+ This is part of the [WordPress Action Refererence](http://codex.wordpress.org/Plugin_API/Action_Reference).
6
+
7
+ 1. `wp-less_compiler_construct_pre` WP-LESS action
8
+ 1. `wp-less_compiler_construct` WP-LESS filter
9
+ 1. `wp-less_init` WP-LESS action
10
+ 1. …
11
+ 1. `plugin_loaded` WordPress action
12
+ 1. `after_setup_theme` WordPress action
13
+ 1. `init` WordPress action
14
+ 1. …
15
+ 1. `wp` WordPress event
16
+ 1. …
17
+ 1. `wp_head` WordPress action
18
+ 1. `wp-less_plugin_process_stylesheets` WP-LESS action during `wp_enqueue_scripts`
19
+ 1. Then, for each stylesheet:
20
+ 1. `wp-less_stylesheet_construct` WP-LESS action
21
+ 1. `wp-less_stylesheet_compute_target_path` WP-LESS filter
22
+ 1. `wp-less_stylesheet_save_pre` WP-LESS action (if it has to compile)
23
+ 1. `wp-less_stylesheet_save` WP-LESS filter (it it has to compile)
24
+ 1. `wp-less_stylesheet_save_post` WP-LESS action (it it has to compile)
25
+ 1. …
26
+ 1. `wp_print_styles` WordPress action
27
+
28
+ This workflow means if you have to alter some configuration values, it has to be done before `wp` priority 999.
29
+
30
+ It also means if you register stylesheets **after** `wp` action, they won’t be handled by the plugin.
31
+
32
+ ## Plugin Hooks
33
+
34
+ ## `WPLessPlugin` Class
35
+
36
+ You can access to the known instance of `WPLessPlugin` at any time by doing the following:
37
+
38
+ ```php
39
+ $less = WPLessPlugin::getInstance();
40
+
41
+ // do stuff with its API like:
42
+ $less->addVariable('red', '#f00');
43
+ ```
44
+
45
+ ### `addVariable`
46
+
47
+ TBD.
48
+
49
+ ### `setVariables`
50
+
51
+ TBD.
52
+
53
+ ### `registerFunction`
54
+
55
+ TBD.
56
+
57
+ ### `unregisterFunction`
58
+
59
+ TBD.
60
+
61
+ ### `addImportDir`
62
+
63
+ TBD.
64
+
65
+ ### `setImportDir`
66
+
67
+ TBD.
68
+
69
+ ### `getImportDir`
70
+
71
+ TBD.
72
+
73
+ ### `install`
74
+
75
+ TBD.
76
+
77
+ ### `uninstall`
78
+
79
+ TBD.
80
+
81
+ ### `processStylesheet`
82
+
83
+ TBD.
84
+
85
+ ### `processStylesheets`
86
+
87
+ TBD.
88
+
89
+ ## `WPLessConfiguration` Class
90
+
91
+ You can access to the known instance of `WPLessConfiguration ` at any time by doing the following:
92
+
93
+ ```php
94
+ $config = WPLessPlugin::getInstance()->getConfiguration();
95
+
96
+ // do stuff with its API like:
97
+ $config->setCompilationStrategy('legacy');
98
+ $config->getTtl(); // returns 432000 (5 days)
99
+ ```
100
+
101
+ ### `getConfigurationStrategy`
102
+
103
+ TBD.
104
+
105
+ ### `alwaysRecompile`
106
+
107
+ TBD.
108
+
109
+ ### `setConfigurationStrategy`
110
+
111
+ TBD.
112
+
113
+ ### `getTtl`
114
+
115
+ TBD.
116
+
117
+ ## Scheduled Tasks
118
+
119
+ TBD.
doc/Advanced-Usage.md ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Advanced Usage
2
+
3
+ This part of the documentation will help you adapting `wp-less` plugin to your very own needs without breaking anything.
4
+
5
+ ## Registering a LESS variable
6
+
7
+ You can inject [LESS variables](http://leafo.net/lessphp/docs/#variables) before the compilation process. You can then use **dynamic variables** defined upon WordPress settings or your own business logic.
8
+
9
+ This can be performed with 2 methods of the `WPLessPlugin` class:
10
+ * `addVariable($name string, $value string|number)`: sets 1 LESS variable value;
11
+ * `setVariables($variables array)`: sets several LESS variables value at one.
12
+
13
+ ```php
14
+ // wp-content/themes/your-theme/functions.php
15
+
16
+ if (class_exists('WPLessPlugin')){
17
+ $less = WPLessPlugin::getInstance();
18
+
19
+ $less->addVariable('myColor', '#666');
20
+ // you can now use @myColor in your *.less files
21
+
22
+ $less->setVariables(array(
23
+ 'myColor' => '#777',
24
+ 'minSize' => '18px'
25
+ ));
26
+ // you can now use @minSize in your *.less files
27
+ // @myColor value has been updated to #777
28
+ }
29
+ ```
30
+
31
+ ## Registering a LESS function
32
+
33
+ You can inject [custom LESS functions](http://leafo.net/lessphp/docs/#custom_functions) before the compilation process. You can now package LESS helpers for your theme in a very useable way (even as WordPress plugins).
34
+
35
+ This can be performed with 1 method of the `WPLessPlugin` class:
36
+ * `registerFunction($name string, $callback string)`: binds a PHP callback function to a LESS function.
37
+
38
+ ```php
39
+ // wp-content/themes/your-theme/functions.php
40
+
41
+ if (class_exists('WPLessPlugin')){
42
+ $less = WPLessPlugin::getInstance();
43
+
44
+ function less_generate_random($max = 1000){
45
+ return rand(1, $max);
46
+ }
47
+
48
+ $less->registerFunction('random', 'less_generate_random');
49
+ // you can now use random() in your *.less files, like
50
+ // div.random-size{
51
+ // width: less_generate_random(666);
52
+ // }
53
+ }
54
+ ```
55
+
56
+ **Notice**: don't forget the handy [native LESS functions](http://leafo.net/lessphp/docs/#built_in_functions).
57
+
58
+ ## Changing compilation target directory
59
+
60
+ By default `wp-less` will outputs compiled CSS to your WordPress upload folder (by default: `wp-content/uploads/wp-less`).
61
+ It’s done this way because this folder is usually available in *write mode*, even with tricky filesystem permissions.
62
+
63
+ You can alter the compile path both for filesystem and URIs. It is usefull if you have a CDN for theme assets or if your browser path is different than the filesystem one.
64
+
65
+ This can be performed with 2 methods of the `WPLessConfiguration` class:
66
+ * `setUploadDir($dir string)`: sets the new compile filesystem directory;
67
+ * `setUploadUrl($url string)`: sets several LESS variables value at one.
68
+
69
+ ```php
70
+ // wp-content/themes/your-theme/functions.php
71
+
72
+ if (class_exists('WPLessPlugin')){
73
+ $lessConfig = WPLessPlugin::getInstance()->getConfiguration();
74
+
75
+ // compiles in the active theme, in a ‘compiled-css’ subfolder
76
+ $lessConfig->setUploadDir(get_stylesheet_directory() . '/compiled-css');
77
+ $lessConfig->setUploadUrl(get_stylesheet_directory_uri() . '/compiled-css');
78
+ }
79
+ ```
80
+ ## Changing the less compiler
81
+
82
+ By default `wp-less` will use the compiler from the [leafo/lessphp](https://github.com/leafo/lessphp) library. wp-less also ships with the [oyejorge/less.php](https://github.com/oyejorge/less.php) library, which is more up-to-date and contains the ":extend" less language construct (which is needed for the compilation of certain frameworks including the latest Twitter Bootstrap).
83
+
84
+ __Note__ The `less.php` library does not support registering custom php functions.
85
+
86
+ If you would like to change the compiler, you can use the `wp_less_compiler` filter. The returned value can be either:
87
+ * "lessphp" (Default)
88
+ * "less.php"
89
+ * Path to a file containing a class named "lessc" (see the less.php [`lessc.inc.php`](https://github.com/oyejorge/less.php/blob/master/lessc.inc.php) for an example of what needs to be defined).
90
+
91
+ ### Example using the less.php library
92
+ ```php
93
+ // wp-content/themes/your-theme/functions.php
94
+
95
+ if (class_exists('WPLessPlugin')){
96
+ function my_theme_wp_less_compiler()
97
+ {
98
+ return 'less.php';
99
+ }
100
+ add_filter('wp_less_compiler', 'my_theme_wp_less_compiler');
101
+ }
102
+ ```
103
+
104
+ ## Make Less output minified
105
+ ``` php
106
+ // wp-content/themes/your-theme/functions.php
107
+
108
+ add_action('wp-less_init', function($WPLess) {
109
+ $WPLess->getCompiler()->setFormatter('compressed');
110
+ } );
111
+ ```
doc/Common-Usage.md ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Common Usage
2
+
3
+ Once installed, **the plugin is ready to work**. All you have to do is to define which LESS files you want to automatically compile into static CSS.
4
+
5
+ The advantage over the less.js embed in the browser is purely a matter of **performance and caching**.
6
+
7
+ The default values of `wp-less` **already take care about server overhead**, browser cache-hit issues and settings change!
8
+
9
+ ## Registering a LESS stylesheet
10
+
11
+ Just like you would add a CSS stylesheet in Wordpress using Wordpress' built-in function [`wp_enqueue_style`](http://codex.wordpress.org/Function_Reference/wp_enqueue_style) you can now also add your LESS stylesheets using this function. This function can only be called upon in your theme's [`functions.php`](http://codex.wordpress.org/Theme_Development#Functions_File). Register your LESS stylesheets as following:
12
+
13
+ ```php
14
+ add_action('wp_enqueue_scripts', 'theme_enqueue_styles');
15
+
16
+ function theme_enqueue_styles() {
17
+ wp_enqueue_style('theme-main', get_stylesheet_directory_uri().'/stylesheets/theme-main.less');
18
+ wp_enqueue_style('theme-extra', get_stylesheet_directory_uri().'/stylesheets/theme-extra.less');
19
+ }
20
+ ```
21
+
22
+ This will compile the specified files into CSS files which will be put in a separate folder belonging to the plugin. HTML code linking to them is then automatically added to your main page by adding `<link>` tags when Wordpress calls `wp_head()`.
23
+
24
+ Note that you can still use `wp_enqueue_style` for CSS files as well, compilation will just be skipped then.
25
+
26
+ ## Configuration Constants
27
+
28
+ Default configuration may not suit your needs so a few constants are available.
29
+
30
+ You should alter them in your regular `wp-config.php` file, under the root of your WordPress install.
31
+
32
+ ### Compilation Strategy
33
+
34
+ ```php
35
+ // wp-config.php
36
+
37
+ define('WP_LESS_COMPILATION', '<value:string>');
38
+ ```
39
+
40
+ `<value:string>` can be replaced by the following values:
41
+
42
+ * **deep** (default): LESS stylesheets will be recompiled if the file **or** imported files have been modified;
43
+ * **always**: LESS stylesheets will be recompiled on *every page*, even if they have not been altered;
44
+ * **legacy**: LESS stylesheets will be recompiled **only** if the registered stylesheet has been modified, regardless of imported files (it was the default behavior prior to `wp-less 1.5`).
45
+
46
+ **NOTICE**: The `always` strategy compiles stylesheets on every page, every time. It can cause serious server overhead with high volumes of traffic and LESS code.
47
+
48
+ ### Always Recompile
49
+
50
+ Sometimes, you want to always recompile, whatever the reasons are. **This is not a recommended *production* setting**.
51
+
52
+ You should also have in mind these 2 things:
53
+
54
+ 1. this value is equivalent to `define(‘WP_LESS_COMPILATION’, ‘always’);`;
55
+ 1. this value overrides the setting defined by `WP_LESS_COMPILATION `.
56
+
57
+ ```php
58
+ // wp-config.php
59
+
60
+ define('WP_LESS_ALWAYS_RECOMPILE', <value:bool>);
61
+ ```
62
+
63
+ `<value:bool>` can be replaced by the following values:
64
+
65
+ * `true`: all LESS stylesheets will be recompiled on each visited page on your blog;
66
+ * `false`(default): stylesheets are not recompiled on each visited page.
67
+
68
+ ### WP_DEBUG
69
+
70
+ If `WP_DEBUG` is set to true, LESS stylesheets will be recompiled on each visited page of your blog.
71
+
72
+ It is the same behavior as `WP_LESS_ALWAYS_RECOMPILE` except you also gets all WordPress debug stuff with it.
73
+
74
+ ## Available variables
75
+
76
+ Since version `1.5`, `wp-less` setup default variables useable in every LESS stylesheet (even imported ones):
77
+
78
+ ### `@stylesheet_directory_uri`
79
+
80
+ > Since 1.5
81
+
82
+ Equals to the value returned by [`get_stylesheet_directory_uri()`](http://codex.wordpress.org/get_stylesheet_directory_uri).
83
+
84
+ It is particularly useful if you want to link to assets located in your stylesheet theme directory.
85
+
86
+ ### `@template_directory_uri`
87
+
88
+ > Since 1.5
89
+
90
+ Equals to the value returned by [`get_template_directory_uri()`](http://codex.wordpress.org/get_template_directory_uri).
91
+
92
+ It is particularly useful if you want to link to assets located in your template theme directory.
doc/Embedding-within-a-WordPress-Theme.md ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Embedding within a WordPress Theme
2
+
3
+ For some reasons, you want to bundle `wp-less` plugin within a theme or a plugin. 2 things to know:
4
+
5
+ 1. it’s totally feasible and it’s officially supported;
6
+ 1. theme bundling will take over plugin bundling, or even the official plugin.
7
+
8
+ ## Basics of embedding
9
+
10
+ `wp-less` plugin provides a special file for embedding. It does the dirty job and let you the hand on what to do before dispatching stuff.
11
+
12
+ ### First: embedding
13
+ The first part of embedding is… embedding:
14
+
15
+ ```php
16
+ // wp-content/themes/your-theme/functions.php
17
+
18
+ require dirname(__FILE__) . '/vendor/wp-less/bootstrap-for-theme.php';
19
+ ```
20
+
21
+ At this point, the plugin is available these way:
22
+ * `$WPLessPlugin` variable available within the global scope;
23
+ * `WPLessPlugin::getInstance()` will always return you the active plugin instance, whatever the scope is (longer but safer).
24
+
25
+ ### Second: configuring
26
+
27
+ You can do whatever you want with the plugin. It hasn’t been initialized yet. Deal with folders, change compilation strategy or whatever.
28
+
29
+ You can register any LESS stylesheet, it has no incidence.
30
+ There won’t be parsed at this moment.
31
+
32
+ ### Third: initializing
33
+
34
+ You now have to plug `wp-less` on WordPress events to make the plugin work.
35
+
36
+ This is as simple as the following code:
37
+
38
+ ```php
39
+ // wp-content/themes/your-theme/functions.php
40
+
41
+ $less = WPLessPlugin::getInstance();
42
+ $less->dispatch();
43
+ ```
44
+
45
+ The `dispatch` method deals with everything you need.
46
+
47
+ You’re done!
48
+
49
+ ## Manual registration of scheduled tasks
50
+
51
+ However, by embedding the plugin, you have to manually activate the garbage collector. This feature cleans every compiled file older than 5 days, every day.
52
+
53
+ Regarding of where you embed the plugin, you have to **register the task only once**:
54
+
55
+ ```php
56
+ // wp-content/themes/your-theme/functions.php
57
+ // …
58
+
59
+ $less->install();
60
+ ```
61
+
62
+ The same way, you also have to unregister the garbage collector at the relevant moment:
63
+
64
+ ```php
65
+ // wp-content/themes/your-theme/functions.php
66
+ // …
67
+
68
+ $less->uninstall();
69
+ ```
70
+
71
+ Within a plugin, it would looks like this:
72
+
73
+ ```php
74
+ // wp-content/themes/your-theme/functions.php
75
+ // …
76
+
77
+ register_activation_hook(__FILE__, array($less, 'install'));
78
+ register_deactivation_hook(__FILE__, array($less, 'uninstall'));
79
+ ```
80
+
81
+ ## If the plugin is installed aside
82
+
83
+ Remember that if `wp-less` exists in the `wp-content/plugins` folder, you can use this code as dependency of your theme or plugin.
84
+
85
+ To always rely on the latest up-to-date version, you should detect the existence of the plugin **after all plugins have been loaded**. You could then bundle your own `wp-less` copy as legacy fallback, just in case.
86
+
87
+ ```php
88
+ // wp-content/themes/my-theme/functions.php
89
+
90
+ add_action('plugins_loaded', 'register_less_fallback');
91
+
92
+ function register_less_fallback(){
93
+ if (!class_exists('WPLessPlugin')){
94
+ require dirname(__FILE__) . '/vendor/wp-less/bootstrap-for-theme.php';
95
+ WPLessPlugin::getInstance()->dispatch();
96
+ // we’re done, everything works as if the plugin is activated
97
+ }
98
+ }
99
+ ```
doc/README.md ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Welcome in [wp-less](http://wordpress.org/extend/plugins/wp-less/) usage documentation.
2
+
3
+ Using this plugin, your life becomes easier when dealing with `*.less` files within WordPress.
4
+ You only have to register your stylesheets like before.
5
+
6
+ # Common Usage
7
+
8
+ Covers an out-of-the-box usage of the plugin once activated.
9
+
10
+ * [Registering a LESS stylesheet](Common-Usage.md#Registering a LESS stylesheet)
11
+ * [Configuration Constants](Common-Usage.md#Configuration Constants)
12
+ * [Available variables](Common-Usage.md#Available variables)
13
+
14
+ # Advanced Usage
15
+
16
+ Covers a fine-tuned plugin usage for your very special needs without breaking stuff.
17
+
18
+ * [Registering a LESS variable](Advanced-Usage.md#Registering a LESS variable)
19
+ * [Registering a LESS function](Advanced-Usage.md#Registering a LESS function)
20
+ * [Changing compilation target directory](Advanced-Usage.md#Changing compilation target directory)
21
+
22
+ # Embedding within a WordPress Theme
23
+
24
+ Covers the way to redistribute a theme or a plugin embedding `wp-less`.
25
+
26
+ * [Basics of embedding](Embedding-within-a-WordPress-Theme.md#Basics of embedding)
27
+ * [Manual registration of scheduled tasks](Embedding-within-a-WordPress-Theme.md#Manual registration of scheduled tasks)
28
+ * [If the plugin is installed aside](Embedding-within-a-WordPress-Theme.md#If the plugin is installed aside)
29
+
30
+ # API
31
+
32
+ Low level API to alter deeply its way of working.
33
+
34
+ * [Processing Workflow](API.md#Processing Workflow)
35
+ * [WPLessPlugin Class](API.md#WPLessPlugin Class)
36
+ * [WPLessConfiguration Class](API.md#WPLessConfiguration Class)
37
+ * [Plugin Hooks](API.md#Hooks and Filters)
38
+ * [Scheduled Tasks](API.md#Scheduled Tasks)
doc/api/filters.txt DELETED
File without changes
doc/api/hooks.txt DELETED
File without changes
doc/theme/footer.php DELETED
@@ -1,2 +0,0 @@
1
- </body>
2
- </html>
 
 
doc/theme/functions.php DELETED
@@ -1,6 +0,0 @@
1
- <?php
2
-
3
- if (!is_admin())
4
- {
5
- wp_enqueue_style('wp-less-sample-theme', get_template_directory_uri().'/screen.less', array(), '1.0', 'screen,projection');
6
- }
 
 
 
 
 
 
doc/theme/header.php DELETED
@@ -1,9 +0,0 @@
1
- <!DOCTYPE html>
2
- <html <?php language_attributes() ?>>
3
- <head>
4
- <title><?php wp_title() ?></title>
5
- <?php wp_head() ?>
6
- <meta http-equiv="imagetoolbar" content="no" />
7
- <meta http-equiv="Content-Type" content="<?php bloginfo('html_type') ?>; charset=<?php bloginfo('charset') ?>" />
8
- </head>
9
- <body>
 
 
 
 
 
 
 
 
 
doc/theme/index.php DELETED
@@ -1,7 +0,0 @@
1
- <?php get_header() ?>
2
-
3
- <h1>Unleash LESS power!</h1>
4
-
5
- <p>Thanks for using the plugin.</p>
6
-
7
- <?php get_footer() ?>
 
 
 
 
 
 
 
doc/theme/screen.less DELETED
@@ -1,20 +0,0 @@
1
- @base: 24px;
2
- @border-color: #B2B;
3
-
4
- .underline { border-bottom: 1px solid green }
5
-
6
- #header {
7
- color: black;
8
- border: 1px solid @border-color + #222222;
9
-
10
- .navigation {
11
- font-size: @base/2;
12
- a {
13
- .underline;
14
- }
15
- }
16
- .logo {
17
- width: 300px;
18
- :hover { text-decoration: none }
19
- }
20
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
doc/theme/style.css DELETED
@@ -1,11 +0,0 @@
1
- /*
2
- Theme Name: Sample WP-LESS theme
3
- Theme URI: http://wordpress.org/extend/plugins/wp-less/
4
- Description: Plug 'n play theme showing how to implement WP-LESS
5
- Author: Oncle Tom
6
- Author URI: http://case.oncle-tom.net/
7
- Version: 1.0
8
-
9
- This example relies on the fact the WP-LESS plugin is activated.
10
- A future example will show how to bundle WP-LESS without requiring the plugin. Ideal for themers.
11
- */
 
 
 
 
 
 
 
 
 
 
 
doc/usage.txt DELETED
File without changes
lib/Compiler.class.php CHANGED
@@ -1,7 +1,7 @@
1
  <?php
2
  /**
3
  * LESS compiler
4
- *
5
  * @author oncletom
6
  * @extends lessc
7
  * @package wp-less
@@ -13,71 +13,109 @@ class WPLessCompiler extends lessc
13
  {
14
  /**
15
  * Instantiate a compiler
16
- *
17
- * @api
18
  * @see lessc::__construct
19
  * @param $file string [optional] Additional file to parse
20
  */
21
  public function __construct($file = null)
22
  {
23
- do_action('wp-less_compiler_construct_pre', $this, $file);
24
  parent::__construct(apply_filters('wp-less_compiler_construct', $file));
25
  }
26
 
27
- /**
28
- * Parse a LESS file
29
- *
30
- * @api
31
- * @see lessc::parse
32
- * @throws Exception
33
- * @param string $text [optional] Custom CSS to parse
34
- * @param array $variables [optional] Variables to inject in the stylesheet
35
- * @return string CSS output
36
- */
37
- public function parse($text = null, $variables = null)
38
- {
39
- do_action('wp-less_compiler_parse_pre', $this, $text, $variables);
40
- return apply_filters('wp-less_compiler_parse', parent::parse($text, $variables));
41
- }
42
 
43
- /**
44
- * Registers a set of functions
45
- * Originally stored in WPLessConfiguration instance
46
- *
47
- * @param array $functions
48
- */
49
- public function registerFunctions(array $functions = array())
50
- {
51
- foreach ($functions as $name => $args)
52
- {
53
- $this->registerFunction($name, $args['callback']);
54
- }
55
- }
56
 
57
- /**
58
- * Process a WPLessStylesheet
59
- *
60
- * This logic was previously held in WPLessStylesheet::save()
61
- *
62
- * @since 1.4.2
63
- */
64
- public function saveStylesheet(WPLessStylesheet $stylesheet)
65
- {
66
- wp_mkdir_p(dirname($stylesheet->getTargetPath()));
67
 
68
- try
69
- {
70
- do_action('wp-less_stylesheet_save_pre', $stylesheet, $stylesheet->getVariables());
 
 
 
 
 
 
 
 
 
 
 
 
 
71
 
72
- file_put_contents($stylesheet->getTargetPath(), apply_filters('wp-less_stylesheet_save', $this->parse(null, $stylesheet->getVariables()), $stylesheet));
73
- chmod($stylesheet->getTargetPath(), 0666);
74
 
75
- $stylesheet->save();
76
- do_action('wp-less_stylesheet_save_post', $stylesheet);
77
- }
78
- catch(Exception $e)
79
- {
80
- wp_die($e->getMessage());
81
- }
82
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  }
1
  <?php
2
  /**
3
  * LESS compiler
4
+ *
5
  * @author oncletom
6
  * @extends lessc
7
  * @package wp-less
13
  {
14
  /**
15
  * Instantiate a compiler
16
+ *
17
+ * @api
18
  * @see lessc::__construct
19
  * @param $file string [optional] Additional file to parse
20
  */
21
  public function __construct($file = null)
22
  {
23
+ do_action('wp-less_compiler_construct_pre', $this, $file);
24
  parent::__construct(apply_filters('wp-less_compiler_construct', $file));
25
  }
26
 
27
+ /**
28
+ * Registers a set of functions
29
+ *
30
+ * @param array $functions
31
+ */
32
+ public function registerFunctions(array $functions = array())
33
+ {
34
+ foreach ($functions as $name => $args)
35
+ {
36
+ $this->registerFunction($name, $args['callback']);
37
+ }
38
+ }
 
 
 
39
 
40
+ /**
41
+ * Returns available variables
42
+ *
43
+ * @since 1.5
44
+ * @return array Already defined variables
45
+ */
46
+ public function getVariables()
47
+ {
48
+ return $this->registeredVars;
49
+ }
 
 
 
50
 
51
+ public function setVariable($name, $value)
52
+ {
53
+ $this->registeredVars[ $name ] = $value;
54
+ }
 
 
 
 
 
 
55
 
56
+ public function getImportDir()
57
+ {
58
+ return (array)$this->importDir;
59
+ }
60
+
61
+ /**
62
+ * Smart caching and retrieval of a tree of @import LESS stylesheets
63
+ *
64
+ * @since 1.5
65
+ * @param WPLessStylesheet $stylesheet
66
+ * @param bool $force
67
+ */
68
+ public function cacheStylesheet(WPLessStylesheet $stylesheet, $force = false)
69
+ {
70
+ $cache_name = 'wp_less_compiled_'.md5($stylesheet->getSourcePath());
71
+ $compiled_cache = get_transient($cache_name);
72
 
73
+ if( !$force && !file_exists( $stylesheet->getTargetPath() ) ) $force = true;
 
74
 
75
+ $compiled_cache = $this->cachedCompile($compiled_cache ? $compiled_cache : $stylesheet->getSourcePath(), $force);
76
+
77
+ // saving compiled stuff
78
+ if (isset($compiled_cache['compiled']) && $compiled_cache['compiled'])
79
+ {
80
+ $stylesheet->setSourceTimestamp($compiled_cache['updated']);
81
+ $this->saveStylesheet($stylesheet, $compiled_cache['compiled']);
82
+
83
+ $compiled_cache['compiled'] = NULL;
84
+ set_transient($cache_name, $compiled_cache);
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Process a WPLessStylesheet
90
+ *
91
+ * This logic was previously held in WPLessStylesheet::save()
92
+ *
93
+ * @since 1.4.2
94
+ * @param WPLessStylesheet $stylesheet
95
+ * @param null $css
96
+ */
97
+ public function saveStylesheet(WPLessStylesheet $stylesheet, $css = null)
98
+ {
99
+ wp_mkdir_p(dirname($stylesheet->getTargetPath()));
100
+
101
+ try
102
+ {
103
+ do_action('wp-less_stylesheet_save_pre', $stylesheet, $this->getVariables());
104
+
105
+ if ($css === null)
106
+ {
107
+ $css = $this->compileFile($stylesheet->getSourcePath());
108
+ }
109
+
110
+ file_put_contents($stylesheet->getTargetPath(), apply_filters('wp-less_stylesheet_save', $css, $stylesheet));
111
+ chmod($stylesheet->getTargetPath(), 0666);
112
+
113
+ $stylesheet->save();
114
+ do_action('wp-less_stylesheet_save_post', $stylesheet);
115
+ }
116
+ catch(Exception $e)
117
+ {
118
+ wp_die($e->getMessage());
119
+ }
120
+ }
121
  }
lib/Configuration.class.php CHANGED
@@ -10,19 +10,34 @@ class WPLessConfiguration extends WPPluginToolkitConfiguration
10
  /**
11
  * Refers to the version of the plugin
12
  */
13
- const VERSION = '1.4';
14
 
15
- /**
16
- * @protected
17
- */
18
- protected $variables = array();
19
 
20
- /**
21
- * @protected
22
- * @see http://leafo.net/lessphp/docs/index.html#custom_functions
23
- */
24
- protected $functions = array();
25
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
  protected function configure()
28
  {
@@ -31,74 +46,80 @@ class WPLessConfiguration extends WPPluginToolkitConfiguration
31
 
32
  protected function configureOptions()
33
  {
34
- $this->setVariables(array());
35
- }
 
 
36
 
37
- /**
38
- * Set global Less variables
39
- *
40
- * @since 1.4
41
- */
42
- public function addVariable($name, $value)
43
- {
44
- $this->variables[$name] = $value;
45
  }
46
 
47
- /**
48
- * Returns the registered variables
49
- *
50
- * @since 1.4
51
- * @return array
52
- */
53
- public function getVariables()
54
- {
55
- return $this->variables;
56
- }
 
57
 
58
- /**
59
- * Set global Less variables
60
- *
61
- * @since 1.4
62
- */
63
- public function setVariables(array $variables)
64
- {
65
- $this->variables = $variables;
66
- }
 
67
 
68
- /**
69
- * Return LESS functions
70
- *
71
- * @since 1.4.2
72
- * @return array
73
- */
74
- public function getFunctions()
75
- {
76
- return $this->functions;
77
- }
 
 
 
78
 
79
- /**
80
- * Registers a new LESS function
81
- *
82
- * @param string $name
83
- * @param Closure|function $callback
84
- * @param array $scope CSS handles to limit callback registration to (if empty, applies to every stylesheet) – not used yet
85
- * @see http://leafo.net/lessphp/docs/index.html#custom_functions
86
- */
87
- public function registerFunction($name, $callback, $scope = array())
88
- {
89
- $this->functions[$name] = array(
90
- 'callback' => $callback,
91
- 'scope' => $scope,
92
- );
93
- }
 
 
 
 
 
 
 
 
 
 
 
94
 
95
- /**
96
- * Unregisters a LESS function
97
- *
98
- * @see http://leafo.net/lessphp/docs/index.html#custom_functions
99
- */
100
- public function unregisterFunction($name)
101
- {
102
- unset($this->functions[$name]);
103
- }
104
  }
10
  /**
11
  * Refers to the version of the plugin
12
  */
13
+ const VERSION = '1.8.0';
14
 
 
 
 
 
15
 
 
 
 
 
 
16
 
17
+ /**
18
+ * Current compilation strategy
19
+ *
20
+ * @since 1.5
21
+ * @protected
22
+ * @var string
23
+ */
24
+ protected $compilation_strategy = 'deep';
25
+
26
+ /**
27
+ * Available compilation strategies
28
+ *
29
+ * @since 1.5
30
+ * @var array
31
+ */
32
+ protected $compilation_strategies = array('legacy', 'always', 'deep');
33
+
34
+ /**
35
+ * Time to live before pruning CSS cache
36
+ *
37
+ * @protected
38
+ * @var int delay in seconds
39
+ */
40
+ protected $ttl = 432000; // 5 days
41
 
42
  protected function configure()
43
  {
46
 
47
  protected function configureOptions()
48
  {
49
+ if (defined('WP_LESS_COMPILATION') && WP_LESS_COMPILATION)
50
+ {
51
+ $this->setCompilationStrategy(WP_LESS_COMPILATION);
52
+ }
53
 
54
+ //previous setting can be overridden for special reasons (dev/prod for example)
55
+ if ((defined('WP_DEBUG') && WP_DEBUG) || (defined('WP_LESS_ALWAYS_RECOMPILE') && WP_LESS_ALWAYS_RECOMPILE))
56
+ {
57
+ $this->setCompilationStrategy('always');
58
+ }
 
 
 
59
  }
60
 
61
+ /**
62
+ * Current compilation strategy
63
+ *
64
+ * @api
65
+ * @since 1.5
66
+ * @return string Active compilation strategy
67
+ */
68
+ public function getCompilationStrategy()
69
+ {
70
+ return $this->compilation_strategy;
71
+ }
72
 
73
+ /**
74
+ * Always recompile
75
+ *
76
+ * @since 1.5
77
+ * @return bool
78
+ */
79
+ public function alwaysRecompile()
80
+ {
81
+ return $this->compilation_strategy === 'always';
82
+ }
83
 
84
+ /**
85
+ * Set compilation strategy
86
+ *
87
+ * @api
88
+ * @since 1.5
89
+ * @param $strategy string Actual compilation "strategy"
90
+ */
91
+ public function setCompilationStrategy($strategy)
92
+ {
93
+ if (!in_array($strategy, $this->compilation_strategies))
94
+ {
95
+ throw new WPLessException('Unknown compile strategy: ['.$strategy.'] provided.');
96
+ }
97
 
98
+ $this->compilation_strategy = $strategy;
99
+ }
100
+
101
+ /**
102
+ * Retrieves the TTL of a compiled CSS file
103
+ *
104
+ * @api
105
+ * @since 1.5
106
+ * @return int Time to live of a compiled CSS file
107
+ */
108
+ public function getTtl()
109
+ {
110
+ return $this->ttl;
111
+ }
112
+
113
+ /**
114
+ * Sets the TTL fo a compiled CSS file
115
+ *
116
+ * @api
117
+ * @param $ttl
118
+ * @since 1.5.1
119
+ */
120
+ public function setTtl($ttl)
121
+ {
122
+ $this->ttl = (int)$ttl;
123
+ }
124
 
 
 
 
 
 
 
 
 
 
125
  }
lib/Garbagecollector.class.php ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WPLessGarbagecollector
4
+ {
5
+ /**
6
+ * @static
7
+ * @var int Max number of compiled versions of a same file to keep
8
+ */
9
+ public static $COMPILED_VERSIONS = 3;
10
+
11
+ /**
12
+ * @protected
13
+ * @var WPLessConfiguration
14
+ */
15
+ protected $configuration;
16
+
17
+ public function __construct(WPLessConfiguration $configuration)
18
+ {
19
+ $this->configuration = $configuration;
20
+ }
21
+
22
+ /**
23
+ * Performs the cleanup of outdated CSS files
24
+ *
25
+ */
26
+ public function clean()
27
+ {
28
+ $outdated_files = $this->getOutdatedFiles($this->configuration->getTtl());
29
+ $this->filterVersions($outdated_files);
30
+
31
+ if (!empty($outdated_files))
32
+ {
33
+ $this->deleteFiles($outdated_files);
34
+ }
35
+ }
36
+
37
+ /**
38
+ * Retrieves old CSS files and list them
39
+ *
40
+ * @param $ttl int
41
+ * @return array
42
+ */
43
+ protected function getOutdatedFiles($ttl)
44
+ {
45
+ $outdated = array();
46
+ $time = time();
47
+ $dir = new RecursiveDirectoryIterator($this->configuration->getUploadDir());
48
+ $dir->setFlags(RecursiveDirectoryIterator::SKIP_DOTS);
49
+
50
+ /*
51
+ * Collecting CSS files
52
+ */
53
+ $files = new RegexIterator(
54
+ new RecursiveIteratorIterator($dir),
55
+ '#.css#U',
56
+ RecursiveRegexIterator::ALL_MATCHES
57
+ );
58
+
59
+ /*
60
+ * Checking expiry
61
+ */
62
+ foreach ($files as $filepath => $match)
63
+ {
64
+ (filemtime($filepath) + $ttl < $time) ? array_push($outdated, $filepath) : null;
65
+ }
66
+
67
+ return $outdated;
68
+ }
69
+
70
+ protected function filterVersions(array &$outdated_files)
71
+ {
72
+ $groups = array();
73
+ $keep = array();
74
+
75
+ // Grouping and collecting data
76
+ foreach ($outdated_files as $index => &$file)
77
+ {
78
+ $m = array();
79
+ preg_match_all('#^(?P<group>.+)-(?P<token>[^\-\.]+).css$#sU', $file, $m, PREG_SET_ORDER);
80
+
81
+ if (empty($m))
82
+ {
83
+ continue;
84
+ }
85
+
86
+ if (!isset($groups[ $m['group'] ]))
87
+ {
88
+ $groups[ $m['group'] ] = array();
89
+ }
90
+
91
+ $groups[ $m['group'] ][ $file ] = filemtime($file);
92
+ }
93
+
94
+ // Capping groups
95
+ foreach ($groups as &$versions)
96
+ {
97
+ arsort($versions);
98
+ $chunks = array_chunk($versions, self::$COMPILED_VERSIONS, true);
99
+
100
+ if (!empty($chunks[0]))
101
+ {
102
+ $keep = array_merge($keep, $chunks[0]);
103
+ }
104
+ }
105
+
106
+ // Returning the diff
107
+ $outdated_files = array_diff($outdated_files, array_flip($keep));
108
+ }
109
+
110
+ /**
111
+ * Remove a bunch of files
112
+ *
113
+ * @protected
114
+ * @param array $files
115
+ * @return array
116
+ */
117
+ protected function deleteFiles(array $files)
118
+ {
119
+ return array_map('unlink', $files);
120
+ }
121
+ }
lib/Loader.class.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if (!class_exists('WPLessPlugin')) {
3
+ require dirname(__FILE__) . '/Plugin.class.php';
4
+ }
5
+
6
+ /**
7
+ * Wrapper for plugin loading
8
+ *
9
+ * @author oncletom
10
+ * @author mrgrain
11
+ * @package wp-less
12
+ * @subpackage lib
13
+ */
14
+ class WPLessPluginLoader
15
+ {
16
+ /**
17
+ * @param callable|null $bootstrap
18
+ * @return mixed
19
+ */
20
+ public static function load($bootstrap = null)
21
+ {
22
+ // Create Plugin Instance
23
+ $WPLessPlugin = WPPluginToolkitPlugin::create('WPLess', dirname(__FILE__), 'WPLessPlugin');
24
+
25
+ // Run bootstrap closure and return plugin
26
+ if (is_callable($bootstrap)) {
27
+ $bootstrap($WPLessPlugin);
28
+ }
29
+
30
+ return $WPLessPlugin;
31
+ }
32
+
33
+ /**
34
+ * @param callable|null $bootstrap
35
+ * @return mixed|object
36
+ */
37
+ public static function getInstance($bootstrap = null)
38
+ {
39
+ // Try to get existing instance
40
+ $WPLessPlugin = WPLessPlugin::getInstance();
41
+
42
+ // Create a new instance if needed
43
+ if (is_null($WPLessPlugin)) {
44
+ $WPLessPlugin = self::load($bootstrap);
45
+ }
46
+
47
+ return $WPLessPlugin;
48
+ }
49
+ }
lib/Plugin.class.php CHANGED
@@ -1,7 +1,6 @@
1
  <?php
2
- if (!class_exists('WPPluginToolkitPlugin'))
3
- {
4
- require dirname(__FILE__).'/vendor/plugin-toolkit/BasePlugin.class.php';
5
  }
6
 
7
  /**
@@ -13,227 +12,443 @@ if (!class_exists('WPPluginToolkitPlugin'))
13
  */
14
  class WPLessPlugin extends WPPluginToolkitPlugin
15
  {
16
- protected $is_filters_registered = false;
17
- protected $is_hooks_registered = false;
18
-
19
- /**
20
- * @static
21
- * @var Pattern used to match stylesheet files to process them as pure CSS
22
- */
23
- public static $match_pattern = '/\.less$/U';
24
-
25
- /**
26
- * Dispatches all events of the plugin
27
- *
28
- * @author oncletom
29
- * @since 1.3
30
- */
31
- public function dispatch()
32
- {
33
- $this->registerHooks();
34
- }
35
-
36
- /**
37
- * Correct Stylesheet URI
38
- *
39
- * It enables the cache without loosing reference to URI
40
- *
41
- * @author oncletom
42
- * @since 1.2
43
- * @version 1.1
44
- * @param string $css parsed CSS
45
- * @param WPLessStylesheet Stylesheet currently processed
46
- * @return string parsed and fixed CSS
47
- */
48
- public function filterStylesheetUri($css, WPLessStylesheet $stylesheet)
49
- {
50
- $token = '@'.uniqid('wpless', true).'@';
51
- $css = preg_replace('#url\s*\(([\'"]{0,1})([^\'"\)]+)\1\)#siU', 'url(\1'.$token.'\2\1)', $css);
52
-
53
- /*
54
- * Token replacement:
55
- * - preserve data URI
56
- * - prefix file URI with absolute path to the theme
57
- */
58
- $css = str_replace(
59
- array($token.'data:', $token),
60
- array('data:', dirname($stylesheet->getSourceUri()).'/'),
61
- $css);
62
-
63
- return $css;
64
- }
65
-
66
- /**
67
- * Find any style to process
68
- *
69
- * @author oncletom
70
- * @since 1.0
71
- * @version 1.0
72
- * @return array styles to process
73
- */
74
- protected function getQueuedStylesToProcess()
75
- {
76
- $wp_styles = $this->getStyles();
77
- $to_process = array();
78
-
79
- foreach ((array)$wp_styles->queue as $style_id)
80
- {
81
- if (preg_match(self::$match_pattern, $wp_styles->registered[$style_id]->src))
82
- {
83
- $to_process[] = $style_id;
84
- }
85
- }
86
-
87
- return apply_filters('wp-less_get_queued_styles_to_process', $to_process);
88
- }
89
-
90
- /**
91
- * Returns WordPress Styles manager
92
- *
93
- * @author oncletom
94
- * @uses WP_Styles
95
- * @since 1.0
96
- * @version 1.0
97
- * @return WP_Styles styles instance
98
- */
99
- public function getStyles()
100
- {
101
- global $wp_styles;
102
- return $wp_styles;
103
- }
104
-
105
- /**
106
- * Process a single stylesheet
107
- *
108
- * @author oncletom
109
- * @since 1.1
110
- * @version 1.3
111
- * @param string $handle
112
- * @param $force boolean If set to true, rebuild all stylesheets, without considering they are updated or not
113
- * @return WPLessStylesheet
114
- */
115
- public function processStylesheet($handle, $force = false)
116
- {
117
- $wp_styles = $this->getStyles();
118
- $stylesheet = new WPLessStylesheet($wp_styles->registered[$handle], $this->getConfiguration()->getVariables());
119
-
120
- if ((is_bool($force) && $force) || $stylesheet->hasToCompile())
121
- {
122
- $compiler = new WPLessCompiler($stylesheet->getSourcePath());
123
- $compiler->registerFunctions($this->getConfiguration()->getFunctions());
124
- $compiler->saveStylesheet($stylesheet);
125
- }
126
-
127
- $wp_styles->registered[$handle]->src = $stylesheet->getTargetUri();
128
-
129
- return $stylesheet;
130
- }
131
-
132
- /**
133
- * Process all stylesheets to compile just in time
134
- *
135
- * @author oncletom
136
- * @since 1.0
137
- * @version 1.1
138
- * @param $force boolean If set to true, rebuild all stylesheets, without considering they are updated or not
139
- */
140
- public function processStylesheets($force = false)
141
- {
142
- $styles = $this->getQueuedStylesToProcess();
143
- $wp_styles = $this->getStyles();
144
- $force = is_bool($force) && $force ? !!$force : false;
145
-
146
- WPLessStylesheet::$upload_dir = $this->configuration->getUploadDir();
147
- WPLessStylesheet::$upload_uri = $this->configuration->getUploadUrl();
148
-
149
- if (empty($styles))
150
- {
151
- return;
152
- }
153
-
154
- if (!wp_mkdir_p(WPLessStylesheet::$upload_dir))
155
- {
156
- throw new WPLessException(sprintf('The upload dir folder (`%s`) is not writable from %s.', WPLessStylesheet::$upload_dir, get_class($this)));
157
- }
158
-
159
- foreach ($styles as $style_id)
160
- {
161
- $this->processStylesheet($style_id, $force);
162
- }
163
-
164
- do_action('wp-less_plugin_process_stylesheets', $styles);
165
- }
166
-
167
- /**
168
- * Method to register hooks (and do it only once)
169
- *
170
- * @protected
171
- * @author oncletom
172
- * @since 1.1
173
- * @version 1.1
174
- */
175
- protected function registerHooks()
176
- {
177
- if ($this->is_hooks_registered)
178
- {
179
- return false;
180
- }
181
-
182
- if (!is_admin())
183
- {
184
- do_action('wp-less_init', $this);
185
- add_action('wp', array($this, 'processStylesheets'), 999, 0);
186
- add_filter('wp-less_stylesheet_save', array($this, 'filterStylesheetUri'), 10, 2);
187
- }
188
- else
189
- {
190
- do_action('wp-less_init_admin', $this);
191
- }
192
-
193
- return $this->is_hooks_registered = true;
194
- }
195
-
196
- /**
197
- * Proxy method
198
- *
199
- * @see WPLessConfiguration::setVariables()
200
- * @since 1.4
201
- */
202
- public function addVariable($name, $value)
203
- {
204
- $this->getConfiguration()->addVariable($name, $value);
205
- }
206
-
207
- /**
208
- * Proxy method
209
- *
210
- * @see WPLessConfiguration::setVariables()
211
- * @since 1.4
212
- */
213
- public function setVariables(array $variables)
214
- {
215
- $this->getConfiguration()->setVariables($variables);
216
- }
217
-
218
- /**
219
- * Proxy method
220
- *
221
- * @see WPLessConfiguration::registerFunction()
222
- * @since 1.4.2
223
- */
224
- public function registerFunction($name, $callback, $scope = array())
225
- {
226
- $this->getConfiguration()->registerFunction($name, $callback, $scope);
227
- }
228
-
229
- /**
230
- * Proxy method
231
- *
232
- * @see WPLessConfiguration::unregisterFunction()
233
- * @since 1.4.2
234
- */
235
- public function unregisterFunction($name)
236
- {
237
- $this->getConfiguration()->unregisterFunction($name);
238
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
239
  }
1
  <?php
2
+ if (!class_exists('WPPluginToolkitPlugin')) {
3
+ require dirname(__FILE__) . '/vendor/plugin-toolkit/BasePlugin.class.php';
 
4
  }
5
 
6
  /**
12
  */
13
  class WPLessPlugin extends WPPluginToolkitPlugin
14
  {
15
+ /**
16
+ * @protected
17
+ * @var bool
18
+ */
19
+ protected $is_filters_registered = false;
20
+
21
+ /**
22
+ * @protected
23
+ * @var bool
24
+ */
25
+ protected $is_hooks_registered = false;
26
+
27
+ /**
28
+ * @protected
29
+ * @var null|WPLessCompiler
30
+ */
31
+ protected $compiler = null;
32
+
33
+ /**
34
+ * @static
35
+ * @var Pattern used to match stylesheet files to process them as pure CSS
36
+ */
37
+ public static $match_pattern = '/\.less$/U';
38
+
39
+ public function __construct(WPLessConfiguration $configuration)
40
+ {
41
+ parent::__construct($configuration);
42
+ WPLessStylesheet::$upload_dir = $this->configuration->getUploadDir();
43
+ WPLessStylesheet::$upload_uri = $this->configuration->getUploadUrl();
44
+ }
45
+
46
+ public function instantiateCompiler()
47
+ {
48
+ if (!class_exists('lessc')) {
49
+ // Load the parent compiler class
50
+ require $this->getLessCompilerPath();
51
+ }
52
+
53
+ $this->compiler = new WPLessCompiler;
54
+ $this->compiler->setVariable('stylesheet_directory_uri', "'" . get_stylesheet_directory_uri() . "'");
55
+ $this->compiler->setVariable('template_directory_uri', "'" . get_template_directory_uri() . "'");
56
+ if(!defined('WP_DEBUG') || !WP_DEBUG) {
57
+ $this->compiler->setFormatter('compressed');
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Load the parent compiler class. This is provided via lessc.inc.php for
63
+ * both the lessphp and less.php implementations
64
+ *
65
+ * @author fabrizim
66
+ * @since 1.7.1
67
+ *
68
+ */
69
+ protected function getLessCompilerPath()
70
+ {
71
+ // The usage of the WP_LESS_COMPILER is a holdover from an older implentation
72
+ // of this opt-in functionality
73
+ $compiler = defined('WP_LESS_COMPILER') ? WP_LESS_COMPILER : apply_filters('wp_less_compiler', 'wikimedia/less.php');
74
+
75
+ switch( $compiler ){
76
+ case 'less.php':
77
+ return dirname(__FILE__).'/../vendor/oyejorge/less.php/lessc.inc.php';
78
+ case 'lessphp':
79
+ return dirname(__FILE__).'/../vendor/leafo/lessphp/lessc.inc.php';
80
+ case 'wikimedia/less.php':
81
+ return dirname(__FILE__).'/../vendor/wikimedia/less.php/lessc.inc.php';
82
+ default:
83
+ return $compiler;
84
+ }
85
+ }
86
+
87
+ public function getCompiler()
88
+ {
89
+ if( $this->compiler ) return $this->compiler;
90
+ $this->instantiateCompiler();
91
+ return $this->compiler;
92
+ }
93
+
94
+ /**
95
+ * Dispatches all events of the plugin
96
+ *
97
+ * @author oncletom
98
+ * @since 1.3
99
+ */
100
+ public function dispatch()
101
+ {
102
+ if ($this->is_hooks_registered) {
103
+ return false;
104
+ }
105
+
106
+ /*
107
+ * Garbage Collection Registration
108
+ */
109
+ $gc = new WPLessGarbagecollector($this->configuration);
110
+ add_action('wp-less-garbage-collection', array($gc, 'clean'));
111
+
112
+ /*
113
+ * Last Hooks
114
+ */
115
+ $this->registerHooks();
116
+ }
117
+
118
+ /**
119
+ * Performs plugin install actions
120
+ *
121
+ * @since 1.5
122
+ */
123
+ public function install()
124
+ {
125
+ /*
126
+ * Check to see if it isn't scheduled first, for example
127
+ * this would occur when loaded via theme
128
+ */
129
+ if ( FALSE === wp_get_schedule( 'wp-less-garbage-collection' ) )
130
+ {
131
+ wp_schedule_event(time(), 'daily', 'wp-less-garbage-collection');
132
+ }
133
+
134
+ /*
135
+ * Clear old hooks, prior to hook change
136
+ * #57
137
+ */
138
+ wp_clear_scheduled_hook( 'wp-less_garbage_collection' );
139
+ }
140
+
141
+ /**
142
+ * Performs plugin uninstall actions
143
+ *
144
+ * @since 1.5
145
+ */
146
+ public function uninstall()
147
+ {
148
+ wp_clear_scheduled_hook('wp-less-garbage-collection');
149
+ }
150
+
151
+ /**
152
+ * Correct Stylesheet URI
153
+ *
154
+ * It enables the cache without loosing reference to URI
155
+ *
156
+ * @author oncletom
157
+ * @since 1.2
158
+ * @version 1.2
159
+ * @param string $css parsed CSS
160
+ * @param WPLessStylesheet Stylesheet currently processed
161
+ * @return string parsed and fixed CSS
162
+ */
163
+ public function filterStylesheetUri($css, WPLessStylesheet $stylesheet)
164
+ {
165
+ $this->_TmpBaseDir = dirname($stylesheet->getSourceUri());
166
+
167
+ return preg_replace_callback(
168
+ '#url\s*\((?P<quote>[\'"]{0,1})(?P<url>[^\'"\)]+)\1\)#siU',
169
+ array($this, '_filterStylesheetUri'),
170
+ $css
171
+ );
172
+
173
+ unset($this->_TmpBaseDir);
174
+ }
175
+
176
+ /**
177
+ * Returns a proper url() CSS key with absolute paths if needed
178
+ *
179
+ * @protected
180
+ * @param array $matches Expects at least 0, 'uri' and 'quote' keys
181
+ * @return string
182
+ */
183
+ protected function _filterStylesheetUri($matches)
184
+ {
185
+ if (preg_match('#^(http|@|data:|/)#Ui', $matches[2])) {
186
+ return $matches[0];
187
+ }
188
+
189
+ return sprintf('url(%s%s%1$s)',
190
+ $matches[1],
191
+ $this->_TmpBaseDir . '/' . $matches[2]
192
+ );
193
+ }
194
+
195
+ /**
196
+ * Find any style to process
197
+ *
198
+ * @author oncletom
199
+ * @since 1.0
200
+ * @version 1.0
201
+ * @return array styles to process
202
+ */
203
+ protected function getQueuedStylesToProcess()
204
+ {
205
+ $wp_styles = $this->getStyles();
206
+ $to_process = array();
207
+
208
+ foreach ((array)$wp_styles->queue as $style_id) {
209
+ if (preg_match(self::$match_pattern, $wp_styles->registered[$style_id]->src)) {
210
+ $to_process[] = $style_id;
211
+ }
212
+ }
213
+
214
+ return apply_filters('wp-less_get_queued_styles_to_process', $to_process);
215
+ }
216
+
217
+ /**
218
+ * Returns WordPress Styles manager
219
+ *
220
+ * @author oncletom
221
+ * @uses WP_Styles
222
+ * @since 1.0
223
+ * @version 1.1
224
+ * @return WP_Styles styles instance
225
+ */
226
+ public function getStyles()
227
+ {
228
+ global $wp_styles;
229
+
230
+ //because if someone never registers through `wp_(enqueue|register)_stylesheet`,
231
+ //$wp_styles is never initialized, and thus, equals NULL
232
+ return null === $wp_styles || !$wp_styles instanceof WP_Styles ? new WP_Styles() : $wp_styles;
233
+ }
234
+
235
+ /**
236
+ * Process a single stylesheet
237
+ *
238
+ * @author oncletom
239
+ * @since 1.1
240
+ * @version 1.3
241
+ * @param string $handle
242
+ * @param $force boolean If set to true, rebuild all stylesheets, without considering they are updated or not
243
+ * @return WPLessStylesheet
244
+ */
245
+ public function processStylesheet($handle, $force = false)
246
+ {
247
+ $force = !!$force ? $force : $this->configuration->alwaysRecompile();
248
+
249
+ $wp_styles = $this->getStyles();
250
+ $stylesheet = new WPLessStylesheet($wp_styles->registered[$handle], $this->getCompiler()->getVariables());
251
+
252
+ if ($this->configuration->getCompilationStrategy() === 'legacy' && $stylesheet->hasToCompile()) {
253
+ $this->getCompiler()->saveStylesheet($stylesheet);
254
+ } elseif ($this->configuration->getCompilationStrategy() !== 'legacy') {
255
+ $this->getCompiler()->cacheStylesheet($stylesheet, $force);
256
+ }
257
+
258
+ $wp_styles->registered[$handle]->src = $stylesheet->getTargetUri();
259
+
260
+ return $stylesheet;
261
+ }
262
+
263
+ /**
264
+ * Process all stylesheets to compile just in time
265
+ *
266
+ * @author oncletom
267
+ * @since 1.0
268
+ * @version 1.1
269
+ * @param $force boolean If set to true, rebuild all stylesheets, without considering they are updated or not
270
+ */
271
+ public function processStylesheets($force = false)
272
+ {
273
+ $styles = $this->getQueuedStylesToProcess();
274
+ $force = is_bool($force) && $force ? !!$force : false;
275
+
276
+ if (empty($styles)) {
277
+ return;
278
+ }
279
+
280
+ if (!wp_mkdir_p(WPLessStylesheet::$upload_dir)) {
281
+ throw new WPLessException(sprintf('The upload dir folder (`%s`) is not writable from %s.', WPLessStylesheet::$upload_dir, get_class($this)));
282
+ }
283
+
284
+ foreach ($styles as $style_id) {
285
+ $this->processStylesheet($style_id, $force);
286
+ }
287
+
288
+ do_action('wp-less_plugin_process_stylesheets', $styles);
289
+ }
290
+
291
+ /**
292
+ * Compile editor stylesheets registered via add_editor_style()
293
+ *
294
+ * @param string $mce_css Comma separated list of CSS file URLs
295
+ * @return string $mce_css New comma separated list of CSS file URLs
296
+ */
297
+ public function processEditorStylesheets($mce_css) {
298
+
299
+ if( !$mce_css ) return $mce_css;
300
+
301
+ // extract CSS file URLs
302
+ $style_sheets = explode( ",", $mce_css );
303
+
304
+ if ( count( $style_sheets ) ) {
305
+ $compiled_css = array();
306
+
307
+ // loop through editor styles, any .less files will be compiled and the compiled URL returned
308
+ foreach( $style_sheets as $style_sheet ) {
309
+
310
+ // Remove version from uri
311
+ $parts = parse_url( $style_sheet );
312
+ $style_sheet = $parts['scheme'] . '://' . $parts['host'] . (!$parts['port'] ? '' : (':' . $parts['port'])) . $parts['path'];
313
+
314
+ // Get extension and set handle for wp_register_style()
315
+ $pathinfo = pathinfo($style_sheet);
316
+ $extension = $pathinfo['extension'];
317
+ $handle = $pathinfo['filename'];
318
+
319
+ // Only process less files
320
+ if( $extension === 'less' ) {
321
+
322
+ // Register stylesheet as wp dependency
323
+ wp_register_style( $handle, $style_sheet, array(), null );
324
+
325
+ // Process stylesheet
326
+ $stylesheet = $this->processStylesheet($handle, false);
327
+
328
+ // Add if successfull
329
+ if($stylesheet) {
330
+ $compiled_css[] = $stylesheet->getTargetUri();
331
+ }
332
+
333
+ }
334
+
335
+ else {
336
+ $compiled_css[] = $style_sheet;
337
+ }
338
+ }
339
+
340
+ $mce_css = implode( ",", $compiled_css );
341
+ }
342
+
343
+ // return new URLs
344
+ return $mce_css;
345
+ }
346
+
347
+ /**
348
+ * Method to register hooks (and do it only once)
349
+ *
350
+ * @protected
351
+ * @author oncletom
352
+ * @since 1.1
353
+ * @version 1.1
354
+ */
355
+ protected function registerHooks()
356
+ {
357
+ if ($this->is_hooks_registered) {
358
+ return false;
359
+ }
360
+
361
+ is_admin() ? do_action('wp-less_init_admin', $this) : do_action('wp-less_init', $this);
362
+ add_action('wp_enqueue_scripts', array($this, 'processStylesheets'), PHP_INT_MAX, 0);
363
+ add_action('admin_enqueue_scripts', array($this, 'processStylesheets'), PHP_INT_MAX, 0);
364
+ add_action('login_enqueue_scripts', array($this, 'processStylesheets'), PHP_INT_MAX, 0);
365
+ add_filter('mce_css', array($this, 'processEditorStylesheets'), PHP_INT_MAX);
366
+ add_filter('wp-less_stylesheet_save', array($this, 'filterStylesheetUri'), 10, 2);
367
+ add_filter('wp_theme_editor_filetypes', function($types) {
368
+ $types[] = 'less';
369
+ return $types;
370
+ });
371
+
372
+ return $this->is_hooks_registered = true;
373
+ }
374
+
375
+ /**
376
+ * Proxy method
377
+ *
378
+ * @see http://leafo.net/lessphp/docs/#setting_variables_from_php
379
+ * @since 1.4
380
+ */
381
+ public function addVariable($name, $value)
382
+ {
383
+ $this->getCompiler()->setVariables(array($name => $value));
384
+ }
385
+
386
+ /**
387
+ * Proxy method
388
+ *
389
+ * @see http://leafo.net/lessphp/docs/#setting_variables_from_php
390
+ * @since 1.4
391
+ */
392
+ public function setVariables(array $variables)
393
+ {
394
+ $this->getCompiler()->setVariables($variables);
395
+ }
396
+
397
+ /**
398
+ * Proxy method
399
+ *
400
+ * @see http://leafo.net/lessphp/docs/#custom_functions
401
+ * @since 1.4.2
402
+ */
403
+ public function registerFunction($name, $callback)
404
+ {
405
+ $this->getCompiler()->registerFunction($name, $callback);
406
+ }
407
+
408
+ /**
409
+ * Proxy method
410
+ *
411
+ * @see lessc::unregisterFunction()
412
+ * @since 1.4.2
413
+ */
414
+ public function unregisterFunction($name)
415
+ {
416
+ $this->getCompiler()->unregisterFunction($name);
417
+ }
418
+
419
+ /**
420
+ * Proxy method
421
+ *
422
+ * @see WPLessCompiler::getImportDir()
423
+ * @return array
424
+ * @since 1.5.0
425
+ */
426
+ public function getImportDir()
427
+ {
428
+ return $this->getCompiler()->getImportDir();
429
+ }
430
+
431
+ /**
432
+ * Proxy method
433
+ *
434
+ * @see lessc::addImportDir()
435
+ * @param string $dir
436
+ * @since 1.5.0
437
+ */
438
+ public function addImportDir($dir)
439
+ {
440
+ $this->getCompiler()->addImportDir($dir);
441
+ }
442
+
443
+ /**
444
+ * Proxy method
445
+ *
446
+ * @see lessc::setImportDir()
447
+ * @param array $dirs
448
+ * @since 1.5.0
449
+ */
450
+ public function setImportDir($dirs)
451
+ {
452
+ $this->getCompiler()->setImportDir($dirs);
453
+ }
454
  }
lib/Stylesheet.class.php CHANGED
@@ -1,6 +1,4 @@
1
  <?php
2
- require dirname(__FILE__).'/vendor/lessphp/lessc.inc.php';
3
-
4
  /**
5
  * Stylesheet management
6
  *
@@ -11,8 +9,7 @@ require dirname(__FILE__).'/vendor/lessphp/lessc.inc.php';
11
  class WPLessStylesheet
12
  {
13
  protected $compiler,
14
- $stylesheet,
15
- $variables = array();
16
 
17
  protected $is_new = true,
18
  $signature,
@@ -38,7 +35,6 @@ class WPLessStylesheet
38
  public function __construct(_WP_Dependency $stylesheet, array $variables = array())
39
  {
40
  $this->stylesheet = $stylesheet;
41
- $this->variables = $variables;
42
 
43
  if (!self::$upload_dir || !self::$upload_uri)
44
  {
@@ -47,7 +43,7 @@ class WPLessStylesheet
47
 
48
  $this->stylesheet->ver = null;
49
  $this->configurePath();
50
- $this->configureSignature();
51
 
52
  if (file_exists($this->getTargetPath()))
53
  {
@@ -80,35 +76,38 @@ class WPLessStylesheet
80
  * Since this moment, everything is configured to be usable
81
  *
82
  * @protected
83
- * @author oncletom
84
  * @since 1.0
85
- * @version 1.1
86
  */
87
  protected function configurePath()
88
  {
89
  $target_file = $this->computeTargetPath();
90
-
91
- $this->source_path = WP_CONTENT_DIR.preg_replace('#^'.WP_CONTENT_URL.'#U', '', $this->stylesheet->src);
 
 
92
  $this->source_uri = $this->stylesheet->src;
93
  $this->target_path = self::$upload_dir.$target_file;
94
  $this->target_uri = self::$upload_uri.$target_file;
95
 
96
- $this->source_timestamp = filemtime($this->source_path);
97
  }
98
 
99
  /**
100
  * Configures the file signature
101
- *
102
- * It corresponds to a unique hash taking care of file timestamp and variables.
103
  * It should be called each time stylesheet variables are updated.
104
- *
105
  * @author oncletom
 
106
  * @since 1.4.2
107
- * @version 1.0
108
  */
109
- protected function configureSignature()
110
  {
111
- $this->signature = substr(sha1(serialize($this->variables) . $this->source_timestamp), 0, 10);
112
  }
113
 
114
  /**
@@ -178,29 +177,17 @@ class WPLessStylesheet
178
  return sprintf($this->target_uri, $this->signature);
179
  }
180
 
181
- /**
182
- * Returns stylesheet variables
183
- *
184
- * @author oncletom
185
- * @since 1.4.2
186
- * @return array
187
- */
188
- public function getVariables()
189
- {
190
- return $this->variables;
191
- }
192
-
193
  /**
194
  * Tells if compilation is needed
195
  *
196
  * @author oncletom
197
  * @since 1.0
198
- * @version 1.2
199
  * @return boolean
200
  */
201
  public function hasToCompile()
202
  {
203
- return ($this->is_new || (defined('WP_DEBUG') && WP_DEBUG));
204
  }
205
 
206
  /**
@@ -213,4 +200,16 @@ class WPLessStylesheet
213
  {
214
  $this->is_new = false;
215
  }
 
 
 
 
 
 
 
 
 
 
 
 
216
  }
1
  <?php
 
 
2
  /**
3
  * Stylesheet management
4
  *
9
  class WPLessStylesheet
10
  {
11
  protected $compiler,
12
+ $stylesheet;
 
13
 
14
  protected $is_new = true,
15
  $signature,
35
  public function __construct(_WP_Dependency $stylesheet, array $variables = array())
36
  {
37
  $this->stylesheet = $stylesheet;
 
38
 
39
  if (!self::$upload_dir || !self::$upload_uri)
40
  {
43
 
44
  $this->stylesheet->ver = null;
45
  $this->configurePath();
46
+ $this->configureSignature($variables);
47
 
48
  if (file_exists($this->getTargetPath()))
49
  {
76
  * Since this moment, everything is configured to be usable
77
  *
78
  * @protected
79
+ * @author oncletom, schnoggo
80
  * @since 1.0
81
+ * @version 1.2
82
  */
83
  protected function configurePath()
84
  {
85
  $target_file = $this->computeTargetPath();
86
+ // path to local "wp-content" dir does not match URI in multisite.
87
+ $wp_content_dir = str_replace(ABSPATH, '', WP_CONTENT_DIR); // get the 'wp-content' part of the path
88
+ $lessfile_in_theme = preg_replace ('#^.*?' . DIRECTORY_SEPARATOR . $wp_content_dir . DIRECTORY_SEPARATOR . '(.*)$#', '$1', $this->stylesheet->src, 1); // the part after 'wp-content'
89
+ $this->source_path = WP_CONTENT_DIR . DIRECTORY_SEPARATOR . $lessfile_in_theme;
90
  $this->source_uri = $this->stylesheet->src;
91
  $this->target_path = self::$upload_dir.$target_file;
92
  $this->target_uri = self::$upload_uri.$target_file;
93
 
94
+ $this->setSourceTimestamp(filemtime($this->source_path));
95
  }
96
 
97
  /**
98
  * Configures the file signature
99
+ *
100
+ * It corresponds to a unique hash taking care of file name, timestamp and variables.
101
  * It should be called each time stylesheet variables are updated.
102
+ *
103
  * @author oncletom
104
+ * @param array $variables List of variables used for signature
105
  * @since 1.4.2
106
+ * @version 1.1
107
  */
108
+ protected function configureSignature(array $variables = array())
109
  {
110
+ $this->signature = substr(sha1(serialize($variables) . $this->computeTargetPath() . $this->source_timestamp), 0, 10);
111
  }
112
 
113
  /**
177
  return sprintf($this->target_uri, $this->signature);
178
  }
179
 
 
 
 
 
 
 
 
 
 
 
 
 
180
  /**
181
  * Tells if compilation is needed
182
  *
183
  * @author oncletom
184
  * @since 1.0
185
+ * @version 1.3
186
  * @return boolean
187
  */
188
  public function hasToCompile()
189
  {
190
+ return $this->is_new;
191
  }
192
 
193
  /**
200
  {
201
  $this->is_new = false;
202
  }
203
+
204
+ /**
205
+ * Sets the source timestamp of the file
206
+ * Mostly used to generate a proper cache busting URI
207
+ *
208
+ * @since 1.5.2
209
+ * @param integer $timestamp
210
+ */
211
+ public function setSourceTimestamp($timestamp)
212
+ {
213
+ $this->source_timestamp = $timestamp;
214
+ }
215
  }
lib/vendor/lessphp/.gitignore DELETED
@@ -1,4 +0,0 @@
1
- *.swp
2
- *~
3
- /*.less
4
- /*.css
 
 
 
 
lib/vendor/lessphp/LICENSE DELETED
@@ -1,660 +0,0 @@
1
- For ease of distribution, lessphp 0.2.0 is under a dual license.
2
- You are free to pick which one suits your needs.
3
-
4
-
5
-
6
-
7
- MIT LICENSE
8
-
9
-
10
-
11
-
12
- Copyright (c) 2010 Leaf Corcoran, http://leafo.net/lessphp
13
-
14
- Permission is hereby granted, free of charge, to any person obtaining
15
- a copy of this software and associated documentation files (the
16
- "Software"), to deal in the Software without restriction, including
17
- without limitation the rights to use, copy, modify, merge, publish,
18
- distribute, sublicense, and/or sell copies of the Software, and to
19
- permit persons to whom the Software is furnished to do so, subject to
20
- the following conditions:
21
-
22
- The above copyright notice and this permission notice shall be
23
- included in all copies or substantial portions of the Software.
24
-
25
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32
-
33
-
34
-
35
-
36
- GPL VERSION 3
37
-
38
-
39
-
40
-
41
- GNU GENERAL PUBLIC LICENSE
42
- Version 3, 29 June 2007
43
-
44
- Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
45
- Everyone is permitted to copy and distribute verbatim copies
46
- of this license document, but changing it is not allowed.
47
-
48
- Preamble
49
-
50
- The GNU General Public License is a free, copyleft license for
51
- software and other kinds of works.
52
-
53
- The licenses for most software and other practical works are designed
54
- to take away your freedom to share and change the works. By contrast,
55
- the GNU General Public License is intended to guarantee your freedom to
56
- share and change all versions of a program--to make sure it remains free
57
- software for all its users. We, the Free Software Foundation, use the
58
- GNU General Public License for most of our software; it applies also to
59
- any other work released this way by its authors. You can apply it to
60
- your programs, too.
61
-
62
- When we speak of free software, we are referring to freedom, not
63
- price. Our General Public Licenses are designed to make sure that you
64
- have the freedom to distribute copies of free software (and charge for
65
- them if you wish), that you receive source code or can get it if you
66
- want it, that you can change the software or use pieces of it in new
67
- free programs, and that you know you can do these things.
68
-
69
- To protect your rights, we need to prevent others from denying you
70
- these rights or asking you to surrender the rights. Therefore, you have
71
- certain responsibilities if you distribute copies of the software, or if
72
- you modify it: responsibilities to respect the freedom of others.
73
-
74
- For example, if you distribute copies of such a program, whether
75
- gratis or for a fee, you must pass on to the recipients the same
76
- freedoms that you received. You must make sure that they, too, receive
77
- or can get the source code. And you must show them these terms so they
78
- know their rights.
79
-
80
- Developers that use the GNU GPL protect your rights with two steps:
81
- (1) assert copyright on the software, and (2) offer you this License
82
- giving you legal permission to copy, distribute and/or modify it.
83
-
84
- For the developers' and authors' protection, the GPL clearly explains
85
- that there is no warranty for this free software. For both users' and
86
- authors' sake, the GPL requires that modified versions be marked as
87
- changed, so that their problems will not be attributed erroneously to
88
- authors of previous versions.
89
-
90
- Some devices are designed to deny users access to install or run
91
- modified versions of the software inside them, although the manufacturer
92
- can do so. This is fundamentally incompatible with the aim of
93
- protecting users' freedom to change the software. The systematic
94
- pattern of such abuse occurs in the area of products for individuals to
95
- use, which is precisely where it is most unacceptable. Therefore, we
96
- have designed this version of the GPL to prohibit the practice for those
97
- products. If such problems arise substantially in other domains, we
98
- stand ready to extend this provision to those domains in future versions
99
- of the GPL, as needed to protect the freedom of users.
100
-
101
- Finally, every program is threatened constantly by software patents.
102
- States should not allow patents to restrict development and use of
103
- software on general-purpose computers, but in those that do, we wish to
104
- avoid the special danger that patents applied to a free program could
105
- make it effectively proprietary. To prevent this, the GPL assures that
106
- patents cannot be used to render the program non-free.
107
-
108
- The precise terms and conditions for copying, distribution and
109
- modification follow.
110
-
111
- TERMS AND CONDITIONS
112
-
113
- 0. Definitions.
114
-
115
- "This License" refers to version 3 of the GNU General Public License.
116
-
117
- "Copyright" also means copyright-like laws that apply to other kinds of
118
- works, such as semiconductor masks.
119
-
120
- "The Program" refers to any copyrightable work licensed under this
121
- License. Each licensee is addressed as "you". "Licensees" and
122
- "recipients" may be individuals or organizations.
123
-
124
- To "modify" a work means to copy from or adapt all or part of the work
125
- in a fashion requiring copyright permission, other than the making of an
126
- exact copy. The resulting work is called a "modified version" of the
127
- earlier work or a work "based on" the earlier work.
128
-
129
- A "covered work" means either the unmodified Program or a work based
130
- on the Program.
131
-
132
- To "propagate" a work means to do anything with it that, without
133
- permission, would make you directly or secondarily liable for
134
- infringement under applicable copyright law, except executing it on a
135
- computer or modifying a private copy. Propagation includes copying,
136
- distribution (with or without modification), making available to the
137
- public, and in some countries other activities as well.
138
-
139
- To "convey" a work means any kind of propagation that enables other
140
- parties to make or receive copies. Mere interaction with a user through
141
- a computer network, with no transfer of a copy, is not conveying.
142
-
143
- An interactive user interface displays "Appropriate Legal Notices"
144
- to the extent that it includes a convenient and prominently visible
145
- feature that (1) displays an appropriate copyright notice, and (2)
146
- tells the user that there is no warranty for the work (except to the
147
- extent that warranties are provided), that licensees may convey the
148
- work under this License, and how to view a copy of this License. If
149
- the interface presents a list of user commands or options, such as a
150
- menu, a prominent item in the list meets this criterion.
151
-
152
- 1. Source Code.
153
-
154
- The "source code" for a work means the preferred form of the work
155
- for making modifications to it. "Object code" means any non-source
156
- form of a work.
157
-
158
- A "Standard Interface" means an interface that either is an official
159
- standard defined by a recognized standards body, or, in the case of
160
- interfaces specified for a particular programming language, one that
161
- is widely used among developers working in that language.
162
-
163
- The "System Libraries" of an executable work include anything, other
164
- than the work as a whole, that (a) is included in the normal form of
165
- packaging a Major Component, but which is not part of that Major
166
- Component, and (b) serves only to enable use of the work with that
167
- Major Component, or to implement a Standard Interface for which an
168
- implementation is available to the public in source code form. A
169
- "Major Component", in this context, means a major essential component
170
- (kernel, window system, and so on) of the specific operating system
171
- (if any) on which the executable work runs, or a compiler used to
172
- produce the work, or an object code interpreter used to run it.
173
-
174
- The "Corresponding Source" for a work in object code form means all
175
- the source code needed to generate, install, and (for an executable
176
- work) run the object code and to modify the work, including scripts to
177
- control those activities. However, it does not include the work's
178
- System Libraries, or general-purpose tools or generally available free
179
- programs which are used unmodified in performing those activities but
180
- which are not part of the work. For example, Corresponding Source
181
- includes interface definition files associated with source files for
182
- the work, and the source code for shared libraries and dynamically
183
- linked subprograms that the work is specifically designed to require,
184
- such as by intimate data communication or control flow between those
185
- subprograms and other parts of the work.
186
-
187
- The Corresponding Source need not include anything that users
188
- can regenerate automatically from other parts of the Corresponding
189
- Source.
190
-
191
- The Corresponding Source for a work in source code form is that
192
- same work.
193
-
194
- 2. Basic Permissions.
195
-
196
- All rights granted under this License are granted for the term of
197
- copyright on the Program, and are irrevocable provided the stated
198
- conditions are met. This License explicitly affirms your unlimited
199
- permission to run the unmodified Program. The output from running a
200
- covered work is covered by this License only if the output, given its
201
- content, constitutes a covered work. This License acknowledges your
202
- rights of fair use or other equivalent, as provided by copyright law.
203
-
204
- You may make, run and propagate covered works that you do not
205
- convey, without conditions so long as your license otherwise remains
206
- in force. You may convey covered works to others for the sole purpose
207
- of having them make modifications exclusively for you, or provide you
208
- with facilities for running those works, provided that you comply with
209
- the terms of this License in conveying all material for which you do
210
- not control copyright. Those thus making or running the covered works
211
- for you must do so exclusively on your behalf, under your direction
212
- and control, on terms that prohibit them from making any copies of
213
- your copyrighted material outside their relationship with you.
214
-
215
- Conveying under any other circumstances is permitted solely under
216
- the conditions stated below. Sublicensing is not allowed; section 10
217
- makes it unnecessary.
218
-
219
- 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
220
-
221
- No covered work shall be deemed part of an effective technological
222
- measure under any applicable law fulfilling obligations under article
223
- 11 of the WIPO copyright treaty adopted on 20 December 1996, or
224
- similar laws prohibiting or restricting circumvention of such
225
- measures.
226
-
227
- When you convey a covered work, you waive any legal power to forbid
228
- circumvention of technological measures to the extent such circumvention
229
- is effected by exercising rights under this License with respect to
230
- the covered work, and you disclaim any intention to limit operation or
231
- modification of the work as a means of enforcing, against the work's
232
- users, your or third parties' legal rights to forbid circumvention of
233
- technological measures.
234
-
235
- 4. Conveying Verbatim Copies.
236
-
237
- You may convey verbatim copies of the Program's source code as you
238
- receive it, in any medium, provided that you conspicuously and
239
- appropriately publish on each copy an appropriate copyright notice;
240
- keep intact all notices stating that this License and any
241
- non-permissive terms added in accord with section 7 apply to the code;
242
- keep intact all notices of the absence of any warranty; and give all
243
- recipients a copy of this License along with the Program.
244
-
245
- You may charge any price or no price for each copy that you convey,
246
- and you may offer support or warranty protection for a fee.
247
-
248
- 5. Conveying Modified Source Versions.
249
-
250
- You may convey a work based on the Program, or the modifications to
251
- produce it from the Program, in the form of source code under the
252
- terms of section 4, provided that you also meet all of these conditions:
253
-
254
- a) The work must carry prominent notices stating that you modified
255
- it, and giving a relevant date.
256
-
257
- b) The work must carry prominent notices stating that it is
258
- released under this License and any conditions added under section
259
- 7. This requirement modifies the requirement in section 4 to
260
- "keep intact all notices".
261
-
262
- c) You must license the entire work, as a whole, under this
263
- License to anyone who comes into possession of a copy. This
264
- License will therefore apply, along with any applicable section 7
265
- additional terms, to the whole of the work, and all its parts,
266
- regardless of how they are packaged. This License gives no
267
- permission to license the work in any other way, but it does not
268
- invalidate such permission if you have separately received it.
269
-
270
- d) If the work has interactive user interfaces, each must display
271
- Appropriate Legal Notices; however, if the Program has interactive
272
- interfaces that do not display Appropriate Legal Notices, your
273
- work need not make them do so.
274
-
275
- A compilation of a covered work with other separate and independent
276
- works, which are not by their nature extensions of the covered work,
277
- and which are not combined with it such as to form a larger program,
278
- in or on a volume of a storage or distribution medium, is called an
279
- "aggregate" if the compilation and its resulting copyright are not
280
- used to limit the access or legal rights of the compilation's users
281
- beyond what the individual works permit. Inclusion of a covered work
282
- in an aggregate does not cause this License to apply to the other
283
- parts of the aggregate.
284
-
285
- 6. Conveying Non-Source Forms.
286
-
287
- You may convey a covered work in object code form under the terms
288
- of sections 4 and 5, provided that you also convey the
289
- machine-readable Corresponding Source under the terms of this License,
290
- in one of these ways:
291
-
292
- a) Convey the object code in, or embodied in, a physical product
293
- (including a physical distribution medium), accompanied by the
294
- Corresponding Source fixed on a durable physical medium
295
- customarily used for software interchange.
296
-
297
- b) Convey the object code in, or embodied in, a physical product
298
- (including a physical distribution medium), accompanied by a
299
- written offer, valid for at least three years and valid for as
300
- long as you offer spare parts or customer support for that product
301
- model, to give anyone who possesses the object code either (1) a
302
- copy of the Corresponding Source for all the software in the
303
- product that is covered by this License, on a durable physical
304
- medium customarily used for software interchange, for a price no
305
- more than your reasonable cost of physically performing this
306
- conveying of source, or (2) access to copy the
307
- Corresponding Source from a network server at no charge.
308
-
309
- c) Convey individual copies of the object code with a copy of the
310
- written offer to provide the Corresponding Source. This
311
- alternative is allowed only occasionally and noncommercially, and
312
- only if you received the object code with such an offer, in accord
313
- with subsection 6b.
314
-
315
- d) Convey the object code by offering access from a designated
316
- place (gratis or for a charge), and offer equivalent access to the
317
- Corresponding Source in the same way through the same place at no
318
- further charge. You need not require recipients to copy the
319
- Corresponding Source along with the object code. If the place to
320
- copy the object code is a network server, the Corresponding Source
321
- may be on a different server (operated by you or a third party)
322
- that supports equivalent copying facilities, provided you maintain
323
- clear directions next to the object code saying where to find the
324
- Corresponding Source. Regardless of what server hosts the
325
- Corresponding Source, you remain obligated to ensure that it is
326
- available for as long as needed to satisfy these requirements.
327
-
328
- e) Convey the object code using peer-to-peer transmission, provided
329
- you inform other peers where the object code and Corresponding
330
- Source of the work are being offered to the general public at no
331
- charge under subsection 6d.
332
-
333
- A separable portion of the object code, whose source code is excluded
334
- from the Corresponding Source as a System Library, need not be
335
- included in conveying the object code work.
336
-
337
- A "User Product" is either (1) a "consumer product", which means any
338
- tangible personal property which is normally used for personal, family,
339
- or household purposes, or (2) anything designed or sold for incorporation
340
- into a dwelling. In determining whether a product is a consumer product,
341
- doubtful cases shall be resolved in favor of coverage. For a particular
342
- product received by a particular user, "normally used" refers to a
343
- typical or common use of that class of product, regardless of the status
344
- of the particular user or of the way in which the particular user
345
- actually uses, or expects or is expected to use, the product. A product
346
- is a consumer product regardless of whether the product has substantial
347
- commercial, industrial or non-consumer uses, unless such uses represent
348
- the only significant mode of use of the product.
349
-
350
- "Installation Information" for a User Product means any methods,
351
- procedures, authorization keys, or other information required to install
352
- and execute modified versions of a covered work in that User Product from
353
- a modified version of its Corresponding Source. The information must
354
- suffice to ensure that the continued functioning of the modified object
355
- code is in no case prevented or interfered with solely because
356
- modification has been made.
357
-
358
- If you convey an object code work under this section in, or with, or
359
- specifically for use in, a User Product, and the conveying occurs as
360
- part of a transaction in which the right of possession and use of the
361
- User Product is transferred to the recipient in perpetuity or for a
362
- fixed term (regardless of how the transaction is characterized), the
363
- Corresponding Source conveyed under this section must be accompanied
364
- by the Installation Information. But this requirement does not apply
365
- if neither you nor any third party retains the ability to install
366
- modified object code on the User Product (for example, the work has
367
- been installed in ROM).
368
-
369
- The requirement to provide Installation Information does not include a
370
- requirement to continue to provide support service, warranty, or updates
371
- for a work that has been modified or installed by the recipient, or for
372
- the User Product in which it has been modified or installed. Access to a
373
- network may be denied when the modification itself materially and
374
- adversely affects the operation of the network or violates the rules and
375
- protocols for communication across the network.
376
-
377
- Corresponding Source conveyed, and Installation Information provided,
378
- in accord with this section must be in a format that is publicly
379
- documented (and with an implementation available to the public in
380
- source code form), and must require no special password or key for
381
- unpacking, reading or copying.
382
-
383
- 7. Additional Terms.
384
-
385
- "Additional permissions" are terms that supplement the terms of this
386
- License by making exceptions from one or more of its conditions.
387
- Additional permissions that are applicable to the entire Program shall
388
- be treated as though they were included in this License, to the extent
389
- that they are valid under applicable law. If additional permissions
390
- apply only to part of the Program, that part may be used separately
391
- under those permissions, but the entire Program remains governed by
392
- this License without regard to the additional permissions.
393
-
394
- When you convey a copy of a covered work, you may at your option
395
- remove any additional permissions from that copy, or from any part of
396
- it. (Additional permissions may be written to require their own
397
- removal in certain cases when you modify the work.) You may place
398
- additional permissions on material, added by you to a covered work,
399
- for which you have or can give appropriate copyright permission.
400
-
401
- Notwithstanding any other provision of this License, for material you
402
- add to a covered work, you may (if authorized by the copyright holders of
403
- that material) supplement the terms of this License with terms:
404
-
405
- a) Disclaiming warranty or limiting liability differently from the
406
- terms of sections 15 and 16 of this License; or
407
-
408
- b) Requiring preservation of specified reasonable legal notices or
409
- author attributions in that material or in the Appropriate Legal
410
- Notices displayed by works containing it; or
411
-
412
- c) Prohibiting misrepresentation of the origin of that material, or
413
- requiring that modified versions of such material be marked in
414
- reasonable ways as different from the original version; or
415
-
416
- d) Limiting the use for publicity purposes of names of licensors or
417
- authors of the material; or
418
-
419
- e) Declining to grant rights under trademark law for use of some
420
- trade names, trademarks, or service marks; or
421
-
422
- f) Requiring indemnification of licensors and authors of that
423
- material by anyone who conveys the material (or modified versions of
424
- it) with contractual assumptions of liability to the recipient, for
425
- any liability that these contractual assumptions directly impose on
426
- those licensors and authors.
427
-
428
- All other non-permissive additional terms are considered "further
429
- restrictions" within the meaning of section 10. If the Program as you
430
- received it, or any part of it, contains a notice stating that it is
431
- governed by this License along with a term that is a further
432
- restriction, you may remove that term. If a license document contains
433
- a further restriction but permits relicensing or conveying under this
434
- License, you may add to a covered work material governed by the terms
435
- of that license document, provided that the further restriction does
436
- not survive such relicensing or conveying.
437
-
438
- If you add terms to a covered work in accord with this section, you
439
- must place, in the relevant source files, a statement of the
440
- additional terms that apply to those files, or a notice indicating
441
- where to find the applicable terms.
442
-
443
- Additional terms, permissive or non-permissive, may be stated in the
444
- form of a separately written license, or stated as exceptions;
445
- the above requirements apply either way.
446
-
447
- 8. Termination.
448
-
449
- You may not propagate or modify a covered work except as expressly
450
- provided under this License. Any attempt otherwise to propagate or
451
- modify it is void, and will automatically terminate your rights under
452
- this License (including any patent licenses granted under the third
453
- paragraph of section 11).
454
-
455
- However, if you cease all violation of this License, then your
456
- license from a particular copyright holder is reinstated (a)
457
- provisionally, unless and until the copyright holder explicitly and
458
- finally terminates your license, and (b) permanently, if the copyright
459
- holder fails to notify you of the violation by some reasonable means
460
- prior to 60 days after the cessation.
461
-
462
- Moreover, your license from a particular copyright holder is
463
- reinstated permanently if the copyright holder notifies you of the
464
- violation by some reasonable means, this is the first time you have
465
- received notice of violation of this License (for any work) from that
466
- copyright holder, and you cure the violation prior to 30 days after
467
- your receipt of the notice.
468
-
469
- Termination of your rights under this section does not terminate the
470
- licenses of parties who have received copies or rights from you under
471
- this License. If your rights have been terminated and not permanently
472
- reinstated, you do not qualify to receive new licenses for the same
473
- material under section 10.
474
-
475
- 9. Acceptance Not Required for Having Copies.
476
-
477
- You are not required to accept this License in order to receive or
478
- run a copy of the Program. Ancillary propagation of a covered work
479
- occurring solely as a consequence of using peer-to-peer transmission
480
- to receive a copy likewise does not require acceptance. However,
481
- nothing other than this License grants you permission to propagate or
482
- modify any covered work. These actions infringe copyright if you do
483
- not accept this License. Therefore, by modifying or propagating a
484
- covered work, you indicate your acceptance of this License to do so.
485
-
486
- 10. Automatic Licensing of Downstream Recipients.
487
-
488
- Each time you convey a covered work, the recipient automatically
489
- receives a license from the original licensors, to run, modify and
490
- propagate that work, subject to this License. You are not responsible
491
- for enforcing compliance by third parties with this License.
492
-
493
- An "entity transaction" is a transaction transferring control of an
494
- organization, or substantially all assets of one, or subdividing an
495
- organization, or merging organizations. If propagation of a covered
496
- work results from an entity transaction, each party to that
497
- transaction who receives a copy of the work also receives whatever
498
- licenses to the work the party's predecessor in interest had or could
499
- give under the previous paragraph, plus a right to possession of the
500
- Corresponding Source of the work from the predecessor in interest, if
501
- the predecessor has it or can get it with reasonable efforts.
502
-
503
- You may not impose any further restrictions on the exercise of the
504
- rights granted or affirmed under this License. For example, you may
505
- not impose a license fee, royalty, or other charge for exercise of
506
- rights granted under this License, and you may not initiate litigation
507
- (including a cross-claim or counterclaim in a lawsuit) alleging that
508
- any patent claim is infringed by making, using, selling, offering for
509
- sale, or importing the Program or any portion of it.
510
-
511
- 11. Patents.
512
-
513
- A "contributor" is a copyright holder who authorizes use under this
514
- License of the Program or a work on which the Program is based. The
515
- work thus licensed is called the contributor's "contributor version".
516
-
517
- A contributor's "essential patent claims" are all patent claims
518
- owned or controlled by the contributor, whether already acquired or
519
- hereafter acquired, that would be infringed by some manner, permitted
520
- by this License, of making, using, or selling its contributor version,
521
- but do not include claims that would be infringed only as a
522
- consequence of further modification of the contributor version. For
523
- purposes of this definition, "control" includes the right to grant
524
- patent sublicenses in a manner consistent with the requirements of
525
- this License.
526
-
527
- Each contributor grants you a non-exclusive, worldwide, royalty-free
528
- patent license under the contributor's essential patent claims, to
529
- make, use, sell, offer for sale, import and otherwise run, modify and
530
- propagate the contents of its contributor version.
531
-
532
- In the following three paragraphs, a "patent license" is any express
533
- agreement or commitment, however denominated, not to enforce a patent
534
- (such as an express permission to practice a patent or covenant not to
535
- sue for patent infringement). To "grant" such a patent license to a
536
- party means to make such an agreement or commitment not to enforce a
537
- patent against the party.
538
-
539
- If you convey a covered work, knowingly relying on a patent license,
540
- and the Corresponding Source of the work is not available for anyone
541
- to copy, free of charge and under the terms of this License, through a
542
- publicly available network server or other readily accessible means,
543
- then you must either (1) cause the Corresponding Source to be so
544
- available, or (2) arrange to deprive yourself of the benefit of the
545
- patent license for this particular work, or (3) arrange, in a manner
546
- consistent with the requirements of this License, to extend the patent
547
- license to downstream recipients. "Knowingly relying" means you have
548
- actual knowledge that, but for the patent license, your conveying the
549
- covered work in a country, or your recipient's use of the covered work
550
- in a country, would infringe one or more identifiable patents in that
551
- country that you have reason to believe are valid.
552
-
553
- If, pursuant to or in connection with a single transaction or
554
- arrangement, you convey, or propagate by procuring conveyance of, a
555
- covered work, and grant a patent license to some of the parties
556
- receiving the covered work authorizing them to use, propagate, modify
557
- or convey a specific copy of the covered work, then the patent license
558
- you grant is automatically extended to all recipients of the covered
559
- work and works based on it.
560
-
561
- A patent license is "discriminatory" if it does not include within
562
- the scope of its coverage, prohibits the exercise of, or is
563
- conditioned on the non-exercise of one or more of the rights that are
564
- specifically granted under this License. You may not convey a covered
565
- work if you are a party to an arrangement with a third party that is
566
- in the business of distributing software, under which you make payment
567
- to the third party based on the extent of your activity of conveying
568
- the work, and under which the third party grants, to any of the
569
- parties who would receive the covered work from you, a discriminatory
570
- patent license (a) in connection with copies of the covered work
571
- conveyed by you (or copies made from those copies), or (b) primarily
572
- for and in connection with specific products or compilations that
573
- contain the covered work, unless you entered into that arrangement,
574
- or that patent license was granted, prior to 28 March 2007.
575
-
576
- Nothing in this License shall be construed as excluding or limiting
577
- any implied license or other defenses to infringement that may
578
- otherwise be available to you under applicable patent law.
579
-
580
- 12. No Surrender of Others' Freedom.
581
-
582
- If conditions are imposed on you (whether by court order, agreement or
583
- otherwise) that contradict the conditions of this License, they do not
584
- excuse you from the conditions of this License. If you cannot convey a
585
- covered work so as to satisfy simultaneously your obligations under this
586
- License and any other pertinent obligations, then as a consequence you may
587
- not convey it at all. For example, if you agree to terms that obligate you
588
- to collect a royalty for further conveying from those to whom you convey
589
- the Program, the only way you could satisfy both those terms and this
590
- License would be to refrain entirely from conveying the Program.
591
-
592
- 13. Use with the GNU Affero General Public License.
593
-
594
- Notwithstanding any other provision of this License, you have
595
- permission to link or combine any covered work with a work licensed
596
- under version 3 of the GNU Affero General Public License into a single
597
- combined work, and to convey the resulting work. The terms of this
598
- License will continue to apply to the part which is the covered work,
599
- but the special requirements of the GNU Affero General Public License,
600
- section 13, concerning interaction through a network will apply to the
601
- combination as such.
602
-
603
- 14. Revised Versions of this License.
604
-
605
- The Free Software Foundation may publish revised and/or new versions of
606
- the GNU General Public License from time to time. Such new versions will
607
- be similar in spirit to the present version, but may differ in detail to
608
- address new problems or concerns.
609
-
610
- Each version is given a distinguishing version number. If the
611
- Program specifies that a certain numbered version of the GNU General
612
- Public License "or any later version" applies to it, you have the
613
- option of following the terms and conditions either of that numbered
614
- version or of any later version published by the Free Software
615
- Foundation. If the Program does not specify a version number of the
616
- GNU General Public License, you may choose any version ever published
617
- by the Free Software Foundation.
618
-
619
- If the Program specifies that a proxy can decide which future
620
- versions of the GNU General Public License can be used, that proxy's
621
- public statement of acceptance of a version permanently authorizes you
622
- to choose that version for the Program.
623
-
624
- Later license versions may give you additional or different
625
- permissions. However, no additional obligations are imposed on any
626
- author or copyright holder as a result of your choosing to follow a
627
- later version.
628
-
629
- 15. Disclaimer of Warranty.
630
-
631
- THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
632
- APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
633
- HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
634
- OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
635
- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
636
- PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
637
- IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
638
- ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
639
-
640
- 16. Limitation of Liability.
641
-
642
- IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
643
- WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
644
- THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
645
- GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
646
- USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
647
- DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
648
- PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
649
- EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
650
- SUCH DAMAGES.
651
-
652
- 17. Interpretation of Sections 15 and 16.
653
-
654
- If the disclaimer of warranty and limitation of liability provided
655
- above cannot be given local legal effect according to their terms,
656
- reviewing courts shall apply local law that most closely approximates
657
- an absolute waiver of all civil liability in connection with the
658
- Program, unless a warranty or assumption of liability accompanies a
659
- copy of the Program in return for a fee.
660
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/README.md DELETED
@@ -1,64 +0,0 @@
1
- # lessphp v0.3.1
2
- ### <http://leafo.net/lessphp>
3
-
4
- `lessphp` is a compiler for LESS written in PHP. The documentation is great,
5
- so check it out: <http://leafo.net/lessphp/docs/>.
6
-
7
- Here's a quick tutorial:
8
-
9
- ### How to use in your PHP project
10
-
11
- Copy `lessc.inc.php` to your include directory and include it into your project.
12
-
13
- There are a few ways to interface with the compiler. The easiest is to have it
14
- compile a LESS file when the page is requested. The static function
15
- `lessc::ccompile`, checked compile, will compile the input LESS file only when it
16
- is newer than the output file.
17
-
18
- try {
19
- lessc::ccompile('input.less', 'output.css');
20
- } catch (exception $ex) {
21
- exit($ex->getMessage());
22
- }
23
-
24
- `lessc::ccompile` is not aware of imported files that change. Read [about
25
- `lessc::cexecute`](http://leafo.net/lessphp/docs/#compiling_automatically).
26
-
27
- Note that all failures with lessc are reported through exceptions.
28
- If you need more control you can make your own instance of lessc.
29
-
30
- $input = 'mystyle.less';
31
-
32
- $lc = new lessc($input);
33
-
34
- try {
35
- file_put_contents('mystyle.css', $lc->parse());
36
- } catch (exception $ex) { ... }
37
-
38
- In addition to loading from file, you can also parse from a string like so:
39
-
40
- $lc = new lessc();
41
- $lesscode = 'body { ... }';
42
- $out = $lc->parse($lesscode);
43
-
44
- ### How to use from the command line
45
-
46
- An additional script has been included to use the compiler from the command
47
- line. In the simplest invocation, you specify an input file and the compiled
48
- css is written to standard out:
49
-
50
- $ plessc input.less > output.css
51
-
52
- Using the -r flag, you can specify LESS code directly as an argument or, if
53
- the argument is left off, from standard in:
54
-
55
- $ plessc -r "my less code here"
56
-
57
- Finally, by using the -w flag you can watch a specified input file and have it
58
- compile as needed to the output file
59
-
60
- $ plessc -w input-file output-file
61
-
62
- Errors from watch mode are written to standard out.
63
-
64
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/docs/docs.md DELETED
@@ -1,798 +0,0 @@
1
- title: v0.3.1 documentation
2
- link_to_home: true
3
- --
4
-
5
- <h2 skip="true">Documentation v0.3.0</h2>
6
-
7
- <div style="margin-bottom: 1em;">$index</div>
8
-
9
- **lessphp** is a compiler that generates CSS from a superset language which
10
- adds a collection of convenient features often seen in other languages. All CSS
11
- is compatible with LESS, so you can start using new features with your existing CSS.
12
-
13
- It is designed to be compatible with [less.js](http://lesscss.org), and suitable
14
- as a drop in replacement for PHP projects.
15
-
16
- ## Getting Started
17
-
18
- The homepage for **lessphp** can be found at [http://leafo.net/lessphp/][1].
19
-
20
- You can follow development at the project's [GitHub][2].
21
-
22
- Including **lessphp** in your project is as simple as dropping the single
23
- include file into your code base and running the appropriate compile method as
24
- described in the [PHP Interface](#php_interface).
25
-
26
- [1]: http://leafo.net/lessphp "lessphp homepage"
27
- [2]: https://github.com/leafo/lessphp "lessphp GitHub page"
28
-
29
- ## Installation
30
-
31
- **lessphp** is distributed entirely in a single stand-alone file. Download the
32
- latest version from either [the homepage][1] or [GitHub][2].
33
-
34
- Development versions can also be downloading from GitHub.
35
-
36
- Place `lessphp.inc.php` in a location available to your PHP scripts, and
37
- include it. That's it! you're ready to begin.
38
-
39
- ## The Language
40
-
41
- **lessphp** is very easy to learn because it generally functions how you would
42
- expect it to. If you feel something is challenging or missing, feel free to
43
- open an issue on the [bug tracker](https://github.com/leafo/lessphp/issues).
44
-
45
- It is also easy to learn because any standards-compliant CSS code is valid LESS
46
- code. You are free to gradually enhance your existing CSS code base with LESS
47
- features without having to worry about rewriting anything.
48
-
49
- The following is a description of the new languages features provided by LESS.
50
-
51
- ### Line Comments
52
-
53
- Simple but very useful; line comments are started with `//`:
54
-
55
- ```less
56
- // this is a comment
57
- body {
58
- color: red; // as is this
59
- /* block comments still work also */
60
- }
61
- ```
62
-
63
- ### Variables
64
- Variables are identified with a name that starts with `@`. To declare a
65
- variable, you create an appropriately named CSS property and assign it a value:
66
-
67
- ```less
68
- @family: "verdana";
69
- @color: red;
70
- body {
71
- @mycolor: red;
72
- font-family: @family;
73
- color: @color;
74
- border-bottom: 1px solid @color;
75
- }
76
- ```
77
-
78
- Variable declarations will not appear in the output. Variables can be declared
79
- in the outer most scope of the file, or anywhere else a CSS property may
80
- appear. They can hold any CSS property value.
81
-
82
- Variables are only visible for use from their current scope, or any enclosed
83
- scopes.
84
-
85
- ### Expressions
86
-
87
- Expressions let you combine values and variables in meaningful ways. For
88
- example you can add to a color to make it a different shade. Or divide up the
89
- width of your layout logically. You can even concatenate strings.
90
-
91
- Use the mathematical operators to evaluate an expression:
92
-
93
- ```less
94
- @width: 960px;
95
- .nav {
96
- width: @width / 3;
97
- color: #001 + #abc;
98
- }
99
- .body {
100
- width: 2 * @width / 3;
101
- font-family: "hel" + "vetica";
102
- }
103
- ```
104
-
105
- Parentheses can be used to control the order of evaluation. They can also be
106
- used to force an evaluation for cases where CSS's syntax makes the expression
107
- ambiguous.
108
-
109
- The following property will produce two numbers, instead of doing the
110
- subtraction:
111
-
112
- ```less
113
- margin: 10px -5px;
114
- ```
115
-
116
- To force the subtraction:
117
-
118
- ```less
119
- margin: (10px -5px);
120
- ```
121
-
122
- It is also safe to surround mathematical operators by spaces to ensure that
123
- they are evaluated:
124
-
125
- ```less
126
- margin: 10px - 5px;
127
- ```
128
-
129
- Division has a special quirk. Due to CSS font shorthand syntax, we need to be
130
- careful about how we place spaces. In the following example we are using font
131
- size and lineheight shorthand. No division should take place:
132
-
133
- ```less
134
- .font {
135
- font: 20px/80px "Times New Roman";
136
- }
137
- ```
138
-
139
- In order to force division we can surround the `/` by spaces, or we can wrap
140
- the expression in parentheses:
141
-
142
- ```less
143
- .font {
144
- // these two will evaluate
145
- font: 20px / 80px "Times New Roman";
146
- font: (20px/80px) "Times New Roman";
147
- }
148
- ```
149
-
150
- ### Nested Blocks
151
-
152
- By nesting blocks we can build up a chain of CSS selectors through scope
153
- instead of repeating them. In addition to reducing repetition, this also helps
154
- logically organize the structure of our CSS.
155
-
156
- ```less
157
- ol.list {
158
- li.special {
159
- border: 1px solid red;
160
- }
161
-
162
- li.plain {
163
- font-weight: bold;
164
- }
165
- }
166
- ```
167
-
168
-
169
- This will produce two blocks, a `ol.list li.special` and `ol.list li.plain`.
170
-
171
- Blocks can be nested as deep as required in order to build a hierarchy of
172
- relationships.
173
-
174
- The `&` operator can be used in a selector to represent its parent's selector.
175
- If the `&` operator is used, then the default action of appending the parent to
176
- the front of the child selector separated by space is not performed.
177
-
178
- ```less
179
- b {
180
- a & {
181
- color: red;
182
- }
183
-
184
- // the following have the same effect
185
-
186
- & i {
187
- color: blue;
188
- }
189
-
190
- i {
191
- color: blue;
192
- }
193
- }
194
- ```
195
-
196
-
197
- Because the `&` operator respects the whitespace around it, we can use it to
198
- control how the child blocks are joined. Consider the differences between the
199
- following:
200
-
201
- ```less
202
- div {
203
- .child-class { color: purple; }
204
-
205
- &.isa-class { color: green; }
206
-
207
- #child-id { height: 200px; }
208
-
209
- &#div-id { height: 400px; }
210
-
211
- &:hover { color: red; }
212
-
213
- :link { color: blue; }
214
- }
215
- ```
216
-
217
- The `&` operator also works with [mixins](#mixins), which produces interesting results:
218
-
219
- ```less
220
- .within_box_style() {
221
- .box & {
222
- color: blue;
223
- }
224
- }
225
-
226
- #menu {
227
- .within_box_style;
228
- }
229
- ```
230
-
231
- ### Mixins
232
-
233
- Any block can be mixed in just by naming it:
234
-
235
- ```less
236
- .mymixin {
237
- color: blue;
238
- border: 1px solid red;
239
-
240
- .special {
241
- font-weight: bold;
242
- }
243
- }
244
-
245
-
246
- h1 {
247
- font-size: 200px;
248
- .mixin;
249
- }
250
- ```
251
-
252
- All properties and child blocks are mixed in.
253
-
254
- Mixins can be made parametric, meaning they can take arguments, in order to
255
- enhance their utility. A parametric mixin all by itself is not outputted when
256
- compiled. Its properties will only appear when mixed into another block.
257
-
258
- The canonical example is to create a rounded corners mixin that works across
259
- browsers:
260
-
261
- ```less
262
- .rounded-corners(@radius: 5px) {
263
- border-radius: @radius;
264
- -webkit-border-radius: @radius;
265
- -moz-border-radius: @radius;
266
- }
267
-
268
- .header {
269
- .rounded-corners();
270
- }
271
-
272
- .info {
273
- background: red;
274
- .rounded-corners(14px);
275
- }
276
- ```
277
-
278
- If you have a mixin that doesn't have any arguments, but you don't want it to
279
- show up in the output, give it a blank argument list:
280
-
281
- ```less
282
- .secret() {
283
- font-size: 6000px;
284
- }
285
-
286
- .div {
287
- .secret;
288
- }
289
- ```
290
-
291
- If the mixin doesn't need any arguments, you can leave off the parentheses when
292
- mixing it in, as seen above.
293
-
294
- You can also mixin a block that is nested inside other blocks. You can think of
295
- the outer block as a way of making a scope for your mixins. You just list the
296
- names of the mixins separated by spaces, which describes the path to the mixin
297
- you want to include. Optionally you can separate them by `>`.
298
-
299
- ```less
300
- .my_scope {
301
- .some_color {
302
- color: red;
303
- .inner_block {
304
- text-decoration: underline;
305
- }
306
- }
307
- .bold {
308
- font-weight: bold;
309
- color: blue;
310
- }
311
- }
312
-
313
- .a_block {
314
- .my_scope .some_color;
315
- .my_scope .some_color .inner_block;
316
- }
317
-
318
- .another_block {
319
- // the alternative syntax
320
- .my_scope > .bold;
321
- }
322
- ```
323
-
324
- #### `@arguments` Variable
325
-
326
- Within an mixin there is a special variable named `@arguments` that contains
327
- all the arguments passed to the mixin along with any remaining arguments that
328
- have default values. The value of the variable has all the values separated by
329
- spaces.
330
-
331
- This useful for quickly assigning all the arguments:
332
-
333
- ```less
334
- .box-shadow(@inset, @x, @y, @blur, @spread, @color) {
335
- box-shadow: @arguments;
336
- -webkit-box-shadow: @arguments;
337
- -moz-box-shadow: @arguments;
338
- }
339
- .menu {
340
- .box-shadow(1px, 1px, 5px, #aaa);
341
- }
342
- ```
343
-
344
- In addition to the arguments passed to the mixin, `@arguments` will also inlude
345
- remaining default values assigned by the mixin:
346
-
347
-
348
- ```less
349
- .border-mixin(@width, @style: solid, @color: black) {
350
- border: @arguments;
351
- }
352
-
353
- pre {
354
- .border-mixin(4px, dotted);
355
- }
356
-
357
- ```
358
-
359
- ### Import
360
-
361
- Multiple LESS files can be compiled into a single CSS file by using the
362
- `@import` statement. Be careful, the LESS import statement shares syntax with
363
- the CSS import statement. If the file being imported ends in a `.less`
364
- extension, or no extension, then it is treated as a LESS import. Otherwise it
365
- is left alone and outputted directly:
366
-
367
- ```less
368
- // my_file.less
369
- .some-mixin(@height) {
370
- height: @height;
371
- }
372
-
373
- // main.less
374
- @import "main.less" // will import the file if it can be found
375
- @import "main.css" // will be left alone
376
-
377
- body {
378
- .some-mixin(400px);
379
- }
380
- ```
381
-
382
- All of the following lines are valid ways to import the same file:
383
-
384
- ```less
385
- @import "file";
386
- @import 'file.less';
387
- @import url("file");
388
- @import url('file');
389
- @import url(file);
390
- ```
391
-
392
- When importing, the `importDir` is searched for files. This can be configured,
393
- see [PHP Interface](#php_interface).
394
-
395
- ### String Interpolation
396
-
397
- String interpolation is a convenient way to insert the value of a variable
398
- right into a string literal. Given some variable named `@var_name`, you just
399
- need to write it as `@{var_name}` from within the string to have its value
400
- inserted:
401
-
402
- ```less
403
- @symbol: ">";
404
- h1:before {
405
- content: "@{symbol}: ";
406
- }
407
-
408
- h2:before {
409
- content: "@{symbol}@{symbol}: ";
410
- }
411
- ```
412
-
413
- There are two kinds of strings, implicit and explicit strings. Explicit strings
414
- are wrapped by double quotes, `"hello I am a string"`, or single quotes `'I am
415
- another string'`. Implicit strings only appear when using `url()`. The text
416
- between the parentheses is considered a string and thus string interpolation is
417
- possible:
418
-
419
- ```less
420
- @path: "files/";
421
- body {
422
- background: url(@{path}my_background.png);
423
- }
424
- ```
425
-
426
- ### String Format Function
427
-
428
- The `%` function can be used to insert values into strings using a *format
429
- string*. It works similar to `printf` seen in other languages. It has the
430
- same purpose as string interpolation above, but gives explicit control over
431
- the output format.
432
-
433
- ```less
434
- @symbol: ">";
435
- h1:before {
436
- content: %("%s: ", @symbol);
437
- }
438
- ```
439
-
440
- The `%` function takes as its first argument the format string, following any
441
- number of addition arguments that are inserted in place of the format
442
- directives.
443
-
444
- A format directive starts with a `%` and is followed by a single character that
445
- is either `a`, `d`, or `s`:
446
-
447
- ```less
448
- strings: %("%a %d %s %a", hi, 1, 'ok', 'cool');
449
- ```
450
-
451
- `%a` and `%d` format the value the same way: they compile the argument to its
452
- CSS value and insert it directly. When used with a string, the quotes are
453
- included in the output. This typically isn't what we want, so we have the `%s`
454
- format directive which strips quotes from strings before inserting them.
455
-
456
- The `%d` directive functions the same as `%a`, but is typically used for numbers
457
- assuming the output format of numbers might change in the future.
458
-
459
- ### String Unquoting
460
-
461
- Sometimes you will need to write proprietary CSS syntax that is unable to be
462
- parsed. As a workaround you can place the code into a string and unquote it.
463
- Unquoting is the process of outputting a string without its surrounding quotes.
464
- There are two ways to unquote a string.
465
-
466
- The `~` operator in front of a string will unquote that string:
467
-
468
- ```less
469
- .class {
470
- // a made up, but problematic vendor specific CSS
471
- filter: ~"Microsoft.AlphaImage(src='image.png')";
472
- }
473
- ```
474
-
475
- If you are working with other types, such as variables, there is a built in
476
- function that let's you unquote any value. It is called `e`.
477
-
478
- ```less
479
- @color: "red";
480
- .class {
481
- color: e(@color);
482
- }
483
- ```
484
-
485
- ### Built In Functions
486
-
487
- **lessphp** has a collection of built in functions:
488
-
489
- * `e(str)` -- returns a string without the surrounding quotes.
490
- See [String Unquoting](#string_unquoting)
491
-
492
- * `floor(number)` -- returns the floor of a numerical input
493
- * `round(number)` -- returns the rounded value of numerical input
494
-
495
- * `lighten(color, percent)` -- lightens `color` by `percent` and returns it
496
- * `darken(color, percent)` -- darkens `color` by `percent` and returns it
497
-
498
- * `saturate(color, percent)` -- saturates `color` by `percent` and returns it
499
- * `desaturate(color, percent)` -- desaturates `color` by `percent` and returns it
500
-
501
- * `fadein(color, percent)` -- makes `color` less transparent by `percent` and returns it
502
- * `fadeout(color, percent)` -- makes `color` more transparent by `percent` and returns it
503
-
504
- * `spin(color, amount)` -- returns a color with `amount` degrees added to hue
505
-
506
- * `fade(color, amount)` -- retuns a color with the alpha set to `amount`
507
-
508
- * `hue(color)` -- retuns the hue of `color`
509
-
510
- * `saturation(color)` -- retuns the saturation of `color`
511
-
512
- * `lightness(color)` -- retuns the lightness of `color`
513
-
514
- * `alpha(color)` -- retuns the alpha value of `color` or 1.0 if it doesn't have an alpha
515
-
516
- * `percentage(number)` -- converts a floating point number to a percentage, eg. `0.65` -> `65%`
517
-
518
- * `mix(color1, color1, percent)` -- mixes two colors by percentagle where 100%
519
- keeps all of `color1`, and 0% keeps all of `color2`. Will take into account
520
- the alpha of the colors if it exists. See
521
- <http://sass-lang.com/docs/yardoc/Sass/Script/Functions.html#mix-instance_method>.
522
-
523
- * `rgbahex(color)` -- returns a string containing 4 part hex color.
524
-
525
- This is used to convert a CSS color into the hex format that IE's filter
526
- method expects when working with an alpha component.
527
-
528
- ```less
529
- .class {
530
- @start: rgbahex(rgba(25, 34, 23, .5));
531
- @end: rgbahex(rgba(85, 74, 103, .6));
532
- // abridged example
533
- -ms-filter:
534
- e("gradient(start=@{start},end=@{end})");
535
- }
536
- ```
537
-
538
- ## PHP Interface
539
-
540
- The PHP interface lets you control the compiler from your PHP scripts. There is
541
- only one file to include to get access to everything:
542
-
543
- ```php
544
- <?php
545
- include "lessc.inc.php";
546
- ```
547
-
548
- To compile a file to a string (of CSS code):
549
-
550
- ```php
551
- $less = new lessc("myfile.less");
552
- $css = $less->parse();
553
- ```
554
-
555
- To compile a string to a string:
556
-
557
- ```php
558
- $less = new lessc(); // a blank lessc
559
- $css = $less->parse("body { a { color: red } }");
560
- ```
561
-
562
- ### Compiling Automatically
563
-
564
- Often, you want to write the compiled CSS to a file, and only recompile when
565
- the original LESS file has changed. The following function will check if the
566
- modification date of the LESS file is more recent than the CSS file. The LESS
567
- file will be compiled if it is. If the CSS file doesn't exist yet, then it will
568
- also compile the LESS file.
569
-
570
- ```php
571
- lessc::ccompile('myfile.less', 'mystyle.css');
572
- ```
573
-
574
- `ccompile` is very basic, it only checks if the input file's modification time.
575
- It is not of any files that are brought in using `@import`.
576
-
577
- For this reason we also have `lessc::cexecute`. It functions slightly
578
- differently, but gives us the ability to check changes to all files used during
579
- the compile. It takes one argument, either the name of the file we want to
580
- compile, or an existing *cache object*. Its return value is an updated cache
581
- object.
582
-
583
- If we don't have a cache object, then we call the function with the name of the
584
- file to get the initial cache object. If we do have a cache object, then we
585
- call the function with it. In both cases, an updated cache object is returned.
586
-
587
- The cache object keeps track of all the files that must be checked in order to
588
- determine if a rebuild is required.
589
-
590
- The cache object is a plain PHP `array`. It stores the last time it compiled in
591
- `$cache['updated']` and output of the compile in `$cache['compiled']`.
592
-
593
- Here we demonstrate creating an new cache object, then using it to see if we
594
- have a recompiled version available to be written:
595
-
596
-
597
- ```php
598
- $less_file = 'myfile.less';
599
- $css_file = 'myfile.css';
600
-
601
- // create a new cache object, and compile
602
- $cache = lessc::cexecute('myfile.less');
603
- file_put_contents($css_file, $cache['compiled']);
604
-
605
- // the next time we run, write only if it has updated
606
- $last_updated = $cache['updated'];
607
- $cache = lessc::cexecute($cache);
608
- if ($cache['updated'] > $last_updated) {
609
- file_put_contents($css_file, $cache['compiled']);
610
- }
611
-
612
- ```
613
-
614
- In order for the system to fully work, we must save cache object between
615
- requests. Because it's a plain PHP `array`, it's sufficient to
616
- [`serialize`](http://php.net/serialize) it and save it the string somewhere
617
- like a file or in persistent memory.
618
-
619
- An example with saving cache object to a file:
620
-
621
- ```php
622
- function auto_compile_less($less_fname, $css_fname) {
623
- // load the cache
624
- $cache_fname = $less_fname.".cache";
625
- if (file_exists($cache_fname)) {
626
- $cache = unserialize(file_get_contents($cache_fname));
627
- } else {
628
- $cache = $less_fname;
629
- }
630
-
631
- $new_cache = lessc::cexecute($cache);
632
- if (!is_array($cache) || $new_cache['updated'] > $cache['updated']) {
633
- file_put_contents($cache_fname, serialize($new_cache));
634
- file_put_contents($css_fname, $new_cache['compiled']);
635
- }
636
- }
637
-
638
- auto_compile_less('myfile.less', 'myfile.css')
639
-
640
- ```
641
-
642
- `lessc:cexecute` takes an optional second argument, `$force`. Passing in true
643
- will cause the input to always be recompiled.
644
-
645
- ### Error Handling
646
-
647
- All of the following methods will throw an `Exception` if the parsing fails:
648
-
649
- ```php
650
- $less = new lessc();
651
- try {
652
- $less->parse("} invalid LESS }}}");
653
- } catch (Exception $ex) {
654
- echo "lessphp fatal error: ".$ex->getMessage();
655
- }
656
- ```
657
- ### Setting Variables From PHP
658
-
659
- The `parse` function takes a second optional argument. If you want to
660
- initialize variables from outside the LESS file then you can pass in an
661
- associative array of names and values. The values will parsed as CSS values:
662
-
663
- ```php
664
- $less = new lessc();
665
- echo $less->parse(".magic { color: @color; width: @base - 200; }",
666
- array(
667
- 'color' => 'red';
668
- 'base' => '960px';
669
- ));
670
- ```
671
-
672
- You can also do this when loading from a file, but remember to set the first
673
- argument of the parse function to `null`, otherwise it will try to compile that
674
- instead of the file:
675
-
676
- ```php
677
- $less = new lessc("myfile.less");
678
- echo $less->parse(null, array('color' => 'blue'));
679
- ```
680
-
681
- ### Custom Functions
682
-
683
- **lessphp** has a simple extension interface where you can implement user
684
- functions that will be exposed in LESS code during the compile. They can be a
685
- little tricky though because you need to work with the **lessphp** type system.
686
-
687
- An instance of `lessc`, the **lessphp** compiler has two relevant methods:
688
- `registerFunction` and `unregisterFunction`. `registerFunction` takes two
689
- arguments, a name and a callable value. `unregisterFunction` just takes the
690
- name of an existing function to remove.
691
-
692
- Here's an example that adds a function called `double` that doubles any numeric
693
- argument:
694
-
695
- ```php
696
- <?php
697
- include "lessc.inc.php";
698
-
699
- function lessphp_double($arg) {
700
- list($type, $value) = $arg;
701
- return array($type, $value*2);
702
- }
703
-
704
- $myless = new myless();
705
- $myless->registerFunction("double", "lessphp_double");
706
-
707
- // gives us a width of 800px
708
- echo $myless->parse("div { width: double(400px); }");
709
- ```
710
-
711
- The second argument to `registerFunction` is any *callable value* that is
712
- understood by [`call_user_func`](http://php.net/call_user_func).
713
-
714
- If we are using PHP 5.3 or above then we are free to pass a function literal
715
- like so:
716
-
717
- ```php
718
- $myless->registerFunction("double", function($arg) {
719
- list($type, $value) = $arg;
720
- return array($type, $value*2);
721
- });
722
- ```
723
-
724
- Now let's talk about the `double` function itself.
725
-
726
- Although a little verbose, the implementation gives us some insight on the type
727
- system. All values in **lessphp** are stored in an array where the 0th element
728
- is a string representing the type, and the other elements make up the
729
- associated data for that value.
730
-
731
- The best way to get an understanding of the system is to register is dummy
732
- function which does a `vardump` on the argument. Try passing the function
733
- different values from LESS and see what the results are.
734
-
735
- The return value of the registered function must also be a LESS type, but if it is
736
- a string or numeric value, it will automatically be coerced into an appropriate
737
- typed value. In our example, we reconstruct the value with our modifications
738
- while making sure that we preserve the original type.
739
-
740
- ## Command Line Interface
741
-
742
- **lessphp** comes with a command line script written in PHP that can be used to
743
- invoke the compiler from the terminal. On Linux an OSX, all you need to do is
744
- place `plessc` and `lessc.inc.php` somewhere in your PATH (or you can run it in
745
- the current directory as well). On windows you'll need a copy of `php.exe` to
746
- run the file. To compile a file, `input.less` to CSS, run:
747
-
748
- ```bash
749
- $ plessc input.less
750
- ```
751
-
752
- To write to a file, redirect standard out:
753
-
754
- ```bash
755
- $ plessc input.less > output.css
756
- ```
757
-
758
- To compile code directly on the command line:
759
-
760
- ```bash
761
- $ plessc -r "@color: red; body { color: @color; }"
762
- ```
763
-
764
- To watch a file for changes, and compile it as needed, use the `-w` flag:
765
-
766
- ```bash
767
- $ plessc -w input-file output-file
768
- ```
769
-
770
- Errors from watch mode are written to standard out.
771
-
772
-
773
- ## License
774
-
775
- Copyright (c) 2010 Leaf Corcoran, <http://leafo.net/lessphp>
776
-
777
- Permission is hereby granted, free of charge, to any person obtaining
778
- a copy of this software and associated documentation files (the
779
- "Software"), to deal in the Software without restriction, including
780
- without limitation the rights to use, copy, modify, merge, publish,
781
- distribute, sublicense, and/or sell copies of the Software, and to
782
- permit persons to whom the Software is furnished to do so, subject to
783
- the following conditions:
784
-
785
- The above copyright notice and this permission notice shall be
786
- included in all copies or substantial portions of the Software.
787
-
788
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
789
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
790
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
791
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
792
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
793
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
794
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
795
-
796
-
797
- *Also under GPL3 if required, see `LICENSE` file*
798
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/lessc.inc.php DELETED
@@ -1,2239 +0,0 @@
1
- <?php
2
-
3
- /**
4
- * lessphp v0.3.1
5
- * http://leafo.net/lessphp
6
- *
7
- * LESS css compiler, adapted from http://lesscss.org
8
- *
9
- * Copyright 2011, Leaf Corcoran <leafot@gmail.com>
10
- * Licensed under MIT or GPLv3, see LICENSE
11
- */
12
-
13
-
14
- /**
15
- * The less compiler and parser.
16
- *
17
- * Converting LESS to CSS is a two stage process. First the incoming document
18
- * must be parsed. Parsing creates a tree in memory that represents the
19
- * structure of the document. Then, the tree of the document is recursively
20
- * compiled into the CSS text. The compile step has an implicit step called
21
- * reduction, where values are brought to their lowest form before being
22
- * turned to text, eg. mathematical equations are solved, and variables are
23
- * dereferenced.
24
- *
25
- * The parsing stage produces the final structure of the document, for this
26
- * reason mixins are mixed in and attribute accessors are referenced during
27
- * the parse step. A reduction is done on the mixed in block as it is mixed in.
28
- *
29
- * See the following:
30
- * - entry point for parsing and compiling: lessc::parse()
31
- * - parsing: lessc::parseChunk()
32
- * - compiling: lessc::compileBlock()
33
- *
34
- */
35
- class lessc {
36
- protected $buffer;
37
- protected $count;
38
- protected $line;
39
- protected $libFunctions = array();
40
- static protected $nextBlockId = 0;
41
-
42
- public $indentLevel;
43
- public $indentChar = ' ';
44
-
45
- protected $env = null;
46
-
47
- protected $allParsedFiles = array();
48
-
49
- public $vPrefix = '@'; // prefix of abstract properties
50
- public $mPrefix = '$'; // prefix of abstract blocks
51
- public $imPrefix = '!'; // special character to add !important
52
- public $parentSelector = '&';
53
-
54
- static protected $precedence = array(
55
- '+' => 0,
56
- '-' => 0,
57
- '*' => 1,
58
- '/' => 1,
59
- '%' => 1,
60
- );
61
- static protected $operatorString; // regex string to match any of the operators
62
-
63
- // types that have delayed computation
64
- static protected $dtypes = array('expression', 'variable',
65
- 'function', 'negative', 'list', 'lookup');
66
-
67
- /**
68
- * @link http://www.w3.org/TR/css3-values/
69
- */
70
- static protected $units = array(
71
- 'em', 'ex', 'px', 'gd', 'rem', 'vw', 'vh', 'vm', 'ch', // Relative length units
72
- 'in', 'cm', 'mm', 'pt', 'pc', // Absolute length units
73
- '%', // Percentages
74
- 'deg', 'grad', 'rad', 'turn', // Angles
75
- 'ms', 's', // Times
76
- 'Hz', 'kHz', //Frequencies
77
- );
78
-
79
- public $importDisabled = false;
80
- public $importDir = '';
81
-
82
- public $compat = false; // lessjs compatibility mode, does nothing right now
83
-
84
- /**
85
- * if we are in an expression then we don't need to worry about parsing font shorthand
86
- * $inExp becomes true after the first value in an expression, or if we enter parens
87
- */
88
- protected $inExp = false;
89
-
90
- /**
91
- * if we are in parens we can be more liberal with whitespace around operators because
92
- * it must evaluate to a single value and thus is less ambiguous.
93
- *
94
- * Consider:
95
- * property1: 10 -5; // is two numbers, 10 and -5
96
- * property2: (10 -5); // should evaluate to 5
97
- */
98
- protected $inParens = false;
99
-
100
- /**
101
- * Parse a single chunk off the head of the buffer and place it.
102
- * @return false when the buffer is empty, or there is an error
103
- *
104
- * This functions is called repeatedly until the entire document is
105
- * parsed.
106
- *
107
- * This parser is most similar to a recursive descent parser. Single
108
- * functions represent discrete grammatical rules for the language, and
109
- * they are able to capture the text that represents those rules.
110
- *
111
- * Consider the function lessc::keyword(). (all parse functions are
112
- * structured the same)
113
- *
114
- * The function takes a single reference argument. When calling the the
115
- * function it will attempt to match a keyword on the head of the buffer.
116
- * If it is successful, it will place the keyword in the referenced
117
- * argument, advance the position in the buffer, and return true. If it
118
- * fails then it won't advance the buffer and it will return false.
119
- *
120
- * All of these parse functions are powered by lessc::match(), which behaves
121
- * the same way, but takes a literal regular expression. Sometimes it is
122
- * more convenient to use match instead of creating a new function.
123
- *
124
- * Because of the format of the functions, to parse an entire string of
125
- * grammatical rules, you can chain them together using &&.
126
- *
127
- * But, if some of the rules in the chain succeed before one fails, then
128
- * then buffer position will be left at an invalid state. In order to
129
- * avoid this, lessc::seek() is used to remember and set buffer positions.
130
- *
131
- * Before doing a chain, use $s = $this->seek() to remember the current
132
- * position into $s. Then if a chain fails, use $this->seek($s) to
133
- * go back where we started.
134
- */
135
- function parseChunk() {
136
- if (empty($this->buffer)) return false;
137
- $s = $this->seek();
138
-
139
- // setting a property
140
- if ($this->keyword($key) && $this->assign() &&
141
- $this->propertyValue($value) && $this->end())
142
- {
143
- $this->append(array('assign', $key, $value));
144
- return true;
145
- } else {
146
- $this->seek($s);
147
- }
148
-
149
- // look for special css blocks
150
- if ($this->env->parent == null && $this->literal('@', false)) {
151
- $this->count--;
152
-
153
- // a font-face block
154
- if ($this->literal('@font-face') && $this->literal('{')) {
155
- $b = $this->pushSpecialBlock('@font-face');
156
- return true;
157
- } else {
158
- $this->seek($s);
159
- }
160
-
161
- // charset
162
- if ($this->literal('@charset') && $this->propertyValue($value) &&
163
- $this->end())
164
- {
165
- $this->append(array('charset', $value));
166
- return true;
167
- } else {
168
- $this->seek($s);
169
- }
170
-
171
-
172
- // media
173
- if ($this->literal('@media') && $this->mediaTypes($types) &&
174
- $this->literal('{'))
175
- {
176
- $b = $this->pushSpecialBlock('@media');
177
- $b->media = $types;
178
- return true;
179
- } else {
180
- $this->seek($s);
181
- }
182
-
183
- // css animations
184
- if ($this->match('(@(-[a-z]+-)?keyframes)', $m) &&
185
- $this->propertyValue($value) && $this->literal('{'))
186
- {
187
- $b = $this->pushSpecialBlock(trim($m[0]));
188
- $b->keyframes = $value;
189
- return true;
190
- } else {
191
- $this->seek($s);
192
- }
193
- }
194
-
195
- if (isset($this->env->keyframes)) {
196
- if ($this->match("(to|from|[0-9]+%)", $m) && $this->literal('{')) {
197
- $this->pushSpecialBlock($m[1]);
198
- return true;
199
- } else {
200
- $this->seek($s);
201
- }
202
- }
203
-
204
- // setting a variable
205
- if ($this->variable($name) && $this->assign() &&
206
- $this->propertyValue($value) && $this->end())
207
- {
208
- $this->append(array('assign', $this->vPrefix.$name, $value));
209
- return true;
210
- } else {
211
- $this->seek($s);
212
- }
213
-
214
- if ($this->import($url, $media)) {
215
- // don't check .css files
216
- if (empty($media) && substr_compare($url, '.css', -4, 4) !== 0) {
217
- if ($this->importDisabled) {
218
- $this->append(array('raw', '/* import disabled */'));
219
- } else {
220
- $path = $this->findImport($url);
221
- if (!is_null($path)) {
222
- $this->append(array('import', $path));
223
- return true;
224
- }
225
- }
226
- }
227
-
228
- $this->append(array('raw', '@import url("'.$url.'")'.
229
- ($media ? ' '.$media : '').';'));
230
- return true;
231
- }
232
-
233
- // opening parametric mixin
234
- if ($this->tag($tag, true) && $this->argumentDef($args) &&
235
- $this->literal('{'))
236
- {
237
- $block = $this->pushBlock($this->fixTags(array($tag)));
238
- $block->args = $args;
239
- return true;
240
- } else {
241
- $this->seek($s);
242
- }
243
-
244
- // opening a simple block
245
- if ($this->tags($tags) && $this->literal('{')) {
246
- $tags = $this->fixTags($tags);
247
- $this->pushBlock($tags);
248
- return true;
249
- } else {
250
- $this->seek($s);
251
- }
252
-
253
- // closing a block
254
- if ($this->literal('}')) {
255
- try {
256
- $block = $this->pop();
257
- } catch (exception $e) {
258
- $this->seek($s);
259
- $this->throwParseError($e->getMessage());
260
- }
261
-
262
- $hidden = true;
263
- if (!isset($block->args)) foreach ($block->tags as $tag) {
264
- if ($tag{0} != $this->mPrefix) {
265
- $hidden = false;
266
- break;
267
- }
268
- }
269
-
270
- if (!$hidden) $this->append(array('block', $block));
271
- foreach ($block->tags as $tag) {
272
- if (isset($this->env->children[$tag])) {
273
- $block = $this->mergeBlock($this->env->children[$tag], $block);
274
- }
275
- $this->env->children[$tag] = $block;
276
- }
277
-
278
- return true;
279
- }
280
-
281
- // mixin
282
- if ($this->mixinTags($tags) &&
283
- ($this->argumentValues($argv) || true) && $this->end())
284
- {
285
- $tags = $this->fixTags($tags);
286
- $this->append(array('mixin', $tags, $argv));
287
- return true;
288
- } else {
289
- $this->seek($s);
290
- }
291
-
292
- // spare ;
293
- if ($this->literal(';')) return true;
294
-
295
- return false; // got nothing, throw error
296
- }
297
-
298
- function fixTags($tags) {
299
- // move @ tags out of variable namespace
300
- foreach ($tags as &$tag) {
301
- if ($tag{0} == $this->vPrefix) $tag[0] = $this->mPrefix;
302
- }
303
- return $tags;
304
- }
305
-
306
- // attempts to find the path of an import url, returns null for css files
307
- function findImport($url) {
308
- foreach ((array)$this->importDir as $dir) {
309
- $full = $dir.(substr($dir, -1) != '/' ? '/' : '').$url;
310
- if ($this->fileExists($file = $full.'.less') || $this->fileExists($file = $full)) {
311
- return $file;
312
- }
313
- }
314
-
315
- return null;
316
- }
317
-
318
- function fileExists($name) {
319
- // sym link workaround
320
- return file_exists($name) || file_exists(realpath(preg_replace('/\w+\/\.\.\//', '', $name)));
321
- }
322
-
323
- // a list of expressions
324
- function expressionList(&$exps) {
325
- $values = array();
326
-
327
- while ($this->expression($exp)) {
328
- $values[] = $exp;
329
- }
330
-
331
- if (count($values) == 0) return false;
332
-
333
- $exps = $this->compressList($values, ' ');
334
- return true;
335
- }
336
-
337
- /**
338
- * Attempt to consume an expression.
339
- * @link http://en.wikipedia.org/wiki/Operator-precedence_parser#Pseudo-code
340
- */
341
- function expression(&$out) {
342
- $s = $this->seek();
343
- if ($this->literal('(') && ($this->inExp = $this->inParens = true) && $this->expression($exp) && $this->literal(')')) {
344
- $lhs = $exp;
345
- } elseif ($this->seek($s) && $this->value($val)) {
346
- $lhs = $val;
347
- } else {
348
- $this->inParens = $this->inExp = false;
349
- $this->seek($s);
350
- return false;
351
- }
352
-
353
- $out = $this->expHelper($lhs, 0);
354
- $this->inParens = $this->inExp = false;
355
- return true;
356
- }
357
-
358
- /**
359
- * recursively parse infix equation with $lhs at precedence $minP
360
- */
361
- function expHelper($lhs, $minP) {
362
- $this->inExp = true;
363
- $ss = $this->seek();
364
-
365
- // if the if there was whitespace before the operator, then we require whitespace after
366
- // the operator for it to be a mathematical operator.
367
-
368
- $needWhite = false;
369
- if (!$this->inParens && preg_match('/\s/', $this->buffer{$this->count - 1})) {
370
- $needWhite = true;
371
- }
372
-
373
- // try to find a valid operator
374
- while ($this->match(self::$operatorString.($needWhite ? '\s' : ''), $m) && self::$precedence[$m[1]] >= $minP) {
375
- // get rhs
376
- $s = $this->seek();
377
- $p = $this->inParens;
378
- if ($this->literal('(') && ($this->inParens = true) && $this->expression($exp) && $this->literal(')')) {
379
- $this->inParens = $p;
380
- $rhs = $exp;
381
- } else {
382
- $this->inParens = $p;
383
- if ($this->seek($s) && $this->value($val)) {
384
- $rhs = $val;
385
- } else {
386
- break;
387
- }
388
- }
389
-
390
- // peek for next operator to see what to do with rhs
391
- if ($this->peek(self::$operatorString, $next) && self::$precedence[$next[1]] > $minP) {
392
- $rhs = $this->expHelper($rhs, self::$precedence[$next[1]]);
393
- }
394
-
395
- // don't evaluate yet if it is dynamic
396
- if (in_array($rhs[0], self::$dtypes) || in_array($lhs[0], self::$dtypes))
397
- $lhs = array('expression', $m[1], $lhs, $rhs);
398
- else
399
- $lhs = $this->evaluate($m[1], $lhs, $rhs);
400
-
401
- $ss = $this->seek();
402
-
403
- $needWhite = false;
404
- if (!$this->inParens && preg_match('/\s/', $this->buffer{$this->count - 1})) {
405
- $needWhite = true;
406
- }
407
- }
408
- $this->seek($ss);
409
-
410
- return $lhs;
411
- }
412
-
413
- // consume a list of values for a property
414
- function propertyValue(&$value) {
415
- $values = array();
416
-
417
- $s = null;
418
- while ($this->expressionList($v)) {
419
- $values[] = $v;
420
- $s = $this->seek();
421
- if (!$this->literal(',')) break;
422
- }
423
-
424
- if ($s) $this->seek($s);
425
-
426
- if (count($values) == 0) return false;
427
-
428
- $value = $this->compressList($values, ', ');
429
- return true;
430
- }
431
-
432
- // a single value
433
- function value(&$value) {
434
- // try a unit
435
- if ($this->unit($value)) return true;
436
-
437
- // see if there is a negation
438
- $s = $this->seek();
439
- if ($this->literal('-', false) && $this->variable($vname)) {
440
- $value = array('negative', array('variable', $this->vPrefix.$vname));
441
- return true;
442
- } else {
443
- $this->seek($s);
444
- }
445
-
446
- // accessor
447
- // must be done before color
448
- // this needs negation too
449
- if ($this->accessor($a)) {
450
- $a[1] = $this->fixTags($a[1]);
451
- $value = $a;
452
- return true;
453
- }
454
-
455
- // color
456
- if ($this->color($value)) return true;
457
-
458
- // css function
459
- // must be done after color
460
- if ($this->func($value)) return true;
461
-
462
- // string
463
- if ($this->string($tmp, $d)) {
464
- $value = array('string', $d.$tmp.$d);
465
- return true;
466
- }
467
-
468
- // try a keyword
469
- if ($this->keyword($word)) {
470
- $value = array('keyword', $word);
471
- return true;
472
- }
473
-
474
- // try a variable
475
- if ($this->variable($vname)) {
476
- $value = array('variable', $this->vPrefix.$vname);
477
- return true;
478
- }
479
-
480
- // unquote string
481
- if ($this->literal("~") && $this->string($value, $d)) {
482
- $value = array("keyword", $value);
483
- return true;
484
- } else {
485
- $this->seek($s);
486
- }
487
-
488
- // css hack: \0
489
- if ($this->literal('\\') && $this->match('([0-9]+)', $m)) {
490
- $value = array('keyword', '\\'.$m[1]);
491
- return true;
492
- } else {
493
- $this->seek($s);
494
- }
495
-
496
- return false;
497
- }
498
-
499
- // an import statement
500
- function import(&$url, &$media) {
501
- $s = $this->seek();
502
- if (!$this->literal('@import')) return false;
503
-
504
- // @import "something.css" media;
505
- // @import url("something.css") media;
506
- // @import url(something.css) media;
507
-
508
- if ($this->literal('url(')) $parens = true; else $parens = false;
509
-
510
- if (!$this->string($url)) {
511
- if ($parens && $this->to(')', $url)) {
512
- $parens = false; // got em
513
- } else {
514
- $this->seek($s);
515
- return false;
516
- }
517
- }
518
-
519
- if ($parens && !$this->literal(')')) {
520
- $this->seek($s);
521
- return false;
522
- }
523
-
524
- // now the rest is media
525
- return $this->to(';', $media, false, true);
526
- }
527
-
528
- // a list of media types, very lenient
529
- function mediaTypes(&$types) {
530
- if ($this->to('{', $rest, true, true)) {
531
- $types = trim($rest);
532
- return true;
533
- }
534
-
535
- return false;
536
- }
537
-
538
- // a scoped value accessor
539
- // .hello > @scope1 > @scope2['value'];
540
- function accessor(&$var) {
541
- $s = $this->seek();
542
-
543
- if (!$this->tags($scope, true, '>') || !$this->literal('[')) {
544
- $this->seek($s);
545
- return false;
546
- }
547
-
548
- // either it is a variable or a property
549
- // why is a property wrapped in quotes, who knows!
550
- if ($this->variable($name)) {
551
- $name = $this->vPrefix.$name;
552
- } elseif ($this->literal("'") && $this->keyword($name) && $this->literal("'")) {
553
- // .. $this->count is messed up if we wanted to test another access type
554
- } else {
555
- $this->seek($s);
556
- return false;
557
- }
558
-
559
- if (!$this->literal(']')) {
560
- $this->seek($s);
561
- return false;
562
- }
563
-
564
- $var = array('lookup', $scope, $name);
565
- return true;
566
- }
567
-
568
- // a string
569
- function string(&$string, &$d = null) {
570
- $s = $this->seek();
571
- if ($this->literal('"', false)) {
572
- $delim = '"';
573
- } elseif ($this->literal("'", false)) {
574
- $delim = "'";
575
- } else {
576
- return false;
577
- }
578
-
579
- if (!$this->to($delim, $string)) {
580
- $this->seek($s);
581
- return false;
582
- }
583
-
584
- $d = $delim;
585
- return true;
586
- }
587
-
588
- /**
589
- * Consume a number and optionally a unit.
590
- * Can also consume a font shorthand if it is a simple case.
591
- * $allowed restricts the types that are matched.
592
- */
593
- function unit(&$unit, $allowed = null) {
594
- $simpleCase = $allowed == null;
595
- if (!$allowed) $allowed = self::$units;
596
-
597
- if ($this->match('(-?[0-9]*(\.)?[0-9]+)('.implode('|', $allowed).')?', $m, !$simpleCase)) {
598
- if (!isset($m[3])) $m[3] = 'number';
599
- $unit = array($m[3], $m[1]);
600
-
601
- // check for size/height font unit.. should this even be here?
602
- if ($simpleCase) {
603
- $s = $this->seek();
604
- if (!$this->inExp && $this->literal('/', false) && $this->unit($right, self::$units)) {
605
- $unit = array('keyword', $this->compileValue($unit).'/'.$this->compileValue($right));
606
- } else {
607
- // get rid of whitespace
608
- $this->seek($s);
609
- $this->match('', $_);
610
- }
611
- }
612
-
613
- return true;
614
- }
615
-
616
- return false;
617
- }
618
-
619
- // a # color
620
- function color(&$out) {
621
- $color = array('color');
622
-
623
- if ($this->match('(#([0-9a-f]{6})|#([0-9a-f]{3}))', $m)) {
624
- if (isset($m[3])) {
625
- $num = $m[3];
626
- $width = 16;
627
- } else {
628
- $num = $m[2];
629
- $width = 256;
630
- }
631
-
632
- $num = hexdec($num);
633
- foreach (array(3,2,1) as $i) {
634
- $t = $num % $width;
635
- $num /= $width;
636
-
637
- $color[$i] = $t * (256/$width) + $t * floor(16/$width);
638
- }
639
-
640
- $out = $color;
641
- return true;
642
- }
643
-
644
- return false;
645
- }
646
-
647
- // consume a list of property values delimited by ; and wrapped in ()
648
- function argumentValues(&$args, $delim = ',') {
649
- $s = $this->seek();
650
- if (!$this->literal('(')) return false;
651
-
652
- $values = array();
653
- while (true) {
654
- if ($this->expressionList($value)) $values[] = $value;
655
- if (!$this->literal($delim)) break;
656
- else {
657
- if ($value == null) $values[] = null;
658
- $value = null;
659
- }
660
- }
661
-
662
- if (!$this->literal(')')) {
663
- $this->seek($s);
664
- return false;
665
- }
666
-
667
- $args = $values;
668
- return true;
669
- }
670
-
671
- // consume an argument definition list surrounded by ()
672
- // each argument is a variable name with optional value
673
- function argumentDef(&$args, $delim = ',') {
674
- $s = $this->seek();
675
- if (!$this->literal('(')) return false;
676
-
677
- $values = array();
678
- while ($this->variable($vname)) {
679
- $arg = array($vname);
680
- if ($this->assign() && $this->expressionList($value)) {
681
- $arg[] = $value;
682
- // let the : slide if there is no value
683
- }
684
-
685
- $values[] = $arg;
686
- if (!$this->literal($delim)) break;
687
- }
688
-
689
- if (!$this->literal(')')) {
690
- $this->seek($s);
691
- return false;
692
- }
693
-
694
- $args = $values;
695
- return true;
696
- }
697
-
698
- // consume a list of tags
699
- // this accepts a hanging delimiter
700
- function tags(&$tags, $simple = false, $delim = ',') {
701
- $tags = array();
702
- while ($this->tag($tt, $simple)) {
703
- $tags[] = $tt;
704
- if (!$this->literal($delim)) break;
705
- }
706
- if (count($tags) == 0) return false;
707
-
708
- return true;
709
- }
710
-
711
- // list of tags of specifying mixin path
712
- // optionally separated by > (lazy, accepts extra >)
713
- function mixinTags(&$tags) {
714
- $s = $this->seek();
715
- $tags = array();
716
- while ($this->tag($tt, true)) {
717
- $tags[] = $tt;
718
- $this->literal(">");
719
- }
720
-
721
- if (count($tags) == 0) return false;
722
-
723
- return true;
724
- }
725
-
726
- // a bracketed value (contained within in a tag definition)
727
- function tagBracket(&$value) {
728
- $s = $this->seek();
729
- if ($this->literal('[') && $this->to(']', $c, true) && $this->literal(']', false)) {
730
- $value = '['.$c.']';
731
- // whitespace?
732
- if ($this->match('', $_)) $value .= $_[0];
733
-
734
- // escape parent selector
735
- $value = str_replace($this->parentSelector, "&&", $value);
736
- return true;
737
- }
738
-
739
- $this->seek($s);
740
- return false;
741
- }
742
-
743
- // a single tag
744
- function tag(&$tag, $simple = false) {
745
- if ($simple)
746
- $chars = '^,:;{}\][>\(\) "\'';
747
- else
748
- $chars = '^,;{}["\'';
749
-
750
- $tag = '';
751
- while ($this->tagBracket($first)) $tag .= $first;
752
- while ($this->match('(['.$chars.'0-9]['.$chars.']*)', $m)) {
753
- $tag .= $m[1];
754
- if ($simple) break;
755
-
756
- while ($this->tagBracket($brack)) $tag .= $brack;
757
- }
758
- $tag = trim($tag);
759
- if ($tag == '') return false;
760
-
761
- return true;
762
- }
763
-
764
- // a css function
765
- function func(&$func) {
766
- $s = $this->seek();
767
-
768
- if ($this->match('(%|[\w\-_][\w\-_:\.]*)', $m) && $this->literal('(')) {
769
- $fname = $m[1];
770
- if ($fname == 'url') {
771
- $this->to(')', $content, true);
772
- $args = array('string', $content);
773
- } else {
774
- $args = array();
775
- while (true) {
776
- $ss = $this->seek();
777
- if ($this->keyword($name) && $this->literal('=') && $this->expressionList($value)) {
778
- $args[] = array('list', '=', array(array('keyword', $name), $value));
779
- } else {
780
- $this->seek($ss);
781
- if ($this->expressionList($value)) {
782
- $args[] = $value;
783
- }
784
- }
785
-
786
- if (!$this->literal(',')) break;
787
- }
788
- $args = array('list', ',', $args);
789
- }
790
-
791
- if ($this->literal(')')) {
792
- $func = array('function', $fname, $args);
793
- return true;
794
- }
795
- }
796
-
797
- $this->seek($s);
798
- return false;
799
- }
800
-
801
- // consume a less variable
802
- function variable(&$name) {
803
- $s = $this->seek();
804
- if ($this->literal($this->vPrefix, false) && $this->keyword($name)) {
805
- return true;
806
- }
807
-
808
- return false;
809
- }
810
-
811
- /**
812
- * Consume an assignment operator
813
- * Can optionally take a name that will be set to the current property name
814
- */
815
- function assign($name = null) {
816
- if ($name) $this->currentProperty = $name;
817
- return $this->literal(':') || $this->literal('=');
818
- }
819
-
820
- // consume a keyword
821
- function keyword(&$word) {
822
- if ($this->match('([\w_\-\*!"][\w\-_"]*)', $m)) {
823
- $word = $m[1];
824
- return true;
825
- }
826
- return false;
827
- }
828
-
829
- // consume an end of statement delimiter
830
- function end() {
831
- if ($this->literal(';'))
832
- return true;
833
- elseif ($this->count == strlen($this->buffer) || $this->buffer{$this->count} == '}') {
834
- // if there is end of file or a closing block next then we don't need a ;
835
- return true;
836
- }
837
- return false;
838
- }
839
-
840
- function compressList($items, $delim) {
841
- if (count($items) == 1) return $items[0];
842
- else return array('list', $delim, $items);
843
- }
844
-
845
- // just do a shallow propety merge, seems to be what lessjs does
846
- function mergeBlock($target, $from) {
847
- $target = clone $target;
848
- $target->props = array_merge($target->props, $from->props);
849
- return $target;
850
- }
851
-
852
- /**
853
- * Recursively compiles a block.
854
- * @param $block the block
855
- * @param $parentTags the tags of the block that contained this one
856
- *
857
- * A block is analogous to a CSS block in most cases. A single less document
858
- * is encapsulated in a block when parsed, but it does not have parent tags
859
- * so all of it's children appear on the root level when compiled.
860
- *
861
- * Blocks are made up of props and children.
862
- *
863
- * Props are property instructions, array tuples which describe an action
864
- * to be taken, eg. write a property, set a variable, mixin a block.
865
- *
866
- * The children of a block are just all the blocks that are defined within.
867
- *
868
- * Compiling the block involves pushing a fresh environment on the stack,
869
- * and iterating through the props, compiling each one.
870
- *
871
- * See lessc::compileProp()
872
- *
873
- */
874
- function compileBlock($block, $parent_tags = null) {
875
- $isRoot = $parent_tags == null && $block->tags == null;
876
-
877
- $indent = str_repeat($this->indentChar, $this->indentLevel);
878
-
879
- if (!empty($block->no_multiply)) {
880
- $special_block = true;
881
- $this->indentLevel++;
882
- $tags = array();
883
- } else {
884
- $special_block = false;
885
- $tags = $this->multiplyTags($parent_tags, $block->tags);
886
- }
887
-
888
- $this->pushEnv();
889
- $lines = array();
890
- $blocks = array();
891
- foreach ($block->props as $prop) {
892
- $this->compileProp($prop, $block, $tags, $lines, $blocks);
893
- }
894
-
895
- $this->pop();
896
-
897
- $nl = $isRoot ? "\n".$indent :
898
- "\n".$indent.$this->indentChar;
899
-
900
- ob_start();
901
-
902
- if ($special_block) {
903
- $this->indentLevel--;
904
- if (isset($block->media)) {
905
- echo "@media ".$block->media;
906
- } elseif (isset($block->keyframes)) {
907
- echo $block->tags[0]." ".
908
- $this->compileValue($this->reduce($block->keyframes));
909
- } else {
910
- list($name) = $block->tags;
911
- echo $indent.$name;
912
- }
913
-
914
- echo ' {'.(count($lines) > 0 ? $nl : "\n");
915
- }
916
-
917
- // dump it
918
- if (count($lines) > 0) {
919
- if (!$special_block && !$isRoot) {
920
- echo $indent.implode(", ", $tags);
921
- if (count($lines) > 1) echo " {".$nl;
922
- else echo " { ";
923
- }
924
-
925
- echo implode($nl, $lines);
926
-
927
- if (!$special_block && !$isRoot) {
928
- if (count($lines) > 1) echo "\n".$indent."}\n";
929
- else echo " }\n";
930
- } else echo "\n";
931
- }
932
-
933
- foreach ($blocks as $b) echo $b;
934
-
935
- if ($special_block) {
936
- echo $indent."}\n";
937
- }
938
-
939
- return ob_get_clean();
940
- }
941
-
942
-
943
- // find the fully qualified tags for a block and its parent's tags
944
- function multiplyTags($parents, $current) {
945
- if ($parents == null) return $current;
946
-
947
- $tags = array();
948
- foreach ($parents as $ptag) {
949
- foreach ($current as $tag) {
950
- // inject parent in place of parent selector, ignoring escaped valuews
951
- $count = 0;
952
- $parts = explode("&&", $tag);
953
-
954
- foreach ($parts as $i => $chunk) {
955
- $parts[$i] = str_replace($this->parentSelector, $ptag, $chunk, $c);
956
- $count += $c;
957
- }
958
-
959
- $tag = implode("&", $parts);
960
-
961
- if ($count > 0) {
962
- $tags[] = trim($tag);
963
- } else {
964
- $tags[] = trim($ptag . ' ' . $tag);
965
- }
966
- }
967
- }
968
-
969
- return $tags;
970
- }
971
-
972
- // attempt to find block pointed at by path within search_in or its parent
973
- function findBlock($search_in, $path, $seen=array()) {
974
- if ($search_in == null) return null;
975
- if (isset($seen[$search_in->id])) return null;
976
- $seen[$search_in->id] = true;
977
-
978
- $name = $path[0];
979
-
980
- if (isset($search_in->children[$name])) {
981
- $block = $search_in->children[$name];
982
- if (count($path) == 1) {
983
- return $block;
984
- } else {
985
- return $this->findBlock($block, array_slice($path, 1), $seen);
986
- }
987
- } else {
988
- if ($search_in->parent === $search_in) return null;
989
- return $this->findBlock($search_in->parent, $path, $seen);
990
- }
991
- }
992
-
993
- // sets all argument names in $args to either the default value
994
- // or the one passed in through $values
995
- function zipSetArgs($args, $values) {
996
- $i = 0;
997
- $assigned_values = array();
998
- foreach ($args as $a) {
999
- if ($i < count($values) && !is_null($values[$i])) {
1000
- $value = $values[$i];
1001
- } elseif (isset($a[1])) {
1002
- $value = $a[1];
1003
- } else $value = null;
1004
-
1005
- $value = $this->reduce($value);
1006
- $this->set($this->vPrefix.$a[0], $value);
1007
- $assigned_values[] = $value;
1008
- $i++;
1009
- }
1010
-
1011
- // copy over any extra default args
1012
- for ($i = count($values); $i < count($assigned_values); $i++) {
1013
- $values[] = $assigned_values[$i];
1014
- }
1015
-
1016
- $this->env->arguments = $values;
1017
- }
1018
-
1019
- // compile a prop and update $lines or $blocks appropriately
1020
- function compileProp($prop, $block, $tags, &$_lines, &$_blocks) {
1021
- switch ($prop[0]) {
1022
- case 'assign':
1023
- list(, $name, $value) = $prop;
1024
- if ($name[0] == $this->vPrefix) {
1025
- $this->set($name, $this->reduce($value));
1026
- } else {
1027
- $_lines[] = "$name:".
1028
- $this->compileValue($this->reduce($value)).";";
1029
- }
1030
- break;
1031
- case 'block':
1032
- list(, $child) = $prop;
1033
- $_blocks[] = $this->compileBlock($child, $tags);
1034
- break;
1035
- case 'mixin':
1036
- list(, $path, $args) = $prop;
1037
-
1038
- $mixin = $this->findBlock($block, $path);
1039
- if (is_null($mixin)) {
1040
- // echo "failed to find block: ".implode(" > ", $path)."\n";
1041
- break; // throw error here??
1042
- }
1043
-
1044
- $have_args = false;
1045
- if (isset($mixin->args)) {
1046
- $have_args = true;
1047
- $this->pushEnv();
1048
- $this->zipSetArgs($mixin->args, $args);
1049
- }
1050
-
1051
- $old_parent = $mixin->parent;
1052
- $mixin->parent = $block;
1053
-
1054
- foreach ($mixin->props as $sub_prop) {
1055
- $this->compileProp($sub_prop, $mixin, $tags, $_lines, $_blocks);
1056
- }
1057
-
1058
- $mixin->parent = $old_parent;
1059
-
1060
- if ($have_args) $this->pop();
1061
-
1062
- break;
1063
- case 'raw':
1064
- $_lines[] = $prop[1];
1065
- break;
1066
- case 'import':
1067
- list(, $path) = $prop;
1068
- $this->addParsedFile($path);
1069
- $root = $this->createChild($path)->parseTree();
1070
-
1071
- $root->parent = $block;
1072
- foreach ($root->props as $sub_prop) {
1073
- $this->compileProp($sub_prop, $root, $tags, $_lines, $_blocks);
1074
- }
1075
-
1076
- // inject imported blocks into this block, local will overwrite import
1077
- $block->children = array_merge($root->children, $block->children);
1078
- break;
1079
- case 'charset':
1080
- list(, $value) = $prop;
1081
- $_lines[] = '@charset '.$this->compileValue($this->reduce($value)).';';
1082
- break;
1083
- default:
1084
- echo "unknown op: {$prop[0]}\n";
1085
- throw new exception();
1086
- }
1087
- }
1088
-
1089
-
1090
- /**
1091
- * Compiles a primitive value into a CSS property value.
1092
- *
1093
- * Values in lessphp are typed by being wrapped in arrays, their format is
1094
- * typically:
1095
- *
1096
- * array(type, contents [, additional_contents]*)
1097
- *
1098
- * Will not work on non reduced values (expressions, variables, etc)
1099
- */
1100
- function compileValue($value) {
1101
- switch ($value[0]) {
1102
- case 'list':
1103
- // [1] - delimiter
1104
- // [2] - array of values
1105
- return implode($value[1], array_map(array($this, 'compileValue'), $value[2]));
1106
- case 'keyword':
1107
- // [1] - the keyword
1108
- case 'number':
1109
- // [1] - the number
1110
- return $value[1];
1111
- case 'string':
1112
- // [1] - contents of string (includes quotes)
1113
-
1114
- // search for inline variables to replace
1115
- $replace = array();
1116
- if (preg_match_all('/'.$this->preg_quote($this->vPrefix).'\{([\w-_][0-9\w-_]*)\}/', $value[1], $m)) {
1117
- foreach ($m[1] as $name) {
1118
- if (!isset($replace[$name]))
1119
- $replace[$name] = $this->compileValue($this->reduce(array('variable', $this->vPrefix . $name)));
1120
- }
1121
- }
1122
-
1123
- foreach ($replace as $var=>$val) {
1124
- if ($this->quoted($val)) {
1125
- $val = substr($val, 1, -1);
1126
- }
1127
- $value[1] = str_replace($this->vPrefix. '{'.$var.'}', $val, $value[1]);
1128
- }
1129
-
1130
- return $value[1];
1131
- case 'color':
1132
- // [1] - red component (either number for a %)
1133
- // [2] - green component
1134
- // [3] - blue component
1135
- // [4] - optional alpha component
1136
- if (count($value) == 5) { // rgba
1137
- return 'rgba('.$value[1].','.$value[2].','.$value[3].','.$value[4].')';
1138
- }
1139
- return sprintf("#%02x%02x%02x", $value[1], $value[2], $value[3]);
1140
- case 'function':
1141
- // [1] - function name
1142
- // [2] - some value representing arguments
1143
-
1144
- // see if function evaluates to something else
1145
- $value = $this->reduce($value);
1146
- if ($value[0] == 'function') {
1147
- return $value[1].'('.$this->compileValue($value[2]).')';
1148
- }
1149
- else return $this->compileValue($value);
1150
- default: // assumed to be unit
1151
- return $value[1].$value[0];
1152
- }
1153
- }
1154
-
1155
- function lib_rgbahex($color) {
1156
- if ($color[0] != 'color')
1157
- throw new exception("color expected for rgbahex");
1158
-
1159
- return sprintf("#%02x%02x%02x%02x",
1160
- isset($color[4]) ? $color[4]*255 : 0,
1161
- $color[1],$color[2], $color[3]);
1162
- }
1163
-
1164
- // utility func to unquote a string
1165
- function lib_e($arg) {
1166
- switch ($arg[0]) {
1167
- case "list":
1168
- $items = $arg[2];
1169
- if (isset($items[0])) {
1170
- return $this->lib_e($items[0]);
1171
- }
1172
- return "";
1173
- case "string":
1174
- $str = $this->compileValue($arg);
1175
- return substr($str, 1, -1);
1176
- default:
1177
- return $this->compileValue($arg);
1178
- }
1179
- }
1180
-
1181
- function lib__sprintf($args) {
1182
- if ($args[0] != "list") return $args;
1183
- $values = $args[2];
1184
- $source = $this->reduce(array_shift($values));
1185
- if ($source[0] != "string") {
1186
- return $source;
1187
- }
1188
-
1189
- $str = $source[1];
1190
- $i = 0;
1191
- if (preg_match_all('/%[dsa]/', $str, $m)) {
1192
- foreach ($m[0] as $match) {
1193
- $val = isset($values[$i]) ? $this->reduce($values[$i]) : array('keyword', '');
1194
- $i++;
1195
- switch ($match[1]) {
1196
- case "s":
1197
- if ($val[0] == "string") {
1198
- $rep = substr($val[1], 1, -1);
1199
- break;
1200
- }
1201
- default:
1202
- $rep = $this->compileValue($val);
1203
- }
1204
- $str = preg_replace('/'.$this->preg_quote($match).'/', $rep, $str, 1);
1205
- }
1206
- }
1207
-
1208
- return array('string', $str);
1209
- }
1210
-
1211
- function lib_floor($arg) {
1212
- return floor($arg[1]);
1213
- }
1214
-
1215
- function lib_round($arg) {
1216
- return round($arg[1]);
1217
- }
1218
-
1219
- // is a string surrounded in quotes? returns the quoting char if true
1220
- function quoted($s) {
1221
- if (preg_match('/^("|\').*?\1$/', $s, $m))
1222
- return $m[1];
1223
- else return false;
1224
- }
1225
-
1226
- /**
1227
- * Helper function to get argurments for color functions
1228
- * accepts invalid input, non colors interpreted to black
1229
- */
1230
- function colorArgs($args) {
1231
- if ($args[0] != 'list' || count($args[2]) < 2) {
1232
- return array(array('color', 0, 0, 0));
1233
- }
1234
- list($color, $delta) = $args[2];
1235
- $color = $this->coerceColor($color);
1236
- if (is_null($color))
1237
- $color = array('color', 0, 0, 0);
1238
-
1239
- $delta = floatval($delta[1]);
1240
-
1241
- return array($color, $delta);
1242
- }
1243
-
1244
- function lib_darken($args) {
1245
- list($color, $delta) = $this->colorArgs($args);
1246
-
1247
- $hsl = $this->toHSL($color);
1248
- $hsl[3] = $this->clamp($hsl[3] - $delta, 100);
1249
- return $this->toRGB($hsl);
1250
- }
1251
-
1252
- function lib_lighten($args) {
1253
- list($color, $delta) = $this->colorArgs($args);
1254
-
1255
- $hsl = $this->toHSL($color);
1256
- $hsl[3] = $this->clamp($hsl[3] + $delta, 100);
1257
- return $this->toRGB($hsl);
1258
- }
1259
-
1260
- function lib_saturate($args) {
1261
- list($color, $delta) = $this->colorArgs($args);
1262
-
1263
- $hsl = $this->toHSL($color);
1264
- $hsl[2] = $this->clamp($hsl[2] + $delta, 100);
1265
- return $this->toRGB($hsl);
1266
- }
1267
-
1268
- function lib_desaturate($args) {
1269
- list($color, $delta) = $this->colorArgs($args);
1270
-
1271
- $hsl = $this->toHSL($color);
1272
- $hsl[2] = $this->clamp($hsl[2] - $delta, 100);
1273
- return $this->toRGB($hsl);
1274
- }
1275
-
1276
- function lib_spin($args) {
1277
- list($color, $delta) = $this->colorArgs($args);
1278
-
1279
- $hsl = $this->toHSL($color);
1280
- $hsl[1] = $this->clamp($hsl[1] + $delta, 360);
1281
- return $this->toRGB($hsl);
1282
- }
1283
-
1284
- function lib_fadeout($args) {
1285
- list($color, $delta) = $this->colorArgs($args);
1286
- $color[4] = $this->clamp((isset($color[4]) ? $color[4] : 1) - $delta/100);
1287
- return $color;
1288
- }
1289
-
1290
- function lib_fadein($args) {
1291
- list($color, $delta) = $this->colorArgs($args);
1292
- $color[4] = $this->clamp((isset($color[4]) ? $color[4] : 1) + $delta/100);
1293
- return $color;
1294
- }
1295
-
1296
- function lib_hue($color) {
1297
- if ($color[0] != 'color') return 0;
1298
- $hsl = $this->toHSL($color);
1299
- return round($hsl[1]);
1300
- }
1301
-
1302
- function lib_saturation($color) {
1303
- if ($color[0] != 'color') return 0;
1304
- $hsl = $this->toHSL($color);
1305
- return round($hsl[2]);
1306
- }
1307
-
1308
- function lib_lightness($color) {
1309
- if ($color[0] != 'color') return 0;
1310
- $hsl = $this->toHSL($color);
1311
- return round($hsl[3]);
1312
- }
1313
-
1314
- // get the alpha of a color
1315
- // defaults to 1 for non-colors or colors without an alpha
1316
- function lib_alpha($color) {
1317
- if ($color[0] != 'color') return 1;
1318
- return isset($color[4]) ? $color[4] : 1;
1319
- }
1320
-
1321
- // set the alpha of the color
1322
- function lib_fade($args) {
1323
- list($color, $alpha) = $this->colorArgs($args);
1324
- $color[4] = $this->clamp($alpha / 100.0);
1325
- return $color;
1326
- }
1327
-
1328
- function lib_percentage($number) {
1329
- return array('%', $number[1]*100);
1330
- }
1331
-
1332
- // mixes two colors by weight
1333
- // mix(@color1, @color2, @weight);
1334
- // http://sass-lang.com/docs/yardoc/Sass/Script/Functions.html#mix-instance_method
1335
- function lib_mix($args) {
1336
- if ($args[0] != "list")
1337
- throw new exception("mix expects (color1, color2, weight)");
1338
-
1339
- list($first, $second, $weight) = $args[2];
1340
- $first = $this->assertColor($first);
1341
- $second = $this->assertColor($second);
1342
-
1343
- $first_a = $this->lib_alpha($first);
1344
- $second_a = $this->lib_alpha($second);
1345
- $weight = $weight[1] / 100.0;
1346
-
1347
- $w = $weight * 2 - 1;
1348
- $a = $first_a - $second_a;
1349
-
1350
- $w1 = (($w * $a == -1 ? $w : ($w + $a)/(1 + $w * $a)) + 1) / 2.0;
1351
- $w2 = 1.0 - $w1;
1352
-
1353
- $new = array('color',
1354
- $w1 * $first[1] + $w2 * $second[1],
1355
- $w1 * $first[2] + $w2 * $second[2],
1356
- $w1 * $first[3] + $w2 * $second[3],
1357
- );
1358
-
1359
- if ($first_a != 1.0 || $second_a != 1.0) {
1360
- $new[] = $first_a * $p + $second_a * ($p - 1);
1361
- }
1362
-
1363
- return $this->fixColor($new);
1364
- }
1365
-
1366
- function assertColor($value, $error = "expected color value") {
1367
- $color = $this->coerceColor($value);
1368
- if (is_null($color)) throw new exception($error);
1369
- return $color;
1370
- }
1371
-
1372
- function toHSL($color) {
1373
- if ($color[0] == 'hsl') return $color;
1374
-
1375
- $r = $color[1] / 255;
1376
- $g = $color[2] / 255;
1377
- $b = $color[3] / 255;
1378
-
1379
- $min = min($r, $g, $b);
1380
- $max = max($r, $g, $b);
1381
-
1382
- $L = ($min + $max) / 2;
1383
- if ($min == $max) {
1384
- $S = $H = 0;
1385
- } else {
1386
- if ($L < 0.5)
1387
- $S = ($max - $min)/($max + $min);
1388
- else
1389
- $S = ($max - $min)/(2.0 - $max - $min);
1390
-
1391
- if ($r == $max) $H = ($g - $b)/($max - $min);
1392
- elseif ($g == $max) $H = 2.0 + ($b - $r)/($max - $min);
1393
- elseif ($b == $max) $H = 4.0 + ($r - $g)/($max - $min);
1394
-
1395
- }
1396
-
1397
- $out = array('hsl',
1398
- ($H < 0 ? $H + 6 : $H)*60,
1399
- $S*100,
1400
- $L*100,
1401
- );
1402
-
1403
- if (count($color) > 4) $out[] = $color[4]; // copy alpha
1404
- return $out;
1405
- }
1406
-
1407
- function toRGB_helper($comp, $temp1, $temp2) {
1408
- if ($comp < 0) $comp += 1.0;
1409
- elseif ($comp > 1) $comp -= 1.0;
1410
-
1411
- if (6 * $comp < 1) return $temp1 + ($temp2 - $temp1) * 6 * $comp;
1412
- if (2 * $comp < 1) return $temp2;
1413
- if (3 * $comp < 2) return $temp1 + ($temp2 - $temp1)*((2/3) - $comp) * 6;
1414
-
1415
- return $temp1;
1416
- }
1417
-
1418
- /**
1419
- * Converts an hsl array into a color value in rgb.
1420
- * Expects H to be in range of 0 to 360, S and L in 0 to 100
1421
- */
1422
- function toRGB($color) {
1423
- if ($color == 'color') return $color;
1424
-
1425
- $H = $color[1] / 360;
1426
- $S = $color[2] / 100;
1427
- $L = $color[3] / 100;
1428
-
1429
- if ($S == 0) {
1430
- $r = $g = $b = $L;
1431
- } else {
1432
- $temp2 = $L < 0.5 ?
1433
- $L*(1.0 + $S) :
1434
- $L + $S - $L * $S;
1435
-
1436
- $temp1 = 2.0 * $L - $temp2;
1437
-
1438
- $r = $this->toRGB_helper($H + 1/3, $temp1, $temp2);
1439
- $g = $this->toRGB_helper($H, $temp1, $temp2);
1440
- $b = $this->toRGB_helper($H - 1/3, $temp1, $temp2);
1441
- }
1442
-
1443
- $out = array('color', round($r*255), round($g*255), round($b*255));
1444
- if (count($color) > 4) $out[] = $color[4]; // copy alpha
1445
- return $out;
1446
- }
1447
-
1448
- function clamp($v, $max = 1, $min = 0) {
1449
- return min($max, max($min, $v));
1450
- }
1451
-
1452
- /**
1453
- * Convert the rgb, rgba, hsl color literals of function type
1454
- * as returned by the parser into values of color type.
1455
- */
1456
- function funcToColor($func) {
1457
- $fname = $func[1];
1458
- if ($func[2][0] != 'list') return false; // need a list of arguments
1459
- $rawComponents = $func[2][2];
1460
-
1461
- if ($fname == 'hsl' || $fname == 'hsla') {
1462
- $hsl = array('hsl');
1463
- $i = 0;
1464
- foreach ($rawComponents as $c) {
1465
- $val = $this->reduce($c);
1466
- $val = isset($val[1]) ? floatval($val[1]) : 0;
1467
-
1468
- if ($i == 0) $clamp = 360;
1469
- elseif ($i < 4) $clamp = 100;
1470
- else $clamp = 1;
1471
-
1472
- $hsl[] = $this->clamp($val, $clamp);
1473
- $i++;
1474
- }
1475
-
1476
- while (count($hsl) < 4) $hsl[] = 0;
1477
- return $this->toRGB($hsl);
1478
-
1479
- } elseif ($fname == 'rgb' || $fname == 'rgba') {
1480
- $components = array();
1481
- $i = 1;
1482
- foreach ($rawComponents as $c) {
1483
- $c = $this->reduce($c);
1484
- if ($i < 4) {
1485
- if ($c[0] == '%') $components[] = 255 * ($c[1] / 100);
1486
- else $components[] = floatval($c[1]);
1487
- } elseif ($i == 4) {
1488
- if ($c[0] == '%') $components[] = 1.0 * ($c[1] / 100);
1489
- else $components[] = floatval($c[1]);
1490
- } else break;
1491
-
1492
- $i++;
1493
- }
1494
- while (count($components) < 3) $components[] = 0;
1495
- array_unshift($components, 'color');
1496
- return $this->fixColor($components);
1497
- }
1498
-
1499
- return false;
1500
- }
1501
-
1502
- // reduce a delayed type to its final value
1503
- // dereference variables and solve equations
1504
- function reduce($var, $defaultValue = array('number', 0)) {
1505
- while (in_array($var[0], self::$dtypes)) {
1506
- if ($var[0] == 'list') {
1507
- foreach ($var[2] as &$value) $value = $this->reduce($value);
1508
- break;
1509
- } elseif ($var[0] == 'expression') {
1510
- $var = $this->evaluate($var[1], $var[2], $var[3]);
1511
- } elseif ($var[0] == 'variable') {
1512
- $var = $this->get($var[1]);
1513
- } elseif ($var[0] == 'lookup') {
1514
- // do accessor here....
1515
- $var = array('number', 0);
1516
- } elseif ($var[0] == 'function') {
1517
- $color = $this->funcToColor($var);
1518
- if ($color) $var = $color;
1519
- else {
1520
- list($_, $name, $args) = $var;
1521
- if ($name == "%") $name = "_sprintf";
1522
- $f = isset($this->libFunctions[$name]) ?
1523
- $this->libFunctions[$name] : array($this, 'lib_'.$name);
1524
-
1525
- if (is_callable($f)) {
1526
- if ($args[0] == 'list')
1527
- $args = $this->compressList($args[2], $args[1]);
1528
-
1529
- $var = call_user_func($f, $this->reduce($args));
1530
-
1531
- // convet to a typed value if the result is a php primitive
1532
- if (is_numeric($var)) $var = array('number', $var);
1533
- elseif (!is_array($var)) $var = array('keyword', $var);
1534
- } else {
1535
- // plain function, reduce args
1536
- $var[2] = $this->reduce($var[2]);
1537
- }
1538
- }
1539
- break; // done reducing after a function
1540
- } elseif ($var[0] == 'negative') {
1541
- $value = $this->reduce($var[1]);
1542
- if (is_numeric($value[1])) {
1543
- $value[1] = -1*$value[1];
1544
- }
1545
- $var = $value;
1546
- }
1547
- }
1548
-
1549
- return $var;
1550
- }
1551
-
1552
- function coerceColor($value) {
1553
- switch($value[0]) {
1554
- case 'color': return $value;
1555
- case 'keyword':
1556
- $name = $value[1];
1557
- if (isset(self::$cssColors[$name])) {
1558
- list($r, $g, $b) = explode(',', self::$cssColors[$name]);
1559
- return array('color', $r, $g, $b);
1560
- }
1561
- return null;
1562
- }
1563
- }
1564
-
1565
- // evaluate an expression
1566
- function evaluate($op, $left, $right) {
1567
- $left = $this->reduce($left);
1568
- $right = $this->reduce($right);
1569
-
1570
- if ($left_color = $this->coerceColor($left)) {
1571
- $left = $left_color;
1572
- }
1573
-
1574
- if ($right_color = $this->coerceColor($right)) {
1575
- $right = $right_color;
1576
- }
1577
-
1578
- if ($left[0] == 'color' && $right[0] == 'color') {
1579
- $out = $this->op_color_color($op, $left, $right);
1580
- return $out;
1581
- }
1582
-
1583
- if ($left[0] == 'color') {
1584
- return $this->op_color_number($op, $left, $right);
1585
- }
1586
-
1587
- if ($right[0] == 'color') {
1588
- return $this->op_number_color($op, $left, $right);
1589
- }
1590
-
1591
- // concatenate strings
1592
- if ($op == '+' && $left[0] == 'string') {
1593
- $append = $this->compileValue($right);
1594
- if ($this->quoted($append)) $append = substr($append, 1, -1);
1595
-
1596
- $lhs = $this->compileValue($left);
1597
- if ($q = $this->quoted($lhs)) $lhs = substr($lhs, 1, -1);
1598
- if (!$q) $q = '';
1599
-
1600
- return array('string', $q.$lhs.$append.$q);
1601
- }
1602
-
1603
- if ($left[0] == 'keyword' || $right[0] == 'keyword' ||
1604
- $left[0] == 'string' || $right[0] == 'string')
1605
- {
1606
- // look for negative op
1607
- if ($op == '-') $right[1] = '-'.$right[1];
1608
- return array('keyword', $this->compileValue($left) .' '. $this->compileValue($right));
1609
- }
1610
-
1611
- // default to number operation
1612
- return $this->op_number_number($op, $left, $right);
1613
- }
1614
-
1615
- // make sure a color's components don't go out of bounds
1616
- function fixColor($c) {
1617
- foreach (range(1, 3) as $i) {
1618
- if ($c[$i] < 0) $c[$i] = 0;
1619
- if ($c[$i] > 255) $c[$i] = 255;
1620
- $c[$i] = floor($c[$i]);
1621
- }
1622
-
1623
- return $c;
1624
- }
1625
-
1626
- function op_number_color($op, $lft, $rgt) {
1627
- if ($op == '+' || $op = '*') {
1628
- return $this->op_color_number($op, $rgt, $lft);
1629
- }
1630
- }
1631
-
1632
- function op_color_number($op, $lft, $rgt) {
1633
- if ($rgt[0] == '%') $rgt[1] /= 100;
1634
-
1635
- return $this->op_color_color($op, $lft,
1636
- array_fill(1, count($lft) - 1, $rgt[1]));
1637
- }
1638
-
1639
- function op_color_color($op, $left, $right) {
1640
- $out = array('color');
1641
- $max = count($left) > count($right) ? count($left) : count($right);
1642
- foreach (range(1, $max - 1) as $i) {
1643
- $lval = isset($left[$i]) ? $left[$i] : 0;
1644
- $rval = isset($right[$i]) ? $right[$i] : 0;
1645
- switch ($op) {
1646
- case '+':
1647
- $out[] = $lval + $rval;
1648
- break;
1649
- case '-':
1650
- $out[] = $lval - $rval;
1651
- break;
1652
- case '*':
1653
- $out[] = $lval * $rval;
1654
- break;
1655
- case '%':
1656
- $out[] = $lval % $rval;
1657
- break;
1658
- case '/':
1659
- if ($rval == 0) throw new exception("evaluate error: can't divide by zero");
1660
- $out[] = $lval / $rval;
1661
- break;
1662
- default:
1663
- throw new exception('evaluate error: color op number failed on op '.$op);
1664
- }
1665
- }
1666
- return $this->fixColor($out);
1667
- }
1668
-
1669
- // operator on two numbers
1670
- function op_number_number($op, $left, $right) {
1671
- if ($right[0] == '%') $right[1] /= 100;
1672
-
1673
- // figure out type
1674
- if ($right[0] == 'number' || $right[0] == '%') $type = $left[0];
1675
- else $type = $right[0];
1676
-
1677
- $value = 0;
1678
- switch ($op) {
1679
- case '+':
1680
- $value = $left[1] + $right[1];
1681
- break;
1682
- case '*':
1683
- $value = $left[1] * $right[1];
1684
- break;
1685
- case '-':
1686
- $value = $left[1] - $right[1];
1687
- break;
1688
- case '%':
1689
- $value = $left[1] % $right[1];
1690
- break;
1691
- case '/':
1692
- if ($right[1] == 0) throw new exception('parse error: divide by zero');
1693
- $value = $left[1] / $right[1];
1694
- break;
1695
- default:
1696
- throw new exception('parse error: unknown number operator: '.$op);
1697
- }
1698
-
1699
- return array($type, $value);
1700
- }
1701
-
1702
-
1703
- /* environment functions */
1704
-
1705
- // push a new block on the stack, used for parsing
1706
- function pushBlock($tags) {
1707
- $b = new stdclass;
1708
- $b->parent = $this->env;
1709
-
1710
- $b->id = self::$nextBlockId++;
1711
- $b->tags = $tags;
1712
- $b->props = array();
1713
- $b->children = array();
1714
-
1715
- $this->env = $b;
1716
- return $b;
1717
- }
1718
-
1719
- // push a block that doesn't multiply tags
1720
- function pushSpecialBlock($name) {
1721
- $b = $this->pushBlock(array($name));
1722
- $b->no_multiply = true;
1723
- return $b;
1724
- }
1725
-
1726
- // used for compiliation variable state
1727
- function pushEnv() {
1728
- $e = new stdclass;
1729
- $e->parent = $this->env;
1730
-
1731
- $this->store = array();
1732
-
1733
- $this->env = $e;
1734
- return $e;
1735
- }
1736
-
1737
- // pop something off the stack
1738
- function pop() {
1739
- $old = $this->env;
1740
- $this->env = $this->env->parent;
1741
- return $old;
1742
- }
1743
-
1744
- // set something in the current env
1745
- function set($name, $value) {
1746
- $this->env->store[$name] = $value;
1747
- }
1748
-
1749
- // append an property
1750
- function append($prop) {
1751
- $this->env->props[] = $prop;
1752
- }
1753
-
1754
- // get the highest occurrence entry for a name
1755
- function get($name) {
1756
- $current = $this->env;
1757
-
1758
- $is_arguments = $name == $this->vPrefix . 'arguments';
1759
- while ($current) {
1760
- if ($is_arguments && isset($current->arguments)) {
1761
- return array('list', ' ', $current->arguments);
1762
- }
1763
-
1764
- if (isset($current->store[$name]))
1765
- return $current->store[$name];
1766
- else
1767
- $current = $current->parent;
1768
- }
1769
-
1770
- return null;
1771
- }
1772
-
1773
- /* raw parsing functions */
1774
-
1775
- function literal($what, $eatWhitespace = true) {
1776
- // this is here mainly prevent notice from { } string accessor
1777
- if ($this->count >= strlen($this->buffer)) return false;
1778
-
1779
- // shortcut on single letter
1780
- if (!$eatWhitespace && strlen($what) == 1) {
1781
- if ($this->buffer{$this->count} == $what) {
1782
- $this->count++;
1783
- return true;
1784
- }
1785
- else return false;
1786
- }
1787
-
1788
- return $this->match($this->preg_quote($what), $m, $eatWhitespace);
1789
- }
1790
-
1791
- function preg_quote($what) {
1792
- return preg_quote($what, '/');
1793
- }
1794
-
1795
- // advance counter to next occurrence of $what
1796
- // $until - don't include $what in advance
1797
- function to($what, &$out, $until = false, $allowNewline = false) {
1798
- $validChars = $allowNewline ? "." : "[^\n]";
1799
- if (!$this->match('('.$validChars.'*?)'.$this->preg_quote($what), $m, !$until)) return false;
1800
- if ($until) $this->count -= strlen($what); // give back $what
1801
- $out = $m[1];
1802
- return true;
1803
- }
1804
-
1805
- // try to match something on head of buffer
1806
- function match($regex, &$out, $eatWhitespace = true) {
1807
- $r = '/'.$regex.($eatWhitespace ? '\s*' : '').'/Ais';
1808
- if (preg_match($r, $this->buffer, $out, null, $this->count)) {
1809
- $this->count += strlen($out[0]);
1810
- return true;
1811
- }
1812
- return false;
1813
- }
1814
-
1815
- // match something without consuming it
1816
- function peek($regex, &$out = null) {
1817
- $r = '/'.$regex.'/Ais';
1818
- $result = preg_match($r, $this->buffer, $out, null, $this->count);
1819
-
1820
- return $result;
1821
- }
1822
-
1823
- // seek to a spot in the buffer or return where we are on no argument
1824
- function seek($where = null) {
1825
- if ($where === null) return $this->count;
1826
- else $this->count = $where;
1827
- return true;
1828
- }
1829
-
1830
- /**
1831
- * Initialize state for a fresh parse
1832
- */
1833
- protected function prepareParser($buff) {
1834
- $this->env = null;
1835
- $this->expandStack = array();
1836
- $this->indentLevel = 0;
1837
- $this->count = 0;
1838
- $this->line = 1;
1839
-
1840
- $this->buffer = $this->removeComments($buff);
1841
- $this->pushBlock(null); // set up global scope
1842
-
1843
- // trim whitespace on head
1844
- if (preg_match('/^\s+/', $this->buffer, $m)) {
1845
- $this->line += substr_count($m[0], "\n");
1846
- $this->buffer = ltrim($this->buffer);
1847
- }
1848
- }
1849
-
1850
- // create a child parser (for compiling an import)
1851
- protected function createChild($fname) {
1852
- $less = new lessc($fname);
1853
- $less->importDir = $this->importDir;
1854
- $less->indentChar = $this->indentChar;
1855
- $less->compat = $this->compat;
1856
- return $less;
1857
- }
1858
-
1859
- // parse code and return intermediate tree
1860
- public function parseTree($str = null) {
1861
- $this->prepareParser(is_null($str) ? $this->buffer : $str);
1862
- while (false !== $this->parseChunk());
1863
-
1864
- if ($this->count != strlen($this->buffer))
1865
- $this->throwParseError();
1866
-
1867
- if (!is_null($this->env->parent))
1868
- throw new exception('parse error: unclosed block');
1869
-
1870
- $root = $this->env;
1871
- $this->env = null;
1872
- return $root;
1873
- }
1874
-
1875
- // inject array of unparsed strings into environment as variables
1876
- protected function injectVariables($args) {
1877
- $this->pushEnv();
1878
- $parser = new lessc();
1879
- foreach ($args as $name => $str_value) {
1880
- if ($name{0} != '@') $name = '@'.$name;
1881
- $parser->count = 0;
1882
- $parser->buffer = (string)$str_value;
1883
- if (!$parser->propertyValue($value)) {
1884
- throw new Exception("failed to parse passed in variable $name: $str_value");
1885
- }
1886
-
1887
- $this->set($name, $value);
1888
- }
1889
- }
1890
-
1891
- // parse and compile buffer
1892
- function parse($str = null, $initial_variables = null) {
1893
- $locale = setlocale(LC_NUMERIC, 0);
1894
- setlocale(LC_NUMERIC, "C");
1895
- $root = $this->parseTree($str);
1896
-
1897
- if ($initial_variables) $this->injectVariables($initial_variables);
1898
- $out = $this->compileBlock($root);
1899
- setlocale(LC_NUMERIC, $locale);
1900
- return $out;
1901
- }
1902
-
1903
- function throwParseError($msg = 'parse error') {
1904
- $line = $this->line + substr_count(substr($this->buffer, 0, $this->count), "\n");
1905
- if (isset($this->fileName)) {
1906
- $loc = $this->fileName.' on line '.$line;
1907
- } else {
1908
- $loc = "line: ".$line;
1909
- }
1910
-
1911
- if ($this->peek("(.*?)(\n|$)", $m))
1912
- throw new exception($msg.': failed at `'.$m[1].'` '.$loc);
1913
- }
1914
-
1915
- /**
1916
- * Initialize any static state, can initialize parser for a file
1917
- */
1918
- function __construct($fname = null, $opts = null) {
1919
- if (!self::$operatorString) {
1920
- self::$operatorString =
1921
- '('.implode('|', array_map(array($this, 'preg_quote'),
1922
- array_keys(self::$precedence))).')';
1923
- }
1924
-
1925
- if ($fname) {
1926
- if (!is_file($fname)) {
1927
- throw new Exception('load error: failed to find '.$fname);
1928
- }
1929
- $pi = pathinfo($fname);
1930
-
1931
- $this->fileName = $fname;
1932
- $this->importDir = $pi['dirname'].'/';
1933
- $this->buffer = file_get_contents($fname);
1934
-
1935
- $this->addParsedFile($fname);
1936
- }
1937
- }
1938
-
1939
- public function registerFunction($name, $func) {
1940
- $this->libFunctions[$name] = $func;
1941
- }
1942
-
1943
- public function unregisterFunction($name) {
1944
- unset($this->libFunctions[$name]);
1945
- }
1946
-
1947
- // remove comments from $text
1948
- // todo: make it work for all functions, not just url
1949
- function removeComments($text) {
1950
- $look = array(
1951
- 'url(', '//', '/*', '"', "'"
1952
- );
1953
-
1954
- $out = '';
1955
- $min = null;
1956
- $done = false;
1957
- while (true) {
1958
- // find the next item
1959
- foreach ($look as $token) {
1960
- $pos = strpos($text, $token);
1961
- if ($pos !== false) {
1962
- if (!isset($min) || $pos < $min[1]) $min = array($token, $pos);
1963
- }
1964
- }
1965
-
1966
- if (is_null($min)) break;
1967
-
1968
- $count = $min[1];
1969
- $skip = 0;
1970
- $newlines = 0;
1971
- switch ($min[0]) {
1972
- case 'url(':
1973
- if (preg_match('/url\(.*?\)/', $text, $m, 0, $count))
1974
- $count += strlen($m[0]) - strlen($min[0]);
1975
- break;
1976
- case '"':
1977
- case "'":
1978
- if (preg_match('/'.$min[0].'.*?'.$min[0].'/', $text, $m, 0, $count))
1979
- $count += strlen($m[0]) - 1;
1980
- break;
1981
- case '//':
1982
- $skip = strpos($text, "\n", $count);
1983
- if ($skip === false) $skip = strlen($text) - $count;
1984
- else $skip -= $count;
1985
- break;
1986
- case '/*':
1987
- if (preg_match('/\/\*.*?\*\//s', $text, $m, 0, $count)) {
1988
- $skip = strlen($m[0]);
1989
- $newlines = substr_count($m[0], "\n");
1990
- }
1991
- break;
1992
- }
1993
-
1994
- if ($skip == 0) $count += strlen($min[0]);
1995
-
1996
- $out .= substr($text, 0, $count).str_repeat("\n", $newlines);
1997
- $text = substr($text, $count + $skip);
1998
-
1999
- $min = null;
2000
- }
2001
-
2002
- return $out.$text;
2003
- }
2004
-
2005
- public function allParsedFiles() { return $this->allParsedFiles; }
2006
- protected function addParsedFile($file) {
2007
- $this->allParsedFiles[realpath($file)] = filemtime($file);
2008
- }
2009
-
2010
-
2011
- // compile to $in to $out if $in is newer than $out
2012
- // returns true when it compiles, false otherwise
2013
- public static function ccompile($in, $out) {
2014
- if (!is_file($out) || filemtime($in) > filemtime($out)) {
2015
- $less = new lessc($in);
2016
- file_put_contents($out, $less->parse());
2017
- return true;
2018
- }
2019
-
2020
- return false;
2021
- }
2022
-
2023
- /**
2024
- * Execute lessphp on a .less file or a lessphp cache structure
2025
- *
2026
- * The lessphp cache structure contains information about a specific
2027
- * less file having been parsed. It can be used as a hint for future
2028
- * calls to determine whether or not a rebuild is required.
2029
- *
2030
- * The cache structure contains two important keys that may be used
2031
- * externally:
2032
- *
2033
- * compiled: The final compiled CSS
2034
- * updated: The time (in seconds) the CSS was last compiled
2035
- *
2036
- * The cache structure is a plain-ol' PHP associative array and can
2037
- * be serialized and unserialized without a hitch.
2038
- *
2039
- * @param mixed $in Input
2040
- * @param bool $force Force rebuild?
2041
- * @return array lessphp cache structure
2042
- */
2043
- public static function cexecute($in, $force = false) {
2044
-
2045
- // assume no root
2046
- $root = null;
2047
-
2048
- if (is_string($in)) {
2049
- $root = $in;
2050
- } elseif (is_array($in) and isset($in['root'])) {
2051
- if ($force or ! isset($in['files'])) {
2052
- // If we are forcing a recompile or if for some reason the
2053
- // structure does not contain any file information we should
2054
- // specify the root to trigger a rebuild.
2055
- $root = $in['root'];
2056
- } elseif (isset($in['files']) and is_array($in['files'])) {
2057
- foreach ($in['files'] as $fname => $ftime ) {
2058
- if (!file_exists($fname) or filemtime($fname) > $ftime) {
2059
- // One of the files we knew about previously has changed
2060
- // so we should look at our incoming root again.
2061
- $root = $in['root'];
2062
- break;
2063
- }
2064
- }
2065
- }
2066
- } else {
2067
- // TODO: Throw an exception? We got neither a string nor something
2068
- // that looks like a compatible lessphp cache structure.
2069
- return null;
2070
- }
2071
-
2072
- if ($root !== null) {
2073
- // If we have a root value which means we should rebuild.
2074
- $less = new lessc($root);
2075
- $out = array();
2076
- $out['root'] = $root;
2077
- $out['compiled'] = $less->parse();
2078
- $out['files'] = $less->allParsedFiles();
2079
- $out['updated'] = time();
2080
- return $out;
2081
- } else {
2082
- // No changes, pass back the structure
2083
- // we were given initially.
2084
- return $in;
2085
- }
2086
-
2087
- }
2088
-
2089
- static protected $cssColors = array(
2090
- 'aliceblue' => '240,248,255',
2091
- 'antiquewhite' => '250,235,215',
2092
- 'aqua' => '0,255,255',
2093
- 'aquamarine' => '127,255,212',
2094
- 'azure' => '240,255,255',
2095
- 'beige' => '245,245,220',
2096
- 'bisque' => '255,228,196',
2097
- 'black' => '0,0,0',
2098
- 'blanchedalmond' => '255,235,205',
2099
- 'blue' => '0,0,255',
2100
- 'blueviolet' => '138,43,226',
2101
- 'brown' => '165,42,42',
2102
- 'burlywood' => '222,184,135',
2103
- 'cadetblue' => '95,158,160',
2104
- 'chartreuse' => '127,255,0',
2105
- 'chocolate' => '210,105,30',
2106
- 'coral' => '255,127,80',
2107
- 'cornflowerblue' => '100,149,237',
2108
- 'cornsilk' => '255,248,220',
2109
- 'crimson' => '220,20,60',
2110
- 'cyan' => '0,255,255',
2111
- 'darkblue' => '0,0,139',
2112
- 'darkcyan' => '0,139,139',
2113
- 'darkgoldenrod' => '184,134,11',
2114
- 'darkgray' => '169,169,169',
2115
- 'darkgreen' => '0,100,0',
2116
- 'darkgrey' => '169,169,169',
2117
- 'darkkhaki' => '189,183,107',
2118
- 'darkmagenta' => '139,0,139',
2119
- 'darkolivegreen' => '85,107,47',
2120
- 'darkorange' => '255,140,0',
2121
- 'darkorchid' => '153,50,204',
2122
- 'darkred' => '139,0,0',
2123
- 'darksalmon' => '233,150,122',
2124
- 'darkseagreen' => '143,188,143',
2125
- 'darkslateblue' => '72,61,139',
2126
- 'darkslategray' => '47,79,79',
2127
- 'darkslategrey' => '47,79,79',
2128
- 'darkturquoise' => '0,206,209',
2129
- 'darkviolet' => '148,0,211',
2130
- 'deeppink' => '255,20,147',
2131
- 'deepskyblue' => '0,191,255',
2132
- 'dimgray' => '105,105,105',
2133
- 'dimgrey' => '105,105,105',
2134
- 'dodgerblue' => '30,144,255',
2135
- 'firebrick' => '178,34,34',
2136
- 'floralwhite' => '255,250,240',
2137
- 'forestgreen' => '34,139,34',
2138
- 'fuchsia' => '255,0,255',
2139
- 'gainsboro' => '220,220,220',
2140
- 'ghostwhite' => '248,248,255',
2141
- 'gold' => '255,215,0',
2142
- 'goldenrod' => '218,165,32',
2143
- 'gray' => '128,128,128',
2144
- 'green' => '0,128,0',
2145
- 'greenyellow' => '173,255,47',
2146
- 'grey' => '128,128,128',
2147
- 'honeydew' => '240,255,240',
2148
- 'hotpink' => '255,105,180',
2149
- 'indianred' => '205,92,92',
2150
- 'indigo' => '75,0,130',
2151
- 'ivory' => '255,255,240',
2152
- 'khaki' => '240,230,140',
2153
- 'lavender' => '230,230,250',
2154
- 'lavenderblush' => '255,240,245',
2155
- 'lawngreen' => '124,252,0',
2156
- 'lemonchiffon' => '255,250,205',
2157
- 'lightblue' => '173,216,230',
2158
- 'lightcoral' => '240,128,128',
2159
- 'lightcyan' => '224,255,255',
2160
- 'lightgoldenrodyellow' => '250,250,210',
2161
- 'lightgray' => '211,211,211',
2162
- 'lightgreen' => '144,238,144',
2163
- 'lightgrey' => '211,211,211',
2164
- 'lightpink' => '255,182,193',
2165
- 'lightsalmon' => '255,160,122',
2166
- 'lightseagreen' => '32,178,170',
2167
- 'lightskyblue' => '135,206,250',
2168
- 'lightslategray' => '119,136,153',
2169
- 'lightslategrey' => '119,136,153',
2170
- 'lightsteelblue' => '176,196,222',
2171
- 'lightyellow' => '255,255,224',
2172
- 'lime' => '0,255,0',
2173
- 'limegreen' => '50,205,50',
2174
- 'linen' => '250,240,230',
2175
- 'magenta' => '255,0,255',
2176
- 'maroon' => '128,0,0',
2177
- 'mediumaquamarine' => '102,205,170',
2178
- 'mediumblue' => '0,0,205',
2179
- 'mediumorchid' => '186,85,211',
2180
- 'mediumpurple' => '147,112,219',
2181
- 'mediumseagreen' => '60,179,113',
2182
- 'mediumslateblue' => '123,104,238',
2183
- 'mediumspringgreen' => '0,250,154',
2184
- 'mediumturquoise' => '72,209,204',
2185
- 'mediumvioletred' => '199,21,133',
2186
- 'midnightblue' => '25,25,112',
2187
- 'mintcream' => '245,255,250',
2188
- 'mistyrose' => '255,228,225',
2189
- 'moccasin' => '255,228,181',
2190
- 'navajowhite' => '255,222,173',
2191
- 'navy' => '0,0,128',
2192
- 'oldlace' => '253,245,230',
2193
- 'olive' => '128,128,0',
2194
- 'olivedrab' => '107,142,35',
2195
- 'orange' => '255,165,0',
2196
- 'orangered' => '255,69,0',
2197
- 'orchid' => '218,112,214',
2198
- 'palegoldenrod' => '238,232,170',
2199
- 'palegreen' => '152,251,152',
2200
- 'paleturquoise' => '175,238,238',
2201
- 'palevioletred' => '219,112,147',
2202
- 'papayawhip' => '255,239,213',
2203
- 'peachpuff' => '255,218,185',
2204
- 'peru' => '205,133,63',
2205
- 'pink' => '255,192,203',
2206
- 'plum' => '221,160,221',
2207
- 'powderblue' => '176,224,230',
2208
- 'purple' => '128,0,128',
2209
- 'red' => '255,0,0',
2210
- 'rosybrown' => '188,143,143',
2211
- 'royalblue' => '65,105,225',
2212
- 'saddlebrown' => '139,69,19',
2213
- 'salmon' => '250,128,114',
2214
- 'sandybrown' => '244,164,96',
2215
- 'seagreen' => '46,139,87',
2216
- 'seashell' => '255,245,238',
2217
- 'sienna' => '160,82,45',
2218
- 'silver' => '192,192,192',
2219
- 'skyblue' => '135,206,235',
2220
- 'slateblue' => '106,90,205',
2221
- 'slategray' => '112,128,144',
2222
- 'slategrey' => '112,128,144',
2223
- 'snow' => '255,250,250',
2224
- 'springgreen' => '0,255,127',
2225
- 'steelblue' => '70,130,180',
2226
- 'tan' => '210,180,140',
2227
- 'teal' => '0,128,128',
2228
- 'thistle' => '216,191,216',
2229
- 'tomato' => '255,99,71',
2230
- 'turquoise' => '64,224,208',
2231
- 'violet' => '238,130,238',
2232
- 'wheat' => '245,222,179',
2233
- 'white' => '255,255,255',
2234
- 'whitesmoke' => '245,245,245',
2235
- 'yellow' => '255,255,0',
2236
- 'yellowgreen' => '154,205,50'
2237
- );
2238
- }
2239
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/lessify DELETED
@@ -1,23 +0,0 @@
1
- #!/usr/bin/php -q
2
- <?php
3
-
4
- if (php_sapi_name() != "cli") {
5
- err($fa.$argv[0]." must be run in the command line.");
6
- exit(1);
7
- }
8
- $exe = array_shift($argv); // remove filename
9
-
10
- if (!$fname = array_shift($argv)) {
11
- exit("Usage: ".$exe." input-file\n");
12
- }
13
-
14
- require "lessify.inc.php";
15
-
16
- try {
17
- $parser = new lessify($fname);
18
- echo $parser->parse();
19
- } catch (exception $e) {
20
- exit("Fatal error: ".$e->getMessage()."\n");
21
- }
22
-
23
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/lessify.inc.php DELETED
@@ -1,447 +0,0 @@
1
- <?php
2
- /**
3
- * lessify
4
- * Convert a css file into a less file
5
- * http://leafo.net/lessphp
6
- * Copyright 2010, leaf corcoran <leafot@gmail.com>
7
- *
8
- * WARNING: THIS DOES NOT WORK ANYMORE. NEEDS TO BE UPDATED FOR
9
- * LATEST VERSION OF LESSPHP.
10
- *
11
- */
12
-
13
- require "lessc.inc.php";
14
-
15
- //
16
- // check if the merge during mixin is overwriting values. should or should it not?
17
- //
18
-
19
- //
20
- // 1. split apart class tags
21
- //
22
-
23
- class easyparse {
24
- var $buffer;
25
- var $count;
26
-
27
- function __construct($str) {
28
- $this->count = 0;
29
- $this->buffer = trim($str);
30
- }
31
-
32
- function seek($where = null) {
33
- if ($where === null) return $this->count;
34
- else $this->count = $where;
35
- return true;
36
- }
37
-
38
- function preg_quote($what) {
39
- return preg_quote($what, '/');
40
- }
41
-
42
- function match($regex, &$out, $eatWhitespace = true) {
43
- $r = '/'.$regex.($eatWhitespace ? '\s*' : '').'/Ais';
44
- if (preg_match($r, $this->buffer, $out, null, $this->count)) {
45
- $this->count += strlen($out[0]);
46
- return true;
47
- }
48
- return false;
49
- }
50
-
51
- function literal($what, $eatWhitespace = true) {
52
- // this is here mainly prevent notice from { } string accessor
53
- if ($this->count >= strlen($this->buffer)) return false;
54
-
55
- // shortcut on single letter
56
- if (!$eatWhitespace and strlen($what) == 1) {
57
- if ($this->buffer{$this->count} == $what) {
58
- $this->count++;
59
- return true;
60
- }
61
- else return false;
62
- }
63
-
64
- return $this->match($this->preg_quote($what), $m, $eatWhitespace);
65
- }
66
-
67
- }
68
-
69
- class tagparse extends easyparse {
70
- static private $combinators = null;
71
- static private $match_opts = null;
72
-
73
- function parse() {
74
- if (empty(self::$combinators)) {
75
- self::$combinators = '('.implode('|', array_map(array($this, 'preg_quote'),
76
- array('+', '>', '~'))).')';
77
- self::$match_opts = '('.implode('|', array_map(array($this, 'preg_quote'),
78
- array('=', '~=', '|=', '$=', '*='))).')';
79
- }
80
-
81
- // crush whitespace
82
- $this->buffer = preg_replace('/\s+/', ' ', $this->buffer).' ';
83
-
84
- $tags = array();
85
- while ($this->tag($t)) $tags[] = $t;
86
-
87
- return $tags;
88
- }
89
-
90
- static function compileString($string) {
91
- list(, $delim, $str) = $string;
92
- $str = str_replace($delim, "\\".$delim, $str);
93
- $str = str_replace("\n", "\\\n", $str);
94
- return $delim.$str.$delim;
95
- }
96
-
97
- static function compilePaths($paths) {
98
- return implode(', ', array_map(array('self', 'compilePath'), $paths));
99
- }
100
-
101
- // array of tags
102
- static function compilePath($path) {
103
- return implode(' ', array_map(array('self', 'compileTag'), $path));
104
- }
105
-
106
-
107
- static function compileTag($tag) {
108
- ob_start();
109
- if (isset($tag['comb'])) echo $tag['comb']." ";
110
- if (isset($tag['front'])) echo $tag['front'];
111
- if (isset($tag['attr'])) {
112
- echo '['.$tag['attr'];
113
- if (isset($tag['op'])) {
114
- echo $tag['op'].$tag['op_value'];
115
- }
116
- echo ']';
117
- }
118
- return ob_get_clean();
119
- }
120
-
121
- function string(&$out) {
122
- $s = $this->seek();
123
-
124
- if ($this->literal('"')) {
125
- $delim = '"';
126
- } elseif ($this->literal("'")) {
127
- $delim = "'";
128
- } else {
129
- return false;
130
- }
131
-
132
- while (true) {
133
- // step through letters looking for either end or escape
134
- $buff = "";
135
- $escapeNext = false;
136
- $finished = false;
137
- for ($i = $this->count; $i < strlen($this->buffer); $i++) {
138
- $char = $this->buffer[$i];
139
- switch ($char) {
140
- case $delim:
141
- if ($escapeNext) {
142
- $buff .= $char;
143
- $escapeNext = false;
144
- break;
145
- }
146
- $finished = true;
147
- break 2;
148
- case "\\":
149
- if ($escapeNext) {
150
- $buff .= $char;
151
- $escapeNext = false;
152
- } else {
153
- $escapeNext = true;
154
- }
155
- break;
156
- case "\n":
157
- if (!$escapeNext) {
158
- break 3;
159
- }
160
-
161
- $buff .= $char;
162
- $escapeNext = false;
163
- break;
164
- default:
165
- if ($escapeNext) {
166
- $buff .= "\\";
167
- $escapeNext = false;
168
- }
169
- $buff .= $char;
170
- }
171
- }
172
- if (!$finished) break;
173
- $out = array('string', $delim, $buff);
174
- $this->seek($i+1);
175
- return true;
176
- }
177
-
178
- $this->seek($s);
179
- return false;
180
- }
181
-
182
- function tag(&$out) {
183
- $s = $this->seek();
184
- $tag = array();
185
- if ($this->combinator($op)) $tag['comb'] = $op;
186
-
187
- if (!$this->match('(.*?)( |$|\[|'.self::$combinators.')', $match)) {
188
- $this->seek($s);
189
- return false;
190
- }
191
-
192
- if (!empty($match[3])) {
193
- // give back combinator
194
- $this->count-=strlen($match[3]);
195
- }
196
-
197
- if (!empty($match[1])) $tag['front'] = $match[1];
198
-
199
- if ($match[2] == '[') {
200
- if ($this->ident($i)) {
201
- $tag['attr'] = $i;
202
-
203
- if ($this->match(self::$match_opts, $m) && $this->value($v)) {
204
- $tag['op'] = $m[1];
205
- $tag['op_value'] = $v;
206
- }
207
-
208
- if ($this->literal(']')) {
209
- $out = $tag;
210
- return true;
211
- }
212
- }
213
- } elseif (isset($tag['front'])) {
214
- $out = $tag;
215
- return true;
216
- }
217
-
218
- $this->seek($s);
219
- return false;
220
- }
221
-
222
- function ident(&$out) {
223
- // [-]?{nmstart}{nmchar}*
224
- // nmstart: [_a-z]|{nonascii}|{escape}
225
- // nmchar: [_a-z0-9-]|{nonascii}|{escape}
226
- if ($this->match('(-?[_a-z][_\w]*)', $m)) {
227
- $out = $m[1];
228
- return true;
229
- }
230
- return false;
231
- }
232
-
233
- function value(&$out) {
234
- if ($this->string($str)) {
235
- $out = $this->compileString($str);
236
- return true;
237
- } elseif ($this->ident($id)) {
238
- $out = $id;
239
- return true;
240
- }
241
- return false;
242
- }
243
-
244
-
245
- function combinator(&$op) {
246
- if ($this->match(self::$combinators, $m)) {
247
- $op = $m[1];
248
- return true;
249
- }
250
- return false;
251
- }
252
- }
253
-
254
- class nodecounter {
255
- var $count = 0;
256
- var $children = array();
257
-
258
- var $name;
259
- var $child_blocks;
260
- var $the_block;
261
-
262
- function __construct($name) {
263
- $this->name = $name;
264
- }
265
-
266
- function dump($stack = null) {
267
- if (is_null($stack)) $stack = array();
268
- $stack[] = $this->getName();
269
- echo implode(' -> ', $stack)." ($this->count)\n";
270
- foreach ($this->children as $child) {
271
- $child->dump($stack);
272
- }
273
- }
274
-
275
- static function compileProperties($c, $block) {
276
- foreach($block as $name => $value) {
277
- if ($c->isProperty($name, $value)) {
278
- echo $c->compileProperty($name, $value)."\n";
279
- }
280
- }
281
- }
282
-
283
- function compile($c, $path = null) {
284
- if (is_null($path)) $path = array();
285
- $path[] = $this->name;
286
-
287
- $isVisible = !is_null($this->the_block) || !is_null($this->child_blocks);
288
-
289
- if ($isVisible) {
290
- echo $c->indent(implode(' ', $path).' {');
291
- $c->indentLevel++;
292
- $path = array();
293
-
294
- if ($this->the_block) {
295
- $this->compileProperties($c, $this->the_block);
296
- }
297
-
298
- if ($this->child_blocks) {
299
- foreach ($this->child_blocks as $block) {
300
- echo $c->indent(tagparse::compilePaths($block['__tags']).' {');
301
- $c->indentLevel++;
302
- $this->compileProperties($c, $block);
303
- $c->indentLevel--;
304
- echo $c->indent('}');
305
- }
306
- }
307
- }
308
-
309
- // compile child nodes
310
- foreach($this->children as $node) {
311
- $node->compile($c, $path);
312
- }
313
-
314
- if ($isVisible) {
315
- $c->indentLevel--;
316
- echo $c->indent('}');
317
- }
318
-
319
- }
320
-
321
- function getName() {
322
- if (is_null($this->name)) return "[root]";
323
- else return $this->name;
324
- }
325
-
326
- function getNode($name) {
327
- if (!isset($this->children[$name])) {
328
- $this->children[$name] = new nodecounter($name);
329
- }
330
-
331
- return $this->children[$name];
332
- }
333
-
334
- function findNode($path) {
335
- $current = $this;
336
- for ($i = 0; $i < count($path); $i++) {
337
- $t = tagparse::compileTag($path[$i]);
338
- $current = $current->getNode($t);
339
- }
340
-
341
- return $current;
342
- }
343
-
344
- function addBlock($path, $block) {
345
- $node = $this->findNode($path);
346
- if (!is_null($node->the_block)) throw new exception("can this happen?");
347
-
348
- unset($block['__tags']);
349
- $node->the_block = $block;
350
- }
351
-
352
- function addToNode($path, $block) {
353
- $node = $this->findNode($path);
354
- $node->child_blocks[] = $block;
355
- }
356
- }
357
-
358
- /**
359
- * create a less file from a css file by combining blocks where appropriate
360
- */
361
- class lessify extends lessc {
362
- public function dump() {
363
- print_r($this->env);
364
- }
365
-
366
- public function parse($str = null) {
367
- $this->prepareParser($str ? $str : $this->buffer);
368
- while (false !== $this->parseChunk());
369
-
370
- $root = new nodecounter(null);
371
-
372
- // attempt to preserve some of the block order
373
- $order = array();
374
-
375
- $visitedTags = array();
376
- foreach (end($this->env) as $name => $block) {
377
- if (!$this->isBlock($name, $block)) continue;
378
- if (isset($visitedTags[$name])) continue;
379
-
380
- foreach ($block['__tags'] as $t) {
381
- $visitedTags[$t] = true;
382
- }
383
-
384
- // skip those with more than 1
385
- if (count($block['__tags']) == 1) {
386
- $p = new tagparse(end($block['__tags']));
387
- $path = $p->parse();
388
- $root->addBlock($path, $block);
389
- $order[] = array('compressed', $path, $block);
390
- continue;
391
- } else {
392
- $common = null;
393
- $paths = array();
394
- foreach ($block['__tags'] as $rawtag) {
395
- $p = new tagparse($rawtag);
396
- $paths[] = $path = $p->parse();
397
- if (is_null($common)) $common = $path;
398
- else {
399
- $new_common = array();
400
- foreach ($path as $tag) {
401
- $head = array_shift($common);
402
- if ($tag == $head) {
403
- $new_common[] = $head;
404
- } else break;
405
- }
406
- $common = $new_common;
407
- if (empty($common)) {
408
- // nothing in common
409
- break;
410
- }
411
- }
412
- }
413
-
414
- if (!empty($common)) {
415
- $new_paths = array();
416
- foreach ($paths as $p) $new_paths[] = array_slice($p, count($common));
417
- $block['__tags'] = $new_paths;
418
- $root->addToNode($common, $block);
419
- $order[] = array('compressed', $common, $block);
420
- continue;
421
- }
422
-
423
- }
424
-
425
- $order[] = array('none', $block['__tags'], $block);
426
- }
427
-
428
-
429
- $compressed = $root->children;
430
- foreach ($order as $item) {
431
- list($type, $tags, $block) = $item;
432
- if ($type == 'compressed') {
433
- $top = tagparse::compileTag(reset($tags));
434
- if (isset($compressed[$top])) {
435
- $compressed[$top]->compile($this);
436
- unset($compressed[$top]);
437
- }
438
- } else {
439
- echo $this->indent(implode(', ', $tags).' {');
440
- $this->indentLevel++;
441
- nodecounter::compileProperties($this, $block);
442
- $this->indentLevel--;
443
- echo $this->indent('}');
444
- }
445
- }
446
- }
447
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/plessc DELETED
@@ -1,191 +0,0 @@
1
- #!/usr/bin/php -q
2
- <?php
3
- //
4
- // command line utility to compile less to stdout
5
- // leaf corcoran <leafo.net>
6
- $VERSION = "v0.3.0";
7
-
8
- error_reporting(E_ALL);
9
- $path = realpath(dirname(__FILE__)).'/';
10
-
11
- require $path."lessc.inc.php";
12
-
13
- $fa = "Fatal Error: ";
14
- function err($msg) {
15
- fwrite(STDERR, $msg."\n");
16
- }
17
-
18
- if (php_sapi_name() != "cli") {
19
- err($fa.$argv[0]." must be run in the command line.");
20
- exit(1);
21
- }
22
- $exe = array_shift($argv); // remove filename
23
-
24
- function process($data, $import = null) {
25
- global $fa;
26
-
27
- $l = new lessc();
28
- if ($import) $l->importDir = $import;
29
- try {
30
- echo $l->parse($data);
31
- exit(0);
32
- } catch (exception $ex) {
33
- err($fa."\n".str_repeat('=', 20)."\n".
34
- $ex->getMessage());
35
- exit(1);
36
- }
37
- }
38
-
39
- // process args
40
- $opts = array();
41
- foreach ($argv as $loc => $a) {
42
- if (preg_match("/^-([a-zA-Z]+)$/", $a, $m)) {
43
- $m = $m[1];
44
- for ($i = 0; $i < strlen($m); $i++)
45
- $opts[$m{$i}] = $loc;
46
- unset($argv[$loc]);
47
- }
48
- }
49
-
50
- function has($o, &$loc = null) {
51
- global $opts;
52
- if (!isset($opts[$o])) return false;
53
- $loc = $opts[$o];
54
- return true;
55
- }
56
-
57
- function hasValue($o, &$value = null) {
58
- global $argv;
59
- if (!has($o,$loc)) return false;
60
- if (!isset($argv[$loc+1])) return false;
61
- $value = $argv[$loc+1];
62
- return true;
63
- }
64
-
65
- if (has("v")) {
66
- exit($VERSION."\n");
67
- }
68
-
69
- if (has("r", $loc)) {
70
- if (!hasValue("r", $data)) {
71
- while (!feof(STDIN)) {
72
- $data .= fread(STDIN, 8192);
73
- }
74
- }
75
- return process($data);
76
- }
77
-
78
- if (has("w")) {
79
- // need two files
80
- if (!is_file($in = array_shift($argv)) ||
81
- null == $out = array_shift($argv))
82
- {
83
- err($fa.$exe." -w infile outfile");
84
- exit(1);
85
- }
86
-
87
- echo "Watching ".$in.
88
- (has("n") ? ' with notifications' : '').
89
- ", press Ctrl + c to exit.\n";
90
-
91
- $cache = $in;
92
- $last_action = 0;
93
- while (1) {
94
- clearstatcache();
95
-
96
- // check if anything has changed since last fail
97
- $updated = false;
98
- if (is_array($cache)) {
99
- foreach ($cache['files'] as $fname=>$_) {
100
- if (filemtime($fname) > $last_action) {
101
- $updated = true;
102
- break;
103
- }
104
- }
105
- } else $updated = true;
106
-
107
- // try to compile it
108
- if ($updated) {
109
- $last_action = time();
110
-
111
- try {
112
- $cache = lessc::cexecute($cache);
113
- echo "Writing updated file: ".$out."\n";
114
- if (!file_put_contents($out, $cache['compiled'])) {
115
- err($fa."Could not write to file ".$out);
116
- exit(1);
117
- }
118
- } catch (exception $ex) {
119
- echo "\nFatal Error:\n".str_repeat('=', 20)."\n".$ex->getMessage()."\n\n";
120
-
121
- if (has("n")) {
122
- `notify-send -u critical "compile failed" "{$ex->getMessage()}"`;
123
- }
124
- }
125
- }
126
-
127
- sleep(1);
128
- }
129
- exit(0);
130
- }
131
-
132
- if (!$fname = array_shift($argv)) {
133
- echo "Usage: ".$exe." input-file [output-file]\n";
134
- exit(1);
135
- }
136
-
137
- function dumpValue($node, $depth = 0) {
138
- if (is_object($node)) {
139
- $indent = str_repeat(" ", $depth);
140
- $out = array();
141
- foreach ($node->props as $prop) {
142
- $out[] = $indent . dumpValue($prop, $depth + 1);
143
- }
144
- $out = implode("\n", $out);
145
- if (!empty($node->tags)) {
146
- $out = "+ ".implode(", ", $node->tags)."\n".$out;
147
- }
148
- return $out;
149
- } elseif (is_array($node)) {
150
- $type = $node[0];
151
- if ($type == "block")
152
- return dumpValue($node[1], $depth);
153
-
154
- $out = array();
155
- foreach ($node as $value) {
156
- $out[] = dumpValue($value, $depth);
157
- }
158
- return "{ ".implode(", ", $out)." }";
159
- } else {
160
- if (is_string($node) && preg_match("/[\s,]/", $node)) {
161
- return '"'.$node.'"';
162
- }
163
- return $node; // normal value
164
- }
165
- }
166
-
167
- try {
168
- $l = new lessc($fname);
169
- if (has("T") || has("X")) {
170
- $t = $l->parseTree();
171
- if (has("X"))
172
- $out = print_r($t, 1);
173
- else
174
- $out = dumpValue($t)."\n";
175
- } else {
176
- $out = $l->parse();
177
- }
178
-
179
- if (!$fout = array_shift($argv)) {
180
- echo $out;
181
- } else {
182
- file_put_contents($fout, $out);
183
- }
184
-
185
- } catch (exception $ex) {
186
- err($fa.$ex->getMessage());
187
- exit(1);
188
- }
189
-
190
-
191
- ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/tests/README DELETED
@@ -1,23 +0,0 @@
1
- test.php
2
- ========================================
3
-
4
- to run:
5
- php test.php [test-name-glob]
6
-
7
-
8
- Runs through all files in `inputs`, compiles them, then
9
- compares to respective file in `outputs`. If there are
10
- any differences then the test will fail.
11
-
12
- Add the -d flag to show the differences of failed tests
13
- in your diff tool (currently assigned in code, $difftool)
14
- Defaults to diff, but I like using meld.
15
-
16
- Pass the -C flag to save the output of the inputs to
17
- the appropriate file. This will overwrite any existing
18
- outputs. Use this when you want to save verified test
19
- results.
20
-
21
- You can also run specific tests by passing in an argument
22
- that contains any part of the test name.
23
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/tests/inputs/accessors.less.disable DELETED
@@ -1,36 +0,0 @@
1
- /* accessors */
2
-
3
- #defaults {
4
- @width: 960px;
5
- @color: black;
6
- .something {
7
- @space: 10px;
8
- @hello {
9
- color: green;
10
- }
11
- }
12
- }
13
-
14
- .article { color: #294366; }
15
-
16
- .comment {
17
- width: #defaults[@width];
18
- color: .article['color'];
19
- padding: #defaults > .something[@space];
20
- }
21
-
22
- .wow {
23
- height: .comment['width'];
24
- background-color: .comment['color'];
25
- color: #defaults > .something > @hello['color'];
26
-
27
- padding: #defaults > non-existant['padding'];
28
- margin: #defaults > .something['non-existant'];
29
- }
30
-
31
- .mix {
32
- #defaults;
33
- font-size: .something[@space];
34
- }
35
-
36
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/tests/inputs/attributes.less DELETED
@@ -1,41 +0,0 @@
1
- * { color: blue; }
2
- E { color: blue; }
3
- E[foo] { color: blue; }
4
- [foo] { color: blue; }
5
- [foo] .helloWorld { color: blue; }
6
- [foo].helloWorld { color: blue; }
7
- E[foo="barbar"] { color: blue; }
8
- E[foo~="hello#$@%@$#^"] { color: blue; }
9
- E[foo^="color: green;"] { color: blue; }
10
- E[foo$="239023"] { color: blue; }
11
- E[foo*="29302"] { color: blue; }
12
- E[foo|="239032"] { color: blue; }
13
- E:root { color: blue; }
14
-
15
- E:nth-child(odd) { color: blue; }
16
- E:nth-child(2n+1) { color: blue; }
17
- E:nth-child(5) { color: blue; }
18
- E:nth-last-child(-n+2) { color: blue; }
19
- E:nth-of-type(2n) { color: blue; }
20
- E:nth-last-of-type(n) { color: blue; }
21
-
22
- E:first-child { color: blue; }
23
- E:last-child { color: blue; }
24
- E:first-of-type { color: blue; }
25
- E:last-of-type { color: blue; }
26
- E:only-child { color: blue; }
27
- E:only-of-type { color: blue; }
28
- E:empty { color: blue; }
29
-
30
- E:lang(en) { color: blue; }
31
- E::first-line { color: blue; }
32
- E::before { color: blue; }
33
-
34
- E#id { color: blue; }
35
- E:not(:link) { color: blue; }
36
-
37
- E F { color: blue; }
38
- E > F { color: blue; }
39
- E + F { color: blue; }
40
- E ~ F { color: blue; }
41
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/tests/inputs/builtins.less DELETED
@@ -1,33 +0,0 @@
1
- // builtin
2
-
3
- @something: "hello world";
4
- @color: #112233;
5
- @color2: rgba(44,55,66, .6);
6
-
7
- body {
8
- color: @something;
9
-
10
- @num: 7 / 6;
11
- height: @num + 4;
12
- height: floor(@num) + 4px;
13
-
14
- @num2: 2 / 3;
15
- width: @num2;
16
- width: round(@num2);
17
- width: floor(@num2);
18
-
19
- color: rgbahex(@color);
20
- color: rgbahex(@color2);
21
- }
22
-
23
-
24
- format {
25
- @r: 32;
26
- format: %("rgb(%d, %d, %d)", @r, 128, 64);
27
- format-string: %("hello %s", "world");
28
- format-multiple: %("hello %s %d", "earth", 2);
29
- format-url-encode: %('red is %A', #ff0000);
30
- eformat: e(%("rgb(%d, %d, %d)", @r, 128, 64));
31
- }
32
-
33
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/tests/inputs/colors.less DELETED
@@ -1,98 +0,0 @@
1
-
2
- body {
3
- color: hsl(34, 50%, 40%);
4
- color: hsla(34, 50%, 40%, 0.3);
5
-
6
- lighten: lighten(#efefef, 10%);
7
- lighten: lighten(rgb(23, 53, 231), 22%);
8
- lighten: lighten(rgba(212, 103, 88, 0.5), 10%);
9
-
10
- darken: darken(#efefef, 10%);
11
- darken: darken(rgb(23, 53, 231), 22%);
12
- darken: darken(rgba(23, 53, 231, 0.5), 10%);
13
-
14
- saturate: saturate(#efefef, 10%);
15
- saturate: saturate(rgb(23, 53, 231), 22%);
16
- saturate: saturate(rgba(23, 53, 231, 0.5), 10%);
17
-
18
- desaturate: desaturate(#efefef, 10%);
19
- desaturate: desaturate(rgb(23, 53, 231), 22%);
20
- desaturate: desaturate(rgba(23, 53, 231, 0.5), 10%);
21
-
22
- spin: spin(#efefef, 12);
23
- spin: spin(rgb(23, 53, 231), 15);
24
- spin: spin(rgba(23, 53, 231, 0.5), 19);
25
-
26
- spin: spin(#efefef, -12);
27
- spin: spin(rgb(23, 53, 231), -15);
28
- spin: spin(rgba(23, 53, 231, 0.5), -19);
29
-
30
- one: fadein(#abcdef, 10%);
31
- one: fadeout(#abcdef, -10%);
32
-
33
- two: fadeout(#029f23, 10%);
34
- two: fadein(#029f23, -10%);
35
-
36
-
37
- three: fadein(rgba(1,2,3, 0.5), 10%);
38
- three: fadeout(rgba(1,2,3, 0.5), -10%);
39
-
40
- four: fadeout(rgba(1,2,3, 0), 10%);
41
- four: fadein(rgba(1,2,3, 0), -10%);
42
-
43
- hue: hue(rgb(34,20,40));
44
- sat: saturation(rgb(34,20,40));
45
- lit: lightness(rgb(34,20,40));
46
-
47
- @old: #34fa03;
48
- @new: hsl(hue(@old), 45%, 90%);
49
- what: @new;
50
-
51
- zero: saturate(#123456, -100%);
52
- zero: saturate(#123456, 100%);
53
- zero: saturate(#000000, 100%);
54
- zero: saturate(#ffffff, 100%);
55
-
56
- zero: lighten(#123456, -100%);
57
- zero: lighten(#123456, 100%);
58
- zero: lighten(#000000, 100%);
59
- zero: lighten(#ffffff, 100%);
60
-
61
- zero: spin(#123456, -100);
62
- zero: spin(#123456, 100);
63
- zero: spin(#000000, 100);
64
- zero: spin(#ffffff, 100);
65
- }
66
-
67
-
68
- alpha {
69
- // g: alpha(red);
70
- g: alpha(rgba(0,0,0,0));
71
- g: alpha(rgb(155,55,0));
72
- }
73
-
74
- fade {
75
- f: fade(red, 50%);
76
- f: fade(#fff, 20%);
77
- f: fade(rgba(34,23,64,0.4), 50%);
78
- }
79
-
80
- @a: rgb(255,255,255);
81
- @b: rgb(0,0,0);
82
-
83
- .mix {
84
- color: mix(@a, @b, 50%);
85
- }
86
-
87
- .percent {
88
- per: percentage(0.5);
89
- }
90
-
91
- // color keywords
92
-
93
- .colorz {
94
- color: whitesmoke - 10;
95
- color: spin(red, 34);
96
- }
97
-
98
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/tests/inputs/compile_on_mixin.less DELETED
@@ -1,39 +0,0 @@
1
-
2
- @mixin {
3
- height: 22px;
4
- ul {
5
- height: 20px;
6
- li {
7
- @color: red;
8
- height: 10px;
9
- div span, link {
10
- margin: 10px;
11
- color: @color;
12
- }
13
- }
14
-
15
- div, p {
16
- border: 1px;
17
- &.hello {
18
- color: green;
19
- }
20
-
21
- :what {
22
- color: blue;
23
- }
24
- }
25
-
26
-
27
- a {
28
- b {
29
- color: blue;
30
- }
31
- }
32
- }
33
- }
34
-
35
-
36
-
37
- body {
38
- @mixin;
39
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/tests/inputs/escape.less DELETED
@@ -1,19 +0,0 @@
1
-
2
- body {
3
- @hello: "world";
4
- border: e("this is simple");
5
- border: e(this is simple); // bug in lessjs
6
- border: e("this is simple", "cool lad");
7
- border: e(1232);
8
- border: e(@hello);
9
- border: e("one" + 'more'); // no string addition lessjs
10
- border: e(); // syntax error lessjs
11
-
12
- line-height: ~"eating rice";
13
- line-height: ~"string cheese";
14
- line-height: a b c ~"string me" d e f;
15
- }
16
-
17
- .class {
18
- filter: ~"progid:DXImageTransform.Microsoft.AlphaImageLoader(src='image.png')";
19
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/tests/inputs/font_family.less DELETED
@@ -1,28 +0,0 @@
1
-
2
- @font-directory: 'fonts/';
3
- @some-family: Gentium;
4
-
5
- @font-face: maroon; // won't collide with @font-face { }
6
-
7
- @font-face {
8
- font-family: Graublau Sans Web;
9
- src: url(@{font-directory}GraublauWeb.otf) format("opentype");
10
- }
11
-
12
- @font-face {
13
- font-family: @some-family;
14
- src: url('@{font-directory}Gentium.ttf');
15
- }
16
-
17
- @font-face {
18
- font-family: @some-family;
19
- src: url("@{font-directory}GentiumItalic.ttf");
20
- font-style: italic;
21
- }
22
-
23
- h2 {
24
- font-family: @some-family;
25
- crazy: @font-face;
26
- }
27
-
28
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/tests/inputs/hacks.less DELETED
@@ -1,6 +0,0 @@
1
- // css hacks
2
-
3
- :root .alert-message, :root .btn {
4
- border-radius: 0 \0;
5
- }
6
-
 
 
 
 
 
 
lib/vendor/lessphp/tests/inputs/import.less DELETED
@@ -1,21 +0,0 @@
1
-
2
- @import 'file1.less'; // file found and imported
3
-
4
- @import "something.css" media;
5
- @import url("something.css") media;
6
- @import url(something.css) media, screen, print;
7
-
8
- @color: maroon;
9
-
10
- @import url(file2); // found and imported
11
-
12
- body {
13
- line-height: 10em;
14
- @colors;
15
- }
16
-
17
- div {
18
- @color: fuchsia;
19
- @import "file2";
20
- }
21
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/tests/inputs/keyframes.less DELETED
@@ -1,48 +0,0 @@
1
- @keyframes 'bounce' {
2
- from {
3
- top: 100px;
4
- animation-timing-function: ease-out;
5
- }
6
-
7
- 25% {
8
- top: 50px;
9
- animation-timing-function: ease-in;
10
- }
11
-
12
- 50% {
13
- top: 100px;
14
- animation-timing-function: ease-out;
15
- }
16
-
17
- 75% {
18
- top: 75px;
19
- animation-timing-function: ease-in;
20
- }
21
-
22
- to {
23
- top: 100px;
24
- }
25
- }
26
-
27
-
28
-
29
- div {
30
- animation-name: 'diagonal-slide';
31
- animation-duration: 5s;
32
- animation-iteration-count: 10;
33
- }
34
-
35
- @keyframes 'diagonal-slide' {
36
-
37
- from {
38
- left: 0;
39
- top: 0;
40
- }
41
-
42
- to {
43
- left: 100px;
44
- top: 100px;
45
- }
46
-
47
- }
48
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/tests/inputs/math.less DELETED
@@ -1,81 +0,0 @@
1
-
2
- .unary {
3
- // all operators are parsable as unary operators, anything
4
- // but - throws an error right now though,
5
-
6
- // this gives two numbers
7
- sub: 10 -5;
8
- // add: 10 +5; // error
9
- // div: 10 /5; // error
10
- // mul: 10 *5; // error
11
- }
12
-
13
- .spaces {
14
- // we can make the parser do math by leaving out the
15
- // space after the first value, or putting spaces on both sides
16
-
17
- sub: 10-5;
18
- sub: 10 - 5;
19
-
20
- add: 10+5;
21
- add: 10 + 5;
22
-
23
- // div: 10/5; // this wont work, read on
24
- div: 10 / 5;
25
-
26
- mul: 10*5;
27
- mul: 10 * 5;
28
- }
29
-
30
-
31
- .shorthand {
32
- // 10/5 is a special shorthand syntax that can be used in the font property
33
- div: 10/5; // outputs a literal 10/5
34
- }
35
-
36
-
37
- .parens {
38
- // if you are unsure, then just wrap the expression in parentheses and it will
39
- // always evaluate.
40
-
41
- // notice we no longer have unary operators, and these will evaluate
42
- sub: (10 -5);
43
- add: (10 +5);
44
- div: (10 /5);
45
- div: (10/5); // no longer interpreted as the shorthand
46
- mul: (10 *5);
47
- }
48
-
49
- .keyword-names {
50
- // watch out when doing math with keywords, - is a valid keyword character
51
- @a: 100;
52
- @b: 25;
53
- @a-: "hello";
54
- height: @a-@b; // here we get "hello" 25, not 75
55
- }
56
-
57
-
58
- // and now here are the tests
59
-
60
- .test {
61
- single: (5);
62
- single: 5+(5);
63
- single: (5)+((5));
64
-
65
- parens: (5 +(5)) -2;
66
- // parens: ((5 +(5)) -2); // FAILS - fixme
67
-
68
- math: (5 + 5)*(2 / 1);
69
- math: (5+5)*(2/1);
70
-
71
- width: 2 * (4 * (2 + (1 + 6))) - 1;
72
- height: ((2+3)*(2+3) / (9-4)) + 1;
73
- padding: (2px + 4px) 1em 2px 2;
74
-
75
- @var: (2 * 2);
76
- padding: (2 * @var) 4 4 (@var * 1px);
77
- width: (@var * @var) * 6;
78
- height: (7 * 7) + (8 * 8);
79
- margin: 4 * (5 + 5) / 2 - (@var * 2);
80
- }
81
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/tests/inputs/media.less DELETED
@@ -1,27 +0,0 @@
1
- @media screen, 3D {
2
- P { color: green; }
3
- }
4
- @media print {
5
- body { font-size: 10pt }
6
- }
7
- @media screen {
8
- body { font-size: 13px }
9
- }
10
- @media screen, print {
11
- body { line-height: 1.2 }
12
- }
13
-
14
- @media all and (min-width: 0px) {
15
- body { line-height: 1.2 }
16
- }
17
-
18
- @media all and (min-width: 0) {
19
- body { line-height: 1.2 }
20
- }
21
-
22
- @media
23
- screen and (min-width: 102.5em) and (max-width: 117.9375em),
24
- screen and (min-width: 150em) {
25
- body { color: blue }
26
- }
27
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/tests/inputs/misc.less DELETED
@@ -1,71 +0,0 @@
1
-
2
- @hello: "utf-8";
3
- @charset @hello;
4
-
5
- @color: #fff;
6
- @images: "/assets/images/";
7
- @images: @images + "test/";
8
- .topbar { background: url(@{images}topbar.png); }
9
- .hello { test: empty-function(@images, 40%, to(@color)); }
10
-
11
- .css3 {
12
- background-image: -webkit-gradient(linear, 0% 0%, 0% 90%,
13
- from(#E9A000), to(#A37000));
14
- }
15
-
16
-
17
- /**
18
-
19
- Here is a block comment
20
-
21
- **/
22
-
23
-
24
- // this is a comment
25
-
26
- .test, /*hello*/.world {
27
- border: 1px solid red; // world
28
- /* another property */
29
- color: url(http://mage-page.com);
30
- string: "hello /* this is not a comment */";
31
- world: "// neither is this";
32
- string: 'hello /* this is not a comment */' /*what if this is a comment */;
33
- world: '// neither is this' // hell world;
34
- ;
35
- what-/*something?*/ever: 100px;
36
- background: url(/*no comment here*/);
37
- }
38
-
39
-
40
- .mix(@arg) { color: @arg; }
41
- @aaa: aaa;
42
- @bbb: bbb;
43
- // make sure the opening selector isn't too greedy
44
- .cool {.mix("@{aaa}, @{bbb}")}
45
-
46
- .cool("{hello");
47
- .cool('{hello');
48
-
49
-
50
- // merging of mixins
51
- .span-17 { float: left; }
52
- .span-17 { width: 660px; }
53
-
54
- .x {.span-17;}
55
-
56
- .hi {
57
- pre {
58
- color: red;
59
- }
60
- }
61
-
62
- .hi {
63
- pre {
64
- color: blue;
65
- }
66
- }
67
-
68
- .rad {
69
- .hi;
70
- }
71
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/tests/inputs/mixin_functions.less DELETED
@@ -1,39 +0,0 @@
1
-
2
- @outer: 10px;
3
- @class(@var:22px, @car: 400px + @outer) {
4
- margin: @var;
5
- height: @car;
6
- }
7
-
8
- @group {
9
- @f(@color) {
10
- color: @color;
11
- }
12
- .cool {
13
- border-bottom: 1px solid green;
14
- }
15
- }
16
-
17
- .class(@width:200px) {
18
- padding: @width;
19
- }
20
-
21
- body {
22
- .class(2.0em);
23
- @group > @f(red);
24
- @class(10px, 10px + 2);
25
- @group > .cool;
26
- }
27
-
28
-
29
- @lots(@a: 10px, @b: 20px, @c: 30px, @d: 40px, @e: 4px, @f:3px, @g:2px, @h: 1px) {
30
- padding: @a @b @c @d;
31
- margin: @e @f @g @h;
32
- }
33
-
34
- .skip_args {
35
- @class(,12px);
36
- @lots(,,,88px,,12px);
37
- @group > @f(red,,,,);
38
- }
39
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/tests/inputs/mixin_merging.less.disable DELETED
@@ -1,100 +0,0 @@
1
-
2
- @tester {
3
- p, div { height: 10px; }
4
- }
5
-
6
- #test1 {
7
- div { color: red; }
8
- @tester;
9
- }
10
-
11
-
12
- @cool {
13
- a,b,i { width: 1px; }
14
- }
15
-
16
- #test2 {
17
- b { color: red; }
18
- @cool;
19
- }
20
-
21
- #test3 {
22
- @cool;
23
- b { color: red; }
24
- }
25
-
26
- @cooler {
27
- a { margin: 1px; }
28
- }
29
-
30
- #test4 {
31
- a, div, html { color: blue; }
32
- @cooler;
33
- }
34
-
35
- @hi {
36
- img, strong { float: right; }
37
- }
38
-
39
- #test5 {
40
- img, strong { padding: 2px; }
41
- @hi;
42
- }
43
-
44
- @nested {
45
- div, span {
46
- a {
47
- color: red;
48
- }
49
- }
50
- }
51
-
52
- #test6 {
53
- div, span {
54
- a {
55
- line-height: 10px;
56
- }
57
- }
58
- @nested;
59
- }
60
-
61
- @broken-nesting {
62
- div, span {
63
- strong, b {
64
- color: red;
65
- }
66
- }
67
-
68
- }
69
-
70
- #test7 {
71
- div {
72
- strong {
73
- margin: 1px;
74
- }
75
- }
76
- @broken-nesting;
77
- }
78
-
79
-
80
- @another-nest {
81
- a,b {
82
- i {
83
- color: red;
84
- }
85
-
86
- s {
87
- color: blue;
88
- }
89
- }
90
- }
91
-
92
- #test8 {
93
- a, b {
94
- i,s {
95
- background: red;
96
- }
97
- }
98
- @another-nest;
99
- }
100
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/tests/inputs/mixins.less DELETED
@@ -1,122 +0,0 @@
1
-
2
- @rounded-corners {
3
- border-radius: 10px;
4
- }
5
-
6
- .bold {
7
- @font-size: 20px;
8
- font-size: @font-size;
9
- font-weight: bold;
10
- }
11
-
12
- body #window {
13
- @rounded-corners;
14
- .bold;
15
- line-height: @font-size * 1.5;
16
- }
17
-
18
- #bundle {
19
- .button {
20
- display: block;
21
- border: 1px solid black;
22
- background-color: grey;
23
- &:hover { background-color: white }
24
- }
25
- }
26
- #header a {
27
- color: orange;
28
- #bundle > .button; // mixin the button class
29
- }
30
-
31
- div {
32
- @abstract {
33
- hello: world;
34
- b {
35
- color: blue;
36
- }
37
- }
38
-
39
- @abstract > b;
40
- @abstract;
41
- }
42
-
43
- @poop {
44
- big: baby;
45
- }
46
-
47
- body {
48
- div;
49
- }
50
-
51
- // not using > to list mixins
52
-
53
- .hello {
54
- .world {
55
- color: blue;
56
- }
57
- }
58
-
59
- .foobar {
60
- .hello .world;
61
- }
62
-
63
-
64
- .butter {
65
- .this .one .isnt .found;
66
- }
67
-
68
-
69
- // arguments
70
-
71
- .spam(@something: 100) {
72
- @wow: 23434;
73
- foo: @arguments;
74
- bar: @arguments;
75
- }
76
-
77
- .eggs {
78
- .spam(1px, 2px, 4px, 8px);
79
- }
80
-
81
- .first(@one, @two, @three, @four: cool) {
82
- cool: @arguments;
83
- }
84
-
85
- #hello {
86
- .first(one, two, three);
87
- }
88
-
89
-
90
- .rad() {
91
- cool: @arguments;
92
- }
93
-
94
- #world {
95
- @hello: "world";
96
- .rad("@{hello}");
97
- }
98
-
99
- .second() {
100
- things: @arguments;
101
- }
102
-
103
- #another {
104
- .second(red, blue, green);
105
- .second(red blue green);
106
- }
107
-
108
-
109
- .another() {
110
- .cool {
111
- color: @arguments;
112
- }
113
- }
114
-
115
- #day {
116
- .another(one,two, three);
117
- .another(one two three);
118
- }
119
-
120
-
121
-
122
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/tests/inputs/nested.less DELETED
@@ -1,60 +0,0 @@
1
- #header {
2
- color: black;
3
-
4
- .navigation {
5
- font-size: 12px;
6
- .border {
7
- .outside {
8
- color: blue;
9
- }
10
- }
11
- }
12
- .logo {
13
- width: 300px;
14
- &:hover { text-decoration: none }
15
- }
16
- }
17
-
18
- a { b { ul { li { color: green; } } } }
19
-
20
- this { will { not { show { } } } }
21
-
22
- .cool {
23
- div & { color: green; }
24
- p & span { color: yellow; }
25
- }
26
-
27
- another {
28
- .cool;
29
- }
30
-
31
- b {
32
- & .something {
33
- color: blue;
34
- }
35
-
36
- &.something {
37
- color: blue;
38
- }
39
- }
40
-
41
- .foo {
42
- .bar, .baz {
43
- & .qux {
44
- display: block;
45
- }
46
- .qux & {
47
- display: inline;
48
- }
49
- .qux & .biz {
50
- display: none;
51
- }
52
- }
53
- }
54
-
55
- b {
56
- hello [x="&yeah"] {
57
- color: red;
58
- }
59
- }
60
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/tests/inputs/out.html DELETED
File without changes
lib/vendor/lessphp/tests/inputs/scopes.less DELETED
@@ -1,40 +0,0 @@
1
-
2
-
3
- @x: 10;
4
- @some {
5
- @x: @x + 10;
6
- div {
7
- @x: @x + 10;
8
- other {
9
- @x: @x + 10;
10
- world {
11
- @x: @x + 10;
12
- height: @x;
13
- }
14
- }
15
- }
16
- }
17
-
18
-
19
- body {
20
- @some;
21
- }
22
-
23
- @some;
24
-
25
- .test(@x: 10) {
26
- height: @x;
27
- .test(@y: 11) {
28
- height: @y;
29
- .test(@z: 12) {
30
- height: @z;
31
- }
32
- .test;
33
- }
34
- .test;
35
- }
36
-
37
- pre {
38
- .test;
39
- }
40
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/tests/inputs/site_demos.less DELETED
@@ -1,120 +0,0 @@
1
- // these are the demos from the lessphp homepage
2
-
3
- default {
4
- @base: 24px;
5
- @border-color: #B2B;
6
-
7
- .underline { border-bottom: 1px solid green }
8
-
9
- #header {
10
- color: black;
11
- border: 1px solid @border-color + #222222;
12
-
13
- .navigation {
14
- font-size: @base / 2;
15
- a {
16
- .underline;
17
- }
18
- }
19
- .logo {
20
- width: 300px;
21
- &:hover { text-decoration: none }
22
- }
23
- }
24
- }
25
-
26
- variables {
27
- @a: 2;
28
- @x: @a * @a;
29
- @y: @x + 1;
30
- @z: @x * 2 + @y;
31
-
32
- @nice-blue: #5B83AD;
33
- @light-blue: @nice-blue + #111;
34
-
35
- @b: @a * 10;
36
- @c: #888;
37
- @fonts: "Trebuchet MS", Verdana, sans-serif;
38
-
39
- .variables {
40
- width: @z + 1cm; // 14cm
41
- height: @b + @x + 0px; // 24px
42
- color: @c;
43
- background: @light-blue;
44
- font-family: @fonts;
45
- }
46
- }
47
-
48
- mixins {
49
- .bordered {
50
- border-top: dotted 1px black;
51
- border-bottom: solid 2px black;
52
- }
53
- #menu a {
54
- color: #111;
55
- .bordered;
56
- }
57
-
58
- .post a {
59
- color: red;
60
- .bordered;
61
- }
62
- }
63
-
64
- nested-rules {
65
- #header {
66
- color: black;
67
-
68
- .navigation {
69
- font-size: 12px;
70
- }
71
- .logo {
72
- width: 300px;
73
- &:hover { text-decoration: none }
74
- }
75
- }
76
- }
77
-
78
- namespaces {
79
- #bundle {
80
- .button {
81
- display: block;
82
- border: 1px solid black;
83
- background-color: grey;
84
- &:hover { background-color: white }
85
- }
86
- }
87
- #header a {
88
- color: orange;
89
- #bundle > .button; // mixin the button class
90
- }
91
- }
92
-
93
- mixin-functions {
94
- @outer: 10px;
95
- @class(@var:22px, @car: 400px + @outer) {
96
- margin: @var;
97
- height: @car;
98
- }
99
-
100
- @group {
101
- @f(@color) {
102
- color: @color;
103
- }
104
- .cool {
105
- border-bottom: 1px solid green;
106
- }
107
- }
108
-
109
- .class(@width:200px) {
110
- padding: @width;
111
- }
112
-
113
- body {
114
- .class(2.0em);
115
- @group > @f(red);
116
- @class(10px, 10px + 2);
117
- @group > .cool;
118
- }
119
- }
120
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/tests/inputs/test-imports/file1.less DELETED
@@ -1,16 +0,0 @@
1
-
2
-
3
- /**
4
- * This is a test import file
5
- */
6
-
7
- @colors {
8
- div.bright {
9
- color: red;
10
- }
11
-
12
- div.sad {
13
- color: blue;
14
- }
15
- }
16
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/tests/inputs/test-imports/file2.less DELETED
@@ -1,6 +0,0 @@
1
-
2
- b {
3
- color: @color;
4
- padding: 16px;
5
- }
6
-
 
 
 
 
 
 
lib/vendor/lessphp/tests/inputs/variables.less DELETED
@@ -1,38 +0,0 @@
1
- @a: 2;
2
- @x: @a * @a;
3
- @y: @x + 1;
4
- @z: @y + @x * 2;
5
- @m: @z % @y;
6
-
7
- @nice-blue: #5B83AD;
8
- @light-blue: @nice-blue + #111;
9
-
10
- @rgb-color: rgb(20%, 15%, 80%);
11
- @rgba-color: rgba(23,68,149,0.5);
12
-
13
- @b: @a * 10px;
14
- @c: #888;
15
- @fonts: "Trebuchet MS", Verdana, sans-serif;
16
-
17
- .variables {
18
- width: @z + 1cm; // 14cm
19
- height: @b + @x + 0px; // 24px
20
- margin-top: -@b; // -20px
21
- margin-bottom: 10 - -@b; // 30px
22
- @c: @c + #001;
23
- color: @c;
24
- background: @light-blue;
25
- font-family: @fonts;
26
- margin: @m + 0px; // 3px
27
- font-size: 10px/12px;
28
- font-size: 120%/120%;
29
- }
30
-
31
- .external {
32
- color: @c;
33
- border: 1px solid @rgb-color;
34
- background: @rgba-color;
35
- padding: @nonexistant + 4px;
36
- }
37
-
38
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/tests/outputs/accessors.css DELETED
@@ -1,14 +0,0 @@
1
- .article { color:#294366; }
2
- .comment {
3
- width:960px;
4
- color:#294366;
5
- padding:10px;
6
- }
7
- .wow {
8
- height:960px;
9
- background-color:#294366;
10
- color:green;
11
- padding:;
12
- margin:;
13
- }
14
- .mix { font-size:10px; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/tests/outputs/attributes.css DELETED
@@ -1,35 +0,0 @@
1
- * { color:blue; }
2
- E { color:blue; }
3
- E[foo] { color:blue; }
4
- [foo] { color:blue; }
5
- [foo] .helloWorld { color:blue; }
6
- [foo].helloWorld { color:blue; }
7
- E[foo="barbar"] { color:blue; }
8
- E[foo~="hello#$@%@$#^"] { color:blue; }
9
- E[foo^="color: green;"] { color:blue; }
10
- E[foo$="239023"] { color:blue; }
11
- E[foo*="29302"] { color:blue; }
12
- E[foo|="239032"] { color:blue; }
13
- E:root { color:blue; }
14
- E:nth-child(odd) { color:blue; }
15
- E:nth-child(2n+1) { color:blue; }
16
- E:nth-child(5) { color:blue; }
17
- E:nth-last-child(-n+2) { color:blue; }
18
- E:nth-of-type(2n) { color:blue; }
19
- E:nth-last-of-type(n) { color:blue; }
20
- E:first-child { color:blue; }
21
- E:last-child { color:blue; }
22
- E:first-of-type { color:blue; }
23
- E:last-of-type { color:blue; }
24
- E:only-child { color:blue; }
25
- E:only-of-type { color:blue; }
26
- E:empty { color:blue; }
27
- E:lang(en) { color:blue; }
28
- E::first-line { color:blue; }
29
- E::before { color:blue; }
30
- E#id { color:blue; }
31
- E:not(:link) { color:blue; }
32
- E F { color:blue; }
33
- E > F { color:blue; }
34
- E + F { color:blue; }
35
- E ~ F { color:blue; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/tests/outputs/builtins.css DELETED
@@ -1,17 +0,0 @@
1
- body {
2
- color:"hello world";
3
- height:5.1666666666667;
4
- height:5px;
5
- width:0.66666666666667;
6
- width:1;
7
- width:0;
8
- color:#00112233;
9
- color:#992c3742;
10
- }
11
- format {
12
- format:"rgb(32, 128, 64)";
13
- format-string:"hello world";
14
- format-multiple:"hello earth 2";
15
- format-url-encode:'red is %A';
16
- eformat:rgb(32, 128, 64);
17
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/tests/outputs/colors.css DELETED
@@ -1,57 +0,0 @@
1
- body {
2
- color:#996d33;
3
- color:rgba(153,109,51,0.3);
4
- lighten:#ffffff;
5
- lighten:#7c8df2;
6
- lighten:rgba(222,140,129,0.5);
7
- darken:#d6d6d6;
8
- darken:#0d1e81;
9
- darken:rgba(18,42,185,0.5);
10
- saturate:#f1eded;
11
- saturate:#0025fe;
12
- saturate:rgba(10,44,244,0.5);
13
- desaturate:#efefef;
14
- desaturate:#3349cb;
15
- desaturate:rgba(36,62,218,0.5);
16
- spin:#efefef;
17
- spin:#2d17e7;
18
- spin:rgba(59,23,231,0.5);
19
- spin:#efefef;
20
- spin:#1769e7;
21
- spin:rgba(23,119,231,0.5);
22
- one:rgba(171,205,239,1);
23
- one:rgba(171,205,239,1);
24
- two:rgba(2,159,35,0.9);
25
- two:rgba(2,159,35,0.9);
26
- three:rgba(1,2,3,0.6);
27
- three:rgba(1,2,3,0.6);
28
- four:rgba(1,2,3,0);
29
- four:rgba(1,2,3,0);
30
- hue:282;
31
- sat:33;
32
- lit:12;
33
- what:#dff1da;
34
- zero:#343434;
35
- zero:#003468;
36
- zero:#000000;
37
- zero:#ffffff;
38
- zero:#000000;
39
- zero:#ffffff;
40
- zero:#ffffff;
41
- zero:#ffffff;
42
- zero:#1d5612;
43
- zero:#56124b;
44
- zero:#000000;
45
- zero:#ffffff;
46
- }
47
- alpha {
48
- g:0;
49
- g:1;
50
- }
51
- fade {
52
- f:rgba(0,0,0,0.5);
53
- f:rgba(255,255,255,0.2);
54
- f:rgba(34,23,64,0.5);
55
- }
56
- .mix { color:#7f7f7f; }
57
- .percent { per:50%; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/tests/outputs/compile_on_mixin.css DELETED
@@ -1,11 +0,0 @@
1
- body { height:22px; }
2
- body ul { height:20px; }
3
- body ul li { height:10px; }
4
- body ul li div span, body ul li link {
5
- margin:10px;
6
- color:red;
7
- }
8
- body ul div, body ul p { border:1px; }
9
- body ul div.hello, body ul p.hello { color:green; }
10
- body ul div :what, body ul p :what { color:blue; }
11
- body ul a b { color:blue; }
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/tests/outputs/escape.css DELETED
@@ -1,13 +0,0 @@
1
- body {
2
- border:this is simple;
3
- border:this;
4
- border:this is simple;
5
- border:1232;
6
- border:world;
7
- border:onemore;
8
- border:;
9
- line-height:eating rice;
10
- line-height:string cheese;
11
- line-height:a b c string me d e f;
12
- }
13
- .class { filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='image.png'); }
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/tests/outputs/font_family.css DELETED
@@ -1,17 +0,0 @@
1
- @font-face {
2
- font-family:Graublau Sans Web;
3
- src:url(fonts/GraublauWeb.otf) format("opentype");
4
- }
5
- @font-face {
6
- font-family:Gentium;
7
- src:url('fonts/Gentium.ttf');
8
- }
9
- @font-face {
10
- font-family:Gentium;
11
- src:url("fonts/GentiumItalic.ttf");
12
- font-style:italic;
13
- }
14
- h2 {
15
- font-family:Gentium;
16
- crazy:maroon;
17
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/tests/outputs/hacks.css DELETED
@@ -1 +0,0 @@
1
- :root .alert-message, :root .btn { border-radius:0 \0; }
 
lib/vendor/lessphp/tests/outputs/import.css DELETED
@@ -1,14 +0,0 @@
1
- @import url("something.css") media;
2
- @import url("something.css") media;
3
- @import url("something.css") media, screen, print;
4
- b {
5
- color:maroon;
6
- padding:16px;
7
- }
8
- body { line-height:10em; }
9
- body div.bright { color:red; }
10
- body div.sad { color:blue; }
11
- div b {
12
- color:fuchsia;
13
- padding:16px;
14
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/tests/outputs/keyframes.css DELETED
@@ -1,36 +0,0 @@
1
- @keyframes 'bounce' {
2
- from {
3
- top:100px;
4
- animation-timing-function:ease-out;
5
- }
6
- 25% {
7
- top:50px;
8
- animation-timing-function:ease-in;
9
- }
10
- 50% {
11
- top:100px;
12
- animation-timing-function:ease-out;
13
- }
14
- 75% {
15
- top:75px;
16
- animation-timing-function:ease-in;
17
- }
18
- to {
19
- top:100px;
20
- }
21
- }
22
- div {
23
- animation-name:'diagonal-slide';
24
- animation-duration:5s;
25
- animation-iteration-count:10;
26
- }
27
- @keyframes 'diagonal-slide' {
28
- from {
29
- left:0;
30
- top:0;
31
- }
32
- to {
33
- left:100px;
34
- top:100px;
35
- }
36
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/tests/outputs/math.css DELETED
@@ -1,34 +0,0 @@
1
- .unary { sub:10 -5; }
2
- .spaces {
3
- sub:5;
4
- sub:5;
5
- add:15;
6
- add:15;
7
- div:2;
8
- mul:50;
9
- mul:50;
10
- }
11
- .shorthand { div:10/5; }
12
- .parens {
13
- sub:5;
14
- add:15;
15
- div:2;
16
- div:2;
17
- mul:50;
18
- }
19
- .keyword-names { height:"hello" 25; }
20
- .test {
21
- single:5;
22
- single:10;
23
- single:10;
24
- parens:10 -2;
25
- math:20;
26
- math:20;
27
- width:71;
28
- height:6;
29
- padding:6px 1em 2px 2;
30
- padding:8 4 4 4px;
31
- width:96;
32
- height:113;
33
- margin:12;
34
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/tests/outputs/media.css DELETED
@@ -1,22 +0,0 @@
1
- @media screen, 3D {
2
- P { color:green; }
3
- }
4
- @media print {
5
- body { font-size:10pt; }
6
- }
7
- @media screen {
8
- body { font-size:13px; }
9
- }
10
- @media screen, print {
11
- body { line-height:1.2; }
12
- }
13
- @media all and (min-width: 0px) {
14
- body { line-height:1.2; }
15
- }
16
- @media all and (min-width: 0) {
17
- body { line-height:1.2; }
18
- }
19
- @media screen and (min-width: 102.5em) and (max-width: 117.9375em),
20
- screen and (min-width: 150em) {
21
- body { color:blue; }
22
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/tests/outputs/misc.css DELETED
@@ -1,27 +0,0 @@
1
- @charset "utf-8";
2
- color:"aaa, bbb";
3
- color:"aaa, bbb";
4
- .topbar { background:url(/assets/images/test/topbar.png); }
5
- .hello { test:empty-function("/assets/images/test/",40%,to(#ffffff)); }
6
- .css3 { background-image:-webkit-gradient(linear,0% 0%,0% 90%,from(#e9a000),to(#a37000)); }
7
- .test, .world {
8
- border:1px solid red;
9
- color:url(http://mage-page.com);
10
- string:"hello /* this is not a comment */";
11
- world:"// neither is this";
12
- string:'hello /* this is not a comment */';
13
- world:'// neither is this';
14
- what-ever:100px;
15
- background:url(/*no comment here*/);
16
- }
17
- .cool { color:"aaa, bbb"; }
18
- .span-17 { float:left; }
19
- .span-17 { width:660px; }
20
- .x {
21
- float:left;
22
- width:660px;
23
- }
24
- .hi pre { color:red; }
25
- .hi pre { color:blue; }
26
- .rad pre { color:red; }
27
- .rad pre { color:blue; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/tests/outputs/mixin_functions.css DELETED
@@ -1,14 +0,0 @@
1
- body {
2
- padding:2.0em;
3
- color:red;
4
- margin:10px;
5
- height:12px;
6
- border-bottom:1px solid green;
7
- }
8
- .skip_args {
9
- margin:22px;
10
- height:12px;
11
- padding:10px 20px 30px 88px;
12
- margin:4px 12px 2px 1px;
13
- color:red;
14
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/tests/outputs/mixin_merging.css DELETED
@@ -1,42 +0,0 @@
1
- #test1 div {
2
- color:red;
3
- height:10px;
4
- }
5
- #test1 p { height:10px; }
6
- #test2 b {
7
- color:red;
8
- width:1px;
9
- }
10
- #test2 a, #test2 i { width:1px; }
11
- #test3 a, #test3 i { width:1px; }
12
- #test3 b {
13
- width:1px;
14
- color:red;
15
- }
16
- #test4 a {
17
- color:blue;
18
- margin:1px;
19
- }
20
- #test4 div, #test4 html { color:blue; }
21
- #test5 img, #test5 strong {
22
- padding:2px;
23
- float:right;
24
- }
25
- #test6 div a, #test6 span a {
26
- line-height:10px;
27
- color:red;
28
- }
29
- #test7 div strong {
30
- margin:1px;
31
- color:red;
32
- }
33
- #test7 div b { color:red; }
34
- #test7 span strong, #test7 span b { color:red; }
35
- #test8 a i, #test8 b i {
36
- background:red;
37
- color:red;
38
- }
39
- #test8 a s, #test8 b s {
40
- background:red;
41
- color:blue;
42
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/tests/outputs/mixins.css DELETED
@@ -1,47 +0,0 @@
1
- .bold {
2
- font-size:20px;
3
- font-weight:bold;
4
- }
5
- body #window {
6
- border-radius:10px;
7
- font-size:20px;
8
- font-weight:bold;
9
- line-height:30px;
10
- }
11
- #bundle .button {
12
- display:block;
13
- border:1px solid black;
14
- background-color:grey;
15
- }
16
- #bundle .button:hover { background-color:white; }
17
- #header a {
18
- color:orange;
19
- display:block;
20
- border:1px solid black;
21
- background-color:grey;
22
- }
23
- #header a:hover { background-color:white; }
24
- div {
25
- color:blue;
26
- hello:world;
27
- }
28
- div b { color:blue; }
29
- body {
30
- color:blue;
31
- hello:world;
32
- }
33
- body b { color:blue; }
34
- .hello .world { color:blue; }
35
- .foobar { color:blue; }
36
- .eggs {
37
- foo:1px 2px 4px 8px;
38
- bar:1px 2px 4px 8px;
39
- }
40
- #hello { cool:one two three cool; }
41
- #world { cool:"world"; }
42
- #another {
43
- things:red blue green;
44
- things:red blue green;
45
- }
46
- #day .cool { color:one two three; }
47
- #day .cool { color:one two three; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/tests/outputs/nested.css DELETED
@@ -1,16 +0,0 @@
1
- #header { color:black; }
2
- #header .navigation { font-size:12px; }
3
- #header .navigation .border .outside { color:blue; }
4
- #header .logo { width:300px; }
5
- #header .logo:hover { text-decoration:none; }
6
- a b ul li { color:green; }
7
- div .cool { color:green; }
8
- p .cool span { color:yellow; }
9
- div another { color:green; }
10
- p another span { color:yellow; }
11
- b .something { color:blue; }
12
- b.something { color:blue; }
13
- .foo .bar .qux, .foo .baz .qux { display:block; }
14
- .qux .foo .bar, .qux .foo .baz { display:inline; }
15
- .qux .foo .bar .biz, .qux .foo .baz .biz { display:none; }
16
- b hello [x="&yeah"] { color:red; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/tests/outputs/nesting.css DELETED
@@ -1,6 +0,0 @@
1
- #header .navigation .border .outside { color:blue; }
2
- #header .navigation { font-size:12px; }
3
- #header .logo:hover { text-decoration:none; }
4
- #header .logo { width:300px; }
5
- #header { color:black; }
6
- a b ul li { color:green; }
 
 
 
 
 
 
lib/vendor/lessphp/tests/outputs/scopes.css DELETED
@@ -1,7 +0,0 @@
1
- body div other world { height:50; }
2
- div other world { height:50; }
3
- pre {
4
- height:10;
5
- height:11;
6
- height:12;
7
- }
 
 
 
 
 
 
 
lib/vendor/lessphp/tests/outputs/site_demos.css DELETED
@@ -1,54 +0,0 @@
1
- default .underline { border-bottom:1px solid green; }
2
- default #header {
3
- color:black;
4
- border:1px solid #dd44dd;
5
- }
6
- default #header .navigation { font-size:12px; }
7
- default #header .navigation a { border-bottom:1px solid green; }
8
- default #header .logo { width:300px; }
9
- default #header .logo:hover { text-decoration:none; }
10
- variables .variables {
11
- width:14cm;
12
- height:24px;
13
- color:#888888;
14
- background:#6c94be;
15
- font-family:"Trebuchet MS", Verdana, sans-serif;
16
- }
17
- mixins .bordered {
18
- border-top:dotted 1px black;
19
- border-bottom:solid 2px black;
20
- }
21
- mixins #menu a {
22
- color:#111111;
23
- border-top:dotted 1px black;
24
- border-bottom:solid 2px black;
25
- }
26
- mixins .post a {
27
- color:red;
28
- border-top:dotted 1px black;
29
- border-bottom:solid 2px black;
30
- }
31
- nested-rules #header { color:black; }
32
- nested-rules #header .navigation { font-size:12px; }
33
- nested-rules #header .logo { width:300px; }
34
- nested-rules #header .logo:hover { text-decoration:none; }
35
- namespaces #bundle .button {
36
- display:block;
37
- border:1px solid black;
38
- background-color:grey;
39
- }
40
- namespaces #bundle .button:hover { background-color:white; }
41
- namespaces #header a {
42
- color:orange;
43
- display:block;
44
- border:1px solid black;
45
- background-color:grey;
46
- }
47
- namespaces #header a:hover { background-color:white; }
48
- mixin-functions body {
49
- padding:2.0em;
50
- color:red;
51
- margin:10px;
52
- height:12px;
53
- border-bottom:1px solid green;
54
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/tests/outputs/variables.css DELETED
@@ -1,18 +0,0 @@
1
- .variables {
2
- width:14cm;
3
- height:24px;
4
- margin-top:-20px;
5
- margin-bottom:30px;
6
- color:#888899;
7
- background:#6c94be;
8
- font-family:"Trebuchet MS", Verdana, sans-serif;
9
- margin:3px;
10
- font-size:10px/12px;
11
- font-size:120%/120%;
12
- }
13
- .external {
14
- color:#888888;
15
- border:1px solid #3326cc;
16
- background:rgba(23,68,149,0.5);
17
- padding:4px;
18
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/lessphp/tests/test.php DELETED
@@ -1,139 +0,0 @@
1
- <?php
2
- error_reporting(E_ALL);
3
-
4
- /**
5
- * Go through all files matching pattern in input directory
6
- * and compile them, then compare them to paired file in
7
- * output directory.
8
- */
9
- $difftool = 'meld';
10
- $input = array(
11
- 'dir' => 'inputs',
12
- 'glob' => '*.less',
13
- );
14
-
15
- $output = array(
16
- 'dir' => 'outputs',
17
- 'filename' => '%s.css',
18
- );
19
-
20
-
21
- $prefix = realpath(dirname(__FILE__));
22
- require $prefix.'/../lessc.inc.php';
23
-
24
- $compiler = new lessc();
25
- $compiler->importDir = $input['dir'].'/test-imports';
26
-
27
- $fa = 'Fatal Error: ';
28
- if (php_sapi_name() != 'cli') {
29
- exit($fa.$argv[0].' must be run in the command line.');
30
- }
31
-
32
- $exe = array_shift($argv); // remove filename
33
- function flag($f) {
34
- if (func_num_args() > 1) {
35
- foreach (func_get_args() as $f) if (flag($f)) return true;
36
- return false;
37
- }
38
- global $argv;
39
- $pre = strlen($f) > 1 ? '--' : '-';
40
- foreach ($argv as $a) {
41
- if (preg_match('/^'.$pre.$f.'($|\s)/', $a)) return true;
42
- }
43
- return false;
44
- }
45
-
46
- if (flag('h', 'help')) {
47
- exit('help me');
48
- }
49
-
50
- $input['dir'] = $prefix.'/'.$input['dir'];
51
- $output['dir'] = $prefix.'/'.$output['dir'];
52
- if (!is_dir($input['dir']) || !is_dir($output['dir']))
53
- exit($fa." both input and output directories must exist\n");
54
-
55
- // get the first non flag as search string
56
- $searchString = null;
57
- foreach ($argv as $a) {
58
- if (strlen($a) > 0 && $a{0} != '-') {
59
- $searchString = $a;
60
- break;
61
- }
62
- }
63
-
64
- $tests = array();
65
- $matches = glob($input['dir'].'/'.(!is_null($searchString) ? '*'.$searchString : '' ).$input['glob']);
66
- if ($matches) {
67
- foreach ($matches as $fname) {
68
- extract(pathinfo($fname)); // for $filename, from php 5.2
69
- $tests[] = array(
70
- 'in' => $fname,
71
- 'out' => $output['dir'].'/'.sprintf($output['filename'], $filename),
72
- );
73
- }
74
- }
75
-
76
- $count = count($tests);
77
- $compiling = flag('C');
78
- $showDiff = flag('d', 'diff');
79
- echo ($compiling ? "Compiling" : "Running")." $count test".($count == 1 ? '' : 's').":\n";
80
-
81
- function dump($msgs, $depth = 1, $prefix=" ") {
82
- if (!is_array($msgs)) $msgs = array($msgs);
83
- foreach ($msgs as $m) {
84
- echo str_repeat($prefix, $depth).' - '.$m."\n";
85
- }
86
- }
87
-
88
- $fail_prefix = " ** ";
89
-
90
- $i = 1;
91
- foreach ($tests as $test) {
92
- printf(" [Test %04d/%04d] %s -> %s\n", $i, $count, basename($test['in']), basename($test['out']));
93
-
94
- try {
95
- ob_start();
96
- $parsed = trim($compiler->parse(file_get_contents($test['in'])));
97
- ob_end_clean();
98
- } catch (exception $e) {
99
- dump(array(
100
- "Failed to compile input, reason:",
101
- $e->getMessage(),
102
- "Aborting"
103
- ), 1, $fail_prefix);
104
- break;
105
- }
106
-
107
- if ($compiling) {
108
- file_put_contents($test['out'], $parsed);
109
- } else {
110
- if (!is_file($test['out'])) {
111
- dump(array(
112
- "Failed to find output file: $test[out]",
113
- "Maybe you forgot to compile tests?",
114
- "Aborting"
115
- ), 1, $fail_prefix);
116
- break;
117
- }
118
- $expected = trim(file_get_contents($test['out']));
119
-
120
- if ($expected != $parsed) {
121
- if ($showDiff) {
122
- dump("Failed:", 1, $fail_prefix);
123
- $tmp = $test['out'].".tmp";
124
- file_put_contents($tmp, $parsed);
125
- system($difftool.' '.$test['out'].' '.$tmp);
126
- unlink($tmp);
127
-
128
- dump("Aborting");
129
- break;
130
- } else dump("Failed, run with -d flag to view diff", 1, $fail_prefix);
131
- } else {
132
- dump("Passed");
133
- }
134
- }
135
-
136
- $i++;
137
- }
138
-
139
- ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/vendor/plugin-toolkit/BaseConfiguration.class.php CHANGED
@@ -284,7 +284,7 @@ abstract class WPPluginToolkitConfiguration
284
  if (!$url = get_option( 'upload_url_path'))
285
  {
286
  $url = (empty($upload_path) or ($upload_path == $dir))
287
- ? WP_CONTENT_URL . '/uploads'
288
  : trailingslashit($siteurl).$upload_path;
289
  }
290
  }
284
  if (!$url = get_option( 'upload_url_path'))
285
  {
286
  $url = (empty($upload_path) or ($upload_path == $dir))
287
+ ? content_url() . '/uploads'
288
  : trailingslashit($siteurl).$upload_path;
289
  }
290
  }
readme.txt CHANGED
@@ -1,10 +1,10 @@
1
  === WP-LESS ===
2
- Contributors: oncletom
3
  Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=752034
4
  Tags: dev, theme, themes, toolkit, plugin-toolkit, less, lesscss, lessc, lessphp, productivity, style, stylesheet, api
5
- Requires at least: 2.8
6
- Tested up to: 3.3
7
- Stable tag: 1.4.2
8
 
9
  Implementation of LESS (Leaner CSS) in order to make themes development easier.
10
 
@@ -22,15 +22,19 @@ Theme developers can even bundle the plugin without worrying about conflicts: ju
22
  * Accessors (inherit a value from a specific rule)
23
  * Functions (logic operations for dynamic results)
24
 
25
- The plugin lets you concentrate on what you need: coding CSS. Everything else is handled automatically, from cache management to user delivery.
26
  Seriously.
27
 
 
 
 
 
28
  = Requirements =
29
 
30
  The sole requirement is to use WordPress API and LESS convention: the `.less` extension.
31
 
32
- **Minimal Requirements**: PHP 5.1.2 and WordPress 2.8.
33
- **Relies on**: [LESSPHP 0.3.1](http://leafo.net/lessphp/), [plugin-toolkit](http://wordpress.org/extend/plugins/plugin-toolkit/).
34
 
35
  *Notice*: in case you'd like to drop the usage of this plugin, it's safe to do it. You will just need to convert back your stylesheets to CSS.
36
 
@@ -46,8 +50,105 @@ The sole requirement is to use WordPress API and LESS convention: the `.less` ex
46
  1. Unzip it in your plugin folder (by default, `wp-content/plugins`)
47
  1. Activate it through your WordPress plugins administration page
48
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  == Changelog ==
50
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  = Version 1.4.2 =
52
 
53
  * feature: if `WP_DEBUG` is set to true, compilation is done on every page
@@ -117,60 +218,19 @@ The sole requirement is to use WordPress API and LESS convention: the `.less` ex
117
 
118
 
119
  == Frequently Asked Questions ==
120
- = How do I transform a LESS file into CSS? =
121
- Consider this bit of code to automatically enqueue your stylesheet from your theme (or plugin):
122
- `wp_enqueue_style('mytheme', get_bloginfo('template_directory').'/style.css', array(), '', 'screen, projection');`
123
-
124
- To make it process by WP-LESS, switch to this way:
125
- `wp_enqueue_style('mytheme', get_bloginfo('template_directory').'/style.less', array(), '', 'screen, projection');`
126
-
127
- You understood well: you just need to change the extension of the file.
128
 
129
- = And if I don't use the wp_enqueue_style method? =
130
- For the moment, it's the unique way to handle this.
131
- Helpers will be provided soon to include LESS files in your templates in a fluent way.
132
 
133
- = What if a *.less file contains only pure CSS? =
134
- Nothing special. The LESS parser is fully compliant with CSS syntax.
135
- It means nothing will be broken so don't worry.
136
 
137
- = I'm a themer and I don't want to ask my users to activate this plugin =
138
- It's a very good moto. Since the 1.1 release, there is a special bootstrap file: `bootstrap-for-theme.php`.
139
- Everything is prepared and documented inside, with examples and hint.
140
-
141
- Just help yourself!
142
-
143
- = I want to inject custom variables =
144
- LESS PHP 0.3.0 introduced a native way to set variables from PHP.
145
-
146
- If you initialized the class by yourself, do it this way:
147
- `$WPLessPlugin->addVariable('@default_color', '#fff');`
148
-
149
- If you don't manage the plugin by yourself:
150
- `WPPluginToolkitPlugin::getInstance('WPLess')->addVariable($name, $value);`
151
-
152
- And if you want to do that from a theme, with less code:
153
-
154
- 1. include the `wp-less/lib/helper/ThemeHelper.php` file;
155
- 1. call `less_add_variable('@default_color', '#fff')`
156
-
157
- = I want to create a new custom LESS function =
158
- LESS PHP 0.3.1. introducted a new way to register functions without subclassing the compiler.
159
-
160
- If you initialized the class by yourself, do it this way:
161
- `$WPLessPlugin->registerFunction('double', 'lessphp_double');`
162
-
163
- If you don't manage the plugin by yourself:
164
- `WPPluginToolkitPlugin::getInstance('WPLess')->registerFunction('double', 'lessphp_double');`
165
-
166
- And if you want to do that from a theme, with less code:
167
 
168
- 1. include the `wp-less/lib/helper/ThemeHelper.php` file;
169
- 1. call `less_register_function('double', 'lessphp_double')`
170
 
171
- **Notice**: in this example, we assume `lessphp_double` is a valid [PHP callback](http://php.net/manual/en/language.pseudo-types.php#language.types.callback), as stated in `lessphp` documentation.
172
 
173
- == Upgrade Notice ==
174
 
175
  = 1.4 =
176
 
@@ -180,4 +240,4 @@ Please check your LESS syntax [according to the document](http://leafo.net/lessp
180
 
181
  == Screenshots ==
182
 
183
- 1. Sample of LESS to CSS conversion.
1
  === WP-LESS ===
2
+ Contributors: fabrizim,oncletom
3
  Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=752034
4
  Tags: dev, theme, themes, toolkit, plugin-toolkit, less, lesscss, lessc, lessphp, productivity, style, stylesheet, api
5
+ Requires at least: 3.2
6
+ Tested up to: 5.8
7
+ Stable tag: trunk
8
 
9
  Implementation of LESS (Leaner CSS) in order to make themes development easier.
10
 
22
  * Accessors (inherit a value from a specific rule)
23
  * Functions (logic operations for dynamic results)
24
 
25
+ The plugin lets you concentrate on what you need: coding CSS. Everything else is handled automatically, from cache management to user delivery.
26
  Seriously.
27
 
28
+ = Documentation =
29
+
30
+ Advanced topics on how to use the plugin API are [available on the Github project documentation](https://github.com/oncletom/wp-less/tree/master/doc).
31
+
32
  = Requirements =
33
 
34
  The sole requirement is to use WordPress API and LESS convention: the `.less` extension.
35
 
36
+ **Minimal Requirements**: PHP 5.3 and WordPress 3.2.
37
+ **Relies on**: [Less.php](http://lessphp.gpeasy.com/), [plugin-toolkit](http://wordpress.org/extend/plugins/plugin-toolkit/).
38
 
39
  *Notice*: in case you'd like to drop the usage of this plugin, it's safe to do it. You will just need to convert back your stylesheets to CSS.
40
 
50
  1. Unzip it in your plugin folder (by default, `wp-content/plugins`)
51
  1. Activate it through your WordPress plugins administration page
52
 
53
+ == Upgrade Notice ==
54
+
55
+ = 1.6.0 =
56
+
57
+ Warning: this release has some breaking changes. If you the old selector expression syntax (eg. Bootstrap 2.3) then it’s recommended to use 0.3.9.
58
+
59
+ * Add support for ; as argument delimiter
60
+ * Add support for passing arguments by name to mixin
61
+ * Remove old selector expression syntax ("hello")
62
+ * Remove ability to skip arguments by repeating delimiter
63
+ * Add built in functions: sin, cos, tan, asin, acos, atan, pow, pi, mod, sqrt, extract
64
+ * Fix bug where @arguments was not getting values from ...
65
+ * Selector interpolation works inside of brackets in selector
66
+ * Fix bug when resolving mixin that has same name as enclosing class
67
+ * Duplicate properties are now removed from output
68
+
69
  == Changelog ==
70
 
71
+ = Version 1.9.0 =
72
+
73
+ * feature: default less compiler is now [wikimedia/less.php](https://github.com/wikimedia/less.php)
74
+ * feature: output is compressed by default
75
+
76
+ = Version 1.8.0 =
77
+
78
+ * feature: default less compiler is now [oyejorge/less.php](http://lessphp.gpeasy.com/) ([#90](https://github.com/oncletom/wp-less/pull/90)
79
+
80
+ = Version 1.7.6 =
81
+
82
+ * info: updated vendored lessphp libraries versions (oyejorge/less.php@1.7.0.5)
83
+
84
+ = Version 1.7.5 =
85
+
86
+ * feature: now works properly in the admin side ([#68](https://github.com/oncletom/wp-less/pull/68))
87
+
88
+ = Version 1.7.4 =
89
+
90
+ * feature: favour composer autoload to manual PHP `require` ([#64](https://github.com/oncletom/wp-less/pull/64))
91
+
92
+ = Version 1.7.3 =
93
+
94
+ * bug: fixed the LESS library loading ([#63](https://github.com/oncletom/wp-less/issues/63))
95
+ * doc: documented the new LESS library swapping
96
+
97
+ = Version 1.7.0 =
98
+
99
+ * feature: ability to provide your own flavour of `lessphp` or `less.php` ([#53](https://github.com/oncletom/wp-less/pull/53))
100
+ * bug: fixed stylesheet directory computation ([#61](https://github.com/oncletom/wp-less/pull/61))
101
+ * bug: unlink exception during utpdated files cleanup ([#49](https://github.com/oncletom/wp-less/pull/49))
102
+ * style: code cleanup ([#56](https://github.com/oncletom/wp-less/pull/56), [#55](https://github.com/oncletom/wp-less/pull/55))
103
+
104
+ = Version 1.6.0 =
105
+
106
+ Read the UPGRADE NOTICE carefully as this release contains BC change. Hence the version bump to `1.6.0`.
107
+
108
+ * lessphp: updated to v0.4.0
109
+
110
+ = Version 1.5.4 =
111
+
112
+ * bug: fixed stylesheet URL computation ([#38](https://github.com/oncletom/wp-less/pull/38))
113
+ * bug: fixed cache-hit miss after stylesheet garbage collection ([#40](https://github.com/oncletom/wp-less/pull/40))
114
+ * added a CONTRIBUTORS file
115
+
116
+ = Version 1.5.3 =
117
+
118
+ * lessphp: updated to v0.3.9
119
+
120
+ = Version 1.5.2 =
121
+
122
+ * bug: fixed garbage collector bug ([#28](https://github.com/oncletom/wp-less/pull/28))
123
+ * bug: fixed cachebusting URI generation in deep mode ([#29](https://github.com/oncletom/wp-less/pull/29))
124
+ * bug: fixed access to Plugin instance, matching the documentation ([#39](https://github.com/oncletom/wp-less/pull/39))
125
+
126
+ = Version 1.5.1 =
127
+
128
+ * feature(beta): less stylesheets can be enqueued in `wp-admin`
129
+ * feature: added `WPLessConfiguration::getTtl` method to let you configure the delay of old-files cleanup
130
+ * bug: fixed automatic replacements with absolute and data uri ([#19](https://github.com/oncletom/wp-less/pull/19))
131
+ * bug: fixed garbage collector; was pruning active stylesheets even if too old (buggy with active cache) ([#20](https://github.com/oncletom/wp-less/pull/20))
132
+
133
+ = Version 1.5 =
134
+
135
+ Mostly issues related to `lessphp` 0.3.8 features.
136
+
137
+ * /!\ Leveraged PHP Minimum Version to 5.2.4 /!\ ([WordPress already asks you the same](http://wordpress.org/about/requirements/))
138
+ * [dev documentation available online](https://github.com/oncletom/wp-less/tree/master/doc)
139
+ * bug: stylesheets compilation is now processed on `wp_enqueue_scripts` ([prop of @RixTox](https://github.com/oncletom/wp-less/pull/18))
140
+ * feature: providing stylesheet and template directory uri variables (`@stylesheet_directory_uri` & `@template_directory_uri`) following WordPress convention
141
+ * feature: Pruning old compiled files [#15](https://github.com/oncletom/wp-less/pull/15)
142
+ * feature: Smarter LESS compilation (following @import file updates) [#13](https://github.com/oncletom/wp-less/pull/13)
143
+ * feature: Systematic LESS rebuild through configuration [#14](https://github.com/oncletom/wp-less/pull/14)
144
+ * improvement: Match lessphp variable API [#12](https://github.com/oncletom/wp-less/pull/12)
145
+
146
+ = Version 1.4.3 =
147
+
148
+ * bug: fixed HTTPS/Networked Blog URL replacement ([#8](https://github.com/oncletom/wp-less/pull/8), [#9](https://github.com/oncletom/wp-less/pull/9))
149
+ * bug: fixed the `property of non-object in Plugin.class.php` bug
150
+ * lessphp: updated to 0.3.8 (compatible with lessjs 1.3)
151
+
152
  = Version 1.4.2 =
153
 
154
  * feature: if `WP_DEBUG` is set to true, compilation is done on every page
218
 
219
 
220
  == Frequently Asked Questions ==
 
 
 
 
 
 
 
 
221
 
222
+ Lots of efforts have been done to write a [consistent documentation](https://github.com/oncletom/wp-less/tree/master/doc)
223
+ to address issues you may encounter.
 
224
 
225
+ It covers topics like path customization, declaring LESS variables from PHP, creating new LESS functions etc.
 
 
226
 
227
+ == Upgrade Notice ==
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
228
 
229
+ = 1.5 =
 
230
 
231
+ Some changes in the API may breaks compatibility with your PHP code dealing with `wp-less`.
232
 
233
+ Please [open issues](https://github.com/oncletom/wp-less/issues) and describe your technical problems [if the usage is not documented](https://github.com/oncletom/wp-less/tree/master/doc).
234
 
235
  = 1.4 =
236
 
240
 
241
  == Screenshots ==
242
 
243
+ 1. Sample of LESS to CSS conversion.