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