WP-LESS - Version 1.3

Version Description

Download this release

Release Info

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

Code changes from version 1.2 to 1.3

Files changed (36) hide show
  1. bootstrap-for-theme.php +1 -1
  2. bootstrap.php +2 -2
  3. lib/Compiler.class.php +37 -5
  4. lib/Configuration.class.php +1 -1
  5. lib/Plugin.class.php +28 -16
  6. lib/Stylesheet.class.php +15 -34
  7. lib/vendor/lessphp/{README → README.md} +0 -0
  8. lib/vendor/lessphp/docs/docs.html +6 -7
  9. lib/vendor/lessphp/lessc.inc.php +278 -61
  10. lib/vendor/lessphp/plessc +26 -12
  11. lib/vendor/lessphp/tests/README +21 -0
  12. lib/vendor/lessphp/tests/inputs/accessors.less +36 -0
  13. lib/vendor/lessphp/tests/inputs/attributes.less +41 -0
  14. lib/vendor/lessphp/tests/inputs/font_family.less +28 -0
  15. lib/vendor/lessphp/tests/inputs/keyframes.less +48 -0
  16. lib/vendor/lessphp/tests/inputs/media.less +21 -0
  17. lib/vendor/lessphp/tests/inputs/misc.less +15 -0
  18. lib/vendor/lessphp/tests/inputs/mixin_functions.less +39 -0
  19. lib/vendor/lessphp/tests/inputs/mixins.less +51 -0
  20. lib/vendor/lessphp/tests/inputs/nested.less +21 -0
  21. lib/vendor/lessphp/tests/inputs/variables.less +64 -0
  22. lib/vendor/lessphp/tests/outputs/accessors.css +12 -0
  23. lib/vendor/lessphp/tests/outputs/attributes.css +35 -0
  24. lib/vendor/lessphp/tests/outputs/font_family.css +17 -0
  25. lib/vendor/lessphp/tests/outputs/keyframes.css +34 -0
  26. lib/vendor/lessphp/tests/outputs/media.css +18 -0
  27. lib/vendor/lessphp/tests/outputs/misc.css +4 -0
  28. lib/vendor/lessphp/tests/outputs/mixin_functions.css +15 -0
  29. lib/vendor/lessphp/tests/outputs/mixins.css +33 -0
  30. lib/vendor/lessphp/tests/outputs/nested.css +6 -0
  31. lib/vendor/lessphp/tests/outputs/nesting.css +6 -0
  32. lib/vendor/lessphp/tests/outputs/variables.css +25 -0
  33. lib/vendor/lessphp/tests/test.php +121 -0
  34. lib/vendor/plugin-toolkit/BaseConfiguration.class.php +94 -30
  35. lib/vendor/plugin-toolkit/BasePlugin.class.php +28 -12
  36. readme.txt +16 -1
bootstrap-for-theme.php CHANGED
@@ -17,7 +17,7 @@
17
  * And to apply automatic building on page display:
18
  * `add_action('wp_print_styles', array($WPLessPlugin, 'processStylesheets'));`
19
  * Or apply all hooks with:
20
- * `$WPLessPlugin->registerHooks();`
21
  *
22
  * You can rebuild all stylesheets at any time with:
23
  * `$WPLessPlugin->processStylesheets();`
17
  * And to apply automatic building on page display:
18
  * `add_action('wp_print_styles', array($WPLessPlugin, 'processStylesheets'));`
19
  * Or apply all hooks with:
20
+ * `$WPLessPlugin->dispatch();`
21
  *
22
  * You can rebuild all stylesheets at any time with:
23
  * `$WPLessPlugin->processStylesheets();`
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.2
7
  Author URI: http://case.oncle-tom.net/
8
  Plugin URI: http://wordpress.org/extend/plugins/wp-less/
9
 
@@ -14,4 +14,4 @@ Plugin URI: http://wordpress.org/extend/plugins/wp-less/
14
  require dirname(__FILE__).'/lib/Plugin.class.php';
15
  $WPLessPlugin = WPPluginToolkitPlugin::create('WPLess', __FILE__);
16
 
17
- $WPLessPlugin->registerHooks();
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
7
  Author URI: http://case.oncle-tom.net/
8
  Plugin URI: http://wordpress.org/extend/plugins/wp-less/
9
 
14
  require dirname(__FILE__).'/lib/Plugin.class.php';
15
  $WPLessPlugin = WPPluginToolkitPlugin::create('WPLess', __FILE__);
16
 
17
+ $WPLessPlugin->dispatch();
lib/Compiler.class.php CHANGED
@@ -7,24 +7,56 @@
7
  * @package wp-less
8
  * @subpackage lib
9
  * @since 1.2
10
- * @version 1.0
11
  */
12
  class WPLessCompiler extends lessc
13
  {
 
 
 
 
 
 
 
 
 
 
 
 
14
  /**
15
  * Parse a LESS file
16
  *
17
  * @see lessc::parse
18
- * @author oncletom
19
  * @throws Exception
20
  * @param string $text [optional] Custom CSS to parse
21
  * @return string CSS output
22
  */
23
  public function parse($text = null)
24
  {
25
- $output = parent::parse($text);
26
- $output = apply_filters('wp-less_compiler_parse', $output);
 
 
 
 
 
 
 
 
 
 
 
 
27
 
28
- return $output;
 
 
 
 
 
 
 
 
 
29
  }
30
  }
7
  * @package wp-less
8
  * @subpackage lib
9
  * @since 1.2
10
+ * @version 1.1
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
+ */
20
+ public function __construct($file = null)
21
+ {
22
+ do_action('wp-less_compiler_construct', $this, $file);
23
+ parent::__construct(apply_filters('wp-less_compiler_construct', $file));
24
+ }
25
+
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', $this);
37
+ return apply_filters('wp-less_compiler_parse', parent::parse($text));
38
+ }
39
+
40
+ /**
41
+ * Returns the LESS buffer
42
+ *
43
+ * @since 1.1
44
+ * @return string current buffer
45
+ */
46
+ public function getBuffer()
47
+ {
48
+ return $this->buffer;
49
+ }
50
 
51
+ /**
52
+ * Enables to overload the current LESS buffer
53
+ * Use at your own risks.
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
+ {
60
+ $this->buffer = $css;
61
  }
62
  }
lib/Configuration.class.php CHANGED
@@ -10,7 +10,7 @@ class WPLessConfiguration extends WPPluginToolkitConfiguration
10
  /**
11
  * Refers to the version of the plugin
12
  */
13
- const VERSION = '1.2';
14
 
15
 
16
  protected function configure()
10
  /**
11
  * Refers to the version of the plugin
12
  */
13
+ const VERSION = '1.3';
14
 
15
 
16
  protected function configure()
lib/Plugin.class.php CHANGED
@@ -1,12 +1,12 @@
1
  <?php
2
- if (!class_exists('BasePlugin'))
3
  {
4
  require dirname(__FILE__).'/vendor/plugin-toolkit/BasePlugin.class.php';
5
  }
6
 
7
  /**
8
  * WP LESS Plugin class
9
- *
10
  * @author oncletom
11
  * @package wp-less
12
  * @subpackage lib
@@ -22,11 +22,22 @@ class WPLessPlugin extends WPPluginToolkitPlugin
22
  */
23
  public static $match_pattern = '/\.less$/U';
24
 
 
 
 
 
 
 
 
 
 
 
 
25
  /**
26
  * Correct Stylesheet URI
27
- *
28
  * It enables the cache without loosing reference to URI
29
- *
30
  * @author oncletom
31
  * @since 1.2
32
  * @version 1.0
@@ -45,7 +56,7 @@ class WPLessPlugin extends WPPluginToolkitPlugin
45
 
46
  /**
47
  * Find any style to process
48
- *
49
  * @author oncletom
50
  * @since 1.0
51
  * @version 1.0
@@ -56,7 +67,7 @@ class WPLessPlugin extends WPPluginToolkitPlugin
56
  $wp_styles = $this->getStyles();
57
  $to_process = array();
58
 
59
- foreach ($wp_styles->queue as $style_id)
60
  {
61
  if (preg_match(self::$match_pattern, $wp_styles->registered[$style_id]->src))
62
  {
@@ -69,7 +80,7 @@ class WPLessPlugin extends WPPluginToolkitPlugin
69
 
70
  /**
71
  * Returns WordPress Styles manager
72
- *
73
  * @author oncletom
74
  * @uses WP_Styles
75
  * @since 1.0
@@ -84,7 +95,7 @@ class WPLessPlugin extends WPPluginToolkitPlugin
84
 
85
  /**
86
  * Process a single stylesheet
87
- *
88
  * @author oncletom
89
  * @since 1.1
90
  * @version 1.1
@@ -97,7 +108,7 @@ class WPLessPlugin extends WPPluginToolkitPlugin
97
  $wp_styles = $this->getStyles();
98
  $stylesheet = new WPLessStylesheet($wp_styles->registered[$handle]);
99
 
100
- if ($force || $stylesheet->hasToCompile())
101
  {
102
  $stylesheet->save();
103
  }
@@ -109,7 +120,7 @@ class WPLessPlugin extends WPPluginToolkitPlugin
109
 
110
  /**
111
  * Process all stylesheets to compile just in time
112
- *
113
  * @author oncletom
114
  * @since 1.0
115
  * @version 1.1
@@ -119,7 +130,8 @@ class WPLessPlugin extends WPPluginToolkitPlugin
119
  {
120
  $styles = $this->getQueuedStylesToProcess();
121
  $wp_styles = $this->getStyles();
122
-
 
123
  WPLessStylesheet::$upload_dir = $this->configuration->getUploadDir();
124
  WPLessStylesheet::$upload_uri = $this->configuration->getUploadUrl();
125
 
@@ -133,7 +145,6 @@ class WPLessPlugin extends WPPluginToolkitPlugin
133
  throw new WPLessException(sprintf('The upload dir folder (`%s`) is not writable from %s.', WPLessStylesheet::$upload_dir, get_class($this)));
134
  }
135
 
136
-
137
  foreach ($styles as $style_id)
138
  {
139
  $this->processStylesheet($style_id, $force);
@@ -144,12 +155,13 @@ class WPLessPlugin extends WPPluginToolkitPlugin
144
 
145
  /**
146
  * Method to register hooks (and do it only once)
147
- *
 
148
  * @author oncletom
149
  * @since 1.1
150
  * @version 1.1
151
  */
152
- public function registerHooks()
153
  {
154
  if ($this->is_hooks_registered)
155
  {
@@ -159,8 +171,8 @@ class WPLessPlugin extends WPPluginToolkitPlugin
159
  if (!is_admin())
160
  {
161
  do_action('wp-less_init', $this);
162
- add_action('wp_print_styles', array($this, 'processStylesheets'));
163
- add_filter('wp-less_stylesheet_parse', array($this, 'filterStylesheetUri'), 10, 2);
164
  }
165
  else
166
  {
1
  <?php
2
+ if (!class_exists('WPPluginToolkitPlugin'))
3
  {
4
  require dirname(__FILE__).'/vendor/plugin-toolkit/BasePlugin.class.php';
5
  }
6
 
7
  /**
8
  * WP LESS Plugin class
9
+ *
10
  * @author oncletom
11
  * @package wp-less
12
  * @subpackage lib
22
  */
23
  public static $match_pattern = '/\.less$/U';
24
 
25
+ /**
26
+ * Dispatches all events of the plugin
27
+ *
28
+ * @author oncletom
29
+ * @since 1.3
30
+ */
31
+ public function dispatch()
32
+ {
33
+ $this->registerHooks();
34
+ }
35
+
36
  /**
37
  * Correct Stylesheet URI
38
+ *
39
  * It enables the cache without loosing reference to URI
40
+ *
41
  * @author oncletom
42
  * @since 1.2
43
  * @version 1.0
56
 
57
  /**
58
  * Find any style to process
59
+ *
60
  * @author oncletom
61
  * @since 1.0
62
  * @version 1.0
67
  $wp_styles = $this->getStyles();
68
  $to_process = array();
69
 
70
+ foreach ((array)$wp_styles->queue as $style_id)
71
  {
72
  if (preg_match(self::$match_pattern, $wp_styles->registered[$style_id]->src))
73
  {
80
 
81
  /**
82
  * Returns WordPress Styles manager
83
+ *
84
  * @author oncletom
85
  * @uses WP_Styles
86
  * @since 1.0
95
 
96
  /**
97
  * Process a single stylesheet
98
+ *
99
  * @author oncletom
100
  * @since 1.1
101
  * @version 1.1
108
  $wp_styles = $this->getStyles();
109
  $stylesheet = new WPLessStylesheet($wp_styles->registered[$handle]);
110
 
111
+ if ((is_bool($force) && $force) || $stylesheet->hasToCompile())
112
  {
113
  $stylesheet->save();
114
  }
120
 
121
  /**
122
  * Process all stylesheets to compile just in time
123
+ *
124
  * @author oncletom
125
  * @since 1.0
126
  * @version 1.1
130
  {
131
  $styles = $this->getQueuedStylesToProcess();
132
  $wp_styles = $this->getStyles();
133
+ $force = is_bool($force) && $force ? !!$force : false;
134
+
135
  WPLessStylesheet::$upload_dir = $this->configuration->getUploadDir();
136
  WPLessStylesheet::$upload_uri = $this->configuration->getUploadUrl();
137
 
145
  throw new WPLessException(sprintf('The upload dir folder (`%s`) is not writable from %s.', WPLessStylesheet::$upload_dir, get_class($this)));
146
  }
147
 
 
148
  foreach ($styles as $style_id)
149
  {
150
  $this->processStylesheet($style_id, $force);
155
 
156
  /**
157
  * Method to register hooks (and do it only once)
158
+ *
159
+ * @protected
160
  * @author oncletom
161
  * @since 1.1
162
  * @version 1.1
163
  */
164
+ protected function registerHooks()
165
  {
166
  if ($this->is_hooks_registered)
167
  {
171
  if (!is_admin())
172
  {
173
  do_action('wp-less_init', $this);
174
+ add_action('wp', array($this, 'processStylesheets'), 999, 0);
175
+ add_filter('wp-less_stylesheet_save', array($this, 'filterStylesheetUri'), 10, 2);
176
  }
177
  else
178
  {
lib/Stylesheet.class.php CHANGED
@@ -3,7 +3,7 @@ require dirname(__FILE__).'/vendor/lessphp/lessc.inc.php';
3
 
4
  /**
5
  * Stylesheet management
6
- *
7
  * @author oncletom
8
  * @package wp-less
9
  * @subpackage lib
@@ -25,7 +25,7 @@ class WPLessStylesheet
25
 
26
  /**
27
  * Constructs the object, paths and all
28
- *
29
  * @author oncletom
30
  * @since 1.0
31
  * @version 1.0
@@ -49,7 +49,7 @@ class WPLessStylesheet
49
 
50
  /**
51
  * Returns the computed path for a given dependency
52
- *
53
  * @author oncletom
54
  * @since 1.0
55
  * @version 1.0
@@ -68,7 +68,7 @@ class WPLessStylesheet
68
  /**
69
  * Configure paths for the stylesheet
70
  * Since this moment, everything is configured to be usable
71
- *
72
  * @protected
73
  * @author oncletom
74
  * @since 1.0
@@ -86,10 +86,10 @@ class WPLessStylesheet
86
 
87
  /**
88
  * Configures version and timestamp
89
- *
90
  * It can be run only after paths have been configured. Otherwise (or if the calculation went wrong),
91
  * an exception will be thrown.
92
- *
93
  * @author oncletom
94
  * @since 1.2
95
  * @version 1.0
@@ -118,7 +118,7 @@ class WPLessStylesheet
118
 
119
  /**
120
  * Returns source content (CSS to parse)
121
- *
122
  * @author oncletom
123
  * @since 1.0
124
  * @version 1.0
@@ -131,7 +131,7 @@ class WPLessStylesheet
131
 
132
  /**
133
  * Returns source path
134
- *
135
  * @author oncletom
136
  * @since 1.0
137
  * @version 1.0
@@ -144,7 +144,7 @@ class WPLessStylesheet
144
 
145
  /**
146
  * Returns source URI
147
- *
148
  * @author oncletom
149
  * @since 1.0
150
  * @version 1.0
@@ -155,27 +155,9 @@ class WPLessStylesheet
155
  return $this->source_uri;
156
  }
157
 
158
- /**
159
- * Returns parsed CSS
160
- *
161
- * @author oncletom
162
- * @since 1.0
163
- * @version 1.0
164
- * @return string
165
- */
166
- public function getTargetContent()
167
- {
168
- if (!$this->compiler)
169
- {
170
- $this->compiler = new lessc($this->getSourcePath());
171
- }
172
-
173
- return apply_filters('wp-less_stylesheet_target_content', $this->compiler->parse());
174
- }
175
-
176
  /**
177
  * Returns target path
178
- *
179
  * @author oncletom
180
  * @since 1.0
181
  * @version 1.0
@@ -188,7 +170,7 @@ class WPLessStylesheet
188
 
189
  /**
190
  * Returns target URI
191
- *
192
  * @author oncletom
193
  * @since 1.0
194
  * @version 1.1
@@ -203,7 +185,7 @@ class WPLessStylesheet
203
 
204
  /**
205
  * Tells if compilation is needed
206
- *
207
  * @author oncletom
208
  * @since 1.0
209
  * @version 1.0
@@ -216,10 +198,10 @@ class WPLessStylesheet
216
 
217
  /**
218
  * Save the current stylesheet as a parsed css file
219
- *
220
  * @author oncletom
221
  * @since 1.0
222
- * @version 1.1
223
  * @throws Exception in case of parsing went bad
224
  */
225
  public function save()
@@ -231,8 +213,7 @@ class WPLessStylesheet
231
  do_action('wp-less_stylesheet_save_pre', $this);
232
  $compiler = new WPLessCompiler($this->getSourcePath());
233
 
234
- $output = apply_filters('wp-less_stylesheet_parse', $compiler->parse(), $this);
235
- file_put_contents($this->getTargetPath(), $output);
236
  chmod($this->getTargetPath(), 0666);
237
 
238
  $this->is_new = false;
3
 
4
  /**
5
  * Stylesheet management
6
+ *
7
  * @author oncletom
8
  * @package wp-less
9
  * @subpackage lib
25
 
26
  /**
27
  * Constructs the object, paths and all
28
+ *
29
  * @author oncletom
30
  * @since 1.0
31
  * @version 1.0
49
 
50
  /**
51
  * Returns the computed path for a given dependency
52
+ *
53
  * @author oncletom
54
  * @since 1.0
55
  * @version 1.0
68
  /**
69
  * Configure paths for the stylesheet
70
  * Since this moment, everything is configured to be usable
71
+ *
72
  * @protected
73
  * @author oncletom
74
  * @since 1.0
86
 
87
  /**
88
  * Configures version and timestamp
89
+ *
90
  * It can be run only after paths have been configured. Otherwise (or if the calculation went wrong),
91
  * an exception will be thrown.
92
+ *
93
  * @author oncletom
94
  * @since 1.2
95
  * @version 1.0
118
 
119
  /**
120
  * Returns source content (CSS to parse)
121
+ *
122
  * @author oncletom
123
  * @since 1.0
124
  * @version 1.0
131
 
132
  /**
133
  * Returns source path
134
+ *
135
  * @author oncletom
136
  * @since 1.0
137
  * @version 1.0
144
 
145
  /**
146
  * Returns source URI
147
+ *
148
  * @author oncletom
149
  * @since 1.0
150
  * @version 1.0
155
  return $this->source_uri;
156
  }
157
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
158
  /**
159
  * Returns target path
160
+ *
161
  * @author oncletom
162
  * @since 1.0
163
  * @version 1.0
170
 
171
  /**
172
  * Returns target URI
173
+ *
174
  * @author oncletom
175
  * @since 1.0
176
  * @version 1.1
185
 
186
  /**
187
  * Tells if compilation is needed
188
+ *
189
  * @author oncletom
190
  * @since 1.0
191
  * @version 1.0
198
 
199
  /**
200
  * Save the current stylesheet as a parsed css file
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()
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;
lib/vendor/lessphp/{README → README.md} RENAMED
File without changes
lib/vendor/lessphp/docs/docs.html CHANGED
@@ -182,27 +182,26 @@ div {
182
  }</pre>
183
 
184
  <p>It is possible to give an abstract block the same name as an abstract property; their names will not collide.
185
- Block names and property names exist in different spaces.</p>
186
 
 
 
187
 
188
  <a name="args"></a>
189
  <h2>Mixin Arguments</h2>
190
  <p>All blocks have the option of taking argument lists, and the arguments can have default values.</p>
191
 
192
- <pre class="code">.myclass(@width: 200px;@radius) {
193
  border-radius: @radius;
194
  width: @width
195
  }
196
 
197
- @color(@color:red) { color: @color; } // this is valid
198
-
199
  .first {
200
- .myclass(300px; 2em);
201
- @color(blue):
202
  }
203
 
204
  .second {
205
- .myclass(;4px); // blank argument takes default value
206
  }</pre>
207
 
208
  <a name="import"></a>
182
  }</pre>
183
 
184
  <p>It is possible to give an abstract block the same name as an abstract property; their names will not collide.
185
+ Block names and property names exist in different spaces. For example:</p>
186
 
187
+ <pre class="code">@color(@color:red) { color: @color; } // this is valid
188
+ </pre>
189
 
190
  <a name="args"></a>
191
  <h2>Mixin Arguments</h2>
192
  <p>All blocks have the option of taking argument lists, and the arguments can have default values.</p>
193
 
194
+ <pre class="code">@some_mixin(@width: 200px;@radius) {
195
  border-radius: @radius;
196
  width: @width
197
  }
198
 
 
 
199
  .first {
200
+ @some_mixin(300px; 2em);
 
201
  }
202
 
203
  .second {
204
+ @some_mixin(;4px); // blank argument takes default value
205
  }</pre>
206
 
207
  <a name="import"></a>
lib/vendor/lessphp/lessc.inc.php CHANGED
@@ -11,7 +11,6 @@
11
  */
12
 
13
  //
14
- // investigate trouble with ^M
15
  // fix the alpha value with color when using a percent
16
  //
17
 
@@ -20,9 +19,15 @@ class lessc {
20
  private $count;
21
  private $line;
22
  private $expandStack;
 
 
 
 
23
 
24
  private $env = array();
25
 
 
 
26
  public $vPrefix = '@';
27
  public $mPrefix = '$';
28
  public $imPrefix = '!';
@@ -39,7 +44,7 @@ class lessc {
39
 
40
  static private $dtypes = array('expression', 'variable', 'function', 'negative'); // types with delayed computation
41
  static private $units = array(
42
- 'px', '%', 'in', 'cm', 'mm', 'em', 'ex', 'pt', 'pc', 'ms', 's', 'deg');
43
 
44
  public $importDisabled = false;
45
  public $importDir = '';
@@ -84,10 +89,45 @@ class lessc {
84
 
85
  // charset
86
  if ($this->literal('@charset') && $this->propertyValue($value) && $this->end()) {
87
- return "@charset ".$this->compileValue($value).";\n";
 
 
 
 
 
 
 
 
 
88
  } else {
89
  $this->seek($s);
90
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  }
92
 
93
  // opening abstract block
@@ -122,6 +162,18 @@ class lessc {
122
 
123
  // closing block
124
  if ($this->literal('}')) {
 
 
 
 
 
 
 
 
 
 
 
 
125
  $tags = $this->multiplyTags();
126
  $env = end($this->env);
127
  $ctags = $env['__tags'];
@@ -139,7 +191,12 @@ class lessc {
139
  if (!empty($tags))
140
  $out = $this->compileBlock($tags, $env);
141
 
142
- $this->pop();
 
 
 
 
 
143
 
144
  // make the block(s) available in the new current scope
145
  if (!isset($env['__dontsave'])) {
@@ -161,21 +218,14 @@ class lessc {
161
  if ($this->importDisabled) return "/* import is disabled */\n";
162
 
163
  $full = $this->importDir.$url;
164
- if (file_exists($file = $full) || file_exists($file = $full.'.less')) {
165
- $loaded = $this->removeComments(ltrim(file_get_contents($file).";"));
 
166
  $this->buffer = substr($this->buffer, 0, $this->count).$loaded.substr($this->buffer, $this->count);
167
  return true;
168
  }
169
 
170
- return '@import url("'.$url.'")'.($media ? ' '.$media : '').";\n";
171
- }
172
-
173
- // setting variable
174
- if ($this->variable($name) && $this->assign() && $this->propertyValue($value) && $this->end()) {
175
- $this->append($this->vPrefix.$name, $value);
176
- return true;
177
- } else {
178
- $this->seek($s);
179
  }
180
 
181
  // mixin/function expand
@@ -204,9 +254,10 @@ class lessc {
204
  }
205
  }
206
 
207
- // set all properties
208
  ob_start();
209
  $blocks = array();
 
210
  foreach ($env as $name => $value) {
211
  // skip the metatdata
212
  if (preg_match('/^__/', $name)) continue;
@@ -215,6 +266,8 @@ class lessc {
215
  // is mixed in
216
  if (!isset($value[0]))
217
  $blocks[] = array($name, $value);
 
 
218
 
219
  // copy the data
220
  // don't overwrite previous value, look in current env for name
@@ -225,6 +278,35 @@ class lessc {
225
  $this->set($name, $value);
226
  }
227
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
228
  // render sub blocks
229
  foreach ($blocks as $b) {
230
  $rtags = $this->multiplyTags(array($b[0]));
@@ -242,6 +324,11 @@ class lessc {
242
  return false; // couldn't match anything, throw error
243
  }
244
 
 
 
 
 
 
245
  // recursively find the cartesian product of all tags in stack
246
  function multiplyTags($tags = array(' '), $d = null) {
247
  if ($d === null) $d = count($this->env) - 1;
@@ -429,6 +516,23 @@ class lessc {
429
  return $this->to(';', $media, false, true);
430
  }
431
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
432
  // a scoped value accessor
433
  // .hello > @scope1 > @scope2['value'];
434
  function accessor(&$var) {
@@ -540,10 +644,14 @@ class lessc {
540
  if (!$this->literal('(')) return false;
541
 
542
  $values = array();
543
- while ($this->propertyValue($value)) {
544
- $values[] = $value;
545
  if (!$this->literal($delim)) break;
546
- }
 
 
 
 
547
 
548
  if (!$this->literal(')')) {
549
  $this->seek($s);
@@ -593,6 +701,20 @@ class lessc {
593
  return true;
594
  }
595
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
596
  // a single tag
597
  function tag(&$tag, $simple = false) {
598
  if ($simple)
@@ -601,17 +723,12 @@ class lessc {
601
  $chars = '^,;{}[';
602
 
603
  $tag = '';
 
604
  while ($this->match('(['.$chars.'0-9]['.$chars.']*)', $m)) {
605
- $tag.= $m[1];
606
  if ($simple) break;
607
 
608
- $s = $this->seek();
609
- if ($this->literal('[') && $this->to(']', $c, true) && $this->literal(']')) {
610
- $tag .= '['.$c.'] ';
611
- } else {
612
- $this->seek($s);
613
- break;
614
- }
615
  }
616
  $tag = trim($tag);
617
  if ($tag == '') return false;
@@ -662,6 +779,7 @@ class lessc {
662
  if ($this->literal($this->vPrefix, false) && $this->keyword($name)) {
663
  return true;
664
  }
 
665
  return false;
666
  }
667
 
@@ -715,21 +833,30 @@ class lessc {
715
  // but.. don't render special properties (blocks, vars, metadata)
716
  if (isset($value[0]) && $name{0} != $this->vPrefix && $name != '__args') {
717
  echo $this->compileProperty($name, $value, 1)."\n";
718
- $props++;
719
  }
720
  }
721
  $list = ob_get_clean();
722
-
723
  if ($props == 0) return '';
724
 
725
- // do some formatting
726
- if ($props == 1) $list = ' '.trim($list).' ';
727
- return implode(", ", $rtags).' {'.($props > 1 ? "\n" : '').
728
- $list."}\n";
 
 
 
 
 
729
 
 
 
 
 
730
  }
731
 
732
  function compileProperty($name, $value, $level = 0) {
 
733
  // output all repeated properties
734
  foreach ($value as $v)
735
  $props[] = str_repeat(' ', $level).
@@ -759,7 +886,7 @@ class lessc {
759
 
760
  // search for inline variables to replace
761
  $replace = array();
762
- if (preg_match_all('/{(@[\w-_][0-9\w-_]*)}/', $value[1], $m)) {
763
  foreach($m[1] as $name) {
764
  if (!isset($replace[$name]))
765
  $replace[$name] = $this->compileValue(array('variable', $name));
@@ -773,6 +900,7 @@ class lessc {
773
  $value[1] = str_replace('{'.$var.'}', $val, $value[1]);
774
  }
775
 
 
776
  return $value[1];
777
  case 'color':
778
  // [1] - red component (either number for a %)
@@ -1200,8 +1328,11 @@ class lessc {
1200
 
1201
  $this->env = array();
1202
  $this->expandStack = array();
 
 
1203
  $this->count = 0;
1204
  $this->line = 1;
 
1205
 
1206
  $this->buffer = $this->removeComments($this->buffer);
1207
  $this->push(); // set up global scope
@@ -1229,7 +1360,7 @@ class lessc {
1229
 
1230
  function throwParseError($msg = 'parse error') {
1231
  $line = $this->line + substr_count(substr($this->buffer, 0, $this->count), "\n");
1232
- if ($this->peek("(.*?)\n", $m))
1233
  throw new exception($msg.': failed at `'.$m[1].'` line: '.$line);
1234
  }
1235
 
@@ -1248,48 +1379,70 @@ class lessc {
1248
  $this->fileName = $fname;
1249
  $this->importDir = $pi['dirname'].'/';
1250
  $this->buffer = file_get_contents($fname);
 
 
1251
  }
1252
  }
1253
 
1254
  // remove comments from $text
1255
  // todo: make it work for all functions, not just url
1256
- // todo: make it not mess up line counter with block comments
1257
  function removeComments($text) {
1258
- $out = '';
 
 
1259
 
1260
- while (!empty($text) &&
1261
- preg_match('/^(.*?)("|\'|\/\/|\/\*|url\(|$)/is', $text, $m))
1262
- {
1263
- if (!trim($text)) break;
 
 
 
 
 
 
 
1264
 
1265
- $out .= $m[1];
1266
- $text = substr($text, strlen($m[0]));
1267
 
1268
- switch ($m[2]) {
 
 
 
1269
  case 'url(':
1270
- preg_match('/^(.*?)(\)|$)/is', $text, $inner);
1271
- $text = substr($text, strlen($inner[0]));
1272
- $out .= $m[2].$inner[1].$inner[2];
1273
- break;
1274
- case '//':
1275
- preg_match("/^(.*?)(\n|$)/is", $text, $inner);
1276
- // give back the newline
1277
- $text = substr($text, strlen($inner[0]) - 1);
1278
- break;
1279
- case '/*';
1280
- preg_match("/^(.*?)(\*\/|$)/is", $text, $inner);
1281
- $text = substr($text, strlen($inner[0]));
1282
  break;
1283
  case '"':
1284
  case "'":
1285
- preg_match("/^(.*?)(".$m[2]."|$)/is", $text, $inner);
1286
- $text = substr($text, strlen($inner[0]));
1287
- $out .= $m[2].$inner[1].$inner[2];
 
 
 
 
 
 
 
 
1288
  break;
1289
  }
 
 
 
 
 
 
 
1290
  }
1291
 
1292
- return $out;
 
 
 
 
 
1293
  }
1294
 
1295
 
@@ -1305,8 +1458,72 @@ class lessc {
1305
  return false;
1306
  }
1307
 
1308
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1309
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1310
 
 
 
 
1311
 
1312
  ?>
11
  */
12
 
13
  //
 
14
  // fix the alpha value with color when using a percent
15
  //
16
 
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 = '!';
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 = '';
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
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'];
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'])) {
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
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;
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
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]));
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;
516
  return $this->to(';', $media, false, true);
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
537
  // .hello > @scope1 > @scope2['value'];
538
  function accessor(&$var) {
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;
652
+ $value = null;
653
+ }
654
+ }
655
 
656
  if (!$this->literal(')')) {
657
  $this->seek($s);
701
  return true;
702
  }
703
 
704
+ // a bracketed value (contained within in a tag definition)
705
+ function tagBracket(&$value) {
706
+ $s = $this->seek();
707
+ if ($this->literal('[') && $this->to(']', $c, true) && $this->literal(']', false)) {
708
+ $value = '['.$c.']';
709
+ // whitespace?
710
+ if ($this->match('', $_)) $value .= $_[0];
711
+ return true;
712
+ }
713
+
714
+ $this->seek($s);
715
+ return false;
716
+ }
717
+
718
  // a single tag
719
  function tag(&$tag, $simple = false) {
720
  if ($simple)
723
  $chars = '^,;{}[';
724
 
725
  $tag = '';
726
+ while ($this->tagBracket($first)) $tag .= $first;
727
  while ($this->match('(['.$chars.'0-9]['.$chars.']*)', $m)) {
728
+ $tag .= $m[1];
729
  if ($simple) break;
730
 
731
+ while ($this->tagBracket($brack)) $tag .= $brack;
 
 
 
 
 
 
732
  }
733
  $tag = trim($tag);
734
  if ($tag == '') return false;
779
  if ($this->literal($this->vPrefix, false) && $this->keyword($name)) {
780
  return true;
781
  }
782
+
783
  return false;
784
  }
785
 
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).
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));
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 %)
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
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
 
1379
  $this->fileName = $fname;
1380
  $this->importDir = $pi['dirname'].'/';
1381
  $this->buffer = file_get_contents($fname);
1382
+
1383
+ $this->addParsedFile($fname);
1384
  }
1385
  }
1386
 
1387
  // remove comments from $text
1388
  // todo: make it work for all functions, not just url
 
1389
  function removeComments($text) {
1390
+ $look = array(
1391
+ 'url(', '//', '/*', '"', "'"
1392
+ );
1393
 
1394
+ $out = '';
1395
+ $min = null;
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);
1403
+ }
1404
+ }
1405
 
1406
+ if (is_null($min)) break;
 
1407
 
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]);
 
 
 
 
 
 
 
 
 
 
1415
  break;
1416
  case '"':
1417
  case "'":
1418
+ if (preg_match('/'.$min[0].'.*?'.$min[0].'/', $text, $m, 0, $count))
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)) {
1426
+ $skip = strlen($m[0]);
1427
+ $newlines = substr_count($m[0], "\n");
1428
+ }
1429
  break;
1430
  }
1431
+
1432
+ if ($skip == 0) $count += strlen($min[0]);
1433
+
1434
+ $out .= substr($text, 0, $count).str_repeat("\n", $newlines);
1435
+ $text = substr($text, $count + $skip);
1436
+
1437
+ $min = null;
1438
  }
1439
 
1440
+ return $out.$text;
1441
+ }
1442
+
1443
+ public function allParsedFiles() { return $this->allParsedFiles; }
1444
+ protected function addParsedFile($file) {
1445
+ $this->allParsedFiles[realpath($file)] = filemtime($file);
1446
  }
1447
 
1448
 
1458
  return false;
1459
  }
1460
 
1461
+ /**
1462
+ * Execute lessphp on a .less file or a lessphp cache structure
1463
+ *
1464
+ * The lessphp cache structure contains information about a specific
1465
+ * less file having been parsed. It can be used as a hint for future
1466
+ * calls to determine whether or not a rebuild is required.
1467
+ *
1468
+ * The cache structure contains two important keys that may be used
1469
+ * externally:
1470
+ *
1471
+ * compiled: The final compiled CSS
1472
+ * updated: The time (in seconds) the CSS was last compiled
1473
+ *
1474
+ * The cache structure is a plain-ol' PHP associative array and can
1475
+ * be serialized and unserialized without a hitch.
1476
+ *
1477
+ * @param mixed $in Input
1478
+ * @param bool $force Force rebuild?
1479
+ * @return array lessphp cache structure
1480
+ */
1481
+ public static function cexecute($in, $force = false) {
1482
+
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'];
1500
+ break;
1501
+ }
1502
+ }
1503
+ }
1504
+ } else {
1505
+ // TODO: Throw an exception? We got neither a string nor something
1506
+ // that looks like a compatible lessphp cache structure.
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();
1514
+ $out['root'] = $root;
1515
+ $out['compiled'] = $less->parse();
1516
+ $out['files'] = $less->allParsedFiles();
1517
+ $out['updated'] = time();
1518
+ return $out;
1519
+ } else {
1520
+ // No changes, pass back the structure
1521
+ // we were given initially.
1522
+ return $in;
1523
+ }
1524
 
1525
+ }
1526
+
1527
+ }
1528
 
1529
  ?>
lib/vendor/lessphp/plessc CHANGED
@@ -91,24 +91,38 @@ if (has("w")) {
91
  ", press Ctrl + c to exit.\n";
92
 
93
  $fail_time = 0;
 
 
 
94
  while (1) {
95
- if (!is_file($out) || (filemtime($in) > filemtime($out) && $fail_time != filemtime($in))) {
96
- // try to compile it
97
- try {
98
- $l = new lessc($in);
99
- $c = $l->parse();
 
 
 
 
 
 
 
 
 
 
 
100
  echo "Writing updated file: ".$out."\n";
101
- if (!file_put_contents($out, $c)) {
102
  err($fa."Could not write to file ".$out);
103
  exit(1);
104
  }
105
- } catch (exception $ex) {
106
- echo "\nFatal Error:\n".str_repeat('=', 20)."\n".$ex->getMessage()."\n\n";
107
- $fail_time = filemtime($in);
 
108
 
109
- if (has("n")) {
110
- `notify-send -u critical "compile failed" "{$ex->getMessage()}"`;
111
- }
112
  }
113
  }
114
 
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
+ }
106
+ }
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
 
lib/vendor/lessphp/tests/README ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ test.php
2
+ ========================================
3
+
4
+ to run:
5
+ php test.php
6
+
7
+
8
+ Runs through all files in `inputs`, compiles them, then
9
+ compares to respective file in `outputs`. If there are
10
+ any differences then the test will fail.
11
+
12
+ Add the -d flag to show the differences of failed tests
13
+ in your diff tool (currently assigned in code, $difftool)
14
+ Defaults to diff, but I like using meld.
15
+
16
+ Pass the -C flag to save the output of the inputs to
17
+ the appropriate file. This will overwrite any existing
18
+ outputs. Use this when you want to save verified test
19
+ results.
20
+
21
+
lib/vendor/lessphp/tests/inputs/accessors.less ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* accessors */
2
+
3
+ #defaults {
4
+ @width: 960px;
5
+ @color: black;
6
+ .something {
7
+ @space: 10px;
8
+ @hello {
9
+ color: green;
10
+ }
11
+ }
12
+ }
13
+
14
+ .article { color: #294366; }
15
+
16
+ .comment {
17
+ width: #defaults[@width];
18
+ color: .article['color'];
19
+ padding: #defaults > .something[@space];
20
+ }
21
+
22
+ .wow {
23
+ height: .comment['width'];
24
+ background-color: .comment['color'];
25
+ color: #defaults > .something > @hello['color'];
26
+
27
+ padding: #defaults > non-existant['padding'];
28
+ margin: #defaults > .something['non-existant'];
29
+ }
30
+
31
+ .mix {
32
+ #defaults;
33
+ font-size: .something[@space];
34
+ }
35
+
36
+
lib/vendor/lessphp/tests/inputs/attributes.less ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ * { color: blue; }
2
+ E { color: blue; }
3
+ E[foo] { color: blue; }
4
+ [foo] { color: blue; }
5
+ [foo] .helloWorld { color: blue; }
6
+ [foo].helloWorld { color: blue; }
7
+ E[foo="barbar"] { color: blue; }
8
+ E[foo~="hello#$@%@$#^"] { color: blue; }
9
+ E[foo^="color: green;"] { color: blue; }
10
+ E[foo$="239023"] { color: blue; }
11
+ E[foo*="29302"] { color: blue; }
12
+ E[foo|="239032"] { color: blue; }
13
+ E:root { color: blue; }
14
+
15
+ E:nth-child(odd) { color: blue; }
16
+ E:nth-child(2n+1) { color: blue; }
17
+ E:nth-child(5) { color: blue; }
18
+ E:nth-last-child(-n+2) { color: blue; }
19
+ E:nth-of-type(2n) { color: blue; }
20
+ E:nth-last-of-type(n) { color: blue; }
21
+
22
+ E:first-child { color: blue; }
23
+ E:last-child { color: blue; }
24
+ E:first-of-type { color: blue; }
25
+ E:last-of-type { color: blue; }
26
+ E:only-child { color: blue; }
27
+ E:only-of-type { color: blue; }
28
+ E:empty { color: blue; }
29
+
30
+ E:lang(en) { color: blue; }
31
+ E::first-line { color: blue; }
32
+ E::before { color: blue; }
33
+
34
+ E#id { color: blue; }
35
+ E:not(:link) { color: blue; }
36
+
37
+ E F { color: blue; }
38
+ E > F { color: blue; }
39
+ E + F { color: blue; }
40
+ E ~ F { color: blue; }
41
+
lib/vendor/lessphp/tests/inputs/font_family.less ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ @font-directory: 'fonts/';
3
+ @some-family: Gentium;
4
+
5
+ @font-face: maroon; // won't collide with @font-face { }
6
+
7
+ @font-face {
8
+ font-family: Graublau Sans Web;
9
+ src: url({@font-directory}GraublauWeb.otf) format("opentype");
10
+ }
11
+
12
+ @font-face {
13
+ font-family: @some-family;
14
+ src: url('{@font-directory}Gentium.ttf');
15
+ }
16
+
17
+ @font-face {
18
+ font-family: @some-family;
19
+ src: url("{@font-directory}GentiumItalic.ttf");
20
+ font-style: italic;
21
+ }
22
+
23
+ h2 {
24
+ font-family: @some-family;
25
+ crazy: @font-face;
26
+ }
27
+
28
+
lib/vendor/lessphp/tests/inputs/keyframes.less ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @keyframes 'bounce' {
2
+ from {
3
+ top: 100px;
4
+ animation-timing-function: ease-out;
5
+ }
6
+
7
+ 25% {
8
+ top: 50px;
9
+ animation-timing-function: ease-in;
10
+ }
11
+
12
+ 50% {
13
+ top: 100px;
14
+ animation-timing-function: ease-out;
15
+ }
16
+
17
+ 75% {
18
+ top: 75px;
19
+ animation-timing-function: ease-in;
20
+ }
21
+
22
+ to {
23
+ top: 100px;
24
+ }
25
+ }
26
+
27
+
28
+
29
+ div {
30
+ animation-name: 'diagonal-slide';
31
+ animation-duration: 5s;
32
+ animation-iteration-count: 10;
33
+ }
34
+
35
+ @keyframes 'diagonal-slide' {
36
+
37
+ from {
38
+ left: 0;
39
+ top: 0;
40
+ }
41
+
42
+ to {
43
+ left: 100px;
44
+ top: 100px;
45
+ }
46
+
47
+ }
48
+
lib/vendor/lessphp/tests/inputs/media.less ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @media screen, 3D {
2
+ P { color: green; }
3
+ }
4
+ @media print {
5
+ body { font-size: 10pt }
6
+ }
7
+ @media screen {
8
+ body { font-size: 13px }
9
+ }
10
+ @media screen, print {
11
+ body { line-height: 1.2 }
12
+ }
13
+
14
+ @media all and (min-width: 0px) {
15
+ body { line-height: 1.2 }
16
+ }
17
+
18
+ @media all and (min-width: 0) {
19
+ body { line-height: 1.2 }
20
+ }
21
+
lib/vendor/lessphp/tests/inputs/misc.less ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ @hello: "utf-8";
3
+ @charset @hello;
4
+
5
+ @color: #fff;
6
+ @images: "/assets/images/";
7
+ @images: @images + "test/";
8
+ .topbar { background: url({@images}topbar.png); }
9
+ .hello { test: empty-function(@images, 40%, to(@color)); }
10
+
11
+ .css3 {
12
+ background-image: -webkit-gradient(linear, 0% 0%, 0% 90%,
13
+ from(#E9A000), to(#A37000));
14
+ }
15
+
lib/vendor/lessphp/tests/inputs/mixin_functions.less ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ @outer: 10px;
3
+ @class(@var:22px; @car: 400px + @outer) {
4
+ margin: @var;
5
+ height: @car;
6
+ }
7
+
8
+ @group {
9
+ @f(@color) {
10
+ color: @color;
11
+ }
12
+ .cool {
13
+ border-bottom: 1px solid green;
14
+ }
15
+ }
16
+
17
+ .class(@width:200px) {
18
+ padding: @width;
19
+ }
20
+
21
+ body {
22
+ .class(2.0em);
23
+ @group > @f(red);
24
+ @class(10px; 10px + 2);
25
+ @group > .cool;
26
+ }
27
+
28
+
29
+ @lots(@a: 10px; @b: 20px; @c: 30px; @d: 40px; @e: 4px; @f:3px; @g:2px; @h: 1px) {
30
+ padding: @a @b @c @d;
31
+ margin: @e @f @g @h;
32
+ }
33
+
34
+ .skip_args {
35
+ @class(;12px);
36
+ @lots(;;;88px;;12px);
37
+ @group > @f(red;;;;);
38
+ }
39
+
lib/vendor/lessphp/tests/inputs/mixins.less ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ @rounded-corners {
3
+ border-radius: 10px;
4
+ }
5
+
6
+ .bold {
7
+ @font-size: 20px;
8
+ font-size: @font-size;
9
+ font-weight: bold;
10
+ }
11
+
12
+ body #window {
13
+ @rounded-corners;
14
+ .bold;
15
+ line-height: @font-size * 1.5;
16
+ }
17
+
18
+ #bundle {
19
+ .button {
20
+ display: block;
21
+ border: 1px solid black;
22
+ background-color: grey;
23
+ :hover { background-color: white }
24
+ }
25
+ }
26
+ #header a {
27
+ color: orange;
28
+ #bundle > .button; // mixin the button class
29
+ }
30
+
31
+ div {
32
+ @abstract {
33
+ hello: world;
34
+ b {
35
+ color: blue;
36
+ }
37
+ }
38
+
39
+ @abstract > b;
40
+ @abstract;
41
+ }
42
+
43
+ @poop {
44
+ big: baby;
45
+ }
46
+
47
+ body {
48
+ div;
49
+ }
50
+
51
+
lib/vendor/lessphp/tests/inputs/nested.less ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #header {
2
+ color: black;
3
+
4
+ .navigation {
5
+ font-size: 12px;
6
+ .border {
7
+ .outside {
8
+ color: blue;
9
+ }
10
+ }
11
+ }
12
+ .logo {
13
+ width: 300px;
14
+ :hover { text-decoration: none }
15
+ }
16
+ }
17
+
18
+ a { b { ul { li { color: green; } } } }
19
+
20
+ this { will { not { show { } } } }
21
+
lib/vendor/lessphp/tests/inputs/variables.less ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @a: 2;
2
+ @x: @a * @a;
3
+ @y: @x + 1;
4
+ @z: @y + @x * 2;
5
+ @m: @z % @y;
6
+
7
+ @nice-blue: #5B83AD;
8
+ @light-blue: @nice-blue + #111;
9
+
10
+ @rgb-color: rgb(20%, 15%, 80%);
11
+ @rgba-color: rgba(23,68,149,0.5);
12
+
13
+ @b: @a * 10px;
14
+ @c: #888;
15
+ @fonts: "Trebuchet MS", Verdana, sans-serif;
16
+
17
+ .variables {
18
+ width: @z + 1cm; // 14cm
19
+ height: @b + @x + 0px; // 24px
20
+ margin-top: -@b; // -20px
21
+ margin-bottom: 10 - -@b; // 30px
22
+ @c: @c + #001;
23
+ color: @c;
24
+ background: @light-blue;
25
+ font-family: @fonts;
26
+ margin: @m + 0px; // 3px
27
+ font-size: 10px/12px;
28
+ font-size: 120%/120%;
29
+ }
30
+
31
+ .external {
32
+ color: @c;
33
+ border: 1px solid @rgb-color;
34
+ background: @rgba-color;
35
+ padding: @nonexistant + 4px;
36
+ }
37
+
38
+ // 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
+
lib/vendor/lessphp/tests/outputs/accessors.css ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .article { color:#294366; }
2
+ .comment {
3
+ width:960px;
4
+ color:#294366;
5
+ padding:10px;
6
+ }
7
+ .wow {
8
+ height:960px;
9
+ background-color:#294366;
10
+ color:green;
11
+ }
12
+ .mix { font-size:10px; }
lib/vendor/lessphp/tests/outputs/attributes.css ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ * { color:blue; }
2
+ E { color:blue; }
3
+ E[foo] { color:blue; }
4
+ [foo] { color:blue; }
5
+ [foo] .helloWorld { color:blue; }
6
+ [foo].helloWorld { color:blue; }
7
+ E[foo="barbar"] { color:blue; }
8
+ E[foo~="hello#$@%@$#^"] { color:blue; }
9
+ E[foo^="color: green;"] { color:blue; }
10
+ E[foo$="239023"] { color:blue; }
11
+ E[foo*="29302"] { color:blue; }
12
+ E[foo|="239032"] { color:blue; }
13
+ E:root { color:blue; }
14
+ E:nth-child(odd) { color:blue; }
15
+ E:nth-child(2n+1) { color:blue; }
16
+ E:nth-child(5) { color:blue; }
17
+ E:nth-last-child(-n+2) { color:blue; }
18
+ E:nth-of-type(2n) { color:blue; }
19
+ E:nth-last-of-type(n) { color:blue; }
20
+ E:first-child { color:blue; }
21
+ E:last-child { color:blue; }
22
+ E:first-of-type { color:blue; }
23
+ E:last-of-type { color:blue; }
24
+ E:only-child { color:blue; }
25
+ E:only-of-type { color:blue; }
26
+ E:empty { color:blue; }
27
+ E:lang(en) { color:blue; }
28
+ E::first-line { color:blue; }
29
+ E::before { color:blue; }
30
+ E#id { color:blue; }
31
+ E:not(:link) { color:blue; }
32
+ E F { color:blue; }
33
+ E > F { color:blue; }
34
+ E + F { color:blue; }
35
+ E ~ F { color:blue; }
lib/vendor/lessphp/tests/outputs/font_family.css ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @font-face {
2
+ font-family:Graublau Sans Web;
3
+ src:url(fonts/GraublauWeb.otf) format("opentype");
4
+ }
5
+ @font-face {
6
+ font-family:Gentium;
7
+ src:url('fonts/Gentium.ttf');
8
+ }
9
+ @font-face {
10
+ font-family:Gentium;
11
+ src:url("fonts/GentiumItalic.ttf");
12
+ font-style:italic;
13
+ }
14
+ h2 {
15
+ font-family:Gentium;
16
+ crazy:maroon;
17
+ }
lib/vendor/lessphp/tests/outputs/keyframes.css ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @keyframes 'bounce' {
2
+ from {
3
+ top:100px;
4
+ animation-timing-function:ease-out;
5
+ }
6
+ 25% {
7
+ top:50px;
8
+ animation-timing-function:ease-in;
9
+ }
10
+ 50% {
11
+ top:100px;
12
+ animation-timing-function:ease-out;
13
+ }
14
+ 75% {
15
+ top:75px;
16
+ animation-timing-function:ease-in;
17
+ }
18
+ to { top:100px; }
19
+ }
20
+ div {
21
+ animation-name:'diagonal-slide';
22
+ animation-duration:5s;
23
+ animation-iteration-count:10;
24
+ }
25
+ @keyframes 'diagonal-slide' {
26
+ from {
27
+ left:0;
28
+ top:0;
29
+ }
30
+ to {
31
+ left:100px;
32
+ top:100px;
33
+ }
34
+ }
lib/vendor/lessphp/tests/outputs/media.css ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @media screen, 3D {
2
+ P { color:green; }
3
+ }
4
+ @media print {
5
+ body { font-size:10pt; }
6
+ }
7
+ @media screen {
8
+ body { font-size:13px; }
9
+ }
10
+ @media screen, print {
11
+ body { line-height:1.2; }
12
+ }
13
+ @media all and (min-width: 0px) {
14
+ body { line-height:1.2; }
15
+ }
16
+ @media all and (min-width: 0) {
17
+ body { line-height:1.2; }
18
+ }
lib/vendor/lessphp/tests/outputs/misc.css ADDED
@@ -0,0 +1,4 @@
 
 
 
 
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)); }
lib/vendor/lessphp/tests/outputs/mixin_functions.css ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .class { padding:200px; }
2
+ body {
3
+ padding:2.0em;
4
+ color:red;
5
+ margin:10px;
6
+ height:12px;
7
+ border-bottom:1px solid green;
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
+ }
lib/vendor/lessphp/tests/outputs/mixins.css ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .bold {
2
+ font-size:20px;
3
+ font-weight:bold;
4
+ }
5
+ body #window {
6
+ border-radius:10px;
7
+ font-size:20px;
8
+ font-weight:bold;
9
+ line-height:30px;
10
+ }
11
+ #bundle .button: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
+ }
lib/vendor/lessphp/tests/outputs/nested.css ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ #header .navigation .border .outside { color:blue; }
2
+ #header .navigation { font-size:12px; }
3
+ #header .logo:hover { text-decoration:none; }
4
+ #header .logo { width:300px; }
5
+ #header { color:black; }
6
+ a b ul li { color:green; }
lib/vendor/lessphp/tests/outputs/nesting.css ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ #header .navigation .border .outside { color:blue; }
2
+ #header .navigation { font-size:12px; }
3
+ #header .logo:hover { text-decoration:none; }
4
+ #header .logo { width:300px; }
5
+ #header { color:black; }
6
+ a b ul li { color:green; }
lib/vendor/lessphp/tests/outputs/variables.css ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .variables {
2
+ width:14cm;
3
+ height:24px;
4
+ margin-top:-20px;
5
+ margin-bottom:30px;
6
+ color:#888899;
7
+ background:#6c94be;
8
+ font-family:"Trebuchet MS", Verdana, sans-serif;
9
+ margin:3px;
10
+ font-size:10px/12px;
11
+ font-size:120%/120%;
12
+ }
13
+ .external {
14
+ color:#888888;
15
+ border:1px solid 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;
lib/vendor/lessphp/tests/test.php ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ error_reporting(E_ALL);
3
+
4
+ /**
5
+ * Go through all files matching pattern in input directory
6
+ * and compile them, then compare them to paired file in
7
+ * output directory.
8
+ */
9
+ $difftool = 'diff';
10
+ $input = array(
11
+ 'dir' => 'inputs',
12
+ 'match' => '(.*?)\.less',
13
+ );
14
+
15
+ $output = array(
16
+ 'dir' => 'outputs',
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') {
25
+ exit($fa.$argv[0].' must be run in the command line.');
26
+ }
27
+
28
+ $exe = array_shift($argv); // remove filename
29
+ function flag($f) {
30
+ if (func_num_args() > 1) {
31
+ foreach (func_get_args() as $f) if (flag($f)) return true;
32
+ return false;
33
+ }
34
+ global $argv;
35
+ $pre = strlen($f) > 1 ? '--' : '-';
36
+ foreach ($argv as $a) {
37
+ if (preg_match('/^'.$pre.$f.'($|\s)/', $a)) return true;
38
+ }
39
+ return false;
40
+ }
41
+
42
+ 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
+ }
59
+
60
+ $count = count($tests);
61
+ $compiling = flag('C');
62
+ $showDiff = flag('d', 'diff');
63
+ echo ($compiling ? "Compiling" : "Running")." $count test".($count == 1 ? '' : 's').":\n";
64
+
65
+ function dump($msgs, $depth = 1) {
66
+ if (!is_array($msgs)) $msgs = array($msgs);
67
+ foreach ($msgs as $m) {
68
+ echo str_repeat("\t", $depth).' - '.$m."\n";
69
+ }
70
+ }
71
+
72
+ $i = 1;
73
+ foreach ($tests as $test) {
74
+ printf("\t[Test %04d/%04d] %s -> %s\n", $i, $count, basename($test['in']), basename($test['out']));
75
+
76
+ try {
77
+ ob_start();
78
+ $parsed = trim($compiler->parse(file_get_contents($test['in'])));
79
+ ob_end_clean();
80
+ } catch (exception $e) {
81
+ dump(array(
82
+ "Failed to compile input, reason:",
83
+ $e->getMessage(),
84
+ "Aborting"
85
+ ));
86
+ break;
87
+ }
88
+
89
+ if ($compiling) {
90
+ file_put_contents($test['out'], $parsed);
91
+ } else {
92
+ if (!is_file($test['out'])) {
93
+ dump(array(
94
+ "Failed to find output file: $test[out]",
95
+ "Maybe you forgot to compile tests?",
96
+ "Aborting"
97
+ ));
98
+ break;
99
+ }
100
+ $expected = trim(file_get_contents($test['out']));
101
+
102
+ if ($expected != $parsed) {
103
+ if ($showDiff) {
104
+ dump("Failed:");
105
+ $tmp = $test['out'].".tmp";
106
+ file_put_contents($tmp, $parsed);
107
+ system($difftool.' '.$test['out'].' '.$tmp);
108
+ unlink($tmp);
109
+
110
+ dump("Aborting");
111
+ break;
112
+ } else dump("Failed, run with -d flag to view diff");
113
+ } else {
114
+ dump("Passed");
115
+ }
116
+ }
117
+
118
+ $i++;
119
+ }
120
+
121
+ ?>
lib/vendor/plugin-toolkit/BaseConfiguration.class.php CHANGED
@@ -1,12 +1,17 @@
1
  <?php
2
-
 
 
 
 
 
3
  abstract class WPPluginToolkitConfiguration
4
  {
5
  const UNIX_NAME = null;
6
  const I18N_DIR = 'i18n';
7
 
8
- protected static $upload_dir,
9
- $upload_url;
10
 
11
  protected $base_class_name,
12
  $base_dirname,
@@ -17,12 +22,13 @@ abstract class WPPluginToolkitConfiguration
17
  $i18n_path_from_plugins,
18
  $options,
19
  $plugin_path,
 
20
  $unix_name;
21
 
22
  /**
23
  * Launch the configure process
24
  * It is generally totally specific to each plugin.
25
- *
26
  * @author oncletom
27
  * @protected
28
  */
@@ -34,7 +40,7 @@ abstract class WPPluginToolkitConfiguration
34
 
35
  /**
36
  * Let the plugin configure its own options
37
- *
38
  * @author oncletom
39
  * @abstract
40
  * @protected
@@ -43,7 +49,7 @@ abstract class WPPluginToolkitConfiguration
43
 
44
  /**
45
  * Base constructor for a plugin configuration
46
- *
47
  * @author oncletom
48
  * @since 1.0
49
  * @version 1.0
@@ -61,7 +67,7 @@ abstract class WPPluginToolkitConfiguration
61
  }
62
 
63
  $this->base_class_name = $baseClassName;
64
- $this->setupPath($baseFileName, constant($unix_name_pattern));
65
  $this->setupPathGlobal();
66
  //$this->options = new $baseClassName.'OptionCollection';
67
 
@@ -71,7 +77,7 @@ abstract class WPPluginToolkitConfiguration
71
 
72
  /**
73
  * Returns resolved plugin full path location
74
- *
75
  * @author oncletom
76
  * @since 1.0
77
  * @version 1.0
@@ -84,7 +90,7 @@ abstract class WPPluginToolkitConfiguration
84
 
85
  /**
86
  * Returns resolved plugin full path filename
87
- *
88
  * @author oncletom
89
  * @since 1.0
90
  * @version 1.0
@@ -95,9 +101,35 @@ abstract class WPPluginToolkitConfiguration
95
  return $this->filename;
96
  }
97
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  /**
99
  * Returns plugin prefix for classes
100
- *
101
  * @author oncletom
102
  * @since 1.0
103
  * @version 1.0
@@ -110,10 +142,10 @@ abstract class WPPluginToolkitConfiguration
110
 
111
  /**
112
  * Returns resolved plugin path location, from plugin path
113
- *
114
  * In theory, it's the same as Unix path but in fact, if the plugin is renamed it can helps
115
  * Not very used yet, though.
116
- *
117
  * @author oncletom
118
  * @since 1.0
119
  * @version 1.0
@@ -122,11 +154,16 @@ abstract class WPPluginToolkitConfiguration
122
  public function getPluginPath()
123
  {
124
  return $this->plugin_path;
125
- }
 
 
 
 
 
126
 
127
  /**
128
  * Returns unix name of the plugin
129
- *
130
  * @author oncletom
131
  * @since 1.0
132
  * @version 1.0
@@ -139,7 +176,7 @@ abstract class WPPluginToolkitConfiguration
139
 
140
  /**
141
  * Returns the upload dir for this configuration class (common to all instances)
142
- *
143
  * @author oncletom
144
  * @since 1.0
145
  * @version 1.0
@@ -147,12 +184,12 @@ abstract class WPPluginToolkitConfiguration
147
  */
148
  public function getUploadDir()
149
  {
150
- return self::$upload_dir;
151
  }
152
 
153
  /**
154
  * Returns the upload URL for this configuration class (common to all instances)
155
- *
156
  * @author oncletom
157
  * @since 1.0
158
  * @version 1.0
@@ -160,12 +197,12 @@ abstract class WPPluginToolkitConfiguration
160
  */
161
  public function getUploadUrl()
162
  {
163
- return self::$upload_url;
164
  }
165
 
166
  /**
167
  * Build paths for various access
168
- *
169
  * @author oncletom
170
  * @protected
171
  * @since 1.0
@@ -196,29 +233,43 @@ abstract class WPPluginToolkitConfiguration
196
 
197
  $this->dirname = dirname($this->filename);
198
  $this->plugin_path = preg_replace('#(.+)([^/]+/[^/]+)$#sU', "$2", $this->filename);
 
199
  do_action($this->unix_name.'_configuration_setup_path', $this);
200
  }
201
 
 
 
 
 
 
 
 
 
 
 
 
 
 
202
  /**
203
  * Resolves global upload path as WP does not provide any clean and independant solution for that
204
- *
205
  * It's barely based on the logic of `wp_upload_dir` function.
206
- *
207
  * @author oncletom
208
  * @since 1.0
209
- * @version 1.0
210
  * @return boolean
211
  */
212
  protected function setupPathGlobal()
213
  {
214
- if (isset(self::$upload_url))
215
  {
216
  return false;
217
  }
218
 
219
  $siteurl = get_option('siteurl');
220
  $upload_path = trim(get_option('upload_path'));
221
- $subdir = '/'.$this->unix_name;
222
 
223
  if (defined('UPLOADS'))
224
  {
@@ -237,19 +288,32 @@ abstract class WPPluginToolkitConfiguration
237
  : trailingslashit($siteurl).$upload_path;
238
  }
239
  }
240
-
241
  $uploads = apply_filters('upload_dir', array(
242
  'path' => $dir,
243
  'url' => $url,
244
- 'subdir' => $subdir,
245
- 'basedir' => $dir.$subdir,
246
- 'baseurl' => $url.$subdir,
247
  'error' => false,
248
  ));
249
 
250
- self::$upload_dir = $uploads['basedir'];
251
- self::$upload_url = $uploads['baseurl'];
252
 
253
  return $uploads['error'] ? false : true;
254
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
255
  }
1
  <?php
2
+ /**
3
+ * Base class for plugin configuration
4
+ *
5
+ * @author oncletom
6
+ * @version 1.1
7
+ */
8
  abstract class WPPluginToolkitConfiguration
9
  {
10
  const UNIX_NAME = null;
11
  const I18N_DIR = 'i18n';
12
 
13
+ protected $upload_dir,
14
+ $upload_url;
15
 
16
  protected $base_class_name,
17
  $base_dirname,
22
  $i18n_path_from_plugins,
23
  $options,
24
  $plugin_path,
25
+ $plugin_uri,
26
  $unix_name;
27
 
28
  /**
29
  * Launch the configure process
30
  * It is generally totally specific to each plugin.
31
+ *
32
  * @author oncletom
33
  * @protected
34
  */
40
 
41
  /**
42
  * Let the plugin configure its own options
43
+ *
44
  * @author oncletom
45
  * @abstract
46
  * @protected
49
 
50
  /**
51
  * Base constructor for a plugin configuration
52
+ *
53
  * @author oncletom
54
  * @since 1.0
55
  * @version 1.0
67
  }
68
 
69
  $this->base_class_name = $baseClassName;
70
+ $this->setupPath($baseFileName, $this->unix_name);
71
  $this->setupPathGlobal();
72
  //$this->options = new $baseClassName.'OptionCollection';
73
 
77
 
78
  /**
79
  * Returns resolved plugin full path location
80
+ *
81
  * @author oncletom
82
  * @since 1.0
83
  * @version 1.0
90
 
91
  /**
92
  * Returns resolved plugin full path filename
93
+ *
94
  * @author oncletom
95
  * @since 1.0
96
  * @version 1.0
101
  return $this->filename;
102
  }
103
 
104
+ /**
105
+ * Returns i18n path from WordPress root directory
106
+ *
107
+ * @author oncletom
108
+ * @since 1.0
109
+ * @version 1.0
110
+ * @return String
111
+ */
112
+ public function getI18nPath()
113
+ {
114
+ return $this->i18n_path;
115
+ }
116
+
117
+ /**
118
+ * Returns i18n path from plugin directory
119
+ *
120
+ * @author oncletom
121
+ * @since 1.0
122
+ * @version 1.0
123
+ * @return String
124
+ */
125
+ public function getI18nFromPluginPath()
126
+ {
127
+ return $this->i18n_path_from_plugins;
128
+ }
129
+
130
  /**
131
  * Returns plugin prefix for classes
132
+ *
133
  * @author oncletom
134
  * @since 1.0
135
  * @version 1.0
142
 
143
  /**
144
  * Returns resolved plugin path location, from plugin path
145
+ *
146
  * In theory, it's the same as Unix path but in fact, if the plugin is renamed it can helps
147
  * Not very used yet, though.
148
+ *
149
  * @author oncletom
150
  * @since 1.0
151
  * @version 1.0
154
  public function getPluginPath()
155
  {
156
  return $this->plugin_path;
157
+ }
158
+
159
+ public function getPluginUri()
160
+ {
161
+ return $this->plugin_uri;
162
+ }
163
 
164
  /**
165
  * Returns unix name of the plugin
166
+ *
167
  * @author oncletom
168
  * @since 1.0
169
  * @version 1.0
176
 
177
  /**
178
  * Returns the upload dir for this configuration class (common to all instances)
179
+ *
180
  * @author oncletom
181
  * @since 1.0
182
  * @version 1.0
184
  */
185
  public function getUploadDir()
186
  {
187
+ return $this->upload_dir;
188
  }
189
 
190
  /**
191
  * Returns the upload URL for this configuration class (common to all instances)
192
+ *
193
  * @author oncletom
194
  * @since 1.0
195
  * @version 1.0
197
  */
198
  public function getUploadUrl()
199
  {
200
+ return $this->upload_url;
201
  }
202
 
203
  /**
204
  * Build paths for various access
205
+ *
206
  * @author oncletom
207
  * @protected
208
  * @since 1.0
233
 
234
  $this->dirname = dirname($this->filename);
235
  $this->plugin_path = preg_replace('#(.+)([^/]+/[^/]+)$#sU', "$2", $this->filename);
236
+ $this->plugin_uri = WP_PLUGIN_URL.'/'.dirname($this->plugin_path);
237
  do_action($this->unix_name.'_configuration_setup_path', $this);
238
  }
239
 
240
+ /**
241
+ * Defines an upload directory
242
+ *
243
+ * @author oncletom
244
+ * @version 1.1
245
+ * @since 1.1
246
+ * @param String $upload_uri
247
+ */
248
+ public function setUploadDir($upload_dir)
249
+ {
250
+ return $this->upload_dir = $upload_dir;
251
+ }
252
+
253
  /**
254
  * Resolves global upload path as WP does not provide any clean and independant solution for that
255
+ *
256
  * It's barely based on the logic of `wp_upload_dir` function.
257
+ *
258
  * @author oncletom
259
  * @since 1.0
260
+ * @version 1.2
261
  * @return boolean
262
  */
263
  protected function setupPathGlobal()
264
  {
265
+ if (isset($this->upload_url))
266
  {
267
  return false;
268
  }
269
 
270
  $siteurl = get_option('siteurl');
271
  $upload_path = trim(get_option('upload_path'));
272
+ $subdir = $this->unix_name;
273
 
274
  if (defined('UPLOADS'))
275
  {
288
  : trailingslashit($siteurl).$upload_path;
289
  }
290
  }
291
+
292
  $uploads = apply_filters('upload_dir', array(
293
  'path' => $dir,
294
  'url' => $url,
295
+ 'subdir' => '/'.$subdir,
296
+ 'basedir' => trailingslashit($dir).$subdir,
297
+ 'baseurl' => trailingslashit($url).$subdir,
298
  'error' => false,
299
  ));
300
 
301
+ $this->setUploadDir($uploads['basedir']);
302
+ $this->setUploadUrl($uploads['baseurl']);
303
 
304
  return $uploads['error'] ? false : true;
305
  }
306
+
307
+ /**
308
+ * Defines an upload URL
309
+ *
310
+ * @author oncletom
311
+ * @version 1.1
312
+ * @since 1.1
313
+ * @param String $upload_url
314
+ */
315
+ public function setUploadUrl($upload_url)
316
+ {
317
+ return $this->upload_url = $upload_url;
318
+ }
319
  }
lib/vendor/plugin-toolkit/BasePlugin.class.php CHANGED
@@ -1,9 +1,9 @@
1
  <?php
2
  /**
3
  * Base plugin class to extend
4
- *
5
- * @version 1.1
6
- * @author oncletom
7
  * @package plugin-toolkit
8
  */
9
  abstract class WPPluginToolkitPlugin
@@ -13,9 +13,20 @@ abstract class WPPluginToolkitPlugin
13
 
14
  protected static $instances = array();
15
 
 
 
 
 
 
 
 
 
 
 
 
16
  /**
17
  * Plugin constructor
18
- *
19
  * @author oncletom
20
  * @since 1.0
21
  * @version 1.0
@@ -30,12 +41,13 @@ abstract class WPPluginToolkitPlugin
30
  spl_autoload_register(array($this, 'configureAutoload'));
31
  }
32
 
33
- do_action($this->configuration->getUnixName().'_plugin_construct', $this);
 
34
  }
35
 
36
  /**
37
  * Autoloads classes for this plugin
38
- *
39
  * @author oncletom
40
  * @return boolean
41
  * @param string $className
@@ -64,12 +76,12 @@ abstract class WPPluginToolkitPlugin
64
 
65
  /**
66
  * WordPress plugin builder
67
- *
68
  * @author oncletom
69
  * @static
70
  * @final
71
  * @since 1.0
72
- * @version 1.0
73
  * @param string $baseClassName
74
  * @param string $baseFileName
75
  * @param string $singleton_identifier[optional]
@@ -77,7 +89,11 @@ abstract class WPPluginToolkitPlugin
77
  */
78
  public final static function create($baseClassName, $baseFileName, $singleton_identifier = null)
79
  {
80
- require_once dirname(__FILE__).'/BaseConfiguration.class.php';
 
 
 
 
81
  require_once dirname($baseFileName).'/lib/Configuration.class.php';
82
 
83
  $class = $baseClassName.'Plugin';
@@ -99,7 +115,7 @@ abstract class WPPluginToolkitPlugin
99
 
100
  /**
101
  * Returns the current configuration
102
- *
103
  * @author oncletom
104
  * @since 1.0
105
  * @version 1.0
@@ -113,7 +129,7 @@ abstract class WPPluginToolkitPlugin
113
  /**
114
  * Retrieves the instance of an object
115
  * If no identifier is given, the first created instance is returned
116
- *
117
  * @author oncletom
118
  * @since 1.1
119
  * @version 1.0
@@ -134,7 +150,7 @@ abstract class WPPluginToolkitPlugin
134
  /**
135
  * Stores an instance of a created object
136
  * Self storage is so good :)
137
- *
138
  * @author oncletom
139
  * @since 1.1
140
  * @version 1.0
1
  <?php
2
  /**
3
  * Base plugin class to extend
4
+ *
5
+ * @version 1.2
6
+ * @author oncletom
7
  * @package plugin-toolkit
8
  */
9
  abstract class WPPluginToolkitPlugin
13
 
14
  protected static $instances = array();
15
 
16
+ /**
17
+ * Dispatches the plugin hooks, filters etc.
18
+ *
19
+ * This is basically the first thing done when the class is created.
20
+ *
21
+ * @author oncletom
22
+ * @since 1.0
23
+ * @abstract
24
+ */
25
+ abstract public function dispatch();
26
+
27
  /**
28
  * Plugin constructor
29
+ *
30
  * @author oncletom
31
  * @since 1.0
32
  * @version 1.0
41
  spl_autoload_register(array($this, 'configureAutoload'));
42
  }
43
 
44
+ load_plugin_textdomain($configuration->getUnixName(), $configuration->getI18nPath(), $configuration->getI18nFromPluginPath());
45
+ do_action($configuration->getUnixName().'_plugin_construct', $this);
46
  }
47
 
48
  /**
49
  * Autoloads classes for this plugin
50
+ *
51
  * @author oncletom
52
  * @return boolean
53
  * @param string $className
76
 
77
  /**
78
  * WordPress plugin builder
79
+ *
80
  * @author oncletom
81
  * @static
82
  * @final
83
  * @since 1.0
84
+ * @version 1.1
85
  * @param string $baseClassName
86
  * @param string $baseFileName
87
  * @param string $singleton_identifier[optional]
89
  */
90
  public final static function create($baseClassName, $baseFileName, $singleton_identifier = null)
91
  {
92
+ if (!class_exists('WPPluginToolkitConfiguration'))
93
+ {
94
+ require_once dirname(__FILE__).'/BaseConfiguration.class.php';
95
+ }
96
+
97
  require_once dirname($baseFileName).'/lib/Configuration.class.php';
98
 
99
  $class = $baseClassName.'Plugin';
115
 
116
  /**
117
  * Returns the current configuration
118
+ *
119
  * @author oncletom
120
  * @since 1.0
121
  * @version 1.0
129
  /**
130
  * Retrieves the instance of an object
131
  * If no identifier is given, the first created instance is returned
132
+ *
133
  * @author oncletom
134
  * @since 1.1
135
  * @version 1.0
150
  /**
151
  * Stores an instance of a created object
152
  * Self storage is so good :)
153
+ *
154
  * @author oncletom
155
  * @since 1.1
156
  * @version 1.0
readme.txt CHANGED
@@ -4,7 +4,7 @@ Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_i
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: 2.8.x
7
- Stable tag: 1.2
8
 
9
  Implementation of LESS (Leaner CSS) in order to make themes development easier.
10
 
@@ -47,6 +47,21 @@ The sole requirement is to use WordPress API and LESS convention: the `.less` ex
47
  1. Activate it through your WordPress plugins administration page
48
 
49
  == Changelog ==
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  = Version 1.2 =
51
 
52
  * added 2 new filters working on freshly transformed CSS
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: 2.8.x
7
+ Stable tag: 1.3
8
 
9
  Implementation of LESS (Leaner CSS) in order to make themes development easier.
10
 
47
  1. Activate it through your WordPress plugins administration page
48
 
49
  == Changelog ==
50
+ = Version 1.3 =
51
+
52
+ * moved stylesheet processing from `wp_print_styles` to `wp` action
53
+ * added new compiler actions and filters (same name each): `wp-less_compiler_construct` and `wp-less_compiler_parse`
54
+ * added `WPLessCompiler::getBuffer()` and `WPLessCompiler::setBuffer()` method, to enables hooking on LESS content, before being compiled into CSS
55
+ * removed `WPLessStyleseet::getTargetContent` method
56
+ * upgraded `plugin-toolkit`
57
+ * usage of `$WPLessPlugin->dispatch` instead of `$WPLessPlugin->registerHooks` to match the new `plugin-toolkit` signature
58
+ * no more configuration collision if usage of multiple plugins using `plugin-toolkit`
59
+ * lessphp: updated to [eac64a9d5a3bc3186a11c7130968388819f4c403](https://github.com/leafo/lessphp/commit/eac64a9d5a3bc3186a11c7130968388819f4c403) commit
60
+
61
+ = Version 1.2.1 =
62
+
63
+ * fixed the case where no stylesheet is queued (no warning anymore)
64
+
65
  = Version 1.2 =
66
 
67
  * added 2 new filters working on freshly transformed CSS