WP-LESS - Version 1.4

Version Description

As lessphp has been upgraded to 0.3.0, its behavior changed a little bit.

Please check your LESS syntax according to the document before applying this update.

=

Download this release

Release Info

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

Code changes from version 1.3.1 to 1.4

Files changed (57) hide show
  1. bootstrap.php +1 -1
  2. lib/Compiler.class.php +9 -4
  3. lib/Configuration.class.php +37 -1
  4. lib/Plugin.class.php +24 -2
  5. lib/Stylesheet.class.php +4 -4
  6. lib/helper/ThemeHelper.php +15 -0
  7. lib/vendor/lessphp/.gitignore +4 -0
  8. lib/vendor/lessphp/README.md +5 -5
  9. lib/vendor/lessphp/docs/docs.html +2 -2
  10. lib/vendor/lessphp/docs/docs.md +654 -0
  11. lib/vendor/lessphp/lessc.inc.php +970 -533
  12. lib/vendor/lessphp/lessify +23 -0
  13. lib/vendor/lessphp/lessify.inc.php +447 -0
  14. lib/vendor/lessphp/plessc +59 -23
  15. lib/vendor/lessphp/tests/README +3 -1
  16. lib/vendor/lessphp/tests/inputs/{accessors.less → accessors.less.disable} +0 -0
  17. lib/vendor/lessphp/tests/inputs/builtins.less +33 -0
  18. lib/vendor/lessphp/tests/inputs/colors.less +65 -0
  19. lib/vendor/lessphp/tests/inputs/compile_on_mixin.less +39 -0
  20. lib/vendor/lessphp/tests/inputs/escape.less +19 -0
  21. lib/vendor/lessphp/tests/inputs/font_family.less +3 -3
  22. lib/vendor/lessphp/tests/inputs/hacks.less +6 -0
  23. lib/vendor/lessphp/tests/inputs/import.less +21 -0
  24. lib/vendor/lessphp/tests/inputs/math.less +81 -0
  25. lib/vendor/lessphp/tests/inputs/media.less +6 -0
  26. lib/vendor/lessphp/tests/inputs/misc.less +24 -1
  27. lib/vendor/lessphp/tests/inputs/mixin_functions.less +6 -6
  28. lib/vendor/lessphp/tests/inputs/mixin_merging.less.disable +100 -0
  29. lib/vendor/lessphp/tests/inputs/mixins.less +18 -1
  30. lib/vendor/lessphp/tests/inputs/nested.less +40 -1
  31. lib/vendor/lessphp/tests/inputs/out.html +0 -0
  32. lib/vendor/lessphp/tests/inputs/scopes.less +40 -0
  33. lib/vendor/lessphp/tests/inputs/site_demos.less +120 -0
  34. lib/vendor/lessphp/tests/inputs/test-imports/file1.less +16 -0
  35. lib/vendor/lessphp/tests/inputs/test-imports/file2.less +6 -0
  36. lib/vendor/lessphp/tests/inputs/variables.less +0 -26
  37. lib/vendor/lessphp/tests/outputs/accessors.css +2 -0
  38. lib/vendor/lessphp/tests/outputs/builtins.css +17 -0
  39. lib/vendor/lessphp/tests/outputs/colors.css +46 -0
  40. lib/vendor/lessphp/tests/outputs/compile_on_mixin.css +11 -0
  41. lib/vendor/lessphp/tests/outputs/escape.css +13 -0
  42. lib/vendor/lessphp/tests/outputs/hacks.css +1 -0
  43. lib/vendor/lessphp/tests/outputs/import.css +14 -0
  44. lib/vendor/lessphp/tests/outputs/keyframes.css +3 -1
  45. lib/vendor/lessphp/tests/outputs/math.css +34 -0
  46. lib/vendor/lessphp/tests/outputs/media.css +4 -0
  47. lib/vendor/lessphp/tests/outputs/misc.css +11 -1
  48. lib/vendor/lessphp/tests/outputs/mixin_functions.css +1 -2
  49. lib/vendor/lessphp/tests/outputs/mixin_merging.css +42 -0
  50. lib/vendor/lessphp/tests/outputs/mixins.css +7 -5
  51. lib/vendor/lessphp/tests/outputs/nested.css +14 -4
  52. lib/vendor/lessphp/tests/outputs/scopes.css +7 -0
  53. lib/vendor/lessphp/tests/outputs/site_demos.css +54 -0
  54. lib/vendor/lessphp/tests/outputs/variables.css +2 -9
  55. lib/vendor/lessphp/tests/test.php +24 -8
  56. patch/protected-buffer.patch +0 -13
  57. readme.txt +36 -5
bootstrap.php CHANGED
@@ -3,7 +3,7 @@
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.3.1
7
  Author URI: http://case.oncle-tom.net/
8
  Plugin URI: http://wordpress.org/extend/plugins/wp-less/
9
 
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
7
  Author URI: http://case.oncle-tom.net/
8
  Plugin URI: http://wordpress.org/extend/plugins/wp-less/
9
 
lib/Compiler.class.php CHANGED
@@ -7,13 +7,14 @@
7
  * @package wp-less
8
  * @subpackage lib
9
  * @since 1.2
10
- * @version 1.2
11
  */
12
  class WPLessCompiler extends lessc
13
  {
14
  /**
15
  * Instantiate a compiler
16
  *
 
17
  * @see lessc::__construct
18
  * @param $file string [optional] Additional file to parse
19
  */
@@ -26,15 +27,17 @@ class WPLessCompiler extends lessc
26
  /**
27
  * Parse a LESS file
28
  *
 
29
  * @see lessc::parse
30
  * @throws Exception
31
  * @param string $text [optional] Custom CSS to parse
 
32
  * @return string CSS output
33
  */
34
- public function parse($text = null)
35
  {
36
- do_action('wp-less_compiler_parse_pre', $this);
37
- return apply_filters('wp-less_compiler_parse', parent::parse($text));
38
  }
39
 
40
  /**
@@ -42,6 +45,7 @@ class WPLessCompiler extends lessc
42
  *
43
  * @since 1.1
44
  * @return string current buffer
 
45
  */
46
  public function getBuffer()
47
  {
@@ -54,6 +58,7 @@ class WPLessCompiler extends lessc
54
  *
55
  * @since 1.1
56
  * @param $css string CSS you'd like to see in the buffer, before being parse
 
57
  */
58
  public function setBuffer($css)
59
  {
7
  * @package wp-less
8
  * @subpackage lib
9
  * @since 1.2
10
+ * @version 1.3
11
  */
12
  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
  */
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
  /**
45
  *
46
  * @since 1.1
47
  * @return string current buffer
48
+ * @deprecated
49
  */
50
  public function getBuffer()
51
  {
58
  *
59
  * @since 1.1
60
  * @param $css string CSS you'd like to see in the buffer, before being parse
61
+ * @deprecated
62
  */
63
  public function setBuffer($css)
64
  {
lib/Configuration.class.php CHANGED
@@ -10,7 +10,12 @@ class WPLessConfiguration extends WPPluginToolkitConfiguration
10
  /**
11
  * Refers to the version of the plugin
12
  */
13
- const VERSION = '1.3.1';
 
 
 
 
 
14
 
15
 
16
  protected function configure()
@@ -20,6 +25,37 @@ class WPLessConfiguration extends WPPluginToolkitConfiguration
20
 
21
  protected function configureOptions()
22
  {
 
 
 
 
 
 
 
 
 
 
 
 
23
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  }
25
  }
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 function configure()
25
 
26
  protected function configureOptions()
27
  {
28
+ $this->setVariables(array());
29
+ }
30
+
31
+ /**
32
+ * Set global Less variables
33
+ *
34
+ * @since 1.4
35
+ */
36
+ public function addVariable($name, $value)
37
+ {
38
+ $this->variables[$name] = $value;
39
+ }
40
 
41
+ /**
42
+ * Returns the registered variables
43
+ *
44
+ * @since 1.4
45
+ * @return array
46
+ */
47
+ public function getVariables()
48
+ {
49
+ return $this->variables;
50
+ }
51
+
52
+ /**
53
+ * Set global Less variables
54
+ *
55
+ * @since 1.4
56
+ */
57
+ public function setVariables(array $variables)
58
+ {
59
+ $this->variables = $variables;
60
  }
61
  }
lib/Plugin.class.php CHANGED
@@ -22,6 +22,17 @@ class WPLessPlugin extends WPPluginToolkitPlugin
22
  */
23
  public static $match_pattern = '/\.less$/U';
24
 
 
 
 
 
 
 
 
 
 
 
 
25
  /**
26
  * Dispatches all events of the plugin
27
  *
@@ -98,7 +109,7 @@ class WPLessPlugin extends WPPluginToolkitPlugin
98
  *
99
  * @author oncletom
100
  * @since 1.1
101
- * @version 1.1
102
  * @param string $handle
103
  * @param $force boolean If set to true, rebuild all stylesheets, without considering they are updated or not
104
  * @return WPLessStylesheet
@@ -110,7 +121,7 @@ class WPLessPlugin extends WPPluginToolkitPlugin
110
 
111
  if ((is_bool($force) && $force) || $stylesheet->hasToCompile())
112
  {
113
- $stylesheet->save();
114
  }
115
 
116
  $wp_styles->registered[$handle]->src = $stylesheet->getTargetUri();
@@ -181,4 +192,15 @@ class WPLessPlugin extends WPPluginToolkitPlugin
181
 
182
  return $this->is_hooks_registered = true;
183
  }
 
 
 
 
 
 
 
 
 
 
 
184
  }
22
  */
23
  public static $match_pattern = '/\.less$/U';
24
 
25
+ /**
26
+ * Proxy method
27
+ *
28
+ * @see WPLessConfiguration::setVariables()
29
+ * @since 1.4
30
+ */
31
+ public function addVariable($name, $value)
32
+ {
33
+ $this->getConfiguration()->addVariable($name, $value);
34
+ }
35
+
36
  /**
37
  * Dispatches all events of the plugin
38
  *
109
  *
110
  * @author oncletom
111
  * @since 1.1
112
+ * @version 1.2
113
  * @param string $handle
114
  * @param $force boolean If set to true, rebuild all stylesheets, without considering they are updated or not
115
  * @return WPLessStylesheet
121
 
122
  if ((is_bool($force) && $force) || $stylesheet->hasToCompile())
123
  {
124
+ $stylesheet->save($this->getConfiguration()->getVariables());
125
  }
126
 
127
  $wp_styles->registered[$handle]->src = $stylesheet->getTargetUri();
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 setVariables(array $variables)
203
+ {
204
+ $this->getConfiguration()->setVariables($variables);
205
+ }
206
  }
lib/Stylesheet.class.php CHANGED
@@ -201,19 +201,19 @@ class WPLessStylesheet
201
  *
202
  * @author oncletom
203
  * @since 1.0
204
- * @version 1.2
205
  * @throws Exception in case of parsing went bad
206
  */
207
- public function save()
208
  {
209
  wp_mkdir_p(dirname($this->getTargetPath()));
210
 
211
  try
212
  {
213
- do_action('wp-less_stylesheet_save_pre', $this);
214
  $compiler = new WPLessCompiler($this->getSourcePath());
215
 
216
- file_put_contents($this->getTargetPath(), apply_filters('wp-less_stylesheet_save', $compiler->parse(), $this));
217
  chmod($this->getTargetPath(), 0666);
218
 
219
  $this->is_new = false;
201
  *
202
  * @author oncletom
203
  * @since 1.0
204
+ * @version 1.3
205
  * @throws Exception in case of parsing went bad
206
  */
207
+ public function save(array $variables = array())
208
  {
209
  wp_mkdir_p(dirname($this->getTargetPath()));
210
 
211
  try
212
  {
213
+ do_action('wp-less_stylesheet_save_pre', $this, $variables);
214
  $compiler = new WPLessCompiler($this->getSourcePath());
215
 
216
+ file_put_contents($this->getTargetPath(), apply_filters('wp-less_stylesheet_save', $compiler->parse(null, $variables), $this));
217
  chmod($this->getTargetPath(), 0666);
218
 
219
  $this->is_new = false;
lib/helper/ThemeHelper.php CHANGED
@@ -1,4 +1,19 @@
1
  <?php
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  /**
3
  * LESSify a stylesheet on the fly
4
  *
1
  <?php
2
+ /**
3
+ * Creates easily a variable to be replaced on compilation
4
+ *
5
+ * @author oncletom
6
+ * @since 1.4
7
+ * @version 1.0
8
+ * @param string $name
9
+ * @param string $value
10
+ * @return null
11
+ */
12
+ function less_add_variable($name, $value)
13
+ {
14
+ WPPluginToolkitPlugin::getInstance('WPLess')->addVariable($name, $value);
15
+ }
16
+
17
  /**
18
  * LESSify a stylesheet on the fly
19
  *
lib/vendor/lessphp/.gitignore ADDED
@@ -0,0 +1,4 @@
 
 
 
 
1
+ *.swp
2
+ *~
3
+ /*.less
4
+ /*.css
lib/vendor/lessphp/README.md CHANGED
@@ -1,16 +1,16 @@
1
- # lessphp v0.2.0
2
  #### <http://leafo.net/lessphp>
3
 
4
- `lessphp` is a compiler for LESS written in php.
5
- For a complete description of the language see <http://leafo.net/lessphp/docs/>
6
 
7
  ### How to use in your php project
8
 
9
- Copy less.inc.php to your include directory and include it into your project.
10
 
11
  There are a few ways to interface with the compiler. The easiest is to have it
12
  compile a LESS file when the page is requested. The static function
13
- `less::ccompile`, checked compile, will compile the input LESS file only when it
14
  is newer than the output file.
15
 
16
  try {
1
+ # lessphp v0.3.0
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/>. Here's a quick tutorial:
6
 
7
  ### How to use in your php project
8
 
9
+ Copy `lessc.inc.php` to your include directory and include it into your project.
10
 
11
  There are a few ways to interface with the compiler. The easiest is to have it
12
  compile a LESS file when the page is requested. The static function
13
+ `lessc::ccompile`, checked compile, will compile the input LESS file only when it
14
  is newer than the output file.
15
 
16
  try {
lib/vendor/lessphp/docs/docs.html CHANGED
@@ -92,7 +92,7 @@ pre {
92
  width: 3430px + 22; // evaluates to 3452px;
93
  margin: (1.5em / 2) + 2px; // evaluates to 2.85px;
94
  margin: 20 + 2px; // evaluates to 22px;
95
- font-family: "Some " + "Family; // evaluates to "Some Family"
96
  }</pre>
97
 
98
  <p>It is important to notice that in the example above the output will print the margin property twice.
@@ -106,7 +106,7 @@ A single property name can hold more than one value; older values are not overwr
106
 
107
  <pre class="code">body {
108
  a {
109
- color: green
110
  :hover {
111
  color: blue;
112
  }
92
  width: 3430px + 22; // evaluates to 3452px;
93
  margin: (1.5em / 2) + 2px; // evaluates to 2.85px;
94
  margin: 20 + 2px; // evaluates to 22px;
95
+ font-family: "Some " + "Family"; // evaluates to "Some Family"
96
  }</pre>
97
 
98
  <p>It is important to notice that in the example above the output will print the margin property twice.
106
 
107
  <pre class="code">body {
108
  a {
109
+ color: green;
110
  :hover {
111
  color: blue;
112
  }
lib/vendor/lessphp/docs/docs.md ADDED
@@ -0,0 +1,654 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ title: v0.3.0 documentation
2
+ --
3
+
4
+ <h2 skip="true">Documentation v0.3.0</h2>
5
+
6
+ <div style="margin-bottom: 1em;">$index</div>
7
+
8
+ **lessphp** is a compiler that generates CSS from a superset language which
9
+ adds a collection of convenient features often seen in other languages. All CSS
10
+ is compatible with LESS, so you can start using new features with your existing CSS.
11
+
12
+ It is designed to be compatible with [less.js](http://lesscss.org), and suitable
13
+ as a drop in replacement for PHP projects.
14
+
15
+ ## Getting Started
16
+
17
+ The homepage for **lessphp** can be found at [http://leafo.net/lessphp/][1].
18
+
19
+ You can follow development at the project's [GitHub][2].
20
+
21
+ Including **lessphp** in your project is as simple as dropping the single
22
+ include file into your code base and running the appropriate compile method as
23
+ described in the [PHP Interface](#php_interface).
24
+
25
+ [1]: http://leafo.net/lessphp "lessphp homepage"
26
+ [2]: https://github.com/leafo/lessphp "lessphp GitHub page"
27
+
28
+ ## Installation
29
+
30
+ **lessphp** is distributed entirely in a single stand-alone file. Download the
31
+ latest version from either [the homepage][1] or [GitHub][2].
32
+
33
+ Development versions can also be downloading from GitHub.
34
+
35
+ Place `lessphp.inc.php` in a location available to your PHP scripts, and
36
+ include it. That's it! you're ready to begin.
37
+
38
+ ## The Language
39
+
40
+ **lessphp** is very easy to learn because it generally functions how you would
41
+ expect it to. If you feel something is challenging or missing, feel free to
42
+ open an issue on the [bug tracker](https://github.com/leafo/lessphp/issues).
43
+
44
+ It is also easy to learn because any standards-compliant CSS code is valid LESS
45
+ code. You are free to gradually enhance your existing CSS code base with LESS
46
+ features without having to worry about rewriting anything.
47
+
48
+ The following is a description of the new languages features provided by LESS.
49
+
50
+ ### Line Comments
51
+
52
+ Simple but very useful; line comments are started with `//`:
53
+
54
+ ```less
55
+ // this is a comment
56
+ body {
57
+ color: red; // as is this
58
+ /* block comments still work also */
59
+ }
60
+ ```
61
+
62
+ ### Variables
63
+ Variables are identified with a name that starts with `@`. To declare a
64
+ variable, you create an appropriately named CSS property and assign it a value:
65
+
66
+ ```less
67
+ @family: "verdana";
68
+ @color: red;
69
+ body {
70
+ @mycolor: red;
71
+ font-family: @family;
72
+ color: @color;
73
+ border-bottom: 1px solid @color;
74
+ }
75
+ ```
76
+
77
+ Variable declarations will not appear in the output. Variables can be declared
78
+ in the outer most scope of the file, or anywhere else a CSS property may
79
+ appear. They can hold any CSS property value.
80
+
81
+ Variables are only visible for use from their current scope, or any enclosed
82
+ scopes.
83
+
84
+ ### Expressions
85
+
86
+ Expressions let you combine values and variables in meaningful ways. For
87
+ example you can add to a color to make it a different shade. Or divide up the
88
+ width of your layout logically. You can even concatenate strings.
89
+
90
+ Use the mathematical operators to evaluate an expression:
91
+
92
+ ```less
93
+ @width: 960px;
94
+ .nav {
95
+ width: @width / 3;
96
+ color: #001 + #abc;
97
+ }
98
+ .body {
99
+ width: 2 * @width / 3;
100
+ font-family: "hel" + "vetica";
101
+ }
102
+ ```
103
+
104
+ Parentheses can be used to control the order of evaluation. They can also be
105
+ used to force an evaluation for cases where CSS's syntax makes the expression
106
+ ambiguous.
107
+
108
+ The following property will produce two numbers, instead of doing the
109
+ subtraction:
110
+
111
+ ```less
112
+ margin: 10px -5px;
113
+ ```
114
+
115
+ To force the subtraction:
116
+
117
+ ```less
118
+ margin: (10px -5px);
119
+ ```
120
+
121
+ It is also safe to surround mathematical operators by spaces to ensure that
122
+ they are evaluated:
123
+
124
+ ```less
125
+ margin: 10px - 5px;
126
+ ```
127
+
128
+ Division has a special quirk. Due to CSS font shorthand syntax, we need to be
129
+ careful about how we place spaces. In the following example we are using font
130
+ size and lineheight shorthand. No division should take place:
131
+
132
+ ```less
133
+ .font {
134
+ font: 20px/80px "Times New Roman";
135
+ }
136
+ ```
137
+
138
+ In order to force division we can surround the `/` by spaces, or we can wrap
139
+ the expression in parentheses:
140
+
141
+ ```less
142
+ .font {
143
+ // these two will evaluate
144
+ font: 20px / 80px "Times New Roman";
145
+ font: (20px/80px) "Times New Roman";
146
+ }
147
+ ```
148
+
149
+ ### Nested Blocks
150
+
151
+ By nesting blocks we can build up a chain of CSS selectors through scope
152
+ instead of repeating them. In addition to reducing repetition, this also helps
153
+ logically organize the structure of our CSS.
154
+
155
+ ```less
156
+ ol.list {
157
+ li.special {
158
+ border: 1px solid red;
159
+ }
160
+
161
+ li.plain {
162
+ font-weight: bold;
163
+ }
164
+ }
165
+ ```
166
+
167
+
168
+ This will produce two blocks, a `ol.list li.special` and `ol.list li.plain`.
169
+
170
+ Blocks can be nested as deep as required in order to build a hierarchy of
171
+ relationships.
172
+
173
+ The `&` operator can be used in a selector to represent its parent's selector.
174
+ If the `&` operator is used, then the default action of appending the parent to
175
+ the front of the child selector separated by space is not performed.
176
+
177
+ ```less
178
+ b {
179
+ a & {
180
+ color: red;
181
+ }
182
+
183
+ // the following have the same effect
184
+
185
+ & i {
186
+ color: blue;
187
+ }
188
+
189
+ i {
190
+ color: blue;
191
+ }
192
+ }
193
+ ```
194
+
195
+
196
+ Because the `&` operator respects the whitespace around it, we can use it to
197
+ control how the child blocks are joined. Consider the differences between the
198
+ following:
199
+
200
+ ```less
201
+ div {
202
+ .child-class { color: purple; }
203
+
204
+ &.isa-class { color: green; }
205
+
206
+ #child-id { height: 200px; }
207
+
208
+ &#div-id { height: 400px; }
209
+
210
+ &:hover { color: red; }
211
+
212
+ :link { color: blue; }
213
+ }
214
+ ```
215
+
216
+ The `&` operator also works with [mixins](#mixins), which produces interesting results:
217
+
218
+ ```less
219
+ .within_box_style() {
220
+ .box & {
221
+ color: blue;
222
+ }
223
+ }
224
+
225
+ #menu {
226
+ .within_box_style;
227
+ }
228
+ ```
229
+
230
+ ### Mixins
231
+
232
+ Any block can be mixed in just by naming it:
233
+
234
+ ```less
235
+ .mymixin {
236
+ color: blue;
237
+ border: 1px solid red;
238
+
239
+ .special {
240
+ font-weight: bold;
241
+ }
242
+ }
243
+
244
+
245
+ h1 {
246
+ font-size: 200px;
247
+ .mixin;
248
+ }
249
+ ```
250
+
251
+ All properties and child blocks are mixed in.
252
+
253
+ Mixins can be made parametric, meaning they can take arguments, in order to
254
+ enhance their utility. A parametric mixin all by itself is not outputted when
255
+ compiled. Its properties will only appear when mixed into another block.
256
+
257
+ The canonical example is to create a rounded corners mixin that works across
258
+ browsers:
259
+
260
+ ```less
261
+ .rounded-corners(@radius: 5px) {
262
+ border-radius: @radius;
263
+ -webkit-border-radius: @radius;
264
+ -moz-border-radius: @radius;
265
+ }
266
+
267
+ .header {
268
+ .rounded-corners();
269
+ }
270
+
271
+ .info {
272
+ background: red;
273
+ .rounded-corners(14px);
274
+ }
275
+ ```
276
+
277
+ If you have a mixin that doesn't have any arguments, but you don't want it to
278
+ show up in the output, give it a blank argument list:
279
+
280
+ ```less
281
+ .secret() {
282
+ font-size: 6000px;
283
+ }
284
+
285
+ .div {
286
+ .secret;
287
+ }
288
+ ```
289
+
290
+ If the mixin doesn't need any arguments, you can leave off the parentheses when
291
+ mixing it in, as seen above.
292
+
293
+ You can also mixin a block that is nested inside other blocks. You can think of
294
+ the outer block as a way of making a scope for your mixins. You just list the
295
+ names of the mixins separated by spaces, which describes the path to the mixin
296
+ you want to include. Optionally you can separate them by `>`.
297
+
298
+ ```less
299
+ .my_scope {
300
+ .some_color {
301
+ color: red;
302
+ .inner_block {
303
+ text-decoration: underline;
304
+ }
305
+ }
306
+ .bold {
307
+ font-weight: bold;
308
+ color: blue;
309
+ }
310
+ }
311
+
312
+ .a_block {
313
+ .my_scope .some_color;
314
+ .my_scope .some_color .inner_block;
315
+ }
316
+
317
+ .another_block {
318
+ // the alternative syntax
319
+ .my_scope > .bold;
320
+ }
321
+ ```
322
+
323
+ ### Import
324
+
325
+ Multiple LESS files can be compiled into a single CSS file by using the
326
+ `@import` statement. Be careful, the LESS import statement shares syntax with
327
+ the CSS import statement. If the file being imported ends in a `.less`
328
+ extension, or no extension, then it is treated as a LESS import. Otherwise it
329
+ is left alone and outputted directly:
330
+
331
+ ```less
332
+ // my_file.less
333
+ .some-mixin(@height) {
334
+ height: @height;
335
+ }
336
+
337
+ // main.less
338
+ @import "main.less" // will import the file if it can be found
339
+ @import "main.css" // will be left alone
340
+
341
+ body {
342
+ .some-mixin(400px);
343
+ }
344
+ ```
345
+
346
+ All of the following lines are valid ways to import the same file:
347
+
348
+ ```less
349
+ @import "file";
350
+ @import 'file.less';
351
+ @import url("file");
352
+ @import url('file');
353
+ @import url(file);
354
+ ```
355
+
356
+ When importing, the `importDir` is searched for files. This can be configured,
357
+ see [PHP Interface](#php_interface).
358
+
359
+ ### String Interpolation
360
+
361
+ String interpolation is a convenient way to insert the value of a variable
362
+ right into a string literal. Given some variable named `@var_name`, you just
363
+ need to write it as `@{var_name}` from within the string to have its value
364
+ inserted:
365
+
366
+ ```less
367
+ @symbol: ">";
368
+ h1:before {
369
+ content: "@{symbol}: ";
370
+ }
371
+
372
+ h2:before {
373
+ content: "@{symbol}@{symbol}: ";
374
+ }
375
+ ```
376
+
377
+ There are two kinds of strings, implicit and explicit strings. Explicit strings
378
+ are wrapped by double quotes, `"hello I am a string"`, or single quotes `'I am
379
+ another string'`. Implicit strings only appear when using `url()`. The text
380
+ between the parentheses is considered a string and thus string interpolation is
381
+ possible:
382
+
383
+ ```less
384
+ @path: "files/";
385
+ body {
386
+ background: url(@{path}my_background.png);
387
+ }
388
+ ```
389
+
390
+ ### String Format Function
391
+
392
+ The `%` function can be used to insert values into strings using a *format
393
+ string*. It works similar to `printf` seen in other languages. It has the
394
+ same purpose as string interpolation above, but gives explicit control over
395
+ the output format.
396
+
397
+ ```less
398
+ @symbol: ">";
399
+ h1:before {
400
+ content: %("%s: ", @symbol);
401
+ }
402
+ ```
403
+
404
+ The `%` function takes as its first argument the format string, following any
405
+ number of addition arguments that are inserted in place of the format
406
+ directives.
407
+
408
+ A format directive starts with a `%` and is followed by a single character that
409
+ is either `a`, `d`, or `s`:
410
+
411
+ ```less
412
+ strings: %("%a %d %s %a", hi, 1, 'ok', 'cool');
413
+ ```
414
+
415
+ `%a` and `%d` format the value the same way: they compile the argument to its
416
+ CSS value and insert it directly. When used with a string, the quotes are
417
+ included in the output. This typically isn't what we want, so we have the `%s`
418
+ format directive which strips quotes from strings before inserting them.
419
+
420
+ The `%d` directive functions the same as `%a`, but is typically used for numbers
421
+ assuming the output format of numbers might change in the future.
422
+
423
+ ### String Unquoting
424
+
425
+ Sometimes you will need to write proprietary CSS syntax that is unable to be
426
+ parsed. As a workaround you can place the code into a string and unquote it.
427
+ Unquoting is the process of outputting a string without its surrounding quotes.
428
+ There are two ways to unquote a string.
429
+
430
+ The `~` operator in front of a string will unquote that string:
431
+
432
+ ```less
433
+ .class {
434
+ // a made up, but problematic vendor specific CSS
435
+ filter: ~"Microsoft.AlphaImage(src='image.png')";
436
+ }
437
+ ```
438
+
439
+ If you are working with other types, such as variables, there is a built in
440
+ function that let's you unquote any value. It is called `e`.
441
+
442
+ ```less
443
+ @color: "red";
444
+ .class {
445
+ color: e(@color);
446
+ }
447
+ ```
448
+
449
+ ### Built In Functions
450
+
451
+ **lessphp** has a collection of built in functions:
452
+
453
+ * `e(str)` -- returns a string without the surrounding quotes.
454
+ See [String Unquoting](#string_unquoting)
455
+
456
+ * `floor(number)` -- returns the floor of a numerical input
457
+ * `round(number)` -- returns the rounded value of numerical input
458
+
459
+ * `lighten(color, percent)` -- lightens color by percent and returns it
460
+ * `darken(color, percent)` -- darkens color by percent and returns it
461
+
462
+ * `saturate(color, percent)` -- saturates color by percent and returns it
463
+ * `desaturate(color, percent)` -- desaturates color by percent and returns it
464
+
465
+ * `fadein(color, percent)` -- makes color less transparent by percent and returns it
466
+ * `fadeout(color, percent)` -- makes color more transparent by percent and returns it
467
+
468
+ * `spin(color, amount)` -- returns a color with amount degrees added to hue
469
+
470
+ * `rgbahex(color)` -- returns a string containing 4 part hex color.
471
+
472
+ This is used to convert a CSS color into the hex format that IE's filter
473
+ method expects when working with an alpha component.
474
+
475
+ ```less
476
+ .class {
477
+ @start: rgbahex(rgba(25, 34, 23, .5));
478
+ @end: rgbahex(rgba(85, 74, 103, .6));
479
+ // abridged example
480
+ -ms-filter:
481
+ e("gradient(start=@{start},end=@{end})");
482
+ }
483
+ ```
484
+
485
+ ## PHP Interface
486
+
487
+ The PHP interface lets you control the compiler from your PHP scripts. There is
488
+ only one file to include to get access to everything:
489
+
490
+ ```php
491
+ <?php
492
+ include "lessc.inc.php";
493
+ ```
494
+
495
+ To compile a file to a string (of CSS code):
496
+
497
+ ```php
498
+ $less = new lessc("myfile.less");
499
+ $css = $less->parse();
500
+ ```
501
+
502
+ To compile a string to a string:
503
+
504
+ ```php
505
+ $less = new lessc(); // a blank lessc
506
+ $css = $less->parse("body { a { color: red } }");
507
+ ```
508
+
509
+ Often, you want to write the compiled CSS to a file, and only recompile when
510
+ the original LESS file has changed. The following function will check the
511
+ modification date of the LESS file to see if a compile is required:
512
+
513
+ ```php
514
+ lessc::ccompile('myfile.less', 'mystyle.css');
515
+ ```
516
+
517
+ All of the following methods will throw an `Exception` if the parsing fails:
518
+
519
+ ```php
520
+ $less = new lessc();
521
+ try {
522
+ $less->parse("} invalid LESS }}}");
523
+ } catch (Exception $ex) {
524
+ echo "lessphp fatal error: ".$ex->getMessage();
525
+ }
526
+ ```
527
+
528
+ ### Setting Variables From PHP
529
+
530
+ The `parse` function takes a second optional argument. If you want to
531
+ initialize variables from outside the LESS file then you can pass in an
532
+ associative array of names and values. The values will parsed as CSS values:
533
+
534
+ ```php
535
+ $less = new lessc();
536
+ echo $less->parse(".magic { color: @color; width: @base - 200; }",
537
+ array(
538
+ 'color' => 'red';
539
+ 'base' => '960px';
540
+ ));
541
+ ```
542
+
543
+ You can also do this when loading from a file, but remember to set the first
544
+ argument of the parse function to `null`, otherwise it will try to compile that
545
+ instead of the file:
546
+
547
+ ```php
548
+ $less = new lessc("myfile.less");
549
+ echo $less->parse(null, array('color' => 'blue'));
550
+ ```
551
+
552
+ ### Custom Functions
553
+
554
+ **lessphp** has a simple extension interface where you can implement user
555
+ functions that will be exposed in LESS code during the compile. They can be a
556
+ little tricky though because you need to work with the **lessphp** type system.
557
+
558
+ By sub-classing `lessc`, and creating specially named methods we can extend
559
+ **lessphp**. In order for a function to be visible in LESS, its name must
560
+ start with `lib_`.
561
+
562
+ Let's make a function that doubles any numeric argument.
563
+
564
+ ```php
565
+ <?php
566
+ include "lessc.inc.php";
567
+
568
+ class myless extends lessc {
569
+ function lib_double($arg) {
570
+ list($type, $value) = $arg;
571
+ return array($type, $value*2);
572
+ }
573
+ }
574
+
575
+ $myless = new myless();
576
+ echo $myless->parse("div { width: double(400px); }");
577
+ ```
578
+
579
+ Although a little verbose, the implementation of `lib_double` gives us some
580
+ insight on the type system. All values are stored in an array where the 0th
581
+ element is a string representing the type, and the other elements make up the
582
+ associated data for that value.
583
+
584
+ The best way to get an understanding of the system is to make a dummy `lib_`
585
+ function which does a `vardump` on the argument. Try passing the function
586
+ different values from LESS and see what the results are.
587
+
588
+ The return value of the `lib_` function must also be a LESS type, but if it is
589
+ a string or numeric value, it will automatically be coerced into an appropriate
590
+ typed value. In our example, we reconstruct the value with our modifications
591
+ while making sure that we preserve the type.
592
+
593
+ All of the built in functions are implemented in this manner within the `lessc`
594
+ class.
595
+
596
+ ## Command Line Interface
597
+
598
+ **lessphp** comes with a command line script written in PHP that can be used to
599
+ invoke the compiler from the terminal. On Linux an OSX, all you need to do is
600
+ place `plessc` and `lessc.inc.php` somewhere in your PATH (or you can run it in
601
+ the current directory as well). On windows you'll need a copy of `php.exe` to
602
+ run the file. To compile a file, `input.less` to CSS, run:
603
+
604
+ ```bash
605
+ $ plessc input.less
606
+ ```
607
+
608
+ To write to a file, redirect standard out:
609
+
610
+ ```bash
611
+ $ plessc input.less > output.css
612
+ ```
613
+
614
+ To compile code directly on the command line:
615
+
616
+ ```bash
617
+ $ plessc -r "@color: red; body { color: @color; }"
618
+ ```
619
+
620
+ To watch a file for changes, and compile it as needed, use the `-w` flag:
621
+
622
+ ```bash
623
+ $ plessc -w input-file output-file
624
+ ```
625
+
626
+ Errors from watch mode are written to standard out.
627
+
628
+
629
+ ## License
630
+
631
+ Copyright (c) 2010 Leaf Corcoran, <http://leafo.net/lessphp>
632
+
633
+ Permission is hereby granted, free of charge, to any person obtaining
634
+ a copy of this software and associated documentation files (the
635
+ "Software"), to deal in the Software without restriction, including
636
+ without limitation the rights to use, copy, modify, merge, publish,
637
+ distribute, sublicense, and/or sell copies of the Software, and to
638
+ permit persons to whom the Software is furnished to do so, subject to
639
+ the following conditions:
640
+
641
+ The above copyright notice and this permission notice shall be
642
+ included in all copies or substantial portions of the Software.
643
+
644
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
645
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
646
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
647
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
648
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
649
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
650
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
651
+
652
+
653
+ *Also under GPL3 if required, see `LICENSE` file*
654
+
lib/vendor/lessphp/lessc.inc.php CHANGED
@@ -1,355 +1,318 @@
1
  <?php
2
 
3
  /**
4
- * lessphp v0.2.0
5
  * http://leafo.net/lessphp
6
  *
7
- * LESS Css compiler, adapted from http://lesscss.org/docs.html
8
  *
9
- * Copyright 2010, Leaf Corcoran <leafot@gmail.com>
10
  * Licensed under MIT or GPLv3, see LICENSE
11
  */
12
 
13
- //
14
- // fix the alpha value with color when using a percent
15
- //
16
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  class lessc {
18
  protected $buffer;
19
- private $count;
20
- private $line;
21
- private $expandStack;
22
- private $media;
23
- private $indentLevel;
24
- private $level;
25
- private $inAnimations;
26
 
27
- private $env = array();
28
 
29
- private $allParsedFiles = array();
30
 
31
- public $vPrefix = '@';
32
- public $mPrefix = '$';
33
- public $imPrefix = '!';
34
- public $selfSelector = '&';
35
 
36
- static private $precedence = array(
37
  '+' => 0,
38
  '-' => 0,
39
  '*' => 1,
40
  '/' => 1,
41
  '%' => 1,
42
  );
43
- static private $operatorString; // regex string to match any of the operators
44
 
45
- static private $dtypes = array('expression', 'variable', 'function', 'negative'); // types with delayed computation
46
- static private $units = array(
47
- 'px', '%', 'in', 'cm', 'mm', 'em', 'ex', 'pt', 'pc', 'ms', 's', 'deg', 'gr');
48
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  public $importDisabled = false;
50
  public $importDir = '';
51
 
52
- // compile chunk off the head of buffer
53
- function chunk() {
54
- if (empty($this->buffer)) return false;
55
- $s = $this->seek();
56
 
57
- // a property
58
- if ($this->keyword($key) && $this->assign() && $this->propertyValue($value) && $this->end()) {
59
- // look for important prefix
60
- if ($key{0} == $this->imPrefix && strlen($key) > 1) {
61
- $key = substr($key, 1);
62
- if ($value[0] == 'list' && $value[1] == ' ') {
63
- $value[2][] = array('keyword', '!important');
64
- } else {
65
- $value = array('list', ' ', array($value, array('keyword', '!important')));
66
- }
67
- }
68
- $this->append($key, $value);
69
 
70
- if (count($this->env) == 1)
71
- return $this->compileProperty($key, array($value))."\n";
72
- else
73
- return true;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
  } else {
75
  $this->seek($s);
76
  }
77
 
78
- // look for special css @ directives
79
- if (count($this->env) == 1 && $this->count < strlen($this->buffer) && $this->buffer[$this->count] == '@') {
 
 
80
  // a font-face block
81
  if ($this->literal('@font-face') && $this->literal('{')) {
82
- $this->push();
83
- $this->set('__tags', array('@font-face'));
84
- $this->set('__dontsave', true);
85
  return true;
86
  } else {
87
  $this->seek($s);
88
  }
89
 
90
  // charset
91
- if ($this->literal('@charset') && $this->propertyValue($value) && $this->end()) {
92
- return $this->indent('@charset '.$this->compileValue($value).';');
 
 
 
93
  } else {
94
  $this->seek($s);
95
  }
96
 
 
97
  // media
98
- if ($this->literal('@media') && $this->mediaTypes($types, $rest) && $this->literal('{')) {
99
- $this->media = $types;
100
- $this->indentLevel++;
101
- return "@media ".join(', ', $types).(!empty($rest) ? " $rest" : '' )." {\n";
 
 
102
  } else {
103
  $this->seek($s);
104
  }
105
-
106
  // css animations
107
- if ($this->match('(@(-[a-z]+-)?keyframes)', $m) && $this->propertyValue($value) && $this->literal('{')) {
108
- $this->indentLevel++;
109
- $this->inAnimations = true;
110
- return $m[0].$this->compileValue($value)." {\n";
 
 
111
  } else {
112
  $this->seek($s);
113
  }
114
  }
115
-
116
- // see if we're in animations and handle pseudo classes
117
- if($this->inAnimations && $this->match("(to|from|[0-9]+%)", $m) && $this->literal('{')) {
118
- $this->push();
119
- $this->set('__tags', array($m[1]));
120
- return true;
121
- } else {
122
- $this->seek($s);
123
  }
124
 
125
- // setting variable
126
- if ($this->variable($name) && $this->assign() && $this->propertyValue($value) && $this->end()) {
127
- $this->append($this->vPrefix.$name, $value);
 
 
128
  return true;
129
  } else {
130
  $this->seek($s);
131
  }
132
 
133
- // opening abstract block
134
- if ($this->tag($tag, true) && $this->argumentDef($args) && $this->literal('{')) {
135
- $this->push();
136
-
137
- // move out of variable scope
138
- if ($tag{0} == $this->vPrefix) $tag[0] = $this->mPrefix;
 
 
 
 
 
 
 
139
 
140
- $this->set('__tags', array($tag));
141
- if (isset($args)) $this->set('__args', $args);
 
 
142
 
 
 
 
 
 
 
143
  return true;
144
  } else {
145
  $this->seek($s);
146
  }
147
 
148
- // opening css block
149
  if ($this->tags($tags) && $this->literal('{')) {
150
- // move @ tags out of variable namespace!
151
- foreach($tags as &$tag) {
152
- if ($tag{0} == $this->vPrefix) $tag[0] = $this->mPrefix;
153
- }
154
-
155
- $this->push();
156
- $this->set('__tags', $tags);
157
-
158
  return true;
159
  } else {
160
  $this->seek($s);
161
  }
162
 
163
- // closing block
164
  if ($this->literal('}')) {
165
- if ($this->level == 1 && !is_null($this->media)) {
166
- $this->indentLevel--;
167
- $this->media = null;
168
- return "}\n";
169
- }
170
-
171
- if ($this->level == 1 && $this->inAnimations === true) {
172
- $this->indentLevel--;
173
- $this->inAnimations = false;
174
- return "}\n";
175
- }
176
-
177
- $tags = $this->multiplyTags();
178
- $env = end($this->env);
179
- $ctags = $env['__tags'];
180
- unset($env['__tags']);
181
-
182
- // insert the default arguments
183
- if (isset($env['__args'])) {
184
- foreach ($env['__args'] as $arg) {
185
- if (isset($arg[1])) {
186
- $this->prepend($this->vPrefix.$arg[0], $arg[1]);
187
- }
188
- }
189
- }
190
-
191
- if (!empty($tags))
192
- $out = $this->compileBlock($tags, $env);
193
-
194
  try {
195
- $this->pop();
196
  } catch (exception $e) {
197
  $this->seek($s);
198
  $this->throwParseError($e->getMessage());
199
  }
200
 
201
- // make the block(s) available in the new current scope
202
- if (!isset($env['__dontsave'])) {
203
- foreach ($ctags as $t) {
204
- // if the block already exists then merge
205
- if ($this->get($t, array(end($this->env)))) {
206
- $this->merge($t, $env);
207
- } else {
208
- $this->set($t, $env);
209
- }
210
  }
211
  }
212
 
213
- return isset($out) ? $out : true;
214
- }
215
-
216
- // import statement
217
- if ($this->import($url, $media)) {
218
- if ($this->importDisabled) return "/* import is disabled */\n";
219
-
220
- $full = $this->importDir.$url;
221
- if ($this->fileExists($file = $full) || $this->fileExists($file = $full.'.less')) {
222
- $this->addParsedFile($file);
223
- $loaded = ltrim($this->removeComments(file_get_contents($file).";"));
224
- $this->buffer = substr($this->buffer, 0, $this->count).$loaded.substr($this->buffer, $this->count);
225
- return true;
226
  }
227
 
228
- return $this->indent('@import url("'.$url.'")'.($media ? ' '.$media : '').';');
229
  }
230
 
231
- // mixin/function expand
232
- if ($this->tags($tags, true, '>') && ($this->argumentValues($argv) || true) && $this->end()) {
233
- $env = $this->getEnv($tags);
234
- if ($env == null) return true;
235
-
236
- // if we have arguments then insert them
237
- if (!empty($env['__args'])) {
238
- foreach($env['__args'] as $arg) {
239
- $vname = $this->vPrefix.$arg[0];
240
- $value = is_array($argv) ? array_shift($argv) : null;
241
- // copy default value if there isn't one supplied
242
- if ($value == null && isset($arg[1]))
243
- $value = $arg[1];
244
-
245
- // if ($value == null) continue; // don't define so it can search up
246
-
247
- // create new entry if var doesn't exist in scope
248
- if (isset($env[$vname])) {
249
- array_unshift($env[$vname], $value);
250
- } else {
251
- // new element
252
- $env[$vname] = array($value);
253
- }
254
- }
255
- }
256
-
257
- // copy all properties from tmp env to current block
258
- ob_start();
259
- $blocks = array();
260
- $toReduce = array();
261
- foreach ($env as $name => $value) {
262
- // skip the metatdata
263
- if (preg_match('/^__/', $name)) continue;
264
-
265
- // if it is a block, remember it to compile after everything
266
- // is mixed in
267
- if (!isset($value[0]))
268
- $blocks[] = array($name, $value);
269
- else if ($name{0} != $this->vPrefix)
270
- $toReduce[] = $name;
271
-
272
- // copy the data
273
- // don't overwrite previous value, look in current env for name
274
- if ($this->get($name, array(end($this->env)))) {
275
- while ($tval = array_shift($value))
276
- $this->append($name, $tval);
277
- } else
278
- $this->set($name, $value);
279
- }
280
-
281
- // extract the args as a temp environment, put them before top
282
- if (isset($env['__args'])) {
283
- $tmp = array();
284
- foreach ($env['__args'] as $arg) {
285
- if (isset($arg[1])) // if there is a value
286
- $tmp[$this->vPrefix.$arg[0]] = array($arg[1]);
287
- }
288
-
289
- $top = array_pop($this->env);
290
- array_push($this->env, $tmp, $top);
291
- }
292
-
293
-
294
- // reduce all values that came out of this mixin
295
- foreach ($toReduce as $name) {
296
- $reduced = array();
297
- foreach ($this->get($name) as $value) {
298
- $reduced[] = $this->reduce($value);
299
- }
300
- $this->set($name, $reduced);
301
- }
302
-
303
- if (isset($env['__args'])) {
304
- // get rid of tmp
305
- $top = array_pop($this->env);
306
- array_pop($this->env);
307
- array_push($this->env, $top);
308
- }
309
-
310
- // render sub blocks
311
- foreach ($blocks as $b) {
312
- $rtags = $this->multiplyTags(array($b[0]));
313
- echo $this->compileBlock($rtags, $b[1]);
314
- }
315
 
316
- return ob_get_clean();
 
 
 
 
 
 
317
  } else {
318
  $this->seek($s);
319
  }
320
-
321
  // spare ;
322
  if ($this->literal(';')) return true;
323
 
324
- return false; // couldn't match anything, throw error
325
  }
326
 
327
- function fileExists($name) {
328
- // sym link workaround
329
- return file_exists($name) || file_exists(realpath(preg_replace('/\w+\/\.\.\//', '', $name)));
 
 
 
330
  }
331
 
332
- // recursively find the cartesian product of all tags in stack
333
- function multiplyTags($tags = array(' '), $d = null) {
334
- if ($d === null) $d = count($this->env) - 1;
335
-
336
- $parents = $d == 0 ? $this->env[$d]['__tags']
337
- : $this->multiplyTags($this->env[$d]['__tags'], $d - 1);
338
-
339
- $rtags = array();
340
- foreach ($parents as $p) {
341
- foreach ($tags as $t) {
342
- if ($t{0} == $this->mPrefix) continue; // skip functions
343
- $d = ' ';
344
- if ($t{0} == ':' || $t{0} == $this->selfSelector) {
345
- $t = ltrim($t, $this->selfSelector);
346
- $d = '';
347
- }
348
- $rtags[] = trim($p.$d.$t);
349
  }
350
  }
351
 
352
- return $rtags;
 
 
 
 
 
353
  }
354
 
355
  // a list of expressions
@@ -366,37 +329,58 @@ class lessc {
366
  return true;
367
  }
368
 
369
- // a single expression
 
 
 
370
  function expression(&$out) {
371
  $s = $this->seek();
372
- $needWhite = true;
373
- if ($this->literal('(') && $this->expression($exp) && $this->literal(')')) {
374
  $lhs = $exp;
375
- $needWhite = false;
376
  } elseif ($this->seek($s) && $this->value($val)) {
377
  $lhs = $val;
378
  } else {
 
 
379
  return false;
380
  }
381
 
382
- $out = $this->expHelper($lhs, 0, $needWhite);
 
383
  return true;
384
  }
385
 
386
- // resursively parse infix equation with $lhs at precedence $minP
387
- function expHelper($lhs, $minP, $needWhite = true) {
 
 
 
388
  $ss = $this->seek();
389
- // try to find a valid operator
390
- while ($this->match(self::$operatorString.($needWhite ? '\s+' : ''), $m) && self::$precedence[$m[1]] >= $minP) {
 
 
 
 
391
  $needWhite = true;
 
 
 
 
392
  // get rhs
393
  $s = $this->seek();
394
- if ($this->literal('(') && $this->expression($exp) && $this->literal(')')) {
395
- $needWhite = false;
 
396
  $rhs = $exp;
397
- } elseif ($this->seek($s) && $this->value($val)) {
398
- $rhs = $val;
399
- } else break;
 
 
 
 
 
400
 
401
  // peek for next operator to see what to do with rhs
402
  if ($this->peek(self::$operatorString, $next) && self::$precedence[$next[1]] > $minP) {
@@ -410,6 +394,11 @@ class lessc {
410
  $lhs = $this->evaluate($m[1], $lhs, $rhs);
411
 
412
  $ss = $this->seek();
 
 
 
 
 
413
  }
414
  $this->seek($ss);
415
 
@@ -453,9 +442,8 @@ class lessc {
453
  // must be done before color
454
  // this needs negation too
455
  if ($this->accessor($a)) {
456
- $tmp = $this->getEnv($a[0]);
457
- if ($tmp && isset($tmp[$a[1]]))
458
- $value = end($tmp[$a[1]]);
459
  return true;
460
  }
461
 
@@ -484,6 +472,22 @@ class lessc {
484
  return true;
485
  }
486
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
487
  return false;
488
  }
489
 
@@ -517,20 +521,13 @@ class lessc {
517
  }
518
 
519
  // a list of media types, very lenient
520
- function mediaTypes(&$types, &$rest) {
521
- $s = $this->seek();
522
- $types = array();
523
- while ($this->match('([^,{\s]+)', $m)) {
524
- $types[] = $m[1];
525
- if (!$this->literal(',')) break;
526
- }
527
-
528
- // get everything else
529
  if ($this->to('{', $rest, true, true)) {
530
- $rest = trim($rest);
 
531
  }
532
 
533
- return count($types) > 0;
534
  }
535
 
536
  // a scoped value accessor
@@ -547,7 +544,7 @@ class lessc {
547
  // why is a property wrapped in quotes, who knows!
548
  if ($this->variable($name)) {
549
  $name = $this->vPrefix.$name;
550
- } elseif($this->literal("'") && $this->keyword($name) && $this->literal("'")) {
551
  // .. $this->count is messed up if we wanted to test another access type
552
  } else {
553
  $this->seek($s);
@@ -559,7 +556,7 @@ class lessc {
559
  return false;
560
  }
561
 
562
- $var = array($scope, $name);
563
  return true;
564
  }
565
 
@@ -568,7 +565,7 @@ class lessc {
568
  $s = $this->seek();
569
  if ($this->literal('"', false)) {
570
  $delim = '"';
571
- } else if($this->literal("'", false)) {
572
  $delim = "'";
573
  } else {
574
  return false;
@@ -583,7 +580,11 @@ class lessc {
583
  return true;
584
  }
585
 
586
- // a numerical unit
 
 
 
 
587
  function unit(&$unit, $allowed = null) {
588
  $simpleCase = $allowed == null;
589
  if (!$allowed) $allowed = self::$units;
@@ -595,7 +596,7 @@ class lessc {
595
  // check for size/height font unit.. should this even be here?
596
  if ($simpleCase) {
597
  $s = $this->seek();
598
- if ($this->literal('/', false) && $this->unit($right, self::$units)) {
599
  $unit = array('keyword', $this->compileValue($unit).'/'.$this->compileValue($right));
600
  } else {
601
  // get rid of whitespace
@@ -624,7 +625,7 @@ class lessc {
624
  }
625
 
626
  $num = hexdec($num);
627
- foreach(array(3,2,1) as $i) {
628
  $t = $num % $width;
629
  $num /= $width;
630
 
@@ -639,13 +640,13 @@ class lessc {
639
  }
640
 
641
  // consume a list of property values delimited by ; and wrapped in ()
642
- function argumentValues(&$args, $delim = ';') {
643
  $s = $this->seek();
644
  if (!$this->literal('(')) return false;
645
 
646
  $values = array();
647
  while (true) {
648
- if ($this->propertyValue($value)) $values[] = $value;
649
  if (!$this->literal($delim)) break;
650
  else {
651
  if ($value == null) $values[] = null;
@@ -662,15 +663,16 @@ class lessc {
662
  return true;
663
  }
664
 
665
- // consume an argument definition list surrounded by (), each argument is a variable name with optional value
666
- function argumentDef(&$args, $delim = ';') {
 
667
  $s = $this->seek();
668
  if (!$this->literal('(')) return false;
669
 
670
  $values = array();
671
  while ($this->variable($vname)) {
672
  $arg = array($vname);
673
- if ($this->assign() && $this->propertyValue($value)) {
674
  $arg[] = $value;
675
  // let the : slide if there is no value
676
  }
@@ -701,6 +703,21 @@ class lessc {
701
  return true;
702
  }
703
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
704
  // a bracketed value (contained within in a tag definition)
705
  function tagBracket(&$value) {
706
  $s = $this->seek();
@@ -708,6 +725,9 @@ class lessc {
708
  $value = '['.$c.']';
709
  // whitespace?
710
  if ($this->match('', $_)) $value .= $_[0];
 
 
 
711
  return true;
712
  }
713
 
@@ -740,7 +760,7 @@ class lessc {
740
  function func(&$func) {
741
  $s = $this->seek();
742
 
743
- if ($this->match('([\w\-_][\w\-_:\.]*)', $m) && $this->literal('(')) {
744
  $fname = $m[1];
745
  if ($fname == 'url') {
746
  $this->to(')', $content, true);
@@ -783,8 +803,12 @@ class lessc {
783
  return false;
784
  }
785
 
786
- // consume an assignment operator
787
- function assign() {
 
 
 
 
788
  return $this->literal(':') || $this->literal('=');
789
  }
790
 
@@ -813,60 +837,242 @@ class lessc {
813
  else return array('list', $delim, $items);
814
  }
815
 
816
- function compileBlock($rtags, $env) {
817
- // don't render functions
818
- // todo: this shouldn't need to happen because multiplyTags prunes them, verify
819
- /*
820
- foreach ($rtags as $i => $tag) {
821
- if (preg_match('/( |^)%/', $tag))
822
- unset($rtags[$i]);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
823
  }
824
- */
825
- if (empty($rtags)) return '';
826
 
827
- $props = 0;
828
- // print all the visible properties
 
 
 
829
  ob_start();
830
- foreach ($env as $name => $value) {
831
- // todo: change this, poor hack
832
- // make a better name storage system!!! (value types are fine)
833
- // but.. don't render special properties (blocks, vars, metadata)
834
- if (isset($value[0]) && $name{0} != $this->vPrefix && $name != '__args') {
835
- echo $this->compileProperty($name, $value, 1)."\n";
836
- $props += count($value);
 
 
 
 
837
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
838
  }
839
- $list = ob_get_clean();
840
- if ($props == 0) return '';
841
 
842
- $blockDecl = implode(", ", $rtags).' {';
843
- if ($props > 1)
844
- return $this->indent($blockDecl).$list.$this->indent('}');
845
- else {
846
- $list = ' '.trim($list).' ';
847
- return $this->indent($blockDecl.$list.'}');
848
  }
849
 
 
850
  }
851
 
852
- // write a line a the proper indent
853
- function indent($str, $level = null) {
854
- if (is_null($level)) $level = $this->indentLevel;
855
- return str_repeat(' ', $level).$str."\n";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
856
  }
857
 
858
- function compileProperty($name, $value, $level = 0) {
859
- $level = $this->indentLevel + $level;
860
- // output all repeated properties
861
- foreach ($value as $v)
862
- $props[] = str_repeat(' ', $level).
863
- $name.':'.$this->compileValue($v).';';
864
 
865
- return implode("\n", $props);
 
 
 
 
 
 
 
 
 
866
  }
867
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
868
  function compileValue($value) {
869
- switch($value[0]) {
870
  case 'list':
871
  // [1] - delimiter
872
  // [2] - array of values
@@ -876,31 +1082,25 @@ class lessc {
876
  case 'number':
877
  // [1] - the number
878
  return $value[1];
879
- case 'expression':
880
- // [1] - operator
881
- // [2] - value of left hand side
882
- // [3] - value of right
883
- return $this->compileValue($this->evaluate($value[1], $value[2], $value[3]));
884
  case 'string':
885
  // [1] - contents of string (includes quotes)
886
 
887
  // search for inline variables to replace
888
  $replace = array();
889
- if (preg_match_all('/{('.$this->preg_quote($this->vPrefix).'[\w-_][0-9\w-_]*?)}/', $value[1], $m)) {
890
- foreach($m[1] as $name) {
891
  if (!isset($replace[$name]))
892
- $replace[$name] = $this->compileValue(array('variable', $name));
893
  }
894
  }
 
895
  foreach ($replace as $var=>$val) {
896
- // strip quotes
897
- if (preg_match('/^(["\']).*?(\1)$/', $val)) {
898
  $val = substr($val, 1, -1);
899
  }
900
- $value[1] = str_replace('{'.$var.'}', $val, $value[1]);
901
  }
902
 
903
-
904
  return $value[1];
905
  case 'color':
906
  // [1] - red component (either number for a %)
@@ -910,47 +1110,84 @@ class lessc {
910
  if (count($value) == 5) { // rgba
911
  return 'rgba('.$value[1].','.$value[2].','.$value[3].','.$value[4].')';
912
  }
913
-
914
- $out = '#';
915
- foreach (range(1,3) as $i)
916
- $out .= ($value[$i] < 16 ? '0' : '').dechex($value[$i]);
917
- return $out;
918
- case 'variable':
919
- // [1] - the name of the variable including @
920
- $tmp = $this->compileValue(
921
- $this->getVal($value[1], $this->pushName($value[1]))
922
- );
923
- $this->popName();
924
-
925
- return $tmp;
926
- case 'negative':
927
- // [1] - some value that needs to become negative
928
- return $this->compileValue($this->reduce($value));
929
  case 'function':
930
  // [1] - function name
931
  // [2] - some value representing arguments
932
 
933
- // see if there is a library function for this func
934
- $f = array($this, 'lib_'.$value[1]);
935
- if (is_callable($f)) {
936
- return call_user_func($f, $value[2]);
937
  }
938
-
939
- return $value[1].'('.$this->compileValue($value[2]).')';
940
-
941
  default: // assumed to be unit
942
  return $value[1].$value[0];
943
  }
944
  }
945
 
946
- function lib_quote($arg) {
947
- return '"'.$this->compileValue($arg).'"';
 
 
 
 
 
948
  }
949
 
950
- function lib_unquote($arg) {
951
- $out = $this->compileValue($arg);
952
- if ($this->quoted($out)) $out = substr($out, 1, -1);
953
- return $out;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
954
  }
955
 
956
  // is a string surrounded in quotes? returns the quoting char if true
@@ -960,49 +1197,260 @@ class lessc {
960
  else return false;
961
  }
962
 
963
- // convert rgb, rgba into color type suitable for math
964
- // todo: add hsl
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
965
  function funcToColor($func) {
966
  $fname = $func[1];
967
- if (!preg_match('/^(rgb|rgba)$/', $fname)) return false;
968
  if ($func[2][0] != 'list') return false; // need a list of arguments
 
969
 
970
- $components = array();
971
- $i = 1;
972
- foreach ($func[2][2] as $c) {
973
- $c = $this->reduce($c);
974
- if ($i < 4) {
975
- if ($c[0] == '%') $components[] = 255 * ($c[1] / 100);
976
- else $components[] = floatval($c[1]);
977
- } elseif ($i == 4) {
978
- if ($c[0] == '%') $components[] = 1.0 * ($c[1] / 100);
979
- else $components[] = floatval($c[1]);
980
- } else break;
981
 
982
- $i++;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
983
  }
984
- while (count($components) < 3) $components[] = 0;
985
 
986
- array_unshift($components, 'color');
987
- return $this->fixColor($components);
988
  }
989
 
990
  // reduce a delayed type to its final value
991
  // dereference variables and solve equations
992
  function reduce($var, $defaultValue = array('number', 0)) {
993
- $pushed = 0; // number of variable names pushed
994
-
995
  while (in_array($var[0], self::$dtypes)) {
996
- if ($var[0] == 'expression') {
 
 
 
997
  $var = $this->evaluate($var[1], $var[2], $var[3]);
998
- } else if ($var[0] == 'variable') {
999
- $var = $this->getVal($var[1], $this->pushName($var[1]), $defaultValue);
1000
- $pushed++;
1001
- } else if ($var[0] == 'function') {
 
 
1002
  $color = $this->funcToColor($var);
1003
  if ($color) $var = $color;
1004
- break; // no where to go after a function
1005
- } else if ($var[0] == 'negative') {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1006
  $value = $this->reduce($var[1]);
1007
  if (is_numeric($value[1])) {
1008
  $value[1] = -1*$value[1];
@@ -1011,7 +1459,6 @@ class lessc {
1011
  }
1012
  }
1013
 
1014
- while ($pushed != 0) { $this->popName(); $pushed--; }
1015
  return $var;
1016
  }
1017
 
@@ -1120,7 +1567,7 @@ class lessc {
1120
  else $type = $right[0];
1121
 
1122
  $value = 0;
1123
- switch($op) {
1124
  case '+':
1125
  $value = $left[1] + $right[1];
1126
  break;
@@ -1147,124 +1594,68 @@ class lessc {
1147
 
1148
  /* environment functions */
1149
 
1150
- // push name on expand stack, and return its
1151
- // count before being pushed
1152
- function pushName($name) {
1153
- $count = array_count_values($this->expandStack);
1154
- $count = isset($count[$name]) ? $count[$name] : 0;
1155
 
1156
- $this->expandStack[] = $name;
 
 
1157
 
1158
- return $count;
 
1159
  }
1160
-
1161
- // pop name off expand stack and return it
1162
- function popName() {
1163
- return array_pop($this->expandStack);
 
 
1164
  }
1165
 
1166
- // push a new environment
1167
- function push() {
1168
- $this->level++;
1169
- $this->env[] = array();
 
 
 
 
 
1170
  }
1171
 
1172
- // pop environment off the stack
1173
  function pop() {
1174
- if ($this->level == 1)
1175
- throw new exception('parse error: unexpected end of block');
1176
-
1177
- $this->level--;
1178
- return array_pop($this->env);
1179
  }
1180
 
1181
  // set something in the current env
1182
  function set($name, $value) {
1183
- $this->env[count($this->env) - 1][$name] = $value;
1184
- }
1185
-
1186
- // append to array in the current env
1187
- function append($name, $value) {
1188
- $this->env[count($this->env) - 1][$name][] = $value;
1189
- }
1190
-
1191
- // put on the front of the value
1192
- function prepend($name, $value) {
1193
- if (isset($this->env[count($this->env) - 1][$name]))
1194
- array_unshift($this->env[count($this->env) - 1][$name], $value);
1195
- else $this->append($name, $value);
1196
- }
1197
-
1198
- // get the highest occurrence of value
1199
- function get($name, $env = null) {
1200
- if (empty($env)) $env = $this->env;
1201
-
1202
- for ($i = count($env) - 1; $i >= 0; $i--)
1203
- if (isset($env[$i][$name])) return $env[$i][$name];
1204
-
1205
- return null;
1206
  }
1207
 
1208
- // get the most recent value of a variable
1209
- // return default if it isn't found
1210
- // $skip is number of vars to skip
1211
- function getVal($name, $skip = 0, $default = array('keyword', '')) {
1212
- $val = $this->get($name);
1213
- if ($val == null) return $default;
1214
-
1215
- $tmp = $this->env;
1216
- while (!isset($tmp[count($tmp) - 1][$name])) array_pop($tmp);
1217
- while ($skip > 0) {
1218
- $skip--;
1219
-
1220
- if (!empty($val)) {
1221
- array_pop($val);
1222
- }
1223
-
1224
- if (empty($val)) {
1225
- array_pop($tmp);
1226
- $val = $this->get($name, $tmp);
1227
- }
1228
-
1229
- if (empty($val)) return $default;
1230
- }
1231
-
1232
- return end($val);
1233
  }
1234
 
1235
- // get the environment described by path, an array of env names
1236
- function getEnv($path) {
1237
- if (!is_array($path)) $path = array($path);
1238
-
1239
- // move @ tags out of variable namespace
1240
- foreach($path as &$tag)
1241
- if ($tag{0} == $this->vPrefix) $tag[0] = $this->mPrefix;
1242
-
1243
- $env = $this->get(array_shift($path));
1244
- while ($sub = array_shift($path)) {
1245
- if (isset($env[$sub])) // todo add a type check for environment
1246
- $env = $env[$sub];
1247
- else {
1248
- $env = null;
1249
- break;
1250
- }
1251
  }
1252
- return $env;
1253
- }
1254
-
1255
- // merge a block into the current env
1256
- function merge($name, $value) {
1257
- // if the current block isn't there then just set
1258
- $top =& $this->env[count($this->env) - 1];
1259
- if (!isset($top[$name])) return $this->set($name, $value);
1260
 
1261
- // copy the block into the old one, including meta data
1262
- foreach ($value as $k=>$v) {
1263
- // todo: merge property values instead of replacing
1264
- // have to check type for this
1265
- $top[$name][$k] = $v;
1266
- }
1267
  }
 
 
1268
 
1269
  function literal($what, $eatWhitespace = true) {
1270
  // this is here mainly prevent notice from { } string accessor
@@ -1289,7 +1680,7 @@ class lessc {
1289
  // advance counter to next occurrence of $what
1290
  // $until - don't include $what in advance
1291
  function to($what, &$out, $until = false, $allowNewline = false) {
1292
- $validChars = $allowNewline ? "[^\n]" : '.';
1293
  if (!$this->match('('.$validChars.'*?)'.$this->preg_quote($what), $m, !$until)) return false;
1294
  if ($until) $this->count -= strlen($what); // give back $what
1295
  $out = $m[1];
@@ -1306,7 +1697,6 @@ class lessc {
1306
  return false;
1307
  }
1308
 
1309
-
1310
  // match something without consuming it
1311
  function peek($regex, &$out = null) {
1312
  $r = '/'.$regex.'/Ais';
@@ -1322,52 +1712,99 @@ class lessc {
1322
  return true;
1323
  }
1324
 
1325
- // parse and compile buffer
1326
- function parse($str = null) {
1327
- if ($str) $this->buffer = $str;
1328
-
1329
- $this->env = array();
1330
  $this->expandStack = array();
1331
  $this->indentLevel = 0;
1332
- $this->media = null;
1333
  $this->count = 0;
1334
  $this->line = 1;
1335
- $this->level = 0;
1336
 
1337
- $this->buffer = $this->removeComments($this->buffer);
1338
- $this->push(); // set up global scope
1339
- $this->set('__tags', array('')); // equivalent to 1 in tag multiplication
1340
 
1341
  // trim whitespace on head
1342
  if (preg_match('/^\s+/', $this->buffer, $m)) {
1343
  $this->line += substr_count($m[0], "\n");
1344
  $this->buffer = ltrim($this->buffer);
1345
  }
 
1346
 
1347
- $out = '';
1348
- while (false !== ($compiled = $this->chunk())) {
1349
- if (is_string($compiled)) $out .= $compiled;
1350
- }
 
 
 
 
 
 
 
 
 
1351
 
1352
- if ($this->count != strlen($this->buffer)) $this->throwParseError();
 
1353
 
1354
- if (count($this->env) > 1)
1355
  throw new exception('parse error: unclosed block');
1356
 
1357
- // print_r($this->env);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1358
  return $out;
1359
  }
1360
 
1361
  function throwParseError($msg = 'parse error') {
1362
  $line = $this->line + substr_count(substr($this->buffer, 0, $this->count), "\n");
 
 
 
 
 
 
1363
  if ($this->peek("(.*?)(\n|$)", $m))
1364
- throw new exception($msg.': failed at `'.$m[1].'` line: '.$line);
1365
  }
1366
 
1367
- function __construct($fname = null) {
 
 
 
1368
  if (!self::$operatorString) {
1369
  self::$operatorString =
1370
- '('.implode('|', array_map(array($this, 'preg_quote'), array_keys(self::$precedence))).')';
 
1371
  }
1372
 
1373
  if ($fname) {
@@ -1396,7 +1833,7 @@ class lessc {
1396
  $done = false;
1397
  while (true) {
1398
  // find the next item
1399
- foreach($look as $token) {
1400
  $pos = strpos($text, $token);
1401
  if ($pos !== false) {
1402
  if (!isset($min) || $pos < $min[1]) $min = array($token, $pos);
@@ -1408,7 +1845,7 @@ class lessc {
1408
  $count = $min[1];
1409
  $skip = 0;
1410
  $newlines = 0;
1411
- switch($min[0]) {
1412
  case 'url(':
1413
  if (preg_match('/url\(.*?\)/', $text, $m, 0, $count))
1414
  $count += strlen($m[0]) - strlen($min[0]);
@@ -1419,7 +1856,9 @@ class lessc {
1419
  $count += strlen($m[0]) - 1;
1420
  break;
1421
  case '//':
1422
- $skip = strpos($text, "\n", $count) - $count;
 
 
1423
  break;
1424
  case '/*':
1425
  if (preg_match('/\/\*.*?\*\//s', $text, $m, 0, $count)) {
@@ -1483,17 +1922,17 @@ class lessc {
1483
  // assume no root
1484
  $root = null;
1485
 
1486
- if ( is_string($in) ) {
1487
  $root = $in;
1488
- } elseif ( is_array($in) and isset($in['root']) ) {
1489
- if ( $force or ! isset($in['files']) ) {
1490
  // If we are forcing a recompile or if for some reason the
1491
  // structure does not contain any file information we should
1492
  // specify the root to trigger a rebuild.
1493
  $root = $in['root'];
1494
- } elseif ( isset($in['files']) and is_array($in['files']) ) {
1495
- foreach ( $in['files'] as $fname => $ftime ) {
1496
- if ( ! file_exists($fname) or filemtime($fname) > $ftime ) {
1497
  // One of the files we knew about previously has changed
1498
  // so we should look at our incoming root again.
1499
  $root = $in['root'];
@@ -1507,7 +1946,7 @@ class lessc {
1507
  return null;
1508
  }
1509
 
1510
- if ( $root !== null ) {
1511
  // If we have a root value which means we should rebuild.
1512
  $less = new lessc($root);
1513
  $out = array();
@@ -1523,7 +1962,5 @@ class lessc {
1523
  }
1524
 
1525
  }
1526
-
1527
  }
1528
 
1529
- ?>
1
  <?php
2
 
3
  /**
4
+ * lessphp v0.3.0
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
+
40
+ public $indentLevel;
41
+ public $indentChar = ' ';
 
 
42
 
43
+ protected $env = null;
44
 
45
+ protected $allParsedFiles = array();
46
 
47
+ public $vPrefix = '@'; // prefix of abstract properties
48
+ public $mPrefix = '$'; // prefix of abstract blocks
49
+ public $imPrefix = '!'; // special character to add !important
50
+ public $parentSelector = '&';
51
 
52
+ static protected $precedence = array(
53
  '+' => 0,
54
  '-' => 0,
55
  '*' => 1,
56
  '/' => 1,
57
  '%' => 1,
58
  );
59
+ static protected $operatorString; // regex string to match any of the operators
60
 
61
+ // types that have delayed computation
62
+ static protected $dtypes = array('expression', 'variable',
63
+ 'function', 'negative', 'list', 'lookup');
64
 
65
+ /**
66
+ * @link http://www.w3.org/TR/css3-values/
67
+ */
68
+ static protected $units=array(
69
+ 'em', 'ex', 'px', 'gd', 'rem', 'vw', 'vh', 'vm', 'ch', // Relative length units
70
+ 'in', 'cm', 'mm', 'pt', 'pc', // Absolute length units
71
+ '%', // Percentages
72
+ 'deg', 'grad', 'rad', 'turn', // Angles
73
+ 'ms', 's', // Times
74
+ 'Hz', 'kHz', //Frequencies
75
+ );
76
+
77
  public $importDisabled = false;
78
  public $importDir = '';
79
 
80
+ public $compat = false; // lessjs compatibility mode, does nothing right now
 
 
 
81
 
82
+ /**
83
+ * if we are in an expression then we don't need to worry about parsing font shorthand
84
+ * $inExp becomes true after the first value in an expression, or if we enter parens
85
+ */
86
+ protected $inExp = false;
 
 
 
 
 
 
 
87
 
88
+ /**
89
+ * if we are in parens we can be more liberal with whitespace around operators because
90
+ * it must evaluate to a single value and thus is less ambiguous.
91
+ *
92
+ * Consider:
93
+ * property1: 10 -5; // is two numbers, 10 and -5
94
+ * property2: (10 -5); // should evaluate to 5
95
+ */
96
+ protected $inParens = false;
97
+
98
+ /**
99
+ * Parse a single chunk off the head of the buffer and place it.
100
+ * @return false when the buffer is empty, or there is an error
101
+ *
102
+ * This functions is called repeatedly until the entire document is
103
+ * parsed.
104
+ *
105
+ * This parser is most similar to a recursive descent parser. Single
106
+ * functions represent discrete grammatical rules for the language, and
107
+ * they are able to capture the text that represents those rules.
108
+ *
109
+ * Consider the function lessc::keyword(). (all parse functions are
110
+ * structured the same)
111
+ *
112
+ * The function takes a single reference argument. When calling the the
113
+ * function it will attempt to match a keyword on the head of the buffer.
114
+ * If it is successful, it will place the keyword in the referenced
115
+ * argument, advance the position in the buffer, and return true. If it
116
+ * fails then it won't advance the buffer and it will return false.
117
+ *
118
+ * All of these parse functions are powered by lessc::match(), which behaves
119
+ * the same way, but takes a literal regular expression. Sometimes it is
120
+ * more convenient to use match instead of creating a new function.
121
+ *
122
+ * Because of the format of the functions, to parse an entire string of
123
+ * grammatical rules, you can chain them together using &&.
124
+ *
125
+ * But, if some of the rules in the chain succeed before one fails, then
126
+ * then buffer position will be left at an invalid state. In order to
127
+ * avoid this, lessc::seek() is used to remember and set buffer positions.
128
+ *
129
+ * Before doing a chain, use $s = $this->seek() to remember the current
130
+ * position into $s. Then if a chain fails, use $this->seek($s) to
131
+ * go back where we started.
132
+ */
133
+ function parseChunk() {
134
+ if (empty($this->buffer)) return false;
135
+ $s = $this->seek();
136
+
137
+ // setting a property
138
+ if ($this->keyword($key) && $this->assign() &&
139
+ $this->propertyValue($value) && $this->end())
140
+ {
141
+ $this->append(array('assign', $key, $value));
142
+ return true;
143
  } else {
144
  $this->seek($s);
145
  }
146
 
147
+ // look for special css blocks
148
+ if ($this->env->parent == null && $this->literal('@', false)) {
149
+ $this->count--;
150
+
151
  // a font-face block
152
  if ($this->literal('@font-face') && $this->literal('{')) {
153
+ $b = $this->pushSpecialBlock('@font-face');
 
 
154
  return true;
155
  } else {
156
  $this->seek($s);
157
  }
158
 
159
  // charset
160
+ if ($this->literal('@charset') && $this->propertyValue($value) &&
161
+ $this->end())
162
+ {
163
+ $this->append(array('charset', $value));
164
+ return true;
165
  } else {
166
  $this->seek($s);
167
  }
168
 
169
+
170
  // media
171
+ if ($this->literal('@media') && $this->mediaTypes($types) &&
172
+ $this->literal('{'))
173
+ {
174
+ $b = $this->pushSpecialBlock('@media');
175
+ $b->media = $types;
176
+ return true;
177
  } else {
178
  $this->seek($s);
179
  }
180
+
181
  // css animations
182
+ if ($this->match('(@(-[a-z]+-)?keyframes)', $m) &&
183
+ $this->propertyValue($value) && $this->literal('{'))
184
+ {
185
+ $b = $this->pushSpecialBlock(trim($m[0]));
186
+ $b->keyframes = $value;
187
+ return true;
188
  } else {
189
  $this->seek($s);
190
  }
191
  }
192
+
193
+ if (isset($this->env->keyframes)) {
194
+ if ($this->match("(to|from|[0-9]+%)", $m) && $this->literal('{')) {
195
+ $this->pushSpecialBlock($m[1]);
196
+ return true;
197
+ } else {
198
+ $this->seek($s);
199
+ }
200
  }
201
 
202
+ // setting a variable
203
+ if ($this->variable($name) && $this->assign() &&
204
+ $this->propertyValue($value) && $this->end())
205
+ {
206
+ $this->append(array('assign', $this->vPrefix.$name, $value));
207
  return true;
208
  } else {
209
  $this->seek($s);
210
  }
211
 
212
+ if ($this->import($url, $media)) {
213
+ // don't check .css files
214
+ if (empty($media) && substr_compare($url, '.css', -4, 4) !== 0) {
215
+ if ($this->importDisabled) {
216
+ $this->append(array('raw', '/* import disabled */'));
217
+ } else {
218
+ $path = $this->findImport($url);
219
+ if (!is_null($path)) {
220
+ $this->append(array('import', $path));
221
+ return true;
222
+ }
223
+ }
224
+ }
225
 
226
+ $this->append(array('raw', '@import url("'.$url.'")'.
227
+ ($media ? ' '.$media : '').';'));
228
+ return true;
229
+ }
230
 
231
+ // opening parametric mixin
232
+ if ($this->tag($tag, true) && $this->argumentDef($args) &&
233
+ $this->literal('{'))
234
+ {
235
+ $block = $this->pushBlock($this->fixTags(array($tag)));
236
+ $block->args = $args;
237
  return true;
238
  } else {
239
  $this->seek($s);
240
  }
241
 
242
+ // opening a simple block
243
  if ($this->tags($tags) && $this->literal('{')) {
244
+ $tags = $this->fixTags($tags);
245
+ $this->pushBlock($tags);
 
 
 
 
 
 
246
  return true;
247
  } else {
248
  $this->seek($s);
249
  }
250
 
251
+ // closing a block
252
  if ($this->literal('}')) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253
  try {
254
+ $block = $this->pop();
255
  } catch (exception $e) {
256
  $this->seek($s);
257
  $this->throwParseError($e->getMessage());
258
  }
259
 
260
+ $hidden = true;
261
+ if (!isset($block->args)) foreach ($block->tags as $tag) {
262
+ if ($tag{0} != $this->mPrefix) {
263
+ $hidden = false;
264
+ break;
 
 
 
 
265
  }
266
  }
267
 
268
+ if (!$hidden) $this->append(array('block', $block));
269
+ foreach ($block->tags as $tag) {
270
+ $this->env->children[$tag] = $block;
 
 
 
 
 
 
 
 
 
 
271
  }
272
 
273
+ return true;
274
  }
275
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
276
 
277
+ // mixin
278
+ if ($this->mixinTags($tags) &&
279
+ ($this->argumentValues($argv) || true) && $this->end())
280
+ {
281
+ $tags = $this->fixTags($tags);
282
+ $this->append(array('mixin', $tags, $argv));
283
+ return true;
284
  } else {
285
  $this->seek($s);
286
  }
 
287
  // spare ;
288
  if ($this->literal(';')) return true;
289
 
290
+ return false; // got nothing, throw error
291
  }
292
 
293
+ function fixTags($tags) {
294
+ // move @ tags out of variable namespace
295
+ foreach ($tags as &$tag) {
296
+ if ($tag{0} == $this->vPrefix) $tag[0] = $this->mPrefix;
297
+ }
298
+ return $tags;
299
  }
300
 
301
+ // attempts to find the path of an import url, returns null for css files
302
+ function findImport($url) {
303
+ foreach ((array)$this->importDir as $dir) {
304
+ $full = $dir.(substr($dir, -1) != '/' ? '/' : '').$url;
305
+ if ($this->fileExists($file = $full.'.less') || $this->fileExists($file = $full)) {
306
+ return $file;
 
 
 
 
 
 
 
 
 
 
 
307
  }
308
  }
309
 
310
+ return null;
311
+ }
312
+
313
+ function fileExists($name) {
314
+ // sym link workaround
315
+ return file_exists($name) || file_exists(realpath(preg_replace('/\w+\/\.\.\//', '', $name)));
316
  }
317
 
318
  // a list of expressions
329
  return true;
330
  }
331
 
332
+ /**
333
+ * Attempt to consume an expression.
334
+ * @link http://en.wikipedia.org/wiki/Operator-precedence_parser#Pseudo-code
335
+ */
336
  function expression(&$out) {
337
  $s = $this->seek();
338
+ if ($this->literal('(') && ($this->inExp = $this->inParens = true) && $this->expression($exp) && $this->literal(')')) {
 
339
  $lhs = $exp;
 
340
  } elseif ($this->seek($s) && $this->value($val)) {
341
  $lhs = $val;
342
  } else {
343
+ $this->inParens = $this->inExp = false;
344
+ $this->seek($s);
345
  return false;
346
  }
347
 
348
+ $out = $this->expHelper($lhs, 0);
349
+ $this->inParens = $this->inExp = false;
350
  return true;
351
  }
352
 
353
+ /**
354
+ * recursively parse infix equation with $lhs at precedence $minP
355
+ */
356
+ function expHelper($lhs, $minP) {
357
+ $this->inExp = true;
358
  $ss = $this->seek();
359
+
360
+ // if the if there was whitespace before the operator, then we require whitespace after
361
+ // the operator for it to be a mathematical operator.
362
+
363
+ $needWhite = false;
364
+ if (!$this->inParens && preg_match('/\s/', $this->buffer{$this->count - 1})) {
365
  $needWhite = true;
366
+ }
367
+
368
+ // try to find a valid operator
369
+ while ($this->match(self::$operatorString.($needWhite ? '\s' : ''), $m) && self::$precedence[$m[1]] >= $minP) {
370
  // get rhs
371
  $s = $this->seek();
372
+ $p = $this->inParens;
373
+ if ($this->literal('(') && ($this->inParens = true) && $this->expression($exp) && $this->literal(')')) {
374
+ $this->inParens = $p;
375
  $rhs = $exp;
376
+ } else {
377
+ $this->inParens = $p;
378
+ if ($this->seek($s) && $this->value($val)) {
379
+ $rhs = $val;
380
+ } else {
381
+ break;
382
+ }
383
+ }
384
 
385
  // peek for next operator to see what to do with rhs
386
  if ($this->peek(self::$operatorString, $next) && self::$precedence[$next[1]] > $minP) {
394
  $lhs = $this->evaluate($m[1], $lhs, $rhs);
395
 
396
  $ss = $this->seek();
397
+
398
+ $needWhite = false;
399
+ if (!$this->inParens && preg_match('/\s/', $this->buffer{$this->count - 1})) {
400
+ $needWhite = true;
401
+ }
402
  }
403
  $this->seek($ss);
404
 
442
  // must be done before color
443
  // this needs negation too
444
  if ($this->accessor($a)) {
445
+ $a[1] = $this->fixTags($a[1]);
446
+ $value = $a;
 
447
  return true;
448
  }
449
 
472
  return true;
473
  }
474
 
475
+ // unquote string
476
+ if ($this->literal("~") && $this->string($value, $d)) {
477
+ $value = array("keyword", $value);
478
+ return true;
479
+ } else {
480
+ $this->seek($s);
481
+ }
482
+
483
+ // css hack: \0
484
+ if ($this->literal('\\') && $this->match('([0-9]+)', $m)) {
485
+ $value = array('keyword', '\\'.$m[1]);
486
+ return true;
487
+ } else {
488
+ $this->seek($s);
489
+ }
490
+
491
  return false;
492
  }
493
 
521
  }
522
 
523
  // a list of media types, very lenient
524
+ function mediaTypes(&$types) {
 
 
 
 
 
 
 
 
525
  if ($this->to('{', $rest, true, true)) {
526
+ $types = trim($rest);
527
+ return true;
528
  }
529
 
530
+ return false;
531
  }
532
 
533
  // a scoped value accessor
544
  // why is a property wrapped in quotes, who knows!
545
  if ($this->variable($name)) {
546
  $name = $this->vPrefix.$name;
547
+ } elseif ($this->literal("'") && $this->keyword($name) && $this->literal("'")) {
548
  // .. $this->count is messed up if we wanted to test another access type
549
  } else {
550
  $this->seek($s);
556
  return false;
557
  }
558
 
559
+ $var = array('lookup', $scope, $name);
560
  return true;
561
  }
562
 
565
  $s = $this->seek();
566
  if ($this->literal('"', false)) {
567
  $delim = '"';
568
+ } elseif ($this->literal("'", false)) {
569
  $delim = "'";
570
  } else {
571
  return false;
580
  return true;
581
  }
582
 
583
+ /**
584
+ * Consume a number and optionally a unit.
585
+ * Can also consume a font shorthand if it is a simple case.
586
+ * $allowed restricts the types that are matched.
587
+ */
588
  function unit(&$unit, $allowed = null) {
589
  $simpleCase = $allowed == null;
590
  if (!$allowed) $allowed = self::$units;
596
  // check for size/height font unit.. should this even be here?
597
  if ($simpleCase) {
598
  $s = $this->seek();
599
+ if (!$this->inExp && $this->literal('/', false) && $this->unit($right, self::$units)) {
600
  $unit = array('keyword', $this->compileValue($unit).'/'.$this->compileValue($right));
601
  } else {
602
  // get rid of whitespace
625
  }
626
 
627
  $num = hexdec($num);
628
+ foreach (array(3,2,1) as $i) {
629
  $t = $num % $width;
630
  $num /= $width;
631
 
640
  }
641
 
642
  // consume a list of property values delimited by ; and wrapped in ()
643
+ function argumentValues(&$args, $delim = ',') {
644
  $s = $this->seek();
645
  if (!$this->literal('(')) return false;
646
 
647
  $values = array();
648
  while (true) {
649
+ if ($this->expressionList($value)) $values[] = $value;
650
  if (!$this->literal($delim)) break;
651
  else {
652
  if ($value == null) $values[] = null;
663
  return true;
664
  }
665
 
666
+ // consume an argument definition list surrounded by ()
667
+ // each argument is a variable name with optional value
668
+ function argumentDef(&$args, $delim = ',') {
669
  $s = $this->seek();
670
  if (!$this->literal('(')) return false;
671
 
672
  $values = array();
673
  while ($this->variable($vname)) {
674
  $arg = array($vname);
675
+ if ($this->assign() && $this->expressionList($value)) {
676
  $arg[] = $value;
677
  // let the : slide if there is no value
678
  }
703
  return true;
704
  }
705
 
706
+ // list of tags of specifying mixin path
707
+ // optionally separated by > (lazy, accepts extra >)
708
+ function mixinTags(&$tags) {
709
+ $s = $this->seek();
710
+ $tags = array();
711
+ while ($this->tag($tt, true)) {
712
+ $tags[] = $tt;
713
+ $this->literal(">");
714
+ }
715
+
716
+ if (count($tags) == 0) return false;
717
+
718
+ return true;
719
+ }
720
+
721
  // a bracketed value (contained within in a tag definition)
722
  function tagBracket(&$value) {
723
  $s = $this->seek();
725
  $value = '['.$c.']';
726
  // whitespace?
727
  if ($this->match('', $_)) $value .= $_[0];
728
+
729
+ // escape parent selector
730
+ $value = str_replace($this->parentSelector, "&&", $value);
731
  return true;
732
  }
733
 
760
  function func(&$func) {
761
  $s = $this->seek();
762
 
763
+ if ($this->match('(%|[\w\-_][\w\-_:\.]*)', $m) && $this->literal('(')) {
764
  $fname = $m[1];
765
  if ($fname == 'url') {
766
  $this->to(')', $content, true);
803
  return false;
804
  }
805
 
806
+ /**
807
+ * Consume an assignment operator
808
+ * Can optionally take a name that will be set to the current property name
809
+ */
810
+ function assign($name = null) {
811
+ if ($name) $this->currentProperty = $name;
812
  return $this->literal(':') || $this->literal('=');
813
  }
814
 
837
  else return array('list', $delim, $items);
838
  }
839
 
840
+ /**
841
+ * Recursively compiles a block.
842
+ * @param $block the block
843
+ * @param $parentTags the tags of the block that contained this one
844
+ *
845
+ * A block is analogous to a CSS block in most cases. A single less document
846
+ * is encapsulated in a block when parsed, but it does not have parent tags
847
+ * so all of it's children appear on the root level when compiled.
848
+ *
849
+ * Blocks are made up of props and children.
850
+ *
851
+ * Props are property instructions, array tuples which describe an action
852
+ * to be taken, eg. write a property, set a variable, mixin a block.
853
+ *
854
+ * The children of a block are just all the blocks that are defined within.
855
+ *
856
+ * Compiling the block involves pushing a fresh environment on the stack,
857
+ * and iterating through the props, compiling each one.
858
+ *
859
+ * See lessc::compileProp()
860
+ *
861
+ */
862
+ function compileBlock($block, $parent_tags = null) {
863
+ $isRoot = $parent_tags == null && $block->tags == null;
864
+
865
+ $indent = str_repeat($this->indentChar, $this->indentLevel);
866
+
867
+ if (!empty($block->no_multiply)) {
868
+ $special_block = true;
869
+ $this->indentLevel++;
870
+ $tags = array();
871
+ } else {
872
+ $special_block = false;
873
+ $tags = $this->multiplyTags($parent_tags, $block->tags);
874
+ }
875
+
876
+ $this->pushEnv();
877
+ $lines = array();
878
+ $blocks = array();
879
+ foreach ($block->props as $prop) {
880
+ $this->compileProp($prop, $block, $tags, $lines, $blocks);
881
  }
 
 
882
 
883
+ $this->pop();
884
+
885
+ $nl = $isRoot ? "\n".$indent :
886
+ "\n".$indent.$this->indentChar;
887
+
888
  ob_start();
889
+
890
+ if ($special_block) {
891
+ $this->indentLevel--;
892
+ if (isset($block->media)) {
893
+ echo "@media ".$block->media;
894
+ } elseif (isset($block->keyframes)) {
895
+ echo $block->tags[0]." ".
896
+ $this->compileValue($this->reduce($block->keyframes));
897
+ } else {
898
+ list($name) = $block->tags;
899
+ echo $indent.$name;
900
  }
901
+
902
+ echo ' {'.(count($lines) > 0 ? $nl : "\n");
903
+ }
904
+
905
+ // dump it
906
+ if (count($lines) > 0) {
907
+ if (!$special_block && !$isRoot) {
908
+ echo $indent.implode(", ", $tags);
909
+ if (count($lines) > 1) echo " {".$nl;
910
+ else echo " { ";
911
+ }
912
+
913
+ echo implode($nl, $lines);
914
+
915
+ if (!$special_block && !$isRoot) {
916
+ if (count($lines) > 1) echo "\n".$indent."}\n";
917
+ else echo " }\n";
918
+ } else echo "\n";
919
  }
 
 
920
 
921
+ foreach ($blocks as $b) echo $b;
922
+
923
+ if ($special_block) {
924
+ echo $indent."}\n";
 
 
925
  }
926
 
927
+ return ob_get_clean();
928
  }
929
 
930
+
931
+ // find the fully qualified tags for a block and its parent's tags
932
+ function multiplyTags($parents, $current) {
933
+ if ($parents == null) return $current;
934
+
935
+ $tags = array();
936
+ foreach ($parents as $ptag) {
937
+ foreach ($current as $tag) {
938
+ // inject parent in place of parent selector, ignoring escaped valuews
939
+ $count = 0;
940
+ $parts = explode("&&", $tag);
941
+
942
+ foreach ($parts as $i => $chunk) {
943
+ $parts[$i] = str_replace($this->parentSelector, $ptag, $chunk, $c);
944
+ $count += $c;
945
+ }
946
+
947
+ $tag = implode("&", $parts);
948
+
949
+ if ($count > 0) {
950
+ $tags[] = trim($tag);
951
+ } else {
952
+ $tags[] = trim($ptag . ' ' . $tag);
953
+ }
954
+ }
955
+ }
956
+
957
+ return $tags;
958
  }
959
 
960
+ // attempt to find block pointed at by path within search_in or its parent
961
+ function findBlock($search_in, $path) {
962
+ if ($search_in == null) return null;
963
+ $name = $path[0];
 
 
964
 
965
+ if (isset($search_in->children[$name])) {
966
+ $block = $search_in->children[$name];
967
+ if (count($path) == 1) {
968
+ return $block;
969
+ } else {
970
+ return $this->findBlock($block, array_slice($path, 1));
971
+ }
972
+ } else {
973
+ return $this->findBlock($search_in->parent, $path);
974
+ }
975
  }
976
 
977
+ // sets all argument names in $args to either the default value
978
+ // or the one passed in through $values
979
+ function zipSetArgs($args, $values) {
980
+ $i = 0;
981
+ foreach ($args as $a) {
982
+ if ($i < count($values) && !is_null($values[$i])) {
983
+ $value = $values[$i];
984
+ } elseif (isset($a[1])) {
985
+ $value = $a[1];
986
+ } else $value = null;
987
+
988
+ $this->set($this->vPrefix.$a[0], $this->reduce($value));
989
+ $i++;
990
+ }
991
+ }
992
+
993
+ // compile a prop and update $lines or $blocks appropriately
994
+ function compileProp($prop, $block, $tags, &$_lines, &$_blocks) {
995
+ switch ($prop[0]) {
996
+ case 'assign':
997
+ list(, $name, $value) = $prop;
998
+ if ($name[0] == $this->vPrefix) {
999
+ $this->set($name, $this->reduce($value));
1000
+ } else {
1001
+ $_lines[] = "$name:".
1002
+ $this->compileValue($this->reduce($value)).";";
1003
+ }
1004
+ break;
1005
+ case 'block':
1006
+ list(, $child) = $prop;
1007
+ $_blocks[] = $this->compileBlock($child, $tags);
1008
+ break;
1009
+ case 'mixin':
1010
+ list(, $path, $args) = $prop;
1011
+
1012
+ $mixin = $this->findBlock($block, $path);
1013
+ if (is_null($mixin)) {
1014
+ // echo "failed to find block: ".implode(" > ", $path)."\n";
1015
+ break; // throw error here??
1016
+ }
1017
+
1018
+ $have_args = false;
1019
+ if (isset($mixin->args)) {
1020
+ $have_args = true;
1021
+ $this->pushEnv();
1022
+ $this->zipSetArgs($mixin->args, $args);
1023
+ }
1024
+
1025
+ $old_parent = $mixin->parent;
1026
+ $mixin->parent = $block;
1027
+
1028
+ foreach ($mixin->props as $sub_prop) {
1029
+ $this->compileProp($sub_prop, $mixin, $tags, $_lines, $_blocks);
1030
+ }
1031
+
1032
+ $mixin->parent = $old_parent;
1033
+
1034
+ if ($have_args) $this->pop();
1035
+
1036
+ break;
1037
+ case 'raw':
1038
+ $_lines[] = $prop[1];
1039
+ break;
1040
+ case 'import':
1041
+ list(, $path) = $prop;
1042
+ $this->addParsedFile($path);
1043
+ $root = $this->createChild($path)->parseTree();
1044
+
1045
+ $root->parent = $block;
1046
+ foreach ($root->props as $sub_prop) {
1047
+ $this->compileProp($sub_prop, $root, $tags, $_lines, $_blocks);
1048
+ }
1049
+
1050
+ // inject imported blocks into this block, local will overwrite import
1051
+ $block->children = array_merge($root->children, $block->children);
1052
+ break;
1053
+ case 'charset':
1054
+ list(, $value) = $prop;
1055
+ $_lines[] = '@charset '.$this->compileValue($this->reduce($value)).';';
1056
+ break;
1057
+ default:
1058
+ echo "unknown op: {$prop[0]}\n";
1059
+ throw new exception();
1060
+ }
1061
+ }
1062
+
1063
+
1064
+ /**
1065
+ * Compiles a primitive value into a CSS property value.
1066
+ *
1067
+ * Values in lessphp are typed by being wrapped in arrays, their format is
1068
+ * typically:
1069
+ *
1070
+ * array(type, contents [, additional_contents]*)
1071
+ *
1072
+ * Will not work on non reduced values (expressions, variables, etc)
1073
+ */
1074
  function compileValue($value) {
1075
+ switch ($value[0]) {
1076
  case 'list':
1077
  // [1] - delimiter
1078
  // [2] - array of values
1082
  case 'number':
1083
  // [1] - the number
1084
  return $value[1];
 
 
 
 
 
1085
  case 'string':
1086
  // [1] - contents of string (includes quotes)
1087
 
1088
  // search for inline variables to replace
1089
  $replace = array();
1090
+ if (preg_match_all('/'.$this->preg_quote($this->vPrefix).'\{([\w-_][0-9\w-_]*)\}/', $value[1], $m)) {
1091
+ foreach ($m[1] as $name) {
1092
  if (!isset($replace[$name]))
1093
+ $replace[$name] = $this->compileValue($this->reduce(array('variable', $this->vPrefix . $name)));
1094
  }
1095
  }
1096
+
1097
  foreach ($replace as $var=>$val) {
1098
+ if ($this->quoted($val)) {
 
1099
  $val = substr($val, 1, -1);
1100
  }
1101
+ $value[1] = str_replace($this->vPrefix. '{'.$var.'}', $val, $value[1]);
1102
  }
1103
 
 
1104
  return $value[1];
1105
  case 'color':
1106
  // [1] - red component (either number for a %)
1110
  if (count($value) == 5) { // rgba
1111
  return 'rgba('.$value[1].','.$value[2].','.$value[3].','.$value[4].')';
1112
  }
1113
+ return sprintf("#%02x%02x%02x", $value[1], $value[2], $value[3]);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1114
  case 'function':
1115
  // [1] - function name
1116
  // [2] - some value representing arguments
1117
 
1118
+ // see if function evaluates to something else
1119
+ $value = $this->reduce($value);
1120
+ if ($value[0] == 'function') {
1121
+ return $value[1].'('.$this->compileValue($value[2]).')';
1122
  }
1123
+ else return $this->compileValue($value);
 
 
1124
  default: // assumed to be unit
1125
  return $value[1].$value[0];
1126
  }
1127
  }
1128
 
1129
+ function lib_rgbahex($color) {
1130
+ if ($color[0] != 'color')
1131
+ throw new exception("color expected for rgbahex");
1132
+
1133
+ return sprintf("#%02x%02x%02x%02x",
1134
+ isset($color[4]) ? $color[4]*255 : 0,
1135
+ $color[1],$color[2], $color[3]);
1136
  }
1137
 
1138
+ // utility func to unquote a string
1139
+ function lib_e($arg) {
1140
+ switch ($arg[0]) {
1141
+ case "list":
1142
+ $items = $arg[2];
1143
+ if (isset($items[0])) {
1144
+ return $this->lib_e($items[0]);
1145
+ }
1146
+ return "";
1147
+ case "string":
1148
+ $str = $this->compileValue($arg);
1149
+ return substr($str, 1, -1);
1150
+ default:
1151
+ return $this->compileValue($arg);
1152
+ }
1153
+ }
1154
+
1155
+ function lib__sprintf($args) {
1156
+ if ($args[0] != "list") return $args;
1157
+ $values = $args[2];
1158
+ $source = $this->reduce(array_shift($values));
1159
+ if ($source[0] != "string") {
1160
+ return $source;
1161
+ }
1162
+
1163
+ $str = $source[1];
1164
+ $i = 0;
1165
+ if (preg_match_all('/%[dsa]/', $str, $m)) {
1166
+ foreach ($m[0] as $match) {
1167
+ $val = isset($values[$i]) ? $this->reduce($values[$i]) : array('keyword', '');
1168
+ $i++;
1169
+ switch ($match[1]) {
1170
+ case "s":
1171
+ if ($val[0] == "string") {
1172
+ $rep = substr($val[1], 1, -1);
1173
+ break;
1174
+ }
1175
+ default:
1176
+ $rep = $this->compileValue($val);
1177
+ }
1178
+ $str = preg_replace('/'.$this->preg_quote($match).'/', $rep, $str, 1);
1179
+ }
1180
+ }
1181
+
1182
+ return array('string', $str);
1183
+ }
1184
+
1185
+ function lib_floor($arg) {
1186
+ return floor($arg[1]);
1187
+ }
1188
+
1189
+ function lib_round($arg) {
1190
+ return round($arg[1]);
1191
  }
1192
 
1193
  // is a string surrounded in quotes? returns the quoting char if true
1197
  else return false;
1198
  }
1199
 
1200
+ /**
1201
+ * Helper function to get argurments for color functions
1202
+ * accepts invalid input, non colors interpreted to black
1203
+ */
1204
+ function colorArgs($args) {
1205
+ if ($args[0] != 'list' || count($args[2]) < 2) {
1206
+ return array(array('color', 0, 0, 0));
1207
+ }
1208
+ list($color, $delta) = $args[2];
1209
+ if ($color[0] != 'color')
1210
+ $color = array('color', 0, 0, 0);
1211
+
1212
+ $delta = floatval($delta[1]);
1213
+
1214
+ return array($color, $delta);
1215
+ }
1216
+
1217
+ function lib_darken($args) {
1218
+ list($color, $delta) = $this->colorArgs($args);
1219
+
1220
+ $hsl = $this->toHSL($color);
1221
+ $hsl[3] = $this->clamp($hsl[3] - $delta, 100);
1222
+ return $this->toRGB($hsl);
1223
+ }
1224
+
1225
+ function lib_lighten($args) {
1226
+ list($color, $delta) = $this->colorArgs($args);
1227
+
1228
+ $hsl = $this->toHSL($color);
1229
+ $hsl[3] = $this->clamp($hsl[3] + $delta, 100);
1230
+ return $this->toRGB($hsl);
1231
+ }
1232
+
1233
+ function lib_saturate($args) {
1234
+ list($color, $delta) = $this->colorArgs($args);
1235
+
1236
+ $hsl = $this->toHSL($color);
1237
+ $hsl[2] = $this->clamp($hsl[2] + $delta, 100);
1238
+ return $this->toRGB($hsl);
1239
+ }
1240
+
1241
+ function lib_desaturate($args) {
1242
+ list($color, $delta) = $this->colorArgs($args);
1243
+
1244
+ $hsl = $this->toHSL($color);
1245
+ $hsl[2] = $this->clamp($hsl[2] - $delta, 100);
1246
+ return $this->toRGB($hsl);
1247
+ }
1248
+
1249
+ function lib_spin($args) {
1250
+ list($color, $delta) = $this->colorArgs($args);
1251
+
1252
+ $hsl = $this->toHSL($color);
1253
+ $hsl[1] = $this->clamp($hsl[1] + $delta, 360);
1254
+ return $this->toRGB($hsl);
1255
+ }
1256
+
1257
+ function lib_fadeout($args) {
1258
+ list($color, $delta) = $this->colorArgs($args);
1259
+ $color[4] = $this->clamp((isset($color[4]) ? $color[4] : 1) - $delta/100);
1260
+ return $color;
1261
+ }
1262
+
1263
+ function lib_fadein($args) {
1264
+ list($color, $delta) = $this->colorArgs($args);
1265
+ $color[4] = $this->clamp((isset($color[4]) ? $color[4] : 1) + $delta/100);
1266
+ return $color;
1267
+ }
1268
+
1269
+ function lib_hue($color) {
1270
+ if ($color[0] != 'color') return 0;
1271
+ $hsl = $this->toHSL($color);
1272
+ return round($hsl[1]);
1273
+ }
1274
+
1275
+ function lib_saturation($color) {
1276
+ if ($color[0] != 'color') return 0;
1277
+ $hsl = $this->toHSL($color);
1278
+ return round($hsl[2]);
1279
+ }
1280
+
1281
+ function lib_lightness($color) {
1282
+ if ($color[0] != 'color') return 0;
1283
+ $hsl = $this->toHSL($color);
1284
+ return round($hsl[3]);
1285
+ }
1286
+
1287
+ function toHSL($color) {
1288
+ if ($color[0] == 'hsl') return $color;
1289
+
1290
+ $r = $color[1] / 255;
1291
+ $g = $color[2] / 255;
1292
+ $b = $color[3] / 255;
1293
+
1294
+ $min = min($r, $g, $b);
1295
+ $max = max($r, $g, $b);
1296
+
1297
+ $L = ($min + $max) / 2;
1298
+ if ($min == $max) {
1299
+ $S = $H = 0;
1300
+ } else {
1301
+ if ($L < 0.5)
1302
+ $S = ($max - $min)/($max + $min);
1303
+ else
1304
+ $S = ($max - $min)/(2.0 - $max - $min);
1305
+
1306
+ if ($r == $max) $H = ($g - $b)/($max - $min);
1307
+ elseif ($g == $max) $H = 2.0 + ($b - $r)/($max - $min);
1308
+ elseif ($b == $max) $H = 4.0 + ($r - $g)/($max - $min);
1309
+
1310
+ }
1311
+
1312
+ $out = array('hsl',
1313
+ ($H < 0 ? $H + 6 : $H)*60,
1314
+ $S*100,
1315
+ $L*100,
1316
+ );
1317
+
1318
+ if (count($color) > 4) $out[] = $color[4]; // copy alpha
1319
+ return $out;
1320
+ }
1321
+
1322
+ function toRGB_helper($comp, $temp1, $temp2) {
1323
+ if ($comp < 0) $comp += 1.0;
1324
+ elseif ($comp > 1) $comp -= 1.0;
1325
+
1326
+ if (6 * $comp < 1) return $temp1 + ($temp2 - $temp1) * 6 * $comp;
1327
+ if (2 * $comp < 1) return $temp2;
1328
+ if (3 * $comp < 2) return $temp1 + ($temp2 - $temp1)*((2/3) - $comp) * 6;
1329
+
1330
+ return $temp1;
1331
+ }
1332
+
1333
+ /**
1334
+ * Converts an hsl array into a color value in rgb.
1335
+ * Expects H to be in range of 0 to 360, S and L in 0 to 100
1336
+ */
1337
+ function toRGB($color) {
1338
+ if ($color == 'color') return $color;
1339
+
1340
+ $H = $color[1] / 360;
1341
+ $S = $color[2] / 100;
1342
+ $L = $color[3] / 100;
1343
+
1344
+ if ($S == 0) {
1345
+ $r = $g = $b = $L;
1346
+ } else {
1347
+ $temp2 = $L < 0.5 ?
1348
+ $L*(1.0 + $S) :
1349
+ $L + $S - $L * $S;
1350
+
1351
+ $temp1 = 2.0 * $L - $temp2;
1352
+
1353
+ $r = $this->toRGB_helper($H + 1/3, $temp1, $temp2);
1354
+ $g = $this->toRGB_helper($H, $temp1, $temp2);
1355
+ $b = $this->toRGB_helper($H - 1/3, $temp1, $temp2);
1356
+ }
1357
+
1358
+ $out = array('color', round($r*255), round($g*255), round($b*255));
1359
+ if (count($color) > 4) $out[] = $color[4]; // copy alpha
1360
+ return $out;
1361
+ }
1362
+
1363
+ function clamp($v, $max = 1, $min = 0) {
1364
+ return min($max, max($min, $v));
1365
+ }
1366
+
1367
+ /**
1368
+ * Convert the rgb, rgba, hsl color literals of function type
1369
+ * as returned by the parser into values of color type.
1370
+ */
1371
  function funcToColor($func) {
1372
  $fname = $func[1];
 
1373
  if ($func[2][0] != 'list') return false; // need a list of arguments
1374
+ $rawComponents = $func[2][2];
1375
 
1376
+ if ($fname == 'hsl' || $fname == 'hsla') {
1377
+ $hsl = array('hsl');
1378
+ $i = 0;
1379
+ foreach ($rawComponents as $c) {
1380
+ $val = $this->reduce($c);
1381
+ $val = isset($val[1]) ? floatval($val[1]) : 0;
 
 
 
 
 
1382
 
1383
+ if ($i == 0) $clamp = 360;
1384
+ elseif ($i < 4) $clamp = 100;
1385
+ else $clamp = 1;
1386
+
1387
+ $hsl[] = $this->clamp($val, $clamp);
1388
+ $i++;
1389
+ }
1390
+
1391
+ while (count($hsl) < 4) $hsl[] = 0;
1392
+ return $this->toRGB($hsl);
1393
+
1394
+ } elseif ($fname == 'rgb' || $fname == 'rgba') {
1395
+ $components = array();
1396
+ $i = 1;
1397
+ foreach ($rawComponents as $c) {
1398
+ $c = $this->reduce($c);
1399
+ if ($i < 4) {
1400
+ if ($c[0] == '%') $components[] = 255 * ($c[1] / 100);
1401
+ else $components[] = floatval($c[1]);
1402
+ } elseif ($i == 4) {
1403
+ if ($c[0] == '%') $components[] = 1.0 * ($c[1] / 100);
1404
+ else $components[] = floatval($c[1]);
1405
+ } else break;
1406
+
1407
+ $i++;
1408
+ }
1409
+ while (count($components) < 3) $components[] = 0;
1410
+ array_unshift($components, 'color');
1411
+ return $this->fixColor($components);
1412
  }
 
1413
 
1414
+ return false;
 
1415
  }
1416
 
1417
  // reduce a delayed type to its final value
1418
  // dereference variables and solve equations
1419
  function reduce($var, $defaultValue = array('number', 0)) {
 
 
1420
  while (in_array($var[0], self::$dtypes)) {
1421
+ if ($var[0] == 'list') {
1422
+ foreach ($var[2] as &$value) $value = $this->reduce($value);
1423
+ break;
1424
+ } elseif ($var[0] == 'expression') {
1425
  $var = $this->evaluate($var[1], $var[2], $var[3]);
1426
+ } elseif ($var[0] == 'variable') {
1427
+ $var = $this->get($var[1]);
1428
+ } elseif ($var[0] == 'lookup') {
1429
+ // do accessor here....
1430
+ $var = array('number', 0);
1431
+ } elseif ($var[0] == 'function') {
1432
  $color = $this->funcToColor($var);
1433
  if ($color) $var = $color;
1434
+ else {
1435
+ list($_, $name, $args) = $var;
1436
+ if ($name == "%") $name = "_sprintf";
1437
+ $f = array($this, 'lib_'.$name);
1438
+ if (is_callable($f)) {
1439
+ if ($args[0] == 'list')
1440
+ $args = $this->compressList($args[2], $args[1]);
1441
+
1442
+ $var = call_user_func($f, $this->reduce($args));
1443
+
1444
+ // convet to a typed value if the result is a php primitive
1445
+ if (is_numeric($var)) $var = array('number', $var);
1446
+ elseif (!is_array($var)) $var = array('keyword', $var);
1447
+ } else {
1448
+ // plain function, reduce args
1449
+ $var[2] = $this->reduce($var[2]);
1450
+ }
1451
+ }
1452
+ break; // done reducing after a function
1453
+ } elseif ($var[0] == 'negative') {
1454
  $value = $this->reduce($var[1]);
1455
  if (is_numeric($value[1])) {
1456
  $value[1] = -1*$value[1];
1459
  }
1460
  }
1461
 
 
1462
  return $var;
1463
  }
1464
 
1567
  else $type = $right[0];
1568
 
1569
  $value = 0;
1570
+ switch ($op) {
1571
  case '+':
1572
  $value = $left[1] + $right[1];
1573
  break;
1594
 
1595
  /* environment functions */
1596
 
1597
+ // push a new block on the stack, used for parsing
1598
+ function pushBlock($tags) {
1599
+ $b = new stdclass;
1600
+ $b->parent = $this->env;
 
1601
 
1602
+ $b->tags = $tags;
1603
+ $b->props = array();
1604
+ $b->children = array();
1605
 
1606
+ $this->env = $b;
1607
+ return $b;
1608
  }
1609
+
1610
+ // push a block that doesn't multiply tags
1611
+ function pushSpecialBlock($name) {
1612
+ $b = $this->pushBlock(array($name));
1613
+ $b->no_multiply = true;
1614
+ return $b;
1615
  }
1616
 
1617
+ // used for compiliation variable state
1618
+ function pushEnv() {
1619
+ $e = new stdclass;
1620
+ $e->parent = $this->env;
1621
+
1622
+ $this->store = array();
1623
+
1624
+ $this->env = $e;
1625
+ return $e;
1626
  }
1627
 
1628
+ // pop something off the stack
1629
  function pop() {
1630
+ $old = $this->env;
1631
+ $this->env = $this->env->parent;
1632
+ return $old;
 
 
1633
  }
1634
 
1635
  // set something in the current env
1636
  function set($name, $value) {
1637
+ $this->env->store[$name] = $value;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1638
  }
1639
 
1640
+ // append an property
1641
+ function append($prop) {
1642
+ $this->env->props[] = $prop;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1643
  }
1644
 
1645
+ // get the highest occurrence entry for a name
1646
+ function get($name) {
1647
+ $current = $this->env;
1648
+ while ($current) {
1649
+ if (isset($current->store[$name]))
1650
+ return $current->store[$name];
1651
+ else
1652
+ $current = $current->parent;
 
 
 
 
 
 
 
 
1653
  }
 
 
 
 
 
 
 
 
1654
 
1655
+ return null;
 
 
 
 
 
1656
  }
1657
+
1658
+ /* raw parsing functions */
1659
 
1660
  function literal($what, $eatWhitespace = true) {
1661
  // this is here mainly prevent notice from { } string accessor
1680
  // advance counter to next occurrence of $what
1681
  // $until - don't include $what in advance
1682
  function to($what, &$out, $until = false, $allowNewline = false) {
1683
+ $validChars = $allowNewline ? "." : "[^\n]";
1684
  if (!$this->match('('.$validChars.'*?)'.$this->preg_quote($what), $m, !$until)) return false;
1685
  if ($until) $this->count -= strlen($what); // give back $what
1686
  $out = $m[1];
1697
  return false;
1698
  }
1699
 
 
1700
  // match something without consuming it
1701
  function peek($regex, &$out = null) {
1702
  $r = '/'.$regex.'/Ais';
1712
  return true;
1713
  }
1714
 
1715
+ /**
1716
+ * Initialize state for a fresh parse
1717
+ */
1718
+ protected function prepareParser($buff) {
1719
+ $this->env = null;
1720
  $this->expandStack = array();
1721
  $this->indentLevel = 0;
 
1722
  $this->count = 0;
1723
  $this->line = 1;
 
1724
 
1725
+ $this->buffer = $this->removeComments($buff);
1726
+ $this->pushBlock(null); // set up global scope
 
1727
 
1728
  // trim whitespace on head
1729
  if (preg_match('/^\s+/', $this->buffer, $m)) {
1730
  $this->line += substr_count($m[0], "\n");
1731
  $this->buffer = ltrim($this->buffer);
1732
  }
1733
+ }
1734
 
1735
+ // create a child parser (for compiling an import)
1736
+ protected function createChild($fname) {
1737
+ $less = new lessc($fname);
1738
+ $less->importDir = $this->importDir;
1739
+ $less->indentChar = $this->indentChar;
1740
+ $less->compat = $this->compat;
1741
+ return $less;
1742
+ }
1743
+
1744
+ // parse code and return intermediate tree
1745
+ public function parseTree($str = null) {
1746
+ $this->prepareParser(is_null($str) ? $this->buffer : $str);
1747
+ while (false !== $this->parseChunk());
1748
 
1749
+ if ($this->count != strlen($this->buffer))
1750
+ $this->throwParseError();
1751
 
1752
+ if (!is_null($this->env->parent))
1753
  throw new exception('parse error: unclosed block');
1754
 
1755
+ $root = $this->env;
1756
+ $this->env = null;
1757
+ return $root;
1758
+ }
1759
+
1760
+ // inject array of unparsed strings into environment as variables
1761
+ protected function injectVariables($args) {
1762
+ $this->pushEnv();
1763
+ $parser = new lessc();
1764
+ foreach ($args as $name => $str_value) {
1765
+ if ($name{0} != '@') $name = '@'.$name;
1766
+ $parser->count = 0;
1767
+ $parser->buffer = (string)$str_value;
1768
+ if (!$parser->propertyValue($value)) {
1769
+ throw new Exception("failed to parse passed in variable $name: $str_value");
1770
+ }
1771
+
1772
+ $this->set($name, $value);
1773
+ }
1774
+ }
1775
+
1776
+ // parse and compile buffer
1777
+ function parse($str = null, $initial_variables = null) {
1778
+ $locale = setlocale(LC_NUMERIC, 0);
1779
+ setlocale(LC_NUMERIC, "C");
1780
+ $root = $this->parseTree($str);
1781
+
1782
+ if ($initial_variables) $this->injectVariables($initial_variables);
1783
+ $out = $this->compileBlock($root);
1784
+ setlocale(LC_NUMERIC, $locale);
1785
  return $out;
1786
  }
1787
 
1788
  function throwParseError($msg = 'parse error') {
1789
  $line = $this->line + substr_count(substr($this->buffer, 0, $this->count), "\n");
1790
+ if (isset($this->fileName)) {
1791
+ $loc = $this->fileName.' on line '.$line;
1792
+ } else {
1793
+ $loc = "line: ".$line;
1794
+ }
1795
+
1796
  if ($this->peek("(.*?)(\n|$)", $m))
1797
+ throw new exception($msg.': failed at `'.$m[1].'` '.$loc);
1798
  }
1799
 
1800
+ /**
1801
+ * Initialize any static state, can initialize parser for a file
1802
+ */
1803
+ function __construct($fname = null, $opts = null) {
1804
  if (!self::$operatorString) {
1805
  self::$operatorString =
1806
+ '('.implode('|', array_map(array($this, 'preg_quote'),
1807
+ array_keys(self::$precedence))).')';
1808
  }
1809
 
1810
  if ($fname) {
1833
  $done = false;
1834
  while (true) {
1835
  // find the next item
1836
+ foreach ($look as $token) {
1837
  $pos = strpos($text, $token);
1838
  if ($pos !== false) {
1839
  if (!isset($min) || $pos < $min[1]) $min = array($token, $pos);
1845
  $count = $min[1];
1846
  $skip = 0;
1847
  $newlines = 0;
1848
+ switch ($min[0]) {
1849
  case 'url(':
1850
  if (preg_match('/url\(.*?\)/', $text, $m, 0, $count))
1851
  $count += strlen($m[0]) - strlen($min[0]);
1856
  $count += strlen($m[0]) - 1;
1857
  break;
1858
  case '//':
1859
+ $skip = strpos($text, "\n", $count);
1860
+ if ($skip === false) $skip = strlen($text) - $count;
1861
+ else $skip -= $count;
1862
  break;
1863
  case '/*':
1864
  if (preg_match('/\/\*.*?\*\//s', $text, $m, 0, $count)) {
1922
  // assume no root
1923
  $root = null;
1924
 
1925
+ if (is_string($in)) {
1926
  $root = $in;
1927
+ } elseif (is_array($in) and isset($in['root'])) {
1928
+ if ($force or ! isset($in['files'])) {
1929
  // If we are forcing a recompile or if for some reason the
1930
  // structure does not contain any file information we should
1931
  // specify the root to trigger a rebuild.
1932
  $root = $in['root'];
1933
+ } elseif (isset($in['files']) and is_array($in['files'])) {
1934
+ foreach ($in['files'] as $fname => $ftime ) {
1935
+ if (!file_exists($fname) or filemtime($fname) > $ftime) {
1936
  // One of the files we knew about previously has changed
1937
  // so we should look at our incoming root again.
1938
  $root = $in['root'];
1946
  return null;
1947
  }
1948
 
1949
+ if ($root !== null) {
1950
  // If we have a root value which means we should rebuild.
1951
  $less = new lessc($root);
1952
  $out = array();
1962
  }
1963
 
1964
  }
 
1965
  }
1966
 
 
lib/vendor/lessphp/lessify ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,447 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 CHANGED
@@ -3,7 +3,7 @@
3
  //
4
  // command line utility to compile less to stdout
5
  // leaf corcoran <leafo.net>
6
- $VERSION = "v0.1.6";
7
 
8
  error_reporting(E_ALL);
9
  $path = realpath(dirname(__FILE__)).'/';
@@ -15,7 +15,7 @@ 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
  }
@@ -47,16 +47,14 @@ foreach ($argv as $loc => $a) {
47
  }
48
  }
49
 
50
- function has($o, &$loc = null)
51
- {
52
  global $opts;
53
  if (!isset($opts[$o])) return false;
54
  $loc = $opts[$o];
55
  return true;
56
  }
57
 
58
- function hasValue($o, &$value = null)
59
- {
60
  global $argv;
61
  if (!has($o,$loc)) return false;
62
  if (!isset($argv[$loc+1])) return false;
@@ -79,7 +77,7 @@ if (has("r", $loc)) {
79
 
80
  if (has("w")) {
81
  // need two files
82
- if (!is_file($in = array_shift($argv)) ||
83
  null == $out = array_shift($argv))
84
  {
85
  err($fa.$exe." -w infile outfile");
@@ -90,16 +88,16 @@ if (has("w")) {
90
  (has("n") ? ' with notifications' : '').
91
  ", press Ctrl + c to exit.\n";
92
 
93
- $fail_time = 0;
94
- // $l = new lessc($in);
95
  $cache = $in;
96
- $last_build = 0;
97
  while (1) {
 
 
98
  // check if anything has changed since last fail
99
  $updated = false;
100
  if (is_array($cache)) {
101
  foreach ($cache['files'] as $fname=>$_) {
102
- if (filemtime($fname) > $fail_time) {
103
  $updated = true;
104
  break;
105
  }
@@ -107,22 +105,22 @@ if (has("w")) {
107
  } else $updated = true;
108
 
109
  // try to compile it
110
- if ($updated) try {
111
- $cache = lessc::cexecute($cache);
112
- if ($cache['updated'] > $last_build) {
113
- $last_build = $cache['updated'];
114
- echo "Writing updated file: ".$out."\n";
 
115
  if (!file_put_contents($out, $cache['compiled'])) {
116
  err($fa."Could not write to file ".$out);
117
  exit(1);
118
  }
119
- }
120
- } catch (exception $ex) {
121
- echo "\nFatal Error:\n".str_repeat('=', 20)."\n".$ex->getMessage()."\n\n";
122
- $fail_time = time();
123
 
124
- if (has("n")) {
125
- `notify-send -u critical "compile failed" "{$ex->getMessage()}"`;
 
126
  }
127
  }
128
 
@@ -136,9 +134,47 @@ if (!$fname = array_shift($argv)) {
136
  exit(1);
137
  }
138
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
  try {
140
  $l = new lessc($fname);
141
- $out = $l->parse();
 
 
 
 
 
 
 
 
142
 
143
  if (!$fout = array_shift($argv)) {
144
  echo $out;
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__)).'/';
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
  }
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;
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");
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
  }
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
 
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;
lib/vendor/lessphp/tests/README CHANGED
@@ -2,7 +2,7 @@ test.php
2
  ========================================
3
 
4
  to run:
5
- php test.php
6
 
7
 
8
  Runs through all files in `inputs`, compiles them, then
@@ -18,4 +18,6 @@ the appropriate file. This will overwrite any existing
18
  outputs. Use this when you want to save verified test
19
  results.
20
 
 
 
21
 
2
  ========================================
3
 
4
  to run:
5
+ php test.php [test-name-glob]
6
 
7
 
8
  Runs through all files in `inputs`, compiles them, then
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 → accessors.less.disable} RENAMED
File without changes
lib/vendor/lessphp/tests/inputs/builtins.less ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ }
lib/vendor/lessphp/tests/inputs/compile_on_mixin.less ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 CHANGED
@@ -6,17 +6,17 @@
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
 
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
 
lib/vendor/lessphp/tests/inputs/hacks.less ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ // css hacks
2
+
3
+ :root .alert-message, :root .btn {
4
+ border-radius: 0 \0;
5
+ }
6
+
lib/vendor/lessphp/tests/inputs/import.less ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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/math.less ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 CHANGED
@@ -19,3 +19,9 @@
19
  body { line-height: 1.2 }
20
  }
21
 
 
 
 
 
 
 
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 CHANGED
@@ -5,7 +5,7 @@
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 {
@@ -13,3 +13,26 @@
13
  from(#E9A000), to(#A37000));
14
  }
15
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 {
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
+
lib/vendor/lessphp/tests/inputs/mixin_functions.less CHANGED
@@ -1,6 +1,6 @@
1
 
2
  @outer: 10px;
3
- @class(@var:22px; @car: 400px + @outer) {
4
  margin: @var;
5
  height: @car;
6
  }
@@ -21,19 +21,19 @@
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
 
1
 
2
  @outer: 10px;
3
+ @class(@var:22px, @car: 400px + @outer) {
4
  margin: @var;
5
  height: @car;
6
  }
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 ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 CHANGED
@@ -20,7 +20,7 @@ body #window {
20
  display: block;
21
  border: 1px solid black;
22
  background-color: grey;
23
- :hover { background-color: white }
24
  }
25
  }
26
  #header a {
@@ -48,4 +48,21 @@ body {
48
  div;
49
  }
50
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
 
20
  display: block;
21
  border: 1px solid black;
22
  background-color: grey;
23
+ &:hover { background-color: white }
24
  }
25
  }
26
  #header a {
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
 
lib/vendor/lessphp/tests/inputs/nested.less CHANGED
@@ -11,7 +11,7 @@
11
  }
12
  .logo {
13
  width: 300px;
14
- :hover { text-decoration: none }
15
  }
16
  }
17
 
@@ -19,3 +19,42 @@ a { b { ul { li { color: green; } } } }
19
 
20
  this { will { not { show { } } } }
21
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  }
12
  .logo {
13
  width: 300px;
14
+ &:hover { text-decoration: none }
15
  }
16
  }
17
 
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 ADDED
File without changes
lib/vendor/lessphp/tests/inputs/scopes.less ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+
2
+ b {
3
+ color: @color;
4
+ padding: 16px;
5
+ }
6
+
lib/vendor/lessphp/tests/inputs/variables.less CHANGED
@@ -35,30 +35,4 @@
35
  padding: @nonexistant + 4px;
36
  }
37
 
38
- // some math tests & considerations
39
- division: 10px / 2;
40
- division: 10px/ 2;
41
- //division: 10px /2; // doesn't work, probably should support this
42
-
43
- // consideratins for subtraction:
44
- // something-something is a single keyword
45
- // @something-@something should be subtraction? eg.
46
- @test-: 10;
47
- @west: "hello";
48
- subtract: @test-@west; // this will compile but the output will be: 10 "hello"
49
-
50
- subtract: 2-2; // this will give you: 2 -2, similar to above
51
-
52
- // addition: 5+5; // doesn't work
53
- // addition: 5 +5; // doesn't work, similar to broken division above
54
- addition: 5+ 5;
55
- addition: 5 + 5;
56
-
57
-
58
- // conclusion:
59
- // probably best to enforce space around both sides of every operator
60
- // (except when touching a paren)
61
- math: (5 + 5)*(2 / 1); // this already works
62
-
63
-
64
 
35
  padding: @nonexistant + 4px;
36
  }
37
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
 
lib/vendor/lessphp/tests/outputs/accessors.css CHANGED
@@ -8,5 +8,7 @@
8
  height:960px;
9
  background-color:#294366;
10
  color:green;
 
 
11
  }
12
  .mix { font-size:10px; }
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/builtins.css ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ }
lib/vendor/lessphp/tests/outputs/compile_on_mixin.css ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
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/hacks.css ADDED
@@ -0,0 +1 @@
 
1
+ :root .alert-message, :root .btn { border-radius:0 \0; }
lib/vendor/lessphp/tests/outputs/import.css ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 CHANGED
@@ -15,7 +15,9 @@
15
  top:75px;
16
  animation-timing-function:ease-in;
17
  }
18
- to { top:100px; }
 
 
19
  }
20
  div {
21
  animation-name:'diagonal-slide';
15
  top:75px;
16
  animation-timing-function:ease-in;
17
  }
18
+ to {
19
+ top:100px;
20
+ }
21
  }
22
  div {
23
  animation-name:'diagonal-slide';
lib/vendor/lessphp/tests/outputs/math.css ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 CHANGED
@@ -15,4 +15,8 @@
15
  }
16
  @media all and (min-width: 0) {
17
  body { line-height:1.2; }
 
 
 
 
18
  }
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 CHANGED
@@ -1,4 +1,14 @@
1
  @charset "utf-8";
2
  .topbar { background:url(/assets/images/test/topbar.png); }
3
  .hello { test:empty-function("/assets/images/test/",40%,to(#ffffff)); }
4
- .css3 { background-image:-webkit-gradient(linear,0% 0%,0% 90%,from(#e9a000),to(#a37000)); }
 
 
 
 
 
 
 
 
 
 
1
  @charset "utf-8";
2
  .topbar { background:url(/assets/images/test/topbar.png); }
3
  .hello { test:empty-function("/assets/images/test/",40%,to(#ffffff)); }
4
+ .css3 { background-image:-webkit-gradient(linear,0% 0%,0% 90%,from(#e9a000),to(#a37000)); }
5
+ .test, .world {
6
+ border:1px solid red;
7
+ color:url(http://mage-page.com);
8
+ string:"hello /* this is not a comment */";
9
+ world:"// neither is this";
10
+ string:'hello /* this is not a comment */';
11
+ world:'// neither is this';
12
+ what-ever:100px;
13
+ background:url(/*no comment here*/);
14
+ }
lib/vendor/lessphp/tests/outputs/mixin_functions.css CHANGED
@@ -1,4 +1,3 @@
1
- .class { padding:200px; }
2
  body {
3
  padding:2.0em;
4
  color:red;
@@ -8,8 +7,8 @@ body {
8
  }
9
  .skip_args {
10
  margin:22px;
11
- margin:4px 12px 2px 1px;
12
  height:12px;
13
  padding:10px 20px 30px 88px;
 
14
  color:red;
15
  }
 
1
  body {
2
  padding:2.0em;
3
  color:red;
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 ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 CHANGED
@@ -8,26 +8,28 @@ body #window {
8
  font-weight:bold;
9
  line-height:30px;
10
  }
11
- #bundle .button:hover { background-color:white; }
12
  #bundle .button {
13
  display:block;
14
  border:1px solid black;
15
  background-color:grey;
16
  }
17
- #header a:hover { background-color:white; }
18
  #header a {
19
  color:orange;
20
  display:block;
21
  border:1px solid black;
22
  background-color:grey;
23
  }
24
- div b { color:blue; }
25
  div {
26
  color:blue;
27
  hello:world;
28
  }
29
- body b { color:blue; }
30
  body {
31
  color:blue;
32
  hello:world;
33
- }
 
 
 
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; }
lib/vendor/lessphp/tests/outputs/nested.css CHANGED
@@ -1,6 +1,16 @@
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; }
 
 
 
 
 
 
 
 
 
 
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/scopes.css ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 CHANGED
@@ -12,14 +12,7 @@
12
  }
13
  .external {
14
  color:#888888;
15
- border:1px solid rgb(20%,15%,80%);
16
  background:rgba(23,68,149,0.5);
17
  padding:4px;
18
- }
19
- division:5px;
20
- division:5px;
21
- subtract:10 "hello";
22
- subtract:2 -2;
23
- addition:10;
24
- addition:10;
25
- math:20;
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 CHANGED
@@ -6,10 +6,10 @@ error_reporting(E_ALL);
6
  * and compile them, then compare them to paired file in
7
  * output directory.
8
  */
9
- $difftool = 'diff';
10
  $input = array(
11
  'dir' => 'inputs',
12
- 'match' => '(.*?)\.less',
13
  );
14
 
15
  $output = array(
@@ -17,8 +17,12 @@ $output = array(
17
  'filename' => '%s.css',
18
  );
19
 
20
- require realpath(dirname(__FILE__)).'/../lessc.inc.php';
 
 
 
21
  $compiler = new lessc();
 
22
 
23
  $fa = 'Fatal Error: ';
24
  if (php_sapi_name() != 'cli') {
@@ -43,16 +47,28 @@ if (flag('h', 'help')) {
43
  exit('help me');
44
  }
45
 
 
 
46
  if (!is_dir($input['dir']) || !is_dir($output['dir']))
47
  exit($fa." both input and output directories must exist\n");
48
 
 
 
 
 
 
 
 
 
 
49
  $tests = array();
50
- // todo: use glob so user can select tests by name
51
- foreach (scandir($input['dir']) as $fname) {
52
- if (preg_match('/^'.$input['match'].'$/', $fname, $match)) {
 
53
  $tests[] = array(
54
- 'in' => $input['dir'].'/'.$fname,
55
- 'out' => $output['dir'].'/'.sprintf($output['filename'], $match[1]),
56
  );
57
  }
58
  }
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(
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') {
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
  }
patch/protected-buffer.patch DELETED
@@ -1,13 +0,0 @@
1
- Index: wp-less/lib/vendor/lessphp/lessc.inc.php
2
- ===================================================================
3
- --- wp-less/lib/vendor/lessphp/lessc.inc.php (revision 317994)
4
- +++ wp-less/lib/vendor/lessphp/lessc.inc.php (working copy)
5
- @@ -15,7 +15,7 @@
6
- //
7
-
8
- class lessc {
9
- - private $buffer;
10
- + protected $buffer;
11
- private $count;
12
- private $line;
13
- private $expandStack;
 
 
 
 
 
 
 
 
 
 
 
 
 
readme.txt CHANGED
@@ -3,8 +3,8 @@ 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.0.2
7
- Stable tag: 1.3.1
8
 
9
  Implementation of LESS (Leaner CSS) in order to make themes development easier.
10
 
@@ -30,7 +30,7 @@ Seriously.
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.2.0](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
 
@@ -48,6 +48,15 @@ The sole requirement is to use WordPress API and LESS convention: the `.less` ex
48
 
49
  == Changelog ==
50
 
 
 
 
 
 
 
 
 
 
51
  = Version 1.3.1 =
52
 
53
  * renamed `wp-less_compiler_parse` action to `wp-less_compiler_parse_pre` to avoid name conflicts
@@ -99,10 +108,10 @@ The sole requirement is to use WordPress API and LESS convention: the `.less` ex
99
  == Frequently Asked Questions ==
100
  = How do I transform a LESS file into CSS? =
101
  Consider this bit of code to automatically enqueue your stylesheet from your theme (or plugin):
102
- `wp_enqueue_style('mytheme', get_bloginfo('template_directory').'/style.css', array('blueprint'), '', 'screen, projection');`
103
 
104
  To make it process by WP-LESS, switch to this way:
105
- `wp_enqueue_style('mytheme', get_bloginfo('template_directory').'/style.less', array('blueprint'), '', 'screen, projection');`
106
 
107
  You understood well: you just need to change the extension of the file.
108
 
@@ -120,6 +129,28 @@ Everything is prepared and documented inside, with examples and hint.
120
 
121
  Just help yourself!
122
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
  == Screenshots ==
124
 
125
  1. Sample of LESS to CSS conversion.
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
8
 
9
  Implementation of LESS (Leaner CSS) in order to make themes development easier.
10
 
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.0](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
 
48
 
49
  == Changelog ==
50
 
51
+ = Version 1.4 =
52
+
53
+ * action: `wp-less_compiler_parse_pre` now takes 3 arguments: class instance, text and variable arguments
54
+ * action: `wp-less_stylesheet_save_pre` now takes 2 arguments: class instance and variable arguments
55
+ * helper: added `less_add_variable` to ease manipulations from theme, if needed (the file needs to be included manually)
56
+ * stylesheet: `getBuffer()` and `setBuffer` will be removed in 1.5 version
57
+ * lessphp: removed the custom patch for buffer manipulation, due to built-in variable management
58
+ * lessphp: updated to version 0.3.0
59
+
60
  = Version 1.3.1 =
61
 
62
  * renamed `wp-less_compiler_parse` action to `wp-less_compiler_parse_pre` to avoid name conflicts
108
  == Frequently Asked Questions ==
109
  = How do I transform a LESS file into CSS? =
110
  Consider this bit of code to automatically enqueue your stylesheet from your theme (or plugin):
111
+ `wp_enqueue_style('mytheme', get_bloginfo('template_directory').'/style.css', array(), '', 'screen, projection');`
112
 
113
  To make it process by WP-LESS, switch to this way:
114
+ `wp_enqueue_style('mytheme', get_bloginfo('template_directory').'/style.less', array(), '', 'screen, projection');`
115
 
116
  You understood well: you just need to change the extension of the file.
117
 
129
 
130
  Just help yourself!
131
 
132
+ = I want to inject custom variables =
133
+ LESS PHP 0.3.0 introduced a native way to set variables from PHP.
134
+
135
+ If you initialized the class by yourself, do it this way:
136
+ `$WPLessPlugin->addVariable('@default_color', '#fff');`
137
+
138
+ If you don't manage the plugin by yourself:
139
+ `WPPluginToolkitPlugin::getInstance('WPLess')->addVariable($name, $value);`
140
+
141
+ And if you want to do that from a theme, with less code:
142
+
143
+ 1. include the `wp-less/lib/helper/ThemeHelper.php` file;
144
+ 1. call `less_add_variable('@default_color', '#fff')`
145
+
146
+ == Upgrade Notice ==
147
+
148
+ = 1.4 =
149
+
150
+ As `lessphp` has been upgraded to `0.3.0`, its behavior changed a little bit.
151
+
152
+ Please check your LESS syntax [according to the document](http://leafo.net/lessphp/docs/) before applying this update.
153
+
154
  == Screenshots ==
155
 
156
  1. Sample of LESS to CSS conversion.