WP-LESS - Version 1.0

Version Description

Download this release

Release Info

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

Version 1.0

bootstrap.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
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.0
7
+ Author URI: http://case.oncle-tom.net/
8
+ Plugin URI: http://wordpress.org/extend/plugins/wp-less/
9
+
10
+ This plugin is released under version 3 of the GPL:
11
+ http://www.opensource.org/licenses/gpl-3.0.html
12
+ */
13
+
14
+ require dirname(__FILE__).'/lib/Plugin.class.php';
15
+ $WPLessPlugin = WPPluginToolkitPlugin::create('WPLess', __FILE__);
16
+
17
+ if (!is_admin())
18
+ {
19
+ do_action('wp-less_init', $WPLessPlugin);
20
+ add_action('wp_print_styles', array($WPLessPlugin, 'processStylesheets'));
21
+ }
22
+ else
23
+ {
24
+ do_action('wp-less_init_admin', $WPLessPlugin);
25
+ }
lib/Configuration.class.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WPLessConfiguration extends WPPluginToolkitConfiguration
4
+ {
5
+ /**
6
+ * Refers to the name of the plugin
7
+ */
8
+ const UNIX_NAME = 'wp-less';
9
+
10
+ /**
11
+ * Refers to the version of the plugin
12
+ */
13
+ const VERSION = '1.0';
14
+
15
+
16
+ protected function configure()
17
+ {
18
+ $this->configureOptions();
19
+ }
20
+
21
+ protected function configureOptions()
22
+ {
23
+
24
+ }
25
+ }
lib/Exception.class.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Basic Exception
4
+ *
5
+ * @author oncletom
6
+ * @package wp-less
7
+ * @subpackage lib
8
+ */
9
+ class WPLessException extends Exception
10
+ {
11
+
12
+ }
lib/Plugin.class.php ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
13
+ */
14
+ class WPLessPlugin extends WPPluginToolkitPlugin
15
+ {
16
+ /**
17
+ * @static
18
+ * @var Pattern used to match stylesheet files to process them as pure CSS
19
+ */
20
+ public static $match_pattern = '/\.less$/U';
21
+
22
+ /**
23
+ * Process all stylesheets to compile just in time
24
+ *
25
+ * @author oncletom
26
+ * @since 1.0
27
+ * @version 1.0
28
+ */
29
+ public function processStylesheets()
30
+ {
31
+ $styles = $this->getQueuedStylesToProcess();
32
+ $wp_styles = $this->getStyles();
33
+ $upload_dir = $this->configuration->getUploadDir();
34
+
35
+ if (empty($styles))
36
+ {
37
+ return;
38
+ }
39
+
40
+ if (!wp_mkdir_p($upload_dir))
41
+ {
42
+ throw new WPLessException(sprintf('The upload dir folder (`%s`) is not writable from %s.', $upload_dir, get_class($this)));
43
+ }
44
+
45
+ WPLessStylesheet::$upload_dir = $this->configuration->getUploadDir();
46
+ WPLessStylesheet::$upload_uri = $this->configuration->getUploadUrl();
47
+
48
+ foreach ($styles as $style_id)
49
+ {
50
+ $stylesheet = new WPLessStylesheet($wp_styles->registered[$style_id]);
51
+
52
+ if ($stylesheet->hasToCompile())
53
+ {
54
+ $stylesheet->save();
55
+ }
56
+
57
+ $wp_styles->registered[$style_id]->src = $stylesheet->getTargetUri();
58
+ }
59
+
60
+ do_action('wp-less_plugin_process_stylesheets', $styles);
61
+ }
62
+
63
+ /**
64
+ * Find any style to process
65
+ *
66
+ * @author oncletom
67
+ * @since 1.0
68
+ * @version 1.0
69
+ * @return array styles to process
70
+ */
71
+ protected function getQueuedStylesToProcess()
72
+ {
73
+ $wp_styles = $this->getStyles();
74
+ $to_process = array();
75
+
76
+ foreach ($wp_styles->queue as $style_id)
77
+ {
78
+ if (preg_match(self::$match_pattern, $wp_styles->registered[$style_id]->src))
79
+ {
80
+ $to_process[] = $style_id;
81
+ }
82
+ }
83
+
84
+ return apply_filters('wp-less_get_queued_styles_to_process', $to_process);
85
+ }
86
+
87
+ /**
88
+ * Returns WordPress Styles manager
89
+ *
90
+ * @author oncletom
91
+ * @uses WP_Styles
92
+ * @since 1.0
93
+ * @version 1.0
94
+ * @return WP_Styles styles instance
95
+ */
96
+ public function getStyles()
97
+ {
98
+ global $wp_styles;
99
+ return $wp_styles;
100
+ }
101
+ }
lib/Stylesheet.class.php ADDED
@@ -0,0 +1,203 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ require dirname(__FILE__).'/vendor/lessphp/lessc.inc.php';
3
+
4
+ /**
5
+ * Stylesheet management
6
+ *
7
+ * @author oncletom
8
+ * @package wp-less
9
+ * @subpackage lib
10
+ */
11
+ class WPLessStylesheet
12
+ {
13
+ protected $compiler,
14
+ $stylesheet;
15
+
16
+ protected $source_path,
17
+ $source_uri,
18
+ $target_path,
19
+ $target_uri;
20
+
21
+ public static $upload_dir,
22
+ $upload_uri;
23
+
24
+ /**
25
+ * Constructs the object, paths and all
26
+ *
27
+ * @author oncletom
28
+ * @since 1.0
29
+ * @version 1.0
30
+ * @throws WPLessException if something is not properly configured
31
+ * @param _WP_Dependency $stylesheet
32
+ */
33
+ public function __construct(_WP_Dependency $stylesheet)
34
+ {
35
+ $this->stylesheet = $stylesheet;
36
+
37
+ if (!self::$upload_dir || !self::$upload_uri)
38
+ {
39
+ throw new WPLessException('You must configure `upload_dir` and `upload_uri` static attributes before constructing this object.');
40
+ }
41
+
42
+ $this->configurePath();
43
+ do_action('wp-less_stylesheet_construct', $this);
44
+ }
45
+
46
+ /**
47
+ * Returns the computed path for a given dependency
48
+ *
49
+ * @author oncletom
50
+ * @since 1.0
51
+ * @version 1.0
52
+ * @return string
53
+ */
54
+ public function computeTargetPath()
55
+ {
56
+ $target_path = preg_replace('#^'.get_theme_root_uri().'#U', '', $this->stylesheet->src);
57
+ $target_path = preg_replace('/.less$/U', '', $target_path);
58
+
59
+ $target_path .= '.css';
60
+
61
+ return apply_filters('wp-less_stylesheet_compute_target_path', $target_path);
62
+ }
63
+
64
+ /**
65
+ * Configure paths for the stylesheet
66
+ * Since this moment, everything is configured to be usable
67
+ *
68
+ * @protected
69
+ * @author oncletom
70
+ * @since 1.0
71
+ * @version 1.0
72
+ */
73
+ protected function configurePath()
74
+ {
75
+ $target_file = $this->computeTargetPath();
76
+
77
+ $this->source_path = WP_CONTENT_DIR.preg_replace('#^'.WP_CONTENT_URL.'#U', '', $this->stylesheet->src);
78
+ $this->source_uri = $this->stylesheet->src;
79
+ $this->target_path = self::$upload_dir.$target_file;
80
+ $this->target_uri = self::$upload_uri.$target_file;
81
+ }
82
+
83
+ /**
84
+ * Returns source content (CSS to parse)
85
+ *
86
+ * @author oncletom
87
+ * @since 1.0
88
+ * @version 1.0
89
+ * @return string
90
+ */
91
+ public function getSourceContent()
92
+ {
93
+ return apply_filters('wp-less_stylesheet_source_content', file_get_contents($this->source_path));
94
+ }
95
+
96
+ /**
97
+ * Returns source path
98
+ *
99
+ * @author oncletom
100
+ * @since 1.0
101
+ * @version 1.0
102
+ * @return string
103
+ */
104
+ public function getSourcePath()
105
+ {
106
+ return $this->source_path;
107
+ }
108
+
109
+ /**
110
+ * Returns source URI
111
+ *
112
+ * @author oncletom
113
+ * @since 1.0
114
+ * @version 1.0
115
+ * @return string
116
+ */
117
+ public function getSourceUri()
118
+ {
119
+ return $this->source_uri;
120
+ }
121
+
122
+ /**
123
+ * Returns parsed CSS
124
+ *
125
+ * @author oncletom
126
+ * @since 1.0
127
+ * @version 1.0
128
+ * @return string
129
+ */
130
+ public function getTargetContent()
131
+ {
132
+ if (!$this->compiler)
133
+ {
134
+ $this->compiler = new lessc($this->getSourcePath());
135
+ }
136
+
137
+ return apply_filters('wp-less_stylesheet_target_content', $this->compiler->parse());
138
+ }
139
+
140
+ /**
141
+ * Returns target path
142
+ *
143
+ * @author oncletom
144
+ * @since 1.0
145
+ * @version 1.0
146
+ * @return string
147
+ */
148
+ public function getTargetPath()
149
+ {
150
+ return $this->target_path;
151
+ }
152
+
153
+ /**
154
+ * Returns target URI
155
+ *
156
+ * @author oncletom
157
+ * @since 1.0
158
+ * @version 1.0
159
+ * @return string
160
+ */
161
+ public function getTargetUri()
162
+ {
163
+ return $this->target_uri;
164
+ }
165
+
166
+ /**
167
+ * Tells if compilation is needed
168
+ *
169
+ * @author oncletom
170
+ * @since 1.0
171
+ * @version 1.0
172
+ * @return boolean
173
+ */
174
+ public function hasToCompile()
175
+ {
176
+ return !file_exists($this->getTargetPath()) || filemtime($this->getSourcePath()) > filemtime($this->getTargetPath());
177
+ }
178
+
179
+ /**
180
+ * Save the current stylesheet as a parsed css file
181
+ *
182
+ * @author oncletom
183
+ * @since 1.0
184
+ * @version 1.0
185
+ * @throws Exception in case of parsing went bad
186
+ */
187
+ public function save()
188
+ {
189
+ wp_mkdir_p(dirname($this->getTargetPath()));
190
+
191
+ try
192
+ {
193
+ do_action('wp-less_stylesheet_save_pre', $this);
194
+ lessc::ccompile($this->getSourcePath(), $this->getTargetPath());
195
+ chmod($this->getTargetPath(), 0666);
196
+ do_action('wp-less_stylesheet_save_post', $this);
197
+ }
198
+ catch(Exception $e)
199
+ {
200
+ wp_die($e->getMessage());
201
+ }
202
+ }
203
+ }
lib/vendor/lessphp/LICENSE ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ http://www.apache.org/licenses/
5
+
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
+
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
177
+ END OF TERMS AND CONDITIONS
178
+
179
+ Copyright 2009 Leaf Corcoran
180
+
lib/vendor/lessphp/README ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ lessphp v0.1.5
2
+ http://leafo.net/lessphp
3
+ ========================================
4
+
5
+ lessphp is a compiler for LESS written in php.
6
+
7
+
8
+ How to use in your php project
9
+ ========================================
10
+
11
+ Copy less.inc.php to your include directory and include it into your project.
12
+
13
+ There are a few ways to interface with the compiler. The easiest is to have it
14
+ compile a LESS file when the page is requested. The static function
15
+ less::ccompile, checked compile, will compile the input LESS file only when it
16
+ is newer than the output file.
17
+
18
+ try {
19
+ lessc::ccompile('input.less', 'output.css');
20
+ catch (exception $ex) {
21
+ exit($ex->getMessage());
22
+ }
23
+
24
+ Note that all failures with lessc are reported through exceptions.
25
+ If you need more control you can make your own instance of lessc.
26
+
27
+ $input = 'mystyle.less';
28
+
29
+ $lc = new lessc($input);
30
+
31
+ try {
32
+ file_put_contents('mystyle.css', $lc->parse());
33
+ } catch (exception $ex) { ... }
34
+
35
+ In addition to loading from file, you can also parse from a string like so:
36
+
37
+ $lc = new lessc();
38
+ $lesscode = 'body { ... }';
39
+ $out = $lc->parse($lesscode);
40
+
41
+
42
+
43
+ How to use from the command line
44
+ ========================================
45
+
46
+ An additional script has been included to use the compiler from the command
47
+ line. In the simplest invocation, you specify an input file and the compiled
48
+ css is written to standard out:
49
+
50
+ ~> plessc input.less > output.css
51
+
52
+ Using the -r flag, you can specify LESS code directly as an argument or, if
53
+ the argument is left off, from standard in:
54
+
55
+ ~> plessc -r "my less code here"
56
+
57
+ Finally, by using the -w flag you can watch a specified input file and have it
58
+ compile as needed to the output file
59
+
60
+ ~> plessc -w input-file output-file
61
+
62
+ Errors from watch mode are written to standard out.
63
+
64
+
65
+
66
+
lib/vendor/lessphp/docs/docs.html ADDED
@@ -0,0 +1,342 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3
+
4
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
5
+ <head>
6
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
7
+ <link rel="stylesheet" type="text/css" href="style.css" media="screen" />
8
+ <link rel="alternate" type="application/rss+xml" title="lessphp changelog feed" href="http://leafo.net/lessphp/feed/" />
9
+ <title>Documentation - lessphp</title>
10
+ </head>
11
+ <body>
12
+
13
+ <h1>Documentation - lessphp</h1>
14
+ <div class="content">
15
+
16
+ <ul id="nav">
17
+ <li><a href="#start">Getting Started</a></li>
18
+ <li><a href="#language">The Language</a>
19
+ <ul><li><a href="#vars">Abstract Properties (Variables)</a></li></ul>
20
+ <ul><li><a href="#pvalues">Property Values &amp; Expressions</a></li></ul>
21
+ <ul><li><a href="#nested">Nested Blocks</a></li></ul>
22
+ <ul><li><a href="#mixins">Mixins &amp; Namespace Access</a></li></ul>
23
+ <ul><li><a href="#ablocks">Abstract Blocks</a></li></ul>
24
+ <ul><li><a href="#args">Mixin Arguments</a></li></ul>
25
+ <ul><li><a href="#import">Import Statement</a></li></ul>
26
+ <ul><li><a href="#strings">String Mixins</a></li></ul>
27
+ <ul><li><a href="#misc">Miscellaneous</a></li></ul>
28
+ </li>
29
+ <li><a href="#interface">The Interface</a></li>
30
+ </ul>
31
+
32
+ <p class="important">This documentation is specific to the
33
+ <a href="http://leafo.net/lessphp/">php version of LESS, <strong>lessphp</strong></a>. <strong>lessphp</strong> is
34
+ a superset of LESS classic. Everything you can do in LESS classic will work
35
+ in lessphp but there are additional features unique to the php version.
36
+ </p>
37
+
38
+ <a name="start"></a>
39
+ <h2>Getting Started</h2>
40
+ <p>Download the latest version of <strong>lessphp</strong> <a href="http://leafo.net/lessphp/">here</a>.</p>
41
+
42
+ </div>
43
+
44
+ <a name="language"></a>
45
+ <h1>The Language</h1>
46
+ <div class="content">
47
+ <br />
48
+ <p><strong>lessphp</strong> is a data description language built on top of CSS. The two major components of the language are
49
+ blocks and property-value pairs. A block is a scope for a collection of property-value pairs.</p>
50
+
51
+ <p>Blocks and properties have special characteristics depending on how they are named.</p>
52
+
53
+ <p>It is important to realize that a block's state does not change over time. When a block is defined, all of its properties are constant. The best way to demonstrate this is to look at the following LESS snippet:</p>
54
+
55
+ <pre class="code">
56
+ body {
57
+ color: @val;
58
+ @val: blue;
59
+ }</pre>
60
+
61
+ <p>Because the state of the block is not changing over time, but constant after its creation,
62
+ the color property is printed with the value <code>blue</code>.
63
+
64
+ <a name="vars"></a>
65
+ <h2>Abstract Properties (Variables)</h2>
66
+ <p>Abstract properties are defined with a name starting with <code>@</code>. These types of properties are special in two ways: first, they are not included in the output of the compiler. Second, they can be easily accessed in the values of other properties just by writing their name. If a property is referenced but can not be found, a blank string is returned.</p>
67
+ <p>As a LESS programmer, it means that you can define a collection of hidden values that can be referenced in other locations.</p>
68
+
69
+ <pre class="code">
70
+ @mycolor: #fff;
71
+ body {
72
+ color: @mycolor;
73
+ }
74
+ pre {
75
+ color: @mycolor;
76
+ }</pre>
77
+
78
+ <p>Also take note that you can define abstract properties in the global scope.</p>
79
+
80
+ <a name="pvalues"></a>
81
+ <h2>Property Values & Expressions</h2>
82
+ <p>All properties have at least one value. The value is a list of expressions separated by spaces or commas.
83
+ An expression is any CSS value with optional mathematical operators applied to it. The operators only function
84
+ with number and color value types.<p>
85
+
86
+ <p>Operations on units will keep the unit of the rightmost value,
87
+ unless it is unit-less, then then leftmost unit will be kept for the result. See the following examples below.<p>
88
+
89
+ <pre class="code">body {
90
+ color: #001 + #abc; // evaulates to #aabbdd:
91
+ width: 3430px + 22; // evaluates to 3452px;
92
+ margin: (1.5em / 2) + 2px; // evaluates to 2.85px;
93
+ margin: 20 + 2px; // evaluates to 22px;
94
+ }</pre>
95
+
96
+ <p>It is important to notice that in the example above the output will print the margin property twice.
97
+ A single property name can hold more than one value; older values are not overwritten.</p>
98
+
99
+ <a name="nested"></a>
100
+ <h2>Nested Blocks</h2>
101
+ <p>Blocks can be nested inside of each other in order to achieve the same effect as listing out the
102
+ path of identifiers in CSS. This can help to increase organization of your LESS code and reduce
103
+ the amount of repeated tag names you have to type.</p>
104
+
105
+ <pre class="code">body {
106
+ a {
107
+ color: green
108
+ :hover {
109
+ color: blue;
110
+ }
111
+ }
112
+ }</pre>
113
+
114
+
115
+ <a name="mixins"></a>
116
+ <h2>Mixins & Namespace Access</h2>
117
+
118
+ <p>Any block can be "mixed" into the current block. This means that all properties and blocks
119
+ in the target block are brought into the current block. It is possible to achieve the same effect
120
+ with just CSS and HTML, but there are some additional features discussed below that make it worthwhile
121
+ to utilize LESS-style mixing.</p>
122
+ <p>The syntax is as follows:<p>
123
+
124
+ <pre class="code">
125
+ .myclass {
126
+ @fonts: Helvetica, Arial;
127
+ margin: 1.0em;
128
+ line-spacing: 150%;
129
+
130
+ a {
131
+ background-color: black;
132
+ }
133
+ }
134
+
135
+ pre {
136
+ .myclass;
137
+ font-family: @fonts; // uses the mixed in variable
138
+ }
139
+
140
+ div.notice {
141
+ .myclass;
142
+ }</pre>
143
+
144
+ <p>If you want to mix in a specific block within another block you can use the <code>&gt;</code> namespace operator.
145
+ Additionally you can pull out specific values of properties from blocks using the <code>[ ]</code> operator.
146
+
147
+ <pre class="code">// using .myclass from above
148
+ li {
149
+ .myclass &gt; a; // just mix in properties from 'a' tag in .myclass
150
+ fonts: .myclass[@fonts];
151
+ padding: .myclass['margin'];
152
+ }</pre>
153
+
154
+
155
+ <a name="ablocks"></a>
156
+ <h2>Abstract Blocks</h2>
157
+
158
+ <p>Abstract blocks are like any other blocks, but their names start with a <code>@</code>. Like abstract variables, they are not
159
+ included in the compiler's output. This allows you to do utilize mixins without adding any unused blocks to the output.
160
+ You can also use an abstract class to define a package of invisible, but extractable blocks and properties.<p>
161
+
162
+ <pre class="code">@mypackage {
163
+ .coolColors {
164
+ color: pink;
165
+ background-color: green;
166
+ }
167
+
168
+ .hotColors {
169
+ color: red;
170
+ background-color: orange;
171
+ }
172
+ }
173
+
174
+ p {
175
+ @mypackage &gt; .coolColors;
176
+ }
177
+
178
+ div {
179
+ @mypackage; // inserts both classes into this block
180
+ }</pre>
181
+
182
+ <p>It is possible to give an abstract block the same name as an abstract property; their names will not collide.
183
+ Block names and property names exist in different spaces.</p>
184
+
185
+
186
+ <a name="args"></a>
187
+ <h2>Mixin Arguments</h2>
188
+ <p>All blocks have the option of taking argument lists, and the arguments can have default values.</p>
189
+
190
+ <pre class="code">.myclass(@width: 200px;@radius) {
191
+ border-radius: @radius;
192
+ width: @width
193
+ }
194
+
195
+ @color(@color:red) { color: @color; } // this is valid
196
+
197
+ .first {
198
+ .myclass(300px; 2em);
199
+ @color(blue):
200
+ }
201
+
202
+ .second {
203
+ .myclass(;4px); // blank argument takes default value
204
+ }</pre>
205
+
206
+ <a name="import"></a>
207
+ <h2>Import Statement</h2>
208
+ <p>If you have multiple LESS files, you can combine them into a single CSS file during compilation using the
209
+ <code>@import</code> directive. LESS import uses the same syntax as CSS import. If it can find the file specified
210
+ then it will pull it into the compiler in place of the statement. If the file can't be found, the statement is
211
+ printed to the output. The following are all valid:</p>
212
+
213
+ <pre class="code">@import "file";
214
+ @import 'file.less';
215
+ @import url("file");
216
+ @import url('file');
217
+ @import url(file); </pre>
218
+
219
+ <p>Note that if it fails to find a file it will append <code>.less</code> to the filename and try again.
220
+ This means <code>@import 'somefile'</code> and <code>@import 'somefile.less'</code> will
221
+ both import the file <code>somefile.less</code>.
222
+
223
+
224
+ <a name="strings"></a>
225
+ <h2>String Mixins</h2>
226
+ <p>It is possible to access the value of an abstract property from a string using <code>{ }</code> operators.</p>
227
+
228
+ <pre class="code">@image_folder: darktheme;
229
+ .header {
230
+ background-image: url(/images/{@image_folder}/header.png);
231
+ }</pre>
232
+
233
+ <p>The <code>{ }</code> syntax will also work in any string value type, which is any text
234
+ wrapped in single or double quotes</p>
235
+
236
+
237
+ <a name="misc"></a>
238
+ <h2>Miscellaneous</h2>
239
+ <p>As mentioned before, all properties hold constant data. This includes abstract values. While it may be convenient to
240
+ think of them as variables, they don't have the ability to vary. For convenience, some tricks were implemented to
241
+ make self referencing properties evaluate in order as if they had changing state. Consider the following statement:</p>
242
+
243
+ <pre class="code">
244
+ @what: 1;
245
+ body {
246
+ @what: @what + 1;
247
+ @what: @what + 2;
248
+ .class {
249
+ @what: @what + 1;
250
+ width: @what;
251
+ }
252
+ }
253
+
254
+ #something {
255
+ @what: 200;
256
+ body > .class;
257
+ }</pre>
258
+
259
+ <p>In the output, <code>body .class</code> has width set to 5, and <code>#something</code> has width set to 201. It appears from that result that
260
+ the property <code>@what</code> is being incremented. But, as mentioned above, the name <code>@what</code> stores a series of unchanging values.
261
+ The values use delayed evaluation, so each value holds an equation.</p>
262
+
263
+ <p>What this means is that the values of the properties don't change while the block is parsed, only additional property-value pairs are added
264
+ to the block's definition.
265
+ When the block's is compiled into CSS, the equations and property references are solved using the data in the scope. Technically, it is
266
+ ambiguous what value to use for referencing a variable, because the single name can store multiple values.<p>
267
+
268
+ <p>The approach taken in <strong>lessphp</strong> is to use the most recent value. This gives the appearance that the block is parsed line by line.
269
+ In order to prevent infinite loops when a variable references itself, a single value in the set of all values for a given name can only be used once in
270
+ the chain of dereferencing.</p>
271
+
272
+
273
+
274
+
275
+ <pre class="code bnf" style="display: none"><b>property-value</b> <u>:=</u> <b>inner-list</b> , <b>property-value</b> <u>|</u> <b>inner-list</b>
276
+ <b>inner-list</b> <u>:=</u> <b>expression</b> <b>inner-list</b> <u>|</u> <b>expression</b>
277
+ <b>expression</b> <u>:=</u> <b>simple-value</b> <b>operator</b> <b>expression</b> <u>|</u> <b>simple-value</b>
278
+ <b>operator</b> <u>:=</u> + <u>|</u> - <u>|</u> * <u>|</u> /
279
+ <b>simple-value</b> <u>:=</u> <b>keyword</b> <u>|</u> <b>string</b> <u>|</u> <b>unit</b> <u>|</u>
280
+ </pre>
281
+
282
+ </div>
283
+
284
+ <a name="interface"></a>
285
+ <h1>The PHP Interface</h1><br />
286
+ <div class="content">
287
+ <p>There are a few ways to interface with the compiler. The easiest is to have it
288
+ compile a LESS file when the page is requested. The static function
289
+ <code>less::ccompile</code>, checked compile, will compile the inputed LESS file only when it
290
+ is newer than the output file.<p>
291
+
292
+ <pre class="code">
293
+ <span class="PreProc">require</span> '<span class="String">lessc.inc.php</span>';
294
+
295
+ <span class="Statement">try</span> <span class="Delimiter">{</span>
296
+ lessc<span class="Operator">::</span>ccompile<span class="Delimiter">(</span>'<span class="String">input.less</span>', '<span class="String">out.css</span>'<span class="Delimiter">)</span>;
297
+ <span class="Delimiter">}</span> <span class="Statement">catch</span> <span class="Delimiter">(</span><span class="Function">exception</span> <span class="Operator">$</span><span class="Identifier">ex</span><span class="Delimiter">)</span> <span class="Delimiter">{</span>
298
+ <span class="Statement">exit</span><span class="Delimiter">(</span>'<span class="String">lessc fatal error:&lt;br /&gt;</span>'<span class="Operator">.</span><span class="Operator">$</span><span class="Identifier">ex</span><span class="Type">-&gt;</span>getMessage<span class="Delimiter">())</span>;
299
+ <span class="Delimiter">}</span>
300
+ </pre>
301
+
302
+ <br />
303
+ <p>Note that all failures with lessc are reported through exceptions.
304
+ If you need more control then you can make your own instance of <code>lessc</code>.</p>
305
+ <pre class="code">
306
+ <span class="PreProc">require</span> '<span class="String">lessc.inc.php</span>';
307
+
308
+ <span class="Operator">$</span><span class="Identifier">less</span> <span class="Operator">=</span> <span class="PreProc">new</span> lessc<span class="Delimiter">(</span>'<span class="String">path/to/style.less</span>'<span class="Delimiter">)</span>;
309
+ <span class="Function">file_put_contents</span><span class="Delimiter">(</span>'<span class="String">path/to/style.css</span>', <span class="Operator">$</span><span class="Identifier">less</span><span class="Type">-&gt;</span>parse<span class="Delimiter">())</span>;
310
+ </pre>
311
+
312
+ <br />
313
+ <p>In addition to loading from a file, you can also parse from a string like so:</p>
314
+ <pre class="code">
315
+ <span class="PreProc">require</span> '<span class="String">lessc.inc.php</span>';
316
+
317
+ <span class="Operator">$</span><span class="Identifier">less</span> <span class="Operator">=</span> <span class="PreProc">new</span> lessc<span class="Delimiter">()</span>;
318
+ <span class="Operator">$</span><span class="Identifier">style</span> <span class="Operator">=</span> '<span class="String">&lt;style type=&quot;text/css&quot;&gt;</span>'<span class="Operator">.</span>
319
+ <span class="Operator">$</span><span class="Identifier">less</span><span class="Type">-&gt;</span>parse<span class="Delimiter">(</span>'<span class="String">.block { padding: 3 + 4px }</span>'<span class="Delimiter">)</span><span class="Operator">.</span>
320
+ '<span class="String">&lt;/style&gt;</span>';
321
+ </pre>
322
+
323
+
324
+ <h2>Import Directoy</h2>
325
+ <p>When using the <code>@import</code> directive, the compiler searches for files
326
+ in the <code>$importDir</code> public property of <code>lessc</code>. If the compiler is loaded with a filename
327
+ (either in the constructor or using <code>ccompile</code>) it will extract the directory and set that as the
328
+ import directory</p>
329
+
330
+
331
+
332
+ <br />
333
+ <br />
334
+ <hr />
335
+ <p class="foot">
336
+ <a href="http://leafo.net/lessphp/">http://leafo.net/lessphp</a> - Last updated August 6th 2009</p>
337
+
338
+ </div>
339
+
340
+ </body>
341
+ </html>
342
+
lib/vendor/lessphp/docs/style.css ADDED
@@ -0,0 +1,207 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ body {
3
+ margin: 0;
4
+ padding: 0;
5
+ font: 14px verdana, sans-serif;
6
+ }
7
+
8
+ #content, .content {
9
+ width: 966px;
10
+ margin-bottom: 1.0em;
11
+ }
12
+
13
+ h1,h2 {
14
+ padding: 0.5em;
15
+ font-family: calibri, helvetica, sans-serif;
16
+ margin: 0;
17
+ }
18
+
19
+ h1 {
20
+ background: #E7F3FB;
21
+ color: #182F51;
22
+ }
23
+
24
+ h2 {
25
+ color: #3AA6EE;
26
+ color: #1C97E8;
27
+ }
28
+
29
+
30
+ p {
31
+ margin: 0px;
32
+ padding: 0px 1.0em 0.5em 1.0em;
33
+ }
34
+
35
+ a:link, a:visited {
36
+ color: #4570B8;
37
+ border-bottom: 1px solid #4570B8;
38
+ text-decoration: none;
39
+ }
40
+
41
+ a:hover {
42
+ background-color: #E7F3FB;
43
+ }
44
+
45
+ ul {
46
+ margin: 0px;
47
+ }
48
+
49
+ ul#nav {
50
+ margin: 1.0em 0.0em;
51
+ }
52
+
53
+ hr {
54
+ border: 0;
55
+ height: 1px;
56
+ background-color: #afafaf;
57
+ margin: 0px 1em;
58
+ }
59
+
60
+ .foot {
61
+ color: #afafaf;
62
+ font-size: 80%;
63
+ text-align: center;
64
+ margin: 1.0em 0em;
65
+ }
66
+
67
+ .foot a {
68
+ text-decoration: none;
69
+ color: #777777;
70
+ border-bottom: 1px solid #777777;
71
+ }
72
+
73
+ .foot a:hover {
74
+ color: #000000;
75
+ background: none;
76
+ }
77
+
78
+ .demo {
79
+ border: 1px solid #777777;
80
+ margin: 1.0em;
81
+ }
82
+
83
+ table.demo {
84
+ width: 938px;
85
+ }
86
+
87
+ div.demo {
88
+ margin-bottom: 0;
89
+ padding: 0.5em 0em;
90
+ }
91
+
92
+ .demo .input, .demo .output {
93
+ width: 50%;
94
+ padding: 0.5em;
95
+ }
96
+
97
+ .demo .output {
98
+ background: #f4f4f4;
99
+ border-left: 1px dashed #777;
100
+ }
101
+
102
+ .demo textarea {
103
+ width: 99%;
104
+ }
105
+
106
+ .demo pre {
107
+ overflow-x: auto;
108
+ white-space: pre-wrap;
109
+ white-space: -moz-pre-wrap !important;
110
+ white-space: -pre-wrap;
111
+ white-space: -o-pre-wrap;
112
+ word-wrap: break-word;
113
+ }
114
+
115
+ .demo .buttons {
116
+ text-align: right;
117
+ }
118
+
119
+ .examples {
120
+ display: none;
121
+ }
122
+
123
+ .comments {
124
+ margin: 0.0em 1.0em;
125
+ padding: 0.5em;
126
+ border: 1px solid #777777;
127
+ }
128
+
129
+ #twitter {
130
+ border: 0;
131
+ text-decoration: none;
132
+ display: block;
133
+ position: absolute;
134
+ top:4px;
135
+ left:776px;
136
+ }
137
+
138
+ #twitter:hover {
139
+ background: none;
140
+ }
141
+
142
+ #twitter img {
143
+ border: 0;
144
+ }
145
+
146
+ p.important {
147
+ background-color: #FBE7E7;
148
+ background-color: #FFD6D7;
149
+ background-color: #FFDEE2;
150
+ padding: 1em 0.5em;
151
+ margin: 0.5em 0.0em 0.5em 0.5em;
152
+ }
153
+
154
+
155
+ .info {
156
+ background: #ECFBE7;
157
+ background: #E0FAD7;
158
+ -moz-border-radius: 8px;
159
+ -webkit-border-radius: 8px;
160
+ padding: 1em;
161
+ margin-left: 0.5em;
162
+ float: right;
163
+ width: 200px;
164
+ }
165
+
166
+ .info h3 {
167
+ padding: 0.5em;
168
+ margin: 0px;
169
+ font-size: 18px;
170
+ font-family: calibri, helvetica, sans-serif;
171
+ }
172
+
173
+ .info a:hover {
174
+ background: none;
175
+ }
176
+
177
+
178
+ /* vim stuff */
179
+ .Comment { color: #7c7c7c; }
180
+ .Conditional { color: #6699cc; }
181
+ .Statement { color: #6699cc; }
182
+ .String { color: #a8ff60; }
183
+ .Operator { color: #ffffff; }
184
+ .Identifier { color: #c6c5fe; }
185
+ .Function { color: #ffd2a7; }
186
+ .Type { color: #ffffb6; }
187
+ pre.code { font-family: monospace;
188
+ color: #f6f3e8; background-color: #000000;
189
+ background-color: #101C2D /*#090E16*/;
190
+ background-color: #101010;
191
+ padding: 1.0em 4.0em;
192
+ margin: 1.0em;
193
+ }
194
+ pre.code b {
195
+ color: #96cbfe;
196
+ }
197
+
198
+ pre.code u {
199
+ color: #a8ff60;
200
+ text-decoration: none;
201
+ }
202
+
203
+
204
+ .Delimiter { color: #00a0a0; }
205
+ .PreProc { color: #96cbfe; }
206
+
207
+
lib/vendor/lessphp/lessc.inc.php ADDED
@@ -0,0 +1,1238 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * less.inc.php
5
+ * v0.1.5
6
+ *
7
+ * less css compiler
8
+ * adapted from http://lesscss.org/docs.html
9
+ *
10
+ * leaf corcoran <leafo.net>
11
+ */
12
+
13
+
14
+ // future todo: define type names as constants
15
+
16
+ // todo: potential problem with parse tree search order:
17
+ //
18
+ // #default['color'] is an accessor, but if color is searched
19
+ // first then #def is matched as a color and it returns true and the head is
20
+ // moved to ault['color']; That is then seen as a second value in the list
21
+ //
22
+ // solution, enforce at least a space, } or {, or ; after match
23
+ // i can consume the spaces but not the symbols
24
+ // need more time to think about this, maybe leaving order requirement is good
25
+ //
26
+
27
+ class lessc
28
+ {
29
+ private $buffer;
30
+ private $out;
31
+ private $env = array(); // environment stack
32
+ private $count = 0; // temporary advance
33
+ private $level = 0;
34
+ private $line = 1; // the current line
35
+ private $precedence = array(
36
+ '+' => '0',
37
+ '-' => '0',
38
+ '*' => '1',
39
+ '/' => '1',
40
+ );
41
+
42
+ // delayed types
43
+ private $dtypes = array('expression', 'variable');
44
+
45
+ // the default set of units
46
+ private $units = array(
47
+ 'px', '%', 'in', 'cm', 'mm', 'em', 'ex', 'pt', 'pc', 's');
48
+
49
+ public $importDisabled = false;
50
+ public $importDir = '';
51
+
52
+ public function __construct($fname = null)
53
+ {
54
+ if ($fname) $this->load($fname);
55
+
56
+ $this->matchString =
57
+ '('.implode('|',array_map(array($this, 'preg_quote'), array_keys($this->precedence))).')';
58
+ }
59
+
60
+ // load a css from file
61
+ public function load($fname)
62
+ {
63
+ if (!is_file($fname)) {
64
+ throw new Exception('load error: failed to find '.$fname);
65
+ }
66
+ $pi = pathinfo($fname);
67
+
68
+ $this->file = $fname;
69
+ $this->importDir = $pi['dirname'].'/';
70
+ $this->buffer = file_get_contents($fname);
71
+ }
72
+
73
+ public function parse($text = null)
74
+ {
75
+ if ($text) $this->buffer = $text;
76
+ $this->reset();
77
+
78
+ $this->push(); // set up global scope
79
+ $this->set('__tags', array('')); // equivalent to 1 in tag multiplication
80
+
81
+ $this->buffer = $this->removeComments($this->buffer);
82
+
83
+ // trim whitespace on head
84
+ if (preg_match('/^\s+/', $this->buffer, $m)) {
85
+ $this->line += substr_count($m[0], "\n");
86
+ $this->buffer = ltrim($this->buffer);
87
+ }
88
+
89
+ while (false !== ($dat = $this->readChunk())) {
90
+ if (is_string($dat)) $this->out .= $dat;
91
+ }
92
+
93
+ if ($count = count($this->env) > 1) {
94
+ throw new
95
+ exception('Failed to parse '.(count($this->env) - 1).
96
+ ' unclosed block'.($count > 1 ? 's' : ''));
97
+ }
98
+
99
+ // print_r($this->env);
100
+ return $this->out;
101
+ }
102
+
103
+
104
+ // read a chunk off the head of the buffer
105
+ // chunks are separated by ; (in most cases)
106
+ private function readChunk()
107
+ {
108
+ if ($this->buffer == '') return false;
109
+
110
+ // todo: media directive
111
+ // media screen {
112
+ // blocks
113
+ // }
114
+
115
+ // a property
116
+ try {
117
+ $this->keyword($name)->literal(':')->propertyValue($value)->end()->advance();
118
+ $this->append($name, $value);
119
+
120
+ // we can print it right away if we are in global scope (makes no sense, but w/e)
121
+ if ($this->level > 1)
122
+ return true;
123
+ else
124
+ return $this->compileProperty($name,
125
+ array($this->getVal($name)))."\n";
126
+ } catch (exception $ex) {
127
+ $this->undo();
128
+ }
129
+
130
+ // entering a block
131
+ try {
132
+ $this->tags($tags);
133
+
134
+ // it can only be a function if there is one tag
135
+ if (count($tags) == 1) {
136
+ try {
137
+ $save = $this->count;
138
+ $this->argumentDef($args);
139
+ } catch (exception $ex) {
140
+ $this->count = $save;
141
+ }
142
+ }
143
+
144
+ $this->literal('{')->advance();
145
+ $this->push();
146
+
147
+ // move @ tags out of variable namespace!
148
+ foreach($tags as &$tag) {
149
+ if ($tag{0} == "@") $tag[0] = "%";
150
+ }
151
+
152
+ $this->set('__tags', $tags);
153
+ if (isset($args)) $this->set('__args', $args);
154
+
155
+ return true;
156
+ } catch (exception $ex) {
157
+ $this->undo();
158
+ }
159
+
160
+ // leaving a block
161
+ try {
162
+ $this->literal('}')->advance();
163
+
164
+ $tags = $this->multiplyTags();
165
+
166
+ $env = end($this->env);
167
+ $ctags = $env['__tags'];
168
+ unset($env['__tags']);
169
+
170
+ // insert the default arguments
171
+ if (isset($env['__args'])) {
172
+ foreach ($env['__args'] as $arg) {
173
+ if (isset($arg[1])) {
174
+ $this->prepend('@'.$arg[0], $arg[1]);
175
+ }
176
+ }
177
+ }
178
+
179
+ if (!empty($tags))
180
+ $out = $this->compileBlock($tags, $env);
181
+
182
+ $this->pop();
183
+
184
+ // make the block(s) available in the new current scope
185
+ foreach ($ctags as $t)
186
+ $this->set($t, $env);
187
+
188
+ return isset($out) ? $out : true;
189
+ } catch (exception $ex) {
190
+ $this->undo();
191
+ }
192
+
193
+ // look for import
194
+ try {
195
+ $this->import($url, $media)->advance();
196
+ if ($this->importDisabled) return "/* import is disabled */\n";
197
+
198
+ $full = $this->importDir.$url;
199
+
200
+ if (file_exists($file = $full) || file_exists($file = $full.'.less')) {
201
+ $this->buffer =
202
+ $this->removeComments(file_get_contents($file).";\n".$this->buffer);
203
+ return true;
204
+ }
205
+
206
+ return '@import url("'.$url.'")'.($media ? ' '.$media : '').";\n";
207
+ } catch (exception $ex) {
208
+ $this->undo();
209
+ }
210
+
211
+ // setting a variable
212
+ try {
213
+ $this->variable($name)->literal(':')->propertyValue($value)->end()->advance();
214
+ $this->append('@'.$name, $value);
215
+ return true;
216
+ } catch (exception $ex) {
217
+ $this->undo();
218
+ }
219
+
220
+
221
+ // look for a namespace/function to expand
222
+ // todo: this catches a lot of invalid syntax because tag
223
+ // consumer is liberal. This causes errors to be hidden
224
+ try {
225
+ $this->tags($tags, true, '>');
226
+
227
+ // move @ tags out of variable namespace
228
+ foreach($tags as &$tag) {
229
+ if ($tag{0} == "@") $tag[0] = "%";
230
+ }
231
+
232
+ // look for arguments
233
+ $save = $this->count;
234
+ try {
235
+ $this->argumentValues($argv);
236
+ } catch (exception $ex) { $this->count = $save; }
237
+
238
+ $this->end()->advance();
239
+
240
+ // find the final environment
241
+ $env = $this->get(array_shift($tags));
242
+
243
+ while ($sub = array_shift($tags)) {
244
+ if (isset($env[$sub])) // todo add a type check for environment
245
+ $env = $env[$sub];
246
+ else {
247
+ $env = null;
248
+ break;
249
+ }
250
+ }
251
+
252
+ if ($env == null) return true;
253
+
254
+ // if we have arguments then insert them
255
+ if (!empty($env['__args'])) {
256
+ foreach($env['__args'] as $arg) {
257
+ $name = $arg[0];
258
+ $value = is_array($argv) ? array_shift($argv) : null;
259
+ // copy default value if there isn't one supplied
260
+ if ($value == null && isset($arg[1]))
261
+ $value = $arg[1];
262
+
263
+ // if ($value == null) continue; // don't define so it can search up
264
+
265
+ // create new entry if var doesn't exist in scope
266
+ if (isset($env['@'.$name])) {
267
+ array_unshift($env['@'.$name], $value);
268
+ } else {
269
+ // new element
270
+ $env['@'.$name] = array($value);
271
+ }
272
+ }
273
+ }
274
+
275
+ // set all properties
276
+ ob_start();
277
+ foreach ($env as $name => $value) {
278
+ // if it is a block then render it
279
+ if (!isset($value[0])) {
280
+ $rtags = $this->multiplyTags(array($name));
281
+ echo $this->compileBlock($rtags, $value);
282
+ }
283
+
284
+ // copy everything except metadata
285
+ if (!preg_match('/^__/', $name)) {
286
+ // don't overwrite previous value
287
+ if ($this->get($name)) {
288
+ while ($tval = array_shift($value))
289
+ $this->append($name, $tval);
290
+ } else
291
+ $this->set($name, $value); // fixme: this should be append?
292
+ }
293
+ }
294
+
295
+ return ob_get_clean();
296
+ } catch (exception $ex) { $this->undo(); }
297
+
298
+ // ignore spare ;
299
+ try {
300
+ $this->literal(';')->advance();
301
+ return true;
302
+ } catch (exception $ex) { $this->undo(); }
303
+
304
+ // something failed
305
+ // print_r($this->env);
306
+ $this->match("(.*?)(\n|$)", $m);
307
+ throw new exception('Failed to parse line '.$this->line."\nOffending line: ".$m[1]);
308
+ }
309
+
310
+
311
+ /**
312
+ * consume functions
313
+ *
314
+ * they return instance of class so they can be chained
315
+ * any return vals are put into referenced arguments
316
+ */
317
+
318
+ // look for an import statement on the head of the buffer
319
+ private function import(&$url, &$media)
320
+ {
321
+ $this->literal('@import');
322
+ $save = $this->count;
323
+ try {
324
+ // todo: merge this with the keyword url('')
325
+ $this->literal('url(')->string($url)->literal(')');
326
+ } catch (exception $ex) {
327
+ $this->count = $save;
328
+ $this->string($url);
329
+ }
330
+
331
+ $this->to(';', $media);
332
+
333
+ return $this;
334
+ }
335
+
336
+ private function string(&$string, &$d = null)
337
+ {
338
+ try {
339
+ $this->literal('"', true);
340
+ $delim = '"';
341
+ } catch (exception $ex) {
342
+ $this->literal("'", true);
343
+ $delim = "'";
344
+ }
345
+
346
+ $this->to($delim, $string);
347
+
348
+ if (!isset($d)) $d = $delim;
349
+
350
+ return $this;
351
+ }
352
+
353
+ private function end()
354
+ {
355
+ try {
356
+ $this->literal(';');
357
+ } catch (exception $ex) {
358
+ // there is an end of block next, then no problem
359
+ if ($this->buffer{$this->count} != '}')
360
+ throw new exception('parse error: failed to find end');
361
+ }
362
+
363
+ return $this;
364
+ }
365
+
366
+ // gets a list of property values separated by ; between ( and )
367
+ private function argumentValues(&$args, $delim = ';')
368
+ {
369
+ $this->literal('(');
370
+
371
+ $values = array();
372
+ while (true){
373
+ try {
374
+ $this->propertyValue($values[])->literal(';');
375
+ } catch (exception $ex) { break; }
376
+ }
377
+
378
+ $this->literal(')');
379
+ $args = $values;
380
+
381
+ return $this;
382
+ }
383
+
384
+ // consume agument definition, variable names with optional value
385
+ private function argumentDef(&$args, $delim = ';')
386
+ {
387
+ $this->literal('(');
388
+
389
+ $values = array();
390
+ while (true) {
391
+ try {
392
+ $arg = array();
393
+ $this->variable($arg[]);
394
+ // look for a default value
395
+ try {
396
+ $this->literal(':')->propertyValue($value);
397
+ $arg[] = $value;
398
+ } catch (exception $ax) { }
399
+
400
+ $values[] = $arg;
401
+ $this->literal($delim);
402
+ } catch (exception $ex) {
403
+ break;
404
+ }
405
+ }
406
+
407
+ $this->literal(')');
408
+ $args = $values;
409
+
410
+ return $this;
411
+ }
412
+
413
+
414
+ // get a list of tags separated by commas
415
+ private function tags(&$tags, $simple = false, $delim = ',')
416
+ {
417
+ $tags = array();
418
+ while (1) {
419
+ $this->tag($tmp, $simple);
420
+ $tags[] = trim($tmp);
421
+
422
+ try { $this->literal($delim); }
423
+ catch (Exception $ex) { break; }
424
+ }
425
+
426
+ return $this;
427
+ }
428
+
429
+ // match a single tag, aka the block identifier
430
+ // $simple only match simple tags, no funny selectors allowed
431
+ // this accepts spaces so it can mis comments...
432
+ private function tag(&$tag, $simple = false)
433
+ {
434
+ if ($simple)
435
+ $chars = '^,:;{}\][>\(\)';
436
+ else
437
+ $chars = '^,;{}\(\)';
438
+
439
+ // can't start with a number
440
+ if (!$this->match('(['.$chars.'0-9]['.$chars.']*)', $m))
441
+ throw new exception('parse error: failed to parse tag');
442
+
443
+ $tag = trim($m[1]);
444
+
445
+ return $this;
446
+ }
447
+
448
+ // consume $what and following whitespace from the head of buffer
449
+ private function literal($what)
450
+ {
451
+ // if $what is one char we can speed things up
452
+ // fixme: throws a notice here when going over the len of the buffer
453
+ if ((strlen($what) == 1 && $what != $this->buffer{$this->count}) ||
454
+ !$this->match($this->preg_quote($what), $m))
455
+ {
456
+ throw new
457
+ Exception('parse error: failed to prase literal '.$what);
458
+ }
459
+ return $this;
460
+ }
461
+
462
+ // consume list of values for property
463
+ private function propertyValue(&$value)
464
+ {
465
+ $out = array();
466
+
467
+ while (1) {
468
+ try {
469
+ $this->expressionList($out[]);
470
+ $this->literal(','); }
471
+ catch (exception $ex) { break; }
472
+ }
473
+
474
+ if (!empty($out)) {
475
+ $out = array_map(array($this, 'compressValues'), $out);
476
+ $value = $this->compressValues($out, ', ');
477
+ }
478
+
479
+ return $this;
480
+ }
481
+
482
+ // evaluate a list of expressions separated by spaces
483
+ private function expressionList(&$vals)
484
+ {
485
+ $vals = array();
486
+ $this->expression($vals[]); // there should be at least one
487
+
488
+ while (1) {
489
+ try { $this->expression($tmp); }
490
+ catch (Exception $ex) { break; }
491
+
492
+ $vals[] = $tmp;
493
+ }
494
+
495
+ return $this;
496
+ }
497
+
498
+ // evaluate a group of values separated by operators
499
+ private function expression(&$result)
500
+ {
501
+ try {
502
+ $this->literal('(')->expression($exp)->literal(')');
503
+ $lhs = $exp;
504
+ } catch (exception $ex) {
505
+ $this->value($lhs);
506
+ }
507
+ $result = $this->expHelper($lhs, 0);
508
+ return $this;
509
+ }
510
+
511
+
512
+ // used to recursively love infix equation with proper operator order
513
+ private function expHelper($lhs, $minP)
514
+ {
515
+ // while there is an operator and the precedence is greater or equal to min
516
+ while ($this->match($this->matchString, $m) && $this->precedence[$m[1]] >= $minP) {
517
+ // check for subexp
518
+ try {
519
+ $this->literal('(')->expression($exp)->literal(')');
520
+ $rhs = $exp;
521
+ } catch (exception $ex) {
522
+ $this->value($rhs);
523
+ }
524
+
525
+ // find out if next up needs rhs
526
+ if ($this->peek($this->matchString, $mi) && $this->precedence[$mi[1]] > $minP) {
527
+ $rhs = $this->expHelper($rhs, $this->precedence[$mi[1]]);
528
+ }
529
+
530
+ // todo: find a way to precalculate non-delayed types
531
+ if (in_array($rhs[0], $this->dtypes) || in_array($lhs[0], $this->dtypes))
532
+ $lhs = array('expression', $m[1], $lhs, $rhs);
533
+ else
534
+ $lhs = $this->evaluate($m[1], $lhs, $rhs);
535
+ }
536
+ return $lhs;
537
+ }
538
+
539
+ // consume a css value:
540
+ // a keyword
541
+ // a variable (includes accessor);
542
+ // a color
543
+ // a unit (em, px, pt, %, mm), can also have no unit 4px + 3;
544
+ // a string
545
+ private function value(&$val)
546
+ {
547
+ try {
548
+ return $this->unit($val);
549
+ } catch (exception $ex) { /* $this->undo(); */ }
550
+
551
+ // look for accessor
552
+ // must be done before color
553
+ try {
554
+ $save = $this->count; // todo: replace with counter stack
555
+ $this->accessor($a);
556
+ $tmp = $this->get($a[0]); // get env
557
+ $val = end($tmp[$a[1]]); // get latest var
558
+
559
+ return $this;
560
+ } catch (exception $ex) { $this->count = $save; /* $this->undo(); */ }
561
+
562
+ try {
563
+ return $this->color($val);
564
+ } catch (exception $ex) { /* $this->undo(); */ }
565
+
566
+ try {
567
+ $save = $this->count;
568
+ $this->func($f);
569
+
570
+ $val = array('string', $f);
571
+
572
+ return $this;
573
+ } catch (exception $ex) { $this->count = $save; }
574
+
575
+ // a string
576
+ try {
577
+ $save = $this->count;
578
+ $this->string($tmp, $d);
579
+ $val = array('string', $d.$tmp.$d);
580
+ return $this;
581
+ } catch (exception $ex) { $this->count = $save; }
582
+
583
+ try {
584
+ $this->keyword($k);
585
+ $val = array('keyword', $k);
586
+ return $this;
587
+ } catch (exception $ex) { /* $this->undo(); */ }
588
+
589
+
590
+
591
+ // try to get a variable
592
+ try {
593
+ $this->variable($name);
594
+ $val = array('variable', '@'.$name);
595
+
596
+ return $this;
597
+ } catch (exception $ex) { /* $this->undo(); */ }
598
+
599
+ throw new exception('parse error: failed to find value');
600
+ }
601
+
602
+ // $units the allowed units
603
+ // number is always allowed (is this okay?)
604
+ private function unit(&$unit, $units = null)
605
+ {
606
+ if (!$units) $units = $this->units;
607
+
608
+ if (!$this->match('(-?[0-9]*(\.)?[0-9]+)('.implode('|', $units).')?', $m)) {
609
+ throw new exception('parse error: failed to consume unit');
610
+ }
611
+
612
+ // throw on a default unit
613
+ if (!isset($m[3])) $m[3] = 'number';
614
+
615
+ $unit = array($m[3], $m[1]);
616
+ return $this;
617
+ }
618
+
619
+ // todo: hue saturation lightness support (hsl)
620
+ // need a function to convert hsl to rgb
621
+ private function color(&$out)
622
+ {
623
+ $color = array('color');
624
+ if($this->match('(#([0-9a-f]{6})|#([0-9a-f]{3}))', $m)) {
625
+ if (isset($m[3])) {
626
+ $num = $m[3];
627
+ $width = 16;
628
+ } else {
629
+ $num = $m[2];
630
+ $width = 256;
631
+ }
632
+
633
+ $num = hexdec($num);
634
+ foreach(array(3,2,1) as $i) {
635
+ $t = $num % $width;
636
+ $num /= $width;
637
+
638
+ // todo: this is retarded
639
+ $color[$i] = $t * (256/$width) + $t * floor(16/$width);
640
+ }
641
+
642
+ } else {
643
+ $save = $this->count;
644
+ try {
645
+ $this->literal('rgb');
646
+
647
+ try {
648
+ $this->literal('a');
649
+ $count = 4;
650
+ } catch (exception $ex) {
651
+ $count = 3;
652
+ }
653
+
654
+ $this->literal('(');
655
+
656
+ // grab the numbers and format
657
+ foreach (range(1, $count) as $i) {
658
+ $this->unit($color[], array('%'));
659
+ if ($i != $count) $this->literal(',');
660
+
661
+ if ($color[$i][0] == '%')
662
+ $color[$i] = 255 * ($color[$i][1] / 100);
663
+ else
664
+ $color[$i] = $color[$i][1];
665
+ }
666
+
667
+ $this->literal(')');
668
+
669
+ $color = $this->fixColor($color);
670
+ } catch (exception $ex) {
671
+ $this->count = $save;
672
+
673
+ throw new exception('failed to find color');
674
+ }
675
+ }
676
+
677
+ $out = $color; // don't put things on out unless everything works out
678
+ return $this;
679
+ }
680
+
681
+ private function variable(&$var)
682
+ {
683
+ $this->literal('@')->keyword($var);
684
+ return $this;
685
+ }
686
+
687
+ private function accessor(&$var)
688
+ {
689
+ $this->tag($scope, true)->literal('[');
690
+
691
+ // see if it is a variable
692
+ try {
693
+ $this->variable($name);
694
+ $name = '@'.$name;
695
+ } catch (exception $ex) {
696
+ // try to see if it is a property
697
+ try {
698
+ $this->literal("'")->keyword($name)->literal("'");
699
+ } catch (exception $ex) {
700
+ throw new exception('parse error: failed to parse accessor');
701
+ }
702
+ }
703
+
704
+ $this->literal(']');
705
+
706
+ $var = array($scope, $name);
707
+
708
+ return $this;
709
+ }
710
+
711
+ // read a css function off the head of the buffer
712
+ private function func(&$func)
713
+ {
714
+ $this->keyword($fname)->literal('(')->to(')', $args);
715
+
716
+ $func = $fname.'('.$args.')';
717
+ return $this;
718
+ }
719
+
720
+ // read a keyword off the head of the buffer
721
+ private function keyword(&$word)
722
+ {
723
+ if (!$this->match('([\w_\-!"][\w\-_"]*)', $m)) {
724
+ throw new Exception('parse error: failed to find keyword');
725
+ }
726
+
727
+ $word = $m[1];
728
+ return $this;
729
+ }
730
+
731
+ // this ignores comments because it doesn't grab by token
732
+ private function to($what, &$out)
733
+ {
734
+ if (!$this->match('(.*?)'.$this->preg_quote($what), $m))
735
+ throw new exception('parse error: failed to consume to '.$what);
736
+
737
+ $out = $m[1];
738
+
739
+ return $this;
740
+ }
741
+
742
+
743
+ /**
744
+ * compile functions turn data into css code
745
+ */
746
+ private function compileBlock($rtags, $env)
747
+ {
748
+ // don't render functions
749
+ foreach ($rtags as $i => $tag) {
750
+ if (preg_match('/( |^)%/', $tag))
751
+ unset($rtags[$i]);
752
+ }
753
+ if (empty($rtags)) return '';
754
+
755
+ $props = 0;
756
+ // print all the properties
757
+ ob_start();
758
+ foreach ($env as $name => $value) {
759
+ // todo: change this, poor hack
760
+ // make a better name storage system!!! (value types are fine)
761
+ // but.. don't render special properties (blocks, vars, metadata)
762
+ if (isset($value[0]) && $name{0} != '@' && $name != '__args') {
763
+ echo $this->compileProperty($name, $value, 1)."\n";
764
+ $props++;
765
+ }
766
+ }
767
+ $list = ob_get_clean();
768
+
769
+ if ($props == 0) return true;
770
+
771
+ // do some formatting
772
+ if ($props == 1) $list = ' '.trim($list).' ';
773
+ return implode(", ", $rtags).' {'.($props > 1 ? "\n" : '').
774
+ $list."}\n";
775
+ }
776
+
777
+ private function compileProperty($name, $value, $level = 0)
778
+ {
779
+ // compile all repeated properties
780
+ foreach ($value as $v)
781
+ $props[] = str_repeat(' ', $level).
782
+ $name.':'.$this->compileValue($v).';';
783
+
784
+ return implode("\n", $props);
785
+ }
786
+
787
+
788
+ // todo replace render color
789
+ private function compileValue($value)
790
+ {
791
+ switch ($value[0]) {
792
+ case 'list':
793
+ return implode($value[1], array_map(array($this, 'compileValue'), $value[2]));
794
+ case 'expression':
795
+ return $this->compileValue($this->evaluate($value[1], $value[2], $value[3]));
796
+
797
+ case 'variable':
798
+ $tmp = $this->compileValue(
799
+ $this->getVal($value[1],
800
+ $this->pushName($value[1]))
801
+ );
802
+ $this->popName();
803
+
804
+ return $tmp;
805
+
806
+ case 'string':
807
+ // search for values inside the string
808
+ $replace = array();
809
+ if (preg_match_all('/{(@[\w-_][0-9\w-_]*)}/', $value[1], $m)) {
810
+ foreach($m[1] as $name) {
811
+ if (!isset($replace[$name]))
812
+ $replace[$name] = $this->compileValue(array('variable', $name));
813
+ }
814
+ }
815
+ foreach ($replace as $var=>$val)
816
+ $value[1] = str_replace('{'.$var.'}', $val, $value[1]);
817
+
818
+ return $value[1];
819
+
820
+ case 'color':
821
+ return $this->compileColor($value);
822
+
823
+ case 'keyword':
824
+ return $value[1];
825
+
826
+ case 'number':
827
+ return $value[1];
828
+
829
+ default: // assumed to be a unit
830
+ return $value[1].$value[0];
831
+ }
832
+ }
833
+
834
+
835
+ private function compileColor($c)
836
+ {
837
+ if (count($c) == 5) { // rgba
838
+ return 'rgba('.$c[1].','.$c[2].','.$c[3].','.$c[4].')';
839
+ }
840
+
841
+ $out = '#';
842
+ foreach (range(1,3) as $i)
843
+ $out .= ($c[$i] < 16 ? '0' : '').dechex($c[$i]);
844
+ return $out;
845
+ }
846
+
847
+
848
+ /**
849
+ * arithmetic evaluator and operators
850
+ */
851
+
852
+ // evalue an operator
853
+ // this is a messy function, probably a better way to do it
854
+ private function evaluate($op, $lft, $rgt)
855
+ {
856
+ $pushed = 0;
857
+ // figure out what expressions and variables are equal to
858
+ while (in_array($lft[0], $this->dtypes))
859
+ {
860
+ if ($lft[0] == 'expression')
861
+ $lft = $this->evaluate($lft[1], $lft[2], $lft[3]);
862
+ else if ($lft[0] == 'variable') {
863
+ $lft = $this->getVal($lft[1], $this->pushName($lft[1]), array('number', 0));
864
+ $pushed++;
865
+ }
866
+
867
+ }
868
+ while ($pushed != 0) { $this->popName(); $pushed--; }
869
+
870
+ while (in_array($rgt[0], $this->dtypes))
871
+ {
872
+ if ($rgt[0] == 'expression')
873
+ $rgt = $this->evaluate($rgt[1], $rgt[2], $rgt[3]);
874
+ else if ($rgt[0] == 'variable') {
875
+ $rgt = $this->getVal($rgt[1], $this->pushName($rgt[1]), array('number', 0));
876
+ $pushed++;
877
+ }
878
+ }
879
+ while ($pushed != 0) { $this->popName(); $pushed--; }
880
+
881
+ if ($lft [0] == 'color' && $rgt[0] == 'color') {
882
+ return $this->op_color_color($op, $lft, $rgt);
883
+ }
884
+
885
+ if ($lft[0] == 'color') {
886
+ return $this->op_color_number($op, $lft, $rgt);
887
+ }
888
+
889
+ if ($rgt[0] == 'color') {
890
+ return $this->op_number_color($op, $lft, $rgt);
891
+ }
892
+
893
+ // default number number
894
+ return $this->op_number_number($op, $lft, $rgt);
895
+ }
896
+
897
+ private function op_number_number($op, $lft, $rgt)
898
+ {
899
+ if ($rgt[0] == '%') $rgt[1] /= 100;
900
+
901
+ // figure out the type
902
+ if ($rgt[0] == 'number' || $rgt[0] == '%') $type = $lft[0];
903
+ else $type = $rgt[0];
904
+
905
+ $num = array($type);
906
+
907
+ switch($op) {
908
+ case '+':
909
+ $num[] = $lft[1] + $rgt[1];
910
+ break;
911
+ case '*':
912
+ $num[] = $lft[1] * $rgt[1];
913
+ break;
914
+ case '-':
915
+ $num[] = $lft[1] - $rgt[1];
916
+ break;
917
+ case '/';
918
+ if ($rgt[1] == 0) throw new exception("parse error: can't divide by zero");
919
+ $num[] = $lft[1] / $rgt[1];
920
+ break;
921
+ default:
922
+ throw new exception('parse error: number op number failed on op '.$op);
923
+ }
924
+
925
+ return $num;
926
+ }
927
+
928
+ private function op_number_color($op, $lft, $rgt)
929
+ {
930
+ if ($op == '+' || $op = '*') {
931
+ return $this->op_color_number($op, $rgt, $lft);
932
+ }
933
+ }
934
+
935
+ private function op_color_number($op, $lft, $rgt)
936
+ {
937
+ if ($rgt[0] == '%') $rgt[1] /= 100;
938
+
939
+ return $this->op_color_color($op, $lft,
940
+ array('color', $rgt[1], $rgt[1], $rgt[1]));
941
+ }
942
+
943
+ private function op_color_color($op, $lft, $rgt)
944
+ {
945
+ $newc = array('color');
946
+
947
+ switch ($op) {
948
+ case '+':
949
+ $newc[] = $lft[1] + $rgt[1];
950
+ $newc[] = $lft[2] + $rgt[2];
951
+ $newc[] = $lft[3] + $rgt[3];
952
+ break;
953
+ case '*':
954
+ $newc[] = $lft[1] * $rgt[1];
955
+ $newc[] = $lft[2] * $rgt[2];
956
+ $newc[] = $lft[3] * $rgt[3];
957
+ break;
958
+ case '-':
959
+ $newc[] = $lft[1] - $rgt[1];
960
+ $newc[] = $lft[2] - $rgt[2];
961
+ $newc[] = $lft[3] - $rgt[3];
962
+ break;
963
+ case '/';
964
+ if ($rgt[1] == 0 || $rgt[2] == 0 || $rgt[3] == 0)
965
+ throw new exception("parse error: can't divide by zero");
966
+ $newc[] = $lft[1] / $rgt[1];
967
+ $newc[] = $lft[2] / $rgt[2];
968
+ $newc[] = $lft[3] / $rgt[3];
969
+ break;
970
+ default:
971
+ throw new exception('parse error: color op number failed on op '.$op);
972
+ }
973
+ return $this->fixColor($newc);
974
+ }
975
+
976
+
977
+ /**
978
+ * functions for controlling the environment
979
+ */
980
+
981
+ // get something out of the env
982
+ // search from the head of the stack down
983
+ // $env what environment to search in
984
+ private function get($name, $env = null)
985
+ {
986
+ if (empty($env)) $env = $this->env;
987
+
988
+ for ($i = count($env) - 1; $i >= 0; $i--)
989
+ if (isset($env[$i][$name])) return $env[$i][$name];
990
+
991
+ return null;
992
+ }
993
+
994
+
995
+ // get the most recent value of a variable
996
+ // return default if it isn't found
997
+ // $skip is number of vars to skip
998
+ // todo: rename to getVar ?
999
+ private function getVal($name, $skip = 0, $default = array('keyword', ''))
1000
+ {
1001
+ $val = $this->get($name);
1002
+ if ($val == null) return $default;
1003
+
1004
+ $tmp = $this->env;
1005
+ while (!isset($tmp[count($tmp) - 1][$name])) array_pop($tmp);
1006
+ while ($skip > 0) {
1007
+ $skip--;
1008
+
1009
+ if (!empty($val)) {
1010
+ array_pop($val);
1011
+ }
1012
+
1013
+ if (empty($val)) {
1014
+ array_pop($tmp);
1015
+ $val = $this->get($name, $tmp);
1016
+ }
1017
+
1018
+ if (empty($val)) return $default;
1019
+ }
1020
+
1021
+ return end($val);
1022
+ }
1023
+
1024
+ // set something in the current env
1025
+ private function set($name, $value)
1026
+ {
1027
+ $this->env[count($this->env) - 1][$name] = $value;
1028
+ }
1029
+
1030
+ // append to array in the current env
1031
+ private function append($name, $value)
1032
+ {
1033
+ $this->env[count($this->env) - 1][$name][] = $value;
1034
+ }
1035
+
1036
+ // put on the front of the value
1037
+ private function prepend($name, $value)
1038
+ {
1039
+ if (isset($this->env[count($this->env) - 1][$name]))
1040
+ array_unshift($this->env[count($this->env) - 1][$name], $value);
1041
+ else $this->append($name, $value);
1042
+ }
1043
+
1044
+ // push a new environment stack
1045
+ private function push()
1046
+ {
1047
+ $this->level++;
1048
+ $this->env[] = array();
1049
+ }
1050
+
1051
+ // pop environment off the stack
1052
+ private function pop()
1053
+ {
1054
+ if ($this->level == 1)
1055
+ throw new exception('parse error: unexpected end of block');
1056
+
1057
+ $this->level--;
1058
+ return array_pop($this->env);
1059
+ }
1060
+
1061
+ /**
1062
+ * misc functions
1063
+ */
1064
+
1065
+ // functions for manipulating the expand stack
1066
+ private $expandStack = array();
1067
+
1068
+ // push name on expand stack and return its count
1069
+ // before being pushed
1070
+ private function pushName($name) {
1071
+ $count = array_count_values($this->expandStack);
1072
+ $count = isset($count[$name]) ? $count[$name] : 0;
1073
+
1074
+ $this->expandStack[] = $name;
1075
+ return $count;
1076
+ }
1077
+
1078
+ // pop name of expand stack and return it
1079
+ private function popName() {
1080
+ return array_pop($this->expandStack);
1081
+ }
1082
+
1083
+
1084
+ // remove comments from $text
1085
+ // todo: make it work for all functions, not just url
1086
+ private function removeComments($text)
1087
+ {
1088
+ $out = '';
1089
+
1090
+ while (!empty($text) &&
1091
+ preg_match('/^(.*?)("|\'|\/\/|\/\*|url\(|$)/is', $text, $m))
1092
+ {
1093
+ if (!trim($text)) break;
1094
+
1095
+ $out .= $m[1];
1096
+ $text = substr($text, strlen($m[0]));
1097
+
1098
+ switch ($m[2]) {
1099
+ case 'url(':
1100
+ preg_match('/^(.*?)(\)|$)/is', $text, $inner);
1101
+ $text = substr($text, strlen($inner[0]));
1102
+ $out .= $m[2].$inner[1].$inner[2];
1103
+ break;
1104
+ case '//':
1105
+ preg_match("/^(.*?)(\n|$)/is", $text, $inner);
1106
+ // give back the newline
1107
+ $text = substr($text, strlen($inner[0]) - 1);
1108
+ break;
1109
+ case '/*';
1110
+ preg_match("/^(.*?)(\*\/|$)/is", $text, $inner);
1111
+ $text = substr($text, strlen($inner[0]));
1112
+ break;
1113
+ case '"':
1114
+ case "'":
1115
+ preg_match("/^(.*?)(".$m[2]."|$)/is", $text, $inner);
1116
+ $text = substr($text, strlen($inner[0]));
1117
+ $out .= $m[2].$inner[1].$inner[2];
1118
+ break;
1119
+ }
1120
+ }
1121
+
1122
+ $this->count = 0;
1123
+ return $out;
1124
+ }
1125
+
1126
+
1127
+ // match text from the head while skipping $count characters
1128
+ // advances the temp counter if it succeeds
1129
+ private function match($regex, &$out, $eatWhitespace = true)
1130
+ {
1131
+ // if ($this->count > 100) echo '-- '.$this->count."\n";
1132
+ $r = '/^.{'.$this->count.'}'.$regex.($eatWhitespace ? '\s*' : '').'/is';
1133
+ if (preg_match($r, $this->buffer, $out)) {
1134
+ $this->count = strlen($out[0]);
1135
+ return true;
1136
+ }
1137
+
1138
+ return false;
1139
+
1140
+ }
1141
+
1142
+ private function peek($regex, &$out = null)
1143
+ {
1144
+ return preg_match('/^.{'.$this->count.'}'.$regex.'/is', $this->buffer, $out);
1145
+ }
1146
+
1147
+
1148
+ // compress a list of values into a single type
1149
+ // if the list contains one thing, then return that thing
1150
+ private function compressValues($values, $delim = ' ')
1151
+ {
1152
+ if (count($values) == 1) return $values[0];
1153
+ return array('list', $delim, $values);
1154
+ }
1155
+
1156
+ // make sure a color's components don't go out of bounds
1157
+ private function fixColor($c)
1158
+ {
1159
+ for ($i = 1; $i < 4; $i++) {
1160
+ if ($c[$i] < 0) $c[$i] = 0;
1161
+ if ($c[$i] > 255) $c[$i] = 255;
1162
+ $c[$i] = floor($c[$i]);
1163
+ }
1164
+ return $c;
1165
+ }
1166
+
1167
+ private function preg_quote($what)
1168
+ {
1169
+ // I don't know why it doesn't include it by default
1170
+ return preg_quote($what, '/');
1171
+ }
1172
+
1173
+ // reset all internal state to default
1174
+ private function reset()
1175
+ {
1176
+ $this->out = '';
1177
+ $this->env = array();
1178
+ $this->line = 1;
1179
+ $this->count = 0;
1180
+ }
1181
+
1182
+ // advance the buffer n places
1183
+ private function advance()
1184
+ {
1185
+ // this is probably slow
1186
+ $tmp = substr($this->buffer, 0, $this->count);
1187
+ $this->line += substr_count($tmp, "\n");
1188
+
1189
+ $this->buffer = substr($this->buffer, $this->count);
1190
+ $this->count = 0;
1191
+ }
1192
+
1193
+ // reset the temporary advance
1194
+ private function undo()
1195
+ {
1196
+ $this->count = 0;
1197
+ }
1198
+
1199
+ // find the cartesian product of all tags in stack
1200
+ private function multiplyTags($tags = array(' '), $d = null)
1201
+ {
1202
+ if ($d === null) $d = count($this->env) - 1;
1203
+
1204
+ $parents = $d == 0 ? $this->env[$d]['__tags']
1205
+ : $this->multiplyTags($this->env[$d]['__tags'], $d - 1);
1206
+
1207
+ $rtags = array();
1208
+ foreach ($parents as $p) {
1209
+ foreach ($tags as $t) {
1210
+ if ($t{0} == '@') continue; // skip functions
1211
+ $rtags[] = trim($p.($t{0} == ':' ? '' : ' ').$t);
1212
+ }
1213
+ }
1214
+
1215
+ return $rtags;
1216
+ }
1217
+
1218
+
1219
+ /**
1220
+ * static utility functions
1221
+ */
1222
+
1223
+ // compile to $in to $out if $in is newer than $out
1224
+ // returns true when it compiles, false otherwise
1225
+ public static function ccompile($in, $out)
1226
+ {
1227
+ if (!is_file($out) || filemtime($in) > filemtime($out)) {
1228
+ $less = new lessc($in);
1229
+ file_put_contents($out, $less->parse());
1230
+ return true;
1231
+ }
1232
+
1233
+ return false;
1234
+ }
1235
+ }
1236
+
1237
+
1238
+ ?>
lib/vendor/lessphp/plessc ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/php -q
2
+ <?php
3
+ //
4
+ // command line utility to compile less to stdout
5
+ // leaf corcoran <leafo.net>
6
+
7
+ require './lessc.inc.php';
8
+
9
+ $colors = array (
10
+ 'color' => array(
11
+ 'black' => 30,
12
+ 'red' => 31,
13
+ 'green' => 32,
14
+ 'brown' => 33,
15
+ 'blue' => 34,
16
+ 'purple' => 35,
17
+ 'cyan' => 36,
18
+ 'grey' => 37,
19
+ 'yellow' => 33
20
+ ),
21
+ 'style' => array(
22
+ 'normal' => 0,
23
+ 'bold' => 1,
24
+ 'light' => 1,
25
+ 'underscore' => 4,
26
+ 'underline' => 4,
27
+ 'blink' => 5,
28
+ 'inverse' => 6,
29
+ 'hidden' => 8,
30
+ 'concealed' => 8
31
+ ),
32
+ 'background' => array(
33
+ 'black' => 40,
34
+ 'red' => 41,
35
+ 'green' => 42,
36
+ 'brown' => 43,
37
+ 'yellow' => 43,
38
+ 'blue' => 44,
39
+ 'purple' => 45,
40
+ 'cyan' => 46,
41
+ 'grey' => 47
42
+ )
43
+ );
44
+
45
+ array_shift($argv);
46
+
47
+ // read less code from argument or from std in if there is no argument
48
+ if (false !== ($loc = array_search("-r", $argv))) {
49
+ unset($argv[$loc]);
50
+ $c = array_shift($argv);
51
+ if (!$c) {
52
+ while (!feof(STDIN)) {
53
+ $c .= fread(STDIN, 8192);
54
+ }
55
+ }
56
+
57
+ } else if (false !== ($loc = array_search("-w", $argv))) {
58
+ unset($argv[$loc]);
59
+
60
+ // see if we are sending notifications
61
+ $notif = array_search("-n", $argv);
62
+ if ($notif) unset($argv[$notif]);
63
+
64
+ if (!is_file($in = array_shift($argv)) ||
65
+ null == $out = array_shift($argv))
66
+ {
67
+ exit("\033[31m\033[1mFatal Error:\033[0m plessc -w infile outfile\n");
68
+ }
69
+
70
+ $fail_time = 0;
71
+ echo "Watching ".$in.($notif ? ' with notifications' : '').", press Ctrl + c to exit.\n";
72
+ while (1) {
73
+ if (!is_file($out) || (filemtime($in) > filemtime($out) && $fail_time != filemtime($in))) {
74
+ // try to compile it
75
+ try {
76
+ $l = new lessc($in);
77
+ $c = $l->parse();
78
+ echo "Writing updated file: ".$out."\n";
79
+ if (!file_put_contents($out, $c))
80
+ exit('Fatal Error: Failed to write file '.$out."\n");
81
+ } catch (exception $ex) {
82
+ echo "\nFatal Error:\n".str_repeat('=', 20)."\n".$ex->getMessage()."\n\n";
83
+ $fail_time = filemtime($in);
84
+
85
+ if ($notif) {
86
+ `notify-send -u critical "compile failed" "{$ex->getMessage()}"`;
87
+ }
88
+ }
89
+ }
90
+ sleep(1);
91
+ }
92
+
93
+ } else {
94
+ if (!is_file($fname = array_shift($argv)))
95
+ exit('Fatal Error: failed to find file: '.$fname."\n");
96
+ $c = file_get_contents($fname);
97
+
98
+ $pi = pathinfo($fname);
99
+ $importDir = $pi['dirname'].'/';
100
+ }
101
+
102
+ error_reporting(E_ALL);
103
+
104
+ $l = new lessc();
105
+ if ($importDir) $l->importDir = $importDir;
106
+ try {
107
+ echo $l->parse($c);
108
+ } catch (exception $ex) {
109
+ echo "Fatal Error:\n".str_repeat('=', 20)."\n".$ex->getMessage()."\n";
110
+ }
111
+
112
+ ?>
lib/vendor/plugin-toolkit/BaseConfiguration.class.php ADDED
@@ -0,0 +1,255 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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,
13
+ $base_filename,
14
+ $dirname,
15
+ $filename,
16
+ $i18n_path,
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
+ */
29
+ protected function configure()
30
+ {
31
+ $this->configureOptions();
32
+ do_action($this->unix_name.'_configuration_configure', $this);
33
+ }
34
+
35
+ /**
36
+ * Let the plugin configure its own options
37
+ *
38
+ * @author oncletom
39
+ * @abstract
40
+ * @protected
41
+ */
42
+ abstract protected function configureOptions();
43
+
44
+ /**
45
+ * Base constructor for a plugin configuration
46
+ *
47
+ * @author oncletom
48
+ * @since 1.0
49
+ * @version 1.0
50
+ * @param string $baseClassName
51
+ * @param string $baseFileName
52
+ */
53
+ public function __construct($baseClassName, $baseFileName)
54
+ {
55
+ $unix_name_pattern = $baseClassName.'Configuration::UNIX_NAME';
56
+ $this->unix_name = constant($unix_name_pattern);
57
+
58
+ if (is_null($this->unix_name))
59
+ {
60
+ throw new Exception(sprintf('%s has not been configured for %sConfiguration.', $unix_name_pattern, $baseClassName));
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
+
68
+ $this->configure();
69
+ do_action($this->unix_name.'_configuration_construct', $this);
70
+ }
71
+
72
+ /**
73
+ * Returns resolved plugin full path location
74
+ *
75
+ * @author oncletom
76
+ * @since 1.0
77
+ * @version 1.0
78
+ * @return string
79
+ */
80
+ public function getDirname()
81
+ {
82
+ return $this->dirname;
83
+ }
84
+
85
+ /**
86
+ * Returns resolved plugin full path filename
87
+ *
88
+ * @author oncletom
89
+ * @since 1.0
90
+ * @version 1.0
91
+ * @return string
92
+ */
93
+ public function getFilename()
94
+ {
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
104
+ * @return string
105
+ */
106
+ public function getPrefix()
107
+ {
108
+ return $this->base_class_name;
109
+ }
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
120
+ * @return string
121
+ */
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
133
+ * @return string
134
+ */
135
+ public function getUnixName()
136
+ {
137
+ return $this->unix_name;
138
+ }
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
146
+ * @return string
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
159
+ * @return string
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
172
+ * @version 1.0
173
+ * @param string $baseFileName
174
+ * @param string $unix_name
175
+ */
176
+ protected function setupPath($baseFileName, $unix_name)
177
+ {
178
+ $this->base_filename = $baseFileName;
179
+ $this->base_dirname = dirname($baseFileName);
180
+
181
+ /*
182
+ * Plugin & i18n path
183
+ */
184
+ if (function_exists('is_link') && is_link(WP_PLUGIN_DIR.'/'.$unix_name))
185
+ {
186
+ $this->filename = WP_PLUGIN_DIR.'/'.$unix_name.'/'.basename($this->base_filename);
187
+ $this->i18n_path = PLUGINDIR.'/'.$unix_name.'/i18n';
188
+ $this->i18n_path_from_plugins = $unix_name.'/i18n';
189
+ }
190
+ else
191
+ {
192
+ $this->filename = $this->base_filename;
193
+ $this->i18n_path = PLUGINDIR.'/'.dirname(plugin_basename($this->filename)).'/i18n';
194
+ $this->i18n_path_from_plugins = dirname(plugin_basename($this->filename)).'/i18n';
195
+ }
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
+ {
225
+ $dir = ABSPATH.UPLOADS;
226
+ $url = trailingslashit($siteurl).UPLOADS;
227
+ }
228
+ else
229
+ {
230
+ $dir = $upload_path ? $upload_path : WP_CONTENT_DIR.'/uploads';
231
+ $dir = path_join(ABSPATH, $dir);
232
+
233
+ if (!$url = get_option( 'upload_url_path'))
234
+ {
235
+ $url = empty($upload_path) or ($upload_path == $dir)
236
+ ? WP_CONTENT_URL . '/uploads'
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
+ }
lib/vendor/plugin-toolkit/BasePlugin.class.php ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Base plugin class to extend
4
+ *
5
+ * @author oncletom
6
+ * @package plugin-toolkit
7
+ */
8
+ abstract class WPPluginToolkitPlugin
9
+ {
10
+ protected $configuration;
11
+ protected static $autoload_configured = false;
12
+
13
+ /**
14
+ * Plugin constructor
15
+ *
16
+ * @author oncletom
17
+ * @since 1.0
18
+ * @version 1.0
19
+ * @param WPPluginToolkitConfiguration $configuration
20
+ */
21
+ public function __construct(WPPluginToolkitConfiguration $configuration)
22
+ {
23
+ $this->configuration = $configuration;
24
+
25
+ if (!self::$autoload_configured)
26
+ {
27
+ spl_autoload_register(array($this, 'configureAutoload'));
28
+ }
29
+
30
+ do_action($this->configuration->getUnixName().'_plugin_construct', $this);
31
+ }
32
+
33
+ /**
34
+ * Autoloads classes for this plugin
35
+ *
36
+ * @author oncletom
37
+ * @return boolean
38
+ * @param string $className
39
+ * @version 1.0
40
+ * @since 1.0
41
+ */
42
+ public function configureAutoload($className)
43
+ {
44
+ $prefix = $this->configuration->getPrefix();
45
+
46
+ if (!preg_match('/^'.$prefix.'/U', $className))
47
+ {
48
+ return;
49
+ }
50
+
51
+ $libdir = $this->configuration->getDirname().'/lib';
52
+ $path = preg_replace('/([A-Z]{1})/U', "/$1", str_replace($prefix, '', $className)).'.class.php';
53
+
54
+ if (file_exists($libdir.$path))
55
+ {
56
+ require $libdir.$path;
57
+ }
58
+
59
+ return false;
60
+ }
61
+
62
+ /**
63
+ * WordPress plugin builder
64
+ *
65
+ * @author oncletom
66
+ * @static
67
+ * @final
68
+ * @since 1.0
69
+ * @version 1.0
70
+ * @param string $baseClassName
71
+ * @param string $baseFileName
72
+ * @return $baseClassName+Plugin instance
73
+ */
74
+ public final static function create($baseClassName, $baseFileName)
75
+ {
76
+ require_once dirname(__FILE__).'/BaseConfiguration.class.php';
77
+ require_once dirname($baseFileName).'/lib/Configuration.class.php';
78
+
79
+ $class = $baseClassName.'Plugin';
80
+ $configuration = $baseClassName.'Configuration';
81
+
82
+ list($class, $configuration) = apply_filters('plugin-toolkit_create', array($class, $configuration));
83
+
84
+ $object = new $class(new $configuration($baseClassName, $baseFileName));
85
+ do_action('plugin-toolkit_create', $object);
86
+
87
+ return $object;
88
+ }
89
+
90
+ /**
91
+ * Returns the current configuration
92
+ *
93
+ * @author oncletom
94
+ * @since 1.0
95
+ * @version 1.0
96
+ * @return WPPluginToolkitConfiguration instance
97
+ */
98
+ public function getConfiguration()
99
+ {
100
+ return $this->configuration;
101
+ }
102
+ }
readme.txt ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === WP-LESS ===
2
+ Contributors: oncletom
3
+ Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=752034
4
+ Tags: dev, theme, themes, toolkit, plugin-toolkit, less, lesscss, lessc, lessphp, productivity, style, stylesheet, api
5
+ Requires at least: 2.8
6
+ Tested up to: 2.8.x
7
+ Stable tag: 1.0
8
+
9
+ Implementation of LESS (Leaner CSS) in order to make themes development easier.
10
+
11
+
12
+ == Description ==
13
+ [LESS](http://lesscss.org) is a templating language based on top of CSS. It provides numerous enhancements to speed up development and make its maintenance easier.
14
+
15
+ You can count on:
16
+
17
+ * Variables
18
+ * Mixins (inheritance of rules)
19
+ * Nested Rules (write less, do more)
20
+ * Accessors (inherit a value from a specific rule)
21
+ * Functions (logic operations for dynamic results)
22
+
23
+ The plugin lets you concentrate on what you need: coding CSS. Everything else is handled automatically, from cache management to user delivery.
24
+ Seriously.
25
+
26
+ The sole requirement is to use WordPress API and LESS convention: the `.less` extension.
27
+
28
+ **Minimal Requirements**: PHP 5.1.2 and WordPress 2.8.
29
+ **Relies on**: [LESSPHP 0.1.5](http://leafo.net/lessphp/), [plugin-toolkit](http://wordpress.org/extend/plugins/plugin-toolkit/).
30
+
31
+
32
+ == Installation ==
33
+
34
+ = Automatic =
35
+ 1. Search for the plugin name (`WP-LESS`)
36
+ 1. Click on the install button
37
+ 1. Activate it
38
+
39
+ = Manual =
40
+ 1. Download the latest stable archive of the plugin
41
+ 1. Unzip it in your plugin folder (by default, `wp-content/plugins`)
42
+ 1. Activate it through your WordPress plugins administration page
43
+
44
+ == Changelog ==
45
+ = Version 1.0 =
46
+
47
+ * implemented the forthcoming `plugin-toolkit` to speed up plugin development
48
+ * bundled lessphp 0.1.5
49
+ * implemented API to let you control the plugin the way you want
50
+ * just in time compilation with static file caching
51
+
52
+
53
+ == Frequently Asked Questions ==
54
+ = How do I transform a LESS file into CSS? =
55
+ Consider this bit of code to automatically enqueue your stylesheet from your theme (or plugin):
56
+ `wp_enqueue_style('mytheme', get_bloginfo('template_directory').'/style.css', array('blueprint'), '', 'screen, projection');`
57
+
58
+ To make it process by WP-LESS, switch to this way:
59
+ `wp_enqueue_style('mytheme', get_bloginfo('template_directory').'/style.less', array('blueprint'), '', 'screen, projection');`
60
+
61
+ You understood well: you just need to change the extension of the file.
62
+
63
+ = And if I don't use the wp_enqueue_style method? =
64
+ For the moment, it's the unique way to handle this.
65
+ Helpers will be provided soon to include LESS files in your templates in a fluent way.
66
+
67
+ = What if a *.less file contains only pure CSS? =
68
+ Nothing special. The LESS parser is fully compliant with CSS syntax.
69
+ It means nothing will be broken so don't worry.
70
+
71
+ = I'm a themer and I don't want to ask my users to activate this plugin =
72
+ It's a very good moto. Anyway, at the moment I don't provide any helper for this purpose.
73
+ It is planned for later.
74
+
75
+ If you are familiar with OOP programmation, you can make your way out of this. The source code is fully self-documented.
76
+
77
+ == Screenshots ==
78
+
79
+ 1. Sample of LESS to CSS conversion.
screenshot-1.png ADDED
Binary file