SiteOrigin Widgets Bundle - Version 1.18.1

Version Description

  • 20 April 2021 =
  • Reverted Less 3.1.0 update to restore compatibility for PHP versions below 7.1.
Download this release

Release Info

Developer SiteOrigin
Plugin Icon 128x128 SiteOrigin Widgets Bundle
Version 1.18.1
Comparing to
See all releases

Code changes from version 1.18.0 to 1.18.1

Files changed (63) hide show
  1. base/inc/lessc.inc.php +17 -15
  2. base/inc/lib/Less/Autoloader.php +17 -15
  3. base/inc/lib/Less/Cache.php +119 -93
  4. base/inc/lib/Less/Colors.php +151 -150
  5. base/inc/lib/Less/Configurable.php +69 -66
  6. base/inc/lib/Less/Environment.php +36 -27
  7. base/inc/lib/Less/Exception/Chunk.php +203 -203
  8. base/inc/lib/Less/Exception/Compiler.php +11 -11
  9. base/inc/lib/Less/Exception/Parser.php +125 -116
  10. base/inc/lib/Less/Functions.php +509 -507
  11. base/inc/lib/Less/Mime.php +19 -19
  12. base/inc/lib/Less/Output.php +8 -7
  13. base/inc/lib/Less/Output/Mapped.php +122 -119
  14. base/inc/lib/Less/Parser.php +903 -778
  15. base/inc/lib/Less/SourceMap/Base64VLQ.php +24 -24
  16. base/inc/lib/Less/SourceMap/Generator.php +365 -354
  17. base/inc/lib/Less/Tree.php +32 -26
  18. base/inc/lib/Less/Tree/Alpha.php +19 -16
  19. base/inc/lib/Less/Tree/Anonymous.php +12 -12
  20. base/inc/lib/Less/Tree/Assignment.php +10 -10
  21. base/inc/lib/Less/Tree/Attribute.php +17 -16
  22. base/inc/lib/Less/Tree/Call.php +52 -48
  23. base/inc/lib/Less/Tree/Color.php +80 -80
  24. base/inc/lib/Less/Tree/Comment.php +15 -15
  25. base/inc/lib/Less/Tree/Condition.php +16 -16
  26. base/inc/lib/Less/Tree/DefaultFunc.php +10 -10
  27. base/inc/lib/Less/Tree/DetachedRuleset.php +12 -11
  28. base/inc/lib/Less/Tree/Dimension.php +87 -84
  29. base/inc/lib/Less/Tree/Directive.php +35 -31
  30. base/inc/lib/Less/Tree/Element.php +24 -19
  31. base/inc/lib/Less/Tree/Expression.php +31 -29
  32. base/inc/lib/Less/Tree/Extend.php +13 -11
  33. base/inc/lib/Less/Tree/Import.php +108 -92
  34. base/inc/lib/Less/Tree/Javascript.php +4 -4
  35. base/inc/lib/Less/Tree/Keyword.php +12 -11
  36. base/inc/lib/Less/Tree/Media.php +65 -59
  37. base/inc/lib/Less/Tree/Mixin/Call.php +65 -56
  38. base/inc/lib/Less/Tree/Mixin/Definition.php +90 -82
  39. base/inc/lib/Less/Tree/NameValue.php +10 -8
  40. base/inc/lib/Less/Tree/Negative.php +13 -13
  41. base/inc/lib/Less/Tree/Operation.php +23 -21
  42. base/inc/lib/Less/Tree/Paren.php +10 -10
  43. base/inc/lib/Less/Tree/Quoted.php +30 -28
  44. base/inc/lib/Less/Tree/Rule.php +36 -33
  45. base/inc/lib/Less/Tree/Ruleset.php +217 -195
  46. base/inc/lib/Less/Tree/RulesetCall.php +8 -8
  47. base/inc/lib/Less/Tree/Selector.php +52 -44
  48. base/inc/lib/Less/Tree/UnicodeDescriptor.php +8 -7
  49. base/inc/lib/Less/Tree/Unit.php +49 -44
  50. base/inc/lib/Less/Tree/UnitConversions.php +14 -14
  51. base/inc/lib/Less/Tree/Url.php +25 -25
  52. base/inc/lib/Less/Tree/Value.php +17 -16
  53. base/inc/lib/Less/Tree/Variable.php +20 -19
  54. base/inc/lib/Less/Version.php +4 -4
  55. base/inc/lib/Less/Visitor.php +16 -13
  56. base/inc/lib/Less/Visitor/extendFinder.php +35 -30
  57. base/inc/lib/Less/Visitor/import.php +2 -0
  58. base/inc/lib/Less/Visitor/joinSelector.php +22 -20
  59. base/inc/lib/Less/Visitor/processExtends.php +142 -112
  60. base/inc/lib/Less/Visitor/toCSS.php +91 -79
  61. base/inc/lib/Less/VisitorReplacing.php +28 -23
  62. readme.txt +56 -47
  63. so-widgets-bundle.php +2 -2
base/inc/lessc.inc.php CHANGED
@@ -23,8 +23,7 @@ class SiteOrigin_LessC {
23
  private $formatterName;
24
  private $options = array();
25
 
26
- public function __construct( $lessc = null, $sourceName = null ) {
27
- }
28
 
29
  public function setImportDir( $dirs ) {
30
  $this->importDir = (array)$dirs;
@@ -39,8 +38,7 @@ class SiteOrigin_LessC {
39
  $this->formatterName = $name;
40
  }
41
 
42
- public function setPreserveComments( $preserve ) {
43
- }
44
 
45
  public function registerFunction( $name, $func ) {
46
  $this->libFunctions[$name] = $func;
@@ -50,7 +48,7 @@ class SiteOrigin_LessC {
50
  unset( $this->libFunctions[$name] );
51
  }
52
 
53
- public function setVariables( $variables ) {
54
  foreach ( $variables as $name => $value ) {
55
  $this->setVariable( $name, $value );
56
  }
@@ -66,7 +64,7 @@ class SiteOrigin_LessC {
66
 
67
  public function setOptions( $options ) {
68
  foreach ( $options as $name => $value ) {
69
- $this->setOption( $name, $value );
70
  }
71
  }
72
 
@@ -75,6 +73,7 @@ class SiteOrigin_LessC {
75
  }
76
 
77
  public function parse( $buffer, $presets = array() ) {
 
78
  $this->setVariables( $presets );
79
 
80
  $parser = new Less_Parser( $this->getOptions() );
@@ -82,7 +81,7 @@ class SiteOrigin_LessC {
82
  foreach ( $this->libFunctions as $name => $func ) {
83
  $parser->registerFunction( $name, $func );
84
  }
85
- $parser->parse( $buffer );
86
  if ( count( $this->registeredVars ) ) {
87
  $parser->ModifyVars( $this->registeredVars );
88
  }
@@ -91,14 +90,15 @@ class SiteOrigin_LessC {
91
  }
92
 
93
  protected function getOptions() {
94
- $options = array( 'relativeUrls' => false );
95
- switch ( $this->formatterName ) {
96
  case 'compressed':
97
  $options['compress'] = true;
98
  break;
99
  }
100
- if ( is_array( $this->options ) ) {
101
- $options = array_merge( $options, $this->options );
 
102
  }
103
  return $options;
104
  }
@@ -113,6 +113,7 @@ class SiteOrigin_LessC {
113
  }
114
 
115
  public function compile( $string, $name = null ) {
 
116
  $oldImport = $this->importDir;
117
  $this->importDir = (array)$this->importDir;
118
 
@@ -181,12 +182,13 @@ class SiteOrigin_LessC {
181
 
182
  public function checkedCompile( $in, $out ) {
183
  if ( !is_file( $out ) || filemtime( $in ) > filemtime( $out ) ) {
184
- $this->compileFile( $in, $out );
185
  return true;
186
  }
187
  return false;
188
  }
189
 
 
190
  /**
191
  * Execute lessphp on a .less file or a lessphp cache structure
192
  *
@@ -214,7 +216,7 @@ class SiteOrigin_LessC {
214
  if ( is_string( $in ) ) {
215
  $root = $in;
216
  } elseif ( is_array( $in ) and isset( $in['root'] ) ) {
217
- if ( $force or !isset( $in['files'] ) ) {
218
  // If we are forcing a recompile or if for some reason the
219
  // structure does not contain any file information we should
220
  // specify the root to trigger a rebuild.
@@ -239,7 +241,7 @@ class SiteOrigin_LessC {
239
  // If we have a root value which means we should rebuild.
240
  $out = array();
241
  $out['root'] = $root;
242
- $out['compiled'] = $this->compileFile( $root );
243
  $out['files'] = $this->allParsedFiles();
244
  $out['updated'] = time();
245
  return $out;
@@ -261,7 +263,7 @@ class SiteOrigin_LessC {
261
  if ( $less === null ) {
262
  $less = new self;
263
  }
264
- return $less->cachedCompile( $in, $force );
265
  }
266
 
267
  public function allParsedFiles() {
23
  private $formatterName;
24
  private $options = array();
25
 
26
+ public function __construct( $lessc=null, $sourceName=null ) {}
 
27
 
28
  public function setImportDir( $dirs ) {
29
  $this->importDir = (array)$dirs;
38
  $this->formatterName = $name;
39
  }
40
 
41
+ public function setPreserveComments( $preserve ) {}
 
42
 
43
  public function registerFunction( $name, $func ) {
44
  $this->libFunctions[$name] = $func;
48
  unset( $this->libFunctions[$name] );
49
  }
50
 
51
+ public function setVariables( $variables ){
52
  foreach ( $variables as $name => $value ) {
53
  $this->setVariable( $name, $value );
54
  }
64
 
65
  public function setOptions( $options ) {
66
  foreach ( $options as $name => $value ) {
67
+ $this->setOption( $name, $value);
68
  }
69
  }
70
 
73
  }
74
 
75
  public function parse( $buffer, $presets = array() ) {
76
+
77
  $this->setVariables( $presets );
78
 
79
  $parser = new Less_Parser( $this->getOptions() );
81
  foreach ( $this->libFunctions as $name => $func ) {
82
  $parser->registerFunction( $name, $func );
83
  }
84
+ $parser->parse($buffer);
85
  if ( count( $this->registeredVars ) ) {
86
  $parser->ModifyVars( $this->registeredVars );
87
  }
90
  }
91
 
92
  protected function getOptions() {
93
+ $options = array( 'relativeUrls'=>false );
94
+ switch( $this->formatterName ) {
95
  case 'compressed':
96
  $options['compress'] = true;
97
  break;
98
  }
99
+ if (is_array($this->options))
100
+ {
101
+ $options = array_merge($options, $this->options);
102
  }
103
  return $options;
104
  }
113
  }
114
 
115
  public function compile( $string, $name = null ) {
116
+
117
  $oldImport = $this->importDir;
118
  $this->importDir = (array)$this->importDir;
119
 
182
 
183
  public function checkedCompile( $in, $out ) {
184
  if ( !is_file( $out ) || filemtime( $in ) > filemtime( $out ) ) {
185
+ $this->compileFile($in, $out);
186
  return true;
187
  }
188
  return false;
189
  }
190
 
191
+
192
  /**
193
  * Execute lessphp on a .less file or a lessphp cache structure
194
  *
216
  if ( is_string( $in ) ) {
217
  $root = $in;
218
  } elseif ( is_array( $in ) and isset( $in['root'] ) ) {
219
+ if ( $force or ! isset( $in['files'] ) ) {
220
  // If we are forcing a recompile or if for some reason the
221
  // structure does not contain any file information we should
222
  // specify the root to trigger a rebuild.
241
  // If we have a root value which means we should rebuild.
242
  $out = array();
243
  $out['root'] = $root;
244
+ $out['compiled'] = $this->compileFile($root);
245
  $out['files'] = $this->allParsedFiles();
246
  $out['updated'] = time();
247
  return $out;
263
  if ( $less === null ) {
264
  $less = new self;
265
  }
266
+ return $less->cachedCompile($in, $force);
267
  }
268
 
269
  public function allParsedFiles() {
base/inc/lib/Less/Autoloader.php CHANGED
@@ -28,15 +28,15 @@ class Less_Autoloader {
28
  * @return void
29
  * @throws Exception If there was an error in registration
30
  */
31
- public static function register() {
32
- if ( self::$registered ) {
33
  return;
34
  }
35
 
36
- self::$libDir = dirname( __FILE__ );
37
 
38
- if ( false === spl_autoload_register( array( 'Less_Autoloader', 'loadClass' ) ) ) {
39
- throw new Exception( 'Unable to register Less_Autoloader::loadClass as an autoloading method.' );
40
  }
41
 
42
  self::$registered = true;
@@ -47,8 +47,8 @@ class Less_Autoloader {
47
  *
48
  * @return void
49
  */
50
- public static function unregister() {
51
- spl_autoload_unregister( array( 'Less_Autoloader', 'loadClass' ) );
52
  self::$registered = false;
53
  }
54
 
@@ -57,21 +57,23 @@ class Less_Autoloader {
57
  *
58
  * @param string $className The class to load
59
  */
60
- public static function loadClass( $className ) {
 
 
61
  // handle only package classes
62
- if ( strpos( $className, 'Less_' ) !== 0 ) {
63
  return;
64
  }
65
 
66
- $className = substr( $className, 5 );
67
- $fileName = self::$libDir . DIRECTORY_SEPARATOR . str_replace( '_', DIRECTORY_SEPARATOR, $className ) . '.php';
68
 
69
- if ( file_exists( $fileName ) ) {
70
  require $fileName;
71
  return true;
72
- } else {
73
- throw new Exception( 'file not loadable '.$fileName );
74
  }
75
  }
76
 
77
- }
28
  * @return void
29
  * @throws Exception If there was an error in registration
30
  */
31
+ public static function register(){
32
+ if( self::$registered ){
33
  return;
34
  }
35
 
36
+ self::$libDir = dirname(__FILE__);
37
 
38
+ if(false === spl_autoload_register(array('Less_Autoloader', 'loadClass'))){
39
+ throw new Exception('Unable to register Less_Autoloader::loadClass as an autoloading method.');
40
  }
41
 
42
  self::$registered = true;
47
  *
48
  * @return void
49
  */
50
+ public static function unregister(){
51
+ spl_autoload_unregister(array('Less_Autoloader', 'loadClass'));
52
  self::$registered = false;
53
  }
54
 
57
  *
58
  * @param string $className The class to load
59
  */
60
+ public static function loadClass($className){
61
+
62
+
63
  // handle only package classes
64
+ if(strpos($className, 'Less_') !== 0){
65
  return;
66
  }
67
 
68
+ $className = substr($className,5);
69
+ $fileName = self::$libDir . DIRECTORY_SEPARATOR . str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';
70
 
71
+ if(file_exists($fileName)){
72
  require $fileName;
73
  return true;
74
+ }else{
75
+ throw new Exception('file not loadable '.$fileName);
76
  }
77
  }
78
 
79
+ }
base/inc/lib/Less/Cache.php CHANGED
@@ -1,6 +1,6 @@
1
  <?php
2
 
3
- require_once dirname( __FILE__ ).'/Version.php';
4
 
5
  /**
6
  * Utility for handling the generation and caching of css files
@@ -9,7 +9,7 @@ require_once dirname( __FILE__ ).'/Version.php';
9
  * @subpackage cache
10
  *
11
  */
12
- class Less_Cache {
13
 
14
  // directory less.php can use for storing data
15
  public static $cache_dir = false;
@@ -23,6 +23,7 @@ class Less_Cache {
23
  // specifies the number of seconds after which data created by less.php will be seen as 'garbage' and potentially cleaned up
24
  public static $gc_lifetime = 604800;
25
 
 
26
  /**
27
  * Save and reuse the results of compiled less files.
28
  * The first call to Get() will generate css and save it.
@@ -33,92 +34,99 @@ class Less_Cache {
33
  * @param array $modify_vars Array of variables
34
  * @return string Name of the css file
35
  */
36
- public static function Get( $less_files, $parser_options = array(), $modify_vars = array() ) {
37
- // check $cache_dir
38
- if ( isset( $parser_options['cache_dir'] ) ) {
 
 
39
  Less_Cache::$cache_dir = $parser_options['cache_dir'];
40
  }
41
 
42
- if ( empty( Less_Cache::$cache_dir ) ) {
43
- throw new Exception( 'cache_dir not set' );
44
  }
45
 
46
- if ( isset( $parser_options['prefix'] ) ) {
47
  Less_Cache::$prefix = $parser_options['prefix'];
48
  }
49
 
50
- if ( empty( Less_Cache::$prefix ) ) {
51
- throw new Exception( 'prefix not set' );
52
  }
53
 
54
- if ( isset( $parser_options['prefix_vars'] ) ) {
55
  Less_Cache::$prefix_vars = $parser_options['prefix_vars'];
56
  }
57
 
58
- if ( empty( Less_Cache::$prefix_vars ) ) {
59
- throw new Exception( 'prefix_vars not set' );
60
  }
61
 
62
  self::CheckCacheDir();
63
  $less_files = (array)$less_files;
64
 
65
- // create a file for variables
66
- if ( !empty( $modify_vars ) ) {
67
- $lessvars = Less_Parser::serializeVars( $modify_vars );
68
- $vars_file = Less_Cache::$cache_dir . Less_Cache::$prefix_vars . sha1( $lessvars ) . '.less';
69
 
70
- if ( !file_exists( $vars_file ) ) {
71
- file_put_contents( $vars_file, $lessvars );
 
 
 
 
 
72
  }
73
 
74
- $less_files += array( $vars_file => '/' );
75
  }
76
 
 
77
  // generate name for compiled css file
78
- $hash = md5( json_encode( $less_files ) );
79
- $list_file = Less_Cache::$cache_dir . Less_Cache::$prefix . $hash . '.list';
80
 
81
- // check cached content
82
- if ( !isset( $parser_options['use_cache'] ) || $parser_options['use_cache'] === true ) {
83
- if ( file_exists( $list_file ) ) {
84
 
85
- self::ListFiles( $list_file, $list, $cached_name );
86
- $compiled_name = self::CompiledName( $list, $hash );
87
 
88
  // if $cached_name is the same as the $compiled name, don't regenerate
89
- if ( !$cached_name || $cached_name === $compiled_name ) {
90
 
91
- $output_file = self::OutputFile( $compiled_name, $parser_options );
92
 
93
- if ( $output_file && file_exists( $output_file ) ) {
94
- @touch( $list_file );
95
- return basename( $output_file ); // for backwards compatibility, we just return the name of the file
96
  }
97
  }
98
  }
99
  }
100
 
101
  $compiled = self::Cache( $less_files, $parser_options );
102
- if ( !$compiled ) {
103
  return false;
104
  }
105
 
106
  $compiled_name = self::CompiledName( $less_files, $hash );
107
- $output_file = self::OutputFile( $compiled_name, $parser_options );
 
108
 
109
- // save the file list
110
  $list = $less_files;
111
  $list[] = $compiled_name;
112
- $cache = implode( "\n", $list );
113
  file_put_contents( $list_file, $cache );
114
 
115
- // save the css
 
116
  file_put_contents( $output_file, $compiled );
117
 
118
- // clean up
 
119
  self::CleanCache();
120
 
121
- return basename( $output_file );
122
  }
123
 
124
  /**
@@ -129,26 +137,29 @@ class Less_Cache {
129
  * @param array $modify_vars Array of variables
130
  * @return string Name of the css file
131
  */
132
- public static function Regen( $less_files, $parser_options = array(), $modify_vars = array() ) {
133
  $parser_options['use_cache'] = false;
134
  return self::Get( $less_files, $parser_options, $modify_vars );
135
  }
136
 
137
- public static function Cache( &$less_files, $parser_options = array() ) {
 
 
138
  // get less.php if it exists
139
- $file = dirname( __FILE__ ) . '/Less.php';
140
- if ( file_exists( $file ) && !class_exists( 'Less_Parser' ) ) {
141
- require_once $file;
142
  }
143
 
144
  $parser_options['cache_dir'] = Less_Cache::$cache_dir;
145
- $parser = new Less_Parser( $parser_options );
 
146
 
147
  // combine files
148
- foreach ( $less_files as $file_path => $uri_or_less ) {
149
 
150
- // treat as less markup if there are newline characters
151
- if ( strpos( $uri_or_less, "\n" ) !== false ) {
152
  $parser->Parse( $uri_or_less );
153
  continue;
154
  }
@@ -158,17 +169,20 @@ class Less_Cache {
158
 
159
  $compiled = $parser->getCss();
160
 
 
161
  $less_files = $parser->allParsedFiles();
162
 
163
  return $compiled;
164
  }
165
 
166
- private static function OutputFile( $compiled_name, $parser_options ) {
167
- // custom output file
168
- if ( !empty( $parser_options['output'] ) ) {
169
 
170
- // relative to cache directory?
171
- if ( preg_match( '#[\\\\/]#', $parser_options['output'] ) ) {
 
 
 
 
 
172
  return $parser_options['output'];
173
  }
174
 
@@ -178,48 +192,54 @@ class Less_Cache {
178
  return Less_Cache::$cache_dir.$compiled_name;
179
  }
180
 
181
- private static function CompiledName( $files, $extrahash ) {
182
- // save the file list
183
- $temp = array( Less_Version::cache_version );
184
- foreach ( $files as $file ) {
185
- $temp[] = filemtime( $file )."\t".filesize( $file )."\t".$file;
 
 
186
  }
187
 
188
- return Less_Cache::$prefix.sha1( json_encode( $temp ).$extrahash ).'.css';
189
  }
190
 
191
- public static function SetCacheDir( $dir ) {
 
192
  Less_Cache::$cache_dir = $dir;
193
  self::CheckCacheDir();
194
  }
195
 
196
- public static function CheckCacheDir() {
197
- Less_Cache::$cache_dir = str_replace( '\\', '/', Less_Cache::$cache_dir );
198
- Less_Cache::$cache_dir = rtrim( Less_Cache::$cache_dir, '/' ).'/';
199
 
200
- if ( !file_exists( Less_Cache::$cache_dir ) ) {
201
- if ( !mkdir( Less_Cache::$cache_dir ) ) {
202
- throw new Less_Exception_Parser( 'Less.php cache directory couldn\'t be created: '.Less_Cache::$cache_dir );
 
 
 
203
  }
204
 
205
- } elseif ( !is_dir( Less_Cache::$cache_dir ) ) {
206
- throw new Less_Exception_Parser( 'Less.php cache directory doesn\'t exist: '.Less_Cache::$cache_dir );
207
 
208
- } elseif ( !is_writable( Less_Cache::$cache_dir ) ) {
209
- throw new Less_Exception_Parser( 'Less.php cache directory isn\'t writable: '.Less_Cache::$cache_dir );
210
 
211
  }
212
 
213
  }
214
 
 
215
  /**
216
  * Delete unused less.php files
217
  *
218
  */
219
- public static function CleanCache() {
220
  static $clean = false;
221
 
222
- if ( $clean || empty( Less_Cache::$cache_dir ) ) {
 
223
  return;
224
  }
225
 
@@ -227,63 +247,69 @@ class Less_Cache {
227
 
228
  // only remove files with extensions created by less.php
229
  // css files removed based on the list files
230
- $remove_types = array( 'lesscache' => 1,'list' => 1,'less' => 1,'map' => 1 );
231
 
232
- $files = scandir( Less_Cache::$cache_dir );
233
- if ( !$files ) {
234
  return;
235
  }
236
 
237
  $check_time = time() - self::$gc_lifetime;
238
- foreach ( $files as $file ) {
 
239
 
240
  // don't delete if the file wasn't created with less.php
241
- if ( strpos( $file, Less_Cache::$prefix ) !== 0 ) {
242
  continue;
243
  }
244
 
245
- $parts = explode( '.', $file );
246
- $type = array_pop( $parts );
 
247
 
248
- if ( !isset( $remove_types[$type] ) ) {
249
  continue;
250
  }
251
 
252
  $full_path = Less_Cache::$cache_dir . $file;
253
- $mtime = filemtime( $full_path );
254
 
255
  // don't delete if it's a relatively new file
256
- if ( $mtime > $check_time ) {
257
  continue;
258
  }
259
 
 
260
  // delete the list file and associated css file
261
- if ( $type === 'list' ) {
262
- self::ListFiles( $full_path, $list, $css_file_name );
263
- if ( $css_file_name ) {
264
  $css_file = Less_Cache::$cache_dir . $css_file_name;
265
- if ( file_exists( $css_file ) ) {
266
- unlink( $css_file );
267
  }
268
  }
269
  }
270
 
271
- unlink( $full_path );
272
  }
273
 
 
274
  }
275
 
 
276
  /**
277
  * Get the list of less files and generated css file from a list file
278
  *
279
  */
280
- static function ListFiles( $list_file, &$list, &$css_file_name ) {
281
- $list = explode( "\n", file_get_contents( $list_file ) );
 
282
 
283
- // pop the cached name that should match $compiled_name
284
- $css_file_name = array_pop( $list );
285
 
286
- if ( !preg_match( '/^' . Less_Cache::$prefix . '[a-f0-9]+\.css$/', $css_file_name ) ) {
287
  $list[] = $css_file_name;
288
  $css_file_name = false;
289
  }
1
  <?php
2
 
3
+ require_once( dirname(__FILE__).'/Version.php');
4
 
5
  /**
6
  * Utility for handling the generation and caching of css files
9
  * @subpackage cache
10
  *
11
  */
12
+ class Less_Cache{
13
 
14
  // directory less.php can use for storing data
15
  public static $cache_dir = false;
23
  // specifies the number of seconds after which data created by less.php will be seen as 'garbage' and potentially cleaned up
24
  public static $gc_lifetime = 604800;
25
 
26
+
27
  /**
28
  * Save and reuse the results of compiled less files.
29
  * The first call to Get() will generate css and save it.
34
  * @param array $modify_vars Array of variables
35
  * @return string Name of the css file
36
  */
37
+ public static function Get( $less_files, $parser_options = array(), $modify_vars = array() ){
38
+
39
+
40
+ //check $cache_dir
41
+ if( isset($parser_options['cache_dir']) ){
42
  Less_Cache::$cache_dir = $parser_options['cache_dir'];
43
  }
44
 
45
+ if( empty(Less_Cache::$cache_dir) ){
46
+ throw new Exception('cache_dir not set');
47
  }
48
 
49
+ if( isset($parser_options['prefix']) ){
50
  Less_Cache::$prefix = $parser_options['prefix'];
51
  }
52
 
53
+ if( empty(Less_Cache::$prefix) ){
54
+ throw new Exception('prefix not set');
55
  }
56
 
57
+ if( isset($parser_options['prefix_vars']) ){
58
  Less_Cache::$prefix_vars = $parser_options['prefix_vars'];
59
  }
60
 
61
+ if( empty(Less_Cache::$prefix_vars) ){
62
+ throw new Exception('prefix_vars not set');
63
  }
64
 
65
  self::CheckCacheDir();
66
  $less_files = (array)$less_files;
67
 
 
 
 
 
68
 
69
+ //create a file for variables
70
+ if( !empty($modify_vars) ){
71
+ $lessvars = Less_Parser::serializeVars($modify_vars);
72
+ $vars_file = Less_Cache::$cache_dir . Less_Cache::$prefix_vars . sha1($lessvars) . '.less';
73
+
74
+ if( !file_exists($vars_file) ){
75
+ file_put_contents($vars_file, $lessvars);
76
  }
77
 
78
+ $less_files += array($vars_file => '/');
79
  }
80
 
81
+
82
  // generate name for compiled css file
83
+ $hash = md5(json_encode($less_files));
84
+ $list_file = Less_Cache::$cache_dir . Less_Cache::$prefix . $hash . '.list';
85
 
86
+ // check cached content
87
+ if( !isset($parser_options['use_cache']) || $parser_options['use_cache'] === true ){
88
+ if( file_exists($list_file) ){
89
 
90
+ self::ListFiles($list_file, $list, $cached_name);
91
+ $compiled_name = self::CompiledName($list, $hash);
92
 
93
  // if $cached_name is the same as the $compiled name, don't regenerate
94
+ if( !$cached_name || $cached_name === $compiled_name ){
95
 
96
+ $output_file = self::OutputFile($compiled_name, $parser_options );
97
 
98
+ if( $output_file && file_exists($output_file) ){
99
+ @touch($list_file);
100
+ return basename($output_file); // for backwards compatibility, we just return the name of the file
101
  }
102
  }
103
  }
104
  }
105
 
106
  $compiled = self::Cache( $less_files, $parser_options );
107
+ if( !$compiled ){
108
  return false;
109
  }
110
 
111
  $compiled_name = self::CompiledName( $less_files, $hash );
112
+ $output_file = self::OutputFile($compiled_name, $parser_options );
113
+
114
 
115
+ //save the file list
116
  $list = $less_files;
117
  $list[] = $compiled_name;
118
+ $cache = implode("\n",$list);
119
  file_put_contents( $list_file, $cache );
120
 
121
+
122
+ //save the css
123
  file_put_contents( $output_file, $compiled );
124
 
125
+
126
+ //clean up
127
  self::CleanCache();
128
 
129
+ return basename($output_file);
130
  }
131
 
132
  /**
137
  * @param array $modify_vars Array of variables
138
  * @return string Name of the css file
139
  */
140
+ public static function Regen( $less_files, $parser_options = array(), $modify_vars = array() ){
141
  $parser_options['use_cache'] = false;
142
  return self::Get( $less_files, $parser_options, $modify_vars );
143
  }
144
 
145
+ public static function Cache( &$less_files, $parser_options = array() ){
146
+
147
+
148
  // get less.php if it exists
149
+ $file = dirname(__FILE__) . '/Less.php';
150
+ if( file_exists($file) && !class_exists('Less_Parser') ){
151
+ require_once($file);
152
  }
153
 
154
  $parser_options['cache_dir'] = Less_Cache::$cache_dir;
155
+ $parser = new Less_Parser($parser_options);
156
+
157
 
158
  // combine files
159
+ foreach($less_files as $file_path => $uri_or_less ){
160
 
161
+ //treat as less markup if there are newline characters
162
+ if( strpos($uri_or_less,"\n") !== false ){
163
  $parser->Parse( $uri_or_less );
164
  continue;
165
  }
169
 
170
  $compiled = $parser->getCss();
171
 
172
+
173
  $less_files = $parser->allParsedFiles();
174
 
175
  return $compiled;
176
  }
177
 
 
 
 
178
 
179
+ private static function OutputFile( $compiled_name, $parser_options ){
180
+
181
+ //custom output file
182
+ if( !empty($parser_options['output']) ){
183
+
184
+ //relative to cache directory?
185
+ if( preg_match('#[\\\\/]#',$parser_options['output']) ){
186
  return $parser_options['output'];
187
  }
188
 
192
  return Less_Cache::$cache_dir.$compiled_name;
193
  }
194
 
195
+
196
+ private static function CompiledName( $files, $extrahash ){
197
+
198
+ //save the file list
199
+ $temp = array(Less_Version::cache_version);
200
+ foreach($files as $file){
201
+ $temp[] = filemtime($file)."\t".filesize($file)."\t".$file;
202
  }
203
 
204
+ return Less_Cache::$prefix.sha1(json_encode($temp).$extrahash).'.css';
205
  }
206
 
207
+
208
+ public static function SetCacheDir( $dir ){
209
  Less_Cache::$cache_dir = $dir;
210
  self::CheckCacheDir();
211
  }
212
 
213
+ public static function CheckCacheDir(){
 
 
214
 
215
+ Less_Cache::$cache_dir = str_replace('\\','/',Less_Cache::$cache_dir);
216
+ Less_Cache::$cache_dir = rtrim(Less_Cache::$cache_dir,'/').'/';
217
+
218
+ if( !file_exists(Less_Cache::$cache_dir) ){
219
+ if( !mkdir(Less_Cache::$cache_dir) ){
220
+ throw new Less_Exception_Parser('Less.php cache directory couldn\'t be created: '.Less_Cache::$cache_dir);
221
  }
222
 
223
+ }elseif( !is_dir(Less_Cache::$cache_dir) ){
224
+ throw new Less_Exception_Parser('Less.php cache directory doesn\'t exist: '.Less_Cache::$cache_dir);
225
 
226
+ }elseif( !is_writable(Less_Cache::$cache_dir) ){
227
+ throw new Less_Exception_Parser('Less.php cache directory isn\'t writable: '.Less_Cache::$cache_dir);
228
 
229
  }
230
 
231
  }
232
 
233
+
234
  /**
235
  * Delete unused less.php files
236
  *
237
  */
238
+ public static function CleanCache(){
239
  static $clean = false;
240
 
241
+
242
+ if( $clean || empty(Less_Cache::$cache_dir) ){
243
  return;
244
  }
245
 
247
 
248
  // only remove files with extensions created by less.php
249
  // css files removed based on the list files
250
+ $remove_types = array('lesscache'=>1,'list'=>1,'less'=>1,'map'=>1);
251
 
252
+ $files = scandir(Less_Cache::$cache_dir);
253
+ if( !$files ){
254
  return;
255
  }
256
 
257
  $check_time = time() - self::$gc_lifetime;
258
+ foreach($files as $file){
259
+
260
 
261
  // don't delete if the file wasn't created with less.php
262
+ if( strpos($file,Less_Cache::$prefix) !== 0 ){
263
  continue;
264
  }
265
 
266
+ $parts = explode('.',$file);
267
+ $type = array_pop($parts);
268
+
269
 
270
+ if( !isset($remove_types[$type]) ){
271
  continue;
272
  }
273
 
274
  $full_path = Less_Cache::$cache_dir . $file;
275
+ $mtime = filemtime($full_path);
276
 
277
  // don't delete if it's a relatively new file
278
+ if( $mtime > $check_time ){
279
  continue;
280
  }
281
 
282
+
283
  // delete the list file and associated css file
284
+ if( $type === 'list' ){
285
+ self::ListFiles($full_path, $list, $css_file_name);
286
+ if( $css_file_name ){
287
  $css_file = Less_Cache::$cache_dir . $css_file_name;
288
+ if( file_exists($css_file) ){
289
+ unlink($css_file);
290
  }
291
  }
292
  }
293
 
294
+ unlink($full_path);
295
  }
296
 
297
+
298
  }
299
 
300
+
301
  /**
302
  * Get the list of less files and generated css file from a list file
303
  *
304
  */
305
+ static function ListFiles($list_file, &$list, &$css_file_name ){
306
+
307
+ $list = explode("\n",file_get_contents($list_file));
308
 
309
+ //pop the cached name that should match $compiled_name
310
+ $css_file_name = array_pop($list);
311
 
312
+ if( !preg_match('/^' . Less_Cache::$prefix . '[a-f0-9]+\.css$/',$css_file_name) ){
313
  $list[] = $css_file_name;
314
  $css_file_name = false;
315
  }
base/inc/lib/Less/Colors.php CHANGED
@@ -9,160 +9,161 @@
9
  class Less_Colors {
10
 
11
  public static $colors = array(
12
- 'aliceblue' => '#f0f8ff',
13
- 'antiquewhite' => '#faebd7',
14
- 'aqua' => '#00ffff',
15
- 'aquamarine' => '#7fffd4',
16
- 'azure' => '#f0ffff',
17
- 'beige' => '#f5f5dc',
18
- 'bisque' => '#ffe4c4',
19
- 'black' => '#000000',
20
- 'blanchedalmond' => '#ffebcd',
21
- 'blue' => '#0000ff',
22
- 'blueviolet' => '#8a2be2',
23
- 'brown' => '#a52a2a',
24
- 'burlywood' => '#deb887',
25
- 'cadetblue' => '#5f9ea0',
26
- 'chartreuse' => '#7fff00',
27
- 'chocolate' => '#d2691e',
28
- 'coral' => '#ff7f50',
29
- 'cornflowerblue' => '#6495ed',
30
- 'cornsilk' => '#fff8dc',
31
- 'crimson' => '#dc143c',
32
- 'cyan' => '#00ffff',
33
- 'darkblue' => '#00008b',
34
- 'darkcyan' => '#008b8b',
35
- 'darkgoldenrod' => '#b8860b',
36
- 'darkgray' => '#a9a9a9',
37
- 'darkgrey' => '#a9a9a9',
38
- 'darkgreen' => '#006400',
39
- 'darkkhaki' => '#bdb76b',
40
- 'darkmagenta' => '#8b008b',
41
- 'darkolivegreen' => '#556b2f',
42
- 'darkorange' => '#ff8c00',
43
- 'darkorchid' => '#9932cc',
44
- 'darkred' => '#8b0000',
45
- 'darksalmon' => '#e9967a',
46
- 'darkseagreen' => '#8fbc8f',
47
- 'darkslateblue' => '#483d8b',
48
- 'darkslategray' => '#2f4f4f',
49
- 'darkslategrey' => '#2f4f4f',
50
- 'darkturquoise' => '#00ced1',
51
- 'darkviolet' => '#9400d3',
52
- 'deeppink' => '#ff1493',
53
- 'deepskyblue' => '#00bfff',
54
- 'dimgray' => '#696969',
55
- 'dimgrey' => '#696969',
56
- 'dodgerblue' => '#1e90ff',
57
- 'firebrick' => '#b22222',
58
- 'floralwhite' => '#fffaf0',
59
- 'forestgreen' => '#228b22',
60
- 'fuchsia' => '#ff00ff',
61
- 'gainsboro' => '#dcdcdc',
62
- 'ghostwhite' => '#f8f8ff',
63
- 'gold' => '#ffd700',
64
- 'goldenrod' => '#daa520',
65
- 'gray' => '#808080',
66
- 'grey' => '#808080',
67
- 'green' => '#008000',
68
- 'greenyellow' => '#adff2f',
69
- 'honeydew' => '#f0fff0',
70
- 'hotpink' => '#ff69b4',
71
- 'indianred' => '#cd5c5c',
72
- 'indigo' => '#4b0082',
73
- 'ivory' => '#fffff0',
74
- 'khaki' => '#f0e68c',
75
- 'lavender' => '#e6e6fa',
76
- 'lavenderblush' => '#fff0f5',
77
- 'lawngreen' => '#7cfc00',
78
- 'lemonchiffon' => '#fffacd',
79
- 'lightblue' => '#add8e6',
80
- 'lightcoral' => '#f08080',
81
- 'lightcyan' => '#e0ffff',
82
- 'lightgoldenrodyellow' => '#fafad2',
83
- 'lightgray' => '#d3d3d3',
84
- 'lightgrey' => '#d3d3d3',
85
- 'lightgreen' => '#90ee90',
86
- 'lightpink' => '#ffb6c1',
87
- 'lightsalmon' => '#ffa07a',
88
- 'lightseagreen' => '#20b2aa',
89
- 'lightskyblue' => '#87cefa',
90
- 'lightslategray' => '#778899',
91
- 'lightslategrey' => '#778899',
92
- 'lightsteelblue' => '#b0c4de',
93
- 'lightyellow' => '#ffffe0',
94
- 'lime' => '#00ff00',
95
- 'limegreen' => '#32cd32',
96
- 'linen' => '#faf0e6',
97
- 'magenta' => '#ff00ff',
98
- 'maroon' => '#800000',
99
- 'mediumaquamarine' => '#66cdaa',
100
- 'mediumblue' => '#0000cd',
101
- 'mediumorchid' => '#ba55d3',
102
- 'mediumpurple' => '#9370d8',
103
- 'mediumseagreen' => '#3cb371',
104
- 'mediumslateblue' => '#7b68ee',
105
- 'mediumspringgreen' => '#00fa9a',
106
- 'mediumturquoise' => '#48d1cc',
107
- 'mediumvioletred' => '#c71585',
108
- 'midnightblue' => '#191970',
109
- 'mintcream' => '#f5fffa',
110
- 'mistyrose' => '#ffe4e1',
111
- 'moccasin' => '#ffe4b5',
112
- 'navajowhite' => '#ffdead',
113
- 'navy' => '#000080',
114
- 'oldlace' => '#fdf5e6',
115
- 'olive' => '#808000',
116
- 'olivedrab' => '#6b8e23',
117
- 'orange' => '#ffa500',
118
- 'orangered' => '#ff4500',
119
- 'orchid' => '#da70d6',
120
- 'palegoldenrod' => '#eee8aa',
121
- 'palegreen' => '#98fb98',
122
- 'paleturquoise' => '#afeeee',
123
- 'palevioletred' => '#d87093',
124
- 'papayawhip' => '#ffefd5',
125
- 'peachpuff' => '#ffdab9',
126
- 'peru' => '#cd853f',
127
- 'pink' => '#ffc0cb',
128
- 'plum' => '#dda0dd',
129
- 'powderblue' => '#b0e0e6',
130
- 'purple' => '#800080',
131
- 'red' => '#ff0000',
132
- 'rosybrown' => '#bc8f8f',
133
- 'royalblue' => '#4169e1',
134
- 'saddlebrown' => '#8b4513',
135
- 'salmon' => '#fa8072',
136
- 'sandybrown' => '#f4a460',
137
- 'seagreen' => '#2e8b57',
138
- 'seashell' => '#fff5ee',
139
- 'sienna' => '#a0522d',
140
- 'silver' => '#c0c0c0',
141
- 'skyblue' => '#87ceeb',
142
- 'slateblue' => '#6a5acd',
143
- 'slategray' => '#708090',
144
- 'slategrey' => '#708090',
145
- 'snow' => '#fffafa',
146
- 'springgreen' => '#00ff7f',
147
- 'steelblue' => '#4682b4',
148
- 'tan' => '#d2b48c',
149
- 'teal' => '#008080',
150
- 'thistle' => '#d8bfd8',
151
- 'tomato' => '#ff6347',
152
- 'turquoise' => '#40e0d0',
153
- 'violet' => '#ee82ee',
154
- 'wheat' => '#f5deb3',
155
- 'white' => '#ffffff',
156
- 'whitesmoke' => '#f5f5f5',
157
- 'yellow' => '#ffff00',
158
- 'yellowgreen' => '#9acd32'
159
  );
160
 
161
- public static function hasOwnProperty( $color ) {
162
- return isset( self::$colors[$color] );
163
  }
164
 
165
- public static function color( $color ) {
 
166
  return self::$colors[$color];
167
  }
168
 
9
  class Less_Colors {
10
 
11
  public static $colors = array(
12
+ 'aliceblue'=>'#f0f8ff',
13
+ 'antiquewhite'=>'#faebd7',
14
+ 'aqua'=>'#00ffff',
15
+ 'aquamarine'=>'#7fffd4',
16
+ 'azure'=>'#f0ffff',
17
+ 'beige'=>'#f5f5dc',
18
+ 'bisque'=>'#ffe4c4',
19
+ 'black'=>'#000000',
20
+ 'blanchedalmond'=>'#ffebcd',
21
+ 'blue'=>'#0000ff',
22
+ 'blueviolet'=>'#8a2be2',
23
+ 'brown'=>'#a52a2a',
24
+ 'burlywood'=>'#deb887',
25
+ 'cadetblue'=>'#5f9ea0',
26
+ 'chartreuse'=>'#7fff00',
27
+ 'chocolate'=>'#d2691e',
28
+ 'coral'=>'#ff7f50',
29
+ 'cornflowerblue'=>'#6495ed',
30
+ 'cornsilk'=>'#fff8dc',
31
+ 'crimson'=>'#dc143c',
32
+ 'cyan'=>'#00ffff',
33
+ 'darkblue'=>'#00008b',
34
+ 'darkcyan'=>'#008b8b',
35
+ 'darkgoldenrod'=>'#b8860b',
36
+ 'darkgray'=>'#a9a9a9',
37
+ 'darkgrey'=>'#a9a9a9',
38
+ 'darkgreen'=>'#006400',
39
+ 'darkkhaki'=>'#bdb76b',
40
+ 'darkmagenta'=>'#8b008b',
41
+ 'darkolivegreen'=>'#556b2f',
42
+ 'darkorange'=>'#ff8c00',
43
+ 'darkorchid'=>'#9932cc',
44
+ 'darkred'=>'#8b0000',
45
+ 'darksalmon'=>'#e9967a',
46
+ 'darkseagreen'=>'#8fbc8f',
47
+ 'darkslateblue'=>'#483d8b',
48
+ 'darkslategray'=>'#2f4f4f',
49
+ 'darkslategrey'=>'#2f4f4f',
50
+ 'darkturquoise'=>'#00ced1',
51
+ 'darkviolet'=>'#9400d3',
52
+ 'deeppink'=>'#ff1493',
53
+ 'deepskyblue'=>'#00bfff',
54
+ 'dimgray'=>'#696969',
55
+ 'dimgrey'=>'#696969',
56
+ 'dodgerblue'=>'#1e90ff',
57
+ 'firebrick'=>'#b22222',
58
+ 'floralwhite'=>'#fffaf0',
59
+ 'forestgreen'=>'#228b22',
60
+ 'fuchsia'=>'#ff00ff',
61
+ 'gainsboro'=>'#dcdcdc',
62
+ 'ghostwhite'=>'#f8f8ff',
63
+ 'gold'=>'#ffd700',
64
+ 'goldenrod'=>'#daa520',
65
+ 'gray'=>'#808080',
66
+ 'grey'=>'#808080',
67
+ 'green'=>'#008000',
68
+ 'greenyellow'=>'#adff2f',
69
+ 'honeydew'=>'#f0fff0',
70
+ 'hotpink'=>'#ff69b4',
71
+ 'indianred'=>'#cd5c5c',
72
+ 'indigo'=>'#4b0082',
73
+ 'ivory'=>'#fffff0',
74
+ 'khaki'=>'#f0e68c',
75
+ 'lavender'=>'#e6e6fa',
76
+ 'lavenderblush'=>'#fff0f5',
77
+ 'lawngreen'=>'#7cfc00',
78
+ 'lemonchiffon'=>'#fffacd',
79
+ 'lightblue'=>'#add8e6',
80
+ 'lightcoral'=>'#f08080',
81
+ 'lightcyan'=>'#e0ffff',
82
+ 'lightgoldenrodyellow'=>'#fafad2',
83
+ 'lightgray'=>'#d3d3d3',
84
+ 'lightgrey'=>'#d3d3d3',
85
+ 'lightgreen'=>'#90ee90',
86
+ 'lightpink'=>'#ffb6c1',
87
+ 'lightsalmon'=>'#ffa07a',
88
+ 'lightseagreen'=>'#20b2aa',
89
+ 'lightskyblue'=>'#87cefa',
90
+ 'lightslategray'=>'#778899',
91
+ 'lightslategrey'=>'#778899',
92
+ 'lightsteelblue'=>'#b0c4de',
93
+ 'lightyellow'=>'#ffffe0',
94
+ 'lime'=>'#00ff00',
95
+ 'limegreen'=>'#32cd32',
96
+ 'linen'=>'#faf0e6',
97
+ 'magenta'=>'#ff00ff',
98
+ 'maroon'=>'#800000',
99
+ 'mediumaquamarine'=>'#66cdaa',
100
+ 'mediumblue'=>'#0000cd',
101
+ 'mediumorchid'=>'#ba55d3',
102
+ 'mediumpurple'=>'#9370d8',
103
+ 'mediumseagreen'=>'#3cb371',
104
+ 'mediumslateblue'=>'#7b68ee',
105
+ 'mediumspringgreen'=>'#00fa9a',
106
+ 'mediumturquoise'=>'#48d1cc',
107
+ 'mediumvioletred'=>'#c71585',
108
+ 'midnightblue'=>'#191970',
109
+ 'mintcream'=>'#f5fffa',
110
+ 'mistyrose'=>'#ffe4e1',
111
+ 'moccasin'=>'#ffe4b5',
112
+ 'navajowhite'=>'#ffdead',
113
+ 'navy'=>'#000080',
114
+ 'oldlace'=>'#fdf5e6',
115
+ 'olive'=>'#808000',
116
+ 'olivedrab'=>'#6b8e23',
117
+ 'orange'=>'#ffa500',
118
+ 'orangered'=>'#ff4500',
119
+ 'orchid'=>'#da70d6',
120
+ 'palegoldenrod'=>'#eee8aa',
121
+ 'palegreen'=>'#98fb98',
122
+ 'paleturquoise'=>'#afeeee',
123
+ 'palevioletred'=>'#d87093',
124
+ 'papayawhip'=>'#ffefd5',
125
+ 'peachpuff'=>'#ffdab9',
126
+ 'peru'=>'#cd853f',
127
+ 'pink'=>'#ffc0cb',
128
+ 'plum'=>'#dda0dd',
129
+ 'powderblue'=>'#b0e0e6',
130
+ 'purple'=>'#800080',
131
+ 'red'=>'#ff0000',
132
+ 'rosybrown'=>'#bc8f8f',
133
+ 'royalblue'=>'#4169e1',
134
+ 'saddlebrown'=>'#8b4513',
135
+ 'salmon'=>'#fa8072',
136
+ 'sandybrown'=>'#f4a460',
137
+ 'seagreen'=>'#2e8b57',
138
+ 'seashell'=>'#fff5ee',
139
+ 'sienna'=>'#a0522d',
140
+ 'silver'=>'#c0c0c0',
141
+ 'skyblue'=>'#87ceeb',
142
+ 'slateblue'=>'#6a5acd',
143
+ 'slategray'=>'#708090',
144
+ 'slategrey'=>'#708090',
145
+ 'snow'=>'#fffafa',
146
+ 'springgreen'=>'#00ff7f',
147
+ 'steelblue'=>'#4682b4',
148
+ 'tan'=>'#d2b48c',
149
+ 'teal'=>'#008080',
150
+ 'thistle'=>'#d8bfd8',
151
+ 'tomato'=>'#ff6347',
152
+ 'turquoise'=>'#40e0d0',
153
+ 'violet'=>'#ee82ee',
154
+ 'wheat'=>'#f5deb3',
155
+ 'white'=>'#ffffff',
156
+ 'whitesmoke'=>'#f5f5f5',
157
+ 'yellow'=>'#ffff00',
158
+ 'yellowgreen'=>'#9acd32'
159
  );
160
 
161
+ public static function hasOwnProperty($color) {
162
+ return isset(self::$colors[$color]);
163
  }
164
 
165
+
166
+ public static function color($color) {
167
  return self::$colors[$color];
168
  }
169
 
base/inc/lib/Less/Configurable.php CHANGED
@@ -1,66 +1,69 @@
1
- <?php
2
-
3
- /**
4
- * Configurable
5
- *
6
- * @package Less
7
- * @subpackage Core
8
- */
9
- abstract class Less_Configurable {
10
-
11
- /**
12
- * Array of options
13
- *
14
- * @var array
15
- */
16
- protected $options = array();
17
-
18
- /**
19
- * Array of default options
20
- *
21
- * @var array
22
- */
23
- protected $defaultOptions = array();
24
-
25
- /**
26
- * Set options
27
- *
28
- * If $options is an object it will be converted into an array by called
29
- * it's toArray method.
30
- *
31
- * @throws Exception
32
- * @param array|object $options
33
- *
34
- */
35
- public function setOptions( $options ) {
36
- $options = array_intersect_key( $options, $this->defaultOptions );
37
- $this->options = array_merge( $this->defaultOptions, $this->options, $options );
38
- }
39
-
40
- /**
41
- * Get an option value by name
42
- *
43
- * If the option is empty or not set a NULL value will be returned.
44
- *
45
- * @param string $name
46
- * @param mixed $default Default value if confiuration of $name is not present
47
- * @return mixed
48
- */
49
- public function getOption( $name, $default = null ) {
50
- if ( isset( $this->options[$name] ) ) {
51
- return $this->options[$name];
52
- }
53
- return $default;
54
- }
55
-
56
- /**
57
- * Set an option
58
- *
59
- * @param string $name
60
- * @param mixed $value
61
- */
62
- public function setOption( $name, $value ) {
63
- $this->options[$name] = $value;
64
- }
65
-
66
- }
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Configurable
5
+ *
6
+ * @package Less
7
+ * @subpackage Core
8
+ */
9
+ abstract class Less_Configurable {
10
+
11
+ /**
12
+ * Array of options
13
+ *
14
+ * @var array
15
+ */
16
+ protected $options = array();
17
+
18
+ /**
19
+ * Array of default options
20
+ *
21
+ * @var array
22
+ */
23
+ protected $defaultOptions = array();
24
+
25
+
26
+ /**
27
+ * Set options
28
+ *
29
+ * If $options is an object it will be converted into an array by called
30
+ * it's toArray method.
31
+ *
32
+ * @throws Exception
33
+ * @param array|object $options
34
+ *
35
+ */
36
+ public function setOptions($options){
37
+ $options = array_intersect_key($options,$this->defaultOptions);
38
+ $this->options = array_merge($this->defaultOptions, $this->options, $options);
39
+ }
40
+
41
+
42
+ /**
43
+ * Get an option value by name
44
+ *
45
+ * If the option is empty or not set a NULL value will be returned.
46
+ *
47
+ * @param string $name
48
+ * @param mixed $default Default value if confiuration of $name is not present
49
+ * @return mixed
50
+ */
51
+ public function getOption($name, $default = null){
52
+ if(isset($this->options[$name])){
53
+ return $this->options[$name];
54
+ }
55
+ return $default;
56
+ }
57
+
58
+
59
+ /**
60
+ * Set an option
61
+ *
62
+ * @param string $name
63
+ * @param mixed $value
64
+ */
65
+ public function setOption($name, $value){
66
+ $this->options[$name] = $value;
67
+ }
68
+
69
+ }
base/inc/lib/Less/Environment.php CHANGED
@@ -1,14 +1,15 @@
1
  <?php
2
 
 
3
  /**
4
  * Environment
5
  *
6
  * @package Less
7
  * @subpackage environment
8
  */
9
- class Less_Environment {
10
 
11
- // public $paths = array(); // option - unmodified - paths to search for imports on
12
  //public static $files = array(); // list of files that have been imported, used for import-once
13
  //public $rootpath; // option - rootpath to append to URL's
14
  //public static $strictImports = null; // option -
@@ -20,6 +21,7 @@ class Less_Environment {
20
 
21
  public $importMultiple = false; // whether we are currently importing multiple copies
22
 
 
23
  /**
24
  * @var array
25
  */
@@ -50,13 +52,15 @@ class Less_Environment {
50
  */
51
  public $functions = array();
52
 
53
- public function Init() {
 
 
54
  self::$parensStack = 0;
55
  self::$tabLevel = 0;
56
  self::$lastRule = false;
57
  self::$mixin_stack = 0;
58
 
59
- if ( Less_Parser::$options['compress'] ) {
60
 
61
  Less_Environment::$_outputMap = array(
62
  ',' => ',',
@@ -68,11 +72,11 @@ class Less_Environment {
68
  '~' => '~',
69
  '>' => '>',
70
  '|' => '|',
71
- '^' => '^',
72
- '^^' => '^^'
73
  );
74
 
75
- } else {
76
 
77
  Less_Environment::$_outputMap = array(
78
  ',' => ', ',
@@ -84,27 +88,30 @@ class Less_Environment {
84
  '~' => ' ~ ',
85
  '>' => ' > ',
86
  '|' => '|',
87
- '^' => ' ^ ',
88
- '^^' => ' ^^ '
89
  );
90
 
91
  }
92
  }
93
 
94
- public function copyEvalEnv( $frames = array() ) {
 
95
  $new_env = new Less_Environment();
96
  $new_env->frames = $frames;
97
  return $new_env;
98
  }
99
 
100
- public static function isMathOn() {
 
101
  return !Less_Parser::$options['strictMath'] || Less_Environment::$parensStack;
102
  }
103
 
104
- public static function isPathRelative( $path ) {
105
- return !preg_match( '/^(?:[a-z-]+:|\/)/', $path );
106
  }
107
 
 
108
  /**
109
  * Canonicalize a path by resolving references to '/./', '/../'
110
  * Does not remove leading "../"
@@ -112,26 +119,27 @@ class Less_Environment {
112
  * @return string Canonicalized path
113
  *
114
  */
115
- public static function normalizePath( $path ) {
116
- $segments = explode( '/', $path );
117
- $segments = array_reverse( $segments );
 
118
 
119
  $path = array();
120
  $path_len = 0;
121
 
122
- while ( $segments ) {
123
- $segment = array_pop( $segments );
124
- switch ( $segment ) {
125
 
126
  case '.':
127
  break;
128
 
129
  case '..':
130
- if ( !$path_len || ( $path[$path_len - 1] === '..' ) ) {
131
  $path[] = $segment;
132
  $path_len++;
133
- } else {
134
- array_pop( $path );
135
  $path_len--;
136
  }
137
  break;
@@ -143,15 +151,16 @@ class Less_Environment {
143
  }
144
  }
145
 
146
- return implode( '/', $path );
147
  }
148
 
149
- public function unshiftFrame( $frame ) {
150
- array_unshift( $this->frames, $frame );
 
151
  }
152
 
153
- public function shiftFrame() {
154
- return array_shift( $this->frames );
155
  }
156
 
157
  }
1
  <?php
2
 
3
+
4
  /**
5
  * Environment
6
  *
7
  * @package Less
8
  * @subpackage environment
9
  */
10
+ class Less_Environment{
11
 
12
+ //public $paths = array(); // option - unmodified - paths to search for imports on
13
  //public static $files = array(); // list of files that have been imported, used for import-once
14
  //public $rootpath; // option - rootpath to append to URL's
15
  //public static $strictImports = null; // option -
21
 
22
  public $importMultiple = false; // whether we are currently importing multiple copies
23
 
24
+
25
  /**
26
  * @var array
27
  */
52
  */
53
  public $functions = array();
54
 
55
+
56
+ public function Init(){
57
+
58
  self::$parensStack = 0;
59
  self::$tabLevel = 0;
60
  self::$lastRule = false;
61
  self::$mixin_stack = 0;
62
 
63
+ if( Less_Parser::$options['compress'] ){
64
 
65
  Less_Environment::$_outputMap = array(
66
  ',' => ',',
72
  '~' => '~',
73
  '>' => '>',
74
  '|' => '|',
75
+ '^' => '^',
76
+ '^^' => '^^'
77
  );
78
 
79
+ }else{
80
 
81
  Less_Environment::$_outputMap = array(
82
  ',' => ', ',
88
  '~' => ' ~ ',
89
  '>' => ' > ',
90
  '|' => '|',
91
+ '^' => ' ^ ',
92
+ '^^' => ' ^^ '
93
  );
94
 
95
  }
96
  }
97
 
98
+
99
+ public function copyEvalEnv($frames = array() ){
100
  $new_env = new Less_Environment();
101
  $new_env->frames = $frames;
102
  return $new_env;
103
  }
104
 
105
+
106
+ public static function isMathOn(){
107
  return !Less_Parser::$options['strictMath'] || Less_Environment::$parensStack;
108
  }
109
 
110
+ public static function isPathRelative($path){
111
+ return !preg_match('/^(?:[a-z-]+:|\/)/',$path);
112
  }
113
 
114
+
115
  /**
116
  * Canonicalize a path by resolving references to '/./', '/../'
117
  * Does not remove leading "../"
119
  * @return string Canonicalized path
120
  *
121
  */
122
+ public static function normalizePath($path){
123
+
124
+ $segments = explode('/',$path);
125
+ $segments = array_reverse($segments);
126
 
127
  $path = array();
128
  $path_len = 0;
129
 
130
+ while( $segments ){
131
+ $segment = array_pop($segments);
132
+ switch( $segment ) {
133
 
134
  case '.':
135
  break;
136
 
137
  case '..':
138
+ if( !$path_len || ( $path[$path_len-1] === '..') ){
139
  $path[] = $segment;
140
  $path_len++;
141
+ }else{
142
+ array_pop($path);
143
  $path_len--;
144
  }
145
  break;
151
  }
152
  }
153
 
154
+ return implode('/',$path);
155
  }
156
 
157
+
158
+ public function unshiftFrame($frame){
159
+ array_unshift($this->frames, $frame);
160
  }
161
 
162
+ public function shiftFrame(){
163
+ return array_shift($this->frames);
164
  }
165
 
166
  }
base/inc/lib/Less/Exception/Chunk.php CHANGED
@@ -1,203 +1,203 @@
1
- <?php
2
-
3
- /**
4
- * Chunk Exception
5
- *
6
- * @package Less
7
- * @subpackage exception
8
- */
9
- class Less_Exception_Chunk extends Less_Exception_Parser {
10
-
11
- protected $parserCurrentIndex = 0;
12
-
13
- protected $emitFrom = 0;
14
-
15
- protected $input_len;
16
-
17
- /**
18
- * Constructor
19
- *
20
- * @param string $input
21
- * @param Exception $previous Previous exception
22
- * @param integer $index The current parser index
23
- * @param Less_FileInfo|string $currentFile The file
24
- * @param integer $code The exception code
25
- */
26
- public function __construct( $input, Exception $previous = null, $index = null, $currentFile = null, $code = 0 ) {
27
- $this->message = 'ParseError: Unexpected input'; // default message
28
-
29
- $this->index = $index;
30
-
31
- $this->currentFile = $currentFile;
32
-
33
- $this->input = $input;
34
- $this->input_len = strlen( $input );
35
-
36
- $this->Chunks();
37
- $this->genMessage();
38
- }
39
-
40
- /**
41
- * See less.js chunks()
42
- * We don't actually need the chunks
43
- *
44
- */
45
- protected function Chunks() {
46
- $level = 0;
47
- $parenLevel = 0;
48
- $lastMultiCommentEndBrace = null;
49
- $lastOpening = null;
50
- $lastMultiComment = null;
51
- $lastParen = null;
52
-
53
- for ( $this->parserCurrentIndex = 0; $this->parserCurrentIndex < $this->input_len; $this->parserCurrentIndex++ ) {
54
- $cc = $this->CharCode( $this->parserCurrentIndex );
55
- if ( ( ( $cc >= 97 ) && ( $cc <= 122 ) ) || ( $cc < 34 ) ) {
56
- // a-z or whitespace
57
- continue;
58
- }
59
-
60
- switch ( $cc ) {
61
-
62
- // (
63
- case 40:
64
- $parenLevel++;
65
- $lastParen = $this->parserCurrentIndex;
66
- break;
67
-
68
- // )
69
- case 41:
70
- $parenLevel--;
71
- if ( $parenLevel < 0 ) {
72
- return $this->fail( "missing opening `(`" );
73
- }
74
- break;
75
-
76
- // ;
77
- case 59:
78
- // if (!$parenLevel) { $this->emitChunk(); }
79
- break;
80
-
81
- // {
82
- case 123:
83
- $level++;
84
- $lastOpening = $this->parserCurrentIndex;
85
- break;
86
-
87
- // }
88
- case 125:
89
- $level--;
90
- if ( $level < 0 ) {
91
- return $this->fail( "missing opening `{`" );
92
-
93
- }
94
- // if (!$level && !$parenLevel) { $this->emitChunk(); }
95
- break;
96
- // \
97
- case 92:
98
- if ( $this->parserCurrentIndex < $this->input_len - 1 ) { $this->parserCurrentIndex++; break;
99
- }
100
- return $this->fail( "unescaped `\\`" );
101
-
102
- // ", ' and `
103
- case 34:
104
- case 39:
105
- case 96:
106
- $matched = 0;
107
- $currentChunkStartIndex = $this->parserCurrentIndex;
108
- for ( $this->parserCurrentIndex = $this->parserCurrentIndex + 1; $this->parserCurrentIndex < $this->input_len; $this->parserCurrentIndex++ ) {
109
- $cc2 = $this->CharCode( $this->parserCurrentIndex );
110
- if ( $cc2 > 96 ) { continue;
111
- }
112
- if ( $cc2 == $cc ) { $matched = 1; break;
113
- }
114
- if ( $cc2 == 92 ) { // \
115
- if ( $this->parserCurrentIndex == $this->input_len - 1 ) {
116
- return $this->fail( "unescaped `\\`" );
117
- }
118
- $this->parserCurrentIndex++;
119
- }
120
- }
121
- if ( $matched ) { break;
122
- }
123
- return $this->fail( "unmatched `" . chr( $cc ) . "`", $currentChunkStartIndex );
124
-
125
- // /, check for comment
126
- case 47:
127
- if ( $parenLevel || ( $this->parserCurrentIndex == $this->input_len - 1 ) ) { break;
128
- }
129
- $cc2 = $this->CharCode( $this->parserCurrentIndex + 1 );
130
- if ( $cc2 == 47 ) {
131
- // //, find lnfeed
132
- for ( $this->parserCurrentIndex = $this->parserCurrentIndex + 2; $this->parserCurrentIndex < $this->input_len; $this->parserCurrentIndex++ ) {
133
- $cc2 = $this->CharCode( $this->parserCurrentIndex );
134
- if ( ( $cc2 <= 13 ) && ( ( $cc2 == 10 ) || ( $cc2 == 13 ) ) ) { break;
135
- }
136
- }
137
- } else if ( $cc2 == 42 ) {
138
- // /*, find */
139
- $lastMultiComment = $currentChunkStartIndex = $this->parserCurrentIndex;
140
- for ( $this->parserCurrentIndex = $this->parserCurrentIndex + 2; $this->parserCurrentIndex < $this->input_len - 1; $this->parserCurrentIndex++ ) {
141
- $cc2 = $this->CharCode( $this->parserCurrentIndex );
142
- if ( $cc2 == 125 ) { $lastMultiCommentEndBrace = $this->parserCurrentIndex;
143
- }
144
- if ( $cc2 != 42 ) { continue;
145
- }
146
- if ( $this->CharCode( $this->parserCurrentIndex + 1 ) == 47 ) { break;
147
- }
148
- }
149
- if ( $this->parserCurrentIndex == $this->input_len - 1 ) {
150
- return $this->fail( "missing closing `*/`", $currentChunkStartIndex );
151
- }
152
- }
153
- break;
154
-
155
- // *, check for unmatched */
156
- case 42:
157
- if ( ( $this->parserCurrentIndex < $this->input_len - 1 ) && ( $this->CharCode( $this->parserCurrentIndex + 1 ) == 47 ) ) {
158
- return $this->fail( "unmatched `/*`" );
159
- }
160
- break;
161
- }
162
- }
163
-
164
- if ( $level !== 0 ) {
165
- if ( ( $lastMultiComment > $lastOpening ) && ( $lastMultiCommentEndBrace > $lastMultiComment ) ) {
166
- return $this->fail( "missing closing `}` or `*/`", $lastOpening );
167
- } else {
168
- return $this->fail( "missing closing `}`", $lastOpening );
169
- }
170
- } else if ( $parenLevel !== 0 ) {
171
- return $this->fail( "missing closing `)`", $lastParen );
172
- }
173
-
174
- // chunk didn't fail
175
-
176
- //$this->emitChunk(true);
177
- }
178
-
179
- public function CharCode( $pos ) {
180
- return ord( $this->input[$pos] );
181
- }
182
-
183
- public function fail( $msg, $index = null ) {
184
- if ( !$index ) {
185
- $this->index = $this->parserCurrentIndex;
186
- } else {
187
- $this->index = $index;
188
- }
189
- $this->message = 'ParseError: '.$msg;
190
- }
191
-
192
- /*
193
- function emitChunk( $force = false ){
194
- $len = $this->parserCurrentIndex - $this->emitFrom;
195
- if ((($len < 512) && !$force) || !$len) {
196
- return;
197
- }
198
- $chunks[] = substr($this->input, $this->emitFrom, $this->parserCurrentIndex + 1 - $this->emitFrom );
199
- $this->emitFrom = $this->parserCurrentIndex + 1;
200
- }
201
- */
202
-
203
- }
1
+ <?php
2
+
3
+ /**
4
+ * Chunk Exception
5
+ *
6
+ * @package Less
7
+ * @subpackage exception
8
+ */
9
+ class Less_Exception_Chunk extends Less_Exception_Parser{
10
+
11
+
12
+ protected $parserCurrentIndex = 0;
13
+
14
+ protected $emitFrom = 0;
15
+
16
+ protected $input_len;
17
+
18
+
19
+ /**
20
+ * Constructor
21
+ *
22
+ * @param string $input
23
+ * @param Exception $previous Previous exception
24
+ * @param integer $index The current parser index
25
+ * @param Less_FileInfo|string $currentFile The file
26
+ * @param integer $code The exception code
27
+ */
28
+ public function __construct($input, Exception $previous = null, $index = null, $currentFile = null, $code = 0){
29
+
30
+ $this->message = 'ParseError: Unexpected input'; //default message
31
+
32
+ $this->index = $index;
33
+
34
+ $this->currentFile = $currentFile;
35
+
36
+ $this->input = $input;
37
+ $this->input_len = strlen($input);
38
+
39
+ $this->Chunks();
40
+ $this->genMessage();
41
+ }
42
+
43
+
44
+ /**
45
+ * See less.js chunks()
46
+ * We don't actually need the chunks
47
+ *
48
+ */
49
+ protected function Chunks(){
50
+ $level = 0;
51
+ $parenLevel = 0;
52
+ $lastMultiCommentEndBrace = null;
53
+ $lastOpening = null;
54
+ $lastMultiComment = null;
55
+ $lastParen = null;
56
+
57
+ for( $this->parserCurrentIndex = 0; $this->parserCurrentIndex < $this->input_len; $this->parserCurrentIndex++ ){
58
+ $cc = $this->CharCode($this->parserCurrentIndex);
59
+ if ((($cc >= 97) && ($cc <= 122)) || ($cc < 34)) {
60
+ // a-z or whitespace
61
+ continue;
62
+ }
63
+
64
+ switch ($cc) {
65
+
66
+ // (
67
+ case 40:
68
+ $parenLevel++;
69
+ $lastParen = $this->parserCurrentIndex;
70
+ break;
71
+
72
+ // )
73
+ case 41:
74
+ $parenLevel--;
75
+ if( $parenLevel < 0 ){
76
+ return $this->fail("missing opening `(`");
77
+ }
78
+ break;
79
+
80
+ // ;
81
+ case 59:
82
+ //if (!$parenLevel) { $this->emitChunk(); }
83
+ break;
84
+
85
+ // {
86
+ case 123:
87
+ $level++;
88
+ $lastOpening = $this->parserCurrentIndex;
89
+ break;
90
+
91
+ // }
92
+ case 125:
93
+ $level--;
94
+ if( $level < 0 ){
95
+ return $this->fail("missing opening `{`");
96
+
97
+ }
98
+ //if (!$level && !$parenLevel) { $this->emitChunk(); }
99
+ break;
100
+ // \
101
+ case 92:
102
+ if ($this->parserCurrentIndex < $this->input_len - 1) { $this->parserCurrentIndex++; break; }
103
+ return $this->fail("unescaped `\\`");
104
+
105
+ // ", ' and `
106
+ case 34:
107
+ case 39:
108
+ case 96:
109
+ $matched = 0;
110
+ $currentChunkStartIndex = $this->parserCurrentIndex;
111
+ for ($this->parserCurrentIndex = $this->parserCurrentIndex + 1; $this->parserCurrentIndex < $this->input_len; $this->parserCurrentIndex++) {
112
+ $cc2 = $this->CharCode($this->parserCurrentIndex);
113
+ if ($cc2 > 96) { continue; }
114
+ if ($cc2 == $cc) { $matched = 1; break; }
115
+ if ($cc2 == 92) { // \
116
+ if ($this->parserCurrentIndex == $this->input_len - 1) {
117
+ return $this->fail("unescaped `\\`");
118
+ }
119
+ $this->parserCurrentIndex++;
120
+ }
121
+ }
122
+ if ($matched) { break; }
123
+ return $this->fail("unmatched `" . chr($cc) . "`", $currentChunkStartIndex);
124
+
125
+ // /, check for comment
126
+ case 47:
127
+ if ($parenLevel || ($this->parserCurrentIndex == $this->input_len - 1)) { break; }
128
+ $cc2 = $this->CharCode($this->parserCurrentIndex+1);
129
+ if ($cc2 == 47) {
130
+ // //, find lnfeed
131
+ for ($this->parserCurrentIndex = $this->parserCurrentIndex + 2; $this->parserCurrentIndex < $this->input_len; $this->parserCurrentIndex++) {
132
+ $cc2 = $this->CharCode($this->parserCurrentIndex);
133
+ if (($cc2 <= 13) && (($cc2 == 10) || ($cc2 == 13))) { break; }
134
+ }
135
+ } else if ($cc2 == 42) {
136
+ // /*, find */
137
+ $lastMultiComment = $currentChunkStartIndex = $this->parserCurrentIndex;
138
+ for ($this->parserCurrentIndex = $this->parserCurrentIndex + 2; $this->parserCurrentIndex < $this->input_len - 1; $this->parserCurrentIndex++) {
139
+ $cc2 = $this->CharCode($this->parserCurrentIndex);
140
+ if ($cc2 == 125) { $lastMultiCommentEndBrace = $this->parserCurrentIndex; }
141
+ if ($cc2 != 42) { continue; }
142
+ if ($this->CharCode($this->parserCurrentIndex+1) == 47) { break; }
143
+ }
144
+ if ($this->parserCurrentIndex == $this->input_len - 1) {
145
+ return $this->fail("missing closing `*/`", $currentChunkStartIndex);
146
+ }
147
+ }
148
+ break;
149
+
150
+ // *, check for unmatched */
151
+ case 42:
152
+ if (($this->parserCurrentIndex < $this->input_len - 1) && ($this->CharCode($this->parserCurrentIndex+1) == 47)) {
153
+ return $this->fail("unmatched `/*`");
154
+ }
155
+ break;
156
+ }
157
+ }
158
+
159
+ if( $level !== 0 ){
160
+ if( ($lastMultiComment > $lastOpening) && ($lastMultiCommentEndBrace > $lastMultiComment) ){
161
+ return $this->fail("missing closing `}` or `*/`", $lastOpening);
162
+ } else {
163
+ return $this->fail("missing closing `}`", $lastOpening);
164
+ }
165
+ } else if ( $parenLevel !== 0 ){
166
+ return $this->fail("missing closing `)`", $lastParen);
167
+ }
168
+
169
+
170
+ //chunk didn't fail
171
+
172
+
173
+ //$this->emitChunk(true);
174
+ }
175
+
176
+ public function CharCode($pos){
177
+ return ord($this->input[$pos]);
178
+ }
179
+
180
+
181
+ public function fail( $msg, $index = null ){
182
+
183
+ if( !$index ){
184
+ $this->index = $this->parserCurrentIndex;
185
+ }else{
186
+ $this->index = $index;
187
+ }
188
+ $this->message = 'ParseError: '.$msg;
189
+ }
190
+
191
+
192
+ /*
193
+ function emitChunk( $force = false ){
194
+ $len = $this->parserCurrentIndex - $this->emitFrom;
195
+ if ((($len < 512) && !$force) || !$len) {
196
+ return;
197
+ }
198
+ $chunks[] = substr($this->input, $this->emitFrom, $this->parserCurrentIndex + 1 - $this->emitFrom );
199
+ $this->emitFrom = $this->parserCurrentIndex + 1;
200
+ }
201
+ */
202
+
203
+ }
base/inc/lib/Less/Exception/Compiler.php CHANGED
@@ -1,11 +1,11 @@
1
- <?php
2
-
3
- /**
4
- * Compiler Exception
5
- *
6
- * @package Less
7
- * @subpackage exception
8
- */
9
- class Less_Exception_Compiler extends Less_Exception_Parser {
10
-
11
- }
1
+ <?php
2
+
3
+ /**
4
+ * Compiler Exception
5
+ *
6
+ * @package Less
7
+ * @subpackage exception
8
+ */
9
+ class Less_Exception_Compiler extends Less_Exception_Parser{
10
+
11
+ }
base/inc/lib/Less/Exception/Parser.php CHANGED
@@ -1,116 +1,125 @@
1
- <?php
2
-
3
- /**
4
- * Parser Exception
5
- *
6
- * @package Less
7
- * @subpackage exception
8
- */
9
- class Less_Exception_Parser extends Exception {
10
-
11
- /**
12
- * The current file
13
- *
14
- * @var Less_ImportedFile
15
- */
16
- public $currentFile;
17
-
18
- /**
19
- * The current parser index
20
- *
21
- * @var integer
22
- */
23
- public $index;
24
-
25
- protected $input;
26
-
27
- protected $details = array();
28
-
29
- /**
30
- * Constructor
31
- *
32
- * @param string $message
33
- * @param Exception $previous Previous exception
34
- * @param integer $index The current parser index
35
- * @param Less_FileInfo|string $currentFile The file
36
- * @param integer $code The exception code
37
- */
38
- public function __construct( $message = null, Exception $previous = null, $index = null, $currentFile = null, $code = 0 ) {
39
- if ( PHP_VERSION_ID < 50300 ) {
40
- $this->previous = $previous;
41
- parent::__construct( $message, $code );
42
- } else {
43
- parent::__construct( $message, $code, $previous );
44
- }
45
-
46
- $this->currentFile = $currentFile;
47
- $this->index = $index;
48
-
49
- $this->genMessage();
50
- }
51
-
52
- protected function getInput() {
53
- if ( !$this->input && $this->currentFile && $this->currentFile['filename'] && file_exists( $this->currentFile['filename'] ) ) {
54
- $this->input = file_get_contents( $this->currentFile['filename'] );
55
- }
56
- }
57
-
58
- /**
59
- * Converts the exception to string
60
- *
61
- * @return string
62
- */
63
- public function genMessage() {
64
- if ( $this->currentFile && $this->currentFile['filename'] ) {
65
- $this->message .= ' in '.basename( $this->currentFile['filename'] );
66
- }
67
-
68
- if ( $this->index !== null ) {
69
- $this->getInput();
70
- if ( $this->input ) {
71
- $line = self::getLineNumber();
72
- $this->message .= ' on line '.$line.', column '.self::getColumn();
73
-
74
- $lines = explode( "\n", $this->input );
75
-
76
- $count = count( $lines );
77
- $start_line = max( 0, $line - 3 );
78
- $last_line = min( $count, $start_line + 6 );
79
- $num_len = strlen( $last_line );
80
- for ( $i = $start_line; $i < $last_line; $i++ ) {
81
- $this->message .= "\n".str_pad( $i + 1, $num_len, '0', STR_PAD_LEFT ).'| '.$lines[$i];
82
- }
83
- }
84
- }
85
-
86
- }
87
-
88
- /**
89
- * Returns the line number the error was encountered
90
- *
91
- * @return integer
92
- */
93
- public function getLineNumber() {
94
- if ( $this->index ) {
95
- // https://bugs.php.net/bug.php?id=49790
96
- if ( ini_get( "mbstring.func_overload" ) ) {
97
- return substr_count( substr( $this->input, 0, $this->index ), "\n" ) + 1;
98
- } else {
99
- return substr_count( $this->input, "\n", 0, $this->index ) + 1;
100
- }
101
- }
102
- return 1;
103
- }
104
-
105
- /**
106
- * Returns the column the error was encountered
107
- *
108
- * @return integer
109
- */
110
- public function getColumn() {
111
- $part = substr( $this->input, 0, $this->index );
112
- $pos = strrpos( $part, "\n" );
113
- return $this->index - $pos;
114
- }
115
-
116
- }
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Parser Exception
5
+ *
6
+ * @package Less
7
+ * @subpackage exception
8
+ */
9
+ class Less_Exception_Parser extends Exception{
10
+
11
+ /**
12
+ * The current file
13
+ *
14
+ * @var Less_ImportedFile
15
+ */
16
+ public $currentFile;
17
+
18
+ /**
19
+ * The current parser index
20
+ *
21
+ * @var integer
22
+ */
23
+ public $index;
24
+
25
+ protected $input;
26
+
27
+ protected $details = array();
28
+
29
+
30
+ /**
31
+ * Constructor
32
+ *
33
+ * @param string $message
34
+ * @param Exception $previous Previous exception
35
+ * @param integer $index The current parser index
36
+ * @param Less_FileInfo|string $currentFile The file
37
+ * @param integer $code The exception code
38
+ */
39
+ public function __construct($message = null, Exception $previous = null, $index = null, $currentFile = null, $code = 0){
40
+
41
+ if (PHP_VERSION_ID < 50300) {
42
+ $this->previous = $previous;
43
+ parent::__construct($message, $code);
44
+ } else {
45
+ parent::__construct($message, $code, $previous);
46
+ }
47
+
48
+ $this->currentFile = $currentFile;
49
+ $this->index = $index;
50
+
51
+ $this->genMessage();
52
+ }
53
+
54
+
55
+ protected function getInput(){
56
+
57
+ if( !$this->input && $this->currentFile && $this->currentFile['filename'] && file_exists($this->currentFile['filename']) ){
58
+ $this->input = file_get_contents( $this->currentFile['filename'] );
59
+ }
60
+ }
61
+
62
+
63
+
64
+ /**
65
+ * Converts the exception to string
66
+ *
67
+ * @return string
68
+ */
69
+ public function genMessage(){
70
+
71
+ if( $this->currentFile && $this->currentFile['filename'] ){
72
+ $this->message .= ' in '.basename($this->currentFile['filename']);
73
+ }
74
+
75
+ if( $this->index !== null ){
76
+ $this->getInput();
77
+ if( $this->input ){
78
+ $line = self::getLineNumber();
79
+ $this->message .= ' on line '.$line.', column '.self::getColumn();
80
+
81
+ $lines = explode("\n",$this->input);
82
+
83
+ $count = count($lines);
84
+ $start_line = max(0, $line-3);
85
+ $last_line = min($count, $start_line+6);
86
+ $num_len = strlen($last_line);
87
+ for( $i = $start_line; $i < $last_line; $i++ ){
88
+ $this->message .= "\n".str_pad($i+1,$num_len,'0',STR_PAD_LEFT).'| '.$lines[$i];
89
+ }
90
+ }
91
+ }
92
+
93
+ }
94
+
95
+ /**
96
+ * Returns the line number the error was encountered
97
+ *
98
+ * @return integer
99
+ */
100
+ public function getLineNumber(){
101
+ if( $this->index ){
102
+ // https://bugs.php.net/bug.php?id=49790
103
+ if (ini_get("mbstring.func_overload")) {
104
+ return substr_count(substr($this->input, 0, $this->index), "\n") + 1;
105
+ } else {
106
+ return substr_count($this->input, "\n", 0, $this->index) + 1;
107
+ }
108
+ }
109
+ return 1;
110
+ }
111
+
112
+
113
+ /**
114
+ * Returns the column the error was encountered
115
+ *
116
+ * @return integer
117
+ */
118
+ public function getColumn(){
119
+
120
+ $part = substr($this->input, 0, $this->index);
121
+ $pos = strrpos($part,"\n");
122
+ return $this->index - $pos;
123
+ }
124
+
125
+ }
base/inc/lib/Less/Functions.php CHANGED
@@ -7,12 +7,12 @@
7
  * @subpackage function
8
  * @see http://lesscss.org/functions/
9
  */
10
- class Less_Functions {
11
 
12
  public $env;
13
  public $currentFileInfo;
14
 
15
- function __construct( $env, $currentFileInfo = null ) {
16
  $this->env = $env;
17
  $this->currentFileInfo = $currentFileInfo;
18
  }
@@ -20,367 +20,374 @@ class Less_Functions {
20
  /**
21
  * @param string $op
22
  */
23
- public static function operate( $op, $a, $b ) {
24
- switch ( $op ) {
25
- case '+':
26
- return $a + $b;
27
- case '-':
28
- return $a - $b;
29
- case '*':
30
- return $a * $b;
31
- case '/':
32
- return $a / $b;
33
  }
34
  }
35
 
36
- public static function clamp( $val, $max = 1 ) {
37
- return min( max( $val, 0 ), $max );
38
  }
39
 
40
- public static function fround( $value ) {
41
- if ( $value === 0 ) {
 
42
  return $value;
43
  }
44
 
45
- if ( Less_Parser::$options['numPrecision'] ) {
46
- $p = pow( 10, Less_Parser::$options['numPrecision'] );
47
- return round( $value * $p ) / $p;
48
  }
49
  return $value;
50
  }
51
 
52
- public static function number( $n ) {
53
- if ( $n instanceof Less_Tree_Dimension ) {
54
- return floatval( $n->unit->is( '%' ) ? $n->value / 100 : $n->value );
55
- } else if ( is_numeric( $n ) ) {
 
56
  return $n;
57
  } else {
58
- throw new Less_Exception_Compiler( "color functions take numbers as parameters" );
59
  }
60
  }
61
 
62
- public static function scaled( $n, $size = 255 ) {
63
- if ( $n instanceof Less_Tree_Dimension && $n->unit->is( '%' ) ) {
64
  return (float)$n->value * $size / 100;
65
  } else {
66
- return Less_Functions::number( $n );
67
  }
68
  }
69
 
70
- public function rgb( $r = null, $g = null, $b = null ) {
71
- if ( is_null( $r ) || is_null( $g ) || is_null( $b ) ) {
72
- throw new Less_Exception_Compiler( "rgb expects three parameters" );
73
  }
74
- return $this->rgba( $r, $g, $b, 1.0 );
75
  }
76
 
77
- public function rgba( $r = null, $g = null, $b = null, $a = null ) {
78
- $rgb = array( $r, $g, $b );
79
- $rgb = array_map( array( 'Less_Functions','scaled' ), $rgb );
80
 
81
- $a = self::number( $a );
82
- return new Less_Tree_Color( $rgb, $a );
83
  }
84
 
85
- public function hsl( $h, $s, $l ) {
86
- return $this->hsla( $h, $s, $l, 1.0 );
87
  }
88
 
89
- public function hsla( $h, $s, $l, $a ) {
90
- $h = fmod( self::number( $h ), 360 ) / 360; // Classic % operator will change float to int
91
- $s = self::clamp( self::number( $s ) );
92
- $l = self::clamp( self::number( $l ) );
93
- $a = self::clamp( self::number( $a ) );
94
 
95
- $m2 = $l <= 0.5 ? $l * ( $s + 1 ) : $l + $s - $l * $s;
 
 
 
 
 
96
 
97
  $m1 = $l * 2 - $m2;
98
 
99
- return $this->rgba( self::hsla_hue( $h + 1 / 3, $m1, $m2 ) * 255,
100
- self::hsla_hue( $h, $m1, $m2 ) * 255,
101
- self::hsla_hue( $h - 1 / 3, $m1, $m2 ) * 255,
102
- $a );
103
  }
104
 
105
  /**
106
  * @param double $h
107
  */
108
- public function hsla_hue( $h, $m1, $m2 ) {
109
- $h = $h < 0 ? $h + 1 : ( $h > 1 ? $h - 1 : $h );
110
- if ( $h * 6 < 1 ) return $m1 + ( $m2 - $m1 ) * $h * 6; else if ( $h * 2 < 1 ) return $m2; else if ( $h * 3 < 2 ) return $m1 + ( $m2 - $m1 ) * ( 2 / 3 - $h ) * 6; else return $m1;
 
 
 
111
  }
112
 
113
- public function hsv( $h, $s, $v ) {
114
- return $this->hsva( $h, $s, $v, 1.0 );
115
  }
116
 
117
  /**
118
  * @param double $a
119
  */
120
- public function hsva( $h, $s, $v, $a ) {
121
- $h = ( ( Less_Functions::number( $h ) % 360 ) / 360 ) * 360;
122
- $s = Less_Functions::number( $s );
123
- $v = Less_Functions::number( $v );
124
- $a = Less_Functions::number( $a );
125
 
126
- $i = floor( ( $h / 60 ) % 6 );
127
- $f = ( $h / 60 ) - $i;
128
 
129
  $vs = array( $v,
130
- $v * ( 1 - $s ),
131
- $v * ( 1 - $f * $s ),
132
- $v * ( 1 - ( 1 - $f ) * $s ) );
133
-
134
- $perm = array( array( 0, 3, 1 ),
135
- array( 2, 0, 1 ),
136
- array( 1, 0, 3 ),
137
- array( 1, 2, 0 ),
138
- array( 3, 1, 0 ),
139
- array( 0, 1, 2 ) );
140
-
141
- return $this->rgba( $vs[$perm[$i][0]] * 255,
142
  $vs[$perm[$i][1]] * 255,
143
  $vs[$perm[$i][2]] * 255,
144
- $a );
145
  }
146
 
147
- public function hue( $color = null ) {
148
- if ( !$color instanceof Less_Tree_Color ) {
149
- throw new Less_Exception_Compiler( 'The first argument to hue must be a color' . ( $color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
150
  }
151
 
152
  $c = $color->toHSL();
153
- return new Less_Tree_Dimension( Less_Parser::round( $c['h'] ) );
154
  }
155
 
156
- public function saturation( $color = null ) {
157
- if ( !$color instanceof Less_Tree_Color ) {
158
- throw new Less_Exception_Compiler( 'The first argument to saturation must be a color' . ( $color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
159
  }
160
 
161
  $c = $color->toHSL();
162
- return new Less_Tree_Dimension( Less_Parser::round( $c['s'] * 100 ), '%' );
163
  }
164
 
165
- public function lightness( $color = null ) {
166
- if ( !$color instanceof Less_Tree_Color ) {
167
- throw new Less_Exception_Compiler( 'The first argument to lightness must be a color' . ( $color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
168
  }
169
 
170
  $c = $color->toHSL();
171
- return new Less_Tree_Dimension( Less_Parser::round( $c['l'] * 100 ), '%' );
172
  }
173
 
174
- public function hsvhue( $color = null ) {
175
- if ( !$color instanceof Less_Tree_Color ) {
176
- throw new Less_Exception_Compiler( 'The first argument to hsvhue must be a color' . ( $color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
177
  }
178
 
179
  $hsv = $color->toHSV();
180
- return new Less_Tree_Dimension( Less_Parser::round( $hsv['h'] ) );
181
  }
182
 
183
- public function hsvsaturation( $color = null ) {
184
- if ( !$color instanceof Less_Tree_Color ) {
185
- throw new Less_Exception_Compiler( 'The first argument to hsvsaturation must be a color' . ( $color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
 
186
  }
187
 
188
  $hsv = $color->toHSV();
189
- return new Less_Tree_Dimension( Less_Parser::round( $hsv['s'] * 100 ), '%' );
190
  }
191
 
192
- public function hsvvalue( $color = null ) {
193
- if ( !$color instanceof Less_Tree_Color ) {
194
- throw new Less_Exception_Compiler( 'The first argument to hsvvalue must be a color' . ( $color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
195
  }
196
 
197
  $hsv = $color->toHSV();
198
- return new Less_Tree_Dimension( Less_Parser::round( $hsv['v'] * 100 ), '%' );
199
  }
200
 
201
- public function red( $color = null ) {
202
- if ( !$color instanceof Less_Tree_Color ) {
203
- throw new Less_Exception_Compiler( 'The first argument to red must be a color' . ( $color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
204
  }
205
 
206
  return new Less_Tree_Dimension( $color->rgb[0] );
207
  }
208
 
209
- public function green( $color = null ) {
210
- if ( !$color instanceof Less_Tree_Color ) {
211
- throw new Less_Exception_Compiler( 'The first argument to green must be a color' . ( $color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
212
  }
213
 
214
  return new Less_Tree_Dimension( $color->rgb[1] );
215
  }
216
 
217
- public function blue( $color = null ) {
218
- if ( !$color instanceof Less_Tree_Color ) {
219
- throw new Less_Exception_Compiler( 'The first argument to blue must be a color' . ( $color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
220
  }
221
 
222
  return new Less_Tree_Dimension( $color->rgb[2] );
223
  }
224
 
225
- public function alpha( $color = null ) {
226
- if ( !$color instanceof Less_Tree_Color ) {
227
- throw new Less_Exception_Compiler( 'The first argument to alpha must be a color' . ( $color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
228
  }
229
 
230
  $c = $color->toHSL();
231
- return new Less_Tree_Dimension( $c['a'] );
232
  }
233
 
234
- public function luma( $color = null ) {
235
- if ( !$color instanceof Less_Tree_Color ) {
236
- throw new Less_Exception_Compiler( 'The first argument to luma must be a color' . ( $color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
237
  }
238
 
239
- return new Less_Tree_Dimension( Less_Parser::round( $color->luma() * $color->alpha * 100 ), '%' );
240
  }
241
 
242
- public function luminance( $color = null ) {
243
- if ( !$color instanceof Less_Tree_Color ) {
244
- throw new Less_Exception_Compiler( 'The first argument to luminance must be a color' . ( $color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
245
  }
246
 
247
  $luminance =
248
- ( 0.2126 * $color->rgb[0] / 255 )
249
- + ( 0.7152 * $color->rgb[1] / 255 )
250
- + ( 0.0722 * $color->rgb[2] / 255 );
251
 
252
- return new Less_Tree_Dimension( Less_Parser::round( $luminance * $color->alpha * 100 ), '%' );
253
  }
254
 
255
- public function saturate( $color = null, $amount = null ) {
256
  // filter: saturate(3.2);
257
  // should be kept as is, so check for color
258
- if ( $color instanceof Less_Tree_Dimension ) {
259
  return null;
260
  }
261
 
262
- if ( !$color instanceof Less_Tree_Color ) {
263
- throw new Less_Exception_Compiler( 'The first argument to saturate must be a color' . ( $color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
264
  }
265
- if ( !$amount instanceof Less_Tree_Dimension ) {
266
- throw new Less_Exception_Compiler( 'The second argument to saturate must be a percentage' . ( $amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
267
  }
268
 
269
  $hsl = $color->toHSL();
270
 
271
  $hsl['s'] += $amount->value / 100;
272
- $hsl['s'] = self::clamp( $hsl['s'] );
273
 
274
- return $this->hsla( $hsl['h'], $hsl['s'], $hsl['l'], $hsl['a'] );
275
  }
276
 
277
  /**
278
  * @param Less_Tree_Dimension $amount
279
  */
280
- public function desaturate( $color = null, $amount = null ) {
281
- if ( !$color instanceof Less_Tree_Color ) {
282
- throw new Less_Exception_Compiler( 'The first argument to desaturate must be a color' . ( $color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
283
  }
284
- if ( !$amount instanceof Less_Tree_Dimension ) {
285
- throw new Less_Exception_Compiler( 'The second argument to desaturate must be a percentage' . ( $amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
286
  }
287
 
288
  $hsl = $color->toHSL();
289
 
290
  $hsl['s'] -= $amount->value / 100;
291
- $hsl['s'] = self::clamp( $hsl['s'] );
292
 
293
- return $this->hsla( $hsl['h'], $hsl['s'], $hsl['l'], $hsl['a'] );
294
  }
295
 
296
- public function lighten( $color = null, $amount = null ) {
297
- if ( !$color instanceof Less_Tree_Color ) {
298
- throw new Less_Exception_Compiler( 'The first argument to lighten must be a color' . ( $color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
 
 
299
  }
300
- if ( !$amount instanceof Less_Tree_Dimension ) {
301
- throw new Less_Exception_Compiler( 'The second argument to lighten must be a percentage' . ( $amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
302
  }
303
 
304
  $hsl = $color->toHSL();
305
 
306
  $hsl['l'] += $amount->value / 100;
307
- $hsl['l'] = self::clamp( $hsl['l'] );
308
 
309
- return $this->hsla( $hsl['h'], $hsl['s'], $hsl['l'], $hsl['a'] );
310
  }
311
 
312
- public function darken( $color = null, $amount = null ) {
313
- if ( !$color instanceof Less_Tree_Color ) {
314
- throw new Less_Exception_Compiler( 'The first argument to darken must be a color' . ( $color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
315
  }
316
- if ( !$amount instanceof Less_Tree_Dimension ) {
317
- throw new Less_Exception_Compiler( 'The second argument to darken must be a percentage' . ( $amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
318
  }
319
 
320
  $hsl = $color->toHSL();
321
  $hsl['l'] -= $amount->value / 100;
322
- $hsl['l'] = self::clamp( $hsl['l'] );
323
 
324
- return $this->hsla( $hsl['h'], $hsl['s'], $hsl['l'], $hsl['a'] );
325
  }
326
 
327
- public function fadein( $color = null, $amount = null ) {
328
- if ( !$color instanceof Less_Tree_Color ) {
329
- throw new Less_Exception_Compiler( 'The first argument to fadein must be a color' . ( $color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
330
  }
331
- if ( !$amount instanceof Less_Tree_Dimension ) {
332
- throw new Less_Exception_Compiler( 'The second argument to fadein must be a percentage' . ( $amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
333
  }
334
 
335
  $hsl = $color->toHSL();
336
  $hsl['a'] += $amount->value / 100;
337
- $hsl['a'] = self::clamp( $hsl['a'] );
338
- return $this->hsla( $hsl['h'], $hsl['s'], $hsl['l'], $hsl['a'] );
339
  }
340
 
341
- public function fadeout( $color = null, $amount = null ) {
342
- if ( !$color instanceof Less_Tree_Color ) {
343
- throw new Less_Exception_Compiler( 'The first argument to fadeout must be a color' . ( $color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
344
  }
345
- if ( !$amount instanceof Less_Tree_Dimension ) {
346
- throw new Less_Exception_Compiler( 'The second argument to fadeout must be a percentage' . ( $amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
347
  }
348
 
349
  $hsl = $color->toHSL();
350
  $hsl['a'] -= $amount->value / 100;
351
- $hsl['a'] = self::clamp( $hsl['a'] );
352
- return $this->hsla( $hsl['h'], $hsl['s'], $hsl['l'], $hsl['a'] );
353
  }
354
 
355
- public function fade( $color = null, $amount = null ) {
356
- if ( !$color instanceof Less_Tree_Color ) {
357
- throw new Less_Exception_Compiler( 'The first argument to fade must be a color' . ( $color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
358
  }
359
- if ( !$amount instanceof Less_Tree_Dimension ) {
360
- throw new Less_Exception_Compiler( 'The second argument to fade must be a percentage' . ( $amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
361
  }
362
 
363
  $hsl = $color->toHSL();
364
 
365
  $hsl['a'] = $amount->value / 100;
366
- $hsl['a'] = self::clamp( $hsl['a'] );
367
- return $this->hsla( $hsl['h'], $hsl['s'], $hsl['l'], $hsl['a'] );
368
  }
369
 
370
- public function spin( $color = null, $amount = null ) {
371
- if ( !$color instanceof Less_Tree_Color ) {
372
- throw new Less_Exception_Compiler( 'The first argument to spin must be a color' . ( $color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
 
 
373
  }
374
- if ( !$amount instanceof Less_Tree_Dimension ) {
375
- throw new Less_Exception_Compiler( 'The second argument to spin must be a number' . ( $amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
376
  }
377
 
378
  $hsl = $color->toHSL();
379
- $hue = fmod( $hsl['h'] + $amount->value, 360 );
380
 
381
  $hsl['h'] = $hue < 0 ? 360 + $hue : $hue;
382
 
383
- return $this->hsla( $hsl['h'], $hsl['s'], $hsl['l'], $hsl['a'] );
384
  }
385
 
386
  //
@@ -391,18 +398,18 @@ return $a / $b;
391
  /**
392
  * @param Less_Tree_Color $color1
393
  */
394
- public function mix( $color1 = null, $color2 = null, $weight = null ) {
395
- if ( !$color1 instanceof Less_Tree_Color ) {
396
- throw new Less_Exception_Compiler( 'The first argument to mix must be a color' . ( $color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
397
  }
398
- if ( !$color2 instanceof Less_Tree_Color ) {
399
- throw new Less_Exception_Compiler( 'The second argument to mix must be a color' . ( $color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
400
  }
401
- if ( !$weight ) {
402
- $weight = new Less_Tree_Dimension( '50', '%' );
403
  }
404
- if ( !$weight instanceof Less_Tree_Dimension ) {
405
- throw new Less_Exception_Compiler( 'The third argument to contrast must be a percentage' . ( $weight instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
406
  }
407
 
408
  $p = $weight->value / 100.0;
@@ -411,100 +418,104 @@ return $a / $b;
411
  $hsl2 = $color2->toHSL();
412
  $a = $hsl1['a'] - $hsl2['a'];
413
 
414
- $w1 = ( ( ( ( $w * $a ) == -1 ) ? $w : ( $w + $a ) / ( 1 + $w * $a ) ) + 1 ) / 2;
415
  $w2 = 1 - $w1;
416
 
417
- $rgb = array( $color1->rgb[0] * $w1 + $color2->rgb[0] * $w2,
418
  $color1->rgb[1] * $w1 + $color2->rgb[1] * $w2,
419
- $color1->rgb[2] * $w1 + $color2->rgb[2] * $w2 );
420
 
421
- $alpha = $color1->alpha * $p + $color2->alpha * ( 1 - $p );
422
 
423
- return new Less_Tree_Color( $rgb, $alpha );
424
  }
425
 
426
- public function greyscale( $color ) {
427
- return $this->desaturate( $color, new Less_Tree_Dimension( 100, '%' ) );
428
  }
429
 
430
- public function contrast( $color, $dark = null, $light = null, $threshold = null ) {
 
431
  // filter: contrast(3.2);
432
  // should be kept as is, so check for color
433
- if ( !$color instanceof Less_Tree_Color ) {
434
  return null;
435
  }
436
- if ( !$light ) {
437
- $light = $this->rgba( 255, 255, 255, 1.0 );
438
  }
439
- if ( !$dark ) {
440
- $dark = $this->rgba( 0, 0, 0, 1.0 );
441
  }
442
 
443
- if ( !$dark instanceof Less_Tree_Color ) {
444
- throw new Less_Exception_Compiler( 'The second argument to contrast must be a color' . ( $dark instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
445
  }
446
- if ( !$light instanceof Less_Tree_Color ) {
447
- throw new Less_Exception_Compiler( 'The third argument to contrast must be a color' . ( $light instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
448
  }
449
 
450
- // Figure out which is actually light and dark!
451
- if ( $dark->luma() > $light->luma() ) {
452
  $t = $light;
453
  $light = $dark;
454
  $dark = $t;
455
  }
456
- if ( !$threshold ) {
457
  $threshold = 0.43;
458
  } else {
459
- $threshold = Less_Functions::number( $threshold );
460
  }
461
 
462
- if ( $color->luma() < $threshold ) {
463
  return $light;
464
  } else {
465
  return $dark;
466
  }
467
  }
468
 
469
- public function e( $str ) {
470
- if ( is_string( $str ) ) {
471
- return new Less_Tree_Anonymous( $str );
472
  }
473
- return new Less_Tree_Anonymous( $str instanceof Less_Tree_JavaScript ? $str->expression : $str->value );
474
  }
475
 
476
- public function escape( $str ) {
477
- $revert = array( '%21' => '!', '%2A' => '*', '%27' => "'",'%3F' => '?','%26' => '&','%2C' => ',','%2F' => '/','%40' => '@','%2B' => '+','%24' => '$' );
 
478
 
479
- return new Less_Tree_Anonymous( strtr( rawurlencode( $str->value ), $revert ) );
480
  }
481
 
 
482
  /**
483
  * todo: This function will need some additional work to make it work the same as less.js
484
  *
485
  */
486
- public function replace( $string, $pattern, $replacement, $flags = null ) {
487
  $result = $string->value;
488
 
489
- $expr = '/'.str_replace( '/', '\\/', $pattern->value ).'/';
490
- if ( $flags && $flags->value ) {
491
- $expr .= self::replace_flags( $flags->value );
492
  }
493
 
494
- $result = preg_replace( $expr, $replacement->value, $result );
 
495
 
496
- if ( property_exists( $string, 'quote' ) ) {
497
- return new Less_Tree_Quoted( $string->quote, $result, $string->escaped );
498
  }
499
  return new Less_Tree_Quoted( '', $result );
500
  }
501
 
502
- public static function replace_flags( $flags ) {
503
- $flags = str_split( $flags, 1 );
504
  $new_flags = '';
505
 
506
- foreach ( $flags as $flag ) {
507
- switch ( $flag ) {
508
  case 'e':
509
  case 'g':
510
  break;
@@ -518,33 +529,33 @@ return $a / $b;
518
  return $new_flags;
519
  }
520
 
521
- public function _percent() {
522
- $string = func_get_arg( 0 );
523
 
524
  $args = func_get_args();
525
- array_shift( $args );
526
  $result = $string->value;
527
 
528
- foreach ( $args as $arg ) {
529
- if ( preg_match( '/%[sda]/i', $result, $token ) ) {
530
  $token = $token[0];
531
- $value = stristr( $token, 's' ) ? $arg->value : $arg->toCSS();
532
- $value = preg_match( '/[A-Z]$/', $token ) ? urlencode( $value ) : $value;
533
- $result = preg_replace( '/%[sda]/i', $value, $result, 1 );
534
  }
535
  }
536
- $result = str_replace( '%%', '%', $result );
537
 
538
- return new Less_Tree_Quoted( $string->quote, $result, $string->escaped );
539
  }
540
 
541
- public function unit( $val, $unit = null ) {
542
- if ( !( $val instanceof Less_Tree_Dimension ) ) {
543
- throw new Less_Exception_Compiler( 'The first argument to unit must be a number' . ( $val instanceof Less_Tree_Operation ? '. Have you forgotten parenthesis?' : '.' ) );
544
  }
545
 
546
- if ( $unit ) {
547
- if ( $unit instanceof Less_Tree_Keyword ) {
548
  $unit = $unit->value;
549
  } else {
550
  $unit = $unit->toCSS();
@@ -552,335 +563,320 @@ return $a / $b;
552
  } else {
553
  $unit = "";
554
  }
555
- return new Less_Tree_Dimension( $val->value, $unit );
556
  }
557
 
558
- public function convert( $val, $unit ) {
559
- return $val->convertTo( $unit->value );
560
  }
561
 
562
- public function round( $n, $f = false ) {
 
563
  $fraction = 0;
564
- if ( $f !== false ) {
565
  $fraction = $f->value;
566
  }
567
 
568
- return $this->_math( 'Less_Parser::round', null, $n, $fraction );
569
- }
570
-
571
- public function pi() {
572
- return new Less_Tree_Dimension( M_PI );
573
- }
574
-
575
- public function mod( $a, $b ) {
576
- return new Less_Tree_Dimension( $a->value % $b->value, $a->unit );
577
- }
578
-
579
- public function pow( $x, $y ) {
580
- if ( is_numeric( $x ) && is_numeric( $y ) ) {
581
- $x = new Less_Tree_Dimension( $x );
582
- $y = new Less_Tree_Dimension( $y );
583
- } elseif ( !( $x instanceof Less_Tree_Dimension ) || !( $y instanceof Less_Tree_Dimension ) ) {
584
- throw new Less_Exception_Compiler( 'Arguments must be numbers' );
585
- }
586
-
587
- return new Less_Tree_Dimension( pow( $x->value, $y->value ), $x->unit );
588
  }
589
 
590
- // var mathFunctions = [{name:"ce ...
591
- public function ceil( $n ) {
592
- return $this->_math( 'ceil', null, $n );
593
  }
594
 
595
- public function floor( $n ) {
596
- return $this->_math( 'floor', null, $n );
597
  }
598
 
599
- public function sqrt( $n ) {
600
- return $this->_math( 'sqrt', null, $n );
601
- }
602
 
603
- public function abs( $n ) {
604
- return $this->_math( 'abs', null, $n );
605
- }
606
 
607
- public function tan( $n ) {
608
- return $this->_math( 'tan', '', $n );
609
- }
 
 
 
 
610
 
611
- public function sin( $n ) {
612
- return $this->_math( 'sin', '', $n );
613
  }
614
 
615
- public function cos( $n ) {
616
- return $this->_math( 'cos', '', $n );
617
- }
 
 
618
 
619
- public function atan( $n ) {
620
- return $this->_math( 'atan', 'rad', $n );
621
- }
622
 
623
- public function asin( $n ) {
624
- return $this->_math( 'asin', 'rad', $n );
625
- }
626
-
627
- public function acos( $n ) {
628
- return $this->_math( 'acos', 'rad', $n );
629
- }
630
 
631
  private function _math() {
632
  $args = func_get_args();
633
- $fn = array_shift( $args );
634
- $unit = array_shift( $args );
635
 
636
- if ( $args[0] instanceof Less_Tree_Dimension ) {
637
 
638
- if ( $unit === null ) {
639
  $unit = $args[0]->unit;
640
- } else {
641
  $args[0] = $args[0]->unify();
642
  }
643
  $args[0] = (float)$args[0]->value;
644
- return new Less_Tree_Dimension( call_user_func_array( $fn, $args ), $unit );
645
- } else if ( is_numeric( $args[0] ) ) {
646
- return call_user_func_array( $fn, $args );
647
  } else {
648
- throw new Less_Exception_Compiler( "math functions take numbers as parameters" );
649
  }
650
  }
651
 
652
  /**
653
  * @param boolean $isMin
654
  */
655
- private function _minmax( $isMin, $args ) {
656
- $arg_count = count( $args );
 
657
 
658
- if ( $arg_count < 1 ) {
659
- throw new Less_Exception_Compiler( 'one or more arguments required' );
660
  }
661
 
662
  $j = null;
663
  $unitClone = null;
664
  $unitStatic = null;
665
 
 
666
  $order = array(); // elems only contains original argument values.
667
  $values = array(); // key is the unit.toString() for unified tree.Dimension values,
668
  // value is the index into the order array.
669
 
670
- for ( $i = 0; $i < $arg_count; $i++ ) {
 
671
  $current = $args[$i];
672
- if ( !( $current instanceof Less_Tree_Dimension ) ) {
673
- if ( is_array( $args[$i]->value ) ) {
674
  $args[] = $args[$i]->value;
675
  }
676
  continue;
677
  }
678
 
679
- if ( $current->unit->toString() === '' && !$unitClone ) {
680
- $temp = new Less_Tree_Dimension( $current->value, $unitClone );
681
  $currentUnified = $temp->unify();
682
- } else {
683
  $currentUnified = $current->unify();
684
  }
685
 
686
- if ( $currentUnified->unit->toString() === "" && !$unitStatic ) {
687
  $unit = $unitStatic;
688
- } else {
689
  $unit = $currentUnified->unit->toString();
690
  }
691
 
692
- if ( $unit !== '' && !$unitStatic || $unit !== '' && $order[0]->unify()->unit->toString() === "" ) {
693
  $unitStatic = $unit;
694
  }
695
 
696
- if ( $unit != '' && !$unitClone ) {
697
  $unitClone = $current->unit->toString();
698
  }
699
 
700
- if ( isset( $values[''] ) && $unit !== '' && $unit === $unitStatic ) {
701
  $j = $values[''];
702
- } elseif ( isset( $values[$unit] ) ) {
703
  $j = $values[$unit];
704
- } else {
705
 
706
- if ( $unitStatic && $unit !== $unitStatic ) {
707
- throw new Less_Exception_Compiler( 'incompatible types' );
708
  }
709
- $values[$unit] = count( $order );
710
  $order[] = $current;
711
  continue;
712
  }
713
 
714
- if ( $order[$j]->unit->toString() === "" && $unitClone ) {
715
- $temp = new Less_Tree_Dimension( $order[$j]->value, $unitClone );
 
716
  $referenceUnified = $temp->unify();
717
- } else {
718
  $referenceUnified = $order[$j]->unify();
719
  }
720
- if ( ( $isMin && $currentUnified->value < $referenceUnified->value ) || ( !$isMin && $currentUnified->value > $referenceUnified->value ) ) {
721
  $order[$j] = $current;
722
  }
723
  }
724
 
725
- if ( count( $order ) == 1 ) {
726
  return $order[0];
727
  }
728
  $args = array();
729
- foreach ( $order as $a ) {
730
- $args[] = $a->toCSS( $this->env );
731
  }
732
- return new Less_Tree_Anonymous( ( $isMin ? 'min(' : 'max(' ) . implode( Less_Environment::$_outputMap[','], $args ).')' );
733
  }
734
 
735
- public function min() {
736
  $args = func_get_args();
737
  return $this->_minmax( true, $args );
738
  }
739
 
740
- public function max() {
741
  $args = func_get_args();
742
  return $this->_minmax( false, $args );
743
  }
744
 
745
- public function getunit( $n ) {
746
- return new Less_Tree_Anonymous( $n->unit );
747
  }
748
 
749
- public function argb( $color ) {
750
- if ( !$color instanceof Less_Tree_Color ) {
751
- throw new Less_Exception_Compiler( 'The first argument to argb must be a color' . ( $color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
752
  }
753
 
754
- return new Less_Tree_Anonymous( $color->toARGB() );
755
  }
756
 
757
- public function percentage( $n ) {
758
- return new Less_Tree_Dimension( $n->value * 100, '%' );
759
  }
760
 
761
- public function color( $n ) {
762
- if ( $n instanceof Less_Tree_Quoted ) {
 
763
  $colorCandidate = $n->value;
764
- $returnColor = Less_Tree_Color::fromKeyword( $colorCandidate );
765
- if ( $returnColor ) {
766
  return $returnColor;
767
  }
768
- if ( preg_match( '/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/', $colorCandidate ) ) {
769
- return new Less_Tree_Color( substr( $colorCandidate, 1 ) );
770
  }
771
- throw new Less_Exception_Compiler( "argument must be a color keyword or 3/6 digit hex e.g. #FFF" );
772
  } else {
773
- throw new Less_Exception_Compiler( "argument must be a string" );
774
  }
775
  }
776
 
777
- public function iscolor( $n ) {
778
- return $this->_isa( $n, 'Less_Tree_Color' );
 
779
  }
780
 
781
- public function isnumber( $n ) {
782
- return $this->_isa( $n, 'Less_Tree_Dimension' );
783
  }
784
 
785
- public function isstring( $n ) {
786
- return $this->_isa( $n, 'Less_Tree_Quoted' );
787
  }
788
 
789
- public function iskeyword( $n ) {
790
- return $this->_isa( $n, 'Less_Tree_Keyword' );
791
  }
792
 
793
- public function isurl( $n ) {
794
- return $this->_isa( $n, 'Less_Tree_Url' );
795
  }
796
 
797
- public function ispixel( $n ) {
798
- return $this->isunit( $n, 'px' );
799
  }
800
 
801
- public function ispercentage( $n ) {
802
- return $this->isunit( $n, '%' );
803
  }
804
 
805
- public function isem( $n ) {
806
- return $this->isunit( $n, 'em' );
807
  }
808
 
809
  /**
810
  * @param string $unit
811
  */
812
- public function isunit( $n, $unit ) {
813
- if ( is_object( $unit ) && property_exists( $unit, 'value' ) ) {
 
814
  $unit = $unit->value;
815
  }
816
 
817
- return ( $n instanceof Less_Tree_Dimension ) && $n->unit->is( $unit ) ? new Less_Tree_Keyword( 'true' ) : new Less_Tree_Keyword( 'false' );
818
  }
819
 
820
  /**
821
  * @param string $type
822
  */
823
- private function _isa( $n, $type ) {
824
- return is_a( $n, $type ) ? new Less_Tree_Keyword( 'true' ) : new Less_Tree_Keyword( 'false' );
825
  }
826
 
827
- public function tint( $color, $amount = null ) {
828
- return $this->mix( $this->rgb( 255, 255, 255 ), $color, $amount );
829
  }
830
 
831
- public function shade( $color, $amount = null ) {
832
- return $this->mix( $this->rgb( 0, 0, 0 ), $color, $amount );
833
  }
834
 
835
- public function extract( $values, $index ) {
836
  $index = (int)$index->value - 1; // (1-based index)
837
  // handle non-array values as an array of length 1
838
  // return 'undefined' if index is invalid
839
- if ( property_exists( $values, 'value' ) && is_array( $values->value ) ) {
840
- if ( isset( $values->value[$index] ) ) {
841
  return $values->value[$index];
842
  }
843
  return null;
844
 
845
- } elseif ( (int)$index === 0 ) {
846
  return $values;
847
  }
848
 
849
  return null;
850
  }
851
 
852
- public function length( $values ) {
853
- $n = ( property_exists( $values, 'value' ) && is_array( $values->value ) ) ? count( $values->value ) : 1;
854
- return new Less_Tree_Dimension( $n );
855
  }
856
 
857
- public function datauri( $mimetypeNode, $filePathNode = null ) {
 
858
  $filePath = ( $filePathNode ? $filePathNode->value : null );
859
  $mimetype = $mimetypeNode->value;
860
 
861
  $args = 2;
862
- if ( !$filePath ) {
863
  $filePath = $mimetype;
864
  $args = 1;
865
  }
866
 
867
- $filePath = str_replace( '\\', '/', $filePath );
868
- if ( Less_Environment::isPathRelative( $filePath ) ) {
869
 
870
- if ( Less_Parser::$options['relativeUrls'] ) {
871
  $temp = $this->currentFileInfo['currentDirectory'];
872
  } else {
873
  $temp = $this->currentFileInfo['entryPath'];
874
  }
875
 
876
- if ( !empty( $temp ) ) {
877
- $filePath = Less_Environment::normalizePath( rtrim( $temp, '/' ).'/'.$filePath );
878
  }
879
 
880
  }
881
 
 
882
  // detect the mimetype if not given
883
- if ( $args < 2 ) {
884
 
885
  /* incomplete
886
  $mime = require('mime');
@@ -892,56 +888,59 @@ return $a / $b;
892
  if (useBase64) mimetype += ';base64';
893
  */
894
 
895
- $mimetype = Less_Mime::lookup( $filePath );
896
 
897
- $charset = Less_Mime::charsets_lookup( $mimetype );
898
- $useBase64 = !in_array( $charset, array( 'US-ASCII', 'UTF-8' ) );
899
- if ( $useBase64 ) { $mimetype .= ';base64';
900
- }
901
 
902
- } else {
903
- $useBase64 = preg_match( '/;base64$/', $mimetype );
904
  }
905
 
906
- if ( file_exists( $filePath ) ) {
907
- $buf = @file_get_contents( $filePath );
908
- } else {
 
909
  $buf = false;
910
  }
911
 
 
912
  // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded
913
  // and the --ieCompat flag is enabled, return a normal url() instead.
914
  $DATA_URI_MAX_KB = 32;
915
- $fileSizeInKB = round( strlen( $buf ) / 1024 );
916
- if ( $fileSizeInKB >= $DATA_URI_MAX_KB ) {
917
- $url = new Less_Tree_Url( ( $filePathNode ? $filePathNode : $mimetypeNode ), $this->currentFileInfo );
918
- return $url->compile( $this );
919
  }
920
 
921
- if ( $buf ) {
922
- $buf = $useBase64 ? base64_encode( $buf ) : rawurlencode( $buf );
923
  $filePath = '"data:' . $mimetype . ',' . $buf . '"';
924
  }
925
 
926
- return new Less_Tree_Url( new Less_Tree_Anonymous( $filePath ) );
927
  }
928
 
929
- // svg-gradient
930
- public function svggradient( $direction ) {
 
931
  $throw_message = 'svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]';
932
  $arguments = func_get_args();
933
 
934
- if ( count( $arguments ) < 3 ) {
935
  throw new Less_Exception_Compiler( $throw_message );
936
  }
937
 
938
- $stops = array_slice( $arguments, 1 );
939
  $gradientType = 'linear';
940
  $rectangleDimension = 'x="0" y="0" width="1" height="1"';
941
  $useBase64 = true;
942
  $directionValue = $direction->toCSS();
943
 
944
- switch ( $directionValue ) {
 
945
  case "to bottom":
946
  $gradientDirectionSvg = 'x1="0%" y1="0%" x2="0%" y2="100%"';
947
  break;
@@ -968,217 +967,220 @@ return $a / $b;
968
  '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="100%" viewBox="0 0 1 1" preserveAspectRatio="none">' .
969
  '<' . $gradientType . 'Gradient id="gradient" gradientUnits="userSpaceOnUse" ' . $gradientDirectionSvg . '>';
970
 
971
- for ( $i = 0; $i < count( $stops ); $i++ ) {
972
- if ( is_object( $stops[$i] ) && property_exists( $stops[$i], 'value' ) ) {
973
  $color = $stops[$i]->value[0];
974
  $position = $stops[$i]->value[1];
975
- } else {
976
  $color = $stops[$i];
977
  $position = null;
978
  }
979
 
980
- if ( !( $color instanceof Less_Tree_Color ) || ( !( ( $i === 0 || $i + 1 === count( $stops ) ) && $position === null ) && !( $position instanceof Less_Tree_Dimension ) ) ) {
981
  throw new Less_Exception_Compiler( $throw_message );
982
  }
983
- if ( $position ) {
984
  $positionValue = $position->toCSS();
985
- } elseif ( $i === 0 ) {
986
  $positionValue = '0%';
987
- } else {
988
  $positionValue = '100%';
989
  }
990
  $alpha = $color->alpha;
991
- $returner .= '<stop offset="' . $positionValue . '" stop-color="' . $color->toRGB() . '"' . ( $alpha < 1 ? ' stop-opacity="' . $alpha . '"' : '' ) . '/>';
992
  }
993
 
994
  $returner .= '</' . $gradientType . 'Gradient><rect ' . $rectangleDimension . ' fill="url(#gradient)" /></svg>';
995
 
996
- if ( $useBase64 ) {
997
- $returner = "'data:image/svg+xml;base64,".base64_encode( $returner )."'";
998
- } else {
 
999
  $returner = "'data:image/svg+xml,".$returner."'";
1000
  }
1001
 
1002
  return new Less_Tree_URL( new Less_Tree_Anonymous( $returner ) );
1003
  }
1004
 
 
1005
  /**
1006
  * Php version of javascript's `encodeURIComponent` function
1007
  *
1008
  * @param string $string The string to encode
1009
  * @return string The encoded string
1010
  */
1011
- public static function encodeURIComponent( $string ) {
1012
- $revert = array( '%21' => '!', '%2A' => '*', '%27' => "'", '%28' => '(', '%29' => ')' );
1013
- return strtr( rawurlencode( $string ), $revert );
1014
  }
1015
 
 
1016
  // Color Blending
1017
  // ref: http://www.w3.org/TR/compositing-1
1018
 
1019
- public function colorBlend( $mode, $color1, $color2 ) {
1020
  $ab = $color1->alpha; // backdrop
1021
  $as = $color2->alpha; // source
1022
  $r = array(); // result
1023
 
1024
- $ar = $as + $ab * ( 1 - $as );
1025
- for ( $i = 0; $i < 3; $i++ ) {
1026
  $cb = $color1->rgb[$i] / 255;
1027
  $cs = $color2->rgb[$i] / 255;
1028
  $cr = call_user_func( $mode, $cb, $cs );
1029
- if ( $ar ) {
1030
- $cr = ( $as * $cs + $ab * ( $cb - $as * ( $cb + $cs - $cr ) ) ) / $ar;
1031
  }
1032
  $r[$i] = $cr * 255;
1033
  }
1034
 
1035
- return new Less_Tree_Color( $r, $ar );
1036
  }
1037
 
1038
- public function multiply( $color1 = null, $color2 = null ) {
1039
- if ( !$color1 instanceof Less_Tree_Color ) {
1040
- throw new Less_Exception_Compiler( 'The first argument to multiply must be a color' . ( $color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
1041
  }
1042
- if ( !$color2 instanceof Less_Tree_Color ) {
1043
- throw new Less_Exception_Compiler( 'The second argument to multiply must be a color' . ( $color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
1044
  }
1045
 
1046
- return $this->colorBlend( array( $this,'colorBlendMultiply' ), $color1, $color2 );
1047
  }
1048
 
1049
- private function colorBlendMultiply( $cb, $cs ) {
1050
  return $cb * $cs;
1051
  }
1052
 
1053
- public function screen( $color1 = null, $color2 = null ) {
1054
- if ( !$color1 instanceof Less_Tree_Color ) {
1055
- throw new Less_Exception_Compiler( 'The first argument to screen must be a color' . ( $color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
1056
  }
1057
- if ( !$color2 instanceof Less_Tree_Color ) {
1058
- throw new Less_Exception_Compiler( 'The second argument to screen must be a color' . ( $color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
1059
  }
1060
 
1061
- return $this->colorBlend( array( $this,'colorBlendScreen' ), $color1, $color2 );
1062
  }
1063
 
1064
- private function colorBlendScreen( $cb, $cs ) {
1065
  return $cb + $cs - $cb * $cs;
1066
  }
1067
 
1068
- public function overlay( $color1 = null, $color2 = null ) {
1069
- if ( !$color1 instanceof Less_Tree_Color ) {
1070
- throw new Less_Exception_Compiler( 'The first argument to overlay must be a color' . ( $color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
1071
  }
1072
- if ( !$color2 instanceof Less_Tree_Color ) {
1073
- throw new Less_Exception_Compiler( 'The second argument to overlay must be a color' . ( $color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
1074
  }
1075
 
1076
- return $this->colorBlend( array( $this,'colorBlendOverlay' ), $color1, $color2 );
1077
  }
1078
 
1079
- private function colorBlendOverlay( $cb, $cs ) {
1080
  $cb *= 2;
1081
- return ( $cb <= 1 )
1082
- ? $this->colorBlendMultiply( $cb, $cs )
1083
- : $this->colorBlendScreen( $cb - 1, $cs );
1084
  }
1085
 
1086
- public function softlight( $color1 = null, $color2 = null ) {
1087
- if ( !$color1 instanceof Less_Tree_Color ) {
1088
- throw new Less_Exception_Compiler( 'The first argument to softlight must be a color' . ( $color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
1089
  }
1090
- if ( !$color2 instanceof Less_Tree_Color ) {
1091
- throw new Less_Exception_Compiler( 'The second argument to softlight must be a color' . ( $color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
1092
  }
1093
 
1094
- return $this->colorBlend( array( $this,'colorBlendSoftlight' ), $color1, $color2 );
1095
  }
1096
 
1097
- private function colorBlendSoftlight( $cb, $cs ) {
1098
  $d = 1;
1099
  $e = $cb;
1100
- if ( $cs > 0.5 ) {
1101
  $e = 1;
1102
- $d = ( $cb > 0.25 ) ? sqrt( $cb )
1103
- : ( ( 16 * $cb - 12 ) * $cb + 4 ) * $cb;
1104
  }
1105
- return $cb - ( 1 - 2 * $cs ) * $e * ( $d - $cb );
1106
  }
1107
 
1108
- public function hardlight( $color1 = null, $color2 = null ) {
1109
- if ( !$color1 instanceof Less_Tree_Color ) {
1110
- throw new Less_Exception_Compiler( 'The first argument to hardlight must be a color' . ( $color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
1111
  }
1112
- if ( !$color2 instanceof Less_Tree_Color ) {
1113
- throw new Less_Exception_Compiler( 'The second argument to hardlight must be a color' . ( $color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
1114
  }
1115
 
1116
- return $this->colorBlend( array( $this,'colorBlendHardlight' ), $color1, $color2 );
1117
  }
1118
 
1119
- private function colorBlendHardlight( $cb, $cs ) {
1120
- return $this->colorBlendOverlay( $cs, $cb );
1121
  }
1122
 
1123
- public function difference( $color1 = null, $color2 = null ) {
1124
- if ( !$color1 instanceof Less_Tree_Color ) {
1125
- throw new Less_Exception_Compiler( 'The first argument to difference must be a color' . ( $color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
1126
  }
1127
- if ( !$color2 instanceof Less_Tree_Color ) {
1128
- throw new Less_Exception_Compiler( 'The second argument to difference must be a color' . ( $color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
1129
  }
1130
 
1131
- return $this->colorBlend( array( $this,'colorBlendDifference' ), $color1, $color2 );
1132
  }
1133
 
1134
- private function colorBlendDifference( $cb, $cs ) {
1135
- return abs( $cb - $cs );
1136
  }
1137
 
1138
- public function exclusion( $color1 = null, $color2 = null ) {
1139
- if ( !$color1 instanceof Less_Tree_Color ) {
1140
- throw new Less_Exception_Compiler( 'The first argument to exclusion must be a color' . ( $color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
1141
  }
1142
- if ( !$color2 instanceof Less_Tree_Color ) {
1143
- throw new Less_Exception_Compiler( 'The second argument to exclusion must be a color' . ( $color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
1144
  }
1145
 
1146
- return $this->colorBlend( array( $this,'colorBlendExclusion' ), $color1, $color2 );
1147
  }
1148
 
1149
- private function colorBlendExclusion( $cb, $cs ) {
1150
  return $cb + $cs - 2 * $cb * $cs;
1151
  }
1152
 
1153
- public function average( $color1 = null, $color2 = null ) {
1154
- if ( !$color1 instanceof Less_Tree_Color ) {
1155
- throw new Less_Exception_Compiler( 'The first argument to average must be a color' . ( $color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
1156
  }
1157
- if ( !$color2 instanceof Less_Tree_Color ) {
1158
- throw new Less_Exception_Compiler( 'The second argument to average must be a color' . ( $color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
1159
  }
1160
 
1161
- return $this->colorBlend( array( $this,'colorBlendAverage' ), $color1, $color2 );
1162
  }
1163
 
1164
  // non-w3c functions:
1165
- public function colorBlendAverage( $cb, $cs ) {
1166
- return ( $cb + $cs ) / 2;
1167
  }
1168
 
1169
- public function negation( $color1 = null, $color2 = null ) {
1170
- if ( !$color1 instanceof Less_Tree_Color ) {
1171
- throw new Less_Exception_Compiler( 'The first argument to negation must be a color' . ( $color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
1172
  }
1173
- if ( !$color2 instanceof Less_Tree_Color ) {
1174
- throw new Less_Exception_Compiler( 'The second argument to negation must be a color' . ( $color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) );
1175
  }
1176
 
1177
- return $this->colorBlend( array( $this,'colorBlendNegation' ), $color1, $color2 );
1178
  }
1179
 
1180
- public function colorBlendNegation( $cb, $cs ) {
1181
- return 1 - abs( $cb + $cs - 1 );
1182
  }
1183
 
1184
  // ~ End of Color Blending
7
  * @subpackage function
8
  * @see http://lesscss.org/functions/
9
  */
10
+ class Less_Functions{
11
 
12
  public $env;
13
  public $currentFileInfo;
14
 
15
+ function __construct($env, $currentFileInfo = null ){
16
  $this->env = $env;
17
  $this->currentFileInfo = $currentFileInfo;
18
  }
20
  /**
21
  * @param string $op
22
  */
23
+ public static function operate( $op, $a, $b ){
24
+ switch ($op) {
25
+ case '+': return $a + $b;
26
+ case '-': return $a - $b;
27
+ case '*': return $a * $b;
28
+ case '/': return $a / $b;
 
 
 
 
29
  }
30
  }
31
 
32
+ public static function clamp($val, $max = 1){
33
+ return min( max($val, 0), $max);
34
  }
35
 
36
+ public static function fround( $value ){
37
+
38
+ if( $value === 0 ){
39
  return $value;
40
  }
41
 
42
+ if( Less_Parser::$options['numPrecision'] ){
43
+ $p = pow(10, Less_Parser::$options['numPrecision']);
44
+ return round( $value * $p) / $p;
45
  }
46
  return $value;
47
  }
48
 
49
+ public static function number($n){
50
+
51
+ if ($n instanceof Less_Tree_Dimension) {
52
+ return floatval( $n->unit->is('%') ? $n->value / 100 : $n->value);
53
+ } else if (is_numeric($n)) {
54
  return $n;
55
  } else {
56
+ throw new Less_Exception_Compiler("color functions take numbers as parameters");
57
  }
58
  }
59
 
60
+ public static function scaled($n, $size = 255 ){
61
+ if( $n instanceof Less_Tree_Dimension && $n->unit->is('%') ){
62
  return (float)$n->value * $size / 100;
63
  } else {
64
+ return Less_Functions::number($n);
65
  }
66
  }
67
 
68
+ public function rgb ($r = null, $g = null, $b = null){
69
+ if (is_null($r) || is_null($g) || is_null($b)) {
70
+ throw new Less_Exception_Compiler("rgb expects three parameters");
71
  }
72
+ return $this->rgba($r, $g, $b, 1.0);
73
  }
74
 
75
+ public function rgba($r = null, $g = null, $b = null, $a = null){
76
+ $rgb = array($r, $g, $b);
77
+ $rgb = array_map(array('Less_Functions','scaled'),$rgb);
78
 
79
+ $a = self::number($a);
80
+ return new Less_Tree_Color($rgb, $a);
81
  }
82
 
83
+ public function hsl($h, $s, $l){
84
+ return $this->hsla($h, $s, $l, 1.0);
85
  }
86
 
87
+ public function hsla($h, $s, $l, $a){
 
 
 
 
88
 
89
+ $h = fmod(self::number($h), 360) / 360; // Classic % operator will change float to int
90
+ $s = self::clamp(self::number($s));
91
+ $l = self::clamp(self::number($l));
92
+ $a = self::clamp(self::number($a));
93
+
94
+ $m2 = $l <= 0.5 ? $l * ($s + 1) : $l + $s - $l * $s;
95
 
96
  $m1 = $l * 2 - $m2;
97
 
98
+ return $this->rgba( self::hsla_hue($h + 1/3, $m1, $m2) * 255,
99
+ self::hsla_hue($h, $m1, $m2) * 255,
100
+ self::hsla_hue($h - 1/3, $m1, $m2) * 255,
101
+ $a);
102
  }
103
 
104
  /**
105
  * @param double $h
106
  */
107
+ public function hsla_hue($h, $m1, $m2){
108
+ $h = $h < 0 ? $h + 1 : ($h > 1 ? $h - 1 : $h);
109
+ if ($h * 6 < 1) return $m1 + ($m2 - $m1) * $h * 6;
110
+ else if ($h * 2 < 1) return $m2;
111
+ else if ($h * 3 < 2) return $m1 + ($m2 - $m1) * (2/3 - $h) * 6;
112
+ else return $m1;
113
  }
114
 
115
+ public function hsv($h, $s, $v) {
116
+ return $this->hsva($h, $s, $v, 1.0);
117
  }
118
 
119
  /**
120
  * @param double $a
121
  */
122
+ public function hsva($h, $s, $v, $a) {
123
+ $h = ((Less_Functions::number($h) % 360) / 360 ) * 360;
124
+ $s = Less_Functions::number($s);
125
+ $v = Less_Functions::number($v);
126
+ $a = Less_Functions::number($a);
127
 
128
+ $i = floor(($h / 60) % 6);
129
+ $f = ($h / 60) - $i;
130
 
131
  $vs = array( $v,
132
+ $v * (1 - $s),
133
+ $v * (1 - $f * $s),
134
+ $v * (1 - (1 - $f) * $s));
135
+
136
+ $perm = array(array(0, 3, 1),
137
+ array(2, 0, 1),
138
+ array(1, 0, 3),
139
+ array(1, 2, 0),
140
+ array(3, 1, 0),
141
+ array(0, 1, 2));
142
+
143
+ return $this->rgba($vs[$perm[$i][0]] * 255,
144
  $vs[$perm[$i][1]] * 255,
145
  $vs[$perm[$i][2]] * 255,
146
+ $a);
147
  }
148
 
149
+ public function hue($color = null){
150
+ if (!$color instanceof Less_Tree_Color) {
151
+ throw new Less_Exception_Compiler('The first argument to hue must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
152
  }
153
 
154
  $c = $color->toHSL();
155
+ return new Less_Tree_Dimension(Less_Parser::round($c['h']));
156
  }
157
 
158
+ public function saturation($color = null){
159
+ if (!$color instanceof Less_Tree_Color) {
160
+ throw new Less_Exception_Compiler('The first argument to saturation must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
161
  }
162
 
163
  $c = $color->toHSL();
164
+ return new Less_Tree_Dimension(Less_Parser::round($c['s'] * 100), '%');
165
  }
166
 
167
+ public function lightness($color = null){
168
+ if (!$color instanceof Less_Tree_Color) {
169
+ throw new Less_Exception_Compiler('The first argument to lightness must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
170
  }
171
 
172
  $c = $color->toHSL();
173
+ return new Less_Tree_Dimension(Less_Parser::round($c['l'] * 100), '%');
174
  }
175
 
176
+ public function hsvhue( $color = null ){
177
+ if (!$color instanceof Less_Tree_Color) {
178
+ throw new Less_Exception_Compiler('The first argument to hsvhue must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
179
  }
180
 
181
  $hsv = $color->toHSV();
182
+ return new Less_Tree_Dimension( Less_Parser::round($hsv['h']) );
183
  }
184
 
185
+
186
+ public function hsvsaturation( $color = null ){
187
+ if (!$color instanceof Less_Tree_Color) {
188
+ throw new Less_Exception_Compiler('The first argument to hsvsaturation must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
189
  }
190
 
191
  $hsv = $color->toHSV();
192
+ return new Less_Tree_Dimension( Less_Parser::round($hsv['s'] * 100), '%' );
193
  }
194
 
195
+ public function hsvvalue( $color = null ){
196
+ if (!$color instanceof Less_Tree_Color) {
197
+ throw new Less_Exception_Compiler('The first argument to hsvvalue must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
198
  }
199
 
200
  $hsv = $color->toHSV();
201
+ return new Less_Tree_Dimension( Less_Parser::round($hsv['v'] * 100), '%' );
202
  }
203
 
204
+ public function red($color = null) {
205
+ if (!$color instanceof Less_Tree_Color) {
206
+ throw new Less_Exception_Compiler('The first argument to red must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
207
  }
208
 
209
  return new Less_Tree_Dimension( $color->rgb[0] );
210
  }
211
 
212
+ public function green($color = null) {
213
+ if (!$color instanceof Less_Tree_Color) {
214
+ throw new Less_Exception_Compiler('The first argument to green must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
215
  }
216
 
217
  return new Less_Tree_Dimension( $color->rgb[1] );
218
  }
219
 
220
+ public function blue($color = null) {
221
+ if (!$color instanceof Less_Tree_Color) {
222
+ throw new Less_Exception_Compiler('The first argument to blue must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
223
  }
224
 
225
  return new Less_Tree_Dimension( $color->rgb[2] );
226
  }
227
 
228
+ public function alpha($color = null){
229
+ if (!$color instanceof Less_Tree_Color) {
230
+ throw new Less_Exception_Compiler('The first argument to alpha must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
231
  }
232
 
233
  $c = $color->toHSL();
234
+ return new Less_Tree_Dimension($c['a']);
235
  }
236
 
237
+ public function luma ($color = null) {
238
+ if (!$color instanceof Less_Tree_Color) {
239
+ throw new Less_Exception_Compiler('The first argument to luma must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
240
  }
241
 
242
+ return new Less_Tree_Dimension(Less_Parser::round( $color->luma() * $color->alpha * 100), '%');
243
  }
244
 
245
+ public function luminance( $color = null ){
246
+ if (!$color instanceof Less_Tree_Color) {
247
+ throw new Less_Exception_Compiler('The first argument to luminance must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
248
  }
249
 
250
  $luminance =
251
+ (0.2126 * $color->rgb[0] / 255)
252
+ + (0.7152 * $color->rgb[1] / 255)
253
+ + (0.0722 * $color->rgb[2] / 255);
254
 
255
+ return new Less_Tree_Dimension(Less_Parser::round( $luminance * $color->alpha * 100), '%');
256
  }
257
 
258
+ public function saturate($color = null, $amount = null){
259
  // filter: saturate(3.2);
260
  // should be kept as is, so check for color
261
+ if ($color instanceof Less_Tree_Dimension) {
262
  return null;
263
  }
264
 
265
+ if (!$color instanceof Less_Tree_Color) {
266
+ throw new Less_Exception_Compiler('The first argument to saturate must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
267
  }
268
+ if (!$amount instanceof Less_Tree_Dimension) {
269
+ throw new Less_Exception_Compiler('The second argument to saturate must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
270
  }
271
 
272
  $hsl = $color->toHSL();
273
 
274
  $hsl['s'] += $amount->value / 100;
275
+ $hsl['s'] = self::clamp($hsl['s']);
276
 
277
+ return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
278
  }
279
 
280
  /**
281
  * @param Less_Tree_Dimension $amount
282
  */
283
+ public function desaturate($color = null, $amount = null){
284
+ if (!$color instanceof Less_Tree_Color) {
285
+ throw new Less_Exception_Compiler('The first argument to desaturate must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
286
  }
287
+ if (!$amount instanceof Less_Tree_Dimension) {
288
+ throw new Less_Exception_Compiler('The second argument to desaturate must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
289
  }
290
 
291
  $hsl = $color->toHSL();
292
 
293
  $hsl['s'] -= $amount->value / 100;
294
+ $hsl['s'] = self::clamp($hsl['s']);
295
 
296
+ return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
297
  }
298
 
299
+
300
+
301
+ public function lighten($color = null, $amount=null){
302
+ if (!$color instanceof Less_Tree_Color) {
303
+ throw new Less_Exception_Compiler('The first argument to lighten must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
304
  }
305
+ if (!$amount instanceof Less_Tree_Dimension) {
306
+ throw new Less_Exception_Compiler('The second argument to lighten must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
307
  }
308
 
309
  $hsl = $color->toHSL();
310
 
311
  $hsl['l'] += $amount->value / 100;
312
+ $hsl['l'] = self::clamp($hsl['l']);
313
 
314
+ return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
315
  }
316
 
317
+ public function darken($color = null, $amount = null){
318
+ if (!$color instanceof Less_Tree_Color) {
319
+ throw new Less_Exception_Compiler('The first argument to darken must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
320
  }
321
+ if (!$amount instanceof Less_Tree_Dimension) {
322
+ throw new Less_Exception_Compiler('The second argument to darken must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
323
  }
324
 
325
  $hsl = $color->toHSL();
326
  $hsl['l'] -= $amount->value / 100;
327
+ $hsl['l'] = self::clamp($hsl['l']);
328
 
329
+ return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
330
  }
331
 
332
+ public function fadein($color = null, $amount = null){
333
+ if (!$color instanceof Less_Tree_Color) {
334
+ throw new Less_Exception_Compiler('The first argument to fadein must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
335
  }
336
+ if (!$amount instanceof Less_Tree_Dimension) {
337
+ throw new Less_Exception_Compiler('The second argument to fadein must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
338
  }
339
 
340
  $hsl = $color->toHSL();
341
  $hsl['a'] += $amount->value / 100;
342
+ $hsl['a'] = self::clamp($hsl['a']);
343
+ return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
344
  }
345
 
346
+ public function fadeout($color = null, $amount = null){
347
+ if (!$color instanceof Less_Tree_Color) {
348
+ throw new Less_Exception_Compiler('The first argument to fadeout must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
349
  }
350
+ if (!$amount instanceof Less_Tree_Dimension) {
351
+ throw new Less_Exception_Compiler('The second argument to fadeout must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
352
  }
353
 
354
  $hsl = $color->toHSL();
355
  $hsl['a'] -= $amount->value / 100;
356
+ $hsl['a'] = self::clamp($hsl['a']);
357
+ return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
358
  }
359
 
360
+ public function fade($color = null, $amount = null){
361
+ if (!$color instanceof Less_Tree_Color) {
362
+ throw new Less_Exception_Compiler('The first argument to fade must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
363
  }
364
+ if (!$amount instanceof Less_Tree_Dimension) {
365
+ throw new Less_Exception_Compiler('The second argument to fade must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
366
  }
367
 
368
  $hsl = $color->toHSL();
369
 
370
  $hsl['a'] = $amount->value / 100;
371
+ $hsl['a'] = self::clamp($hsl['a']);
372
+ return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
373
  }
374
 
375
+
376
+
377
+ public function spin($color = null, $amount = null){
378
+ if (!$color instanceof Less_Tree_Color) {
379
+ throw new Less_Exception_Compiler('The first argument to spin must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
380
  }
381
+ if (!$amount instanceof Less_Tree_Dimension) {
382
+ throw new Less_Exception_Compiler('The second argument to spin must be a number' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
383
  }
384
 
385
  $hsl = $color->toHSL();
386
+ $hue = fmod($hsl['h'] + $amount->value, 360);
387
 
388
  $hsl['h'] = $hue < 0 ? 360 + $hue : $hue;
389
 
390
+ return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
391
  }
392
 
393
  //
398
  /**
399
  * @param Less_Tree_Color $color1
400
  */
401
+ public function mix($color1 = null, $color2 = null, $weight = null){
402
+ if (!$color1 instanceof Less_Tree_Color) {
403
+ throw new Less_Exception_Compiler('The first argument to mix must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
404
  }
405
+ if (!$color2 instanceof Less_Tree_Color) {
406
+ throw new Less_Exception_Compiler('The second argument to mix must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
407
  }
408
+ if (!$weight) {
409
+ $weight = new Less_Tree_Dimension('50', '%');
410
  }
411
+ if (!$weight instanceof Less_Tree_Dimension) {
412
+ throw new Less_Exception_Compiler('The third argument to contrast must be a percentage' . ($weight instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
413
  }
414
 
415
  $p = $weight->value / 100.0;
418
  $hsl2 = $color2->toHSL();
419
  $a = $hsl1['a'] - $hsl2['a'];
420
 
421
+ $w1 = (((($w * $a) == -1) ? $w : ($w + $a) / (1 + $w * $a)) + 1) / 2;
422
  $w2 = 1 - $w1;
423
 
424
+ $rgb = array($color1->rgb[0] * $w1 + $color2->rgb[0] * $w2,
425
  $color1->rgb[1] * $w1 + $color2->rgb[1] * $w2,
426
+ $color1->rgb[2] * $w1 + $color2->rgb[2] * $w2);
427
 
428
+ $alpha = $color1->alpha * $p + $color2->alpha * (1 - $p);
429
 
430
+ return new Less_Tree_Color($rgb, $alpha);
431
  }
432
 
433
+ public function greyscale($color){
434
+ return $this->desaturate($color, new Less_Tree_Dimension(100,'%'));
435
  }
436
 
437
+
438
+ public function contrast( $color, $dark = null, $light = null, $threshold = null){
439
  // filter: contrast(3.2);
440
  // should be kept as is, so check for color
441
+ if (!$color instanceof Less_Tree_Color) {
442
  return null;
443
  }
444
+ if( !$light ){
445
+ $light = $this->rgba(255, 255, 255, 1.0);
446
  }
447
+ if( !$dark ){
448
+ $dark = $this->rgba(0, 0, 0, 1.0);
449
  }
450
 
451
+ if (!$dark instanceof Less_Tree_Color) {
452
+ throw new Less_Exception_Compiler('The second argument to contrast must be a color' . ($dark instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
453
  }
454
+ if (!$light instanceof Less_Tree_Color) {
455
+ throw new Less_Exception_Compiler('The third argument to contrast must be a color' . ($light instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
456
  }
457
 
458
+ //Figure out which is actually light and dark!
459
+ if( $dark->luma() > $light->luma() ){
460
  $t = $light;
461
  $light = $dark;
462
  $dark = $t;
463
  }
464
+ if( !$threshold ){
465
  $threshold = 0.43;
466
  } else {
467
+ $threshold = Less_Functions::number($threshold);
468
  }
469
 
470
+ if( $color->luma() < $threshold ){
471
  return $light;
472
  } else {
473
  return $dark;
474
  }
475
  }
476
 
477
+ public function e ($str){
478
+ if( is_string($str) ){
479
+ return new Less_Tree_Anonymous($str);
480
  }
481
+ return new Less_Tree_Anonymous($str instanceof Less_Tree_JavaScript ? $str->expression : $str->value);
482
  }
483
 
484
+ public function escape ($str){
485
+
486
+ $revert = array('%21'=>'!', '%2A'=>'*', '%27'=>"'",'%3F'=>'?','%26'=>'&','%2C'=>',','%2F'=>'/','%40'=>'@','%2B'=>'+','%24'=>'$');
487
 
488
+ return new Less_Tree_Anonymous(strtr(rawurlencode($str->value), $revert));
489
  }
490
 
491
+
492
  /**
493
  * todo: This function will need some additional work to make it work the same as less.js
494
  *
495
  */
496
+ public function replace( $string, $pattern, $replacement, $flags = null ){
497
  $result = $string->value;
498
 
499
+ $expr = '/'.str_replace('/','\\/',$pattern->value).'/';
500
+ if( $flags && $flags->value){
501
+ $expr .= self::replace_flags($flags->value);
502
  }
503
 
504
+ $result = preg_replace($expr,$replacement->value,$result);
505
+
506
 
507
+ if( property_exists($string,'quote') ){
508
+ return new Less_Tree_Quoted( $string->quote, $result, $string->escaped);
509
  }
510
  return new Less_Tree_Quoted( '', $result );
511
  }
512
 
513
+ public static function replace_flags($flags){
514
+ $flags = str_split($flags,1);
515
  $new_flags = '';
516
 
517
+ foreach($flags as $flag){
518
+ switch($flag){
519
  case 'e':
520
  case 'g':
521
  break;
529
  return $new_flags;
530
  }
531
 
532
+ public function _percent(){
533
+ $string = func_get_arg(0);
534
 
535
  $args = func_get_args();
536
+ array_shift($args);
537
  $result = $string->value;
538
 
539
+ foreach($args as $arg){
540
+ if( preg_match('/%[sda]/i',$result, $token) ){
541
  $token = $token[0];
542
+ $value = stristr($token, 's') ? $arg->value : $arg->toCSS();
543
+ $value = preg_match('/[A-Z]$/', $token) ? urlencode($value) : $value;
544
+ $result = preg_replace('/%[sda]/i',$value, $result, 1);
545
  }
546
  }
547
+ $result = str_replace('%%', '%', $result);
548
 
549
+ return new Less_Tree_Quoted( $string->quote , $result, $string->escaped);
550
  }
551
 
552
+ public function unit( $val, $unit = null) {
553
+ if( !($val instanceof Less_Tree_Dimension) ){
554
+ throw new Less_Exception_Compiler('The first argument to unit must be a number' . ($val instanceof Less_Tree_Operation ? '. Have you forgotten parenthesis?' : '.') );
555
  }
556
 
557
+ if( $unit ){
558
+ if( $unit instanceof Less_Tree_Keyword ){
559
  $unit = $unit->value;
560
  } else {
561
  $unit = $unit->toCSS();
563
  } else {
564
  $unit = "";
565
  }
566
+ return new Less_Tree_Dimension($val->value, $unit );
567
  }
568
 
569
+ public function convert($val, $unit){
570
+ return $val->convertTo($unit->value);
571
  }
572
 
573
+ public function round($n, $f = false) {
574
+
575
  $fraction = 0;
576
+ if( $f !== false ){
577
  $fraction = $f->value;
578
  }
579
 
580
+ return $this->_math('Less_Parser::round',null, $n, $fraction);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
581
  }
582
 
583
+ public function pi(){
584
+ return new Less_Tree_Dimension(M_PI);
 
585
  }
586
 
587
+ public function mod($a, $b) {
588
+ return new Less_Tree_Dimension( $a->value % $b->value, $a->unit);
589
  }
590
 
 
 
 
591
 
 
 
 
592
 
593
+ public function pow($x, $y) {
594
+ if( is_numeric($x) && is_numeric($y) ){
595
+ $x = new Less_Tree_Dimension($x);
596
+ $y = new Less_Tree_Dimension($y);
597
+ }elseif( !($x instanceof Less_Tree_Dimension) || !($y instanceof Less_Tree_Dimension) ){
598
+ throw new Less_Exception_Compiler('Arguments must be numbers');
599
+ }
600
 
601
+ return new Less_Tree_Dimension( pow($x->value, $y->value), $x->unit );
 
602
  }
603
 
604
+ // var mathFunctions = [{name:"ce ...
605
+ public function ceil( $n ){ return $this->_math('ceil', null, $n); }
606
+ public function floor( $n ){ return $this->_math('floor', null, $n); }
607
+ public function sqrt( $n ){ return $this->_math('sqrt', null, $n); }
608
+ public function abs( $n ){ return $this->_math('abs', null, $n); }
609
 
610
+ public function tan( $n ){ return $this->_math('tan', '', $n); }
611
+ public function sin( $n ){ return $this->_math('sin', '', $n); }
612
+ public function cos( $n ){ return $this->_math('cos', '', $n); }
613
 
614
+ public function atan( $n ){ return $this->_math('atan', 'rad', $n); }
615
+ public function asin( $n ){ return $this->_math('asin', 'rad', $n); }
616
+ public function acos( $n ){ return $this->_math('acos', 'rad', $n); }
 
 
 
 
617
 
618
  private function _math() {
619
  $args = func_get_args();
620
+ $fn = array_shift($args);
621
+ $unit = array_shift($args);
622
 
623
+ if ($args[0] instanceof Less_Tree_Dimension) {
624
 
625
+ if( $unit === null ){
626
  $unit = $args[0]->unit;
627
+ }else{
628
  $args[0] = $args[0]->unify();
629
  }
630
  $args[0] = (float)$args[0]->value;
631
+ return new Less_Tree_Dimension( call_user_func_array($fn, $args), $unit);
632
+ } else if (is_numeric($args[0])) {
633
+ return call_user_func_array($fn,$args);
634
  } else {
635
+ throw new Less_Exception_Compiler("math functions take numbers as parameters");
636
  }
637
  }
638
 
639
  /**
640
  * @param boolean $isMin
641
  */
642
+ private function _minmax( $isMin, $args ){
643
+
644
+ $arg_count = count($args);
645
 
646
+ if( $arg_count < 1 ){
647
+ throw new Less_Exception_Compiler( 'one or more arguments required');
648
  }
649
 
650
  $j = null;
651
  $unitClone = null;
652
  $unitStatic = null;
653
 
654
+
655
  $order = array(); // elems only contains original argument values.
656
  $values = array(); // key is the unit.toString() for unified tree.Dimension values,
657
  // value is the index into the order array.
658
 
659
+
660
+ for( $i = 0; $i < $arg_count; $i++ ){
661
  $current = $args[$i];
662
+ if( !($current instanceof Less_Tree_Dimension) ){
663
+ if( is_array($args[$i]->value) ){
664
  $args[] = $args[$i]->value;
665
  }
666
  continue;
667
  }
668
 
669
+ if( $current->unit->toString() === '' && !$unitClone ){
670
+ $temp = new Less_Tree_Dimension($current->value, $unitClone);
671
  $currentUnified = $temp->unify();
672
+ }else{
673
  $currentUnified = $current->unify();
674
  }
675
 
676
+ if( $currentUnified->unit->toString() === "" && !$unitStatic ){
677
  $unit = $unitStatic;
678
+ }else{
679
  $unit = $currentUnified->unit->toString();
680
  }
681
 
682
+ if( $unit !== '' && !$unitStatic || $unit !== '' && $order[0]->unify()->unit->toString() === "" ){
683
  $unitStatic = $unit;
684
  }
685
 
686
+ if( $unit != '' && !$unitClone ){
687
  $unitClone = $current->unit->toString();
688
  }
689
 
690
+ if( isset($values['']) && $unit !== '' && $unit === $unitStatic ){
691
  $j = $values[''];
692
+ }elseif( isset($values[$unit]) ){
693
  $j = $values[$unit];
694
+ }else{
695
 
696
+ if( $unitStatic && $unit !== $unitStatic ){
697
+ throw new Less_Exception_Compiler( 'incompatible types');
698
  }
699
+ $values[$unit] = count($order);
700
  $order[] = $current;
701
  continue;
702
  }
703
 
704
+
705
+ if( $order[$j]->unit->toString() === "" && $unitClone ){
706
+ $temp = new Less_Tree_Dimension( $order[$j]->value, $unitClone);
707
  $referenceUnified = $temp->unify();
708
+ }else{
709
  $referenceUnified = $order[$j]->unify();
710
  }
711
+ if( ($isMin && $currentUnified->value < $referenceUnified->value) || (!$isMin && $currentUnified->value > $referenceUnified->value) ){
712
  $order[$j] = $current;
713
  }
714
  }
715
 
716
+ if( count($order) == 1 ){
717
  return $order[0];
718
  }
719
  $args = array();
720
+ foreach($order as $a){
721
+ $args[] = $a->toCSS($this->env);
722
  }
723
+ return new Less_Tree_Anonymous( ($isMin?'min(':'max(') . implode(Less_Environment::$_outputMap[','],$args).')');
724
  }
725
 
726
+ public function min(){
727
  $args = func_get_args();
728
  return $this->_minmax( true, $args );
729
  }
730
 
731
+ public function max(){
732
  $args = func_get_args();
733
  return $this->_minmax( false, $args );
734
  }
735
 
736
+ public function getunit($n){
737
+ return new Less_Tree_Anonymous($n->unit);
738
  }
739
 
740
+ public function argb($color) {
741
+ if (!$color instanceof Less_Tree_Color) {
742
+ throw new Less_Exception_Compiler('The first argument to argb must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
743
  }
744
 
745
+ return new Less_Tree_Anonymous($color->toARGB());
746
  }
747
 
748
+ public function percentage($n) {
749
+ return new Less_Tree_Dimension($n->value * 100, '%');
750
  }
751
 
752
+ public function color($n) {
753
+
754
+ if( $n instanceof Less_Tree_Quoted ){
755
  $colorCandidate = $n->value;
756
+ $returnColor = Less_Tree_Color::fromKeyword($colorCandidate);
757
+ if( $returnColor ){
758
  return $returnColor;
759
  }
760
+ if( preg_match('/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/',$colorCandidate) ){
761
+ return new Less_Tree_Color(substr($colorCandidate, 1));
762
  }
763
+ throw new Less_Exception_Compiler("argument must be a color keyword or 3/6 digit hex e.g. #FFF");
764
  } else {
765
+ throw new Less_Exception_Compiler("argument must be a string");
766
  }
767
  }
768
 
769
+
770
+ public function iscolor($n) {
771
+ return $this->_isa($n, 'Less_Tree_Color');
772
  }
773
 
774
+ public function isnumber($n) {
775
+ return $this->_isa($n, 'Less_Tree_Dimension');
776
  }
777
 
778
+ public function isstring($n) {
779
+ return $this->_isa($n, 'Less_Tree_Quoted');
780
  }
781
 
782
+ public function iskeyword($n) {
783
+ return $this->_isa($n, 'Less_Tree_Keyword');
784
  }
785
 
786
+ public function isurl($n) {
787
+ return $this->_isa($n, 'Less_Tree_Url');
788
  }
789
 
790
+ public function ispixel($n) {
791
+ return $this->isunit($n, 'px');
792
  }
793
 
794
+ public function ispercentage($n) {
795
+ return $this->isunit($n, '%');
796
  }
797
 
798
+ public function isem($n) {
799
+ return $this->isunit($n, 'em');
800
  }
801
 
802
  /**
803
  * @param string $unit
804
  */
805
+ public function isunit( $n, $unit ){
806
+
807
+ if( is_object($unit) && property_exists($unit,'value') ){
808
  $unit = $unit->value;
809
  }
810
 
811
+ return ($n instanceof Less_Tree_Dimension) && $n->unit->is($unit) ? new Less_Tree_Keyword('true') : new Less_Tree_Keyword('false');
812
  }
813
 
814
  /**
815
  * @param string $type
816
  */
817
+ private function _isa($n, $type) {
818
+ return is_a($n, $type) ? new Less_Tree_Keyword('true') : new Less_Tree_Keyword('false');
819
  }
820
 
821
+ public function tint($color, $amount = null) {
822
+ return $this->mix( $this->rgb(255,255,255), $color, $amount);
823
  }
824
 
825
+ public function shade($color, $amount = null) {
826
+ return $this->mix($this->rgb(0, 0, 0), $color, $amount);
827
  }
828
 
829
+ public function extract($values, $index ){
830
  $index = (int)$index->value - 1; // (1-based index)
831
  // handle non-array values as an array of length 1
832
  // return 'undefined' if index is invalid
833
+ if( property_exists($values,'value') && is_array($values->value) ){
834
+ if( isset($values->value[$index]) ){
835
  return $values->value[$index];
836
  }
837
  return null;
838
 
839
+ }elseif( (int)$index === 0 ){
840
  return $values;
841
  }
842
 
843
  return null;
844
  }
845
 
846
+ public function length($values){
847
+ $n = (property_exists($values,'value') && is_array($values->value)) ? count($values->value) : 1;
848
+ return new Less_Tree_Dimension($n);
849
  }
850
 
851
+ public function datauri($mimetypeNode, $filePathNode = null ) {
852
+
853
  $filePath = ( $filePathNode ? $filePathNode->value : null );
854
  $mimetype = $mimetypeNode->value;
855
 
856
  $args = 2;
857
+ if( !$filePath ){
858
  $filePath = $mimetype;
859
  $args = 1;
860
  }
861
 
862
+ $filePath = str_replace('\\','/',$filePath);
863
+ if( Less_Environment::isPathRelative($filePath) ){
864
 
865
+ if( Less_Parser::$options['relativeUrls'] ){
866
  $temp = $this->currentFileInfo['currentDirectory'];
867
  } else {
868
  $temp = $this->currentFileInfo['entryPath'];
869
  }
870
 
871
+ if( !empty($temp) ){
872
+ $filePath = Less_Environment::normalizePath(rtrim($temp,'/').'/'.$filePath);
873
  }
874
 
875
  }
876
 
877
+
878
  // detect the mimetype if not given
879
+ if( $args < 2 ){
880
 
881
  /* incomplete
882
  $mime = require('mime');
888
  if (useBase64) mimetype += ';base64';
889
  */
890
 
891
+ $mimetype = Less_Mime::lookup($filePath);
892
 
893
+ $charset = Less_Mime::charsets_lookup($mimetype);
894
+ $useBase64 = !in_array($charset,array('US-ASCII', 'UTF-8'));
895
+ if( $useBase64 ){ $mimetype .= ';base64'; }
 
896
 
897
+ }else{
898
+ $useBase64 = preg_match('/;base64$/',$mimetype);
899
  }
900
 
901
+
902
+ if( file_exists($filePath) ){
903
+ $buf = @file_get_contents($filePath);
904
+ }else{
905
  $buf = false;
906
  }
907
 
908
+
909
  // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded
910
  // and the --ieCompat flag is enabled, return a normal url() instead.
911
  $DATA_URI_MAX_KB = 32;
912
+ $fileSizeInKB = round( strlen($buf) / 1024 );
913
+ if( $fileSizeInKB >= $DATA_URI_MAX_KB ){
914
+ $url = new Less_Tree_Url( ($filePathNode ? $filePathNode : $mimetypeNode), $this->currentFileInfo);
915
+ return $url->compile($this);
916
  }
917
 
918
+ if( $buf ){
919
+ $buf = $useBase64 ? base64_encode($buf) : rawurlencode($buf);
920
  $filePath = '"data:' . $mimetype . ',' . $buf . '"';
921
  }
922
 
923
+ return new Less_Tree_Url( new Less_Tree_Anonymous($filePath) );
924
  }
925
 
926
+ //svg-gradient
927
+ public function svggradient( $direction ){
928
+
929
  $throw_message = 'svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]';
930
  $arguments = func_get_args();
931
 
932
+ if( count($arguments) < 3 ){
933
  throw new Less_Exception_Compiler( $throw_message );
934
  }
935
 
936
+ $stops = array_slice($arguments,1);
937
  $gradientType = 'linear';
938
  $rectangleDimension = 'x="0" y="0" width="1" height="1"';
939
  $useBase64 = true;
940
  $directionValue = $direction->toCSS();
941
 
942
+
943
+ switch( $directionValue ){
944
  case "to bottom":
945
  $gradientDirectionSvg = 'x1="0%" y1="0%" x2="0%" y2="100%"';
946
  break;
967
  '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="100%" viewBox="0 0 1 1" preserveAspectRatio="none">' .
968
  '<' . $gradientType . 'Gradient id="gradient" gradientUnits="userSpaceOnUse" ' . $gradientDirectionSvg . '>';
969
 
970
+ for( $i = 0; $i < count($stops); $i++ ){
971
+ if( is_object($stops[$i]) && property_exists($stops[$i],'value') ){
972
  $color = $stops[$i]->value[0];
973
  $position = $stops[$i]->value[1];
974
+ }else{
975
  $color = $stops[$i];
976
  $position = null;
977
  }
978
 
979
+ if( !($color instanceof Less_Tree_Color) || (!(($i === 0 || $i+1 === count($stops)) && $position === null) && !($position instanceof Less_Tree_Dimension)) ){
980
  throw new Less_Exception_Compiler( $throw_message );
981
  }
982
+ if( $position ){
983
  $positionValue = $position->toCSS();
984
+ }elseif( $i === 0 ){
985
  $positionValue = '0%';
986
+ }else{
987
  $positionValue = '100%';
988
  }
989
  $alpha = $color->alpha;
990
+ $returner .= '<stop offset="' . $positionValue . '" stop-color="' . $color->toRGB() . '"' . ($alpha < 1 ? ' stop-opacity="' . $alpha . '"' : '') . '/>';
991
  }
992
 
993
  $returner .= '</' . $gradientType . 'Gradient><rect ' . $rectangleDimension . ' fill="url(#gradient)" /></svg>';
994
 
995
+
996
+ if( $useBase64 ){
997
+ $returner = "'data:image/svg+xml;base64,".base64_encode($returner)."'";
998
+ }else{
999
  $returner = "'data:image/svg+xml,".$returner."'";
1000
  }
1001
 
1002
  return new Less_Tree_URL( new Less_Tree_Anonymous( $returner ) );
1003
  }
1004
 
1005
+
1006
  /**
1007
  * Php version of javascript's `encodeURIComponent` function
1008
  *
1009
  * @param string $string The string to encode
1010
  * @return string The encoded string
1011
  */
1012
+ public static function encodeURIComponent($string){
1013
+ $revert = array('%21' => '!', '%2A' => '*', '%27' => "'", '%28' => '(', '%29' => ')');
1014
+ return strtr(rawurlencode($string), $revert);
1015
  }
1016
 
1017
+
1018
  // Color Blending
1019
  // ref: http://www.w3.org/TR/compositing-1
1020
 
1021
+ public function colorBlend( $mode, $color1, $color2 ){
1022
  $ab = $color1->alpha; // backdrop
1023
  $as = $color2->alpha; // source
1024
  $r = array(); // result
1025
 
1026
+ $ar = $as + $ab * (1 - $as);
1027
+ for( $i = 0; $i < 3; $i++ ){
1028
  $cb = $color1->rgb[$i] / 255;
1029
  $cs = $color2->rgb[$i] / 255;
1030
  $cr = call_user_func( $mode, $cb, $cs );
1031
+ if( $ar ){
1032
+ $cr = ($as * $cs + $ab * ($cb - $as * ($cb + $cs - $cr))) / $ar;
1033
  }
1034
  $r[$i] = $cr * 255;
1035
  }
1036
 
1037
+ return new Less_Tree_Color($r, $ar);
1038
  }
1039
 
1040
+ public function multiply($color1 = null, $color2 = null ){
1041
+ if (!$color1 instanceof Less_Tree_Color) {
1042
+ throw new Less_Exception_Compiler('The first argument to multiply must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
1043
  }
1044
+ if (!$color2 instanceof Less_Tree_Color) {
1045
+ throw new Less_Exception_Compiler('The second argument to multiply must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
1046
  }
1047
 
1048
+ return $this->colorBlend( array($this,'colorBlendMultiply'), $color1, $color2 );
1049
  }
1050
 
1051
+ private function colorBlendMultiply($cb, $cs){
1052
  return $cb * $cs;
1053
  }
1054
 
1055
+ public function screen($color1 = null, $color2 = null ){
1056
+ if (!$color1 instanceof Less_Tree_Color) {
1057
+ throw new Less_Exception_Compiler('The first argument to screen must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
1058
  }
1059
+ if (!$color2 instanceof Less_Tree_Color) {
1060
+ throw new Less_Exception_Compiler('The second argument to screen must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
1061
  }
1062
 
1063
+ return $this->colorBlend( array($this,'colorBlendScreen'), $color1, $color2 );
1064
  }
1065
 
1066
+ private function colorBlendScreen( $cb, $cs){
1067
  return $cb + $cs - $cb * $cs;
1068
  }
1069
 
1070
+ public function overlay($color1 = null, $color2 = null){
1071
+ if (!$color1 instanceof Less_Tree_Color) {
1072
+ throw new Less_Exception_Compiler('The first argument to overlay must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
1073
  }
1074
+ if (!$color2 instanceof Less_Tree_Color) {
1075
+ throw new Less_Exception_Compiler('The second argument to overlay must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
1076
  }
1077
 
1078
+ return $this->colorBlend( array($this,'colorBlendOverlay'), $color1, $color2 );
1079
  }
1080
 
1081
+ private function colorBlendOverlay($cb, $cs ){
1082
  $cb *= 2;
1083
+ return ($cb <= 1)
1084
+ ? $this->colorBlendMultiply($cb, $cs)
1085
+ : $this->colorBlendScreen($cb - 1, $cs);
1086
  }
1087
 
1088
+ public function softlight($color1 = null, $color2 = null){
1089
+ if (!$color1 instanceof Less_Tree_Color) {
1090
+ throw new Less_Exception_Compiler('The first argument to softlight must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
1091
  }
1092
+ if (!$color2 instanceof Less_Tree_Color) {
1093
+ throw new Less_Exception_Compiler('The second argument to softlight must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
1094
  }
1095
 
1096
+ return $this->colorBlend( array($this,'colorBlendSoftlight'), $color1, $color2 );
1097
  }
1098
 
1099
+ private function colorBlendSoftlight($cb, $cs ){
1100
  $d = 1;
1101
  $e = $cb;
1102
+ if( $cs > 0.5 ){
1103
  $e = 1;
1104
+ $d = ($cb > 0.25) ? sqrt($cb)
1105
+ : ((16 * $cb - 12) * $cb + 4) * $cb;
1106
  }
1107
+ return $cb - (1 - 2 * $cs) * $e * ($d - $cb);
1108
  }
1109
 
1110
+ public function hardlight($color1 = null, $color2 = null){
1111
+ if (!$color1 instanceof Less_Tree_Color) {
1112
+ throw new Less_Exception_Compiler('The first argument to hardlight must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
1113
  }
1114
+ if (!$color2 instanceof Less_Tree_Color) {
1115
+ throw new Less_Exception_Compiler('The second argument to hardlight must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
1116
  }
1117
 
1118
+ return $this->colorBlend( array($this,'colorBlendHardlight'), $color1, $color2 );
1119
  }
1120
 
1121
+ private function colorBlendHardlight( $cb, $cs ){
1122
+ return $this->colorBlendOverlay($cs, $cb);
1123
  }
1124
 
1125
+ public function difference($color1 = null, $color2 = null) {
1126
+ if (!$color1 instanceof Less_Tree_Color) {
1127
+ throw new Less_Exception_Compiler('The first argument to difference must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
1128
  }
1129
+ if (!$color2 instanceof Less_Tree_Color) {
1130
+ throw new Less_Exception_Compiler('The second argument to difference must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
1131
  }
1132
 
1133
+ return $this->colorBlend( array($this,'colorBlendDifference'), $color1, $color2 );
1134
  }
1135
 
1136
+ private function colorBlendDifference( $cb, $cs ){
1137
+ return abs($cb - $cs);
1138
  }
1139
 
1140
+ public function exclusion( $color1 = null, $color2 = null ){
1141
+ if (!$color1 instanceof Less_Tree_Color) {
1142
+ throw new Less_Exception_Compiler('The first argument to exclusion must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
1143
  }
1144
+ if (!$color2 instanceof Less_Tree_Color) {
1145
+ throw new Less_Exception_Compiler('The second argument to exclusion must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
1146
  }
1147
 
1148
+ return $this->colorBlend( array($this,'colorBlendExclusion'), $color1, $color2 );
1149
  }
1150
 
1151
+ private function colorBlendExclusion( $cb, $cs ){
1152
  return $cb + $cs - 2 * $cb * $cs;
1153
  }
1154
 
1155
+ public function average($color1 = null, $color2 = null){
1156
+ if (!$color1 instanceof Less_Tree_Color) {
1157
+ throw new Less_Exception_Compiler('The first argument to average must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
1158
  }
1159
+ if (!$color2 instanceof Less_Tree_Color) {
1160
+ throw new Less_Exception_Compiler('The second argument to average must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
1161
  }
1162
 
1163
+ return $this->colorBlend( array($this,'colorBlendAverage'), $color1, $color2 );
1164
  }
1165
 
1166
  // non-w3c functions:
1167
+ public function colorBlendAverage($cb, $cs ){
1168
+ return ($cb + $cs) / 2;
1169
  }
1170
 
1171
+ public function negation($color1 = null, $color2 = null ){
1172
+ if (!$color1 instanceof Less_Tree_Color) {
1173
+ throw new Less_Exception_Compiler('The first argument to negation must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
1174
  }
1175
+ if (!$color2 instanceof Less_Tree_Color) {
1176
+ throw new Less_Exception_Compiler('The second argument to negation must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
1177
  }
1178
 
1179
+ return $this->colorBlend( array($this,'colorBlendNegation'), $color1, $color2 );
1180
  }
1181
 
1182
+ public function colorBlendNegation($cb, $cs){
1183
+ return 1 - abs($cb + $cs - 1);
1184
  }
1185
 
1186
  // ~ End of Color Blending
base/inc/lib/Less/Mime.php CHANGED
@@ -6,36 +6,36 @@
6
  * @package Less
7
  * @subpackage node
8
  */
9
- class Less_Mime {
10
 
11
  // this map is intentionally incomplete
12
  // if you want more, install 'mime' dep
13
  static $_types = array(
14
- '.htm' => 'text/html',
15
- '.html' => 'text/html',
16
- '.gif' => 'image/gif',
17
- '.jpg' => 'image/jpeg',
18
- '.jpeg' => 'image/jpeg',
19
- '.png' => 'image/png',
20
- '.ttf' => 'application/x-font-ttf',
21
- '.otf' => 'application/x-font-otf',
22
- '.eot' => 'application/vnd.ms-fontobject',
23
- '.woff' => 'application/x-font-woff',
24
- '.svg' => 'image/svg+xml',
25
- );
26
 
27
- public static function lookup( $filepath ) {
28
- $parts = explode( '.', $filepath );
29
- $ext = '.'.strtolower( array_pop( $parts ) );
30
 
31
- if ( !isset( self::$_types[$ext] ) ) {
32
  return null;
33
  }
34
  return self::$_types[$ext];
35
  }
36
 
37
- public static function charsets_lookup( $type = null ) {
38
  // assumes all text types are UTF-8
39
- return $type && preg_match( '/^text\//', $type ) ? 'UTF-8' : '';
40
  }
41
  }
6
  * @package Less
7
  * @subpackage node
8
  */
9
+ class Less_Mime{
10
 
11
  // this map is intentionally incomplete
12
  // if you want more, install 'mime' dep
13
  static $_types = array(
14
+ '.htm' => 'text/html',
15
+ '.html'=> 'text/html',
16
+ '.gif' => 'image/gif',
17
+ '.jpg' => 'image/jpeg',
18
+ '.jpeg'=> 'image/jpeg',
19
+ '.png' => 'image/png',
20
+ '.ttf' => 'application/x-font-ttf',
21
+ '.otf' => 'application/x-font-otf',
22
+ '.eot' => 'application/vnd.ms-fontobject',
23
+ '.woff' => 'application/x-font-woff',
24
+ '.svg' => 'image/svg+xml',
25
+ );
26
 
27
+ public static function lookup( $filepath ){
28
+ $parts = explode('.',$filepath);
29
+ $ext = '.'.strtolower(array_pop($parts));
30
 
31
+ if( !isset(self::$_types[$ext]) ){
32
  return null;
33
  }
34
  return self::$_types[$ext];
35
  }
36
 
37
+ public static function charsets_lookup( $type = null ){
38
  // assumes all text types are UTF-8
39
+ return $type && preg_match('/^text\//',$type) ? 'UTF-8' : '';
40
  }
41
  }
base/inc/lib/Less/Output.php CHANGED
@@ -6,7 +6,7 @@
6
  * @package Less
7
  * @subpackage output
8
  */
9
- class Less_Output {
10
 
11
  /**
12
  * Output holder
@@ -23,7 +23,7 @@ class Less_Output {
23
  * @param integer $index The index
24
  * @param mixed $mapLines
25
  */
26
- public function add( $chunk, $fileInfo = null, $index = 0, $mapLines = null ) {
27
  $this->strs[] = $chunk;
28
  }
29
 
@@ -32,17 +32,18 @@ class Less_Output {
32
  *
33
  * @return boolean
34
  */
35
- public function isEmpty() {
36
- return count( $this->strs ) === 0;
37
  }
38
 
 
39
  /**
40
  * Converts the output to string
41
  *
42
  * @return string
43
  */
44
- public function toString() {
45
- return implode( '', $this->strs );
46
  }
47
 
48
- }
6
  * @package Less
7
  * @subpackage output
8
  */
9
+ class Less_Output{
10
 
11
  /**
12
  * Output holder
23
  * @param integer $index The index
24
  * @param mixed $mapLines
25
  */
26
+ public function add($chunk, $fileInfo = null, $index = 0, $mapLines = null){
27
  $this->strs[] = $chunk;
28
  }
29
 
32
  *
33
  * @return boolean
34
  */
35
+ public function isEmpty(){
36
+ return count($this->strs) === 0;
37
  }
38
 
39
+
40
  /**
41
  * Converts the output to string
42
  *
43
  * @return string
44
  */
45
+ public function toString(){
46
+ return implode('',$this->strs);
47
  }
48
 
49
+ }
base/inc/lib/Less/Output/Mapped.php CHANGED
@@ -1,119 +1,122 @@
1
- <?php
2
-
3
- /**
4
- * Parser output with source map
5
- *
6
- * @package Less
7
- * @subpackage Output
8
- */
9
- class Less_Output_Mapped extends Less_Output {
10
-
11
- /**
12
- * The source map generator
13
- *
14
- * @var Less_SourceMap_Generator
15
- */
16
- protected $generator;
17
-
18
- /**
19
- * Current line
20
- *
21
- * @var integer
22
- */
23
- protected $lineNumber = 0;
24
-
25
- /**
26
- * Current column
27
- *
28
- * @var integer
29
- */
30
- protected $column = 0;
31
-
32
- /**
33
- * Array of contents map (file and its content)
34
- *
35
- * @var array
36
- */
37
- protected $contentsMap = array();
38
-
39
- /**
40
- * Constructor
41
- *
42
- * @param array $contentsMap Array of filename to contents map
43
- * @param Less_SourceMap_Generator $generator
44
- */
45
- public function __construct( array $contentsMap, $generator ) {
46
- $this->contentsMap = $contentsMap;
47
- $this->generator = $generator;
48
- }
49
-
50
- /**
51
- * Adds a chunk to the stack
52
- * The $index for less.php may be different from less.js since less.php does not chunkify inputs
53
- *
54
- * @param string $chunk
55
- * @param string $fileInfo
56
- * @param integer $index
57
- * @param mixed $mapLines
58
- */
59
- public function add( $chunk, $fileInfo = null, $index = 0, $mapLines = null ) {
60
- // ignore adding empty strings
61
- if ( $chunk === '' ) {
62
- return;
63
- }
64
-
65
- $sourceLines = array();
66
- $sourceColumns = ' ';
67
-
68
- if ( $fileInfo ) {
69
-
70
- $url = $fileInfo['currentUri'];
71
-
72
- if ( isset( $this->contentsMap[$url] ) ) {
73
- $inputSource = substr( $this->contentsMap[$url], 0, $index );
74
- $sourceLines = explode( "\n", $inputSource );
75
- $sourceColumns = end( $sourceLines );
76
- } else {
77
- throw new Exception( 'Filename '.$url.' not in contentsMap' );
78
- }
79
-
80
- }
81
-
82
- $lines = explode( "\n", $chunk );
83
- $columns = end( $lines );
84
-
85
- if ( $fileInfo ) {
86
-
87
- if ( !$mapLines ) {
88
- $this->generator->addMapping(
89
- $this->lineNumber + 1, // generated_line
90
- $this->column, // generated_column
91
- count( $sourceLines ), // original_line
92
- strlen( $sourceColumns ), // original_column
93
- $fileInfo
94
- );
95
- } else {
96
- for ( $i = 0, $count = count( $lines ); $i < $count; $i++ ) {
97
- $this->generator->addMapping(
98
- $this->lineNumber + $i + 1, // generated_line
99
- $i === 0 ? $this->column : 0, // generated_column
100
- count( $sourceLines ) + $i, // original_line
101
- $i === 0 ? strlen( $sourceColumns ) : 0, // original_column
102
- $fileInfo
103
- );
104
- }
105
- }
106
- }
107
-
108
- if ( count( $lines ) === 1 ) {
109
- $this->column += strlen( $columns );
110
- } else {
111
- $this->lineNumber += count( $lines ) - 1;
112
- $this->column = strlen( $columns );
113
- }
114
-
115
- // add only chunk
116
- parent::add( $chunk );
117
- }
118
-
119
- }
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Parser output with source map
5
+ *
6
+ * @package Less
7
+ * @subpackage Output
8
+ */
9
+ class Less_Output_Mapped extends Less_Output {
10
+
11
+ /**
12
+ * The source map generator
13
+ *
14
+ * @var Less_SourceMap_Generator
15
+ */
16
+ protected $generator;
17
+
18
+ /**
19
+ * Current line
20
+ *
21
+ * @var integer
22
+ */
23
+ protected $lineNumber = 0;
24
+
25
+ /**
26
+ * Current column
27
+ *
28
+ * @var integer
29
+ */
30
+ protected $column = 0;
31
+
32
+ /**
33
+ * Array of contents map (file and its content)
34
+ *
35
+ * @var array
36
+ */
37
+ protected $contentsMap = array();
38
+
39
+ /**
40
+ * Constructor
41
+ *
42
+ * @param array $contentsMap Array of filename to contents map
43
+ * @param Less_SourceMap_Generator $generator
44
+ */
45
+ public function __construct(array $contentsMap, $generator){
46
+ $this->contentsMap = $contentsMap;
47
+ $this->generator = $generator;
48
+ }
49
+
50
+ /**
51
+ * Adds a chunk to the stack
52
+ * The $index for less.php may be different from less.js since less.php does not chunkify inputs
53
+ *
54
+ * @param string $chunk
55
+ * @param string $fileInfo
56
+ * @param integer $index
57
+ * @param mixed $mapLines
58
+ */
59
+ public function add($chunk, $fileInfo = null, $index = 0, $mapLines = null){
60
+
61
+ //ignore adding empty strings
62
+ if( $chunk === '' ){
63
+ return;
64
+ }
65
+
66
+
67
+ $sourceLines = array();
68
+ $sourceColumns = ' ';
69
+
70
+
71
+ if( $fileInfo ){
72
+
73
+ $url = $fileInfo['currentUri'];
74
+
75
+ if( isset($this->contentsMap[$url]) ){
76
+ $inputSource = substr($this->contentsMap[$url], 0, $index);
77
+ $sourceLines = explode("\n", $inputSource);
78
+ $sourceColumns = end($sourceLines);
79
+ }else{
80
+ throw new Exception('Filename '.$url.' not in contentsMap');
81
+ }
82
+
83
+ }
84
+
85
+ $lines = explode("\n", $chunk);
86
+ $columns = end($lines);
87
+
88
+ if($fileInfo){
89
+
90
+ if(!$mapLines){
91
+ $this->generator->addMapping(
92
+ $this->lineNumber + 1, // generated_line
93
+ $this->column, // generated_column
94
+ count($sourceLines), // original_line
95
+ strlen($sourceColumns), // original_column
96
+ $fileInfo
97
+ );
98
+ }else{
99
+ for($i = 0, $count = count($lines); $i < $count; $i++){
100
+ $this->generator->addMapping(
101
+ $this->lineNumber + $i + 1, // generated_line
102
+ $i === 0 ? $this->column : 0, // generated_column
103
+ count($sourceLines) + $i, // original_line
104
+ $i === 0 ? strlen($sourceColumns) : 0, // original_column
105
+ $fileInfo
106
+ );
107
+ }
108
+ }
109
+ }
110
+
111
+ if(count($lines) === 1){
112
+ $this->column += strlen($columns);
113
+ }else{
114
+ $this->lineNumber += count($lines) - 1;
115
+ $this->column = strlen($columns);
116
+ }
117
+
118
+ // add only chunk
119
+ parent::add($chunk);
120
+ }
121
+
122
+ }
base/inc/lib/Less/Parser.php CHANGED
@@ -1,6 +1,6 @@
1
  <?php
2
 
3
- require_once dirname( __FILE__ ).'/Cache.php';
4
 
5
  /**
6
  * Class for parsing and compiling less files into css
@@ -9,7 +9,8 @@ require_once dirname( __FILE__ ).'/Cache.php';
9
  * @subpackage parser
10
  *
11
  */
12
- class Less_Parser {
 
13
 
14
  /**
15
  * Default parser options
@@ -42,6 +43,7 @@ class Less_Parser {
42
 
43
  public static $options = array();
44
 
 
45
  private $input; // Less input string
46
  private $input_len; // input string length
47
  private $pos; // current index in `input`
@@ -69,46 +71,49 @@ class Less_Parser {
69
  */
70
  public static $contentsMap = array();
71
 
 
72
  /**
73
  * @param Less_Environment|array|null $env
74
  */
75
- public function __construct( $env = null ) {
 
76
  // Top parser on an import tree must be sure there is one "env"
77
  // which will then be passed around by reference.
78
- if ( $env instanceof Less_Environment ) {
79
  $this->env = $env;
80
- } else {
81
- $this->SetOptions( Less_Parser::$default_options );
82
  $this->Reset( $env );
83
  }
84
 
85
  // mbstring.func_overload > 1 bugfix
86
  // The encoding value must be set for each source file,
87
  // therefore, to conserve resources and improve the speed of this design is taken here
88
- if ( ini_get( 'mbstring.func_overload' ) ) {
89
- $this->mb_internal_encoding = ini_get( 'mbstring.internal_encoding' );
90
- @ini_set( 'mbstring.internal_encoding', 'ascii' );
91
  }
92
 
93
  }
94
 
 
95
  /**
96
  * Reset the parser state completely
97
  *
98
  */
99
- public function Reset( $options = null ) {
100
  $this->rules = array();
101
  self::$imports = array();
102
  self::$has_extends = false;
103
  self::$imports = array();
104
  self::$contentsMap = array();
105
 
106
- $this->env = new Less_Environment( $options );
107
 
108
- // set new options
109
- if ( is_array( $options ) ) {
110
- $this->SetOptions( Less_Parser::$default_options );
111
- $this->SetOptions( $options );
112
  }
113
 
114
  $this->env->Init();
@@ -119,9 +124,9 @@ class Less_Parser {
119
  * options: import_dirs, cache_dir, cache_method
120
  *
121
  */
122
- public function SetOptions( $options ) {
123
- foreach ( $options as $option => $value ) {
124
- $this->SetOption( $option, $value );
125
  }
126
  }
127
 
@@ -129,16 +134,17 @@ class Less_Parser {
129
  * Set one compiler option
130
  *
131
  */
132
- public function SetOption( $option, $value ) {
133
- switch ( $option ) {
 
134
 
135
  case 'import_dirs':
136
- $this->SetImportDirs( $value );
137
  return;
138
 
139
  case 'cache_dir':
140
- if ( is_string( $value ) ) {
141
- Less_Cache::SetCacheDir( $value );
142
  Less_Cache::CheckCacheDir();
143
  }
144
  return;
@@ -150,87 +156,93 @@ class Less_Parser {
150
  /**
151
  * Registers a new custom function
152
  *
153
- * @param string $name function name
154
- * @param callable $callback callback
155
  */
156
- public function registerFunction( $name, $callback ) {
157
  $this->env->functions[$name] = $callback;
158
  }
159
 
160
  /**
161
  * Removed an already registered function
162
  *
163
- * @param string $name function name
164
  */
165
- public function unregisterFunction( $name ) {
166
- if ( isset( $this->env->functions[$name] ) )
167
- unset( $this->env->functions[$name] );
168
  }
169
 
 
170
  /**
171
  * Get the current css buffer
172
  *
173
  * @return string
174
  */
175
- public function getCss() {
176
- $precision = ini_get( 'precision' );
177
- @ini_set( 'precision', 16 );
178
- $locale = setlocale( LC_NUMERIC, 0 );
179
- setlocale( LC_NUMERIC, "C" );
 
180
 
181
  try {
182
 
183
- $root = new Less_Tree_Ruleset( array(), $this->rules );
184
  $root->root = true;
185
  $root->firstRoot = true;
186
 
187
- $this->PreVisitors( $root );
 
188
 
189
  self::$has_extends = false;
190
- $evaldRoot = $root->compile( $this->env );
191
 
192
- $this->PostVisitors( $evaldRoot );
193
 
194
- if ( Less_Parser::$options['sourceMap'] ) {
195
- $generator = new Less_SourceMap_Generator( $evaldRoot, Less_Parser::$contentsMap, Less_Parser::$options );
 
 
 
196
  // will also save file
197
  // FIXME: should happen somewhere else?
198
  $css = $generator->generateCSS();
199
- } else {
200
  $css = $evaldRoot->toCSS();
201
  }
202
 
203
- if ( Less_Parser::$options['compress'] ) {
204
- $css = preg_replace( '/(^(\s)+)|((\s)+$)/', '', $css );
205
  }
206
 
207
- } catch ( Exception $exc ) {
208
  // Intentional fall-through so we can reset environment
209
  }
210
 
211
- // reset php settings
212
- @ini_set( 'precision', $precision );
213
- setlocale( LC_NUMERIC, $locale );
214
 
215
  // If you previously defined $this->mb_internal_encoding
216
  // is required to return the encoding as it was before
217
- if ( $this->mb_internal_encoding != '' ) {
218
- @ini_set( "mbstring.internal_encoding", $this->mb_internal_encoding );
219
  $this->mb_internal_encoding = '';
220
  }
221
 
222
  // Rethrow exception after we handled resetting the environment
223
- if ( !empty( $exc ) ) {
224
  throw $exc;
225
  }
226
 
227
  return $css;
228
  }
229
 
230
- public function findValueOf( $varName ) {
231
- foreach ( $this->rules as $rule ) {
232
- if ( isset( $rule->variable ) && ( $rule->variable == true ) && ( str_replace( "@", "", $rule->name ) == $varName ) ) {
233
- return $this->getVariableValue( $rule );
 
234
  }
235
  }
236
  return null;
@@ -243,7 +255,8 @@ class Less_Parser {
243
  *
244
  * @return array
245
  */
246
- public function getVariables() {
 
247
  $variables = array();
248
 
249
  $not_variable_type = array(
@@ -254,28 +267,29 @@ class Less_Parser {
254
  );
255
 
256
  // @TODO run compilation if not runned yet
257
- foreach ( $this->rules as $key => $rule ) {
258
- if ( in_array( $rule->type, $not_variable_type ) ) {
259
  continue;
260
  }
261
 
262
  // Note: it seems rule->type is always Rule when variable = true
263
- if ( $rule->type == 'Rule' && $rule->variable ) {
264
- $variables[$rule->name] = $this->getVariableValue( $rule );
265
  } else {
266
- if ( $rule->type == 'Comment' ) {
267
- $variables[] = $this->getVariableValue( $rule );
268
  }
269
  }
270
  }
271
  return $variables;
272
  }
273
 
274
- public function findVarByName( $var_name ) {
275
- foreach ( $this->rules as $rule ) {
276
- if ( isset( $rule->variable ) && ( $rule->variable == true ) ) {
277
- if ( $rule->name == $var_name ) {
278
- return $this->getVariableValue( $rule );
 
279
  }
280
  }
281
  }
@@ -291,67 +305,69 @@ class Less_Parser {
291
  *
292
  * @return bool|string
293
  */
294
- private function getVariableValue( $var ) {
295
- if ( !is_a( $var, 'Less_Tree' ) ) {
296
- throw new Exception( 'var is not a Less_Tree object' );
 
297
  }
298
 
299
- switch ( $var->type ) {
300
  case 'Color':
301
- return $this->rgb2html( $var->rgb );
302
  case 'Unit':
303
  return $var->value. $var->unit->numerator[0];
304
  case 'Variable':
305
- return $this->findVarByName( $var->name );
306
  case 'Keyword':
307
  return $var->value;
308
  case 'Rule':
309
- return $this->getVariableValue( $var->value );
310
  case 'Value':
311
  $value = '';
312
- foreach ( $var->value as $sub_value ) {
313
- $value .= $this->getVariableValue( $sub_value ).' ';
314
  }
315
  return $value;
316
  case 'Quoted':
317
  return $var->quote.$var->value.$var->quote;
318
  case 'Dimension':
319
  $value = $var->value;
320
- if ( $var->unit && $var->unit->numerator ) {
321
  $value .= $var->unit->numerator[0];
322
  }
323
  return $value;
324
  case 'Expression':
325
  $value = "";
326
- foreach ( $var->value as $item ) {
327
- $value .= $this->getVariableValue( $item )." ";
328
  }
329
  return $value;
330
  case 'Operation':
331
- throw new Exception( 'getVariables() require Less to be compiled. please use $parser->getCss() before calling getVariables()' );
332
  case 'Comment':
333
  case 'Import':
334
  case 'Ruleset':
335
  default:
336
- throw new Exception( "type missing in switch/case getVariableValue for ".$var->type );
337
  }
338
  return false;
339
  }
340
 
341
- private function rgb2html( $r, $g = -1, $b = -1 ) {
342
- if ( is_array( $r ) && sizeof( $r ) == 3 )
343
- list( $r, $g, $b ) = $r;
 
344
 
345
- $r = intval( $r ); $g = intval( $g );
346
- $b = intval( $b );
347
 
348
- $r = dechex( $r < 0 ? 0 : ( $r > 255 ? 255 : $r ) );
349
- $g = dechex( $g < 0 ? 0 : ( $g > 255 ? 255 : $g ) );
350
- $b = dechex( $b < 0 ? 0 : ( $b > 255 ? 255 : $b ) );
351
 
352
- $color = ( strlen( $r ) < 2 ? '0' : '' ).$r;
353
- $color .= ( strlen( $g ) < 2 ? '0' : '' ).$g;
354
- $color .= ( strlen( $b ) < 2 ? '0' : '' ).$b;
355
  return '#'.$color;
356
  }
357
 
@@ -359,48 +375,54 @@ class Less_Parser {
359
  * Run pre-compile visitors
360
  *
361
  */
362
- private function PreVisitors( $root ) {
363
- if ( Less_Parser::$options['plugins'] ) {
364
- foreach ( Less_Parser::$options['plugins'] as $plugin ) {
365
- if ( !empty( $plugin->isPreEvalVisitor ) ) {
366
- $plugin->run( $root );
 
367
  }
368
  }
369
  }
370
  }
371
 
 
372
  /**
373
  * Run post-compile visitors
374
  *
375
  */
376
- private function PostVisitors( $evaldRoot ) {
 
377
  $visitors = array();
378
  $visitors[] = new Less_Visitor_joinSelector();
379
- if ( self::$has_extends ) {
380
  $visitors[] = new Less_Visitor_processExtends();
381
  }
382
  $visitors[] = new Less_Visitor_toCSS();
383
 
384
- if ( Less_Parser::$options['plugins'] ) {
385
- foreach ( Less_Parser::$options['plugins'] as $plugin ) {
386
- if ( property_exists( $plugin, 'isPreEvalVisitor' ) && $plugin->isPreEvalVisitor ) {
 
387
  continue;
388
  }
389
 
390
- if ( property_exists( $plugin, 'isPreVisitor' ) && $plugin->isPreVisitor ) {
391
- array_unshift( $visitors, $plugin );
392
- } else {
393
  $visitors[] = $plugin;
394
  }
395
  }
396
  }
397
 
398
- for ( $i = 0; $i < count( $visitors ); $i++ ) {
399
- $visitors[$i]->run( $evaldRoot );
 
400
  }
401
 
402
  }
403
 
 
404
  /**
405
  * Parse a Less string into css
406
  *
@@ -408,30 +430,32 @@ class Less_Parser {
408
  * @param string $uri_root The url of the file
409
  * @return Less_Tree_Ruleset|Less_Parser
410
  */
411
- public function parse( $str, $file_uri = null ) {
412
- if ( !$file_uri ) {
 
413
  $uri_root = '';
414
  $filename = 'anonymous-file-'.Less_Parser::$next_id++.'.less';
415
- } else {
416
- $file_uri = self::WinPath( $file_uri );
417
  $filename = $file_uri;
418
- $uri_root = dirname( $file_uri );
419
  }
420
 
421
  $previousFileInfo = $this->env->currentFileInfo;
422
- $uri_root = self::WinPath( $uri_root );
423
- $this->SetFileInfo( $filename, $uri_root );
424
 
425
  $this->input = $str;
426
  $this->_parse();
427
 
428
- if ( $previousFileInfo ) {
429
  $this->env->currentFileInfo = $previousFileInfo;
430
  }
431
 
432
  return $this;
433
  }
434
 
 
435
  /**
436
  * Parse a Less string from a given file
437
  *
@@ -441,136 +465,148 @@ class Less_Parser {
441
  * @param bool $returnRoot Indicates whether the return value should be a css string a root node
442
  * @return Less_Tree_Ruleset|Less_Parser
443
  */
444
- public function parseFile( $filename, $uri_root = '', $returnRoot = false ) {
445
- if ( !file_exists( $filename ) ) {
446
- $this->Error( sprintf( 'File `%s` not found.', $filename ) );
 
447
  }
448
 
 
449
  // fix uri_root?
450
  // Instead of The mixture of file path for the first argument and directory path for the second argument has bee
451
- if ( !$returnRoot && !empty( $uri_root ) && basename( $uri_root ) == basename( $filename ) ) {
452
- $uri_root = dirname( $uri_root );
453
  }
454
 
 
455
  $previousFileInfo = $this->env->currentFileInfo;
456
 
457
- if ( $filename ) {
458
- $filename = self::AbsPath( $filename, true );
 
459
  }
460
- $uri_root = self::WinPath( $uri_root );
461
 
462
- $this->SetFileInfo( $filename, $uri_root );
463
 
464
- self::AddParsedFile( $filename );
465
 
466
- if ( $returnRoot ) {
467
  $rules = $this->GetRules( $filename );
468
- $return = new Less_Tree_Ruleset( array(), $rules );
469
- } else {
470
  $this->_parse( $filename );
471
  $return = $this;
472
  }
473
 
474
- if ( $previousFileInfo ) {
475
  $this->env->currentFileInfo = $previousFileInfo;
476
  }
477
 
478
  return $return;
479
  }
480
 
 
481
  /**
482
  * Allows a user to set variables values
483
  * @param array $vars
484
  * @return Less_Parser
485
  */
486
- public function ModifyVars( $vars ) {
 
487
  $this->input = Less_Parser::serializeVars( $vars );
488
  $this->_parse();
489
 
490
  return $this;
491
  }
492
 
 
493
  /**
494
  * @param string $filename
495
  */
496
- public function SetFileInfo( $filename, $uri_root = '' ) {
497
- $filename = Less_Environment::normalizePath( $filename );
498
- $dirname = preg_replace( '/[^\/\\\\]*$/', '', $filename );
 
499
 
500
- if ( !empty( $uri_root ) ) {
501
- $uri_root = rtrim( $uri_root, '/' ).'/';
502
  }
503
 
504
  $currentFileInfo = array();
505
 
506
- // entry info
507
- if ( isset( $this->env->currentFileInfo ) ) {
508
  $currentFileInfo['entryPath'] = $this->env->currentFileInfo['entryPath'];
509
  $currentFileInfo['entryUri'] = $this->env->currentFileInfo['entryUri'];
510
  $currentFileInfo['rootpath'] = $this->env->currentFileInfo['rootpath'];
511
 
512
- } else {
513
  $currentFileInfo['entryPath'] = $dirname;
514
  $currentFileInfo['entryUri'] = $uri_root;
515
  $currentFileInfo['rootpath'] = $dirname;
516
  }
517
 
518
  $currentFileInfo['currentDirectory'] = $dirname;
519
- $currentFileInfo['currentUri'] = $uri_root.basename( $filename );
520
  $currentFileInfo['filename'] = $filename;
521
  $currentFileInfo['uri_root'] = $uri_root;
522
 
523
- // inherit reference
524
- if ( isset( $this->env->currentFileInfo['reference'] ) && $this->env->currentFileInfo['reference'] ) {
 
525
  $currentFileInfo['reference'] = true;
526
  }
527
 
528
  $this->env->currentFileInfo = $currentFileInfo;
529
  }
530
 
 
531
  /**
532
  * @deprecated 1.5.1.2
533
  *
534
  */
535
- public function SetCacheDir( $dir ) {
536
- if ( !file_exists( $dir ) ) {
537
- if ( mkdir( $dir ) ) {
 
538
  return true;
539
  }
540
- throw new Less_Exception_Parser( 'Less.php cache directory couldn\'t be created: '.$dir );
541
 
542
- } elseif ( !is_dir( $dir ) ) {
543
- throw new Less_Exception_Parser( 'Less.php cache directory doesn\'t exist: '.$dir );
544
 
545
- } elseif ( !is_writable( $dir ) ) {
546
- throw new Less_Exception_Parser( 'Less.php cache directory isn\'t writable: '.$dir );
547
 
548
- } else {
549
- $dir = self::WinPath( $dir );
550
- Less_Cache::$cache_dir = rtrim( $dir, '/' ).'/';
551
  return true;
552
  }
553
  }
554
 
 
555
  /**
556
  * Set a list of directories or callbacks the parser should use for determining import paths
557
  *
558
  * @param array $dirs
559
  */
560
- public function SetImportDirs( $dirs ) {
561
  Less_Parser::$options['import_dirs'] = array();
562
 
563
- foreach ( $dirs as $path => $uri_root ) {
564
 
565
- $path = self::WinPath( $path );
566
- if ( !empty( $path ) ) {
567
- $path = rtrim( $path, '/' ).'/';
568
  }
569
 
570
- if ( !is_callable( $uri_root ) ) {
571
- $uri_root = self::WinPath( $uri_root );
572
- if ( !empty( $uri_root ) ) {
573
- $uri_root = rtrim( $uri_root, '/' ).'/';
574
  }
575
  }
576
 
@@ -581,87 +617,91 @@ class Less_Parser {
581
  /**
582
  * @param string $file_path
583
  */
584
- private function _parse( $file_path = null ) {
585
- $this->rules = array_merge( $this->rules, $this->GetRules( $file_path ) );
586
  }
587
 
 
588
  /**
589
  * Return the results of parsePrimary for $file_path
590
  * Use cache and save cached results if possible
591
  *
592
  * @param string|null $file_path
593
  */
594
- private function GetRules( $file_path ) {
595
- $this->SetInput( $file_path );
 
596
 
597
  $cache_file = $this->CacheFile( $file_path );
598
- if ( $cache_file ) {
599
- if ( Less_Parser::$options['cache_method'] == 'callback' ) {
600
- if ( is_callable( Less_Parser::$options['cache_callback_get'] ) ) {
601
  $cache = call_user_func_array(
602
  Less_Parser::$options['cache_callback_get'],
603
- array( $this, $file_path, $cache_file )
604
  );
605
 
606
- if ( $cache ) {
607
  $this->UnsetInput();
608
  return $cache;
609
  }
610
  }
611
 
612
- } elseif ( file_exists( $cache_file ) ) {
613
- switch ( Less_Parser::$options['cache_method'] ) {
614
 
615
  // Using serialize
616
  // Faster but uses more memory
617
  case 'serialize':
618
- $cache = unserialize( file_get_contents( $cache_file ) );
619
- if ( $cache ) {
620
- touch( $cache_file );
621
  $this->UnsetInput();
622
  return $cache;
623
  }
624
  break;
625
 
 
626
  // Using generated php code
627
  case 'var_export':
628
  case 'php':
629
  $this->UnsetInput();
630
- return include $cache_file;
631
  }
632
  }
633
  }
634
 
635
  $rules = $this->parsePrimary();
636
 
637
- if ( $this->pos < $this->input_len ) {
638
- throw new Less_Exception_Chunk( $this->input, null, $this->furthest, $this->env->currentFileInfo );
639
  }
640
 
641
  $this->UnsetInput();
642
 
643
- // save the cache
644
- if ( $cache_file ) {
645
- if ( Less_Parser::$options['cache_method'] == 'callback' ) {
646
- if ( is_callable( Less_Parser::$options['cache_callback_set'] ) ) {
 
647
  call_user_func_array(
648
  Less_Parser::$options['cache_callback_set'],
649
- array( $this, $file_path, $cache_file, $rules )
650
  );
651
  }
652
 
653
- } else {
654
- // msg('write cache file');
655
- switch ( Less_Parser::$options['cache_method'] ) {
656
  case 'serialize':
657
- file_put_contents( $cache_file, serialize( $rules ) );
658
  break;
659
  case 'php':
660
- file_put_contents( $cache_file, '<?php return '.self::ArgString( $rules ).'; ?>' );
661
  break;
662
  case 'var_export':
663
- // Requires __set_state()
664
- file_put_contents( $cache_file, '<?php return '.var_export( $rules, true ).'; ?>' );
665
  break;
666
  }
667
 
@@ -672,42 +712,48 @@ class Less_Parser {
672
  return $rules;
673
  }
674
 
 
675
  /**
676
  * Set up the input buffer
677
  *
678
  */
679
- public function SetInput( $file_path ) {
680
- if ( $file_path ) {
 
681
  $this->input = file_get_contents( $file_path );
682
  }
683
 
684
  $this->pos = $this->furthest = 0;
685
 
686
  // Remove potential UTF Byte Order Mark
687
- $this->input = preg_replace( '/\\G\xEF\xBB\xBF/', '', $this->input );
688
- $this->input_len = strlen( $this->input );
689
 
690
- if ( Less_Parser::$options['sourceMap'] && $this->env->currentFileInfo ) {
 
691
  $uri = $this->env->currentFileInfo['currentUri'];
692
  Less_Parser::$contentsMap[$uri] = $this->input;
693
  }
694
 
695
  }
696
 
 
697
  /**
698
  * Free up some memory
699
  *
700
  */
701
- public function UnsetInput() {
702
- unset( $this->input, $this->pos, $this->input_len, $this->furthest );
703
  $this->saveStack = array();
704
  }
705
 
706
- public function CacheFile( $file_path ) {
707
- if ( $file_path && $this->CacheEnabled() ) {
708
 
709
- $env = get_object_vars( $this->env );
710
- unset( $env['frames'] );
 
 
 
 
711
 
712
  $parts = array();
713
  $parts[] = $file_path;
@@ -716,35 +762,37 @@ class Less_Parser {
716
  $parts[] = $env;
717
  $parts[] = Less_Version::cache_version;
718
  $parts[] = Less_Parser::$options['cache_method'];
719
- return Less_Cache::$cache_dir . Less_Cache::$prefix . base_convert( sha1( json_encode( $parts ) ), 16, 36 ) . '.lesscache';
720
  }
721
  }
722
 
723
- static function AddParsedFile( $file ) {
 
724
  self::$imports[] = $file;
725
  }
726
 
727
- static function AllParsedFiles() {
728
  return self::$imports;
729
  }
730
 
731
  /**
732
  * @param string $file
733
  */
734
- static function FileParsed( $file ) {
735
- return in_array( $file, self::$imports );
736
  }
737
 
 
738
  function save() {
739
  $this->saveStack[] = $this->pos;
740
  }
741
 
742
  private function restore() {
743
- $this->pos = array_pop( $this->saveStack );
744
  }
745
 
746
- private function forget() {
747
- array_pop( $this->saveStack );
748
  }
749
 
750
  /**
@@ -754,8 +802,8 @@ class Less_Parser {
754
  *
755
  * @return bool
756
  */
757
- private function isWhitespace( $offset = 0 ) {
758
- return strpos( " \t\n\r\v\f", $this->input[$this->pos + $offset] ) !== false;
759
  }
760
 
761
  /**
@@ -764,34 +812,35 @@ class Less_Parser {
764
  * @param array $toks
765
  * @return array
766
  */
767
- private function match( $toks ) {
 
768
  // The match is confirmed, add the match length to `this::pos`,
769
  // and consume any extra white-space characters (' ' || '\n')
770
  // which come after that. The reason for this is that LeSS's
771
  // grammar is mostly white-space insensitive.
772
  //
773
 
774
- foreach ( $toks as $tok ) {
775
 
776
  $char = $tok[0];
777
 
778
- if ( $char === '/' ) {
779
- $match = $this->MatchReg( $tok );
780
 
781
- if ( $match ) {
782
- return count( $match ) === 1 ? $match[0] : $match;
783
  }
784
 
785
- } elseif ( $char === '#' ) {
786
- $match = $this->MatchChar( $tok[1] );
787
 
788
- } else {
789
  // Non-terminal, match using a function call
790
  $match = $this->$tok();
791
 
792
  }
793
 
794
- if ( $match ) {
795
  return $match;
796
  }
797
  }
@@ -802,11 +851,12 @@ class Less_Parser {
802
  *
803
  * @return string
804
  */
805
- private function MatchFuncs( $toks ) {
806
- if ( $this->pos < $this->input_len ) {
807
- foreach ( $toks as $tok ) {
 
808
  $match = $this->$tok();
809
- if ( $match ) {
810
  return $match;
811
  }
812
  }
@@ -815,21 +865,23 @@ class Less_Parser {
815
  }
816
 
817
  // Match a single character in the input,
818
- private function MatchChar( $tok ) {
819
- if ( ( $this->pos < $this->input_len ) && ( $this->input[$this->pos] === $tok ) ) {
820
- $this->skipWhitespace( 1 );
821
  return $tok;
822
  }
823
  }
824
 
825
  // Match a regexp from the current start point
826
- private function MatchReg( $tok ) {
827
- if ( preg_match( $tok, $this->input, $match, 0, $this->pos ) ) {
828
- $this->skipWhitespace( strlen( $match[0] ) );
 
829
  return $match;
830
  }
831
  }
832
 
 
833
  /**
834
  * Same as match(), but don't change the state of the parser,
835
  * just return the match.
@@ -837,41 +889,44 @@ class Less_Parser {
837
  * @param string $tok
838
  * @return integer
839
  */
840
- public function PeekReg( $tok ) {
841
- return preg_match( $tok, $this->input, $match, 0, $this->pos );
842
  }
843
 
844
  /**
845
  * @param string $tok
846
  */
847
- public function PeekChar( $tok ) {
848
- // return ($this->input[$this->pos] === $tok );
849
- return ( $this->pos < $this->input_len ) && ( $this->input[$this->pos] === $tok );
850
  }
851
 
 
852
  /**
853
  * @param integer $length
854
  */
855
- public function skipWhitespace( $length ) {
 
856
  $this->pos += $length;
857
 
858
- for ( ; $this->pos < $this->input_len; $this->pos++ ) {
859
  $c = $this->input[$this->pos];
860
 
861
- if ( ( $c !== "\n" ) && ( $c !== "\r" ) && ( $c !== "\t" ) && ( $c !== ' ' ) ) {
862
  break;
863
  }
864
  }
865
  }
866
 
 
867
  /**
868
  * @param string $tok
869
  * @param string|null $msg
870
  */
871
- public function expect( $tok, $msg = NULL ) {
872
- $result = $this->match( array( $tok ) );
873
- if ( !$result ) {
874
- $this->Error( $msg ? "Expected '" . $tok . "' got '" . $this->input[$this->pos] . "'" : $msg );
875
  } else {
876
  return $result;
877
  }
@@ -880,12 +935,12 @@ class Less_Parser {
880
  /**
881
  * @param string $tok
882
  */
883
- public function expectChar( $tok, $msg = null ) {
884
- $result = $this->MatchChar( $tok );
885
- if ( !$result ) {
886
  $msg = $msg ? $msg : "Expected '" . $tok . "' got '" . $this->input[$this->pos] . "'";
887
  $this->Error( $msg );
888
- } else {
889
  return $result;
890
  }
891
  }
@@ -935,31 +990,31 @@ class Less_Parser {
935
  // Only at one point is the primary rule not called from the
936
  // block rule: at the root level.
937
  //
938
- private function parsePrimary() {
939
  $root = array();
940
 
941
- while ( true ) {
942
 
943
- if ( $this->pos >= $this->input_len ) {
944
  break;
945
  }
946
 
947
- $node = $this->parseExtend( true );
948
- if ( $node ) {
949
- $root = array_merge( $root, $node );
950
  continue;
951
  }
952
 
953
- // $node = $this->MatchFuncs( array( 'parseMixinDefinition', 'parseRule', 'parseRuleset', 'parseMixinCall', 'parseComment', 'parseDirective'));
954
- $node = $this->MatchFuncs( array( 'parseMixinDefinition', 'parseNameValue', 'parseRule', 'parseRuleset', 'parseMixinCall', 'parseComment', 'parseRulesetCall', 'parseDirective' ) );
955
 
956
- if ( $node ) {
957
  $root[] = $node;
958
- } elseif ( !$this->MatchReg( '/\\G[\s\n;]+/' ) ) {
959
  break;
960
  }
961
 
962
- if ( $this->PeekChar( '}' ) ) {
963
  break;
964
  }
965
  }
@@ -967,32 +1022,35 @@ class Less_Parser {
967
  return $root;
968
  }
969
 
 
 
970
  // We create a Comment node for CSS comments `/* */`,
971
  // but keep the LeSS comments `//` silent, by just skipping
972
  // over them.
973
- private function parseComment() {
974
- if ( $this->input[$this->pos] !== '/' ) {
 
975
  return;
976
  }
977
 
978
- if ( $this->input[$this->pos + 1] === '/' ) {
979
- $match = $this->MatchReg( '/\\G\/\/.*/' );
980
- return $this->NewObj4( 'Less_Tree_Comment', array( $match[0], true, $this->pos, $this->env->currentFileInfo ) );
981
  }
982
 
983
- // $comment = $this->MatchReg('/\\G\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/');
984
- $comment = $this->MatchReg( '/\\G\/\*(?s).*?\*+\/\n?/' );// not the same as less.js to prevent fatal errors
985
- if ( $comment ) {
986
- return $this->NewObj4( 'Less_Tree_Comment', array( $comment[0], false, $this->pos, $this->env->currentFileInfo ) );
987
  }
988
  }
989
 
990
- private function parseComments() {
991
  $comments = array();
992
 
993
- while ( $this->pos < $this->input_len ) {
994
  $comment = $this->parseComment();
995
- if ( !$comment ) {
996
  break;
997
  }
998
 
@@ -1002,6 +1060,8 @@ class Less_Parser {
1002
  return $comments;
1003
  }
1004
 
 
 
1005
  //
1006
  // A string, which supports escaping " and '
1007
  //
@@ -1012,29 +1072,31 @@ class Less_Parser {
1012
  $e = false;
1013
  $index = $this->pos;
1014
 
1015
- if ( $this->input[$this->pos] === '~' ) {
1016
  $j++;
1017
  $e = true; // Escaped strings
1018
  }
1019
 
1020
  $char = $this->input[$j];
1021
- if ( $char !== '"' && $char !== "'" ) {
1022
  return;
1023
  }
1024
 
1025
- if ( $e ) {
1026
- $this->MatchChar( '~' );
1027
  }
1028
 
1029
- $matched = $this->MatchQuoted( $char, $j + 1 );
1030
- if ( $matched === false ) {
 
1031
  return;
1032
  }
1033
 
1034
  $quoted = $char.$matched.$char;
1035
- return $this->NewObj5( 'Less_Tree_Quoted', array( $quoted, $matched, $e, $index, $this->env->currentFileInfo ) );
1036
  }
1037
 
 
1038
  /**
1039
  * When PCRE JIT is enabled in php, regular expressions don't work for matching quoted strings
1040
  *
@@ -1042,25 +1104,26 @@ class Less_Parser {
1042
  * $regex = '/\\G"((?:[^"\\\\\r\n]|\\\\.|\\\\\r\n|\\\\[\n\r\f])*)"/';
1043
  *
1044
  */
1045
- private function MatchQuoted( $quote_char, $i ) {
 
1046
  $matched = '';
1047
- while ( $i < $this->input_len ) {
1048
  $c = $this->input[$i];
1049
 
1050
- // escaped character
1051
- if ( $c === '\\' ) {
1052
- $matched .= $c . $this->input[$i + 1];
1053
  $i += 2;
1054
  continue;
1055
  }
1056
 
1057
- if ( $c === $quote_char ) {
1058
- $this->pos = $i + 1;
1059
- $this->skipWhitespace( 0 );
1060
  return $matched;
1061
  }
1062
 
1063
- if ( $c === "\r" || $c === "\n" ) {
1064
  return false;
1065
  }
1066
 
@@ -1071,35 +1134,37 @@ class Less_Parser {
1071
  return false;
1072
  }
1073
 
 
1074
  //
1075
  // A catch-all word, such as:
1076
  //
1077
  // black border-collapse
1078
  //
1079
- private function parseEntitiesKeyword() {
1080
- // $k = $this->MatchReg('/\\G[_A-Za-z-][_A-Za-z0-9-]*/');
1081
- $k = $this->MatchReg( '/\\G%|\\G[_A-Za-z-][_A-Za-z0-9-]*/' );
1082
- if ( $k ) {
 
1083
  $k = $k[0];
1084
- $color = $this->fromKeyword( $k );
1085
- if ( $color ) {
1086
  return $color;
1087
  }
1088
- return $this->NewObj1( 'Less_Tree_Keyword', $k );
1089
  }
1090
  }
1091
 
1092
  // duplicate of Less_Tree_Color::FromKeyword
1093
- private function FromKeyword( $keyword ) {
1094
- $keyword = strtolower( $keyword );
1095
 
1096
- if ( Less_Colors::hasOwnProperty( $keyword ) ) {
1097
  // detect named color
1098
- return $this->NewObj1( 'Less_Tree_Color', substr( Less_Colors::color( $keyword ), 1 ) );
1099
  }
1100
 
1101
- if ( $keyword === 'transparent' ) {
1102
- return $this->NewObj3( 'Less_Tree_Color', array( array( 0, 0, 0 ), 0, true ) );
1103
  }
1104
  }
1105
 
@@ -1113,38 +1178,38 @@ class Less_Parser {
1113
  //
1114
  // The arguments are parsed with the `entities.arguments` parser.
1115
  //
1116
- private function parseEntitiesCall() {
1117
  $index = $this->pos;
1118
 
1119
- if ( !preg_match( '/\\G([\w-]+|%|progid:[\w\.]+)\(/', $this->input, $name, 0, $this->pos ) ) {
1120
  return;
1121
  }
1122
  $name = $name[1];
1123
- $nameLC = strtolower( $name );
1124
 
1125
- if ( $nameLC === 'url' ) {
1126
  return null;
1127
  }
1128
 
1129
- $this->pos += strlen( $name );
1130
 
1131
- if ( $nameLC === 'alpha' ) {
1132
  $alpha_ret = $this->parseAlpha();
1133
- if ( $alpha_ret ) {
1134
  return $alpha_ret;
1135
  }
1136
  }
1137
 
1138
- $this->MatchChar( '(' ); // Parse the '(' and consume whitespace.
1139
 
1140
  $args = $this->parseEntitiesArguments();
1141
 
1142
- if ( !$this->MatchChar( ')' ) ) {
1143
  return;
1144
  }
1145
 
1146
- if ( $name ) {
1147
- return $this->NewObj4( 'Less_Tree_Call', array( $name, $args, $index, $this->env->currentFileInfo ) );
1148
  }
1149
  }
1150
 
@@ -1153,24 +1218,25 @@ class Less_Parser {
1153
  *
1154
  * @return array
1155
  */
1156
- private function parseEntitiesArguments() {
 
1157
  $args = array();
1158
- while ( true ) {
1159
- $arg = $this->MatchFuncs( array( 'parseEntitiesAssignment','parseExpression' ) );
1160
- if ( !$arg ) {
1161
  break;
1162
  }
1163
 
1164
  $args[] = $arg;
1165
- if ( !$this->MatchChar( ',' ) ) {
1166
  break;
1167
  }
1168
  }
1169
  return $args;
1170
  }
1171
 
1172
- private function parseEntitiesLiteral() {
1173
- return $this->MatchFuncs( array( 'parseEntitiesDimension','parseEntitiesColor','parseEntitiesQuoted','parseUnicodeDescriptor' ) );
1174
  }
1175
 
1176
  // Assignments are argument entities for calls.
@@ -1179,18 +1245,19 @@ class Less_Parser {
1179
  // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* )
1180
  //
1181
  private function parseEntitiesAssignment() {
1182
- $key = $this->MatchReg( '/\\G\w+(?=\s?=)/' );
1183
- if ( !$key ) {
 
1184
  return;
1185
  }
1186
 
1187
- if ( !$this->MatchChar( '=' ) ) {
1188
  return;
1189
  }
1190
 
1191
  $value = $this->parseEntity();
1192
- if ( $value ) {
1193
- return $this->NewObj2( 'Less_Tree_Assignment', array( $key[0], $value ) );
1194
  }
1195
  }
1196
 
@@ -1201,25 +1268,30 @@ class Less_Parser {
1201
  // standard function calls. The difference is that the argument doesn't have
1202
  // to be enclosed within a string, so it can't be parsed as an Expression.
1203
  //
1204
- private function parseEntitiesUrl() {
1205
- if ( $this->input[$this->pos] !== 'u' || !$this->matchReg( '/\\Gurl\(/' ) ) {
 
 
1206
  return;
1207
  }
1208
 
1209
- $value = $this->match( array( 'parseEntitiesQuoted','parseEntitiesVariable','/\\Gdata\:.*?[^\)]+/','/\\G(?:(?:\\\\[\(\)\'"])|[^\(\)\'"])+/' ) );
1210
- if ( !$value ) {
1211
  $value = '';
1212
  }
1213
 
1214
- $this->expectChar( ')' );
1215
 
1216
- if ( isset( $value->value ) || $value instanceof Less_Tree_Variable ) {
1217
- return $this->NewObj2( 'Less_Tree_Url', array( $value, $this->env->currentFileInfo ) );
 
 
 
1218
  }
1219
 
1220
- return $this->NewObj2( 'Less_Tree_Url', array( $this->NewObj1( 'Less_Tree_Anonymous', $value ), $this->env->currentFileInfo ) );
1221
  }
1222
 
 
1223
  //
1224
  // A Variable entity, such as `@fink`, in
1225
  //
@@ -1228,19 +1300,20 @@ class Less_Parser {
1228
  // We use a different parser for variable definitions,
1229
  // see `parsers.variable`.
1230
  //
1231
- private function parseEntitiesVariable() {
1232
  $index = $this->pos;
1233
- if ( $this->PeekChar( '@' ) && ( $name = $this->MatchReg( '/\\G@@?[\w-]+/' ) ) ) {
1234
- return $this->NewObj3( 'Less_Tree_Variable', array( $name[0], $index, $this->env->currentFileInfo ) );
1235
  }
1236
  }
1237
 
 
1238
  // A variable entity using the protective {} e.g. @{var}
1239
  private function parseEntitiesVariableCurly() {
1240
  $index = $this->pos;
1241
 
1242
- if ( $this->input_len > ( $this->pos + 1 ) && $this->input[$this->pos] === '@' && ( $curly = $this->MatchReg( '/\\G@\{([\w-]+)\}/' ) ) ) {
1243
- return $this->NewObj3( 'Less_Tree_Variable', array( '@'.$curly[1], $index, $this->env->currentFileInfo ) );
1244
  }
1245
  }
1246
 
@@ -1251,9 +1324,9 @@ class Less_Parser {
1251
  //
1252
  // `rgb` and `hsl` colors are parsed through the `entities.call` parser.
1253
  //
1254
- private function parseEntitiesColor() {
1255
- if ( $this->PeekChar( '#' ) && ( $rgb = $this->MatchReg( '/\\G#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/' ) ) ) {
1256
- return $this->NewObj1( 'Less_Tree_Color', $rgb[1] );
1257
  }
1258
  }
1259
 
@@ -1262,122 +1335,129 @@ class Less_Parser {
1262
  //
1263
  // 0.5em 95%
1264
  //
1265
- private function parseEntitiesDimension() {
1266
- $c = @ord( $this->input[$this->pos] );
 
1267
 
1268
- // Is the first char of the dimension 0-9, '.', '+' or '-'
1269
- if ( ( $c > 57 || $c < 43 ) || $c === 47 || $c == 44 ) {
1270
  return;
1271
  }
1272
 
1273
- $value = $this->MatchReg( '/\\G([+-]?\d*\.?\d+)(%|[a-z]+)?/' );
1274
- if ( $value ) {
1275
 
1276
- if ( isset( $value[2] ) ) {
1277
- return $this->NewObj2( 'Less_Tree_Dimension', array( $value[1],$value[2] ) );
1278
  }
1279
- return $this->NewObj1( 'Less_Tree_Dimension', $value[1] );
1280
  }
1281
  }
1282
 
 
1283
  //
1284
  // A unicode descriptor, as is used in unicode-range
1285
  //
1286
  // U+0?? or U+00A1-00A9
1287
  //
1288
  function parseUnicodeDescriptor() {
1289
- $ud = $this->MatchReg( '/\\G(U\+[0-9a-fA-F?]+)(\-[0-9a-fA-F?]+)?/' );
1290
- if ( $ud ) {
1291
- return $this->NewObj1( 'Less_Tree_UnicodeDescriptor', $ud[0] );
1292
  }
1293
  }
1294
 
 
1295
  //
1296
  // JavaScript code to be evaluated
1297
  //
1298
  // `window.location.href`
1299
  //
1300
- private function parseEntitiesJavascript() {
1301
  $e = false;
1302
  $j = $this->pos;
1303
- if ( $this->input[$j] === '~' ) {
1304
  $j++;
1305
  $e = true;
1306
  }
1307
- if ( $this->input[$j] !== '`' ) {
1308
  return;
1309
  }
1310
- if ( $e ) {
1311
- $this->MatchChar( '~' );
1312
  }
1313
- $str = $this->MatchReg( '/\\G`([^`]*)`/' );
1314
- if ( $str ) {
1315
- return $this->NewObj3( 'Less_Tree_Javascript', array( $str[1], $this->pos, $e ) );
1316
  }
1317
  }
1318
 
 
1319
  //
1320
  // The variable part of a variable definition. Used in the `rule` parser
1321
  //
1322
  // @fink:
1323
  //
1324
- private function parseVariable() {
1325
- if ( $this->PeekChar( '@' ) && ( $name = $this->MatchReg( '/\\G(@[\w-]+)\s*:/' ) ) ) {
1326
  return $name[1];
1327
  }
1328
  }
1329
 
 
1330
  //
1331
  // The variable part of a variable definition. Used in the `rule` parser
1332
  //
1333
  // @fink();
1334
  //
1335
- private function parseRulesetCall() {
1336
- if ( $this->input[$this->pos] === '@' && ( $name = $this->MatchReg( '/\\G(@[\w-]+)\s*\(\s*\)\s*;/' ) ) ) {
1337
- return $this->NewObj1( 'Less_Tree_RulesetCall', $name[1] );
 
1338
  }
1339
  }
1340
 
 
1341
  //
1342
  // extend syntax - used to extend selectors
1343
  //
1344
- function parseExtend( $isRule = false ) {
 
1345
  $index = $this->pos;
1346
  $extendList = array();
1347
 
1348
- if ( !$this->MatchReg( $isRule ? '/\\G&:extend\(/' : '/\\G:extend\(/' ) ) { return;
1349
- }
1350
 
1351
  do{
1352
  $option = null;
1353
  $elements = array();
1354
- while ( true ) {
1355
- $option = $this->MatchReg( '/\\G(all)(?=\s*(\)|,))/' );
1356
- if ( $option ) { break;
1357
- }
1358
  $e = $this->parseElement();
1359
- if ( !$e ) { break;
1360
- }
1361
  $elements[] = $e;
1362
  }
1363
 
1364
- if ( $option ) {
1365
  $option = $option[1];
1366
  }
1367
 
1368
- $extendList[] = $this->NewObj3( 'Less_Tree_Extend', array( $this->NewObj1( 'Less_Tree_Selector', $elements ), $option, $index ) );
1369
 
1370
- }while ( $this->MatchChar( "," ) );
1371
 
1372
- $this->expect( '/\\G\)/' );
1373
 
1374
- if ( $isRule ) {
1375
- $this->expect( '/\\G;/' );
1376
  }
1377
 
1378
  return $extendList;
1379
  }
1380
 
 
1381
  //
1382
  // A Mixin call, with an optional argument list
1383
  //
@@ -1389,9 +1469,10 @@ class Less_Parser {
1389
  // namespaced, but we only support the child and descendant
1390
  // selector for now.
1391
  //
1392
- private function parseMixinCall() {
 
1393
  $char = $this->input[$this->pos];
1394
- if ( $char !== '.' && $char !== '#' ) {
1395
  return;
1396
  }
1397
 
@@ -1400,105 +1481,110 @@ class Less_Parser {
1400
 
1401
  $elements = $this->parseMixinCallElements();
1402
 
1403
- if ( $elements ) {
1404
 
1405
- if ( $this->MatchChar( '(' ) ) {
1406
- $returned = $this->parseMixinArgs( true );
1407
  $args = $returned['args'];
1408
- $this->expectChar( ')' );
1409
- } else {
1410
  $args = array();
1411
  }
1412
 
1413
  $important = $this->parseImportant();
1414
 
1415
- if ( $this->parseEnd() ) {
1416
  $this->forget();
1417
- return $this->NewObj5( 'Less_Tree_Mixin_Call', array( $elements, $args, $index, $this->env->currentFileInfo, $important ) );
1418
  }
1419
  }
1420
 
1421
  $this->restore();
1422
  }
1423
 
1424
- private function parseMixinCallElements() {
 
1425
  $elements = array();
1426
  $c = null;
1427
 
1428
- while ( true ) {
1429
  $elemIndex = $this->pos;
1430
- $e = $this->MatchReg( '/\\G[#.](?:[\w-]|\\\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/' );
1431
- if ( !$e ) {
1432
  break;
1433
  }
1434
- $elements[] = $this->NewObj4( 'Less_Tree_Element', array( $c, $e[0], $elemIndex, $this->env->currentFileInfo ) );
1435
- $c = $this->MatchChar( '>' );
1436
  }
1437
 
1438
  return $elements;
1439
  }
1440
 
 
 
1441
  /**
1442
  * @param boolean $isCall
1443
  */
1444
- private function parseMixinArgs( $isCall ) {
1445
  $expressions = array();
1446
  $argsSemiColon = array();
1447
  $isSemiColonSeperated = null;
1448
  $argsComma = array();
1449
  $expressionContainsNamed = null;
1450
  $name = null;
1451
- $returner = array( 'args' => array(), 'variadic' => false );
1452
 
1453
  $this->save();
1454
 
1455
- while ( true ) {
1456
- if ( $isCall ) {
1457
  $arg = $this->MatchFuncs( array( 'parseDetachedRuleset','parseExpression' ) );
1458
  } else {
1459
  $this->parseComments();
1460
- if ( $this->input[ $this->pos ] === '.' && $this->MatchReg( '/\\G\.{3}/' ) ) {
1461
  $returner['variadic'] = true;
1462
- if ( $this->MatchChar( ";" ) && !$isSemiColonSeperated ) {
1463
  $isSemiColonSeperated = true;
1464
  }
1465
 
1466
- if ( $isSemiColonSeperated ) {
1467
- $argsSemiColon[] = array( 'variadic' => true );
1468
- } else {
1469
- $argsComma[] = array( 'variadic' => true );
1470
  }
1471
  break;
1472
  }
1473
- $arg = $this->MatchFuncs( array( 'parseEntitiesVariable','parseEntitiesLiteral','parseEntitiesKeyword' ) );
1474
  }
1475
 
1476
- if ( !$arg ) {
1477
  break;
1478
  }
1479
 
 
1480
  $nameLoop = null;
1481
- if ( $arg instanceof Less_Tree_Expression ) {
1482
  $arg->throwAwayComments();
1483
  }
1484
  $value = $arg;
1485
  $val = null;
1486
 
1487
- if ( $isCall ) {
1488
  // Variable
1489
- if ( property_exists( $arg, 'value' ) && count( $arg->value ) == 1 ) {
1490
  $val = $arg->value[0];
1491
  }
1492
  } else {
1493
  $val = $arg;
1494
  }
1495
 
1496
- if ( $val instanceof Less_Tree_Variable ) {
1497
 
1498
- if ( $this->MatchChar( ':' ) ) {
1499
- if ( $expressions ) {
1500
- if ( $isSemiColonSeperated ) {
1501
- $this->Error( 'Cannot mix ; and , as delimiter types' );
 
 
1502
  }
1503
  $expressionContainsNamed = true;
1504
  }
@@ -1507,16 +1593,16 @@ class Less_Parser {
1507
  // However if we do want to add it, there is nothing blocking it, just don't error
1508
  // and remove isCall dependency below
1509
  $value = null;
1510
- if ( $isCall ) {
1511
  $value = $this->parseDetachedRuleset();
1512
  }
1513
- if ( !$value ) {
1514
  $value = $this->parseExpression();
1515
  }
1516
 
1517
- if ( !$value ) {
1518
- if ( $isCall ) {
1519
- $this->Error( 'could not understand value for named argument' );
1520
  } else {
1521
  $this->restore();
1522
  $returner['args'] = array();
@@ -1524,46 +1610,46 @@ class Less_Parser {
1524
  }
1525
  }
1526
 
1527
- $nameLoop = ( $name = $val->name );
1528
- } elseif ( !$isCall && $this->MatchReg( '/\\G\.{3}/' ) ) {
1529
  $returner['variadic'] = true;
1530
- if ( $this->MatchChar( ";" ) && !$isSemiColonSeperated ) {
1531
  $isSemiColonSeperated = true;
1532
  }
1533
- if ( $isSemiColonSeperated ) {
1534
- $argsSemiColon[] = array( 'name' => $arg->name, 'variadic' => true );
1535
- } else {
1536
- $argsComma[] = array( 'name' => $arg->name, 'variadic' => true );
1537
  }
1538
  break;
1539
- } elseif ( !$isCall ) {
1540
  $name = $nameLoop = $val->name;
1541
  $value = null;
1542
  }
1543
  }
1544
 
1545
- if ( $value ) {
1546
  $expressions[] = $value;
1547
  }
1548
 
1549
- $argsComma[] = array( 'name' => $nameLoop, 'value' => $value );
1550
 
1551
- if ( $this->MatchChar( ',' ) ) {
1552
  continue;
1553
  }
1554
 
1555
- if ( $this->MatchChar( ';' ) || $isSemiColonSeperated ) {
1556
 
1557
- if ( $expressionContainsNamed ) {
1558
- $this->Error( 'Cannot mix ; and , as delimiter types' );
1559
  }
1560
 
1561
  $isSemiColonSeperated = true;
1562
 
1563
- if ( count( $expressions ) > 1 ) {
1564
- $value = $this->NewObj1( 'Less_Tree_Value', $expressions );
1565
  }
1566
- $argsSemiColon[] = array( 'name' => $name, 'value' => $value );
1567
 
1568
  $name = null;
1569
  $expressions = array();
@@ -1572,10 +1658,12 @@ class Less_Parser {
1572
  }
1573
 
1574
  $this->forget();
1575
- $returner['args'] = ( $isSemiColonSeperated ? $argsSemiColon : $argsComma );
1576
  return $returner;
1577
  }
1578
 
 
 
1579
  //
1580
  // A Mixin definition, with a list of parameters
1581
  //
@@ -1595,50 +1683,52 @@ class Less_Parser {
1595
  // Once we've got our params list, and a closing `)`, we parse
1596
  // the `{...}` block.
1597
  //
1598
- private function parseMixinDefinition() {
1599
  $cond = null;
1600
 
1601
  $char = $this->input[$this->pos];
1602
- if ( ( $char !== '.' && $char !== '#' ) || ( $char === '{' && $this->PeekReg( '/\\G[^{]*\}/' ) ) ) {
1603
  return;
1604
  }
1605
 
1606
  $this->save();
1607
 
1608
- $match = $this->MatchReg( '/\\G([#.](?:[\w-]|\\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/' );
1609
- if ( $match ) {
1610
  $name = $match[1];
1611
 
1612
  $argInfo = $this->parseMixinArgs( false );
1613
  $params = $argInfo['args'];
1614
  $variadic = $argInfo['variadic'];
1615
 
 
1616
  // .mixincall("@{a}");
1617
  // looks a bit like a mixin definition..
1618
  // also
1619
  // .mixincall(@a: {rule: set;});
1620
  // so we have to be nice and restore
1621
- if ( !$this->MatchChar( ')' ) ) {
1622
  $this->furthest = $this->pos;
1623
  $this->restore();
1624
  return;
1625
  }
1626
 
 
1627
  $this->parseComments();
1628
 
1629
- if ( $this->MatchReg( '/\\Gwhen/' ) ) { // Guard
1630
- $cond = $this->expect( 'parseConditions', 'Expected conditions' );
1631
  }
1632
 
1633
  $ruleset = $this->parseBlock();
1634
 
1635
- if ( is_array( $ruleset ) ) {
1636
  $this->forget();
1637
- return $this->NewObj5( 'Less_Tree_Mixin_Definition', array( $name, $params, $ruleset, $cond, $variadic ) );
1638
  }
1639
 
1640
  $this->restore();
1641
- } else {
1642
  $this->forget();
1643
  }
1644
  }
@@ -1647,8 +1737,9 @@ class Less_Parser {
1647
  // Entities are the smallest recognized token,
1648
  // and can be found inside a rule's value.
1649
  //
1650
- private function parseEntity() {
1651
- return $this->MatchFuncs( array( 'parseEntitiesLiteral','parseEntitiesVariable','parseEntitiesUrl','parseEntitiesCall','parseEntitiesKeyword','parseEntitiesJavascript','parseComment' ) );
 
1652
  }
1653
 
1654
  //
@@ -1656,8 +1747,8 @@ class Less_Parser {
1656
  // because the `block` rule will be expecting it, but we still need to make sure
1657
  // it's there, if ';' was omitted.
1658
  //
1659
- private function parseEnd() {
1660
- return $this->MatchChar( ';' ) || $this->PeekChar( '}' );
1661
  }
1662
 
1663
  //
@@ -1665,25 +1756,27 @@ class Less_Parser {
1665
  //
1666
  // alpha(opacity=88)
1667
  //
1668
- private function parseAlpha() {
1669
- if ( !$this->MatchReg( '/\\G\(opacity=/i' ) ) {
 
1670
  return;
1671
  }
1672
 
1673
- $value = $this->MatchReg( '/\\G[0-9]+/' );
1674
- if ( $value ) {
1675
  $value = $value[0];
1676
- } else {
1677
  $value = $this->parseEntitiesVariable();
1678
- if ( !$value ) {
1679
  return;
1680
  }
1681
  }
1682
 
1683
- $this->expectChar( ')' );
1684
- return $this->NewObj1( 'Less_Tree_Alpha', $value );
1685
  }
1686
 
 
1687
  //
1688
  // A Selector Element
1689
  //
@@ -1696,29 +1789,29 @@ class Less_Parser {
1696
  // they are made out of a `Combinator` (see combinator rule),
1697
  // and an element name, such as a tag a class, or `*`.
1698
  //
1699
- private function parseElement() {
1700
  $c = $this->parseCombinator();
1701
  $index = $this->pos;
1702
 
1703
- $e = $this->match( array( '/\\G(?:\d+\.\d+|\d+)%/', '/\\G(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/',
1704
- '#*', '#&', 'parseAttribute', '/\\G\([^()@]+\)/', '/\\G[\.#](?=@)/', 'parseEntitiesVariableCurly' ) );
1705
 
1706
- if ( is_null( $e ) ) {
1707
  $this->save();
1708
- if ( $this->MatchChar( '(' ) ) {
1709
- if ( ( $v = $this->parseSelector() ) && $this->MatchChar( ')' ) ) {
1710
- $e = $this->NewObj1( 'Less_Tree_Paren', $v );
1711
  $this->forget();
1712
- } else {
1713
  $this->restore();
1714
  }
1715
- } else {
1716
  $this->forget();
1717
  }
1718
  }
1719
 
1720
- if ( !is_null( $e ) ) {
1721
- return $this->NewObj4( 'Less_Tree_Element', array( $c, $e, $index, $this->env->currentFileInfo ) );
1722
  }
1723
  }
1724
 
@@ -1730,23 +1823,23 @@ class Less_Parser {
1730
  // as it's an empty space. We have to check the previous character
1731
  // in the input, to see if it's a ` ` character.
1732
  //
1733
- private function parseCombinator() {
1734
- if ( $this->pos < $this->input_len ) {
1735
  $c = $this->input[$this->pos];
1736
- if ( $c === '>' || $c === '+' || $c === '~' || $c === '|' || $c === '^' ) {
1737
 
1738
  $this->pos++;
1739
- if ( $this->input[$this->pos] === '^' ) {
1740
  $c = '^^';
1741
  $this->pos++;
1742
  }
1743
 
1744
- $this->skipWhitespace( 0 );
1745
 
1746
  return $c;
1747
  }
1748
 
1749
- if ( $this->pos > 0 && $this->isWhitespace( -1 ) ) {
1750
  return ' ';
1751
  }
1752
  }
@@ -1756,8 +1849,8 @@ class Less_Parser {
1756
  // A CSS selector (see selector below)
1757
  // with less extensions e.g. the ability to extend and guard
1758
  //
1759
- private function parseLessSelector() {
1760
- return $this->parseSelector( true );
1761
  }
1762
 
1763
  //
@@ -1768,7 +1861,7 @@ class Less_Parser {
1768
  //
1769
  // Selectors are made out of one or more Elements, see above.
1770
  //
1771
- private function parseSelector( $isLess = false ) {
1772
  $elements = array();
1773
  $extendList = array();
1774
  $condition = null;
@@ -1778,126 +1871,127 @@ class Less_Parser {
1778
  $c = null;
1779
  $index = $this->pos;
1780
 
1781
- while ( ( $isLess && ( $extend = $this->parseExtend() ) ) || ( $isLess && ( $when = $this->MatchReg( '/\\Gwhen/' ) ) ) || ( $e = $this->parseElement() ) ) {
1782
- if ( $when ) {
1783
- $condition = $this->expect( 'parseConditions', 'expected condition' );
1784
- } elseif ( $condition ) {
1785
- // error("CSS guard can only be used at the end of selector");
1786
- } elseif ( $extend ) {
1787
- $extendList = array_merge( $extendList, $extend );
1788
- } else {
1789
- // if( count($extendList) ){
1790
  //error("Extend can only be used at the end of selector");
1791
  //}
1792
- if ( $this->pos < $this->input_len ) {
1793
  $c = $this->input[ $this->pos ];
1794
  }
1795
  $elements[] = $e;
1796
  $e = null;
1797
  }
1798
 
1799
- if ( $c === '{' || $c === '}' || $c === ';' || $c === ',' || $c === ')' ) { break;
1800
- }
1801
  }
1802
 
1803
- if ( $elements ) {
1804
- return $this->NewObj5( 'Less_Tree_Selector', array( $elements, $extendList, $condition, $index, $this->env->currentFileInfo ) );
1805
  }
1806
- if ( $extendList ) {
1807
- $this->Error( 'Extend must be used to extend a selector, it cannot be used on its own' );
1808
  }
1809
  }
1810
 
1811
- private function parseTag() {
1812
- return ( $tag = $this->MatchReg( '/\\G[A-Za-z][A-Za-z-]*[0-9]?/' ) ) ? $tag : $this->MatchChar( '*' );
1813
  }
1814
 
1815
- private function parseAttribute() {
 
1816
  $val = null;
1817
 
1818
- if ( !$this->MatchChar( '[' ) ) {
1819
  return;
1820
  }
1821
 
1822
  $key = $this->parseEntitiesVariableCurly();
1823
- if ( !$key ) {
1824
- $key = $this->expect( '/\\G(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\\\.)+/' );
1825
  }
1826
 
1827
- $op = $this->MatchReg( '/\\G[|~*$^]?=/' );
1828
- if ( $op ) {
1829
- $val = $this->match( array( 'parseEntitiesQuoted','/\\G[0-9]+%/','/\\G[\w-]+/','parseEntitiesVariableCurly' ) );
1830
  }
1831
 
1832
- $this->expectChar( ']' );
1833
 
1834
- return $this->NewObj3( 'Less_Tree_Attribute', array( $key, $op === null ? null : $op[0], $val ) );
1835
  }
1836
 
1837
  //
1838
  // The `block` rule is used by `ruleset` and `mixin.definition`.
1839
  // It's a wrapper around the `primary` rule, with added `{}`.
1840
  //
1841
- private function parseBlock() {
1842
- if ( $this->MatchChar( '{' ) ) {
1843
  $content = $this->parsePrimary();
1844
- if ( $this->MatchChar( '}' ) ) {
1845
  return $content;
1846
  }
1847
  }
1848
  }
1849
 
1850
- private function parseBlockRuleset() {
1851
  $block = $this->parseBlock();
1852
 
1853
- if ( $block ) {
1854
- $block = $this->NewObj2( 'Less_Tree_Ruleset', array( null, $block ) );
1855
  }
1856
 
1857
  return $block;
1858
  }
1859
 
1860
- private function parseDetachedRuleset() {
1861
  $blockRuleset = $this->parseBlockRuleset();
1862
- if ( $blockRuleset ) {
1863
- return $this->NewObj1( 'Less_Tree_DetachedRuleset', $blockRuleset );
1864
  }
1865
  }
1866
 
1867
  //
1868
  // div, .class, body > p {...}
1869
  //
1870
- private function parseRuleset() {
1871
  $selectors = array();
1872
 
1873
  $this->save();
1874
 
1875
- while ( true ) {
1876
  $s = $this->parseLessSelector();
1877
- if ( !$s ) {
1878
  break;
1879
  }
1880
  $selectors[] = $s;
1881
  $this->parseComments();
1882
 
1883
- if ( $s->condition && count( $selectors ) > 1 ) {
1884
- $this->Error( 'Guards are only currently allowed on a single selector.' );
1885
  }
1886
 
1887
- if ( !$this->MatchChar( ',' ) ) {
1888
  break;
1889
  }
1890
- if ( $s->condition ) {
1891
- $this->Error( 'Guards are only currently allowed on a single selector.' );
1892
  }
1893
  $this->parseComments();
1894
  }
1895
 
1896
- if ( $selectors ) {
 
1897
  $rules = $this->parseBlock();
1898
- if ( is_array( $rules ) ) {
1899
  $this->forget();
1900
- return $this->NewObj2( 'Less_Tree_Ruleset', array( $selectors, $rules ) ); // Less_Environment::$strictImports
1901
  }
1902
  }
1903
 
@@ -1911,59 +2005,63 @@ class Less_Parser {
1911
  * ex: width:100px;
1912
  *
1913
  */
1914
- private function parseNameValue() {
 
1915
  $index = $this->pos;
1916
  $this->save();
1917
 
1918
- // $match = $this->MatchReg('/\\G([a-zA-Z\-]+)\s*:\s*((?:\'")?[a-zA-Z0-9\-% \.,!]+?(?:\'")?)\s*([;}])/');
1919
- $match = $this->MatchReg( '/\\G([a-zA-Z\-]+)\s*:\s*([\'"]?[#a-zA-Z0-9\-%\.,]+?[\'"]?) *(! *important)?\s*([;}])/' );
1920
- if ( $match ) {
1921
 
1922
- if ( $match[4] == '}' ) {
1923
- $this->pos = $index + strlen( $match[0] ) - 1;
 
 
 
 
1924
  }
1925
 
1926
- if ( $match[3] ) {
1927
  $match[2] .= ' !important';
1928
  }
1929
 
1930
- return $this->NewObj4( 'Less_Tree_NameValue', array( $match[1], $match[2], $index, $this->env->currentFileInfo ) );
1931
  }
1932
 
1933
  $this->restore();
1934
  }
1935
 
1936
- private function parseRule( $tryAnonymous = null ) {
 
 
1937
  $merge = false;
1938
  $startOfRule = $this->pos;
1939
 
1940
  $c = $this->input[$this->pos];
1941
- if ( $c === '.' || $c === '#' || $c === '&' ) {
1942
  return;
1943
  }
1944
 
1945
  $this->save();
1946
- $name = $this->MatchFuncs( array( 'parseVariable','parseRuleProperty' ) );
1947
 
1948
- if ( $name ) {
1949
 
1950
- $isVariable = is_string( $name );
1951
 
1952
  $value = null;
1953
- if ( $isVariable ) {
1954
  $value = $this->parseDetachedRuleset();
1955
  }
1956
 
1957
  $important = null;
1958
- if ( !$value ) {
1959
 
1960
  // prefer to try to parse first if its a variable or we are compressing
1961
  // but always fallback on the other one
1962
  //if( !$tryAnonymous && is_string($name) && $name[0] === '@' ){
1963
- if ( !$tryAnonymous && ( Less_Parser::$options['compress'] || $isVariable ) ) {
1964
- $value = $this->MatchFuncs( array( 'parseValue','parseAnonymousValue' ) );
1965
- } else {
1966
- $value = $this->MatchFuncs( array( 'parseAnonymousValue','parseValue' ) );
1967
  }
1968
 
1969
  $important = $this->parseImportant();
@@ -1971,33 +2069,35 @@ class Less_Parser {
1971
  // a name returned by this.ruleProperty() is always an array of the form:
1972
  // [string-1, ..., string-n, ""] or [string-1, ..., string-n, "+"]
1973
  // where each item is a tree.Keyword or tree.Variable
1974
- if ( !$isVariable && is_array( $name ) ) {
1975
- $nm = array_pop( $name );
1976
- if ( $nm->value ) {
1977
  $merge = $nm->value;
1978
  }
1979
  }
1980
  }
1981
 
1982
- if ( $value && $this->parseEnd() ) {
 
1983
  $this->forget();
1984
- return $this->NewObj6( 'Less_Tree_Rule', array( $name, $value, $important, $merge, $startOfRule, $this->env->currentFileInfo ) );
1985
- } else {
1986
  $this->furthest = $this->pos;
1987
  $this->restore();
1988
- if ( $value && !$tryAnonymous ) {
1989
- return $this->parseRule( true );
1990
  }
1991
  }
1992
- } else {
1993
  $this->forget();
1994
  }
1995
  }
1996
 
1997
- function parseAnonymousValue() {
1998
- if ( preg_match( '/\\G([^@+\/\'"*`(;{}-]*);/', $this->input, $match, 0, $this->pos ) ) {
1999
- $this->pos += strlen( $match[1] );
2000
- return $this->NewObj1( 'Less_Tree_Anonymous', $match[1] );
 
2001
  }
2002
  }
2003
 
@@ -2011,24 +2111,25 @@ class Less_Parser {
2011
  // file-system operation. The function used for importing is
2012
  // stored in `import`, which we pass to the Import constructor.
2013
  //
2014
- private function parseImport() {
 
2015
  $this->save();
2016
 
2017
- $dir = $this->MatchReg( '/\\G@import?\s+/' );
2018
 
2019
- if ( $dir ) {
2020
  $options = $this->parseImportOptions();
2021
- $path = $this->MatchFuncs( array( 'parseEntitiesQuoted','parseEntitiesUrl' ) );
2022
 
2023
- if ( $path ) {
2024
  $features = $this->parseMediaFeatures();
2025
- if ( $this->MatchChar( ';' ) ) {
2026
- if ( $features ) {
2027
- $features = $this->NewObj1( 'Less_Tree_Value', $features );
2028
  }
2029
 
2030
  $this->forget();
2031
- return $this->NewObj5( 'Less_Tree_Import', array( $path, $features, $options, $this->pos, $this->env->currentFileInfo ) );
2032
  }
2033
  }
2034
  }
@@ -2036,18 +2137,19 @@ class Less_Parser {
2036
  $this->restore();
2037
  }
2038
 
2039
- private function parseImportOptions() {
 
2040
  $options = array();
2041
 
2042
  // list of options, surrounded by parens
2043
- if ( !$this->MatchChar( '(' ) ) {
2044
  return $options;
2045
  }
2046
  do{
2047
  $optionName = $this->parseImportOption();
2048
- if ( $optionName ) {
2049
  $value = true;
2050
- switch ( $optionName ) {
2051
  case "css":
2052
  $optionName = "less";
2053
  $value = false;
@@ -2058,17 +2160,16 @@ class Less_Parser {
2058
  break;
2059
  }
2060
  $options[$optionName] = $value;
2061
- if ( !$this->MatchChar( ',' ) ) { break;
2062
- }
2063
  }
2064
- }while ( $optionName );
2065
- $this->expectChar( ')' );
2066
  return $options;
2067
  }
2068
 
2069
- private function parseImportOption() {
2070
- $opt = $this->MatchReg( '/\\G(less|css|multiple|once|inline|reference|optional)/' );
2071
- if ( $opt ) {
2072
  return $opt[1];
2073
  }
2074
  }
@@ -2077,27 +2178,28 @@ class Less_Parser {
2077
  $nodes = array();
2078
 
2079
  do{
2080
- $e = $this->MatchFuncs( array( 'parseEntitiesKeyword','parseEntitiesVariable' ) );
2081
- if ( $e ) {
2082
  $nodes[] = $e;
2083
- } elseif ( $this->MatchChar( '(' ) ) {
2084
  $p = $this->parseProperty();
2085
  $e = $this->parseValue();
2086
- if ( $this->MatchChar( ')' ) ) {
2087
- if ( $p && $e ) {
2088
- $r = $this->NewObj7( 'Less_Tree_Rule', array( $p, $e, null, null, $this->pos, $this->env->currentFileInfo, true ) );
2089
- $nodes[] = $this->NewObj1( 'Less_Tree_Paren', $r );
2090
- } elseif ( $e ) {
2091
- $nodes[] = $this->NewObj1( 'Less_Tree_Paren', $e );
2092
  } else {
2093
  return null;
2094
  }
2095
- } else return null;
 
2096
  }
2097
- } while ( $e );
2098
 
2099
- if ( $nodes ) {
2100
- return $this->NewObj1( 'Less_Tree_Expression', $nodes );
2101
  }
2102
  }
2103
 
@@ -2106,39 +2208,41 @@ class Less_Parser {
2106
 
2107
  do{
2108
  $e = $this->parseMediaFeature();
2109
- if ( $e ) {
2110
  $features[] = $e;
2111
- if ( !$this->MatchChar( ',' ) ) break;
2112
- } else {
2113
  $e = $this->parseEntitiesVariable();
2114
- if ( $e ) {
2115
  $features[] = $e;
2116
- if ( !$this->MatchChar( ',' ) ) break;
2117
  }
2118
  }
2119
- } while ( $e );
2120
 
2121
  return $features ? $features : null;
2122
  }
2123
 
2124
  private function parseMedia() {
2125
- if ( $this->MatchReg( '/\\G@media/' ) ) {
2126
  $features = $this->parseMediaFeatures();
2127
  $rules = $this->parseBlock();
2128
 
2129
- if ( is_array( $rules ) ) {
2130
- return $this->NewObj4( 'Less_Tree_Media', array( $rules, $features, $this->pos, $this->env->currentFileInfo ) );
2131
  }
2132
  }
2133
  }
2134
 
 
2135
  //
2136
  // A CSS Directive
2137
  //
2138
  // @charset "utf-8";
2139
  //
2140
- private function parseDirective() {
2141
- if ( !$this->PeekChar( '@' ) ) {
 
2142
  return;
2143
  }
2144
 
@@ -2149,25 +2253,28 @@ class Less_Parser {
2149
  $hasExpression = false;
2150
  $hasUnknown = false;
2151
 
2152
- $value = $this->MatchFuncs( array( 'parseImport','parseMedia' ) );
2153
- if ( $value ) {
 
2154
  return $value;
2155
  }
2156
 
2157
  $this->save();
2158
 
2159
- $name = $this->MatchReg( '/\\G@[a-z-]+/' );
2160
 
2161
- if ( !$name ) return;
2162
  $name = $name[0];
2163
 
 
2164
  $nonVendorSpecificName = $name;
2165
- $pos = strpos( $name, '-', 2 );
2166
- if ( $name[1] == '-' && $pos > 0 ) {
2167
- $nonVendorSpecificName = "@" . substr( $name, $pos + 1 );
2168
  }
2169
 
2170
- switch ( $nonVendorSpecificName ) {
 
2171
  /*
2172
  case "@font-face":
2173
  case "@viewport":
@@ -2209,36 +2316,37 @@ class Less_Parser {
2209
  break;
2210
  }
2211
 
2212
- if ( $hasIdentifier ) {
2213
  $value = $this->parseEntity();
2214
- if ( !$value ) {
2215
- $this->error( "expected " . $name . " identifier" );
2216
  }
2217
- } else if ( $hasExpression ) {
2218
  $value = $this->parseExpression();
2219
- if ( !$value ) {
2220
- $this->error( "expected " . $name. " expression" );
2221
  }
2222
- } else if ( $hasUnknown ) {
2223
 
2224
- $value = $this->MatchReg( '/\\G[^{;]+/' );
2225
- if ( $value ) {
2226
- $value = $this->NewObj1( 'Less_Tree_Anonymous', trim( $value[0] ) );
2227
  }
2228
  }
2229
 
2230
- if ( $hasBlock ) {
2231
  $rules = $this->parseBlockRuleset();
2232
  }
2233
 
2234
- if ( $rules || ( !$hasBlock && $value && $this->MatchChar( ';' ) ) ) {
2235
  $this->forget();
2236
- return $this->NewObj5( 'Less_Tree_Directive', array( $name, $value, $rules, $index, $this->env->currentFileInfo ) );
2237
  }
2238
 
2239
  $this->restore();
2240
  }
2241
 
 
2242
  //
2243
  // A Value is a comma-delimited list of Expressions
2244
  //
@@ -2247,116 +2355,121 @@ class Less_Parser {
2247
  // In a Rule, a Value represents everything after the `:`,
2248
  // and before the `;`.
2249
  //
2250
- private function parseValue() {
2251
  $expressions = array();
2252
 
2253
  do{
2254
  $e = $this->parseExpression();
2255
- if ( $e ) {
2256
  $expressions[] = $e;
2257
- if ( !$this->MatchChar( ',' ) ) {
2258
  break;
2259
  }
2260
  }
2261
- }while ( $e );
2262
 
2263
- if ( $expressions ) {
2264
- return $this->NewObj1( 'Less_Tree_Value', $expressions );
2265
  }
2266
  }
2267
 
2268
- private function parseImportant() {
2269
- if ( $this->PeekChar( '!' ) && $this->MatchReg( '/\\G! *important/' ) ) {
2270
  return ' !important';
2271
  }
2272
  }
2273
 
2274
- private function parseSub() {
2275
- if ( $this->MatchChar( '(' ) ) {
 
2276
  $a = $this->parseAddition();
2277
- if ( $a ) {
2278
- $this->expectChar( ')' );
2279
- return $this->NewObj2( 'Less_Tree_Expression', array( array( $a ), true ) ); // instead of $e->parens = true so the value is cached
2280
  }
2281
  }
2282
  }
2283
 
 
2284
  /**
2285
  * Parses multiplication operation
2286
  *
2287
  * @return Less_Tree_Operation|null
2288
  */
2289
- function parseMultiplication() {
 
2290
  $return = $m = $this->parseOperand();
2291
- if ( $return ) {
2292
- while ( true ) {
2293
 
2294
  $isSpaced = $this->isWhitespace( -1 );
2295
 
2296
- if ( $this->PeekReg( '/\\G\/[*\/]/' ) ) {
2297
  break;
2298
  }
2299
 
2300
- $op = $this->MatchChar( '/' );
2301
- if ( !$op ) {
2302
- $op = $this->MatchChar( '*' );
2303
- if ( !$op ) {
2304
  break;
2305
  }
2306
  }
2307
 
2308
  $a = $this->parseOperand();
2309
 
2310
- if ( !$a ) { break;
2311
- }
2312
 
2313
  $m->parensInOp = true;
2314
  $a->parensInOp = true;
2315
- $return = $this->NewObj3( 'Less_Tree_Operation', array( $op, array( $return, $a ), $isSpaced ) );
2316
  }
2317
  }
2318
  return $return;
2319
 
2320
  }
2321
 
 
2322
  /**
2323
  * Parses an addition operation
2324
  *
2325
  * @return Less_Tree_Operation|null
2326
  */
2327
- private function parseAddition() {
 
2328
  $return = $m = $this->parseMultiplication();
2329
- if ( $return ) {
2330
- while ( true ) {
2331
 
2332
  $isSpaced = $this->isWhitespace( -1 );
2333
 
2334
- $op = $this->MatchReg( '/\\G[-+]\s+/' );
2335
- if ( $op ) {
2336
  $op = $op[0];
2337
- } else {
2338
- if ( !$isSpaced ) {
2339
- $op = $this->match( array( '#+','#-' ) );
2340
  }
2341
- if ( !$op ) {
2342
  break;
2343
  }
2344
  }
2345
 
2346
  $a = $this->parseMultiplication();
2347
- if ( !$a ) {
2348
  break;
2349
  }
2350
 
2351
  $m->parensInOp = true;
2352
  $a->parensInOp = true;
2353
- $return = $this->NewObj3( 'Less_Tree_Operation', array( $op, array( $return, $a ), $isSpaced ) );
2354
  }
2355
  }
2356
 
2357
  return $return;
2358
  }
2359
 
 
2360
  /**
2361
  * Parses the conditions
2362
  *
@@ -2365,17 +2478,17 @@ class Less_Parser {
2365
  private function parseConditions() {
2366
  $index = $this->pos;
2367
  $return = $a = $this->parseCondition();
2368
- if ( $a ) {
2369
- while ( true ) {
2370
- if ( !$this->PeekReg( '/\\G,\s*(not\s*)?\(/' ) || !$this->MatchChar( ',' ) ) {
2371
  break;
2372
  }
2373
  $b = $this->parseCondition();
2374
- if ( !$b ) {
2375
  break;
2376
  }
2377
 
2378
- $return = $this->NewObj4( 'Less_Tree_Condition', array( 'or', $return, $b, $index ) );
2379
  }
2380
  return $return;
2381
  }
@@ -2386,25 +2499,25 @@ class Less_Parser {
2386
  $negate = false;
2387
  $c = null;
2388
 
2389
- if ( $this->MatchReg( '/\\Gnot/' ) ) $negate = true;
2390
- $this->expectChar( '(' );
2391
- $a = $this->MatchFuncs( array( 'parseAddition','parseEntitiesKeyword','parseEntitiesQuoted' ) );
2392
 
2393
- if ( $a ) {
2394
- $op = $this->MatchReg( '/\\G(?:>=|<=|=<|[<=>])/' );
2395
- if ( $op ) {
2396
- $b = $this->MatchFuncs( array( 'parseAddition','parseEntitiesKeyword','parseEntitiesQuoted' ) );
2397
- if ( $b ) {
2398
- $c = $this->NewObj5( 'Less_Tree_Condition', array( $op[0], $a, $b, $index, $negate ) );
2399
  } else {
2400
- $this->Error( 'Unexpected expression' );
2401
  }
2402
  } else {
2403
- $k = $this->NewObj1( 'Less_Tree_Keyword', 'true' );
2404
- $c = $this->NewObj5( 'Less_Tree_Condition', array( '=', $a, $k, $index, $negate ) );
2405
  }
2406
- $this->expectChar( ')' );
2407
- return $this->MatchReg( '/\\Gand/' ) ? $this->NewObj3( 'Less_Tree_Condition', array( 'and', $c, $this->parseCondition() ) ) : $c;
2408
  }
2409
  }
2410
 
@@ -2413,279 +2526,291 @@ class Less_Parser {
2413
  * such as a Color, or a Variable
2414
  *
2415
  */
2416
- private function parseOperand() {
 
2417
  $negate = false;
2418
- $offset = $this->pos + 1;
2419
- if ( $offset >= $this->input_len ) {
2420
  return;
2421
  }
2422
  $char = $this->input[$offset];
2423
- if ( $char === '@' || $char === '(' ) {
2424
- $negate = $this->MatchChar( '-' );
2425
  }
2426
 
2427
- $o = $this->MatchFuncs( array( 'parseSub','parseEntitiesDimension','parseEntitiesColor','parseEntitiesVariable','parseEntitiesCall' ) );
2428
 
2429
- if ( $negate ) {
2430
  $o->parensInOp = true;
2431
- $o = $this->NewObj1( 'Less_Tree_Negative', $o );
2432
  }
2433
 
2434
  return $o;
2435
  }
2436
 
 
2437
  /**
2438
  * Expressions either represent mathematical operations,
2439
  * or white-space delimited Entities.
2440
  *
2441
  * 1px solid black
2442
- * @var * 2
2443
  *
2444
  * @return Less_Tree_Expression|null
2445
  */
2446
- private function parseExpression() {
2447
  $entities = array();
2448
 
2449
  do{
2450
- $e = $this->MatchFuncs( array( 'parseAddition','parseEntity' ) );
2451
- if ( $e ) {
2452
  $entities[] = $e;
2453
  // operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here
2454
- if ( !$this->PeekReg( '/\\G\/[\/*]/' ) ) {
2455
- $delim = $this->MatchChar( '/' );
2456
- if ( $delim ) {
2457
- $entities[] = $this->NewObj1( 'Less_Tree_Anonymous', $delim );
2458
  }
2459
  }
2460
  }
2461
- }while ( $e );
2462
 
2463
- if ( $entities ) {
2464
- return $this->NewObj1( 'Less_Tree_Expression', $entities );
2465
  }
2466
  }
2467
 
 
2468
  /**
2469
  * Parse a property
2470
  * eg: 'min-width', 'orientation', etc
2471
  *
2472
  * @return string
2473
  */
2474
- private function parseProperty() {
2475
- $name = $this->MatchReg( '/\\G(\*?-?[_a-zA-Z0-9-]+)\s*:/' );
2476
- if ( $name ) {
2477
  return $name[1];
2478
  }
2479
  }
2480
 
 
2481
  /**
2482
  * Parse a rule property
2483
  * eg: 'color', 'width', 'height', etc
2484
  *
2485
  * @return string
2486
  */
2487
- private function parseRuleProperty() {
2488
  $offset = $this->pos;
2489
  $name = array();
2490
  $index = array();
2491
  $length = 0;
2492
 
2493
- $this->rulePropertyMatch( '/\\G(\*?)/', $offset, $length, $index, $name );
2494
- while ( $this->rulePropertyMatch( '/\\G((?:[\w-]+)|(?:@\{[\w-]+\}))/', $offset, $length, $index, $name ) ); // !
2495
 
2496
- if ( ( count( $name ) > 1 ) && $this->rulePropertyMatch( '/\\G\s*((?:\+_|\+)?)\s*:/', $offset, $length, $index, $name ) ) {
 
 
 
2497
  // at last, we have the complete match now. move forward,
2498
  // convert name particles to tree objects and return:
2499
- $this->skipWhitespace( $length );
2500
 
2501
- if ( $name[0] === '' ) {
2502
- array_shift( $name );
2503
- array_shift( $index );
2504
  }
2505
- foreach ( $name as $k => $s ) {
2506
- if ( !$s || $s[0] !== '@' ) {
2507
- $name[$k] = $this->NewObj1( 'Less_Tree_Keyword', $s );
2508
- } else {
2509
- $name[$k] = $this->NewObj3( 'Less_Tree_Variable', array( '@' . substr( $s, 2, -1 ), $index[$k], $this->env->currentFileInfo ) );
2510
  }
2511
  }
2512
  return $name;
2513
  }
2514
 
 
2515
  }
2516
 
2517
- private function rulePropertyMatch( $re, &$offset, &$length, &$index, &$name ) {
2518
- preg_match( $re, $this->input, $a, 0, $offset );
2519
- if ( $a ) {
2520
  $index[] = $this->pos + $length;
2521
- $length += strlen( $a[0] );
2522
- $offset += strlen( $a[0] );
2523
  $name[] = $a[1];
2524
  return true;
2525
  }
2526
  }
2527
 
2528
- public static function serializeVars( $vars ) {
2529
  $s = '';
2530
 
2531
- foreach ( $vars as $name => $value ) {
2532
- $s .= ( ( $name[0] === '@' ) ? '' : '@' ) . $name .': '. $value . ( ( substr( $value, -1 ) === ';' ) ? '' : ';' );
2533
  }
2534
 
2535
  return $s;
2536
  }
2537
 
 
2538
  /**
2539
  * Some versions of php have trouble with method_exists($a,$b) if $a is not an object
2540
  *
2541
  * @param string $b
2542
  */
2543
- public static function is_method( $a, $b ) {
2544
- return is_object( $a ) && method_exists( $a, $b );
2545
  }
2546
 
 
2547
  /**
2548
  * Round numbers similarly to javascript
2549
  * eg: 1.499999 to 1 instead of 2
2550
  *
2551
  */
2552
- public static function round( $i, $precision = 0 ) {
2553
- $precision = pow( 10, $precision );
2554
- $i = $i * $precision;
2555
-
2556
- $ceil = ceil( $i );
2557
- $floor = floor( $i );
2558
- if ( ( $ceil - $i ) <= ( $i - $floor ) ) {
2559
- return $ceil / $precision;
2560
- } else {
2561
- return $floor / $precision;
 
2562
  }
2563
  }
2564
 
 
2565
  /**
2566
  * Create Less_Tree_* objects and optionally generate a cache string
2567
  *
2568
  * @return mixed
2569
  */
2570
- public function NewObj0( $class ) {
2571
  $obj = new $class();
2572
- if ( $this->CacheEnabled() ) {
2573
  $obj->cache_string = ' new '.$class.'()';
2574
  }
2575
  return $obj;
2576
  }
2577
 
2578
- public function NewObj1( $class, $arg ) {
2579
  $obj = new $class( $arg );
2580
- if ( $this->CacheEnabled() ) {
2581
- $obj->cache_string = ' new '.$class.'('.Less_Parser::ArgString( $arg ).')';
2582
  }
2583
  return $obj;
2584
  }
2585
 
2586
- public function NewObj2( $class, $args ) {
2587
  $obj = new $class( $args[0], $args[1] );
2588
- if ( $this->CacheEnabled() ) {
2589
- $this->ObjCache( $obj, $class, $args );
2590
  }
2591
  return $obj;
2592
  }
2593
 
2594
- public function NewObj3( $class, $args ) {
2595
  $obj = new $class( $args[0], $args[1], $args[2] );
2596
- if ( $this->CacheEnabled() ) {
2597
- $this->ObjCache( $obj, $class, $args );
2598
  }
2599
  return $obj;
2600
  }
2601
 
2602
- public function NewObj4( $class, $args ) {
2603
  $obj = new $class( $args[0], $args[1], $args[2], $args[3] );
2604
- if ( $this->CacheEnabled() ) {
2605
- $this->ObjCache( $obj, $class, $args );
2606
  }
2607
  return $obj;
2608
  }
2609
 
2610
- public function NewObj5( $class, $args ) {
2611
  $obj = new $class( $args[0], $args[1], $args[2], $args[3], $args[4] );
2612
- if ( $this->CacheEnabled() ) {
2613
- $this->ObjCache( $obj, $class, $args );
2614
  }
2615
  return $obj;
2616
  }
2617
 
2618
- public function NewObj6( $class, $args ) {
2619
  $obj = new $class( $args[0], $args[1], $args[2], $args[3], $args[4], $args[5] );
2620
- if ( $this->CacheEnabled() ) {
2621
- $this->ObjCache( $obj, $class, $args );
2622
  }
2623
  return $obj;
2624
  }
2625
 
2626
- public function NewObj7( $class, $args ) {
2627
  $obj = new $class( $args[0], $args[1], $args[2], $args[3], $args[4], $args[5], $args[6] );
2628
- if ( $this->CacheEnabled() ) {
2629
- $this->ObjCache( $obj, $class, $args );
2630
  }
2631
  return $obj;
2632
  }
2633
 
2634
- // caching
2635
- public function ObjCache( $obj, $class, $args = array() ) {
2636
- $obj->cache_string = ' new '.$class.'('. self::ArgCache( $args ).')';
2637
  }
2638
 
2639
- public function ArgCache( $args ) {
2640
- return implode( ',', array_map( array( 'Less_Parser','ArgString' ), $args ) );
2641
  }
2642
 
 
2643
  /**
2644
  * Convert an argument to a string for use in the parser cache
2645
  *
2646
  * @return string
2647
  */
2648
- public static function ArgString( $arg ) {
2649
- $type = gettype( $arg );
 
2650
 
2651
- if ( $type === 'object' ) {
2652
  $string = $arg->cache_string;
2653
- unset( $arg->cache_string );
2654
  return $string;
2655
 
2656
- } elseif ( $type === 'array' ) {
2657
  $string = ' Array(';
2658
- foreach ( $arg as $k => $a ) {
2659
- $string .= var_export( $k, true ).' => '.self::ArgString( $a ).',';
2660
  }
2661
  return $string . ')';
2662
  }
2663
 
2664
- return var_export( $arg, true );
2665
  }
2666
 
2667
- public function Error( $msg ) {
2668
- throw new Less_Exception_Parser( $msg, null, $this->furthest, $this->env->currentFileInfo );
2669
  }
2670
 
2671
- public static function WinPath( $path ) {
2672
- return str_replace( '\\', '/', $path );
2673
  }
2674
 
2675
- public static function AbsPath( $path, $winPath = false ) {
2676
- if ( strpos( $path, '//' ) !== false && preg_match( '_^(https?:)?//\\w+(\\.\\w+)+/\\w+_i', $path ) ) {
2677
  return $winPath ? '' : false;
2678
  } else {
2679
- $path = realpath( $path );
2680
- if ( $winPath ) {
2681
- $path = self::WinPath( $path );
2682
  }
2683
  return $path;
2684
  }
2685
  }
2686
 
2687
- public function CacheEnabled() {
2688
- return ( Less_Parser::$options['cache_method'] && ( Less_Cache::$cache_dir || ( Less_Parser::$options['cache_method'] == 'callback' ) ) );
2689
  }
2690
 
2691
  }
1
  <?php
2
 
3
+ require_once( dirname(__FILE__).'/Cache.php');
4
 
5
  /**
6
  * Class for parsing and compiling less files into css
9
  * @subpackage parser
10
  *
11
  */
12
+ class Less_Parser{
13
+
14
 
15
  /**
16
  * Default parser options
43
 
44
  public static $options = array();
45
 
46
+
47
  private $input; // Less input string
48
  private $input_len; // input string length
49
  private $pos; // current index in `input`
71
  */
72
  public static $contentsMap = array();
73
 
74
+
75
  /**
76
  * @param Less_Environment|array|null $env
77
  */
78
+ public function __construct( $env = null ){
79
+
80
  // Top parser on an import tree must be sure there is one "env"
81
  // which will then be passed around by reference.
82
+ if( $env instanceof Less_Environment ){
83
  $this->env = $env;
84
+ }else{
85
+ $this->SetOptions(Less_Parser::$default_options);
86
  $this->Reset( $env );
87
  }
88
 
89
  // mbstring.func_overload > 1 bugfix
90
  // The encoding value must be set for each source file,
91
  // therefore, to conserve resources and improve the speed of this design is taken here
92
+ if (ini_get('mbstring.func_overload')) {
93
+ $this->mb_internal_encoding = ini_get('mbstring.internal_encoding');
94
+ @ini_set('mbstring.internal_encoding', 'ascii');
95
  }
96
 
97
  }
98
 
99
+
100
  /**
101
  * Reset the parser state completely
102
  *
103
  */
104
+ public function Reset( $options = null ){
105
  $this->rules = array();
106
  self::$imports = array();
107
  self::$has_extends = false;
108
  self::$imports = array();
109
  self::$contentsMap = array();
110
 
111
+ $this->env = new Less_Environment($options);
112
 
113
+ //set new options
114
+ if( is_array($options) ){
115
+ $this->SetOptions(Less_Parser::$default_options);
116
+ $this->SetOptions($options);
117
  }
118
 
119
  $this->env->Init();
124
  * options: import_dirs, cache_dir, cache_method
125
  *
126
  */
127
+ public function SetOptions( $options ){
128
+ foreach($options as $option => $value){
129
+ $this->SetOption($option,$value);
130
  }
131
  }
132
 
134
  * Set one compiler option
135
  *
136
  */
137
+ public function SetOption($option,$value){
138
+
139
+ switch($option){
140
 
141
  case 'import_dirs':
142
+ $this->SetImportDirs($value);
143
  return;
144
 
145
  case 'cache_dir':
146
+ if( is_string($value) ){
147
+ Less_Cache::SetCacheDir($value);
148
  Less_Cache::CheckCacheDir();
149
  }
150
  return;
156
  /**
157
  * Registers a new custom function
158
  *
159
+ * @param string $name function name
160
+ * @param callable $callback callback
161
  */
162
+ public function registerFunction($name, $callback) {
163
  $this->env->functions[$name] = $callback;
164
  }
165
 
166
  /**
167
  * Removed an already registered function
168
  *
169
+ * @param string $name function name
170
  */
171
+ public function unregisterFunction($name) {
172
+ if( isset($this->env->functions[$name]) )
173
+ unset($this->env->functions[$name]);
174
  }
175
 
176
+
177
  /**
178
  * Get the current css buffer
179
  *
180
  * @return string
181
  */
182
+ public function getCss(){
183
+
184
+ $precision = ini_get('precision');
185
+ @ini_set('precision',16);
186
+ $locale = setlocale(LC_NUMERIC, 0);
187
+ setlocale(LC_NUMERIC, "C");
188
 
189
  try {
190
 
191
+ $root = new Less_Tree_Ruleset(array(), $this->rules );
192
  $root->root = true;
193
  $root->firstRoot = true;
194
 
195
+
196
+ $this->PreVisitors($root);
197
 
198
  self::$has_extends = false;
199
+ $evaldRoot = $root->compile($this->env);
200
 
 
201
 
202
+
203
+ $this->PostVisitors($evaldRoot);
204
+
205
+ if( Less_Parser::$options['sourceMap'] ){
206
+ $generator = new Less_SourceMap_Generator($evaldRoot, Less_Parser::$contentsMap, Less_Parser::$options );
207
  // will also save file
208
  // FIXME: should happen somewhere else?
209
  $css = $generator->generateCSS();
210
+ }else{
211
  $css = $evaldRoot->toCSS();
212
  }
213
 
214
+ if( Less_Parser::$options['compress'] ){
215
+ $css = preg_replace('/(^(\s)+)|((\s)+$)/', '', $css);
216
  }
217
 
218
+ } catch (Exception $exc) {
219
  // Intentional fall-through so we can reset environment
220
  }
221
 
222
+ //reset php settings
223
+ @ini_set('precision',$precision);
224
+ setlocale(LC_NUMERIC, $locale);
225
 
226
  // If you previously defined $this->mb_internal_encoding
227
  // is required to return the encoding as it was before
228
+ if ($this->mb_internal_encoding != '') {
229
+ @ini_set("mbstring.internal_encoding", $this->mb_internal_encoding);
230
  $this->mb_internal_encoding = '';
231
  }
232
 
233
  // Rethrow exception after we handled resetting the environment
234
+ if (!empty($exc)) {
235
  throw $exc;
236
  }
237
 
238
  return $css;
239
  }
240
 
241
+ public function findValueOf($varName)
242
+ {
243
+ foreach($this->rules as $rule){
244
+ if(isset($rule->variable) && ($rule->variable == true) && (str_replace("@","",$rule->name) == $varName)){
245
+ return $this->getVariableValue($rule);
246
  }
247
  }
248
  return null;
255
  *
256
  * @return array
257
  */
258
+ public function getVariables()
259
+ {
260
  $variables = array();
261
 
262
  $not_variable_type = array(
267
  );
268
 
269
  // @TODO run compilation if not runned yet
270
+ foreach ($this->rules as $key => $rule) {
271
+ if (in_array($rule->type, $not_variable_type)) {
272
  continue;
273
  }
274
 
275
  // Note: it seems rule->type is always Rule when variable = true
276
+ if ($rule->type == 'Rule' && $rule->variable) {
277
+ $variables[$rule->name] = $this->getVariableValue($rule);
278
  } else {
279
+ if ($rule->type == 'Comment') {
280
+ $variables[] = $this->getVariableValue($rule);
281
  }
282
  }
283
  }
284
  return $variables;
285
  }
286
 
287
+ public function findVarByName($var_name)
288
+ {
289
+ foreach($this->rules as $rule){
290
+ if(isset($rule->variable) && ($rule->variable == true)){
291
+ if($rule->name == $var_name){
292
+ return $this->getVariableValue($rule);
293
  }
294
  }
295
  }
305
  *
306
  * @return bool|string
307
  */
308
+ private function getVariableValue($var)
309
+ {
310
+ if (!is_a($var, 'Less_Tree')) {
311
+ throw new Exception('var is not a Less_Tree object');
312
  }
313
 
314
+ switch ($var->type) {
315
  case 'Color':
316
+ return $this->rgb2html($var->rgb);
317
  case 'Unit':
318
  return $var->value. $var->unit->numerator[0];
319
  case 'Variable':
320
+ return $this->findVarByName($var->name);
321
  case 'Keyword':
322
  return $var->value;
323
  case 'Rule':
324
+ return $this->getVariableValue($var->value);
325
  case 'Value':
326
  $value = '';
327
+ foreach ($var->value as $sub_value) {
328
+ $value .= $this->getVariableValue($sub_value).' ';
329
  }
330
  return $value;
331
  case 'Quoted':
332
  return $var->quote.$var->value.$var->quote;
333
  case 'Dimension':
334
  $value = $var->value;
335
+ if ($var->unit && $var->unit->numerator) {
336
  $value .= $var->unit->numerator[0];
337
  }
338
  return $value;
339
  case 'Expression':
340
  $value = "";
341
+ foreach($var->value as $item) {
342
+ $value .= $this->getVariableValue($item)." ";
343
  }
344
  return $value;
345
  case 'Operation':
346
+ throw new Exception('getVariables() require Less to be compiled. please use $parser->getCss() before calling getVariables()');
347
  case 'Comment':
348
  case 'Import':
349
  case 'Ruleset':
350
  default:
351
+ throw new Exception("type missing in switch/case getVariableValue for ".$var->type);
352
  }
353
  return false;
354
  }
355
 
356
+ private function rgb2html($r, $g=-1, $b=-1)
357
+ {
358
+ if (is_array($r) && sizeof($r) == 3)
359
+ list($r, $g, $b) = $r;
360
 
361
+ $r = intval($r); $g = intval($g);
362
+ $b = intval($b);
363
 
364
+ $r = dechex($r<0?0:($r>255?255:$r));
365
+ $g = dechex($g<0?0:($g>255?255:$g));
366
+ $b = dechex($b<0?0:($b>255?255:$b));
367
 
368
+ $color = (strlen($r) < 2?'0':'').$r;
369
+ $color .= (strlen($g) < 2?'0':'').$g;
370
+ $color .= (strlen($b) < 2?'0':'').$b;
371
  return '#'.$color;
372
  }
373
 
375
  * Run pre-compile visitors
376
  *
377
  */
378
+ private function PreVisitors($root){
379
+
380
+ if( Less_Parser::$options['plugins'] ){
381
+ foreach(Less_Parser::$options['plugins'] as $plugin){
382
+ if( !empty($plugin->isPreEvalVisitor) ){
383
+ $plugin->run($root);
384
  }
385
  }
386
  }
387
  }
388
 
389
+
390
  /**
391
  * Run post-compile visitors
392
  *
393
  */
394
+ private function PostVisitors($evaldRoot){
395
+
396
  $visitors = array();
397
  $visitors[] = new Less_Visitor_joinSelector();
398
+ if( self::$has_extends ){
399
  $visitors[] = new Less_Visitor_processExtends();
400
  }
401
  $visitors[] = new Less_Visitor_toCSS();
402
 
403
+
404
+ if( Less_Parser::$options['plugins'] ){
405
+ foreach(Less_Parser::$options['plugins'] as $plugin){
406
+ if( property_exists($plugin,'isPreEvalVisitor') && $plugin->isPreEvalVisitor ){
407
  continue;
408
  }
409
 
410
+ if( property_exists($plugin,'isPreVisitor') && $plugin->isPreVisitor ){
411
+ array_unshift( $visitors, $plugin);
412
+ }else{
413
  $visitors[] = $plugin;
414
  }
415
  }
416
  }
417
 
418
+
419
+ for($i = 0; $i < count($visitors); $i++ ){
420
+ $visitors[$i]->run($evaldRoot);
421
  }
422
 
423
  }
424
 
425
+
426
  /**
427
  * Parse a Less string into css
428
  *
430
  * @param string $uri_root The url of the file
431
  * @return Less_Tree_Ruleset|Less_Parser
432
  */
433
+ public function parse( $str, $file_uri = null ){
434
+
435
+ if( !$file_uri ){
436
  $uri_root = '';
437
  $filename = 'anonymous-file-'.Less_Parser::$next_id++.'.less';
438
+ }else{
439
+ $file_uri = self::WinPath($file_uri);
440
  $filename = $file_uri;
441
+ $uri_root = dirname($file_uri);
442
  }
443
 
444
  $previousFileInfo = $this->env->currentFileInfo;
445
+ $uri_root = self::WinPath($uri_root);
446
+ $this->SetFileInfo($filename, $uri_root);
447
 
448
  $this->input = $str;
449
  $this->_parse();
450
 
451
+ if( $previousFileInfo ){
452
  $this->env->currentFileInfo = $previousFileInfo;
453
  }
454
 
455
  return $this;
456
  }
457
 
458
+
459
  /**
460
  * Parse a Less string from a given file
461
  *
465
  * @param bool $returnRoot Indicates whether the return value should be a css string a root node
466
  * @return Less_Tree_Ruleset|Less_Parser
467
  */
468
+ public function parseFile( $filename, $uri_root = '', $returnRoot = false){
469
+
470
+ if( !file_exists($filename) ){
471
+ $this->Error(sprintf('File `%s` not found.', $filename));
472
  }
473
 
474
+
475
  // fix uri_root?
476
  // Instead of The mixture of file path for the first argument and directory path for the second argument has bee
477
+ if( !$returnRoot && !empty($uri_root) && basename($uri_root) == basename($filename) ){
478
+ $uri_root = dirname($uri_root);
479
  }
480
 
481
+
482
  $previousFileInfo = $this->env->currentFileInfo;
483
 
484
+
485
+ if( $filename ){
486
+ $filename = self::AbsPath($filename, true);
487
  }
488
+ $uri_root = self::WinPath($uri_root);
489
 
490
+ $this->SetFileInfo($filename, $uri_root);
491
 
492
+ self::AddParsedFile($filename);
493
 
494
+ if( $returnRoot ){
495
  $rules = $this->GetRules( $filename );
496
+ $return = new Less_Tree_Ruleset(array(), $rules );
497
+ }else{
498
  $this->_parse( $filename );
499
  $return = $this;
500
  }
501
 
502
+ if( $previousFileInfo ){
503
  $this->env->currentFileInfo = $previousFileInfo;
504
  }
505
 
506
  return $return;
507
  }
508
 
509
+
510
  /**
511
  * Allows a user to set variables values
512
  * @param array $vars
513
  * @return Less_Parser
514
  */
515
+ public function ModifyVars( $vars ){
516
+
517
  $this->input = Less_Parser::serializeVars( $vars );
518
  $this->_parse();
519
 
520
  return $this;
521
  }
522
 
523
+
524
  /**
525
  * @param string $filename
526
  */
527
+ public function SetFileInfo( $filename, $uri_root = ''){
528
+
529
+ $filename = Less_Environment::normalizePath($filename);
530
+ $dirname = preg_replace('/[^\/\\\\]*$/','',$filename);
531
 
532
+ if( !empty($uri_root) ){
533
+ $uri_root = rtrim($uri_root,'/').'/';
534
  }
535
 
536
  $currentFileInfo = array();
537
 
538
+ //entry info
539
+ if( isset($this->env->currentFileInfo) ){
540
  $currentFileInfo['entryPath'] = $this->env->currentFileInfo['entryPath'];
541
  $currentFileInfo['entryUri'] = $this->env->currentFileInfo['entryUri'];
542
  $currentFileInfo['rootpath'] = $this->env->currentFileInfo['rootpath'];
543
 
544
+ }else{
545
  $currentFileInfo['entryPath'] = $dirname;
546
  $currentFileInfo['entryUri'] = $uri_root;
547
  $currentFileInfo['rootpath'] = $dirname;
548
  }
549
 
550
  $currentFileInfo['currentDirectory'] = $dirname;
551
+ $currentFileInfo['currentUri'] = $uri_root.basename($filename);
552
  $currentFileInfo['filename'] = $filename;
553
  $currentFileInfo['uri_root'] = $uri_root;
554
 
555
+
556
+ //inherit reference
557
+ if( isset($this->env->currentFileInfo['reference']) && $this->env->currentFileInfo['reference'] ){
558
  $currentFileInfo['reference'] = true;
559
  }
560
 
561
  $this->env->currentFileInfo = $currentFileInfo;
562
  }
563
 
564
+
565
  /**
566
  * @deprecated 1.5.1.2
567
  *
568
  */
569
+ public function SetCacheDir( $dir ){
570
+
571
+ if( !file_exists($dir) ){
572
+ if( mkdir($dir) ){
573
  return true;
574
  }
575
+ throw new Less_Exception_Parser('Less.php cache directory couldn\'t be created: '.$dir);
576
 
577
+ }elseif( !is_dir($dir) ){
578
+ throw new Less_Exception_Parser('Less.php cache directory doesn\'t exist: '.$dir);
579
 
580
+ }elseif( !is_writable($dir) ){
581
+ throw new Less_Exception_Parser('Less.php cache directory isn\'t writable: '.$dir);
582
 
583
+ }else{
584
+ $dir = self::WinPath($dir);
585
+ Less_Cache::$cache_dir = rtrim($dir,'/').'/';
586
  return true;
587
  }
588
  }
589
 
590
+
591
  /**
592
  * Set a list of directories or callbacks the parser should use for determining import paths
593
  *
594
  * @param array $dirs
595
  */
596
+ public function SetImportDirs( $dirs ){
597
  Less_Parser::$options['import_dirs'] = array();
598
 
599
+ foreach($dirs as $path => $uri_root){
600
 
601
+ $path = self::WinPath($path);
602
+ if( !empty($path) ){
603
+ $path = rtrim($path,'/').'/';
604
  }
605
 
606
+ if ( !is_callable($uri_root) ){
607
+ $uri_root = self::WinPath($uri_root);
608
+ if( !empty($uri_root) ){
609
+ $uri_root = rtrim($uri_root,'/').'/';
610
  }
611
  }
612
 
617
  /**
618
  * @param string $file_path
619
  */
620
+ private function _parse( $file_path = null ){
621
+ $this->rules = array_merge($this->rules, $this->GetRules( $file_path ));
622
  }
623
 
624
+
625
  /**
626
  * Return the results of parsePrimary for $file_path
627
  * Use cache and save cached results if possible
628
  *
629
  * @param string|null $file_path
630
  */
631
+ private function GetRules( $file_path ){
632
+
633
+ $this->SetInput($file_path);
634
 
635
  $cache_file = $this->CacheFile( $file_path );
636
+ if( $cache_file ){
637
+ if( Less_Parser::$options['cache_method'] == 'callback' ){
638
+ if( is_callable(Less_Parser::$options['cache_callback_get']) ){
639
  $cache = call_user_func_array(
640
  Less_Parser::$options['cache_callback_get'],
641
+ array($this, $file_path, $cache_file)
642
  );
643
 
644
+ if( $cache ){
645
  $this->UnsetInput();
646
  return $cache;
647
  }
648
  }
649
 
650
+ }elseif( file_exists($cache_file) ){
651
+ switch(Less_Parser::$options['cache_method']){
652
 
653
  // Using serialize
654
  // Faster but uses more memory
655
  case 'serialize':
656
+ $cache = unserialize(file_get_contents($cache_file));
657
+ if( $cache ){
658
+ touch($cache_file);
659
  $this->UnsetInput();
660
  return $cache;
661
  }
662
  break;
663
 
664
+
665
  // Using generated php code
666
  case 'var_export':
667
  case 'php':
668
  $this->UnsetInput();
669
+ return include($cache_file);
670
  }
671
  }
672
  }
673
 
674
  $rules = $this->parsePrimary();
675
 
676
+ if( $this->pos < $this->input_len ){
677
+ throw new Less_Exception_Chunk($this->input, null, $this->furthest, $this->env->currentFileInfo);
678
  }
679
 
680
  $this->UnsetInput();
681
 
682
+
683
+ //save the cache
684
+ if( $cache_file ){
685
+ if( Less_Parser::$options['cache_method'] == 'callback' ){
686
+ if( is_callable(Less_Parser::$options['cache_callback_set']) ){
687
  call_user_func_array(
688
  Less_Parser::$options['cache_callback_set'],
689
+ array($this, $file_path, $cache_file, $rules)
690
  );
691
  }
692
 
693
+ }else{
694
+ //msg('write cache file');
695
+ switch(Less_Parser::$options['cache_method']){
696
  case 'serialize':
697
+ file_put_contents( $cache_file, serialize($rules) );
698
  break;
699
  case 'php':
700
+ file_put_contents( $cache_file, '<?php return '.self::ArgString($rules).'; ?>' );
701
  break;
702
  case 'var_export':
703
+ //Requires __set_state()
704
+ file_put_contents( $cache_file, '<?php return '.var_export($rules,true).'; ?>' );
705
  break;
706
  }
707
 
712
  return $rules;
713
  }
714
 
715
+
716
  /**
717
  * Set up the input buffer
718
  *
719
  */
720
+ public function SetInput( $file_path ){
721
+
722
+ if( $file_path ){
723
  $this->input = file_get_contents( $file_path );
724
  }
725
 
726
  $this->pos = $this->furthest = 0;
727
 
728
  // Remove potential UTF Byte Order Mark
729
+ $this->input = preg_replace('/\\G\xEF\xBB\xBF/', '', $this->input);
730
+ $this->input_len = strlen($this->input);
731
 
732
+
733
+ if( Less_Parser::$options['sourceMap'] && $this->env->currentFileInfo ){
734
  $uri = $this->env->currentFileInfo['currentUri'];
735
  Less_Parser::$contentsMap[$uri] = $this->input;
736
  }
737
 
738
  }
739
 
740
+
741
  /**
742
  * Free up some memory
743
  *
744
  */
745
+ public function UnsetInput(){
746
+ unset($this->input, $this->pos, $this->input_len, $this->furthest);
747
  $this->saveStack = array();
748
  }
749
 
 
 
750
 
751
+ public function CacheFile( $file_path ){
752
+
753
+ if( $file_path && $this->CacheEnabled() ){
754
+
755
+ $env = get_object_vars($this->env);
756
+ unset($env['frames']);
757
 
758
  $parts = array();
759
  $parts[] = $file_path;
762
  $parts[] = $env;
763
  $parts[] = Less_Version::cache_version;
764
  $parts[] = Less_Parser::$options['cache_method'];
765
+ return Less_Cache::$cache_dir . Less_Cache::$prefix . base_convert( sha1(json_encode($parts) ), 16, 36) . '.lesscache';
766
  }
767
  }
768
 
769
+
770
+ static function AddParsedFile($file){
771
  self::$imports[] = $file;
772
  }
773
 
774
+ static function AllParsedFiles(){
775
  return self::$imports;
776
  }
777
 
778
  /**
779
  * @param string $file
780
  */
781
+ static function FileParsed($file){
782
+ return in_array($file,self::$imports);
783
  }
784
 
785
+
786
  function save() {
787
  $this->saveStack[] = $this->pos;
788
  }
789
 
790
  private function restore() {
791
+ $this->pos = array_pop($this->saveStack);
792
  }
793
 
794
+ private function forget(){
795
+ array_pop($this->saveStack);
796
  }
797
 
798
  /**
802
  *
803
  * @return bool
804
  */
805
+ private function isWhitespace($offset = 0) {
806
+ return strpos(" \t\n\r\v\f", $this->input[$this->pos + $offset]) !== false;
807
  }
808
 
809
  /**
812
  * @param array $toks
813
  * @return array
814
  */
815
+ private function match($toks){
816
+
817
  // The match is confirmed, add the match length to `this::pos`,
818
  // and consume any extra white-space characters (' ' || '\n')
819
  // which come after that. The reason for this is that LeSS's
820
  // grammar is mostly white-space insensitive.
821
  //
822
 
823
+ foreach($toks as $tok){
824
 
825
  $char = $tok[0];
826
 
827
+ if( $char === '/' ){
828
+ $match = $this->MatchReg($tok);
829
 
830
+ if( $match ){
831
+ return count($match) === 1 ? $match[0] : $match;
832
  }
833
 
834
+ }elseif( $char === '#' ){
835
+ $match = $this->MatchChar($tok[1]);
836
 
837
+ }else{
838
  // Non-terminal, match using a function call
839
  $match = $this->$tok();
840
 
841
  }
842
 
843
+ if( $match ){
844
  return $match;
845
  }
846
  }
851
  *
852
  * @return string
853
  */
854
+ private function MatchFuncs($toks){
855
+
856
+ if( $this->pos < $this->input_len ){
857
+ foreach($toks as $tok){
858
  $match = $this->$tok();
859
+ if( $match ){
860
  return $match;
861
  }
862
  }
865
  }
866
 
867
  // Match a single character in the input,
868
+ private function MatchChar($tok){
869
+ if( ($this->pos < $this->input_len) && ($this->input[$this->pos] === $tok) ){
870
+ $this->skipWhitespace(1);
871
  return $tok;
872
  }
873
  }
874
 
875
  // Match a regexp from the current start point
876
+ private function MatchReg($tok){
877
+
878
+ if( preg_match($tok, $this->input, $match, 0, $this->pos) ){
879
+ $this->skipWhitespace(strlen($match[0]));
880
  return $match;
881
  }
882
  }
883
 
884
+
885
  /**
886
  * Same as match(), but don't change the state of the parser,
887
  * just return the match.
889
  * @param string $tok
890
  * @return integer
891
  */
892
+ public function PeekReg($tok){
893
+ return preg_match($tok, $this->input, $match, 0, $this->pos);
894
  }
895
 
896
  /**
897
  * @param string $tok
898
  */
899
+ public function PeekChar($tok){
900
+ //return ($this->input[$this->pos] === $tok );
901
+ return ($this->pos < $this->input_len) && ($this->input[$this->pos] === $tok );
902
  }
903
 
904
+
905
  /**
906
  * @param integer $length
907
  */
908
+ public function skipWhitespace($length){
909
+
910
  $this->pos += $length;
911
 
912
+ for(; $this->pos < $this->input_len; $this->pos++ ){
913
  $c = $this->input[$this->pos];
914
 
915
+ if( ($c !== "\n") && ($c !== "\r") && ($c !== "\t") && ($c !== ' ') ){
916
  break;
917
  }
918
  }
919
  }
920
 
921
+
922
  /**
923
  * @param string $tok
924
  * @param string|null $msg
925
  */
926
+ public function expect($tok, $msg = NULL) {
927
+ $result = $this->match( array($tok) );
928
+ if (!$result) {
929
+ $this->Error( $msg ? "Expected '" . $tok . "' got '" . $this->input[$this->pos] . "'" : $msg );
930
  } else {
931
  return $result;
932
  }
935
  /**
936
  * @param string $tok
937
  */
938
+ public function expectChar($tok, $msg = null ){
939
+ $result = $this->MatchChar($tok);
940
+ if( !$result ){
941
  $msg = $msg ? $msg : "Expected '" . $tok . "' got '" . $this->input[$this->pos] . "'";
942
  $this->Error( $msg );
943
+ }else{
944
  return $result;
945
  }
946
  }
990
  // Only at one point is the primary rule not called from the
991
  // block rule: at the root level.
992
  //
993
+ private function parsePrimary(){
994
  $root = array();
995
 
996
+ while( true ){
997
 
998
+ if( $this->pos >= $this->input_len ){
999
  break;
1000
  }
1001
 
1002
+ $node = $this->parseExtend(true);
1003
+ if( $node ){
1004
+ $root = array_merge($root,$node);
1005
  continue;
1006
  }
1007
 
1008
+ //$node = $this->MatchFuncs( array( 'parseMixinDefinition', 'parseRule', 'parseRuleset', 'parseMixinCall', 'parseComment', 'parseDirective'));
1009
+ $node = $this->MatchFuncs( array( 'parseMixinDefinition', 'parseNameValue', 'parseRule', 'parseRuleset', 'parseMixinCall', 'parseComment', 'parseRulesetCall', 'parseDirective'));
1010
 
1011
+ if( $node ){
1012
  $root[] = $node;
1013
+ }elseif( !$this->MatchReg('/\\G[\s\n;]+/') ){
1014
  break;
1015
  }
1016
 
1017
+ if( $this->PeekChar('}') ){
1018
  break;
1019
  }
1020
  }
1022
  return $root;
1023
  }
1024
 
1025
+
1026
+
1027
  // We create a Comment node for CSS comments `/* */`,
1028
  // but keep the LeSS comments `//` silent, by just skipping
1029
  // over them.
1030
+ private function parseComment(){
1031
+
1032
+ if( $this->input[$this->pos] !== '/' ){
1033
  return;
1034
  }
1035
 
1036
+ if( $this->input[$this->pos+1] === '/' ){
1037
+ $match = $this->MatchReg('/\\G\/\/.*/');
1038
+ return $this->NewObj4('Less_Tree_Comment',array($match[0], true, $this->pos, $this->env->currentFileInfo));
1039
  }
1040
 
1041
+ //$comment = $this->MatchReg('/\\G\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/');
1042
+ $comment = $this->MatchReg('/\\G\/\*(?s).*?\*+\/\n?/');//not the same as less.js to prevent fatal errors
1043
+ if( $comment ){
1044
+ return $this->NewObj4('Less_Tree_Comment',array($comment[0], false, $this->pos, $this->env->currentFileInfo));
1045
  }
1046
  }
1047
 
1048
+ private function parseComments(){
1049
  $comments = array();
1050
 
1051
+ while( $this->pos < $this->input_len ){
1052
  $comment = $this->parseComment();
1053
+ if( !$comment ){
1054
  break;
1055
  }
1056
 
1060
  return $comments;
1061
  }
1062
 
1063
+
1064
+
1065
  //
1066
  // A string, which supports escaping " and '
1067
  //
1072
  $e = false;
1073
  $index = $this->pos;
1074
 
1075
+ if( $this->input[$this->pos] === '~' ){
1076
  $j++;
1077
  $e = true; // Escaped strings
1078
  }
1079
 
1080
  $char = $this->input[$j];
1081
+ if( $char !== '"' && $char !== "'" ){
1082
  return;
1083
  }
1084
 
1085
+ if ($e) {
1086
+ $this->MatchChar('~');
1087
  }
1088
 
1089
+
1090
+ $matched = $this->MatchQuoted($char, $j+1);
1091
+ if( $matched === false ){
1092
  return;
1093
  }
1094
 
1095
  $quoted = $char.$matched.$char;
1096
+ return $this->NewObj5('Less_Tree_Quoted',array($quoted, $matched, $e, $index, $this->env->currentFileInfo) );
1097
  }
1098
 
1099
+
1100
  /**
1101
  * When PCRE JIT is enabled in php, regular expressions don't work for matching quoted strings
1102
  *
1104
  * $regex = '/\\G"((?:[^"\\\\\r\n]|\\\\.|\\\\\r\n|\\\\[\n\r\f])*)"/';
1105
  *
1106
  */
1107
+ private function MatchQuoted($quote_char, $i){
1108
+
1109
  $matched = '';
1110
+ while( $i < $this->input_len ){
1111
  $c = $this->input[$i];
1112
 
1113
+ //escaped character
1114
+ if( $c === '\\' ){
1115
+ $matched .= $c . $this->input[$i+1];
1116
  $i += 2;
1117
  continue;
1118
  }
1119
 
1120
+ if( $c === $quote_char ){
1121
+ $this->pos = $i+1;
1122
+ $this->skipWhitespace(0);
1123
  return $matched;
1124
  }
1125
 
1126
+ if( $c === "\r" || $c === "\n" ){
1127
  return false;
1128
  }
1129
 
1134
  return false;
1135
  }
1136
 
1137
+
1138
  //
1139
  // A catch-all word, such as:
1140
  //
1141
  // black border-collapse
1142
  //
1143
+ private function parseEntitiesKeyword(){
1144
+
1145
+ //$k = $this->MatchReg('/\\G[_A-Za-z-][_A-Za-z0-9-]*/');
1146
+ $k = $this->MatchReg('/\\G%|\\G[_A-Za-z-][_A-Za-z0-9-]*/');
1147
+ if( $k ){
1148
  $k = $k[0];
1149
+ $color = $this->fromKeyword($k);
1150
+ if( $color ){
1151
  return $color;
1152
  }
1153
+ return $this->NewObj1('Less_Tree_Keyword',$k);
1154
  }
1155
  }
1156
 
1157
  // duplicate of Less_Tree_Color::FromKeyword
1158
+ private function FromKeyword( $keyword ){
1159
+ $keyword = strtolower($keyword);
1160
 
1161
+ if( Less_Colors::hasOwnProperty($keyword) ){
1162
  // detect named color
1163
+ return $this->NewObj1('Less_Tree_Color',substr(Less_Colors::color($keyword), 1));
1164
  }
1165
 
1166
+ if( $keyword === 'transparent' ){
1167
+ return $this->NewObj3('Less_Tree_Color', array( array(0, 0, 0), 0, true));
1168
  }
1169
  }
1170
 
1178
  //
1179
  // The arguments are parsed with the `entities.arguments` parser.
1180
  //
1181
+ private function parseEntitiesCall(){
1182
  $index = $this->pos;
1183
 
1184
+ if( !preg_match('/\\G([\w-]+|%|progid:[\w\.]+)\(/', $this->input, $name,0,$this->pos) ){
1185
  return;
1186
  }
1187
  $name = $name[1];
1188
+ $nameLC = strtolower($name);
1189
 
1190
+ if ($nameLC === 'url') {
1191
  return null;
1192
  }
1193
 
1194
+ $this->pos += strlen($name);
1195
 
1196
+ if( $nameLC === 'alpha' ){
1197
  $alpha_ret = $this->parseAlpha();
1198
+ if( $alpha_ret ){
1199
  return $alpha_ret;
1200
  }
1201
  }
1202
 
1203
+ $this->MatchChar('('); // Parse the '(' and consume whitespace.
1204
 
1205
  $args = $this->parseEntitiesArguments();
1206
 
1207
+ if( !$this->MatchChar(')') ){
1208
  return;
1209
  }
1210
 
1211
+ if ($name) {
1212
+ return $this->NewObj4('Less_Tree_Call',array($name, $args, $index, $this->env->currentFileInfo) );
1213
  }
1214
  }
1215
 
1218
  *
1219
  * @return array
1220
  */
1221
+ private function parseEntitiesArguments(){
1222
+
1223
  $args = array();
1224
+ while( true ){
1225
+ $arg = $this->MatchFuncs( array('parseEntitiesAssignment','parseExpression') );
1226
+ if( !$arg ){
1227
  break;
1228
  }
1229
 
1230
  $args[] = $arg;
1231
+ if( !$this->MatchChar(',') ){
1232
  break;
1233
  }
1234
  }
1235
  return $args;
1236
  }
1237
 
1238
+ private function parseEntitiesLiteral(){
1239
+ return $this->MatchFuncs( array('parseEntitiesDimension','parseEntitiesColor','parseEntitiesQuoted','parseUnicodeDescriptor') );
1240
  }
1241
 
1242
  // Assignments are argument entities for calls.
1245
  // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* )
1246
  //
1247
  private function parseEntitiesAssignment() {
1248
+
1249
+ $key = $this->MatchReg('/\\G\w+(?=\s?=)/');
1250
+ if( !$key ){
1251
  return;
1252
  }
1253
 
1254
+ if( !$this->MatchChar('=') ){
1255
  return;
1256
  }
1257
 
1258
  $value = $this->parseEntity();
1259
+ if( $value ){
1260
+ return $this->NewObj2('Less_Tree_Assignment',array($key[0], $value));
1261
  }
1262
  }
1263
 
1268
  // standard function calls. The difference is that the argument doesn't have
1269
  // to be enclosed within a string, so it can't be parsed as an Expression.
1270
  //
1271
+ private function parseEntitiesUrl(){
1272
+
1273
+
1274
+ if( $this->input[$this->pos] !== 'u' || !$this->matchReg('/\\Gurl\(/') ){
1275
  return;
1276
  }
1277
 
1278
+ $value = $this->match( array('parseEntitiesQuoted','parseEntitiesVariable','/\\Gdata\:.*?[^\)]+/','/\\G(?:(?:\\\\[\(\)\'"])|[^\(\)\'"])+/') );
1279
+ if( !$value ){
1280
  $value = '';
1281
  }
1282
 
 
1283
 
1284
+ $this->expectChar(')');
1285
+
1286
+
1287
+ if( isset($value->value) || $value instanceof Less_Tree_Variable ){
1288
+ return $this->NewObj2('Less_Tree_Url',array($value, $this->env->currentFileInfo));
1289
  }
1290
 
1291
+ return $this->NewObj2('Less_Tree_Url', array( $this->NewObj1('Less_Tree_Anonymous',$value), $this->env->currentFileInfo) );
1292
  }
1293
 
1294
+
1295
  //
1296
  // A Variable entity, such as `@fink`, in
1297
  //
1300
  // We use a different parser for variable definitions,
1301
  // see `parsers.variable`.
1302
  //
1303
+ private function parseEntitiesVariable(){
1304
  $index = $this->pos;
1305
+ if ($this->PeekChar('@') && ($name = $this->MatchReg('/\\G@@?[\w-]+/'))) {
1306
+ return $this->NewObj3('Less_Tree_Variable', array( $name[0], $index, $this->env->currentFileInfo));
1307
  }
1308
  }
1309
 
1310
+
1311
  // A variable entity using the protective {} e.g. @{var}
1312
  private function parseEntitiesVariableCurly() {
1313
  $index = $this->pos;
1314
 
1315
+ if( $this->input_len > ($this->pos+1) && $this->input[$this->pos] === '@' && ($curly = $this->MatchReg('/\\G@\{([\w-]+)\}/')) ){
1316
+ return $this->NewObj3('Less_Tree_Variable',array('@'.$curly[1], $index, $this->env->currentFileInfo));
1317
  }
1318
  }
1319
 
1324
  //
1325
  // `rgb` and `hsl` colors are parsed through the `entities.call` parser.
1326
  //
1327
+ private function parseEntitiesColor(){
1328
+ if ($this->PeekChar('#') && ($rgb = $this->MatchReg('/\\G#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/'))) {
1329
+ return $this->NewObj1('Less_Tree_Color',$rgb[1]);
1330
  }
1331
  }
1332
 
1335
  //
1336
  // 0.5em 95%
1337
  //
1338
+ private function parseEntitiesDimension(){
1339
+
1340
+ $c = @ord($this->input[$this->pos]);
1341
 
1342
+ //Is the first char of the dimension 0-9, '.', '+' or '-'
1343
+ if (($c > 57 || $c < 43) || $c === 47 || $c == 44){
1344
  return;
1345
  }
1346
 
1347
+ $value = $this->MatchReg('/\\G([+-]?\d*\.?\d+)(%|[a-z]+)?/');
1348
+ if( $value ){
1349
 
1350
+ if( isset($value[2]) ){
1351
+ return $this->NewObj2('Less_Tree_Dimension', array($value[1],$value[2]));
1352
  }
1353
+ return $this->NewObj1('Less_Tree_Dimension',$value[1]);
1354
  }
1355
  }
1356
 
1357
+
1358
  //
1359
  // A unicode descriptor, as is used in unicode-range
1360
  //
1361
  // U+0?? or U+00A1-00A9
1362
  //
1363
  function parseUnicodeDescriptor() {
1364
+ $ud = $this->MatchReg('/\\G(U\+[0-9a-fA-F?]+)(\-[0-9a-fA-F?]+)?/');
1365
+ if( $ud ){
1366
+ return $this->NewObj1('Less_Tree_UnicodeDescriptor', $ud[0]);
1367
  }
1368
  }
1369
 
1370
+
1371
  //
1372
  // JavaScript code to be evaluated
1373
  //
1374
  // `window.location.href`
1375
  //
1376
+ private function parseEntitiesJavascript(){
1377
  $e = false;
1378
  $j = $this->pos;
1379
+ if( $this->input[$j] === '~' ){
1380
  $j++;
1381
  $e = true;
1382
  }
1383
+ if( $this->input[$j] !== '`' ){
1384
  return;
1385
  }
1386
+ if( $e ){
1387
+ $this->MatchChar('~');
1388
  }
1389
+ $str = $this->MatchReg('/\\G`([^`]*)`/');
1390
+ if( $str ){
1391
+ return $this->NewObj3('Less_Tree_Javascript', array($str[1], $this->pos, $e));
1392
  }
1393
  }
1394
 
1395
+
1396
  //
1397
  // The variable part of a variable definition. Used in the `rule` parser
1398
  //
1399
  // @fink:
1400
  //
1401
+ private function parseVariable(){
1402
+ if ($this->PeekChar('@') && ($name = $this->MatchReg('/\\G(@[\w-]+)\s*:/'))) {
1403
  return $name[1];
1404
  }
1405
  }
1406
 
1407
+
1408
  //
1409
  // The variable part of a variable definition. Used in the `rule` parser
1410
  //
1411
  // @fink();
1412
  //
1413
+ private function parseRulesetCall(){
1414
+
1415
+ if( $this->input[$this->pos] === '@' && ($name = $this->MatchReg('/\\G(@[\w-]+)\s*\(\s*\)\s*;/')) ){
1416
+ return $this->NewObj1('Less_Tree_RulesetCall', $name[1] );
1417
  }
1418
  }
1419
 
1420
+
1421
  //
1422
  // extend syntax - used to extend selectors
1423
  //
1424
+ function parseExtend($isRule = false){
1425
+
1426
  $index = $this->pos;
1427
  $extendList = array();
1428
 
1429
+
1430
+ if( !$this->MatchReg( $isRule ? '/\\G&:extend\(/' : '/\\G:extend\(/' ) ){ return; }
1431
 
1432
  do{
1433
  $option = null;
1434
  $elements = array();
1435
+ while( true ){
1436
+ $option = $this->MatchReg('/\\G(all)(?=\s*(\)|,))/');
1437
+ if( $option ){ break; }
 
1438
  $e = $this->parseElement();
1439
+ if( !$e ){ break; }
 
1440
  $elements[] = $e;
1441
  }
1442
 
1443
+ if( $option ){
1444
  $option = $option[1];
1445
  }
1446
 
1447
+ $extendList[] = $this->NewObj3('Less_Tree_Extend', array( $this->NewObj1('Less_Tree_Selector',$elements), $option, $index ));
1448
 
1449
+ }while( $this->MatchChar(",") );
1450
 
1451
+ $this->expect('/\\G\)/');
1452
 
1453
+ if( $isRule ){
1454
+ $this->expect('/\\G;/');
1455
  }
1456
 
1457
  return $extendList;
1458
  }
1459
 
1460
+
1461
  //
1462
  // A Mixin call, with an optional argument list
1463
  //
1469
  // namespaced, but we only support the child and descendant
1470
  // selector for now.
1471
  //
1472
+ private function parseMixinCall(){
1473
+
1474
  $char = $this->input[$this->pos];
1475
+ if( $char !== '.' && $char !== '#' ){
1476
  return;
1477
  }
1478
 
1481
 
1482
  $elements = $this->parseMixinCallElements();
1483
 
1484
+ if( $elements ){
1485
 
1486
+ if( $this->MatchChar('(') ){
1487
+ $returned = $this->parseMixinArgs(true);
1488
  $args = $returned['args'];
1489
+ $this->expectChar(')');
1490
+ }else{
1491
  $args = array();
1492
  }
1493
 
1494
  $important = $this->parseImportant();
1495
 
1496
+ if( $this->parseEnd() ){
1497
  $this->forget();
1498
+ return $this->NewObj5('Less_Tree_Mixin_Call', array( $elements, $args, $index, $this->env->currentFileInfo, $important));
1499
  }
1500
  }
1501
 
1502
  $this->restore();
1503
  }
1504
 
1505
+
1506
+ private function parseMixinCallElements(){
1507
  $elements = array();
1508
  $c = null;
1509
 
1510
+ while( true ){
1511
  $elemIndex = $this->pos;
1512
+ $e = $this->MatchReg('/\\G[#.](?:[\w-]|\\\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/');
1513
+ if( !$e ){
1514
  break;
1515
  }
1516
+ $elements[] = $this->NewObj4('Less_Tree_Element', array($c, $e[0], $elemIndex, $this->env->currentFileInfo));
1517
+ $c = $this->MatchChar('>');
1518
  }
1519
 
1520
  return $elements;
1521
  }
1522
 
1523
+
1524
+
1525
  /**
1526
  * @param boolean $isCall
1527
  */
1528
+ private function parseMixinArgs( $isCall ){
1529
  $expressions = array();
1530
  $argsSemiColon = array();
1531
  $isSemiColonSeperated = null;
1532
  $argsComma = array();
1533
  $expressionContainsNamed = null;
1534
  $name = null;
1535
+ $returner = array('args'=>array(), 'variadic'=> false);
1536
 
1537
  $this->save();
1538
 
1539
+ while( true ){
1540
+ if( $isCall ){
1541
  $arg = $this->MatchFuncs( array( 'parseDetachedRuleset','parseExpression' ) );
1542
  } else {
1543
  $this->parseComments();
1544
+ if( $this->input[ $this->pos ] === '.' && $this->MatchReg('/\\G\.{3}/') ){
1545
  $returner['variadic'] = true;
1546
+ if( $this->MatchChar(";") && !$isSemiColonSeperated ){
1547
  $isSemiColonSeperated = true;
1548
  }
1549
 
1550
+ if( $isSemiColonSeperated ){
1551
+ $argsSemiColon[] = array('variadic'=>true);
1552
+ }else{
1553
+ $argsComma[] = array('variadic'=>true);
1554
  }
1555
  break;
1556
  }
1557
+ $arg = $this->MatchFuncs( array('parseEntitiesVariable','parseEntitiesLiteral','parseEntitiesKeyword') );
1558
  }
1559
 
1560
+ if( !$arg ){
1561
  break;
1562
  }
1563
 
1564
+
1565
  $nameLoop = null;
1566
+ if( $arg instanceof Less_Tree_Expression ){
1567
  $arg->throwAwayComments();
1568
  }
1569
  $value = $arg;
1570
  $val = null;
1571
 
1572
+ if( $isCall ){
1573
  // Variable
1574
+ if( property_exists($arg,'value') && count($arg->value) == 1 ){
1575
  $val = $arg->value[0];
1576
  }
1577
  } else {
1578
  $val = $arg;
1579
  }
1580
 
 
1581
 
1582
+ if( $val instanceof Less_Tree_Variable ){
1583
+
1584
+ if( $this->MatchChar(':') ){
1585
+ if( $expressions ){
1586
+ if( $isSemiColonSeperated ){
1587
+ $this->Error('Cannot mix ; and , as delimiter types');
1588
  }
1589
  $expressionContainsNamed = true;
1590
  }
1593
  // However if we do want to add it, there is nothing blocking it, just don't error
1594
  // and remove isCall dependency below
1595
  $value = null;
1596
+ if( $isCall ){
1597
  $value = $this->parseDetachedRuleset();
1598
  }
1599
+ if( !$value ){
1600
  $value = $this->parseExpression();
1601
  }
1602
 
1603
+ if( !$value ){
1604
+ if( $isCall ){
1605
+ $this->Error('could not understand value for named argument');
1606
  } else {
1607
  $this->restore();
1608
  $returner['args'] = array();
1610
  }
1611
  }
1612
 
1613
+ $nameLoop = ($name = $val->name);
1614
+ }elseif( !$isCall && $this->MatchReg('/\\G\.{3}/') ){
1615
  $returner['variadic'] = true;
1616
+ if( $this->MatchChar(";") && !$isSemiColonSeperated ){
1617
  $isSemiColonSeperated = true;
1618
  }
1619
+ if( $isSemiColonSeperated ){
1620
+ $argsSemiColon[] = array('name'=> $arg->name, 'variadic' => true);
1621
+ }else{
1622
+ $argsComma[] = array('name'=> $arg->name, 'variadic' => true);
1623
  }
1624
  break;
1625
+ }elseif( !$isCall ){
1626
  $name = $nameLoop = $val->name;
1627
  $value = null;
1628
  }
1629
  }
1630
 
1631
+ if( $value ){
1632
  $expressions[] = $value;
1633
  }
1634
 
1635
+ $argsComma[] = array('name'=>$nameLoop, 'value'=>$value );
1636
 
1637
+ if( $this->MatchChar(',') ){
1638
  continue;
1639
  }
1640
 
1641
+ if( $this->MatchChar(';') || $isSemiColonSeperated ){
1642
 
1643
+ if( $expressionContainsNamed ){
1644
+ $this->Error('Cannot mix ; and , as delimiter types');
1645
  }
1646
 
1647
  $isSemiColonSeperated = true;
1648
 
1649
+ if( count($expressions) > 1 ){
1650
+ $value = $this->NewObj1('Less_Tree_Value', $expressions);
1651
  }
1652
+ $argsSemiColon[] = array('name'=>$name, 'value'=>$value );
1653
 
1654
  $name = null;
1655
  $expressions = array();
1658
  }
1659
 
1660
  $this->forget();
1661
+ $returner['args'] = ($isSemiColonSeperated ? $argsSemiColon : $argsComma);
1662
  return $returner;
1663
  }
1664
 
1665
+
1666
+
1667
  //
1668
  // A Mixin definition, with a list of parameters
1669
  //
1683
  // Once we've got our params list, and a closing `)`, we parse
1684
  // the `{...}` block.
1685
  //
1686
+ private function parseMixinDefinition(){
1687
  $cond = null;
1688
 
1689
  $char = $this->input[$this->pos];
1690
+ if( ($char !== '.' && $char !== '#') || ($char === '{' && $this->PeekReg('/\\G[^{]*\}/')) ){
1691
  return;
1692
  }
1693
 
1694
  $this->save();
1695
 
1696
+ $match = $this->MatchReg('/\\G([#.](?:[\w-]|\\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/');
1697
+ if( $match ){
1698
  $name = $match[1];
1699
 
1700
  $argInfo = $this->parseMixinArgs( false );
1701
  $params = $argInfo['args'];
1702
  $variadic = $argInfo['variadic'];
1703
 
1704
+
1705
  // .mixincall("@{a}");
1706
  // looks a bit like a mixin definition..
1707
  // also
1708
  // .mixincall(@a: {rule: set;});
1709
  // so we have to be nice and restore
1710
+ if( !$this->MatchChar(')') ){
1711
  $this->furthest = $this->pos;
1712
  $this->restore();
1713
  return;
1714
  }
1715
 
1716
+
1717
  $this->parseComments();
1718
 
1719
+ if ($this->MatchReg('/\\Gwhen/')) { // Guard
1720
+ $cond = $this->expect('parseConditions', 'Expected conditions');
1721
  }
1722
 
1723
  $ruleset = $this->parseBlock();
1724
 
1725
+ if( is_array($ruleset) ){
1726
  $this->forget();
1727
+ return $this->NewObj5('Less_Tree_Mixin_Definition', array( $name, $params, $ruleset, $cond, $variadic));
1728
  }
1729
 
1730
  $this->restore();
1731
+ }else{
1732
  $this->forget();
1733
  }
1734
  }
1737
  // Entities are the smallest recognized token,
1738
  // and can be found inside a rule's value.
1739
  //
1740
+ private function parseEntity(){
1741
+
1742
+ return $this->MatchFuncs( array('parseEntitiesLiteral','parseEntitiesVariable','parseEntitiesUrl','parseEntitiesCall','parseEntitiesKeyword','parseEntitiesJavascript','parseComment') );
1743
  }
1744
 
1745
  //
1747
  // because the `block` rule will be expecting it, but we still need to make sure
1748
  // it's there, if ';' was omitted.
1749
  //
1750
+ private function parseEnd(){
1751
+ return $this->MatchChar(';') || $this->PeekChar('}');
1752
  }
1753
 
1754
  //
1756
  //
1757
  // alpha(opacity=88)
1758
  //
1759
+ private function parseAlpha(){
1760
+
1761
+ if ( ! $this->MatchReg('/\\G\(opacity=/i')) {
1762
  return;
1763
  }
1764
 
1765
+ $value = $this->MatchReg('/\\G[0-9]+/');
1766
+ if( $value ){
1767
  $value = $value[0];
1768
+ }else{
1769
  $value = $this->parseEntitiesVariable();
1770
+ if( !$value ){
1771
  return;
1772
  }
1773
  }
1774
 
1775
+ $this->expectChar(')');
1776
+ return $this->NewObj1('Less_Tree_Alpha',$value);
1777
  }
1778
 
1779
+
1780
  //
1781
  // A Selector Element
1782
  //
1789
  // they are made out of a `Combinator` (see combinator rule),
1790
  // and an element name, such as a tag a class, or `*`.
1791
  //
1792
+ private function parseElement(){
1793
  $c = $this->parseCombinator();
1794
  $index = $this->pos;
1795
 
1796
+ $e = $this->match( array('/\\G(?:\d+\.\d+|\d+)%/', '/\\G(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/',
1797
+ '#*', '#&', 'parseAttribute', '/\\G\([^()@]+\)/', '/\\G[\.#](?=@)/', 'parseEntitiesVariableCurly') );
1798
 
1799
+ if( is_null($e) ){
1800
  $this->save();
1801
+ if( $this->MatchChar('(') ){
1802
+ if( ($v = $this->parseSelector()) && $this->MatchChar(')') ){
1803
+ $e = $this->NewObj1('Less_Tree_Paren',$v);
1804
  $this->forget();
1805
+ }else{
1806
  $this->restore();
1807
  }
1808
+ }else{
1809
  $this->forget();
1810
  }
1811
  }
1812
 
1813
+ if( !is_null($e) ){
1814
+ return $this->NewObj4('Less_Tree_Element',array( $c, $e, $index, $this->env->currentFileInfo));
1815
  }
1816
  }
1817
 
1823
  // as it's an empty space. We have to check the previous character
1824
  // in the input, to see if it's a ` ` character.
1825
  //
1826
+ private function parseCombinator(){
1827
+ if( $this->pos < $this->input_len ){
1828
  $c = $this->input[$this->pos];
1829
+ if ($c === '>' || $c === '+' || $c === '~' || $c === '|' || $c === '^' ){
1830
 
1831
  $this->pos++;
1832
+ if( $this->input[$this->pos] === '^' ){
1833
  $c = '^^';
1834
  $this->pos++;
1835
  }
1836
 
1837
+ $this->skipWhitespace(0);
1838
 
1839
  return $c;
1840
  }
1841
 
1842
+ if( $this->pos > 0 && $this->isWhitespace(-1) ){
1843
  return ' ';
1844
  }
1845
  }
1849
  // A CSS selector (see selector below)
1850
  // with less extensions e.g. the ability to extend and guard
1851
  //
1852
+ private function parseLessSelector(){
1853
+ return $this->parseSelector(true);
1854
  }
1855
 
1856
  //
1861
  //
1862
  // Selectors are made out of one or more Elements, see above.
1863
  //
1864
+ private function parseSelector( $isLess = false ){
1865
  $elements = array();
1866
  $extendList = array();
1867
  $condition = null;
1871
  $c = null;
1872
  $index = $this->pos;
1873
 
1874
+ while( ($isLess && ($extend = $this->parseExtend())) || ($isLess && ($when = $this->MatchReg('/\\Gwhen/') )) || ($e = $this->parseElement()) ){
1875
+ if( $when ){
1876
+ $condition = $this->expect('parseConditions', 'expected condition');
1877
+ }elseif( $condition ){
1878
+ //error("CSS guard can only be used at the end of selector");
1879
+ }elseif( $extend ){
1880
+ $extendList = array_merge($extendList,$extend);
1881
+ }else{
1882
+ //if( count($extendList) ){
1883
  //error("Extend can only be used at the end of selector");
1884
  //}
1885
+ if( $this->pos < $this->input_len ){
1886
  $c = $this->input[ $this->pos ];
1887
  }
1888
  $elements[] = $e;
1889
  $e = null;
1890
  }
1891
 
1892
+ if( $c === '{' || $c === '}' || $c === ';' || $c === ',' || $c === ')') { break; }
 
1893
  }
1894
 
1895
+ if( $elements ){
1896
+ return $this->NewObj5('Less_Tree_Selector',array($elements, $extendList, $condition, $index, $this->env->currentFileInfo));
1897
  }
1898
+ if( $extendList ) {
1899
+ $this->Error('Extend must be used to extend a selector, it cannot be used on its own');
1900
  }
1901
  }
1902
 
1903
+ private function parseTag(){
1904
+ return ( $tag = $this->MatchReg('/\\G[A-Za-z][A-Za-z-]*[0-9]?/') ) ? $tag : $this->MatchChar('*');
1905
  }
1906
 
1907
+ private function parseAttribute(){
1908
+
1909
  $val = null;
1910
 
1911
+ if( !$this->MatchChar('[') ){
1912
  return;
1913
  }
1914
 
1915
  $key = $this->parseEntitiesVariableCurly();
1916
+ if( !$key ){
1917
+ $key = $this->expect('/\\G(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\\\.)+/');
1918
  }
1919
 
1920
+ $op = $this->MatchReg('/\\G[|~*$^]?=/');
1921
+ if( $op ){
1922
+ $val = $this->match( array('parseEntitiesQuoted','/\\G[0-9]+%/','/\\G[\w-]+/','parseEntitiesVariableCurly') );
1923
  }
1924
 
1925
+ $this->expectChar(']');
1926
 
1927
+ return $this->NewObj3('Less_Tree_Attribute',array( $key, $op === null ? null : $op[0], $val));
1928
  }
1929
 
1930
  //
1931
  // The `block` rule is used by `ruleset` and `mixin.definition`.
1932
  // It's a wrapper around the `primary` rule, with added `{}`.
1933
  //
1934
+ private function parseBlock(){
1935
+ if( $this->MatchChar('{') ){
1936
  $content = $this->parsePrimary();
1937
+ if( $this->MatchChar('}') ){
1938
  return $content;
1939
  }
1940
  }
1941
  }
1942
 
1943
+ private function parseBlockRuleset(){
1944
  $block = $this->parseBlock();
1945
 
1946
+ if( $block ){
1947
+ $block = $this->NewObj2('Less_Tree_Ruleset',array( null, $block));
1948
  }
1949
 
1950
  return $block;
1951
  }
1952
 
1953
+ private function parseDetachedRuleset(){
1954
  $blockRuleset = $this->parseBlockRuleset();
1955
+ if( $blockRuleset ){
1956
+ return $this->NewObj1('Less_Tree_DetachedRuleset',$blockRuleset);
1957
  }
1958
  }
1959
 
1960
  //
1961
  // div, .class, body > p {...}
1962
  //
1963
+ private function parseRuleset(){
1964
  $selectors = array();
1965
 
1966
  $this->save();
1967
 
1968
+ while( true ){
1969
  $s = $this->parseLessSelector();
1970
+ if( !$s ){
1971
  break;
1972
  }
1973
  $selectors[] = $s;
1974
  $this->parseComments();
1975
 
1976
+ if( $s->condition && count($selectors) > 1 ){
1977
+ $this->Error('Guards are only currently allowed on a single selector.');
1978
  }
1979
 
1980
+ if( !$this->MatchChar(',') ){
1981
  break;
1982
  }
1983
+ if( $s->condition ){
1984
+ $this->Error('Guards are only currently allowed on a single selector.');
1985
  }
1986
  $this->parseComments();
1987
  }
1988
 
1989
+
1990
+ if( $selectors ){
1991
  $rules = $this->parseBlock();
1992
+ if( is_array($rules) ){
1993
  $this->forget();
1994
+ return $this->NewObj2('Less_Tree_Ruleset',array( $selectors, $rules)); //Less_Environment::$strictImports
1995
  }
1996
  }
1997
 
2005
  * ex: width:100px;
2006
  *
2007
  */
2008
+ private function parseNameValue(){
2009
+
2010
  $index = $this->pos;
2011
  $this->save();
2012
 
 
 
 
2013
 
2014
+ //$match = $this->MatchReg('/\\G([a-zA-Z\-]+)\s*:\s*((?:\'")?[a-zA-Z0-9\-% \.,!]+?(?:\'")?)\s*([;}])/');
2015
+ $match = $this->MatchReg('/\\G([a-zA-Z\-]+)\s*:\s*([\'"]?[#a-zA-Z0-9\-%\.,]+?[\'"]?) *(! *important)?\s*([;}])/');
2016
+ if( $match ){
2017
+
2018
+ if( $match[4] == '}' ){
2019
+ $this->pos = $index + strlen($match[0])-1;
2020
  }
2021
 
2022
+ if( $match[3] ){
2023
  $match[2] .= ' !important';
2024
  }
2025
 
2026
+ return $this->NewObj4('Less_Tree_NameValue',array( $match[1], $match[2], $index, $this->env->currentFileInfo));
2027
  }
2028
 
2029
  $this->restore();
2030
  }
2031
 
2032
+
2033
+ private function parseRule( $tryAnonymous = null ){
2034
+
2035
  $merge = false;
2036
  $startOfRule = $this->pos;
2037
 
2038
  $c = $this->input[$this->pos];
2039
+ if( $c === '.' || $c === '#' || $c === '&' ){
2040
  return;
2041
  }
2042
 
2043
  $this->save();
2044
+ $name = $this->MatchFuncs( array('parseVariable','parseRuleProperty'));
2045
 
2046
+ if( $name ){
2047
 
2048
+ $isVariable = is_string($name);
2049
 
2050
  $value = null;
2051
+ if( $isVariable ){
2052
  $value = $this->parseDetachedRuleset();
2053
  }
2054
 
2055
  $important = null;
2056
+ if( !$value ){
2057
 
2058
  // prefer to try to parse first if its a variable or we are compressing
2059
  // but always fallback on the other one
2060
  //if( !$tryAnonymous && is_string($name) && $name[0] === '@' ){
2061
+ if( !$tryAnonymous && (Less_Parser::$options['compress'] || $isVariable) ){
2062
+ $value = $this->MatchFuncs( array('parseValue','parseAnonymousValue'));
2063
+ }else{
2064
+ $value = $this->MatchFuncs( array('parseAnonymousValue','parseValue'));
2065
  }
2066
 
2067
  $important = $this->parseImportant();
2069
  // a name returned by this.ruleProperty() is always an array of the form:
2070
  // [string-1, ..., string-n, ""] or [string-1, ..., string-n, "+"]
2071
  // where each item is a tree.Keyword or tree.Variable
2072
+ if( !$isVariable && is_array($name) ){
2073
+ $nm = array_pop($name);
2074
+ if( $nm->value ){
2075
  $merge = $nm->value;
2076
  }
2077
  }
2078
  }
2079
 
2080
+
2081
+ if( $value && $this->parseEnd() ){
2082
  $this->forget();
2083
+ return $this->NewObj6('Less_Tree_Rule',array( $name, $value, $important, $merge, $startOfRule, $this->env->currentFileInfo));
2084
+ }else{
2085
  $this->furthest = $this->pos;
2086
  $this->restore();
2087
+ if( $value && !$tryAnonymous ){
2088
+ return $this->parseRule(true);
2089
  }
2090
  }
2091
+ }else{
2092
  $this->forget();
2093
  }
2094
  }
2095
 
2096
+ function parseAnonymousValue(){
2097
+
2098
+ if( preg_match('/\\G([^@+\/\'"*`(;{}-]*);/',$this->input, $match, 0, $this->pos) ){
2099
+ $this->pos += strlen($match[1]);
2100
+ return $this->NewObj1('Less_Tree_Anonymous',$match[1]);
2101
  }
2102
  }
2103
 
2111
  // file-system operation. The function used for importing is
2112
  // stored in `import`, which we pass to the Import constructor.
2113
  //
2114
+ private function parseImport(){
2115
+
2116
  $this->save();
2117
 
2118
+ $dir = $this->MatchReg('/\\G@import?\s+/');
2119
 
2120
+ if( $dir ){
2121
  $options = $this->parseImportOptions();
2122
+ $path = $this->MatchFuncs( array('parseEntitiesQuoted','parseEntitiesUrl'));
2123
 
2124
+ if( $path ){
2125
  $features = $this->parseMediaFeatures();
2126
+ if( $this->MatchChar(';') ){
2127
+ if( $features ){
2128
+ $features = $this->NewObj1('Less_Tree_Value',$features);
2129
  }
2130
 
2131
  $this->forget();
2132
+ return $this->NewObj5('Less_Tree_Import',array( $path, $features, $options, $this->pos, $this->env->currentFileInfo));
2133
  }
2134
  }
2135
  }
2137
  $this->restore();
2138
  }
2139
 
2140
+ private function parseImportOptions(){
2141
+
2142
  $options = array();
2143
 
2144
  // list of options, surrounded by parens
2145
+ if( !$this->MatchChar('(') ){
2146
  return $options;
2147
  }
2148
  do{
2149
  $optionName = $this->parseImportOption();
2150
+ if( $optionName ){
2151
  $value = true;
2152
+ switch( $optionName ){
2153
  case "css":
2154
  $optionName = "less";
2155
  $value = false;
2160
  break;
2161
  }
2162
  $options[$optionName] = $value;
2163
+ if( !$this->MatchChar(',') ){ break; }
 
2164
  }
2165
+ }while( $optionName );
2166
+ $this->expectChar(')');
2167
  return $options;
2168
  }
2169
 
2170
+ private function parseImportOption(){
2171
+ $opt = $this->MatchReg('/\\G(less|css|multiple|once|inline|reference|optional)/');
2172
+ if( $opt ){
2173
  return $opt[1];
2174
  }
2175
  }
2178
  $nodes = array();
2179
 
2180
  do{
2181
+ $e = $this->MatchFuncs(array('parseEntitiesKeyword','parseEntitiesVariable'));
2182
+ if( $e ){
2183
  $nodes[] = $e;
2184
+ } elseif ($this->MatchChar('(')) {
2185
  $p = $this->parseProperty();
2186
  $e = $this->parseValue();
2187
+ if ($this->MatchChar(')')) {
2188
+ if ($p && $e) {
2189
+ $r = $this->NewObj7('Less_Tree_Rule', array( $p, $e, null, null, $this->pos, $this->env->currentFileInfo, true));
2190
+ $nodes[] = $this->NewObj1('Less_Tree_Paren',$r);
2191
+ } elseif ($e) {
2192
+ $nodes[] = $this->NewObj1('Less_Tree_Paren',$e);
2193
  } else {
2194
  return null;
2195
  }
2196
+ } else
2197
+ return null;
2198
  }
2199
+ } while ($e);
2200
 
2201
+ if ($nodes) {
2202
+ return $this->NewObj1('Less_Tree_Expression',$nodes);
2203
  }
2204
  }
2205
 
2208
 
2209
  do{
2210
  $e = $this->parseMediaFeature();
2211
+ if( $e ){
2212
  $features[] = $e;
2213
+ if (!$this->MatchChar(',')) break;
2214
+ }else{
2215
  $e = $this->parseEntitiesVariable();
2216
+ if( $e ){
2217
  $features[] = $e;
2218
+ if (!$this->MatchChar(',')) break;
2219
  }
2220
  }
2221
+ } while ($e);
2222
 
2223
  return $features ? $features : null;
2224
  }
2225
 
2226
  private function parseMedia() {
2227
+ if( $this->MatchReg('/\\G@media/') ){
2228
  $features = $this->parseMediaFeatures();
2229
  $rules = $this->parseBlock();
2230
 
2231
+ if( is_array($rules) ){
2232
+ return $this->NewObj4('Less_Tree_Media',array( $rules, $features, $this->pos, $this->env->currentFileInfo));
2233
  }
2234
  }
2235
  }
2236
 
2237
+
2238
  //
2239
  // A CSS Directive
2240
  //
2241
  // @charset "utf-8";
2242
  //
2243
+ private function parseDirective(){
2244
+
2245
+ if( !$this->PeekChar('@') ){
2246
  return;
2247
  }
2248
 
2253
  $hasExpression = false;
2254
  $hasUnknown = false;
2255
 
2256
+
2257
+ $value = $this->MatchFuncs(array('parseImport','parseMedia'));
2258
+ if( $value ){
2259
  return $value;
2260
  }
2261
 
2262
  $this->save();
2263
 
2264
+ $name = $this->MatchReg('/\\G@[a-z-]+/');
2265
 
2266
+ if( !$name ) return;
2267
  $name = $name[0];
2268
 
2269
+
2270
  $nonVendorSpecificName = $name;
2271
+ $pos = strpos($name,'-', 2);
2272
+ if( $name[1] == '-' && $pos > 0 ){
2273
+ $nonVendorSpecificName = "@" . substr($name, $pos + 1);
2274
  }
2275
 
2276
+
2277
+ switch( $nonVendorSpecificName ){
2278
  /*
2279
  case "@font-face":
2280
  case "@viewport":
2316
  break;
2317
  }
2318
 
2319
+ if( $hasIdentifier ){
2320
  $value = $this->parseEntity();
2321
+ if( !$value ){
2322
+ $this->error("expected " . $name . " identifier");
2323
  }
2324
+ } else if( $hasExpression ){
2325
  $value = $this->parseExpression();
2326
+ if( !$value ){
2327
+ $this->error("expected " . $name. " expression");
2328
  }
2329
+ } else if ($hasUnknown) {
2330
 
2331
+ $value = $this->MatchReg('/\\G[^{;]+/');
2332
+ if( $value ){
2333
+ $value = $this->NewObj1('Less_Tree_Anonymous',trim($value[0]));
2334
  }
2335
  }
2336
 
2337
+ if( $hasBlock ){
2338
  $rules = $this->parseBlockRuleset();
2339
  }
2340
 
2341
+ if( $rules || (!$hasBlock && $value && $this->MatchChar(';'))) {
2342
  $this->forget();
2343
+ return $this->NewObj5('Less_Tree_Directive',array($name, $value, $rules, $index, $this->env->currentFileInfo));
2344
  }
2345
 
2346
  $this->restore();
2347
  }
2348
 
2349
+
2350
  //
2351
  // A Value is a comma-delimited list of Expressions
2352
  //
2355
  // In a Rule, a Value represents everything after the `:`,
2356
  // and before the `;`.
2357
  //
2358
+ private function parseValue(){
2359
  $expressions = array();
2360
 
2361
  do{
2362
  $e = $this->parseExpression();
2363
+ if( $e ){
2364
  $expressions[] = $e;
2365
+ if (! $this->MatchChar(',')) {
2366
  break;
2367
  }
2368
  }
2369
+ }while($e);
2370
 
2371
+ if( $expressions ){
2372
+ return $this->NewObj1('Less_Tree_Value',$expressions);
2373
  }
2374
  }
2375
 
2376
+ private function parseImportant (){
2377
+ if( $this->PeekChar('!') && $this->MatchReg('/\\G! *important/') ){
2378
  return ' !important';
2379
  }
2380
  }
2381
 
2382
+ private function parseSub (){
2383
+
2384
+ if( $this->MatchChar('(') ){
2385
  $a = $this->parseAddition();
2386
+ if( $a ){
2387
+ $this->expectChar(')');
2388
+ return $this->NewObj2('Less_Tree_Expression',array( array($a), true) ); //instead of $e->parens = true so the value is cached
2389
  }
2390
  }
2391
  }
2392
 
2393
+
2394
  /**
2395
  * Parses multiplication operation
2396
  *
2397
  * @return Less_Tree_Operation|null
2398
  */
2399
+ function parseMultiplication(){
2400
+
2401
  $return = $m = $this->parseOperand();
2402
+ if( $return ){
2403
+ while( true ){
2404
 
2405
  $isSpaced = $this->isWhitespace( -1 );
2406
 
2407
+ if( $this->PeekReg('/\\G\/[*\/]/') ){
2408
  break;
2409
  }
2410
 
2411
+ $op = $this->MatchChar('/');
2412
+ if( !$op ){
2413
+ $op = $this->MatchChar('*');
2414
+ if( !$op ){
2415
  break;
2416
  }
2417
  }
2418
 
2419
  $a = $this->parseOperand();
2420
 
2421
+ if(!$a) { break; }
 
2422
 
2423
  $m->parensInOp = true;
2424
  $a->parensInOp = true;
2425
+ $return = $this->NewObj3('Less_Tree_Operation',array( $op, array( $return, $a ), $isSpaced) );
2426
  }
2427
  }
2428
  return $return;
2429
 
2430
  }
2431
 
2432
+
2433
  /**
2434
  * Parses an addition operation
2435
  *
2436
  * @return Less_Tree_Operation|null
2437
  */
2438
+ private function parseAddition (){
2439
+
2440
  $return = $m = $this->parseMultiplication();
2441
+ if( $return ){
2442
+ while( true ){
2443
 
2444
  $isSpaced = $this->isWhitespace( -1 );
2445
 
2446
+ $op = $this->MatchReg('/\\G[-+]\s+/');
2447
+ if( $op ){
2448
  $op = $op[0];
2449
+ }else{
2450
+ if( !$isSpaced ){
2451
+ $op = $this->match(array('#+','#-'));
2452
  }
2453
+ if( !$op ){
2454
  break;
2455
  }
2456
  }
2457
 
2458
  $a = $this->parseMultiplication();
2459
+ if( !$a ){
2460
  break;
2461
  }
2462
 
2463
  $m->parensInOp = true;
2464
  $a->parensInOp = true;
2465
+ $return = $this->NewObj3('Less_Tree_Operation',array($op, array($return, $a), $isSpaced));
2466
  }
2467
  }
2468
 
2469
  return $return;
2470
  }
2471
 
2472
+
2473
  /**
2474
  * Parses the conditions
2475
  *
2478
  private function parseConditions() {
2479
  $index = $this->pos;
2480
  $return = $a = $this->parseCondition();
2481
+ if( $a ){
2482
+ while( true ){
2483
+ if( !$this->PeekReg('/\\G,\s*(not\s*)?\(/') || !$this->MatchChar(',') ){
2484
  break;
2485
  }
2486
  $b = $this->parseCondition();
2487
+ if( !$b ){
2488
  break;
2489
  }
2490
 
2491
+ $return = $this->NewObj4('Less_Tree_Condition',array('or', $return, $b, $index));
2492
  }
2493
  return $return;
2494
  }
2499
  $negate = false;
2500
  $c = null;
2501
 
2502
+ if ($this->MatchReg('/\\Gnot/')) $negate = true;
2503
+ $this->expectChar('(');
2504
+ $a = $this->MatchFuncs(array('parseAddition','parseEntitiesKeyword','parseEntitiesQuoted'));
2505
 
2506
+ if( $a ){
2507
+ $op = $this->MatchReg('/\\G(?:>=|<=|=<|[<=>])/');
2508
+ if( $op ){
2509
+ $b = $this->MatchFuncs(array('parseAddition','parseEntitiesKeyword','parseEntitiesQuoted'));
2510
+ if( $b ){
2511
+ $c = $this->NewObj5('Less_Tree_Condition',array($op[0], $a, $b, $index, $negate));
2512
  } else {
2513
+ $this->Error('Unexpected expression');
2514
  }
2515
  } else {
2516
+ $k = $this->NewObj1('Less_Tree_Keyword','true');
2517
+ $c = $this->NewObj5('Less_Tree_Condition',array('=', $a, $k, $index, $negate));
2518
  }
2519
+ $this->expectChar(')');
2520
+ return $this->MatchReg('/\\Gand/') ? $this->NewObj3('Less_Tree_Condition',array('and', $c, $this->parseCondition())) : $c;
2521
  }
2522
  }
2523
 
2526
  * such as a Color, or a Variable
2527
  *
2528
  */
2529
+ private function parseOperand (){
2530
+
2531
  $negate = false;
2532
+ $offset = $this->pos+1;
2533
+ if( $offset >= $this->input_len ){
2534
  return;
2535
  }
2536
  $char = $this->input[$offset];
2537
+ if( $char === '@' || $char === '(' ){
2538
+ $negate = $this->MatchChar('-');
2539
  }
2540
 
2541
+ $o = $this->MatchFuncs(array('parseSub','parseEntitiesDimension','parseEntitiesColor','parseEntitiesVariable','parseEntitiesCall'));
2542
 
2543
+ if( $negate ){
2544
  $o->parensInOp = true;
2545
+ $o = $this->NewObj1('Less_Tree_Negative',$o);
2546
  }
2547
 
2548
  return $o;
2549
  }
2550
 
2551
+
2552
  /**
2553
  * Expressions either represent mathematical operations,
2554
  * or white-space delimited Entities.
2555
  *
2556
  * 1px solid black
2557
+ * @var * 2
2558
  *
2559
  * @return Less_Tree_Expression|null
2560
  */
2561
+ private function parseExpression (){
2562
  $entities = array();
2563
 
2564
  do{
2565
+ $e = $this->MatchFuncs(array('parseAddition','parseEntity'));
2566
+ if( $e ){
2567
  $entities[] = $e;
2568
  // operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here
2569
+ if( !$this->PeekReg('/\\G\/[\/*]/') ){
2570
+ $delim = $this->MatchChar('/');
2571
+ if( $delim ){
2572
+ $entities[] = $this->NewObj1('Less_Tree_Anonymous',$delim);
2573
  }
2574
  }
2575
  }
2576
+ }while($e);
2577
 
2578
+ if( $entities ){
2579
+ return $this->NewObj1('Less_Tree_Expression',$entities);
2580
  }
2581
  }
2582
 
2583
+
2584
  /**
2585
  * Parse a property
2586
  * eg: 'min-width', 'orientation', etc
2587
  *
2588
  * @return string
2589
  */
2590
+ private function parseProperty (){
2591
+ $name = $this->MatchReg('/\\G(\*?-?[_a-zA-Z0-9-]+)\s*:/');
2592
+ if( $name ){
2593
  return $name[1];
2594
  }
2595
  }
2596
 
2597
+
2598
  /**
2599
  * Parse a rule property
2600
  * eg: 'color', 'width', 'height', etc
2601
  *
2602
  * @return string
2603
  */
2604
+ private function parseRuleProperty(){
2605
  $offset = $this->pos;
2606
  $name = array();
2607
  $index = array();
2608
  $length = 0;
2609
 
 
 
2610
 
2611
+ $this->rulePropertyMatch('/\\G(\*?)/', $offset, $length, $index, $name );
2612
+ while( $this->rulePropertyMatch('/\\G((?:[\w-]+)|(?:@\{[\w-]+\}))/', $offset, $length, $index, $name )); // !
2613
+
2614
+ if( (count($name) > 1) && $this->rulePropertyMatch('/\\G\s*((?:\+_|\+)?)\s*:/', $offset, $length, $index, $name) ){
2615
  // at last, we have the complete match now. move forward,
2616
  // convert name particles to tree objects and return:
2617
+ $this->skipWhitespace($length);
2618
 
2619
+ if( $name[0] === '' ){
2620
+ array_shift($name);
2621
+ array_shift($index);
2622
  }
2623
+ foreach($name as $k => $s ){
2624
+ if( !$s || $s[0] !== '@' ){
2625
+ $name[$k] = $this->NewObj1('Less_Tree_Keyword',$s);
2626
+ }else{
2627
+ $name[$k] = $this->NewObj3('Less_Tree_Variable',array('@' . substr($s,2,-1), $index[$k], $this->env->currentFileInfo));
2628
  }
2629
  }
2630
  return $name;
2631
  }
2632
 
2633
+
2634
  }
2635
 
2636
+ private function rulePropertyMatch( $re, &$offset, &$length, &$index, &$name ){
2637
+ preg_match($re, $this->input, $a, 0, $offset);
2638
+ if( $a ){
2639
  $index[] = $this->pos + $length;
2640
+ $length += strlen($a[0]);
2641
+ $offset += strlen($a[0]);
2642
  $name[] = $a[1];
2643
  return true;
2644
  }
2645
  }
2646
 
2647
+ public static function serializeVars( $vars ){
2648
  $s = '';
2649
 
2650
+ foreach($vars as $name => $value){
2651
+ $s .= (($name[0] === '@') ? '' : '@') . $name .': '. $value . ((substr($value,-1) === ';') ? '' : ';');
2652
  }
2653
 
2654
  return $s;
2655
  }
2656
 
2657
+
2658
  /**
2659
  * Some versions of php have trouble with method_exists($a,$b) if $a is not an object
2660
  *
2661
  * @param string $b
2662
  */
2663
+ public static function is_method($a,$b){
2664
+ return is_object($a) && method_exists($a,$b);
2665
  }
2666
 
2667
+
2668
  /**
2669
  * Round numbers similarly to javascript
2670
  * eg: 1.499999 to 1 instead of 2
2671
  *
2672
  */
2673
+ public static function round($i, $precision = 0){
2674
+
2675
+ $precision = pow(10,$precision);
2676
+ $i = $i*$precision;
2677
+
2678
+ $ceil = ceil($i);
2679
+ $floor = floor($i);
2680
+ if( ($ceil - $i) <= ($i - $floor) ){
2681
+ return $ceil/$precision;
2682
+ }else{
2683
+ return $floor/$precision;
2684
  }
2685
  }
2686
 
2687
+
2688
  /**
2689
  * Create Less_Tree_* objects and optionally generate a cache string
2690
  *
2691
  * @return mixed
2692
  */
2693
+ public function NewObj0($class){
2694
  $obj = new $class();
2695
+ if( $this->CacheEnabled() ){
2696
  $obj->cache_string = ' new '.$class.'()';
2697
  }
2698
  return $obj;
2699
  }
2700
 
2701
+ public function NewObj1($class, $arg){
2702
  $obj = new $class( $arg );
2703
+ if( $this->CacheEnabled() ){
2704
+ $obj->cache_string = ' new '.$class.'('.Less_Parser::ArgString($arg).')';
2705
  }
2706
  return $obj;
2707
  }
2708
 
2709
+ public function NewObj2($class, $args){
2710
  $obj = new $class( $args[0], $args[1] );
2711
+ if( $this->CacheEnabled() ){
2712
+ $this->ObjCache( $obj, $class, $args);
2713
  }
2714
  return $obj;
2715
  }
2716
 
2717
+ public function NewObj3($class, $args){
2718
  $obj = new $class( $args[0], $args[1], $args[2] );
2719
+ if( $this->CacheEnabled() ){
2720
+ $this->ObjCache( $obj, $class, $args);
2721
  }
2722
  return $obj;
2723
  }
2724
 
2725
+ public function NewObj4($class, $args){
2726
  $obj = new $class( $args[0], $args[1], $args[2], $args[3] );
2727
+ if( $this->CacheEnabled() ){
2728
+ $this->ObjCache( $obj, $class, $args);
2729
  }
2730
  return $obj;
2731
  }
2732
 
2733
+ public function NewObj5($class, $args){
2734
  $obj = new $class( $args[0], $args[1], $args[2], $args[3], $args[4] );
2735
+ if( $this->CacheEnabled() ){
2736
+ $this->ObjCache( $obj, $class, $args);
2737
  }
2738
  return $obj;
2739
  }
2740
 
2741
+ public function NewObj6($class, $args){
2742
  $obj = new $class( $args[0], $args[1], $args[2], $args[3], $args[4], $args[5] );
2743
+ if( $this->CacheEnabled() ){
2744
+ $this->ObjCache( $obj, $class, $args);
2745
  }
2746
  return $obj;
2747
  }
2748
 
2749
+ public function NewObj7($class, $args){
2750
  $obj = new $class( $args[0], $args[1], $args[2], $args[3], $args[4], $args[5], $args[6] );
2751
+ if( $this->CacheEnabled() ){
2752
+ $this->ObjCache( $obj, $class, $args);
2753
  }
2754
  return $obj;
2755
  }
2756
 
2757
+ //caching
2758
+ public function ObjCache($obj, $class, $args=array()){
2759
+ $obj->cache_string = ' new '.$class.'('. self::ArgCache($args).')';
2760
  }
2761
 
2762
+ public function ArgCache($args){
2763
+ return implode(',',array_map( array('Less_Parser','ArgString'),$args));
2764
  }
2765
 
2766
+
2767
  /**
2768
  * Convert an argument to a string for use in the parser cache
2769
  *
2770
  * @return string
2771
  */
2772
+ public static function ArgString($arg){
2773
+
2774
+ $type = gettype($arg);
2775
 
2776
+ if( $type === 'object'){
2777
  $string = $arg->cache_string;
2778
+ unset($arg->cache_string);
2779
  return $string;
2780
 
2781
+ }elseif( $type === 'array' ){
2782
  $string = ' Array(';
2783
+ foreach($arg as $k => $a){
2784
+ $string .= var_export($k,true).' => '.self::ArgString($a).',';
2785
  }
2786
  return $string . ')';
2787
  }
2788
 
2789
+ return var_export($arg,true);
2790
  }
2791
 
2792
+ public function Error($msg){
2793
+ throw new Less_Exception_Parser($msg, null, $this->furthest, $this->env->currentFileInfo);
2794
  }
2795
 
2796
+ public static function WinPath($path){
2797
+ return str_replace('\\', '/', $path);
2798
  }
2799
 
2800
+ public static function AbsPath($path, $winPath = false){
2801
+ if (strpos($path, '//') !== false && preg_match('_^(https?:)?//\\w+(\\.\\w+)+/\\w+_i', $path)) {
2802
  return $winPath ? '' : false;
2803
  } else {
2804
+ $path = realpath($path);
2805
+ if ($winPath) {
2806
+ $path = self::WinPath($path);
2807
  }
2808
  return $path;
2809
  }
2810
  }
2811
 
2812
+ public function CacheEnabled(){
2813
+ return (Less_Parser::$options['cache_method'] && (Less_Cache::$cache_dir || (Less_Parser::$options['cache_method'] == 'callback')));
2814
  }
2815
 
2816
  }
base/inc/lib/Less/SourceMap/Base64VLQ.php CHANGED
@@ -67,7 +67,7 @@ class Less_SourceMap_Base64VLQ {
67
  /**
68
  * Constructor
69
  */
70
- public function __construct() {
71
  // I leave it here for future reference
72
  // foreach(str_split('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/') as $i => $char)
73
  // {
@@ -85,8 +85,8 @@ class Less_SourceMap_Base64VLQ {
85
  * even on a 64 bit machine.
86
  * @param string $aValue
87
  */
88
- public function toVLQSigned( $aValue ) {
89
- return 0xffffffff & ( $aValue < 0 ? ( ( -$aValue ) << 1 ) + 1 : ( $aValue << 1 ) + 0 );
90
  }
91
 
92
  /**
@@ -100,8 +100,8 @@ class Less_SourceMap_Base64VLQ {
100
  * even on a 64 bit machine.
101
  * @param integer $aValue
102
  */
103
- public function fromVLQSigned( $aValue ) {
104
- return $aValue & 1 ? $this->zeroFill( ~$aValue + 2, 1 ) | ( -1 - 0x7fffffff ) : $this->zeroFill( $aValue, 1 );
105
  }
106
 
107
  /**
@@ -110,18 +110,18 @@ class Less_SourceMap_Base64VLQ {
110
  * @param string $aValue The value to encode
111
  * @return string The encoded value
112
  */
113
- public function encode( $aValue ) {
114
  $encoded = '';
115
- $vlq = $this->toVLQSigned( $aValue );
116
  do
117
  {
118
  $digit = $vlq & $this->mask;
119
- $vlq = $this->zeroFill( $vlq, $this->shift );
120
- if ( $vlq > 0 ) {
121
  $digit |= $this->continuationBit;
122
  }
123
- $encoded .= $this->base64Encode( $digit );
124
- } while ( $vlq > 0 );
125
 
126
  return $encoded;
127
  }
@@ -132,17 +132,17 @@ class Less_SourceMap_Base64VLQ {
132
  * @param string $encoded The encoded value to decode
133
  * @return integer The decoded value
134
  */
135
- public function decode( $encoded ) {
136
  $vlq = 0;
137
  $i = 0;
138
  do
139
  {
140
- $digit = $this->base64Decode( $encoded[$i] );
141
- $vlq |= ( $digit & $this->mask ) << ( $i * $this->shift );
142
  $i++;
143
- } while ( $digit & $this->continuationBit );
144
 
145
- return $this->fromVLQSigned( $vlq );
146
  }
147
 
148
  /**
@@ -152,8 +152,8 @@ class Less_SourceMap_Base64VLQ {
152
  * @param integer $b number of bits to shift
153
  * @return integer
154
  */
155
- public function zeroFill( $a, $b ) {
156
- return ( $a >= 0 ) ? ( $a >> $b ) : ( $a >> $b ) & ( PHP_INT_MAX >> ( $b - 1 ) );
157
  }
158
 
159
  /**
@@ -163,9 +163,9 @@ class Less_SourceMap_Base64VLQ {
163
  * @return string
164
  * @throws Exception If the number is invalid
165
  */
166
- public function base64Encode( $number ) {
167
- if ( $number < 0 || $number > 63 ) {
168
- throw new Exception( sprintf( 'Invalid number "%s" given. Must be between 0 and 63.', $number ) );
169
  }
170
  return $this->intToCharMap[$number];
171
  }
@@ -177,9 +177,9 @@ class Less_SourceMap_Base64VLQ {
177
  * @return number
178
  * @throws Exception If the number is invalid
179
  */
180
- public function base64Decode( $char ) {
181
- if ( !array_key_exists( $char, $this->charToIntMap ) ) {
182
- throw new Exception( sprintf( 'Invalid base 64 digit "%s" given.', $char ) );
183
  }
184
  return $this->charToIntMap[$char];
185
  }
67
  /**
68
  * Constructor
69
  */
70
+ public function __construct(){
71
  // I leave it here for future reference
72
  // foreach(str_split('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/') as $i => $char)
73
  // {
85
  * even on a 64 bit machine.
86
  * @param string $aValue
87
  */
88
+ public function toVLQSigned($aValue){
89
+ return 0xffffffff & ($aValue < 0 ? ((-$aValue) << 1) + 1 : ($aValue << 1) + 0);
90
  }
91
 
92
  /**
100
  * even on a 64 bit machine.
101
  * @param integer $aValue
102
  */
103
+ public function fromVLQSigned($aValue){
104
+ return $aValue & 1 ? $this->zeroFill(~$aValue + 2, 1) | (-1 - 0x7fffffff) : $this->zeroFill($aValue, 1);
105
  }
106
 
107
  /**
110
  * @param string $aValue The value to encode
111
  * @return string The encoded value
112
  */
113
+ public function encode($aValue){
114
  $encoded = '';
115
+ $vlq = $this->toVLQSigned($aValue);
116
  do
117
  {
118
  $digit = $vlq & $this->mask;
119
+ $vlq = $this->zeroFill($vlq, $this->shift);
120
+ if($vlq > 0){
121
  $digit |= $this->continuationBit;
122
  }
123
+ $encoded .= $this->base64Encode($digit);
124
+ } while($vlq > 0);
125
 
126
  return $encoded;
127
  }
132
  * @param string $encoded The encoded value to decode
133
  * @return integer The decoded value
134
  */
135
+ public function decode($encoded){
136
  $vlq = 0;
137
  $i = 0;
138
  do
139
  {
140
+ $digit = $this->base64Decode($encoded[$i]);
141
+ $vlq |= ($digit & $this->mask) << ($i * $this->shift);
142
  $i++;
143
+ } while($digit & $this->continuationBit);
144
 
145
+ return $this->fromVLQSigned($vlq);
146
  }
147
 
148
  /**
152
  * @param integer $b number of bits to shift
153
  * @return integer
154
  */
155
+ public function zeroFill($a, $b){
156
+ return ($a >= 0) ? ($a >> $b) : ($a >> $b) & (PHP_INT_MAX >> ($b - 1));
157
  }
158
 
159
  /**
163
  * @return string
164
  * @throws Exception If the number is invalid
165
  */
166
+ public function base64Encode($number){
167
+ if($number < 0 || $number > 63){
168
+ throw new Exception(sprintf('Invalid number "%s" given. Must be between 0 and 63.', $number));
169
  }
170
  return $this->intToCharMap[$number];
171
  }
177
  * @return number
178
  * @throws Exception If the number is invalid
179
  */
180
+ public function base64Decode($char){
181
+ if(!array_key_exists($char, $this->charToIntMap)){
182
+ throw new Exception(sprintf('Invalid base 64 digit "%s" given.', $char));
183
  }
184
  return $this->charToIntMap[$char];
185
  }
base/inc/lib/Less/SourceMap/Generator.php CHANGED
@@ -1,354 +1,365 @@
1
- <?php
2
-
3
- /**
4
- * Source map generator
5
- *
6
- * @package Less
7
- * @subpackage Output
8
- */
9
- class Less_SourceMap_Generator extends Less_Configurable {
10
-
11
- /**
12
- * What version of source map does the generator generate?
13
- */
14
- private const VERSION = 3;
15
-
16
- /**
17
- * Array of default options
18
- *
19
- * @var array
20
- */
21
- protected $defaultOptions = array(
22
- // an optional source root, useful for relocating source files
23
- // on a server or removing repeated values in the 'sources' entry.
24
- // This value is prepended to the individual entries in the 'source' field.
25
- 'sourceRoot' => '',
26
-
27
- // an optional name of the generated code that this source map is associated with.
28
- 'sourceMapFilename' => null,
29
-
30
- // url of the map
31
- 'sourceMapURL' => null,
32
-
33
- // absolute path to a file to write the map to
34
- 'sourceMapWriteTo' => null,
35
-
36
- // output source contents?
37
- 'outputSourceFiles' => false,
38
-
39
- // base path for filename normalization
40
- 'sourceMapRootpath' => '',
41
-
42
- // base path for filename normalization
43
- 'sourceMapBasepath' => ''
44
- );
45
-
46
- /**
47
- * The base64 VLQ encoder
48
- *
49
- * @var Less_SourceMap_Base64VLQ
50
- */
51
- protected $encoder;
52
-
53
- /**
54
- * Array of mappings
55
- *
56
- * @var array
57
- */
58
- protected $mappings = array();
59
-
60
- /**
61
- * The root node
62
- *
63
- * @var Less_Tree_Ruleset
64
- */
65
- protected $root;
66
-
67
- /**
68
- * Array of contents map
69
- *
70
- * @var array
71
- */
72
- protected $contentsMap = array();
73
-
74
- /**
75
- * File to content map
76
- *
77
- * @var array
78
- */
79
- protected $sources = array();
80
- protected $source_keys = array();
81
-
82
- /**
83
- * Constructor
84
- *
85
- * @param Less_Tree_Ruleset $root The root node
86
- * @param array $options Array of options
87
- */
88
- public function __construct( Less_Tree_Ruleset $root, $contentsMap, $options = array() ) {
89
- $this->root = $root;
90
- $this->contentsMap = $contentsMap;
91
- $this->encoder = new Less_SourceMap_Base64VLQ();
92
-
93
- $this->SetOptions( $options );
94
-
95
- $this->options['sourceMapRootpath'] = $this->fixWindowsPath( $this->options['sourceMapRootpath'], true );
96
- $this->options['sourceMapBasepath'] = $this->fixWindowsPath( $this->options['sourceMapBasepath'], true );
97
- }
98
-
99
- /**
100
- * Generates the CSS
101
- *
102
- * @return string
103
- */
104
- public function generateCSS() {
105
- $output = new Less_Output_Mapped( $this->contentsMap, $this );
106
-
107
- // catch the output
108
- $this->root->genCSS( $output );
109
-
110
- $sourceMapUrl = $this->getOption( 'sourceMapURL' );
111
- $sourceMapFilename = $this->getOption( 'sourceMapFilename' );
112
- $sourceMapContent = $this->generateJson();
113
- $sourceMapWriteTo = $this->getOption( 'sourceMapWriteTo' );
114
-
115
- if ( !$sourceMapUrl && $sourceMapFilename ) {
116
- $sourceMapUrl = $this->normalizeFilename( $sourceMapFilename );
117
- }
118
-
119
- // write map to a file
120
- if ( $sourceMapWriteTo ) {
121
- $this->saveMap( $sourceMapWriteTo, $sourceMapContent );
122
- }
123
-
124
- // inline the map
125
- if ( !$sourceMapUrl ) {
126
- $sourceMapUrl = sprintf( 'data:application/json,%s', Less_Functions::encodeURIComponent( $sourceMapContent ) );
127
- }
128
-
129
- if ( $sourceMapUrl ) {
130
- $output->add( sprintf( '/*# sourceMappingURL=%s */', $sourceMapUrl ) );
131
- }
132
-
133
- return $output->toString();
134
- }
135
-
136
- /**
137
- * Saves the source map to a file
138
- *
139
- * @param string $file The absolute path to a file
140
- * @param string $content The content to write
141
- * @throws Exception If the file could not be saved
142
- */
143
- protected function saveMap( $file, $content ) {
144
- $dir = dirname( $file );
145
- // directory does not exist
146
- if ( !is_dir( $dir ) ) {
147
- // FIXME: create the dir automatically?
148
- throw new Exception( sprintf( 'The directory "%s" does not exist. Cannot save the source map.', $dir ) );
149
- }
150
- // FIXME: proper saving, with dir write check!
151
- if ( file_put_contents( $file, $content ) === false ) {
152
- throw new Exception( sprintf( 'Cannot save the source map to "%s"', $file ) );
153
- }
154
- return true;
155
- }
156
-
157
- /**
158
- * Normalizes the filename
159
- *
160
- * @param string $filename
161
- * @return string
162
- */
163
- protected function normalizeFilename( $filename ) {
164
- $filename = $this->fixWindowsPath( $filename );
165
-
166
- $rootpath = $this->getOption( 'sourceMapRootpath' );
167
- $basePath = $this->getOption( 'sourceMapBasepath' );
168
-
169
- // "Trim" the 'sourceMapBasepath' from the output filename.
170
- if ( is_string( $basePath ) && strpos( $filename, $basePath ) === 0 ) {
171
- $filename = substr( $filename, strlen( $basePath ) );
172
- }
173
-
174
- // Remove extra leading path separators.
175
- if ( strpos( $filename, '\\' ) === 0 || strpos( $filename, '/' ) === 0 ) {
176
- $filename = substr( $filename, 1 );
177
- }
178
-
179
- return $rootpath . $filename;
180
- }
181
-
182
- /**
183
- * Adds a mapping
184
- *
185
- * @param integer $generatedLine The line number in generated file
186
- * @param integer $generatedColumn The column number in generated file
187
- * @param integer $originalLine The line number in original file
188
- * @param integer $originalColumn The column number in original file
189
- * @param string $sourceFile The original source file
190
- */
191
- public function addMapping( $generatedLine, $generatedColumn, $originalLine, $originalColumn, $fileInfo ) {
192
- $this->mappings[] = array(
193
- 'generated_line' => $generatedLine,
194
- 'generated_column' => $generatedColumn,
195
- 'original_line' => $originalLine,
196
- 'original_column' => $originalColumn,
197
- 'source_file' => $fileInfo['currentUri']
198
- );
199
-
200
- $this->sources[$fileInfo['currentUri']] = $fileInfo['filename'];
201
- }
202
-
203
- /**
204
- * Generates the JSON source map
205
- *
206
- * @return string
207
- * @see https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#
208
- */
209
- protected function generateJson() {
210
- $sourceMap = array();
211
- $mappings = $this->generateMappings();
212
-
213
- // File version (always the first entry in the object) and must be a positive integer.
214
- $sourceMap['version'] = self::VERSION;
215
-
216
- // An optional name of the generated code that this source map is associated with.
217
- $file = $this->getOption( 'sourceMapFilename' );
218
- if ( $file ) {
219
- $sourceMap['file'] = $file;
220
- }
221
-
222
- // An optional source root, useful for relocating source files on a server or removing repeated values in the 'sources' entry. This value is prepended to the individual entries in the 'source' field.
223
- $root = $this->getOption( 'sourceRoot' );
224
- if ( $root ) {
225
- $sourceMap['sourceRoot'] = $root;
226
- }
227
-
228
- // A list of original sources used by the 'mappings' entry.
229
- $sourceMap['sources'] = array();
230
- foreach ( $this->sources as $source_uri => $source_filename ) {
231
- $sourceMap['sources'][] = $this->normalizeFilename( $source_filename );
232
- }
233
-
234
- // A list of symbol names used by the 'mappings' entry.
235
- $sourceMap['names'] = array();
236
-
237
- // A string with the encoded mapping data.
238
- $sourceMap['mappings'] = $mappings;
239
-
240
- if ( $this->getOption( 'outputSourceFiles' ) ) {
241
- // An optional list of source content, useful when the 'source' can't be hosted.
242
- // The contents are listed in the same order as the sources above.
243
- // 'null' may be used if some original sources should be retrieved by name.
244
- $sourceMap['sourcesContent'] = $this->getSourcesContent();
245
- }
246
-
247
- // less.js compat fixes
248
- if ( count( $sourceMap['sources'] ) && empty( $sourceMap['sourceRoot'] ) ) {
249
- unset( $sourceMap['sourceRoot'] );
250
- }
251
-
252
- return json_encode( $sourceMap );
253
- }
254
-
255
- /**
256
- * Returns the sources contents
257
- *
258
- * @return array|null
259
- */
260
- protected function getSourcesContent() {
261
- if ( empty( $this->sources ) ) {
262
- return;
263
- }
264
- $content = array();
265
- foreach ( $this->sources as $sourceFile ) {
266
- $content[] = file_get_contents( $sourceFile );
267
- }
268
- return $content;
269
- }
270
-
271
- /**
272
- * Generates the mappings string
273
- *
274
- * @return string
275
- */
276
- public function generateMappings() {
277
- if ( !count( $this->mappings ) ) {
278
- return '';
279
- }
280
-
281
- $this->source_keys = array_flip( array_keys( $this->sources ) );
282
-
283
- // group mappings by generated line number.
284
- $groupedMap = $groupedMapEncoded = array();
285
- foreach ( $this->mappings as $m ) {
286
- $groupedMap[$m['generated_line']][] = $m;
287
- }
288
- ksort( $groupedMap );
289
-
290
- $lastGeneratedLine = $lastOriginalIndex = $lastOriginalLine = $lastOriginalColumn = 0;
291
-
292
- foreach ( $groupedMap as $lineNumber => $line_map ) {
293
- while ( ++$lastGeneratedLine < $lineNumber ) {
294
- $groupedMapEncoded[] = ';';
295
- }
296
-
297
- $lineMapEncoded = array();
298
- $lastGeneratedColumn = 0;
299
-
300
- foreach ( $line_map as $m ) {
301
- $mapEncoded = $this->encoder->encode( $m['generated_column'] - $lastGeneratedColumn );
302
- $lastGeneratedColumn = $m['generated_column'];
303
-
304
- // find the index
305
- if ( $m['source_file'] ) {
306
- $index = $this->findFileIndex( $m['source_file'] );
307
- if ( $index !== false ) {
308
- $mapEncoded .= $this->encoder->encode( $index - $lastOriginalIndex );
309
- $lastOriginalIndex = $index;
310
-
311
- // lines are stored 0-based in SourceMap spec version 3
312
- $mapEncoded .= $this->encoder->encode( $m['original_line'] - 1 - $lastOriginalLine );
313
- $lastOriginalLine = $m['original_line'] - 1;
314
-
315
- $mapEncoded .= $this->encoder->encode( $m['original_column'] - $lastOriginalColumn );
316
- $lastOriginalColumn = $m['original_column'];
317
- }
318
- }
319
-
320
- $lineMapEncoded[] = $mapEncoded;
321
- }
322
-
323
- $groupedMapEncoded[] = implode( ',', $lineMapEncoded ) . ';';
324
- }
325
-
326
- return rtrim( implode( $groupedMapEncoded ), ';' );
327
- }
328
-
329
- /**
330
- * Finds the index for the filename
331
- *
332
- * @param string $filename
333
- * @return integer|false
334
- */
335
- protected function findFileIndex( $filename ) {
336
- return $this->source_keys[$filename];
337
- }
338
-
339
- /**
340
- * fix windows paths
341
- * @param string $path
342
- * @return string
343
- */
344
- public function fixWindowsPath( $path, $addEndSlash = false ) {
345
- $slash = ( $addEndSlash ) ? '/' : '';
346
- if ( !empty( $path ) ) {
347
- $path = str_replace( '\\', '/', $path );
348
- $path = rtrim( $path, '/' ) . $slash;
349
- }
350
-
351
- return $path;
352
- }
353
-
354
- }
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Source map generator
5
+ *
6
+ * @package Less
7
+ * @subpackage Output
8
+ */
9
+ class Less_SourceMap_Generator extends Less_Configurable {
10
+
11
+ /**
12
+ * What version of source map does the generator generate?
13
+ */
14
+ const VERSION = 3;
15
+
16
+ /**
17
+ * Array of default options
18
+ *
19
+ * @var array
20
+ */
21
+ protected $defaultOptions = array(
22
+ // an optional source root, useful for relocating source files
23
+ // on a server or removing repeated values in the 'sources' entry.
24
+ // This value is prepended to the individual entries in the 'source' field.
25
+ 'sourceRoot' => '',
26
+
27
+ // an optional name of the generated code that this source map is associated with.
28
+ 'sourceMapFilename' => null,
29
+
30
+ // url of the map
31
+ 'sourceMapURL' => null,
32
+
33
+ // absolute path to a file to write the map to
34
+ 'sourceMapWriteTo' => null,
35
+
36
+ // output source contents?
37
+ 'outputSourceFiles' => false,
38
+
39
+ // base path for filename normalization
40
+ 'sourceMapRootpath' => '',
41
+
42
+ // base path for filename normalization
43
+ 'sourceMapBasepath' => ''
44
+ );
45
+
46
+ /**
47
+ * The base64 VLQ encoder
48
+ *
49
+ * @var Less_SourceMap_Base64VLQ
50
+ */
51
+ protected $encoder;
52
+
53
+ /**
54
+ * Array of mappings
55
+ *
56
+ * @var array
57
+ */
58
+ protected $mappings = array();
59
+
60
+ /**
61
+ * The root node
62
+ *
63
+ * @var Less_Tree_Ruleset
64
+ */
65
+ protected $root;
66
+
67
+ /**
68
+ * Array of contents map
69
+ *
70
+ * @var array
71
+ */
72
+ protected $contentsMap = array();
73
+
74
+ /**
75
+ * File to content map
76
+ *
77
+ * @var array
78
+ */
79
+ protected $sources = array();
80
+ protected $source_keys = array();
81
+
82
+ /**
83
+ * Constructor
84
+ *
85
+ * @param Less_Tree_Ruleset $root The root node
86
+ * @param array $options Array of options
87
+ */
88
+ public function __construct(Less_Tree_Ruleset $root, $contentsMap, $options = array()){
89
+ $this->root = $root;
90
+ $this->contentsMap = $contentsMap;
91
+ $this->encoder = new Less_SourceMap_Base64VLQ();
92
+
93
+ $this->SetOptions($options);
94
+
95
+ $this->options['sourceMapRootpath'] = $this->fixWindowsPath($this->options['sourceMapRootpath'], true);
96
+ $this->options['sourceMapBasepath'] = $this->fixWindowsPath($this->options['sourceMapBasepath'], true);
97
+ }
98
+
99
+ /**
100
+ * Generates the CSS
101
+ *
102
+ * @return string
103
+ */
104
+ public function generateCSS(){
105
+ $output = new Less_Output_Mapped($this->contentsMap, $this);
106
+
107
+ // catch the output
108
+ $this->root->genCSS($output);
109
+
110
+
111
+ $sourceMapUrl = $this->getOption('sourceMapURL');
112
+ $sourceMapFilename = $this->getOption('sourceMapFilename');
113
+ $sourceMapContent = $this->generateJson();
114
+ $sourceMapWriteTo = $this->getOption('sourceMapWriteTo');
115
+
116
+ if( !$sourceMapUrl && $sourceMapFilename ){
117
+ $sourceMapUrl = $this->normalizeFilename($sourceMapFilename);
118
+ }
119
+
120
+ // write map to a file
121
+ if( $sourceMapWriteTo ){
122
+ $this->saveMap($sourceMapWriteTo, $sourceMapContent);
123
+ }
124
+
125
+ // inline the map
126
+ if( !$sourceMapUrl ){
127
+ $sourceMapUrl = sprintf('data:application/json,%s', Less_Functions::encodeURIComponent($sourceMapContent));
128
+ }
129
+
130
+ if( $sourceMapUrl ){
131
+ $output->add( sprintf('/*# sourceMappingURL=%s */', $sourceMapUrl) );
132
+ }
133
+
134
+ return $output->toString();
135
+ }
136
+
137
+ /**
138
+ * Saves the source map to a file
139
+ *
140
+ * @param string $file The absolute path to a file
141
+ * @param string $content The content to write
142
+ * @throws Exception If the file could not be saved
143
+ */
144
+ protected function saveMap($file, $content){
145
+ $dir = dirname($file);
146
+ // directory does not exist
147
+ if( !is_dir($dir) ){
148
+ // FIXME: create the dir automatically?
149
+ throw new Exception(sprintf('The directory "%s" does not exist. Cannot save the source map.', $dir));
150
+ }
151
+ // FIXME: proper saving, with dir write check!
152
+ if(file_put_contents($file, $content) === false){
153
+ throw new Exception(sprintf('Cannot save the source map to "%s"', $file));
154
+ }
155
+ return true;
156
+ }
157
+
158
+ /**
159
+ * Normalizes the filename
160
+ *
161
+ * @param string $filename
162
+ * @return string
163
+ */
164
+ protected function normalizeFilename($filename){
165
+
166
+ $filename = $this->fixWindowsPath($filename);
167
+
168
+ $rootpath = $this->getOption('sourceMapRootpath');
169
+ $basePath = $this->getOption('sourceMapBasepath');
170
+
171
+ // "Trim" the 'sourceMapBasepath' from the output filename.
172
+ if (is_string($basePath) && strpos($filename, $basePath) === 0) {
173
+ $filename = substr($filename, strlen($basePath));
174
+ }
175
+
176
+ // Remove extra leading path separators.
177
+ if(strpos($filename, '\\') === 0 || strpos($filename, '/') === 0){
178
+ $filename = substr($filename, 1);
179
+ }
180
+
181
+ return $rootpath . $filename;
182
+ }
183
+
184
+ /**
185
+ * Adds a mapping
186
+ *
187
+ * @param integer $generatedLine The line number in generated file
188
+ * @param integer $generatedColumn The column number in generated file
189
+ * @param integer $originalLine The line number in original file
190
+ * @param integer $originalColumn The column number in original file
191
+ * @param string $sourceFile The original source file
192
+ */
193
+ public function addMapping($generatedLine, $generatedColumn, $originalLine, $originalColumn, $fileInfo ){
194
+
195
+ $this->mappings[] = array(
196
+ 'generated_line' => $generatedLine,
197
+ 'generated_column' => $generatedColumn,
198
+ 'original_line' => $originalLine,
199
+ 'original_column' => $originalColumn,
200
+ 'source_file' => $fileInfo['currentUri']
201
+ );
202
+
203
+ $this->sources[$fileInfo['currentUri']] = $fileInfo['filename'];
204
+ }
205
+
206
+
207
+ /**
208
+ * Generates the JSON source map
209
+ *
210
+ * @return string
211
+ * @see https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#
212
+ */
213
+ protected function generateJson(){
214
+
215
+ $sourceMap = array();
216
+ $mappings = $this->generateMappings();
217
+
218
+ // File version (always the first entry in the object) and must be a positive integer.
219
+ $sourceMap['version'] = self::VERSION;
220
+
221
+
222
+ // An optional name of the generated code that this source map is associated with.
223
+ $file = $this->getOption('sourceMapFilename');
224
+ if( $file ){
225
+ $sourceMap['file'] = $file;
226
+ }
227
+
228
+
229
+ // An optional source root, useful for relocating source files on a server or removing repeated values in the 'sources' entry. This value is prepended to the individual entries in the 'source' field.
230
+ $root = $this->getOption('sourceRoot');
231
+ if( $root ){
232
+ $sourceMap['sourceRoot'] = $root;
233
+ }
234
+
235
+
236
+ // A list of original sources used by the 'mappings' entry.
237
+ $sourceMap['sources'] = array();
238
+ foreach($this->sources as $source_uri => $source_filename){
239
+ $sourceMap['sources'][] = $this->normalizeFilename($source_filename);
240
+ }
241
+
242
+
243
+ // A list of symbol names used by the 'mappings' entry.
244
+ $sourceMap['names'] = array();
245
+
246
+ // A string with the encoded mapping data.
247
+ $sourceMap['mappings'] = $mappings;
248
+
249
+ if( $this->getOption('outputSourceFiles') ){
250
+ // An optional list of source content, useful when the 'source' can't be hosted.
251
+ // The contents are listed in the same order as the sources above.
252
+ // 'null' may be used if some original sources should be retrieved by name.
253
+ $sourceMap['sourcesContent'] = $this->getSourcesContent();
254
+ }
255
+
256
+ // less.js compat fixes
257
+ if( count($sourceMap['sources']) && empty($sourceMap['sourceRoot']) ){
258
+ unset($sourceMap['sourceRoot']);
259
+ }
260
+
261
+ return json_encode($sourceMap);
262
+ }
263
+
264
+ /**
265
+ * Returns the sources contents
266
+ *
267
+ * @return array|null
268
+ */
269
+ protected function getSourcesContent(){
270
+ if(empty($this->sources)){
271
+ return;
272
+ }
273
+ $content = array();
274
+ foreach($this->sources as $sourceFile){
275
+ $content[] = file_get_contents($sourceFile);
276
+ }
277
+ return $content;
278
+ }
279
+
280
+ /**
281
+ * Generates the mappings string
282
+ *
283
+ * @return string
284
+ */
285
+ public function generateMappings(){
286
+
287
+ if( !count($this->mappings) ){
288
+ return '';
289
+ }
290
+
291
+ $this->source_keys = array_flip(array_keys($this->sources));
292
+
293
+
294
+ // group mappings by generated line number.
295
+ $groupedMap = $groupedMapEncoded = array();
296
+ foreach($this->mappings as $m){
297
+ $groupedMap[$m['generated_line']][] = $m;
298
+ }
299
+ ksort($groupedMap);
300
+
301
+ $lastGeneratedLine = $lastOriginalIndex = $lastOriginalLine = $lastOriginalColumn = 0;
302
+
303
+ foreach($groupedMap as $lineNumber => $line_map){
304
+ while(++$lastGeneratedLine < $lineNumber){
305
+ $groupedMapEncoded[] = ';';
306
+ }
307
+
308
+ $lineMapEncoded = array();
309
+ $lastGeneratedColumn = 0;
310
+
311
+ foreach($line_map as $m){
312
+ $mapEncoded = $this->encoder->encode($m['generated_column'] - $lastGeneratedColumn);
313
+ $lastGeneratedColumn = $m['generated_column'];
314
+
315
+ // find the index
316
+ if( $m['source_file'] ){
317
+ $index = $this->findFileIndex($m['source_file']);
318
+ if( $index !== false ){
319
+ $mapEncoded .= $this->encoder->encode($index - $lastOriginalIndex);
320
+ $lastOriginalIndex = $index;
321
+
322
+ // lines are stored 0-based in SourceMap spec version 3
323
+ $mapEncoded .= $this->encoder->encode($m['original_line'] - 1 - $lastOriginalLine);
324
+ $lastOriginalLine = $m['original_line'] - 1;
325
+
326
+ $mapEncoded .= $this->encoder->encode($m['original_column'] - $lastOriginalColumn);
327
+ $lastOriginalColumn = $m['original_column'];
328
+ }
329
+ }
330
+
331
+ $lineMapEncoded[] = $mapEncoded;
332
+ }
333
+
334
+ $groupedMapEncoded[] = implode(',', $lineMapEncoded) . ';';
335
+ }
336
+
337
+ return rtrim(implode($groupedMapEncoded), ';');
338
+ }
339
+
340
+ /**
341
+ * Finds the index for the filename
342
+ *
343
+ * @param string $filename
344
+ * @return integer|false
345
+ */
346
+ protected function findFileIndex($filename){
347
+ return $this->source_keys[$filename];
348
+ }
349
+
350
+ /**
351
+ * fix windows paths
352
+ * @param string $path
353
+ * @return string
354
+ */
355
+ public function fixWindowsPath($path, $addEndSlash = false){
356
+ $slash = ($addEndSlash) ? '/' : '';
357
+ if( !empty($path) ){
358
+ $path = str_replace('\\', '/', $path);
359
+ $path = rtrim($path,'/') . $slash;
360
+ }
361
+
362
+ return $path;
363
+ }
364
+
365
+ }
base/inc/lib/Less/Tree.php CHANGED
@@ -6,36 +6,39 @@
6
  * @package Less
7
  * @subpackage tree
8
  */
9
- class Less_Tree {
10
 
11
  public $cache_string;
12
 
13
- public function toCSS() {
14
  $output = new Less_Output();
15
- $this->genCSS( $output );
16
  return $output->toString();
17
  }
18
 
19
- /**
20
- * Generate CSS by adding it to the output object
21
- *
22
- * @param Less_Output $output The output
23
- * @return void
24
- */
25
- public function genCSS( $output ) {
26
- }
 
27
 
28
  /**
29
  * @param Less_Tree_Ruleset[] $rules
30
  */
31
- public static function outputRuleset( $output, $rules ) {
32
- $ruleCnt = count( $rules );
 
33
  Less_Environment::$tabLevel++;
34
 
 
35
  // Compressed
36
- if ( Less_Parser::$options['compress'] ) {
37
- $output->add( '{' );
38
- for ( $i = 0; $i < $ruleCnt; $i++ ) {
39
  $rules[$i]->genCSS( $output );
40
  }
41
 
@@ -44,12 +47,13 @@ class Less_Tree {
44
  return;
45
  }
46
 
 
47
  // Non-compressed
48
- $tabSetStr = "\n".str_repeat( Less_Parser::$options['indentation'], Less_Environment::$tabLevel - 1 );
49
  $tabRuleStr = $tabSetStr.Less_Parser::$options['indentation'];
50
 
51
  $output->add( " {" );
52
- for ( $i = 0; $i < $ruleCnt; $i++ ) {
53
  $output->add( $tabRuleStr );
54
  $rules[$i]->genCSS( $output );
55
  }
@@ -58,24 +62,26 @@ class Less_Tree {
58
 
59
  }
60
 
61
- public function accept( $visitor ) {
62
- }
63
 
64
- public static function ReferencedArray( $rules ) {
65
- foreach ( $rules as $rule ) {
66
- if ( method_exists( $rule, 'markReferenced' ) ) {
 
67
  $rule->markReferenced();
68
  }
69
  }
70
  }
71
 
 
72
  /**
73
  * Requires php 5.3+
74
  */
75
- public static function __set_state( $args ) {
 
76
  $class = get_called_class();
77
- $obj = new $class( null, null, null, null );
78
- foreach ( $args as $key => $val ) {
79
  $obj->$key = $val;
80
  }
81
  return $obj;
6
  * @package Less
7
  * @subpackage tree
8
  */
9
+ class Less_Tree{
10
 
11
  public $cache_string;
12
 
13
+ public function toCSS(){
14
  $output = new Less_Output();
15
+ $this->genCSS($output);
16
  return $output->toString();
17
  }
18
 
19
+
20
+ /**
21
+ * Generate CSS by adding it to the output object
22
+ *
23
+ * @param Less_Output $output The output
24
+ * @return void
25
+ */
26
+ public function genCSS($output){}
27
+
28
 
29
  /**
30
  * @param Less_Tree_Ruleset[] $rules
31
  */
32
+ public static function outputRuleset( $output, $rules ){
33
+
34
+ $ruleCnt = count($rules);
35
  Less_Environment::$tabLevel++;
36
 
37
+
38
  // Compressed
39
+ if( Less_Parser::$options['compress'] ){
40
+ $output->add('{');
41
+ for( $i = 0; $i < $ruleCnt; $i++ ){
42
  $rules[$i]->genCSS( $output );
43
  }
44
 
47
  return;
48
  }
49
 
50
+
51
  // Non-compressed
52
+ $tabSetStr = "\n".str_repeat( Less_Parser::$options['indentation'] , Less_Environment::$tabLevel-1 );
53
  $tabRuleStr = $tabSetStr.Less_Parser::$options['indentation'];
54
 
55
  $output->add( " {" );
56
+ for($i = 0; $i < $ruleCnt; $i++ ){
57
  $output->add( $tabRuleStr );
58
  $rules[$i]->genCSS( $output );
59
  }
62
 
63
  }
64
 
65
+ public function accept($visitor){}
 
66
 
67
+
68
+ public static function ReferencedArray($rules){
69
+ foreach($rules as $rule){
70
+ if( method_exists($rule, 'markReferenced') ){
71
  $rule->markReferenced();
72
  }
73
  }
74
  }
75
 
76
+
77
  /**
78
  * Requires php 5.3+
79
  */
80
+ public static function __set_state($args){
81
+
82
  $class = get_called_class();
83
+ $obj = new $class(null,null,null,null);
84
+ foreach($args as $key => $val){
85
  $obj->$key = $val;
86
  }
87
  return $obj;
base/inc/lib/Less/Tree/Alpha.php CHANGED
@@ -6,43 +6,46 @@
6
  * @package Less
7
  * @subpackage tree
8
  */
9
- class Less_Tree_Alpha extends Less_Tree {
10
  public $value;
11
  public $type = 'Alpha';
12
 
13
- public function __construct( $val ) {
14
  $this->value = $val;
15
  }
16
 
17
- // function accept( $visitor ){
18
  // $this->value = $visitor->visit( $this->value );
19
  //}
20
 
21
- public function compile( $env ) {
22
- if ( is_object( $this->value ) ) {
23
- $this->value = $this->value->compile( $env );
 
24
  }
25
 
26
  return $this;
27
  }
28
 
29
- /**
30
- * @see Less_Tree::genCSS
31
- */
32
- public function genCSS( $output ) {
 
33
  $output->add( "alpha(opacity=" );
34
 
35
- if ( is_string( $this->value ) ) {
36
  $output->add( $this->value );
37
- } else {
38
- $this->value->genCSS( $output );
39
  }
40
 
41
  $output->add( ')' );
42
  }
43
 
44
- public function toCSS() {
45
- return "alpha(opacity=" . ( is_string( $this->value ) ? $this->value : $this->value->toCSS() ) . ")";
46
  }
47
 
48
- }
 
6
  * @package Less
7
  * @subpackage tree
8
  */
9
+ class Less_Tree_Alpha extends Less_Tree{
10
  public $value;
11
  public $type = 'Alpha';
12
 
13
+ public function __construct($val){
14
  $this->value = $val;
15
  }
16
 
17
+ //function accept( $visitor ){
18
  // $this->value = $visitor->visit( $this->value );
19
  //}
20
 
21
+ public function compile($env){
22
+
23
+ if( is_object($this->value) ){
24
+ $this->value = $this->value->compile($env);
25
  }
26
 
27
  return $this;
28
  }
29
 
30
+ /**
31
+ * @see Less_Tree::genCSS
32
+ */
33
+ public function genCSS( $output ){
34
+
35
  $output->add( "alpha(opacity=" );
36
 
37
+ if( is_string($this->value) ){
38
  $output->add( $this->value );
39
+ }else{
40
+ $this->value->genCSS( $output);
41
  }
42
 
43
  $output->add( ')' );
44
  }
45
 
46
+ public function toCSS(){
47
+ return "alpha(opacity=" . (is_string($this->value) ? $this->value : $this->value->toCSS()) . ")";
48
  }
49
 
50
+
51
+ }
base/inc/lib/Less/Tree/Anonymous.php CHANGED
@@ -6,7 +6,7 @@
6
  * @package Less
7
  * @subpackage tree
8
  */
9
- class Less_Tree_Anonymous extends Less_Tree {
10
  public $value;
11
  public $quote;
12
  public $index;
@@ -18,40 +18,40 @@ class Less_Tree_Anonymous extends Less_Tree {
18
  * @param integer $index
19
  * @param boolean $mapLines
20
  */
21
- public function __construct( $value, $index = null, $currentFileInfo = null, $mapLines = null ) {
22
  $this->value = $value;
23
  $this->index = $index;
24
  $this->mapLines = $mapLines;
25
  $this->currentFileInfo = $currentFileInfo;
26
  }
27
 
28
- public function compile() {
29
- return new Less_Tree_Anonymous( $this->value, $this->index, $this->currentFileInfo, $this->mapLines );
30
  }
31
 
32
- public function compare( $x ) {
33
- if ( !is_object( $x ) ) {
34
  return -1;
35
  }
36
 
37
  $left = $this->toCSS();
38
  $right = $x->toCSS();
39
 
40
- if ( $left === $right ) {
41
  return 0;
42
  }
43
 
44
  return $left < $right ? -1 : 1;
45
  }
46
 
47
- /**
48
- * @see Less_Tree::genCSS
49
- */
50
- public function genCSS( $output ) {
51
  $output->add( $this->value, $this->currentFileInfo, $this->index, $this->mapLines );
52
  }
53
 
54
- public function toCSS() {
55
  return $this->value;
56
  }
57
 
6
  * @package Less
7
  * @subpackage tree
8
  */
9
+ class Less_Tree_Anonymous extends Less_Tree{
10
  public $value;
11
  public $quote;
12
  public $index;
18
  * @param integer $index
19
  * @param boolean $mapLines
20
  */
21
+ public function __construct($value, $index = null, $currentFileInfo = null, $mapLines = null ){
22
  $this->value = $value;
23
  $this->index = $index;
24
  $this->mapLines = $mapLines;
25
  $this->currentFileInfo = $currentFileInfo;
26
  }
27
 
28
+ public function compile(){
29
+ return new Less_Tree_Anonymous($this->value, $this->index, $this->currentFileInfo, $this->mapLines);
30
  }
31
 
32
+ public function compare($x){
33
+ if( !is_object($x) ){
34
  return -1;
35
  }
36
 
37
  $left = $this->toCSS();
38
  $right = $x->toCSS();
39
 
40
+ if( $left === $right ){
41
  return 0;
42
  }
43
 
44
  return $left < $right ? -1 : 1;
45
  }
46
 
47
+ /**
48
+ * @see Less_Tree::genCSS
49
+ */
50
+ public function genCSS( $output ){
51
  $output->add( $this->value, $this->currentFileInfo, $this->index, $this->mapLines );
52
  }
53
 
54
+ public function toCSS(){
55
  return $this->value;
56
  }
57
 
base/inc/lib/Less/Tree/Assignment.php CHANGED
@@ -6,34 +6,34 @@
6
  * @package Less
7
  * @subpackage tree
8
  */
9
- class Less_Tree_Assignment extends Less_Tree {
10
 
11
  public $key;
12
  public $value;
13
  public $type = 'Assignment';
14
 
15
- public function __construct( $key, $val ) {
16
  $this->key = $key;
17
  $this->value = $val;
18
  }
19
 
20
- public function accept( $visitor ) {
21
  $this->value = $visitor->visitObj( $this->value );
22
  }
23
 
24
- public function compile( $env ) {
25
- return new Less_Tree_Assignment( $this->key, $this->value->compile( $env ) );
26
  }
27
 
28
- /**
29
- * @see Less_Tree::genCSS
30
- */
31
- public function genCSS( $output ) {
32
  $output->add( $this->key . '=' );
33
  $this->value->genCSS( $output );
34
  }
35
 
36
- public function toCss() {
37
  return $this->key . '=' . $this->value->toCSS();
38
  }
39
  }
6
  * @package Less
7
  * @subpackage tree
8
  */
9
+ class Less_Tree_Assignment extends Less_Tree{
10
 
11
  public $key;
12
  public $value;
13
  public $type = 'Assignment';
14
 
15
+ public function __construct($key, $val) {
16
  $this->key = $key;
17
  $this->value = $val;
18
  }
19
 
20
+ public function accept( $visitor ){
21
  $this->value = $visitor->visitObj( $this->value );
22
  }
23
 
24
+ public function compile($env) {
25
+ return new Less_Tree_Assignment( $this->key, $this->value->compile($env));
26
  }
27
 
28
+ /**
29
+ * @see Less_Tree::genCSS
30
+ */
31
+ public function genCSS( $output ){
32
  $output->add( $this->key . '=' );
33
  $this->value->genCSS( $output );
34
  }
35
 
36
+ public function toCss(){
37
  return $this->key . '=' . $this->value->toCSS();
38
  }
39
  }
base/inc/lib/Less/Tree/Attribute.php CHANGED
@@ -6,48 +6,49 @@
6
  * @package Less
7
  * @subpackage tree
8
  */
9
- class Less_Tree_Attribute extends Less_Tree {
10
 
11
  public $key;
12
  public $op;
13
  public $value;
14
  public $type = 'Attribute';
15
 
16
- public function __construct( $key, $op, $value ) {
17
  $this->key = $key;
18
  $this->op = $op;
19
  $this->value = $value;
20
  }
21
 
22
- public function compile( $env ) {
23
- $key_obj = is_object( $this->key );
24
- $val_obj = is_object( $this->value );
25
 
26
- if ( !$key_obj && !$val_obj ) {
 
 
 
27
  return $this;
28
  }
29
 
30
  return new Less_Tree_Attribute(
31
- $key_obj ? $this->key->compile( $env ) : $this->key,
32
  $this->op,
33
- $val_obj ? $this->value->compile( $env ) : $this->value );
34
  }
35
 
36
- /**
37
- * @see Less_Tree::genCSS
38
- */
39
- public function genCSS( $output ) {
40
  $output->add( $this->toCSS() );
41
  }
42
 
43
- public function toCSS() {
44
  $value = $this->key;
45
 
46
- if ( $this->op ) {
47
  $value .= $this->op;
48
- $value .= ( is_object( $this->value ) ? $this->value->toCSS() : $this->value );
49
  }
50
 
51
  return '[' . $value . ']';
52
  }
53
- }
6
  * @package Less
7
  * @subpackage tree
8
  */
9
+ class Less_Tree_Attribute extends Less_Tree{
10
 
11
  public $key;
12
  public $op;
13
  public $value;
14
  public $type = 'Attribute';
15
 
16
+ public function __construct($key, $op, $value){
17
  $this->key = $key;
18
  $this->op = $op;
19
  $this->value = $value;
20
  }
21
 
22
+ public function compile($env){
 
 
23
 
24
+ $key_obj = is_object($this->key);
25
+ $val_obj = is_object($this->value);
26
+
27
+ if( !$key_obj && !$val_obj ){
28
  return $this;
29
  }
30
 
31
  return new Less_Tree_Attribute(
32
+ $key_obj ? $this->key->compile($env) : $this->key ,
33
  $this->op,
34
+ $val_obj ? $this->value->compile($env) : $this->value);
35
  }
36
 
37
+ /**
38
+ * @see Less_Tree::genCSS
39
+ */
40
+ public function genCSS( $output ){
41
  $output->add( $this->toCSS() );
42
  }
43
 
44
+ public function toCSS(){
45
  $value = $this->key;
46
 
47
+ if( $this->op ){
48
  $value .= $this->op;
49
+ $value .= (is_object($this->value) ? $this->value->toCSS() : $this->value);
50
  }
51
 
52
  return '[' . $value . ']';
53
  }
54
+ }
base/inc/lib/Less/Tree/Call.php CHANGED
@@ -1,51 +1,52 @@
1
  <?php
2
 
 
3
  /**
4
  * Call
5
  *
6
  * @package Less
7
  * @subpackage tree
8
  */
9
- class Less_Tree_Call extends Less_Tree {
10
- public $value;
11
 
12
- public $name;
13
- public $args;
14
- public $index;
15
- public $currentFileInfo;
16
- public $type = 'Call';
17
 
18
- public function __construct( $name, $args, $index, $currentFileInfo = null ) {
19
  $this->name = $name;
20
  $this->args = $args;
21
  $this->index = $index;
22
  $this->currentFileInfo = $currentFileInfo;
23
  }
24
 
25
- public function accept( $visitor ) {
26
  $this->args = $visitor->visitArray( $this->args );
27
  }
28
 
29
- //
30
- // When evaluating a function call,
31
- // we either find the function in `tree.functions` [1],
32
- // in which case we call it, passing the evaluated arguments,
33
- // or we simply print it out as it appeared originally [2].
34
- //
35
- // The *functions.js* file contains the built-in functions.
36
- //
37
- // The reason why we evaluate the arguments, is in the case where
38
- // we try to pass a variable to a function, like: `saturate(@color)`.
39
- // The function should receive the value, not the variable.
40
- //
41
- public function compile( $env = null ) {
42
  $args = array();
43
- foreach ( $this->args as $a ) {
44
- $args[] = $a->compile( $env );
45
  }
46
 
47
- $nameLC = strtolower( $this->name );
48
- switch ( $nameLC ) {
49
  case '%':
50
  $nameLC = '_percent';
51
  break;
@@ -64,45 +65,47 @@ class Less_Tree_Call extends Less_Tree {
64
  }
65
 
66
  $result = null;
67
- if ( $nameLC === 'default' ) {
68
  $result = Less_Tree_DefaultFunc::compile();
69
 
70
- } else {
71
 
72
- if ( method_exists( 'Less_Functions', $nameLC ) ) { // 1.
73
  try {
74
 
75
- $func = new Less_Functions( $env, $this->currentFileInfo );
76
- $result = call_user_func_array( array( $func,$nameLC ), $args );
77
 
78
- } catch ( Exception $e ) {
79
- throw new Less_Exception_Compiler( 'error evaluating function `' . $this->name . '` '.$e->getMessage().' index: '. $this->index );
80
  }
81
- } elseif ( isset( $env->functions[$nameLC] ) && is_callable( $env->functions[$nameLC] ) ) {
82
  try {
83
  $result = call_user_func_array( $env->functions[$nameLC], $args );
84
- } catch ( Exception $e ) {
85
- throw new Less_Exception_Compiler( 'error evaluating function `' . $this->name . '` '.$e->getMessage().' index: '. $this->index );
86
  }
87
  }
88
  }
89
 
90
- if ( $result !== null ) {
91
  return $result;
92
  }
93
 
 
94
  return new Less_Tree_Call( $this->name, $args, $this->index, $this->currentFileInfo );
95
- }
 
 
 
 
 
96
 
97
- /**
98
- * @see Less_Tree::genCSS
99
- */
100
- public function genCSS( $output ) {
101
  $output->add( $this->name . '(', $this->currentFileInfo, $this->index );
102
- $args_len = count( $this->args );
103
- for ( $i = 0; $i < $args_len; $i++ ) {
104
  $this->args[$i]->genCSS( $output );
105
- if ( $i + 1 < $args_len ) {
106
  $output->add( ', ' );
107
  }
108
  }
@@ -110,8 +113,9 @@ class Less_Tree_Call extends Less_Tree {
110
  $output->add( ')' );
111
  }
112
 
113
- // public function toCSS(){
114
- // return $this->compile()->toCSS();
115
- //}
 
116
 
117
  }
1
  <?php
2
 
3
+
4
  /**
5
  * Call
6
  *
7
  * @package Less
8
  * @subpackage tree
9
  */
10
+ class Less_Tree_Call extends Less_Tree{
11
+ public $value;
12
 
13
+ public $name;
14
+ public $args;
15
+ public $index;
16
+ public $currentFileInfo;
17
+ public $type = 'Call';
18
 
19
+ public function __construct($name, $args, $index, $currentFileInfo = null ){
20
  $this->name = $name;
21
  $this->args = $args;
22
  $this->index = $index;
23
  $this->currentFileInfo = $currentFileInfo;
24
  }
25
 
26
+ public function accept( $visitor ){
27
  $this->args = $visitor->visitArray( $this->args );
28
  }
29
 
30
+ //
31
+ // When evaluating a function call,
32
+ // we either find the function in `tree.functions` [1],
33
+ // in which case we call it, passing the evaluated arguments,
34
+ // or we simply print it out as it appeared originally [2].
35
+ //
36
+ // The *functions.js* file contains the built-in functions.
37
+ //
38
+ // The reason why we evaluate the arguments, is in the case where
39
+ // we try to pass a variable to a function, like: `saturate(@color)`.
40
+ // The function should receive the value, not the variable.
41
+ //
42
+ public function compile($env=null){
43
  $args = array();
44
+ foreach($this->args as $a){
45
+ $args[] = $a->compile($env);
46
  }
47
 
48
+ $nameLC = strtolower($this->name);
49
+ switch($nameLC){
50
  case '%':
51
  $nameLC = '_percent';
52
  break;
65
  }
66
 
67
  $result = null;
68
+ if( $nameLC === 'default' ){
69
  $result = Less_Tree_DefaultFunc::compile();
70
 
71
+ }else{
72
 
73
+ if( method_exists('Less_Functions',$nameLC) ){ // 1.
74
  try {
75
 
76
+ $func = new Less_Functions($env, $this->currentFileInfo);
77
+ $result = call_user_func_array( array($func,$nameLC),$args);
78
 
79
+ } catch (Exception $e) {
80
+ throw new Less_Exception_Compiler('error evaluating function `' . $this->name . '` '.$e->getMessage().' index: '. $this->index);
81
  }
82
+ } elseif( isset( $env->functions[$nameLC] ) && is_callable( $env->functions[$nameLC] ) ) {
83
  try {
84
  $result = call_user_func_array( $env->functions[$nameLC], $args );
85
+ } catch (Exception $e) {
86
+ throw new Less_Exception_Compiler('error evaluating function `' . $this->name . '` '.$e->getMessage().' index: '. $this->index);
87
  }
88
  }
89
  }
90
 
91
+ if( $result !== null ){
92
  return $result;
93
  }
94
 
95
+
96
  return new Less_Tree_Call( $this->name, $args, $this->index, $this->currentFileInfo );
97
+ }
98
+
99
+ /**
100
+ * @see Less_Tree::genCSS
101
+ */
102
+ public function genCSS( $output ){
103
 
 
 
 
 
104
  $output->add( $this->name . '(', $this->currentFileInfo, $this->index );
105
+ $args_len = count($this->args);
106
+ for($i = 0; $i < $args_len; $i++ ){
107
  $this->args[$i]->genCSS( $output );
108
+ if( $i + 1 < $args_len ){
109
  $output->add( ', ' );
110
  }
111
  }
113
  $output->add( ')' );
114
  }
115
 
116
+
117
+ //public function toCSS(){
118
+ // return $this->compile()->toCSS();
119
+ //}
120
 
121
  }
base/inc/lib/Less/Tree/Color.php CHANGED
@@ -6,14 +6,15 @@
6
  * @package Less
7
  * @subpackage tree
8
  */
9
- class Less_Tree_Color extends Less_Tree {
10
  public $rgb;
11
  public $alpha;
12
  public $isTransparentKeyword;
13
  public $type = 'Color';
14
 
15
- public function __construct( $rgb, $a = 1, $isTransparentKeyword = null ) {
16
- if ( $isTransparentKeyword ) {
 
17
  $this->rgb = $rgb;
18
  $this->alpha = $a;
19
  $this->isTransparentKeyword = true;
@@ -21,32 +22,32 @@ class Less_Tree_Color extends Less_Tree {
21
  }
22
 
23
  $this->rgb = array();
24
- if ( is_array( $rgb ) ) {
25
  $this->rgb = $rgb;
26
- } else if ( strlen( $rgb ) == 6 ) {
27
- foreach ( str_split( $rgb, 2 ) as $c ) {
28
- $this->rgb[] = hexdec( $c );
29
  }
30
- } else {
31
- foreach ( str_split( $rgb, 1 ) as $c ) {
32
- $this->rgb[] = hexdec( $c.$c );
33
  }
34
  }
35
- $this->alpha = is_numeric( $a ) ? $a : 1;
36
  }
37
 
38
- public function compile() {
39
  return $this;
40
  }
41
 
42
- public function luma() {
43
  $r = $this->rgb[0] / 255;
44
  $g = $this->rgb[1] / 255;
45
  $b = $this->rgb[2] / 255;
46
 
47
- $r = ( $r <= 0.03928 ) ? $r / 12.92 : pow( ( ( $r + 0.055 ) / 1.055 ), 2.4 );
48
- $g = ( $g <= 0.03928 ) ? $g / 12.92 : pow( ( ( $g + 0.055 ) / 1.055 ), 2.4 );
49
- $b = ( $b <= 0.03928 ) ? $b / 12.92 : pow( ( ( $b + 0.055 ) / 1.055 ), 2.4 );
50
 
51
  return 0.2126 * $r + 0.7152 * $g + 0.0722 * $b;
52
  }
@@ -54,41 +55,42 @@ class Less_Tree_Color extends Less_Tree {
54
  /**
55
  * @see Less_Tree::genCSS
56
  */
57
- public function genCSS( $output ) {
58
  $output->add( $this->toCSS() );
59
  }
60
 
61
- public function toCSS( $doNotCompress = false ) {
62
  $compress = Less_Parser::$options['compress'] && !$doNotCompress;
63
  $alpha = Less_Functions::fround( $this->alpha );
64
 
 
65
  //
66
  // If we have some transparency, the only way to represent it
67
  // is via `rgba`. Otherwise, we use the hex representation,
68
  // which has better compatibility with older browsers.
69
  // Values are capped between `0` and `255`, rounded and zero-padded.
70
  //
71
- if ( $alpha < 1 ) {
72
- if ( ( $alpha === 0 || $alpha === 0.0 ) && isset( $this->isTransparentKeyword ) && $this->isTransparentKeyword ) {
73
  return 'transparent';
74
  }
75
 
76
  $values = array();
77
- foreach ( $this->rgb as $c ) {
78
- $values[] = Less_Functions::clamp( round( $c ), 255 );
79
  }
80
  $values[] = $alpha;
81
 
82
- $glue = ( $compress ? ',' : ', ' );
83
- return "rgba(" . implode( $glue, $values ) . ")";
84
- } else {
85
 
86
  $color = $this->toRGB();
87
 
88
- if ( $compress ) {
89
 
90
  // Convert color to short format
91
- if ( $color[1] === $color[2] && $color[3] === $color[4] && $color[5] === $color[6] ) {
92
  $color = '#'.$color[1] . $color[3] . $color[5];
93
  }
94
  }
@@ -107,123 +109,121 @@ class Less_Tree_Color extends Less_Tree {
107
  /**
108
  * @param string $op
109
  */
110
- public function operate( $op, $other ) {
111
  $rgb = array();
112
- $alpha = $this->alpha * ( 1 - $other->alpha ) + $other->alpha;
113
- for ( $c = 0; $c < 3; $c++ ) {
114
- $rgb[$c] = Less_Functions::operate( $op, $this->rgb[$c], $other->rgb[$c] );
115
  }
116
- return new Less_Tree_Color( $rgb, $alpha );
117
  }
118
 
119
- public function toRGB() {
120
- return $this->toHex( $this->rgb );
121
  }
122
 
123
- public function toHSL() {
124
  $r = $this->rgb[0] / 255;
125
  $g = $this->rgb[1] / 255;
126
  $b = $this->rgb[2] / 255;
127
  $a = $this->alpha;
128
 
129
- $max = max( $r, $g, $b );
130
- $min = min( $r, $g, $b );
131
- $l = ( $max + $min ) / 2;
132
  $d = $max - $min;
133
 
134
  $h = $s = 0;
135
- if ( $max !== $min ) {
136
- $s = $l > 0.5 ? $d / ( 2 - $max - $min ) : $d / ( $max + $min );
137
-
138
- switch ( $max ) {
139
- case $r: $h = ( $g - $b ) / $d + ( $g < $b ? 6 : 0 );
140
- break;
141
- case $g: $h = ( $b - $r ) / $d + 2;
142
- break;
143
- case $b: $h = ( $r - $g ) / $d + 4;
144
- break;
145
  }
146
  $h /= 6;
147
  }
148
- return array( 'h' => $h * 360, 's' => $s, 'l' => $l, 'a' => $a );
149
  }
150
 
151
- // Adapted from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
152
- public function toHSV() {
153
  $r = $this->rgb[0] / 255;
154
  $g = $this->rgb[1] / 255;
155
  $b = $this->rgb[2] / 255;
156
  $a = $this->alpha;
157
 
158
- $max = max( $r, $g, $b );
159
- $min = min( $r, $g, $b );
160
 
161
  $v = $max;
162
 
163
  $d = $max - $min;
164
- if ( $max === 0 ) {
165
  $s = 0;
166
  } else {
167
  $s = $d / $max;
168
  }
169
 
170
  $h = 0;
171
- if ( $max !== $min ) {
172
- switch ( $max ) {
173
- case $r: $h = ( $g - $b ) / $d + ( $g < $b ? 6 : 0 );
174
- break;
175
- case $g: $h = ( $b - $r ) / $d + 2;
176
- break;
177
- case $b: $h = ( $r - $g ) / $d + 4;
178
- break;
179
  }
180
  $h /= 6;
181
  }
182
- return array( 'h' => $h * 360, 's' => $s, 'v' => $v, 'a' => $a );
183
  }
184
 
185
- public function toARGB() {
186
- $argb = array_merge( (array)Less_Parser::round( $this->alpha * 255 ), $this->rgb );
187
  return $this->toHex( $argb );
188
  }
189
 
190
- public function compare( $x ) {
191
- if ( !property_exists( $x, 'rgb' ) ) {
 
192
  return -1;
193
  }
194
 
195
- return ( $x->rgb[0] === $this->rgb[0] &&
 
196
  $x->rgb[1] === $this->rgb[1] &&
197
  $x->rgb[2] === $this->rgb[2] &&
198
- $x->alpha === $this->alpha ) ? 0 : -1;
199
  }
200
 
201
- public function toHex( $v ) {
 
202
  $ret = '#';
203
- foreach ( $v as $c ) {
204
- $c = Less_Functions::clamp( Less_Parser::round( $c ), 255 );
205
- if ( $c < 16 ) {
206
  $ret .= '0';
207
  }
208
- $ret .= dechex( $c );
209
  }
210
 
211
  return $ret;
212
  }
213
 
 
214
  /**
215
  * @param string $keyword
216
  */
217
- public static function fromKeyword( $keyword ) {
218
- $keyword = strtolower( $keyword );
219
 
220
- if ( Less_Colors::hasOwnProperty( $keyword ) ) {
221
  // detect named color
222
- return new Less_Tree_Color( substr( Less_Colors::color( $keyword ), 1 ) );
223
  }
224
 
225
- if ( $keyword === 'transparent' ) {
226
- return new Less_Tree_Color( array( 0, 0, 0 ), 0, true );
227
  }
228
  }
229
 
6
  * @package Less
7
  * @subpackage tree
8
  */
9
+ class Less_Tree_Color extends Less_Tree{
10
  public $rgb;
11
  public $alpha;
12
  public $isTransparentKeyword;
13
  public $type = 'Color';
14
 
15
+ public function __construct($rgb, $a = 1, $isTransparentKeyword = null ){
16
+
17
+ if( $isTransparentKeyword ){
18
  $this->rgb = $rgb;
19
  $this->alpha = $a;
20
  $this->isTransparentKeyword = true;
22
  }
23
 
24
  $this->rgb = array();
25
+ if( is_array($rgb) ){
26
  $this->rgb = $rgb;
27
+ }else if( strlen($rgb) == 6 ){
28
+ foreach(str_split($rgb, 2) as $c){
29
+ $this->rgb[] = hexdec($c);
30
  }
31
+ }else{
32
+ foreach(str_split($rgb, 1) as $c){
33
+ $this->rgb[] = hexdec($c.$c);
34
  }
35
  }
36
+ $this->alpha = is_numeric($a) ? $a : 1;
37
  }
38
 
39
+ public function compile(){
40
  return $this;
41
  }
42
 
43
+ public function luma(){
44
  $r = $this->rgb[0] / 255;
45
  $g = $this->rgb[1] / 255;
46
  $b = $this->rgb[2] / 255;
47
 
48
+ $r = ($r <= 0.03928) ? $r / 12.92 : pow((($r + 0.055) / 1.055), 2.4);
49
+ $g = ($g <= 0.03928) ? $g / 12.92 : pow((($g + 0.055) / 1.055), 2.4);
50
+ $b = ($b <= 0.03928) ? $b / 12.92 : pow((($b + 0.055) / 1.055), 2.4);
51
 
52
  return 0.2126 * $r + 0.7152 * $g + 0.0722 * $b;
53
  }
55
  /**
56
  * @see Less_Tree::genCSS
57
  */
58
+ public function genCSS( $output ){
59
  $output->add( $this->toCSS() );
60
  }
61
 
62
+ public function toCSS( $doNotCompress = false ){
63
  $compress = Less_Parser::$options['compress'] && !$doNotCompress;
64
  $alpha = Less_Functions::fround( $this->alpha );
65
 
66
+
67
  //
68
  // If we have some transparency, the only way to represent it
69
  // is via `rgba`. Otherwise, we use the hex representation,
70
  // which has better compatibility with older browsers.
71
  // Values are capped between `0` and `255`, rounded and zero-padded.
72
  //
73
+ if( $alpha < 1 ){
74
+ if( ( $alpha === 0 || $alpha === 0.0 ) && isset($this->isTransparentKeyword) && $this->isTransparentKeyword ){
75
  return 'transparent';
76
  }
77
 
78
  $values = array();
79
+ foreach($this->rgb as $c){
80
+ $values[] = Less_Functions::clamp( round($c), 255);
81
  }
82
  $values[] = $alpha;
83
 
84
+ $glue = ($compress ? ',' : ', ');
85
+ return "rgba(" . implode($glue, $values) . ")";
86
+ }else{
87
 
88
  $color = $this->toRGB();
89
 
90
+ if( $compress ){
91
 
92
  // Convert color to short format
93
+ if( $color[1] === $color[2] && $color[3] === $color[4] && $color[5] === $color[6]) {
94
  $color = '#'.$color[1] . $color[3] . $color[5];
95
  }
96
  }
109
  /**
110
  * @param string $op
111
  */
112
+ public function operate( $op, $other) {
113
  $rgb = array();
114
+ $alpha = $this->alpha * (1 - $other->alpha) + $other->alpha;
115
+ for ($c = 0; $c < 3; $c++) {
116
+ $rgb[$c] = Less_Functions::operate( $op, $this->rgb[$c], $other->rgb[$c]);
117
  }
118
+ return new Less_Tree_Color($rgb, $alpha);
119
  }
120
 
121
+ public function toRGB(){
122
+ return $this->toHex($this->rgb);
123
  }
124
 
125
+ public function toHSL(){
126
  $r = $this->rgb[0] / 255;
127
  $g = $this->rgb[1] / 255;
128
  $b = $this->rgb[2] / 255;
129
  $a = $this->alpha;
130
 
131
+ $max = max($r, $g, $b);
132
+ $min = min($r, $g, $b);
133
+ $l = ($max + $min) / 2;
134
  $d = $max - $min;
135
 
136
  $h = $s = 0;
137
+ if( $max !== $min ){
138
+ $s = $l > 0.5 ? $d / (2 - $max - $min) : $d / ($max + $min);
139
+
140
+ switch ($max) {
141
+ case $r: $h = ($g - $b) / $d + ($g < $b ? 6 : 0); break;
142
+ case $g: $h = ($b - $r) / $d + 2; break;
143
+ case $b: $h = ($r - $g) / $d + 4; break;
 
 
 
144
  }
145
  $h /= 6;
146
  }
147
+ return array('h' => $h * 360, 's' => $s, 'l' => $l, 'a' => $a );
148
  }
149
 
150
+ //Adapted from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
151
+ public function toHSV() {
152
  $r = $this->rgb[0] / 255;
153
  $g = $this->rgb[1] / 255;
154
  $b = $this->rgb[2] / 255;
155
  $a = $this->alpha;
156
 
157
+ $max = max($r, $g, $b);
158
+ $min = min($r, $g, $b);
159
 
160
  $v = $max;
161
 
162
  $d = $max - $min;
163
+ if ($max === 0) {
164
  $s = 0;
165
  } else {
166
  $s = $d / $max;
167
  }
168
 
169
  $h = 0;
170
+ if( $max !== $min ){
171
+ switch($max){
172
+ case $r: $h = ($g - $b) / $d + ($g < $b ? 6 : 0); break;
173
+ case $g: $h = ($b - $r) / $d + 2; break;
174
+ case $b: $h = ($r - $g) / $d + 4; break;
 
 
 
175
  }
176
  $h /= 6;
177
  }
178
+ return array('h'=> $h * 360, 's'=> $s, 'v'=> $v, 'a' => $a );
179
  }
180
 
181
+ public function toARGB(){
182
+ $argb = array_merge( (array) Less_Parser::round($this->alpha * 255), $this->rgb);
183
  return $this->toHex( $argb );
184
  }
185
 
186
+ public function compare($x){
187
+
188
+ if( !property_exists( $x, 'rgb' ) ){
189
  return -1;
190
  }
191
 
192
+
193
+ return ($x->rgb[0] === $this->rgb[0] &&
194
  $x->rgb[1] === $this->rgb[1] &&
195
  $x->rgb[2] === $this->rgb[2] &&
196
+ $x->alpha === $this->alpha) ? 0 : -1;
197
  }
198
 
199
+ public function toHex( $v ){
200
+
201
  $ret = '#';
202
+ foreach($v as $c){
203
+ $c = Less_Functions::clamp( Less_Parser::round($c), 255);
204
+ if( $c < 16 ){
205
  $ret .= '0';
206
  }
207
+ $ret .= dechex($c);
208
  }
209
 
210
  return $ret;
211
  }
212
 
213
+
214
  /**
215
  * @param string $keyword
216
  */
217
+ public static function fromKeyword( $keyword ){
218
+ $keyword = strtolower($keyword);
219
 
220
+ if( Less_Colors::hasOwnProperty($keyword) ){
221
  // detect named color
222
+ return new Less_Tree_Color(substr(Less_Colors::color($keyword), 1));
223
  }
224
 
225
+ if( $keyword === 'transparent' ){
226
+ return new Less_Tree_Color( array(0, 0, 0), 0, true);
227
  }
228
  }
229
 
base/inc/lib/Less/Tree/Comment.php CHANGED
@@ -6,7 +6,7 @@
6
  * @package Less
7
  * @subpackage tree
8
  */
9
- class Less_Tree_Comment extends Less_Tree {
10
 
11
  public $value;
12
  public $silent;
@@ -14,37 +14,37 @@ class Less_Tree_Comment extends Less_Tree {
14
  public $currentFileInfo;
15
  public $type = 'Comment';
16
 
17
- public function __construct( $value, $silent, $index = null, $currentFileInfo = null ) {
18
  $this->value = $value;
19
- $this->silent = !!$silent;
20
  $this->currentFileInfo = $currentFileInfo;
21
  }
22
 
23
- /**
24
- * @see Less_Tree::genCSS
25
- */
26
- public function genCSS( $output ) {
27
- // if( $this->debugInfo ){
28
  //$output->add( tree.debugInfo($env, $this), $this->currentFileInfo, $this->index);
29
  //}
30
- $output->add( trim( $this->value ) );// TODO shouldn't need to trim, we shouldn't grab the \n
31
  }
32
 
33
- public function toCSS() {
34
  return Less_Parser::$options['compress'] ? '' : $this->value;
35
  }
36
 
37
- public function isSilent() {
38
- $isReference = ( $this->currentFileInfo && isset( $this->currentFileInfo['reference'] ) && ( !isset( $this->isReferenced ) || !$this->isReferenced ) );
39
- $isCompressed = Less_Parser::$options['compress'] && !preg_match( '/^\/\*!/', $this->value );
40
  return $this->silent || $isReference || $isCompressed;
41
  }
42
 
43
- public function compile() {
44
  return $this;
45
  }
46
 
47
- public function markReferenced() {
48
  $this->isReferenced = true;
49
  }
50
 
6
  * @package Less
7
  * @subpackage tree
8
  */
9
+ class Less_Tree_Comment extends Less_Tree{
10
 
11
  public $value;
12
  public $silent;
14
  public $currentFileInfo;
15
  public $type = 'Comment';
16
 
17
+ public function __construct($value, $silent, $index = null, $currentFileInfo = null ){
18
  $this->value = $value;
19
+ $this->silent = !! $silent;
20
  $this->currentFileInfo = $currentFileInfo;
21
  }
22
 
23
+ /**
24
+ * @see Less_Tree::genCSS
25
+ */
26
+ public function genCSS( $output ){
27
+ //if( $this->debugInfo ){
28
  //$output->add( tree.debugInfo($env, $this), $this->currentFileInfo, $this->index);
29
  //}
30
+ $output->add( trim($this->value) );//TODO shouldn't need to trim, we shouldn't grab the \n
31
  }
32
 
33
+ public function toCSS(){
34
  return Less_Parser::$options['compress'] ? '' : $this->value;
35
  }
36
 
37
+ public function isSilent(){
38
+ $isReference = ($this->currentFileInfo && isset($this->currentFileInfo['reference']) && (!isset($this->isReferenced) || !$this->isReferenced) );
39
+ $isCompressed = Less_Parser::$options['compress'] && !preg_match('/^\/\*!/', $this->value);
40
  return $this->silent || $isReference || $isCompressed;
41
  }
42
 
43
+ public function compile(){
44
  return $this;
45
  }
46
 
47
+ public function markReferenced(){
48
  $this->isReferenced = true;
49
  }
50
 
base/inc/lib/Less/Tree/Condition.php CHANGED
@@ -6,7 +6,7 @@
6
  * @package Less
7
  * @subpackage tree
8
  */
9
- class Less_Tree_Condition extends Less_Tree {
10
 
11
  public $op;
12
  public $lvalue;
@@ -15,24 +15,24 @@ class Less_Tree_Condition extends Less_Tree {
15
  public $negate;
16
  public $type = 'Condition';
17
 
18
- public function __construct( $op, $l, $r, $i = 0, $negate = false ) {
19
- $this->op = trim( $op );
20
  $this->lvalue = $l;
21
  $this->rvalue = $r;
22
  $this->index = $i;
23
  $this->negate = $negate;
24
  }
25
 
26
- public function accept( $visitor ) {
27
  $this->lvalue = $visitor->visitObj( $this->lvalue );
28
  $this->rvalue = $visitor->visitObj( $this->rvalue );
29
  }
30
 
31
- public function compile( $env ) {
32
- $a = $this->lvalue->compile( $env );
33
- $b = $this->rvalue->compile( $env );
34
 
35
- switch ( $this->op ) {
36
  case 'and':
37
  $result = $a && $b;
38
  break;
@@ -42,15 +42,15 @@ class Less_Tree_Condition extends Less_Tree {
42
  break;
43
 
44
  default:
45
- if ( Less_Parser::is_method( $a, 'compare' ) ) {
46
- $result = $a->compare( $b );
47
- } elseif ( Less_Parser::is_method( $b, 'compare' ) ) {
48
- $result = $b->compare( $a );
49
- } else {
50
- throw new Less_Exception_Compiler( 'Unable to perform comparison', null, $this->index );
51
  }
52
 
53
- switch ( $result ) {
54
  case -1:
55
  $result = $this->op === '<' || $this->op === '=<' || $this->op === '<=';
56
  break;
@@ -67,6 +67,6 @@ class Less_Tree_Condition extends Less_Tree {
67
  }
68
 
69
  return $this->negate ? !$result : $result;
70
- }
71
 
72
  }
6
  * @package Less
7
  * @subpackage tree
8
  */
9
+ class Less_Tree_Condition extends Less_Tree{
10
 
11
  public $op;
12
  public $lvalue;
15
  public $negate;
16
  public $type = 'Condition';
17
 
18
+ public function __construct($op, $l, $r, $i = 0, $negate = false) {
19
+ $this->op = trim($op);
20
  $this->lvalue = $l;
21
  $this->rvalue = $r;
22
  $this->index = $i;
23
  $this->negate = $negate;
24
  }
25
 
26
+ public function accept($visitor){
27
  $this->lvalue = $visitor->visitObj( $this->lvalue );
28
  $this->rvalue = $visitor->visitObj( $this->rvalue );
29
  }
30
 
31
+ public function compile($env) {
32
+ $a = $this->lvalue->compile($env);
33
+ $b = $this->rvalue->compile($env);
34
 
35
+ switch( $this->op ){
36
  case 'and':
37
  $result = $a && $b;
38
  break;
42
  break;
43
 
44
  default:
45
+ if( Less_Parser::is_method($a, 'compare') ){
46
+ $result = $a->compare($b);
47
+ }elseif( Less_Parser::is_method($b, 'compare') ){
48
+ $result = $b->compare($a);
49
+ }else{
50
+ throw new Less_Exception_Compiler('Unable to perform comparison', null, $this->index);
51
  }
52
 
53
+ switch ($result) {
54
  case -1:
55
  $result = $this->op === '<' || $this->op === '=<' || $this->op === '<=';
56
  break;
67
  }
68
 
69
  return $this->negate ? !$result : $result;
70
+ }
71
 
72
  }
base/inc/lib/Less/Tree/DefaultFunc.php CHANGED
@@ -6,29 +6,29 @@
6
  * @package Less
7
  * @subpackage tree
8
  */
9
- class Less_Tree_DefaultFunc {
10
 
11
  static $error_;
12
  static $value_;
13
 
14
- public static function compile() {
15
- if ( self::$error_ ) {
16
- throw new Exception( self::$error_ );
17
  }
18
- if ( self::$value_ !== null ) {
19
- return self::$value_ ? new Less_Tree_Keyword( 'true' ) : new Less_Tree_Keyword( 'false' );
20
  }
21
  }
22
 
23
- public static function value( $v ) {
24
  self::$value_ = $v;
25
  }
26
 
27
- public static function error( $e ) {
28
  self::$error_ = $e;
29
  }
30
 
31
- public static function reset() {
32
  self::$value_ = self::$error_ = null;
33
  }
34
- }
6
  * @package Less
7
  * @subpackage tree
8
  */
9
+ class Less_Tree_DefaultFunc{
10
 
11
  static $error_;
12
  static $value_;
13
 
14
+ public static function compile(){
15
+ if( self::$error_ ){
16
+ throw new Exception(self::$error_);
17
  }
18
+ if( self::$value_ !== null ){
19
+ return self::$value_ ? new Less_Tree_Keyword('true') : new Less_Tree_Keyword('false');
20
  }
21
  }
22
 
23
+ public static function value( $v ){
24
  self::$value_ = $v;
25
  }
26
 
27
+ public static function error( $e ){
28
  self::$error_ = $e;
29
  }
30
 
31
+ public static function reset(){
32
  self::$value_ = self::$error_ = null;
33
  }
34
+ }
base/inc/lib/Less/Tree/DetachedRuleset.php CHANGED
@@ -6,34 +6,35 @@
6
  * @package Less
7
  * @subpackage tree
8
  */
9
- class Less_Tree_DetachedRuleset extends Less_Tree {
10
 
11
  public $ruleset;
12
  public $frames;
13
  public $type = 'DetachedRuleset';
14
 
15
- public function __construct( $ruleset, $frames = null ) {
16
  $this->ruleset = $ruleset;
17
  $this->frames = $frames;
18
  }
19
 
20
- public function accept( $visitor ) {
21
- $this->ruleset = $visitor->visitObj( $this->ruleset );
22
  }
23
 
24
- public function compile( $env ) {
25
- if ( $this->frames ) {
26
  $frames = $this->frames;
27
- } else {
28
  $frames = $env->frames;
29
  }
30
- return new Less_Tree_DetachedRuleset( $this->ruleset, $frames );
31
  }
32
 
33
- public function callEval( $env ) {
34
- if ( $this->frames ) {
35
- return $this->ruleset->compile( $env->copyEvalEnv( array_merge( $this->frames, $env->frames ) ) );
36
  }
37
  return $this->ruleset->compile( $env );
38
  }
39
  }
 
6
  * @package Less
7
  * @subpackage tree
8
  */
9
+ class Less_Tree_DetachedRuleset extends Less_Tree{
10
 
11
  public $ruleset;
12
  public $frames;
13
  public $type = 'DetachedRuleset';
14
 
15
+ public function __construct( $ruleset, $frames = null ){
16
  $this->ruleset = $ruleset;
17
  $this->frames = $frames;
18
  }
19
 
20
+ public function accept($visitor) {
21
+ $this->ruleset = $visitor->visitObj($this->ruleset);
22
  }
23
 
24
+ public function compile($env){
25
+ if( $this->frames ){
26
  $frames = $this->frames;
27
+ }else{
28
  $frames = $env->frames;
29
  }
30
+ return new Less_Tree_DetachedRuleset($this->ruleset, $frames);
31
  }
32
 
33
+ public function callEval($env) {
34
+ if( $this->frames ){
35
+ return $this->ruleset->compile( $env->copyEvalEnv( array_merge($this->frames,$env->frames) ) );
36
  }
37
  return $this->ruleset->compile( $env );
38
  }
39
  }
40
+
base/inc/lib/Less/Tree/Dimension.php CHANGED
@@ -6,63 +6,64 @@
6
  * @package Less
7
  * @subpackage tree
8
  */
9
- class Less_Tree_Dimension extends Less_Tree {
10
 
11
  public $value;
12
  public $unit;
13
  public $type = 'Dimension';
14
 
15
- public function __construct( $value, $unit = null ) {
16
- $this->value = floatval( $value );
17
 
18
- if ( $unit && ( $unit instanceof Less_Tree_Unit ) ) {
19
  $this->unit = $unit;
20
- } elseif ( $unit ) {
21
- $this->unit = new Less_Tree_Unit( array( $unit ) );
22
- } else {
23
- $this->unit = new Less_Tree_Unit();
24
  }
25
- }
26
 
27
- public function accept( $visitor ) {
28
  $this->unit = $visitor->visitObj( $this->unit );
29
  }
30
 
31
- public function compile() {
32
- return $this;
33
- }
34
 
35
- public function toColor() {
36
- return new Less_Tree_Color( array( $this->value, $this->value, $this->value ) );
37
- }
 
 
 
 
 
38
 
39
- /**
40
- * @see Less_Tree::genCSS
41
- */
42
- public function genCSS( $output ) {
43
- if ( Less_Parser::$options['strictUnits'] && !$this->unit->isSingular() ) {
44
- throw new Less_Exception_Compiler( "Multiple units in dimension. Correct the units or use the unit function. Bad unit: ".$this->unit->toString() );
45
  }
46
 
47
  $value = Less_Functions::fround( $this->value );
48
  $strValue = (string)$value;
49
 
50
- if ( $value !== 0 && $value < 0.000001 && $value > -0.000001 ) {
51
  // would be output 1e-6 etc.
52
- $strValue = number_format( $strValue, 10 );
53
- $strValue = preg_replace( '/\.?0+$/', '', $strValue );
54
  }
55
 
56
- if ( Less_Parser::$options['compress'] ) {
57
  // Zero values doesn't need a unit
58
- if ( $value === 0 && $this->unit->isLength() ) {
59
  $output->add( $strValue );
60
  return $strValue;
61
  }
62
 
63
  // Float values doesn't need a leading zero
64
- if ( $value > 0 && $value < 1 && $strValue[0] === '0' ) {
65
- $strValue = substr( $strValue, 1 );
66
  }
67
  }
68
 
@@ -70,72 +71,73 @@ class Less_Tree_Dimension extends Less_Tree {
70
  $this->unit->genCSS( $output );
71
  }
72
 
73
- public function __toString() {
74
- return $this->toCSS();
75
- }
76
 
77
- // In an operation between two Dimensions,
78
- // we default to the first Dimension's unit,
79
- // so `1px + 2em` will yield `3px`.
80
 
81
- /**
82
- * @param string $op
83
- */
84
- public function operate( $op, $other ) {
85
- $value = Less_Functions::operate( $op, $this->value, $other->value );
 
86
  $unit = clone $this->unit;
87
 
88
- if ( $op === '+' || $op === '-' ) {
89
 
90
- if ( !$unit->numerator && !$unit->denominator ) {
91
  $unit->numerator = $other->unit->numerator;
92
  $unit->denominator = $other->unit->denominator;
93
- } elseif ( !$other->unit->numerator && !$other->unit->denominator ) {
94
  // do nothing
95
- } else {
96
- $other = $other->convertTo( $this->unit->usedUnits() );
97
 
98
- if ( Less_Parser::$options['strictUnits'] && $other->unit->toString() !== $unit->toCSS() ) {
99
- throw new Less_Exception_Compiler( "Incompatible units. Change the units or use the unit function. Bad units: '" . $unit->toString() . "' and " . $other->unit->toString() . "'." );
100
  }
101
 
102
- $value = Less_Functions::operate( $op, $this->value, $other->value );
103
  }
104
- } elseif ( $op === '*' ) {
105
- $unit->numerator = array_merge( $unit->numerator, $other->unit->numerator );
106
- $unit->denominator = array_merge( $unit->denominator, $other->unit->denominator );
107
- sort( $unit->numerator );
108
- sort( $unit->denominator );
109
  $unit->cancel();
110
- } elseif ( $op === '/' ) {
111
- $unit->numerator = array_merge( $unit->numerator, $other->unit->denominator );
112
- $unit->denominator = array_merge( $unit->denominator, $other->unit->numerator );
113
- sort( $unit->numerator );
114
- sort( $unit->denominator );
115
  $unit->cancel();
116
  }
117
- return new Less_Tree_Dimension( $value, $unit );
118
- }
119
 
120
- public function compare( $other ) {
121
- if ( $other instanceof Less_Tree_Dimension ) {
122
 
123
- if ( $this->unit->isEmpty() || $other->unit->isEmpty() ) {
124
  $a = $this;
125
  $b = $other;
126
  } else {
127
  $a = $this->unify();
128
  $b = $other->unify();
129
- if ( $a->unit->compare( $b->unit ) !== 0 ) {
130
  return -1;
131
  }
132
  }
133
  $aValue = $a->value;
134
  $bValue = $b->value;
135
 
136
- if ( $bValue > $aValue ) {
137
  return -1;
138
- } elseif ( $bValue < $aValue ) {
139
  return 1;
140
  } else {
141
  return 0;
@@ -145,47 +147,48 @@ class Less_Tree_Dimension extends Less_Tree {
145
  }
146
  }
147
 
148
- public function unify() {
149
- return $this->convertTo( array( 'length' => 'px', 'duration' => 's', 'angle' => 'rad' ) );
150
  }
151
 
152
- public function convertTo( $conversions ) {
153
  $value = $this->value;
154
  $unit = clone $this->unit;
155
 
156
- if ( is_string( $conversions ) ) {
157
  $derivedConversions = array();
158
- foreach ( Less_Tree_UnitConversions::$groups as $i ) {
159
- if ( isset( Less_Tree_UnitConversions::${$i}[$conversions] ) ) {
160
- $derivedConversions = array( $i => $conversions );
161
  }
162
  }
163
  $conversions = $derivedConversions;
164
  }
165
 
166
- foreach ( $conversions as $groupName => $targetUnit ) {
 
167
  $group = Less_Tree_UnitConversions::${$groupName};
168
 
169
- // numerator
170
- foreach ( $unit->numerator as $i => $atomicUnit ) {
171
  $atomicUnit = $unit->numerator[$i];
172
- if ( !isset( $group[$atomicUnit] ) ) {
173
  continue;
174
  }
175
 
176
- $value = $value * ( $group[$atomicUnit] / $group[$targetUnit] );
177
 
178
  $unit->numerator[$i] = $targetUnit;
179
  }
180
 
181
- // denominator
182
- foreach ( $unit->denominator as $i => $atomicUnit ) {
183
  $atomicUnit = $unit->denominator[$i];
184
- if ( !isset( $group[$atomicUnit] ) ) {
185
  continue;
186
  }
187
 
188
- $value = $value / ( $group[$atomicUnit] / $group[$targetUnit] );
189
 
190
  $unit->denominator[$i] = $targetUnit;
191
  }
@@ -193,6 +196,6 @@ class Less_Tree_Dimension extends Less_Tree {
193
 
194
  $unit->cancel();
195
 
196
- return new Less_Tree_Dimension( $value, $unit );
197
- }
198
  }
6
  * @package Less
7
  * @subpackage tree
8
  */
9
+ class Less_Tree_Dimension extends Less_Tree{
10
 
11
  public $value;
12
  public $unit;
13
  public $type = 'Dimension';
14
 
15
+ public function __construct($value, $unit = null){
16
+ $this->value = floatval($value);
17
 
18
+ if( $unit && ($unit instanceof Less_Tree_Unit) ){
19
  $this->unit = $unit;
20
+ }elseif( $unit ){
21
+ $this->unit = new Less_Tree_Unit( array($unit) );
22
+ }else{
23
+ $this->unit = new Less_Tree_Unit( );
24
  }
25
+ }
26
 
27
+ public function accept( $visitor ){
28
  $this->unit = $visitor->visitObj( $this->unit );
29
  }
30
 
31
+ public function compile(){
32
+ return $this;
33
+ }
34
 
35
+ public function toColor() {
36
+ return new Less_Tree_Color(array($this->value, $this->value, $this->value));
37
+ }
38
+
39
+ /**
40
+ * @see Less_Tree::genCSS
41
+ */
42
+ public function genCSS( $output ){
43
 
44
+ if( Less_Parser::$options['strictUnits'] && !$this->unit->isSingular() ){
45
+ throw new Less_Exception_Compiler("Multiple units in dimension. Correct the units or use the unit function. Bad unit: ".$this->unit->toString());
 
 
 
 
46
  }
47
 
48
  $value = Less_Functions::fround( $this->value );
49
  $strValue = (string)$value;
50
 
51
+ if( $value !== 0 && $value < 0.000001 && $value > -0.000001 ){
52
  // would be output 1e-6 etc.
53
+ $strValue = number_format($strValue,10);
54
+ $strValue = preg_replace('/\.?0+$/','', $strValue);
55
  }
56
 
57
+ if( Less_Parser::$options['compress'] ){
58
  // Zero values doesn't need a unit
59
+ if( $value === 0 && $this->unit->isLength() ){
60
  $output->add( $strValue );
61
  return $strValue;
62
  }
63
 
64
  // Float values doesn't need a leading zero
65
+ if( $value > 0 && $value < 1 && $strValue[0] === '0' ){
66
+ $strValue = substr($strValue,1);
67
  }
68
  }
69
 
71
  $this->unit->genCSS( $output );
72
  }
73
 
74
+ public function __toString(){
75
+ return $this->toCSS();
76
+ }
77
 
78
+ // In an operation between two Dimensions,
79
+ // we default to the first Dimension's unit,
80
+ // so `1px + 2em` will yield `3px`.
81
 
82
+ /**
83
+ * @param string $op
84
+ */
85
+ public function operate( $op, $other){
86
+
87
+ $value = Less_Functions::operate( $op, $this->value, $other->value);
88
  $unit = clone $this->unit;
89
 
90
+ if( $op === '+' || $op === '-' ){
91
 
92
+ if( !$unit->numerator && !$unit->denominator ){
93
  $unit->numerator = $other->unit->numerator;
94
  $unit->denominator = $other->unit->denominator;
95
+ }elseif( !$other->unit->numerator && !$other->unit->denominator ){
96
  // do nothing
97
+ }else{
98
+ $other = $other->convertTo( $this->unit->usedUnits());
99
 
100
+ if( Less_Parser::$options['strictUnits'] && $other->unit->toString() !== $unit->toCSS() ){
101
+ throw new Less_Exception_Compiler("Incompatible units. Change the units or use the unit function. Bad units: '" . $unit->toString() . "' and " . $other->unit->toString() . "'.");
102
  }
103
 
104
+ $value = Less_Functions::operate( $op, $this->value, $other->value);
105
  }
106
+ }elseif( $op === '*' ){
107
+ $unit->numerator = array_merge($unit->numerator, $other->unit->numerator);
108
+ $unit->denominator = array_merge($unit->denominator, $other->unit->denominator);
109
+ sort($unit->numerator);
110
+ sort($unit->denominator);
111
  $unit->cancel();
112
+ }elseif( $op === '/' ){
113
+ $unit->numerator = array_merge($unit->numerator, $other->unit->denominator);
114
+ $unit->denominator = array_merge($unit->denominator, $other->unit->numerator);
115
+ sort($unit->numerator);
116
+ sort($unit->denominator);
117
  $unit->cancel();
118
  }
119
+ return new Less_Tree_Dimension( $value, $unit);
120
+ }
121
 
122
+ public function compare($other) {
123
+ if ($other instanceof Less_Tree_Dimension) {
124
 
125
+ if( $this->unit->isEmpty() || $other->unit->isEmpty() ){
126
  $a = $this;
127
  $b = $other;
128
  } else {
129
  $a = $this->unify();
130
  $b = $other->unify();
131
+ if( $a->unit->compare($b->unit) !== 0 ){
132
  return -1;
133
  }
134
  }
135
  $aValue = $a->value;
136
  $bValue = $b->value;
137
 
138
+ if ($bValue > $aValue) {
139
  return -1;
140
+ } elseif ($bValue < $aValue) {
141
  return 1;
142
  } else {
143
  return 0;
147
  }
148
  }
149
 
150
+ public function unify() {
151
+ return $this->convertTo(array('length'=> 'px', 'duration'=> 's', 'angle' => 'rad' ));
152
  }
153
 
154
+ public function convertTo($conversions) {
155
  $value = $this->value;
156
  $unit = clone $this->unit;
157
 
158
+ if( is_string($conversions) ){
159
  $derivedConversions = array();
160
+ foreach( Less_Tree_UnitConversions::$groups as $i ){
161
+ if( isset(Less_Tree_UnitConversions::${$i}[$conversions]) ){
162
+ $derivedConversions = array( $i => $conversions);
163
  }
164
  }
165
  $conversions = $derivedConversions;
166
  }
167
 
168
+
169
+ foreach($conversions as $groupName => $targetUnit){
170
  $group = Less_Tree_UnitConversions::${$groupName};
171
 
172
+ //numerator
173
+ foreach($unit->numerator as $i => $atomicUnit){
174
  $atomicUnit = $unit->numerator[$i];
175
+ if( !isset($group[$atomicUnit]) ){
176
  continue;
177
  }
178
 
179
+ $value = $value * ($group[$atomicUnit] / $group[$targetUnit]);
180
 
181
  $unit->numerator[$i] = $targetUnit;
182
  }
183
 
184
+ //denominator
185
+ foreach($unit->denominator as $i => $atomicUnit){
186
  $atomicUnit = $unit->denominator[$i];
187
+ if( !isset($group[$atomicUnit]) ){
188
  continue;
189
  }
190
 
191
+ $value = $value / ($group[$atomicUnit] / $group[$targetUnit]);
192
 
193
  $unit->denominator[$i] = $targetUnit;
194
  }
196
 
197
  $unit->cancel();
198
 
199
+ return new Less_Tree_Dimension( $value, $unit);
200
+ }
201
  }
base/inc/lib/Less/Tree/Directive.php CHANGED
@@ -6,7 +6,7 @@
6
  * @package Less
7
  * @subpackage tree
8
  */
9
- class Less_Tree_Directive extends Less_Tree {
10
 
11
  public $name;
12
  public $value;
@@ -17,10 +17,10 @@ class Less_Tree_Directive extends Less_Tree {
17
  public $debugInfo;
18
  public $type = 'Directive';
19
 
20
- public function __construct( $name, $value = null, $rules = null, $index = null, $currentFileInfo = null, $debugInfo = null ) {
21
  $this->name = $name;
22
  $this->value = $value;
23
- if ( $rules ) {
24
  $this->rules = $rules;
25
  $this->rules->allowImports = true;
26
  }
@@ -30,66 +30,70 @@ class Less_Tree_Directive extends Less_Tree {
30
  $this->debugInfo = $debugInfo;
31
  }
32
 
33
- public function accept( $visitor ) {
34
- if ( $this->rules ) {
 
35
  $this->rules = $visitor->visitObj( $this->rules );
36
  }
37
- if ( $this->value ) {
38
  $this->value = $visitor->visitObj( $this->value );
39
  }
40
  }
41
 
42
- /**
43
- * @see Less_Tree::genCSS
44
- */
45
- public function genCSS( $output ) {
 
46
  $value = $this->value;
47
  $rules = $this->rules;
48
  $output->add( $this->name, $this->currentFileInfo, $this->index );
49
- if ( $this->value ) {
50
- $output->add( ' ' );
51
- $this->value->genCSS( $output );
52
  }
53
- if ( $this->rules ) {
54
- Less_Tree::outputRuleset( $output, array( $this->rules ) );
55
  } else {
56
- $output->add( ';' );
57
  }
58
  }
59
 
60
- public function compile( $env ) {
 
61
  $value = $this->value;
62
  $rules = $this->rules;
63
- if ( $value ) {
64
- $value = $value->compile( $env );
65
  }
66
 
67
- if ( $rules ) {
68
- $rules = $rules->compile( $env );
69
  $rules->root = true;
70
  }
71
 
72
  return new Less_Tree_Directive( $this->name, $value, $rules, $this->index, $this->currentFileInfo, $this->debugInfo );
73
  }
74
 
75
- public function variable( $name ) {
76
- if ( $this->rules ) {
77
- return $this->rules->variable( $name );
 
78
  }
79
  }
80
 
81
- public function find( $selector ) {
82
- if ( $this->rules ) {
83
- return $this->rules->find( $selector, $this );
84
  }
85
  }
86
 
87
- // rulesets: function () { if (this.rules) return tree.Ruleset.prototype.rulesets.apply(this.rules); },
88
 
89
- public function markReferenced() {
90
  $this->isReferenced = true;
91
- if ( $this->rules ) {
92
- Less_Tree::ReferencedArray( $this->rules->rules );
93
  }
94
  }
95
 
6
  * @package Less
7
  * @subpackage tree
8
  */
9
+ class Less_Tree_Directive extends Less_Tree{
10
 
11
  public $name;
12
  public $value;
17
  public $debugInfo;
18
  public $type = 'Directive';
19
 
20
+ public function __construct($name, $value = null, $rules = null, $index = null, $currentFileInfo = null, $debugInfo = null ){
21
  $this->name = $name;
22
  $this->value = $value;
23
+ if( $rules ){
24
  $this->rules = $rules;
25
  $this->rules->allowImports = true;
26
  }
30
  $this->debugInfo = $debugInfo;
31
  }
32
 
33
+
34
+ public function accept( $visitor ){
35
+ if( $this->rules ){
36
  $this->rules = $visitor->visitObj( $this->rules );
37
  }
38
+ if( $this->value ){
39
  $this->value = $visitor->visitObj( $this->value );
40
  }
41
  }
42
 
43
+
44
+ /**
45
+ * @see Less_Tree::genCSS
46
+ */
47
+ public function genCSS( $output ){
48
  $value = $this->value;
49
  $rules = $this->rules;
50
  $output->add( $this->name, $this->currentFileInfo, $this->index );
51
+ if( $this->value ){
52
+ $output->add(' ');
53
+ $this->value->genCSS($output);
54
  }
55
+ if( $this->rules ){
56
+ Less_Tree::outputRuleset( $output, array($this->rules));
57
  } else {
58
+ $output->add(';');
59
  }
60
  }
61
 
62
+ public function compile($env){
63
+
64
  $value = $this->value;
65
  $rules = $this->rules;
66
+ if( $value ){
67
+ $value = $value->compile($env);
68
  }
69
 
70
+ if( $rules ){
71
+ $rules = $rules->compile($env);
72
  $rules->root = true;
73
  }
74
 
75
  return new Less_Tree_Directive( $this->name, $value, $rules, $this->index, $this->currentFileInfo, $this->debugInfo );
76
  }
77
 
78
+
79
+ public function variable($name){
80
+ if( $this->rules ){
81
+ return $this->rules->variable($name);
82
  }
83
  }
84
 
85
+ public function find($selector){
86
+ if( $this->rules ){
87
+ return $this->rules->find($selector, $this);
88
  }
89
  }
90
 
91
+ //rulesets: function () { if (this.rules) return tree.Ruleset.prototype.rulesets.apply(this.rules); },
92
 
93
+ public function markReferenced(){
94
  $this->isReferenced = true;
95
+ if( $this->rules ){
96
+ Less_Tree::ReferencedArray($this->rules->rules);
97
  }
98
  }
99
 
base/inc/lib/Less/Tree/Element.php CHANGED
@@ -6,7 +6,7 @@
6
  * @package Less
7
  * @subpackage tree
8
  */
9
- class Less_Tree_Element extends Less_Tree {
10
 
11
  public $combinator = '';
12
  public $value = '';
@@ -16,11 +16,12 @@ class Less_Tree_Element extends Less_Tree {
16
 
17
  public $value_is_object = false;
18
 
19
- public function __construct( $combinator, $value, $index = null, $currentFileInfo = null ) {
 
20
  $this->value = $value;
21
- $this->value_is_object = is_object( $value );
22
 
23
- if ( $combinator ) {
24
  $this->combinator = $combinator;
25
  }
26
 
@@ -28,42 +29,46 @@ class Less_Tree_Element extends Less_Tree {
28
  $this->currentFileInfo = $currentFileInfo;
29
  }
30
 
31
- public function accept( $visitor ) {
32
- if ( $this->value_is_object ) { // object or string
33
  $this->value = $visitor->visitObj( $this->value );
34
  }
35
  }
36
 
37
- public function compile( $env ) {
38
- if ( Less_Environment::$mixin_stack ) {
39
- return new Less_Tree_Element( $this->combinator, ( $this->value_is_object ? $this->value->compile( $env ) : $this->value ), $this->index, $this->currentFileInfo );
 
40
  }
41
 
42
- if ( $this->value_is_object ) {
43
- $this->value = $this->value->compile( $env );
44
  }
45
 
46
  return $this;
47
  }
48
 
49
- /**
50
- * @see Less_Tree::genCSS
51
- */
52
- public function genCSS( $output ) {
53
  $output->add( $this->toCSS(), $this->currentFileInfo, $this->index );
54
  }
55
 
56
- public function toCSS() {
57
- if ( $this->value_is_object ) {
 
58
  $value = $this->value->toCSS();
59
- } else {
60
  $value = $this->value;
61
  }
62
 
63
- if ( $value === '' && $this->combinator && $this->combinator === '&' ) {
 
64
  return '';
65
  }
66
 
 
67
  return Less_Environment::$_outputMap[$this->combinator] . $value;
68
  }
69
 
6
  * @package Less
7
  * @subpackage tree
8
  */
9
+ class Less_Tree_Element extends Less_Tree{
10
 
11
  public $combinator = '';
12
  public $value = '';
16
 
17
  public $value_is_object = false;
18
 
19
+ public function __construct($combinator, $value, $index = null, $currentFileInfo = null ){
20
+
21
  $this->value = $value;
22
+ $this->value_is_object = is_object($value);
23
 
24
+ if( $combinator ){
25
  $this->combinator = $combinator;
26
  }
27
 
29
  $this->currentFileInfo = $currentFileInfo;
30
  }
31
 
32
+ public function accept( $visitor ){
33
+ if( $this->value_is_object ){ //object or string
34
  $this->value = $visitor->visitObj( $this->value );
35
  }
36
  }
37
 
38
+ public function compile($env){
39
+
40
+ if( Less_Environment::$mixin_stack ){
41
+ return new Less_Tree_Element($this->combinator, ($this->value_is_object ? $this->value->compile($env) : $this->value), $this->index, $this->currentFileInfo );
42
  }
43
 
44
+ if( $this->value_is_object ){
45
+ $this->value = $this->value->compile($env);
46
  }
47
 
48
  return $this;
49
  }
50
 
51
+ /**
52
+ * @see Less_Tree::genCSS
53
+ */
54
+ public function genCSS( $output ){
55
  $output->add( $this->toCSS(), $this->currentFileInfo, $this->index );
56
  }
57
 
58
+ public function toCSS(){
59
+
60
+ if( $this->value_is_object ){
61
  $value = $this->value->toCSS();
62
+ }else{
63
  $value = $this->value;
64
  }
65
 
66
+
67
+ if( $value === '' && $this->combinator && $this->combinator === '&' ){
68
  return '';
69
  }
70
 
71
+
72
  return Less_Environment::$_outputMap[$this->combinator] . $value;
73
  }
74
 
base/inc/lib/Less/Tree/Expression.php CHANGED
@@ -6,85 +6,87 @@
6
  * @package Less
7
  * @subpackage tree
8
  */
9
- class Less_Tree_Expression extends Less_Tree {
10
 
11
  public $value = array();
12
  public $parens = false;
13
  public $parensInOp = false;
14
  public $type = 'Expression';
15
 
16
- public function __construct( $value, $parens = null ) {
17
  $this->value = $value;
18
  $this->parens = $parens;
19
  }
20
 
21
- public function accept( $visitor ) {
22
  $this->value = $visitor->visitArray( $this->value );
23
  }
24
 
25
- public function compile( $env ) {
 
26
  $doubleParen = false;
27
 
28
- if ( $this->parens && !$this->parensInOp ) {
29
  Less_Environment::$parensStack++;
30
  }
31
 
32
  $returnValue = null;
33
- if ( $this->value ) {
34
 
35
- $count = count( $this->value );
36
 
37
- if ( $count > 1 ) {
38
 
39
  $ret = array();
40
- foreach ( $this->value as $e ) {
41
- $ret[] = $e->compile( $env );
42
  }
43
- $returnValue = new Less_Tree_Expression( $ret );
44
 
45
- } else {
46
 
47
- if ( ( $this->value[0] instanceof Less_Tree_Expression ) && $this->value[0]->parens && !$this->value[0]->parensInOp ) {
48
  $doubleParen = true;
49
  }
50
 
51
- $returnValue = $this->value[0]->compile( $env );
52
  }
53
 
54
  } else {
55
  $returnValue = $this;
56
  }
57
 
58
- if ( $this->parens ) {
59
- if ( !$this->parensInOp ) {
60
  Less_Environment::$parensStack--;
61
 
62
- } elseif ( !Less_Environment::isMathOn() && !$doubleParen ) {
63
- $returnValue = new Less_Tree_Paren( $returnValue );
64
 
65
  }
66
  }
67
  return $returnValue;
68
  }
69
 
70
- /**
71
- * @see Less_Tree::genCSS
72
- */
73
- public function genCSS( $output ) {
74
- $val_len = count( $this->value );
75
- for ( $i = 0; $i < $val_len; $i++ ) {
76
  $this->value[$i]->genCSS( $output );
77
- if ( $i + 1 < $val_len ) {
78
  $output->add( ' ' );
79
  }
80
  }
81
  }
82
 
83
- public function throwAwayComments() {
84
- if ( is_array( $this->value ) ) {
 
85
  $new_value = array();
86
- foreach ( $this->value as $v ) {
87
- if ( $v instanceof Less_Tree_Comment ) {
88
  continue;
89
  }
90
  $new_value[] = $v;
6
  * @package Less
7
  * @subpackage tree
8
  */
9
+ class Less_Tree_Expression extends Less_Tree{
10
 
11
  public $value = array();
12
  public $parens = false;
13
  public $parensInOp = false;
14
  public $type = 'Expression';
15
 
16
+ public function __construct( $value, $parens = null ){
17
  $this->value = $value;
18
  $this->parens = $parens;
19
  }
20
 
21
+ public function accept( $visitor ){
22
  $this->value = $visitor->visitArray( $this->value );
23
  }
24
 
25
+ public function compile($env) {
26
+
27
  $doubleParen = false;
28
 
29
+ if( $this->parens && !$this->parensInOp ){
30
  Less_Environment::$parensStack++;
31
  }
32
 
33
  $returnValue = null;
34
+ if( $this->value ){
35
 
36
+ $count = count($this->value);
37
 
38
+ if( $count > 1 ){
39
 
40
  $ret = array();
41
+ foreach($this->value as $e){
42
+ $ret[] = $e->compile($env);
43
  }
44
+ $returnValue = new Less_Tree_Expression($ret);
45
 
46
+ }else{
47
 
48
+ if( ($this->value[0] instanceof Less_Tree_Expression) && $this->value[0]->parens && !$this->value[0]->parensInOp ){
49
  $doubleParen = true;
50
  }
51
 
52
+ $returnValue = $this->value[0]->compile($env);
53
  }
54
 
55
  } else {
56
  $returnValue = $this;
57
  }
58
 
59
+ if( $this->parens ){
60
+ if( !$this->parensInOp ){
61
  Less_Environment::$parensStack--;
62
 
63
+ }elseif( !Less_Environment::isMathOn() && !$doubleParen ){
64
+ $returnValue = new Less_Tree_Paren($returnValue);
65
 
66
  }
67
  }
68
  return $returnValue;
69
  }
70
 
71
+ /**
72
+ * @see Less_Tree::genCSS
73
+ */
74
+ public function genCSS( $output ){
75
+ $val_len = count($this->value);
76
+ for( $i = 0; $i < $val_len; $i++ ){
77
  $this->value[$i]->genCSS( $output );
78
+ if( $i + 1 < $val_len ){
79
  $output->add( ' ' );
80
  }
81
  }
82
  }
83
 
84
+ public function throwAwayComments() {
85
+
86
+ if( is_array($this->value) ){
87
  $new_value = array();
88
+ foreach($this->value as $v){
89
+ if( $v instanceof Less_Tree_Comment ){
90
  continue;
91
  }
92
  $new_value[] = $v;
base/inc/lib/Less/Tree/Extend.php CHANGED
@@ -6,7 +6,7 @@
6
  * @package Less
7
  * @subpackage tree
8
  */
9
- class Less_Tree_Extend extends Less_Tree {
10
 
11
  public $selector;
12
  public $option;
@@ -18,19 +18,20 @@ class Less_Tree_Extend extends Less_Tree {
18
  public $type = 'Extend';
19
  public $ruleset;
20
 
 
21
  public $object_id;
22
  public $parent_ids = array();
23
 
24
  /**
25
  * @param integer $index
26
  */
27
- public function __construct( $selector, $option, $index ) {
28
  static $i = 0;
29
  $this->selector = $selector;
30
  $this->option = $option;
31
  $this->index = $index;
32
 
33
- switch ( $option ) {
34
  case "all":
35
  $this->allowBefore = true;
36
  $this->allowAfter = true;
@@ -50,31 +51,32 @@ class Less_Tree_Extend extends Less_Tree {
50
  );
51
  }
52
 
53
- public function accept( $visitor ) {
54
  $this->selector = $visitor->visitObj( $this->selector );
55
  }
56
 
57
- public function compile( $env ) {
58
  Less_Parser::$has_extends = true;
59
- $this->selector = $this->selector->compile( $env );
60
  return $this;
61
- // return new Less_Tree_Extend( $this->selector->compile($env), $this->option, $this->index);
62
  }
63
 
64
- public function findSelfSelectors( $selectors ) {
65
  $selfElements = array();
66
 
67
- for ( $i = 0, $selectors_len = count( $selectors ); $i < $selectors_len; $i++ ) {
 
68
  $selectorElements = $selectors[$i]->elements;
69
  // duplicate the logic in genCSS function inside the selector node.
70
  // future TODO - move both logics into the selector joiner visitor
71
- if ( $i && $selectorElements && $selectorElements[0]->combinator === "" ) {
72
  $selectorElements[0]->combinator = ' ';
73
  }
74
  $selfElements = array_merge( $selfElements, $selectors[$i]->elements );
75
  }
76
 
77
- $this->selfSelectors = array( new Less_Tree_Selector( $selfElements ) );
78
  }
79
 
80
  }
6
  * @package Less
7
  * @subpackage tree
8
  */
9
+ class Less_Tree_Extend extends Less_Tree{
10
 
11
  public $selector;
12
  public $option;
18
  public $type = 'Extend';
19
  public $ruleset;
20
 
21
+
22
  public $object_id;
23
  public $parent_ids = array();
24
 
25
  /**
26
  * @param integer $index
27
  */
28
+ public function __construct($selector, $option, $index){
29
  static $i = 0;
30
  $this->selector = $selector;
31
  $this->option = $option;
32
  $this->index = $index;
33
 
34
+ switch($option){
35
  case "all":
36
  $this->allowBefore = true;
37
  $this->allowAfter = true;
51
  );
52
  }
53
 
54
+ public function accept( $visitor ){
55
  $this->selector = $visitor->visitObj( $this->selector );
56
  }
57
 
58
+ public function compile( $env ){
59
  Less_Parser::$has_extends = true;
60
+ $this->selector = $this->selector->compile($env);
61
  return $this;
62
+ //return new Less_Tree_Extend( $this->selector->compile($env), $this->option, $this->index);
63
  }
64
 
65
+ public function findSelfSelectors( $selectors ){
66
  $selfElements = array();
67
 
68
+
69
+ for( $i = 0, $selectors_len = count($selectors); $i < $selectors_len; $i++ ){
70
  $selectorElements = $selectors[$i]->elements;
71
  // duplicate the logic in genCSS function inside the selector node.
72
  // future TODO - move both logics into the selector joiner visitor
73
+ if( $i && $selectorElements && $selectorElements[0]->combinator === "") {
74
  $selectorElements[0]->combinator = ' ';
75
  }
76
  $selfElements = array_merge( $selfElements, $selectors[$i]->elements );
77
  }
78
 
79
+ $this->selfSelectors = array(new Less_Tree_Selector($selfElements));
80
  }
81
 
82
  }
base/inc/lib/Less/Tree/Import.php CHANGED
@@ -15,7 +15,7 @@
15
  * @package Less
16
  * @subpackage tree
17
  */
18
- class Less_Tree_Import extends Less_Tree {
19
 
20
  public $options;
21
  public $index;
@@ -27,21 +27,21 @@ class Less_Tree_Import extends Less_Tree {
27
  public $root;
28
  public $type = 'Import';
29
 
30
- public function __construct( $path, $features, $options, $index, $currentFileInfo = null ) {
31
  $this->options = $options;
32
  $this->index = $index;
33
  $this->path = $path;
34
  $this->features = $features;
35
  $this->currentFileInfo = $currentFileInfo;
36
 
37
- if ( is_array( $options ) ) {
38
- $this->options += array( 'inline' => false );
39
 
40
- if ( isset( $this->options['less'] ) || $this->options['inline'] ) {
41
- $this->css = !isset( $this->options['less'] ) || !$this->options['less'] || $this->options['inline'];
42
  } else {
43
  $pathValue = $this->getPath();
44
- if ( $pathValue && preg_match( '/css([\?;].*)?$/', $pathValue ) ) {
45
  $this->css = true;
46
  }
47
  }
@@ -58,27 +58,28 @@ class Less_Tree_Import extends Less_Tree {
58
  // ruleset.
59
  //
60
 
61
- public function accept( $visitor ) {
62
- if ( $this->features ) {
63
- $this->features = $visitor->visitObj( $this->features );
 
64
  }
65
- $this->path = $visitor->visitObj( $this->path );
66
 
67
- if ( !$this->options['inline'] && $this->root ) {
68
- $this->root = $visitor->visit( $this->root );
69
  }
70
  }
71
 
72
- /**
73
- * @see Less_Tree::genCSS
74
- */
75
- public function genCSS( $output ) {
76
- if ( $this->css ) {
77
 
78
  $output->add( '@import ', $this->currentFileInfo, $this->index );
79
 
80
  $this->path->genCSS( $output );
81
- if ( $this->features ) {
82
  $output->add( ' ' );
83
  $this->features->genCSS( $output );
84
  }
@@ -86,10 +87,10 @@ class Less_Tree_Import extends Less_Tree {
86
  }
87
  }
88
 
89
- public function toCSS() {
90
  $features = $this->features ? ' ' . $this->features->toCSS() : '';
91
 
92
- if ( $this->css ) {
93
  return "@import " . $this->path->toCSS() . $features . ";\n";
94
  } else {
95
  return "";
@@ -99,121 +100,130 @@ class Less_Tree_Import extends Less_Tree {
99
  /**
100
  * @return string
101
  */
102
- public function getPath() {
103
- if ( $this->path instanceof Less_Tree_Quoted ) {
104
  $path = $this->path->value;
105
- $path = ( isset( $this->css ) || preg_match( '/(\.[a-z]*$)|([\?;].*)$/', $path ) ) ? $path : $path . '.less';
106
- } else if ( $this->path instanceof Less_Tree_URL ) {
107
  $path = $this->path->value->value;
108
- } else {
109
  return null;
110
  }
111
 
112
- // remove query string and fragment
113
- return preg_replace( '/[\?#][^\?]*$/', '', $path );
114
  }
115
 
116
- public function compileForImport( $env ) {
117
- return new Less_Tree_Import( $this->path->compile( $env ), $this->features, $this->options, $this->index, $this->currentFileInfo );
118
  }
119
 
120
- public function compilePath( $env ) {
121
- $path = $this->path->compile( $env );
122
  $rootpath = '';
123
- if ( $this->currentFileInfo && $this->currentFileInfo['rootpath'] ) {
124
  $rootpath = $this->currentFileInfo['rootpath'];
125
  }
126
 
127
- if ( !( $path instanceof Less_Tree_URL ) ) {
128
- if ( $rootpath ) {
 
129
  $pathValue = $path->value;
130
  // Add the base path if the import is relative
131
- if ( $pathValue && Less_Environment::isPathRelative( $pathValue ) ) {
132
  $path->value = $this->currentFileInfo['uri_root'].$pathValue;
133
  }
134
  }
135
- $path->value = Less_Environment::normalizePath( $path->value );
136
  }
137
 
 
 
138
  return $path;
139
  }
140
 
141
- public function compile( $env ) {
142
- $evald = $this->compileForImport( $env );
 
143
 
144
- // get path & uri
145
  $path_and_uri = null;
146
- if ( is_callable( Less_Parser::$options['import_callback'] ) ) {
147
- $path_and_uri = call_user_func( Less_Parser::$options['import_callback'], $evald );
148
  }
149
 
150
- if ( !$path_and_uri ) {
151
  $path_and_uri = $evald->PathAndUri();
152
  }
153
 
154
- if ( $path_and_uri ) {
155
- list( $full_path, $uri ) = $path_and_uri;
156
- } else {
157
  $full_path = $uri = $evald->getPath();
158
  }
159
 
160
- // import once
161
- if ( $evald->skip( $full_path, $env ) ) {
 
162
  return array();
163
  }
164
 
165
- if ( $this->options['inline'] ) {
166
- // todo needs to reference css file not import
167
  //$contents = new Less_Tree_Anonymous($this->root, 0, array('filename'=>$this->importedFilename), true );
168
 
169
- Less_Parser::AddParsedFile( $full_path );
170
- $contents = new Less_Tree_Anonymous( file_get_contents( $full_path ), 0, array(), true );
171
 
172
- if ( $this->features ) {
173
- return new Less_Tree_Media( array( $contents ), $this->features->value );
174
  }
175
 
176
  return array( $contents );
177
  }
178
 
179
  // optional (need to be before "CSS" to support optional CSS imports. CSS should be checked only if empty($this->currentFileInfo))
180
- if ( isset( $this->options['optional'] ) && $this->options['optional'] && !file_exists( $full_path ) && ( !$evald->css || !empty( $this->currentFileInfo ) ) ) {
181
  return array();
182
  }
183
 
 
184
  // css ?
185
- if ( $evald->css ) {
186
- $features = ( $evald->features ? $evald->features->compile( $env ) : null );
187
- return new Less_Tree_Import( $this->compilePath( $env ), $features, $this->options, $this->index );
188
  }
189
 
 
190
  return $this->ParseImport( $full_path, $uri, $env );
191
  }
192
 
 
193
  /**
194
  * Using the import directories, get the full absolute path and uri of the import
195
  *
196
  * @param Less_Tree_Import $evald
197
  */
198
- public function PathAndUri() {
 
199
  $evald_path = $this->getPath();
200
 
201
- if ( $evald_path ) {
202
 
203
  $import_dirs = array();
204
 
205
- if ( Less_Environment::isPathRelative( $evald_path ) ) {
206
- // if the path is relative, the file should be in the current directory
207
- if ( $this->currentFileInfo ) {
208
  $import_dirs[ $this->currentFileInfo['currentDirectory'] ] = $this->currentFileInfo['uri_root'];
209
  }
210
 
211
- } else {
212
- // otherwise, the file should be relative to the server root
213
- if ( $this->currentFileInfo ) {
214
  $import_dirs[ $this->currentFileInfo['entryPath'] ] = $this->currentFileInfo['entryUri'];
215
  }
216
- // if the user supplied entryPath isn't the actual root
217
  $import_dirs[ $_SERVER['DOCUMENT_ROOT'] ] = '';
218
 
219
  }
@@ -221,24 +231,25 @@ class Less_Tree_Import extends Less_Tree {
221
  // always look in user supplied import directories
222
  $import_dirs = array_merge( $import_dirs, Less_Parser::$options['import_dirs'] );
223
 
224
- foreach ( $import_dirs as $rootpath => $rooturi ) {
225
- if ( is_callable( $rooturi ) ) {
226
- list( $path, $uri ) = call_user_func( $rooturi, $evald_path );
227
- if ( is_string( $path ) ) {
 
228
  $full_path = $path;
229
  return array( $full_path, $uri );
230
  }
231
- } elseif ( !empty( $rootpath ) ) {
232
 
233
- $path = rtrim( $rootpath, '/\\' ).'/'.ltrim( $evald_path, '/\\' );
234
 
235
- if ( file_exists( $path ) ) {
236
- $full_path = Less_Environment::normalizePath( $path );
237
- $uri = Less_Environment::normalizePath( dirname( $rooturi.$evald_path ) );
238
  return array( $full_path, $uri );
239
- } elseif ( file_exists( $path.'.less' ) ) {
240
- $full_path = Less_Environment::normalizePath( $path.'.less' );
241
- $uri = Less_Environment::normalizePath( dirname( $rooturi.$evald_path.'.less' ) );
242
  return array( $full_path, $uri );
243
  }
244
  }
@@ -246,45 +257,50 @@ class Less_Tree_Import extends Less_Tree {
246
  }
247
  }
248
 
 
249
  /**
250
  * Parse the import url and return the rules
251
  *
252
  * @return Less_Tree_Media|array
253
  */
254
- public function ParseImport( $full_path, $uri, $env ) {
 
255
  $import_env = clone $env;
256
- if ( ( isset( $this->options['reference'] ) && $this->options['reference'] ) || isset( $this->currentFileInfo['reference'] ) ) {
257
  $import_env->currentFileInfo['reference'] = true;
258
  }
259
 
260
- if ( ( isset( $this->options['multiple'] ) && $this->options['multiple'] ) ) {
261
  $import_env->importMultiple = true;
262
  }
263
 
264
- $parser = new Less_Parser( $import_env );
265
- $root = $parser->parseFile( $full_path, $uri, true );
 
266
 
267
- $ruleset = new Less_Tree_Ruleset( array(), $root->rules );
268
- $ruleset->evalImports( $import_env );
269
 
270
- return $this->features ? new Less_Tree_Media( $ruleset->rules, $this->features->value ) : $ruleset->rules;
271
  }
272
 
 
273
  /**
274
  * Should the import be skipped?
275
  *
276
  * @return boolean|null
277
  */
278
- private function Skip( $path, $env ) {
279
- $path = Less_Parser::AbsPath( $path, true );
 
280
 
281
- if ( $path && Less_Parser::FileParsed( $path ) ) {
282
 
283
- if ( isset( $this->currentFileInfo['reference'] ) ) {
284
  return true;
285
  }
286
 
287
- return !isset( $this->options['multiple'] ) && !$env->importMultiple;
288
  }
289
 
290
  }
15
  * @package Less
16
  * @subpackage tree
17
  */
18
+ class Less_Tree_Import extends Less_Tree{
19
 
20
  public $options;
21
  public $index;
27
  public $root;
28
  public $type = 'Import';
29
 
30
+ public function __construct($path, $features, $options, $index, $currentFileInfo = null ){
31
  $this->options = $options;
32
  $this->index = $index;
33
  $this->path = $path;
34
  $this->features = $features;
35
  $this->currentFileInfo = $currentFileInfo;
36
 
37
+ if( is_array($options) ){
38
+ $this->options += array('inline'=>false);
39
 
40
+ if( isset($this->options['less']) || $this->options['inline'] ){
41
+ $this->css = !isset($this->options['less']) || !$this->options['less'] || $this->options['inline'];
42
  } else {
43
  $pathValue = $this->getPath();
44
+ if( $pathValue && preg_match('/css([\?;].*)?$/',$pathValue) ){
45
  $this->css = true;
46
  }
47
  }
58
  // ruleset.
59
  //
60
 
61
+ public function accept($visitor){
62
+
63
+ if( $this->features ){
64
+ $this->features = $visitor->visitObj($this->features);
65
  }
66
+ $this->path = $visitor->visitObj($this->path);
67
 
68
+ if( !$this->options['inline'] && $this->root ){
69
+ $this->root = $visitor->visit($this->root);
70
  }
71
  }
72
 
73
+ /**
74
+ * @see Less_Tree::genCSS
75
+ */
76
+ public function genCSS( $output ){
77
+ if( $this->css ){
78
 
79
  $output->add( '@import ', $this->currentFileInfo, $this->index );
80
 
81
  $this->path->genCSS( $output );
82
+ if( $this->features ){
83
  $output->add( ' ' );
84
  $this->features->genCSS( $output );
85
  }
87
  }
88
  }
89
 
90
+ public function toCSS(){
91
  $features = $this->features ? ' ' . $this->features->toCSS() : '';
92
 
93
+ if ($this->css) {
94
  return "@import " . $this->path->toCSS() . $features . ";\n";
95
  } else {
96
  return "";
100
  /**
101
  * @return string
102
  */
103
+ public function getPath(){
104
+ if ($this->path instanceof Less_Tree_Quoted) {
105
  $path = $this->path->value;
106
+ $path = ( isset($this->css) || preg_match('/(\.[a-z]*$)|([\?;].*)$/',$path)) ? $path : $path . '.less';
107
+ } else if ($this->path instanceof Less_Tree_URL) {
108
  $path = $this->path->value->value;
109
+ }else{
110
  return null;
111
  }
112
 
113
+ //remove query string and fragment
114
+ return preg_replace('/[\?#][^\?]*$/','',$path);
115
  }
116
 
117
+ public function compileForImport( $env ){
118
+ return new Less_Tree_Import( $this->path->compile($env), $this->features, $this->options, $this->index, $this->currentFileInfo);
119
  }
120
 
121
+ public function compilePath($env) {
122
+ $path = $this->path->compile($env);
123
  $rootpath = '';
124
+ if( $this->currentFileInfo && $this->currentFileInfo['rootpath'] ){
125
  $rootpath = $this->currentFileInfo['rootpath'];
126
  }
127
 
128
+
129
+ if( !($path instanceof Less_Tree_URL) ){
130
+ if( $rootpath ){
131
  $pathValue = $path->value;
132
  // Add the base path if the import is relative
133
+ if( $pathValue && Less_Environment::isPathRelative($pathValue) ){
134
  $path->value = $this->currentFileInfo['uri_root'].$pathValue;
135
  }
136
  }
137
+ $path->value = Less_Environment::normalizePath($path->value);
138
  }
139
 
140
+
141
+
142
  return $path;
143
  }
144
 
145
+ public function compile( $env ){
146
+
147
+ $evald = $this->compileForImport($env);
148
 
149
+ //get path & uri
150
  $path_and_uri = null;
151
+ if( is_callable(Less_Parser::$options['import_callback']) ){
152
+ $path_and_uri = call_user_func(Less_Parser::$options['import_callback'],$evald);
153
  }
154
 
155
+ if( !$path_and_uri ){
156
  $path_and_uri = $evald->PathAndUri();
157
  }
158
 
159
+ if( $path_and_uri ){
160
+ list($full_path, $uri) = $path_and_uri;
161
+ }else{
162
  $full_path = $uri = $evald->getPath();
163
  }
164
 
165
+
166
+ //import once
167
+ if( $evald->skip( $full_path, $env) ){
168
  return array();
169
  }
170
 
171
+ if( $this->options['inline'] ){
172
+ //todo needs to reference css file not import
173
  //$contents = new Less_Tree_Anonymous($this->root, 0, array('filename'=>$this->importedFilename), true );
174
 
175
+ Less_Parser::AddParsedFile($full_path);
176
+ $contents = new Less_Tree_Anonymous( file_get_contents($full_path), 0, array(), true );
177
 
178
+ if( $this->features ){
179
+ return new Less_Tree_Media( array($contents), $this->features->value );
180
  }
181
 
182
  return array( $contents );
183
  }
184
 
185
  // optional (need to be before "CSS" to support optional CSS imports. CSS should be checked only if empty($this->currentFileInfo))
186
+ if( isset($this->options['optional']) && $this->options['optional'] && !file_exists($full_path) && (!$evald->css || !empty($this->currentFileInfo))) {
187
  return array();
188
  }
189
 
190
+
191
  // css ?
192
+ if( $evald->css ){
193
+ $features = ( $evald->features ? $evald->features->compile($env) : null );
194
+ return new Less_Tree_Import( $this->compilePath( $env), $features, $this->options, $this->index);
195
  }
196
 
197
+
198
  return $this->ParseImport( $full_path, $uri, $env );
199
  }
200
 
201
+
202
  /**
203
  * Using the import directories, get the full absolute path and uri of the import
204
  *
205
  * @param Less_Tree_Import $evald
206
  */
207
+ public function PathAndUri(){
208
+
209
  $evald_path = $this->getPath();
210
 
211
+ if( $evald_path ){
212
 
213
  $import_dirs = array();
214
 
215
+ if( Less_Environment::isPathRelative($evald_path) ){
216
+ //if the path is relative, the file should be in the current directory
217
+ if ( $this->currentFileInfo ){
218
  $import_dirs[ $this->currentFileInfo['currentDirectory'] ] = $this->currentFileInfo['uri_root'];
219
  }
220
 
221
+ }else{
222
+ //otherwise, the file should be relative to the server root
223
+ if( $this->currentFileInfo ) {
224
  $import_dirs[ $this->currentFileInfo['entryPath'] ] = $this->currentFileInfo['entryUri'];
225
  }
226
+ //if the user supplied entryPath isn't the actual root
227
  $import_dirs[ $_SERVER['DOCUMENT_ROOT'] ] = '';
228
 
229
  }
231
  // always look in user supplied import directories
232
  $import_dirs = array_merge( $import_dirs, Less_Parser::$options['import_dirs'] );
233
 
234
+
235
+ foreach( $import_dirs as $rootpath => $rooturi){
236
+ if( is_callable($rooturi) ){
237
+ list($path, $uri) = call_user_func($rooturi, $evald_path);
238
+ if( is_string($path) ){
239
  $full_path = $path;
240
  return array( $full_path, $uri );
241
  }
242
+ }elseif( !empty($rootpath) ){
243
 
244
+ $path = rtrim($rootpath,'/\\').'/'.ltrim($evald_path,'/\\');
245
 
246
+ if( file_exists($path) ){
247
+ $full_path = Less_Environment::normalizePath($path);
248
+ $uri = Less_Environment::normalizePath(dirname($rooturi.$evald_path));
249
  return array( $full_path, $uri );
250
+ } elseif( file_exists($path.'.less') ){
251
+ $full_path = Less_Environment::normalizePath($path.'.less');
252
+ $uri = Less_Environment::normalizePath(dirname($rooturi.$evald_path.'.less'));
253
  return array( $full_path, $uri );
254
  }
255
  }
257
  }
258
  }
259
 
260
+
261
  /**
262
  * Parse the import url and return the rules
263
  *
264
  * @return Less_Tree_Media|array
265
  */
266
+ public function ParseImport( $full_path, $uri, $env ){
267
+
268
  $import_env = clone $env;
269
+ if( (isset($this->options['reference']) && $this->options['reference']) || isset($this->currentFileInfo['reference']) ){
270
  $import_env->currentFileInfo['reference'] = true;
271
  }
272
 
273
+ if( (isset($this->options['multiple']) && $this->options['multiple']) ){
274
  $import_env->importMultiple = true;
275
  }
276
 
277
+ $parser = new Less_Parser($import_env);
278
+ $root = $parser->parseFile($full_path, $uri, true);
279
+
280
 
281
+ $ruleset = new Less_Tree_Ruleset(array(), $root->rules );
282
+ $ruleset->evalImports($import_env);
283
 
284
+ return $this->features ? new Less_Tree_Media($ruleset->rules, $this->features->value) : $ruleset->rules;
285
  }
286
 
287
+
288
  /**
289
  * Should the import be skipped?
290
  *
291
  * @return boolean|null
292
  */
293
+ private function Skip($path, $env){
294
+
295
+ $path = Less_Parser::AbsPath($path, true);
296
 
297
+ if( $path && Less_Parser::FileParsed($path) ){
298
 
299
+ if( isset($this->currentFileInfo['reference']) ){
300
  return true;
301
  }
302
 
303
+ return !isset($this->options['multiple']) && !$env->importMultiple;
304
  }
305
 
306
  }
base/inc/lib/Less/Tree/Javascript.php CHANGED
@@ -6,7 +6,7 @@
6
  * @package Less
7
  * @subpackage tree
8
  */
9
- class Less_Tree_Javascript extends Less_Tree {
10
 
11
  public $type = 'Javascript';
12
  public $escaped;
@@ -17,14 +17,14 @@ class Less_Tree_Javascript extends Less_Tree {
17
  * @param boolean $index
18
  * @param boolean $escaped
19
  */
20
- public function __construct( $string, $index, $escaped ) {
21
  $this->escaped = $escaped;
22
  $this->expression = $string;
23
  $this->index = $index;
24
  }
25
 
26
- public function compile() {
27
- return new Less_Tree_Anonymous( '/* Sorry, can not do JavaScript evaluation in PHP... :( */' );
28
  }
29
 
30
  }
6
  * @package Less
7
  * @subpackage tree
8
  */
9
+ class Less_Tree_Javascript extends Less_Tree{
10
 
11
  public $type = 'Javascript';
12
  public $escaped;
17
  * @param boolean $index
18
  * @param boolean $escaped
19
  */
20
+ public function __construct($string, $index, $escaped){
21
  $this->escaped = $escaped;
22
  $this->expression = $string;
23
  $this->index = $index;
24
  }
25
 
26
+ public function compile(){
27
+ return new Less_Tree_Anonymous('/* Sorry, can not do JavaScript evaluation in PHP... :( */');
28
  }
29
 
30
  }
base/inc/lib/Less/Tree/Keyword.php CHANGED
@@ -6,7 +6,7 @@
6
  * @package Less
7
  * @subpackage tree
8
  */
9
- class Less_Tree_Keyword extends Less_Tree {
10
 
11
  public $value;
12
  public $type = 'Keyword';
@@ -14,27 +14,28 @@ class Less_Tree_Keyword extends Less_Tree {
14
  /**
15
  * @param string $value
16
  */
17
- public function __construct( $value ) {
18
  $this->value = $value;
19
  }
20
 
21
- public function compile() {
22
  return $this;
23
  }
24
 
25
- /**
26
- * @see Less_Tree::genCSS
27
- */
28
- public function genCSS( $output ) {
29
- if ( $this->value === '%' ) {
30
- throw new Less_Exception_Compiler( "Invalid % without number" );
 
31
  }
32
 
33
  $output->add( $this->value );
34
  }
35
 
36
- public function compare( $other ) {
37
- if ( $other instanceof Less_Tree_Keyword ) {
38
  return $other->value === $this->value ? 0 : 1;
39
  } else {
40
  return -1;
6
  * @package Less
7
  * @subpackage tree
8
  */
9
+ class Less_Tree_Keyword extends Less_Tree{
10
 
11
  public $value;
12
  public $type = 'Keyword';
14
  /**
15
  * @param string $value
16
  */
17
+ public function __construct($value){
18
  $this->value = $value;
19
  }
20
 
21
+ public function compile(){
22
  return $this;
23
  }
24
 
25
+ /**
26
+ * @see Less_Tree::genCSS
27
+ */
28
+ public function genCSS( $output ){
29
+
30
+ if( $this->value === '%') {
31
+ throw new Less_Exception_Compiler("Invalid % without number");
32
  }
33
 
34
  $output->add( $this->value );
35
  }
36
 
37
+ public function compare($other) {
38
+ if ($other instanceof Less_Tree_Keyword) {
39
  return $other->value === $this->value ? 0 : 1;
40
  } else {
41
  return -1;
base/inc/lib/Less/Tree/Media.php CHANGED
@@ -6,7 +6,7 @@
6
  * @package Less
7
  * @subpackage tree
8
  */
9
- class Less_Tree_Media extends Less_Tree {
10
 
11
  public $features;
12
  public $rules;
@@ -15,88 +15,91 @@ class Less_Tree_Media extends Less_Tree {
15
  public $isReferenced;
16
  public $type = 'Media';
17
 
18
- public function __construct( $value = array(), $features = array(), $index = null, $currentFileInfo = null ) {
 
19
  $this->index = $index;
20
  $this->currentFileInfo = $currentFileInfo;
21
 
22
  $selectors = $this->emptySelectors();
23
 
24
- $this->features = new Less_Tree_Value( $features );
25
 
26
- $this->rules = array( new Less_Tree_Ruleset( $selectors, $value ) );
27
  $this->rules[0]->allowImports = true;
28
  }
29
 
30
- public function accept( $visitor ) {
31
- $this->features = $visitor->visitObj( $this->features );
32
- $this->rules = $visitor->visitArray( $this->rules );
33
  }
34
 
35
- /**
36
- * @see Less_Tree::genCSS
37
- */
38
- public function genCSS( $output ) {
 
39
  $output->add( '@media ', $this->currentFileInfo, $this->index );
40
  $this->features->genCSS( $output );
41
- Less_Tree::outputRuleset( $output, $this->rules );
42
 
43
  }
44
 
45
- public function compile( $env ) {
46
- $media = new Less_Tree_Media( array(), array(), $this->index, $this->currentFileInfo );
 
47
 
48
  $strictMathBypass = false;
49
- if ( Less_Parser::$options['strictMath'] === false ) {
50
  $strictMathBypass = true;
51
  Less_Parser::$options['strictMath'] = true;
52
  }
53
 
54
- $media->features = $this->features->compile( $env );
55
 
56
- if ( $strictMathBypass ) {
57
  Less_Parser::$options['strictMath'] = false;
58
  }
59
 
60
  $env->mediaPath[] = $media;
61
  $env->mediaBlocks[] = $media;
62
 
63
- array_unshift( $env->frames, $this->rules[0] );
64
- $media->rules = array( $this->rules[0]->compile( $env ) );
65
- array_shift( $env->frames );
66
 
67
- array_pop( $env->mediaPath );
68
 
69
- return !$env->mediaPath ? $media->compileTop( $env ) : $media->compileNested( $env );
70
  }
71
 
72
- public function variable( $name ) {
73
- return $this->rules[0]->variable( $name );
74
  }
75
 
76
- public function find( $selector ) {
77
- return $this->rules[0]->find( $selector, $this );
78
  }
79
 
80
- public function emptySelectors() {
81
- $el = new Less_Tree_Element( '', '&', $this->index, $this->currentFileInfo );
82
- $sels = array( new Less_Tree_Selector( array( $el ), array(), null, $this->index, $this->currentFileInfo ) );
83
  $sels[0]->mediaEmpty = true;
84
- return $sels;
85
  }
86
 
87
- public function markReferenced() {
88
  $this->rules[0]->markReferenced();
89
  $this->isReferenced = true;
90
- Less_Tree::ReferencedArray( $this->rules[0]->rules );
91
  }
92
 
93
  // evaltop
94
- public function compileTop( $env ) {
95
  $result = $this;
96
 
97
- if ( count( $env->mediaBlocks ) > 1 ) {
98
  $selectors = $this->emptySelectors();
99
- $result = new Less_Tree_Ruleset( $selectors, $env->mediaBlocks );
100
  $result->multiMedia = true;
101
  }
102
 
@@ -106,13 +109,13 @@ class Less_Tree_Media extends Less_Tree {
106
  return $result;
107
  }
108
 
109
- public function compileNested( $env ) {
110
- $path = array_merge( $env->mediaPath, array( $this ) );
111
 
112
  // Extract the media-query conditions separated with `,` (OR).
113
- foreach ( $path as $key => $p ) {
114
  $value = $p->features instanceof Less_Tree_Value ? $p->features->value : $p->features;
115
- $path[$key] = is_array( $value ) ? $value : array( $value );
116
  }
117
 
118
  // Trace all permutations to generate the resulting media-query.
@@ -123,40 +126,42 @@ class Less_Tree_Media extends Less_Tree {
123
  // b and c and d
124
  // b and c and e
125
 
126
- $permuted = $this->permute( $path );
127
  $expressions = array();
128
- foreach ( $permuted as $path ) {
129
 
130
- for ( $i = 0, $len = count( $path ); $i < $len; $i++ ) {
131
- $path[$i] = Less_Parser::is_method( $path[$i], 'toCSS' ) ? $path[$i] : new Less_Tree_Anonymous( $path[$i] );
132
  }
133
 
134
- for ( $i = count( $path ) - 1; $i > 0; $i-- ) {
135
- array_splice( $path, $i, 0, array( new Less_Tree_Anonymous( 'and' ) ) );
136
  }
137
 
138
- $expressions[] = new Less_Tree_Expression( $path );
139
  }
140
- $this->features = new Less_Tree_Value( $expressions );
 
 
141
 
142
  // Fake a tree-node that doesn't output anything.
143
- return new Less_Tree_Ruleset( array(), array() );
144
  }
145
 
146
- public function permute( $arr ) {
147
- if ( !$arr )
148
  return array();
149
 
150
- if ( count( $arr ) == 1 )
151
  return $arr[0];
152
 
153
  $result = array();
154
- $rest = $this->permute( array_slice( $arr, 1 ) );
155
- foreach ( $rest as $r ) {
156
- foreach ( $arr[0] as $a ) {
157
  $result[] = array_merge(
158
- is_array( $a ) ? $a : array( $a ),
159
- is_array( $r ) ? $r : array( $r )
160
  );
161
  }
162
  }
@@ -164,10 +169,11 @@ class Less_Tree_Media extends Less_Tree {
164
  return $result;
165
  }
166
 
167
- public function bubbleSelectors( $selectors ) {
168
- if ( !$selectors ) return;
 
169
 
170
- $this->rules = array( new Less_Tree_Ruleset( $selectors, array( $this->rules[0] ) ) );
171
  }
172
 
173
  }
6
  * @package Less
7
  * @subpackage tree
8
  */
9
+ class Less_Tree_Media extends Less_Tree{
10
 
11
  public $features;
12
  public $rules;
15
  public $isReferenced;
16
  public $type = 'Media';
17
 
18
+ public function __construct($value = array(), $features = array(), $index = null, $currentFileInfo = null ){
19
+
20
  $this->index = $index;
21
  $this->currentFileInfo = $currentFileInfo;
22
 
23
  $selectors = $this->emptySelectors();
24
 
25
+ $this->features = new Less_Tree_Value($features);
26
 
27
+ $this->rules = array(new Less_Tree_Ruleset($selectors, $value));
28
  $this->rules[0]->allowImports = true;
29
  }
30
 
31
+ public function accept( $visitor ){
32
+ $this->features = $visitor->visitObj($this->features);
33
+ $this->rules = $visitor->visitArray($this->rules);
34
  }
35
 
36
+ /**
37
+ * @see Less_Tree::genCSS
38
+ */
39
+ public function genCSS( $output ){
40
+
41
  $output->add( '@media ', $this->currentFileInfo, $this->index );
42
  $this->features->genCSS( $output );
43
+ Less_Tree::outputRuleset( $output, $this->rules);
44
 
45
  }
46
 
47
+ public function compile($env) {
48
+
49
+ $media = new Less_Tree_Media(array(), array(), $this->index, $this->currentFileInfo );
50
 
51
  $strictMathBypass = false;
52
+ if( Less_Parser::$options['strictMath'] === false) {
53
  $strictMathBypass = true;
54
  Less_Parser::$options['strictMath'] = true;
55
  }
56
 
57
+ $media->features = $this->features->compile($env);
58
 
59
+ if( $strictMathBypass ){
60
  Less_Parser::$options['strictMath'] = false;
61
  }
62
 
63
  $env->mediaPath[] = $media;
64
  $env->mediaBlocks[] = $media;
65
 
66
+ array_unshift($env->frames, $this->rules[0]);
67
+ $media->rules = array($this->rules[0]->compile($env));
68
+ array_shift($env->frames);
69
 
70
+ array_pop($env->mediaPath);
71
 
72
+ return !$env->mediaPath ? $media->compileTop($env) : $media->compileNested($env);
73
  }
74
 
75
+ public function variable($name) {
76
+ return $this->rules[0]->variable($name);
77
  }
78
 
79
+ public function find($selector) {
80
+ return $this->rules[0]->find($selector, $this);
81
  }
82
 
83
+ public function emptySelectors(){
84
+ $el = new Less_Tree_Element('','&', $this->index, $this->currentFileInfo );
85
+ $sels = array( new Less_Tree_Selector(array($el), array(), null, $this->index, $this->currentFileInfo) );
86
  $sels[0]->mediaEmpty = true;
87
+ return $sels;
88
  }
89
 
90
+ public function markReferenced(){
91
  $this->rules[0]->markReferenced();
92
  $this->isReferenced = true;
93
+ Less_Tree::ReferencedArray($this->rules[0]->rules);
94
  }
95
 
96
  // evaltop
97
+ public function compileTop($env) {
98
  $result = $this;
99
 
100
+ if (count($env->mediaBlocks) > 1) {
101
  $selectors = $this->emptySelectors();
102
+ $result = new Less_Tree_Ruleset($selectors, $env->mediaBlocks);
103
  $result->multiMedia = true;
104
  }
105
 
109
  return $result;
110
  }
111
 
112
+ public function compileNested($env) {
113
+ $path = array_merge($env->mediaPath, array($this));
114
 
115
  // Extract the media-query conditions separated with `,` (OR).
116
+ foreach ($path as $key => $p) {
117
  $value = $p->features instanceof Less_Tree_Value ? $p->features->value : $p->features;
118
+ $path[$key] = is_array($value) ? $value : array($value);
119
  }
120
 
121
  // Trace all permutations to generate the resulting media-query.
126
  // b and c and d
127
  // b and c and e
128
 
129
+ $permuted = $this->permute($path);
130
  $expressions = array();
131
+ foreach($permuted as $path){
132
 
133
+ for( $i=0, $len=count($path); $i < $len; $i++){
134
+ $path[$i] = Less_Parser::is_method($path[$i], 'toCSS') ? $path[$i] : new Less_Tree_Anonymous($path[$i]);
135
  }
136
 
137
+ for( $i = count($path) - 1; $i > 0; $i-- ){
138
+ array_splice($path, $i, 0, array(new Less_Tree_Anonymous('and')));
139
  }
140
 
141
+ $expressions[] = new Less_Tree_Expression($path);
142
  }
143
+ $this->features = new Less_Tree_Value($expressions);
144
+
145
+
146
 
147
  // Fake a tree-node that doesn't output anything.
148
+ return new Less_Tree_Ruleset(array(), array());
149
  }
150
 
151
+ public function permute($arr) {
152
+ if (!$arr)
153
  return array();
154
 
155
+ if (count($arr) == 1)
156
  return $arr[0];
157
 
158
  $result = array();
159
+ $rest = $this->permute(array_slice($arr, 1));
160
+ foreach ($rest as $r) {
161
+ foreach ($arr[0] as $a) {
162
  $result[] = array_merge(
163
+ is_array($a) ? $a : array($a),
164
+ is_array($r) ? $r : array($r)
165
  );
166
  }
167
  }
169
  return $result;
170
  }
171
 
172
+ public function bubbleSelectors($selectors) {
173
+
174
+ if( !$selectors) return;
175
 
176
+ $this->rules = array(new Less_Tree_Ruleset( $selectors, array($this->rules[0])));
177
  }
178
 
179
  }
base/inc/lib/Less/Tree/Mixin/Call.php CHANGED
@@ -1,6 +1,7 @@
1
  <?php
2
 
3
- class Less_Tree_Mixin_Call extends Less_Tree {
 
4
 
5
  public $selector;
6
  public $arguments;
@@ -14,20 +15,22 @@ class Less_Tree_Mixin_Call extends Less_Tree {
14
  * less.js: tree.mixin.Call
15
  *
16
  */
17
- public function __construct( $elements, $args, $index, $currentFileInfo, $important = false ) {
18
- $this->selector = new Less_Tree_Selector( $elements );
19
  $this->arguments = $args;
20
  $this->index = $index;
21
  $this->currentFileInfo = $currentFileInfo;
22
  $this->important = $important;
23
  }
24
 
25
- // function accept($visitor){
26
  // $this->selector = $visitor->visit($this->selector);
27
  // $this->arguments = $visitor->visit($this->arguments);
28
  //}
29
 
30
- public function compile( $env ) {
 
 
31
  $rules = array();
32
  $match = false;
33
  $isOneFound = false;
@@ -36,15 +39,15 @@ class Less_Tree_Mixin_Call extends Less_Tree {
36
  $conditionResult = array();
37
 
38
  $args = array();
39
- foreach ( $this->arguments as $a ) {
40
- $args[] = array( 'name' => $a['name'], 'value' => $a['value']->compile( $env ) );
41
  }
42
 
43
- foreach ( $env->frames as $frame ) {
44
 
45
- $mixins = $frame->find( $this->selector );
46
 
47
- if ( !$mixins ) {
48
  continue;
49
  }
50
 
@@ -58,32 +61,32 @@ class Less_Tree_Mixin_Call extends Less_Tree {
58
  // and build candidate list with corresponding flags. Then, when we know all possible matches,
59
  // we make a final decision.
60
 
61
- $mixins_len = count( $mixins );
62
- for ( $m = 0; $m < $mixins_len; $m++ ) {
63
  $mixin = $mixins[$m];
64
 
65
- if ( $this->IsRecursive( $env, $mixin ) ) {
66
  continue;
67
  }
68
 
69
- if ( $mixin->matchArgs( $args, $env ) ) {
70
 
71
- $candidate = array( 'mixin' => $mixin, 'group' => $defNone );
72
 
73
- if ( $mixin instanceof Less_Tree_Ruleset ) {
74
 
75
- for ( $f = 0; $f < 2; $f++ ) {
76
- Less_Tree_DefaultFunc::value( $f );
77
- $conditionResult[$f] = $mixin->matchCondition( $args, $env );
78
  }
79
- if ( $conditionResult[0] || $conditionResult[1] ) {
80
- if ( $conditionResult[0] != $conditionResult[1] ) {
81
  $candidate['group'] = $conditionResult[1] ? $defTrue : $defFalse;
82
  }
83
 
84
  $candidates[] = $candidate;
85
  }
86
- } else {
87
  $candidates[] = $candidate;
88
  }
89
 
@@ -93,54 +96,56 @@ class Less_Tree_Mixin_Call extends Less_Tree {
93
 
94
  Less_Tree_DefaultFunc::reset();
95
 
96
- $count = array( 0, 0, 0 );
97
- for ( $m = 0; $m < count( $candidates ); $m++ ) {
 
98
  $count[ $candidates[$m]['group'] ]++;
99
  }
100
 
101
- if ( $count[$defNone] > 0 ) {
102
  $defaultResult = $defFalse;
103
  } else {
104
  $defaultResult = $defTrue;
105
- if ( ( $count[$defTrue] + $count[$defFalse] ) > 1 ) {
106
- throw new Exception( 'Ambiguous use of `default()` found when matching for `' . $this->format( $args ) . '`' );
107
  }
108
  }
109
 
110
- $candidates_length = count( $candidates );
111
- $length_1 = ( $candidates_length == 1 );
112
 
113
- for ( $m = 0; $m < $candidates_length; $m++ ) {
 
 
 
114
  $candidate = $candidates[$m]['group'];
115
- if ( ( $candidate === $defNone ) || ( $candidate === $defaultResult ) ) {
116
  try{
117
  $mixin = $candidates[$m]['mixin'];
118
- if ( !( $mixin instanceof Less_Tree_Mixin_Definition ) ) {
119
- $mixin = new Less_Tree_Mixin_Definition( '', array(), $mixin->rules, null, false );
120
  $mixin->originalRuleset = $mixins[$m]->originalRuleset;
121
  }
122
- $rules = array_merge( $rules, $mixin->evalCall( $env, $args, $this->important )->rules );
123
- } catch ( Exception $e ) {
124
- // throw new Less_Exception_Compiler($e->getMessage(), $e->index, null, $this->currentFileInfo['filename']);
125
- throw new Less_Exception_Compiler( $e->getMessage(), null, null, $this->currentFileInfo );
126
  }
127
  }
128
  }
129
 
130
- if ( $match ) {
131
- if ( !$this->currentFileInfo || !isset( $this->currentFileInfo['reference'] ) || !$this->currentFileInfo['reference'] ) {
132
- Less_Tree::ReferencedArray( $rules );
133
  }
134
 
135
  return $rules;
136
  }
137
  }
138
 
139
- if ( $isOneFound ) {
140
- throw new Less_Exception_Compiler( 'No matching definition was found for `'.$this->Format( $args ).'`', null, $this->index, $this->currentFileInfo );
141
 
142
- } else {
143
- throw new Less_Exception_Compiler( trim( $this->selector->toCSS() ) . " is undefined in ".$this->currentFileInfo['filename'], null, $this->index );
144
  }
145
 
146
  }
@@ -149,39 +154,41 @@ class Less_Tree_Mixin_Call extends Less_Tree {
149
  * Format the args for use in exception messages
150
  *
151
  */
152
- private function Format( $args ) {
153
  $message = array();
154
- if ( $args ) {
155
- foreach ( $args as $a ) {
156
  $argValue = '';
157
- if ( $a['name'] ) {
158
  $argValue .= $a['name'] . ':';
159
  }
160
- if ( is_object( $a['value'] ) ) {
161
  $argValue .= $a['value']->toCSS();
162
- } else {
163
  $argValue .= '???';
164
  }
165
  $message[] = $argValue;
166
  }
167
  }
168
- return implode( ', ', $message );
169
  }
170
 
 
171
  /**
172
  * Are we in a recursive mixin call?
173
  *
174
  * @return bool
175
  */
176
- private function IsRecursive( $env, $mixin ) {
177
- foreach ( $env->frames as $recur_frame ) {
178
- if ( !( $mixin instanceof Less_Tree_Mixin_Definition ) ) {
 
179
 
180
- if ( $mixin === $recur_frame ) {
181
  return true;
182
  }
183
 
184
- if ( isset( $recur_frame->originalRuleset ) && $mixin->ruleset_id === $recur_frame->originalRuleset ) {
185
  return true;
186
  }
187
  }
@@ -191,3 +198,5 @@ class Less_Tree_Mixin_Call extends Less_Tree {
191
  }
192
 
193
  }
 
 
1
  <?php
2
 
3
+
4
+ class Less_Tree_Mixin_Call extends Less_Tree{
5
 
6
  public $selector;
7
  public $arguments;
15
  * less.js: tree.mixin.Call
16
  *
17
  */
18
+ public function __construct($elements, $args, $index, $currentFileInfo, $important = false){
19
+ $this->selector = new Less_Tree_Selector($elements);
20
  $this->arguments = $args;
21
  $this->index = $index;
22
  $this->currentFileInfo = $currentFileInfo;
23
  $this->important = $important;
24
  }
25
 
26
+ //function accept($visitor){
27
  // $this->selector = $visitor->visit($this->selector);
28
  // $this->arguments = $visitor->visit($this->arguments);
29
  //}
30
 
31
+
32
+ public function compile($env){
33
+
34
  $rules = array();
35
  $match = false;
36
  $isOneFound = false;
39
  $conditionResult = array();
40
 
41
  $args = array();
42
+ foreach($this->arguments as $a){
43
+ $args[] = array('name'=> $a['name'], 'value' => $a['value']->compile($env) );
44
  }
45
 
46
+ foreach($env->frames as $frame){
47
 
48
+ $mixins = $frame->find($this->selector);
49
 
50
+ if( !$mixins ){
51
  continue;
52
  }
53
 
61
  // and build candidate list with corresponding flags. Then, when we know all possible matches,
62
  // we make a final decision.
63
 
64
+ $mixins_len = count($mixins);
65
+ for( $m = 0; $m < $mixins_len; $m++ ){
66
  $mixin = $mixins[$m];
67
 
68
+ if( $this->IsRecursive( $env, $mixin ) ){
69
  continue;
70
  }
71
 
72
+ if( $mixin->matchArgs($args, $env) ){
73
 
74
+ $candidate = array('mixin' => $mixin, 'group' => $defNone);
75
 
76
+ if( $mixin instanceof Less_Tree_Ruleset ){
77
 
78
+ for( $f = 0; $f < 2; $f++ ){
79
+ Less_Tree_DefaultFunc::value($f);
80
+ $conditionResult[$f] = $mixin->matchCondition( $args, $env);
81
  }
82
+ if( $conditionResult[0] || $conditionResult[1] ){
83
+ if( $conditionResult[0] != $conditionResult[1] ){
84
  $candidate['group'] = $conditionResult[1] ? $defTrue : $defFalse;
85
  }
86
 
87
  $candidates[] = $candidate;
88
  }
89
+ }else{
90
  $candidates[] = $candidate;
91
  }
92
 
96
 
97
  Less_Tree_DefaultFunc::reset();
98
 
99
+
100
+ $count = array(0, 0, 0);
101
+ for( $m = 0; $m < count($candidates); $m++ ){
102
  $count[ $candidates[$m]['group'] ]++;
103
  }
104
 
105
+ if( $count[$defNone] > 0 ){
106
  $defaultResult = $defFalse;
107
  } else {
108
  $defaultResult = $defTrue;
109
+ if( ($count[$defTrue] + $count[$defFalse]) > 1 ){
110
+ throw new Exception( 'Ambiguous use of `default()` found when matching for `' . $this->format($args) . '`' );
111
  }
112
  }
113
 
 
 
114
 
115
+ $candidates_length = count($candidates);
116
+ $length_1 = ($candidates_length == 1);
117
+
118
+ for( $m = 0; $m < $candidates_length; $m++){
119
  $candidate = $candidates[$m]['group'];
120
+ if( ($candidate === $defNone) || ($candidate === $defaultResult) ){
121
  try{
122
  $mixin = $candidates[$m]['mixin'];
123
+ if( !($mixin instanceof Less_Tree_Mixin_Definition) ){
124
+ $mixin = new Less_Tree_Mixin_Definition('', array(), $mixin->rules, null, false);
125
  $mixin->originalRuleset = $mixins[$m]->originalRuleset;
126
  }
127
+ $rules = array_merge($rules, $mixin->evalCall($env, $args, $this->important)->rules);
128
+ } catch (Exception $e) {
129
+ //throw new Less_Exception_Compiler($e->getMessage(), $e->index, null, $this->currentFileInfo['filename']);
130
+ throw new Less_Exception_Compiler($e->getMessage(), null, null, $this->currentFileInfo);
131
  }
132
  }
133
  }
134
 
135
+ if( $match ){
136
+ if( !$this->currentFileInfo || !isset($this->currentFileInfo['reference']) || !$this->currentFileInfo['reference'] ){
137
+ Less_Tree::ReferencedArray($rules);
138
  }
139
 
140
  return $rules;
141
  }
142
  }
143
 
144
+ if( $isOneFound ){
145
+ throw new Less_Exception_Compiler('No matching definition was found for `'.$this->Format( $args ).'`', null, $this->index, $this->currentFileInfo);
146
 
147
+ }else{
148
+ throw new Less_Exception_Compiler(trim($this->selector->toCSS()) . " is undefined in ".$this->currentFileInfo['filename'], null, $this->index);
149
  }
150
 
151
  }
154
  * Format the args for use in exception messages
155
  *
156
  */
157
+ private function Format($args){
158
  $message = array();
159
+ if( $args ){
160
+ foreach($args as $a){
161
  $argValue = '';
162
+ if( $a['name'] ){
163
  $argValue .= $a['name'] . ':';
164
  }
165
+ if( is_object($a['value']) ){
166
  $argValue .= $a['value']->toCSS();
167
+ }else{
168
  $argValue .= '???';
169
  }
170
  $message[] = $argValue;
171
  }
172
  }
173
+ return implode(', ',$message);
174
  }
175
 
176
+
177
  /**
178
  * Are we in a recursive mixin call?
179
  *
180
  * @return bool
181
  */
182
+ private function IsRecursive( $env, $mixin ){
183
+
184
+ foreach($env->frames as $recur_frame){
185
+ if( !($mixin instanceof Less_Tree_Mixin_Definition) ){
186
 
187
+ if( $mixin === $recur_frame ){
188
  return true;
189
  }
190
 
191
+ if( isset($recur_frame->originalRuleset) && $mixin->ruleset_id === $recur_frame->originalRuleset ){
192
  return true;
193
  }
194
  }
198
  }
199
 
200
  }
201
+
202
+
base/inc/lib/Less/Tree/Mixin/Definition.php CHANGED
@@ -1,6 +1,6 @@
1
  <?php
2
 
3
- class Less_Tree_Mixin_Definition extends Less_Tree_Ruleset {
4
  public $name;
5
  public $selectors;
6
  public $params;
@@ -13,20 +13,21 @@ class Less_Tree_Mixin_Definition extends Less_Tree_Ruleset {
13
  public $variadic;
14
  public $type = 'MixinDefinition';
15
 
 
16
  // less.js : /lib/less/tree/mixin.js : tree.mixin.Definition
17
- public function __construct( $name, $params, $rules, $condition, $variadic = false, $frames = array() ) {
18
  $this->name = $name;
19
- $this->selectors = array( new Less_Tree_Selector( array( new Less_Tree_Element( null, $name ) ) ) );
20
 
21
  $this->params = $params;
22
  $this->condition = $condition;
23
  $this->variadic = $variadic;
24
  $this->rules = $rules;
25
 
26
- if ( $params ) {
27
- $this->arity = count( $params );
28
- foreach ( $params as $p ) {
29
- if ( !isset( $p['name'] ) || ( $p['name'] && !isset( $p['value'] ) ) ) {
30
  $this->required++;
31
  }
32
  }
@@ -36,139 +37,144 @@ class Less_Tree_Mixin_Definition extends Less_Tree_Ruleset {
36
  $this->SetRulesetIndex();
37
  }
38
 
39
- // function accept( $visitor ){
 
 
40
  // $this->params = $visitor->visit($this->params);
41
  // $this->rules = $visitor->visit($this->rules);
42
  // $this->condition = $visitor->visit($this->condition);
43
  //}
44
 
45
- public function toCSS() {
 
46
  return '';
47
  }
48
 
49
  // less.js : /lib/less/tree/mixin.js : tree.mixin.Definition.evalParams
50
- public function compileParams( $env, $mixinFrames, $args = array(), &$evaldArguments = array() ) {
51
- $frame = new Less_Tree_Ruleset( null, array() );
52
  $params = $this->params;
53
  $mixinEnv = null;
54
  $argsLength = 0;
55
 
56
- if ( $args ) {
57
- $argsLength = count( $args );
58
- for ( $i = 0; $i < $argsLength; $i++ ) {
59
  $arg = $args[$i];
60
 
61
- if ( $arg && $arg['name'] ) {
62
  $isNamedFound = false;
63
 
64
- foreach ( $params as $j => $param ) {
65
- if ( !isset( $evaldArguments[$j] ) && $arg['name'] === $params[$j]['name'] ) {
66
- $evaldArguments[$j] = $arg['value']->compile( $env );
67
- array_unshift( $frame->rules, new Less_Tree_Rule( $arg['name'], $arg['value']->compile( $env ) ) );
68
  $isNamedFound = true;
69
  break;
70
  }
71
  }
72
- if ( $isNamedFound ) {
73
- array_splice( $args, $i, 1 );
74
  $i--;
75
  $argsLength--;
76
  continue;
77
  } else {
78
- throw new Less_Exception_Compiler( "Named argument for " . $this->name .' '.$args[$i]['name'] . ' not found' );
79
  }
80
  }
81
  }
82
  }
83
 
84
  $argIndex = 0;
85
- foreach ( $params as $i => $param ) {
86
 
87
- if ( isset( $evaldArguments[$i] ) ) { continue;
88
- }
89
 
90
  $arg = null;
91
- if ( isset( $args[$argIndex] ) ) {
92
  $arg = $args[$argIndex];
93
  }
94
 
95
- if ( isset( $param['name'] ) && $param['name'] ) {
96
 
97
- if ( isset( $param['variadic'] ) ) {
98
  $varargs = array();
99
- for ( $j = $argIndex; $j < $argsLength; $j++ ) {
100
- $varargs[] = $args[$j]['value']->compile( $env );
101
  }
102
- $expression = new Less_Tree_Expression( $varargs );
103
- array_unshift( $frame->rules, new Less_Tree_Rule( $param['name'], $expression->compile( $env ) ) );
104
- } else {
105
- $val = ( $arg && $arg['value'] ) ? $arg['value'] : false;
106
 
107
- if ( $val ) {
108
- $val = $val->compile( $env );
109
- } else if ( isset( $param['value'] ) ) {
110
 
111
- if ( !$mixinEnv ) {
112
  $mixinEnv = new Less_Environment();
113
- $mixinEnv->frames = array_merge( array( $frame ), $mixinFrames );
114
  }
115
 
116
- $val = $param['value']->compile( $mixinEnv );
117
  $frame->resetCache();
118
  } else {
119
- throw new Less_Exception_Compiler( "Wrong number of arguments for " . $this->name . " (" . $argsLength . ' for ' . $this->arity . ")" );
120
  }
121
 
122
- array_unshift( $frame->rules, new Less_Tree_Rule( $param['name'], $val ) );
123
  $evaldArguments[$i] = $val;
124
  }
125
  }
126
 
127
- if ( isset( $param['variadic'] ) && $args ) {
128
- for ( $j = $argIndex; $j < $argsLength; $j++ ) {
129
- $evaldArguments[$j] = $args[$j]['value']->compile( $env );
130
  }
131
  }
132
  $argIndex++;
133
  }
134
 
135
- ksort( $evaldArguments );
136
- $evaldArguments = array_values( $evaldArguments );
137
 
138
  return $frame;
139
  }
140
 
141
- public function compile( $env ) {
142
- if ( $this->frames ) {
143
- return new Less_Tree_Mixin_Definition( $this->name, $this->params, $this->rules, $this->condition, $this->variadic, $this->frames );
144
  }
145
- return new Less_Tree_Mixin_Definition( $this->name, $this->params, $this->rules, $this->condition, $this->variadic, $env->frames );
146
  }
147
 
148
- public function evalCall( $env, $args = NULL, $important = NULL ) {
 
149
  Less_Environment::$mixin_stack++;
150
 
151
  $_arguments = array();
152
 
153
- if ( $this->frames ) {
154
- $mixinFrames = array_merge( $this->frames, $env->frames );
155
- } else {
156
  $mixinFrames = $env->frames;
157
  }
158
 
159
- $frame = $this->compileParams( $env, $mixinFrames, $args, $_arguments );
160
 
161
- $ex = new Less_Tree_Expression( $_arguments );
162
- array_unshift( $frame->rules, new Less_Tree_Rule( '@arguments', $ex->compile( $env ) ) );
163
 
164
- $ruleset = new Less_Tree_Ruleset( null, $this->rules );
 
165
  $ruleset->originalRuleset = $this->ruleset_id;
166
 
 
167
  $ruleSetEnv = new Less_Environment();
168
- $ruleSetEnv->frames = array_merge( array( $this, $frame ), $mixinFrames );
169
  $ruleset = $ruleset->compile( $ruleSetEnv );
170
 
171
- if ( $important ) {
172
  $ruleset = $ruleset->makeImportant();
173
  }
174
 
@@ -177,51 +183,53 @@ class Less_Tree_Mixin_Definition extends Less_Tree_Ruleset {
177
  return $ruleset;
178
  }
179
 
180
- public function matchCondition( $args, $env ) {
181
- if ( !$this->condition ) {
 
 
182
  return true;
183
  }
184
 
185
  // set array to prevent error on array_merge
186
- if ( !is_array( $this->frames ) ) {
187
- $this->frames = array();
188
- }
189
 
190
- $frame = $this->compileParams( $env, array_merge( $this->frames, $env->frames ), $args );
191
 
192
  $compile_env = new Less_Environment();
193
  $compile_env->frames = array_merge(
194
- array( $frame ), // the parameter variables
195
- $this->frames, // the parent namespace/mixin frames
196
- $env->frames // the current environment frames
197
  );
198
 
199
  $compile_env->functions = $env->functions;
200
 
201
- return (bool)$this->condition->compile( $compile_env );
202
  }
203
 
204
- public function matchArgs( $args, $env = NULL ) {
205
- $argsLength = count( $args );
206
 
207
- if ( !$this->variadic ) {
208
- if ( $argsLength < $this->required ) {
209
  return false;
210
  }
211
- if ( $argsLength > count( $this->params ) ) {
212
  return false;
213
  }
214
- } else {
215
- if ( $argsLength < ( $this->required - 1 ) ) {
216
  return false;
217
  }
218
  }
219
 
220
- $len = min( $argsLength, $this->arity );
221
 
222
- for ( $i = 0; $i < $len; $i++ ) {
223
- if ( !isset( $this->params[$i]['name'] ) && !isset( $this->params[$i]['variadic'] ) ) {
224
- if ( $args[$i]['value']->compile( $env )->toCSS() != $this->params[$i]['value']->compile( $env )->toCSS() ) {
225
  return false;
226
  }
227
  }
1
  <?php
2
 
3
+ class Less_Tree_Mixin_Definition extends Less_Tree_Ruleset{
4
  public $name;
5
  public $selectors;
6
  public $params;
13
  public $variadic;
14
  public $type = 'MixinDefinition';
15
 
16
+
17
  // less.js : /lib/less/tree/mixin.js : tree.mixin.Definition
18
+ public function __construct($name, $params, $rules, $condition, $variadic = false, $frames = array() ){
19
  $this->name = $name;
20
+ $this->selectors = array(new Less_Tree_Selector(array( new Less_Tree_Element(null, $name))));
21
 
22
  $this->params = $params;
23
  $this->condition = $condition;
24
  $this->variadic = $variadic;
25
  $this->rules = $rules;
26
 
27
+ if( $params ){
28
+ $this->arity = count($params);
29
+ foreach( $params as $p ){
30
+ if (! isset($p['name']) || ($p['name'] && !isset($p['value']))) {
31
  $this->required++;
32
  }
33
  }
37
  $this->SetRulesetIndex();
38
  }
39
 
40
+
41
+
42
+ //function accept( $visitor ){
43
  // $this->params = $visitor->visit($this->params);
44
  // $this->rules = $visitor->visit($this->rules);
45
  // $this->condition = $visitor->visit($this->condition);
46
  //}
47
 
48
+
49
+ public function toCSS(){
50
  return '';
51
  }
52
 
53
  // less.js : /lib/less/tree/mixin.js : tree.mixin.Definition.evalParams
54
+ public function compileParams($env, $mixinFrames, $args = array() , &$evaldArguments = array() ){
55
+ $frame = new Less_Tree_Ruleset(null, array());
56
  $params = $this->params;
57
  $mixinEnv = null;
58
  $argsLength = 0;
59
 
60
+ if( $args ){
61
+ $argsLength = count($args);
62
+ for($i = 0; $i < $argsLength; $i++ ){
63
  $arg = $args[$i];
64
 
65
+ if( $arg && $arg['name'] ){
66
  $isNamedFound = false;
67
 
68
+ foreach($params as $j => $param){
69
+ if( !isset($evaldArguments[$j]) && $arg['name'] === $params[$j]['name']) {
70
+ $evaldArguments[$j] = $arg['value']->compile($env);
71
+ array_unshift($frame->rules, new Less_Tree_Rule( $arg['name'], $arg['value']->compile($env) ) );
72
  $isNamedFound = true;
73
  break;
74
  }
75
  }
76
+ if ($isNamedFound) {
77
+ array_splice($args, $i, 1);
78
  $i--;
79
  $argsLength--;
80
  continue;
81
  } else {
82
+ throw new Less_Exception_Compiler("Named argument for " . $this->name .' '.$args[$i]['name'] . ' not found');
83
  }
84
  }
85
  }
86
  }
87
 
88
  $argIndex = 0;
89
+ foreach($params as $i => $param){
90
 
91
+ if ( isset($evaldArguments[$i]) ){ continue; }
 
92
 
93
  $arg = null;
94
+ if( isset($args[$argIndex]) ){
95
  $arg = $args[$argIndex];
96
  }
97
 
98
+ if (isset($param['name']) && $param['name']) {
99
 
100
+ if( isset($param['variadic']) ){
101
  $varargs = array();
102
+ for ($j = $argIndex; $j < $argsLength; $j++) {
103
+ $varargs[] = $args[$j]['value']->compile($env);
104
  }
105
+ $expression = new Less_Tree_Expression($varargs);
106
+ array_unshift($frame->rules, new Less_Tree_Rule($param['name'], $expression->compile($env)));
107
+ }else{
108
+ $val = ($arg && $arg['value']) ? $arg['value'] : false;
109
 
110
+ if ($val) {
111
+ $val = $val->compile($env);
112
+ } else if ( isset($param['value']) ) {
113
 
114
+ if( !$mixinEnv ){
115
  $mixinEnv = new Less_Environment();
116
+ $mixinEnv->frames = array_merge( array($frame), $mixinFrames);
117
  }
118
 
119
+ $val = $param['value']->compile($mixinEnv);
120
  $frame->resetCache();
121
  } else {
122
+ throw new Less_Exception_Compiler("Wrong number of arguments for " . $this->name . " (" . $argsLength . ' for ' . $this->arity . ")");
123
  }
124
 
125
+ array_unshift($frame->rules, new Less_Tree_Rule($param['name'], $val));
126
  $evaldArguments[$i] = $val;
127
  }
128
  }
129
 
130
+ if ( isset($param['variadic']) && $args) {
131
+ for ($j = $argIndex; $j < $argsLength; $j++) {
132
+ $evaldArguments[$j] = $args[$j]['value']->compile($env);
133
  }
134
  }
135
  $argIndex++;
136
  }
137
 
138
+ ksort($evaldArguments);
139
+ $evaldArguments = array_values($evaldArguments);
140
 
141
  return $frame;
142
  }
143
 
144
+ public function compile($env) {
145
+ if( $this->frames ){
146
+ return new Less_Tree_Mixin_Definition($this->name, $this->params, $this->rules, $this->condition, $this->variadic, $this->frames );
147
  }
148
+ return new Less_Tree_Mixin_Definition($this->name, $this->params, $this->rules, $this->condition, $this->variadic, $env->frames );
149
  }
150
 
151
+ public function evalCall($env, $args = NULL, $important = NULL) {
152
+
153
  Less_Environment::$mixin_stack++;
154
 
155
  $_arguments = array();
156
 
157
+ if( $this->frames ){
158
+ $mixinFrames = array_merge($this->frames, $env->frames);
159
+ }else{
160
  $mixinFrames = $env->frames;
161
  }
162
 
163
+ $frame = $this->compileParams($env, $mixinFrames, $args, $_arguments);
164
 
165
+ $ex = new Less_Tree_Expression($_arguments);
166
+ array_unshift($frame->rules, new Less_Tree_Rule('@arguments', $ex->compile($env)));
167
 
168
+
169
+ $ruleset = new Less_Tree_Ruleset(null, $this->rules);
170
  $ruleset->originalRuleset = $this->ruleset_id;
171
 
172
+
173
  $ruleSetEnv = new Less_Environment();
174
+ $ruleSetEnv->frames = array_merge( array($this, $frame), $mixinFrames );
175
  $ruleset = $ruleset->compile( $ruleSetEnv );
176
 
177
+ if( $important ){
178
  $ruleset = $ruleset->makeImportant();
179
  }
180
 
183
  return $ruleset;
184
  }
185
 
186
+
187
+ public function matchCondition($args, $env) {
188
+
189
+ if( !$this->condition ){
190
  return true;
191
  }
192
 
193
  // set array to prevent error on array_merge
194
+ if(!is_array($this->frames)) {
195
+ $this->frames = array();
196
+ }
197
 
198
+ $frame = $this->compileParams($env, array_merge($this->frames,$env->frames), $args );
199
 
200
  $compile_env = new Less_Environment();
201
  $compile_env->frames = array_merge(
202
+ array($frame) // the parameter variables
203
+ , $this->frames // the parent namespace/mixin frames
204
+ , $env->frames // the current environment frames
205
  );
206
 
207
  $compile_env->functions = $env->functions;
208
 
209
+ return (bool)$this->condition->compile($compile_env);
210
  }
211
 
212
+ public function matchArgs($args, $env = NULL){
213
+ $argsLength = count($args);
214
 
215
+ if( !$this->variadic ){
216
+ if( $argsLength < $this->required ){
217
  return false;
218
  }
219
+ if( $argsLength > count($this->params) ){
220
  return false;
221
  }
222
+ }else{
223
+ if( $argsLength < ($this->required - 1)){
224
  return false;
225
  }
226
  }
227
 
228
+ $len = min($argsLength, $this->arity);
229
 
230
+ for( $i = 0; $i < $len; $i++ ){
231
+ if( !isset($this->params[$i]['name']) && !isset($this->params[$i]['variadic']) ){
232
+ if( $args[$i]['value']->compile($env)->toCSS() != $this->params[$i]['value']->compile($env)->toCSS() ){
233
  return false;
234
  }
235
  }
base/inc/lib/Less/Tree/NameValue.php CHANGED
@@ -10,7 +10,7 @@
10
  * @package Less
11
  * @subpackage tree
12
  */
13
- class Less_Tree_NameValue extends Less_Tree {
14
 
15
  public $name;
16
  public $value;
@@ -19,31 +19,33 @@ class Less_Tree_NameValue extends Less_Tree {
19
  public $type = 'NameValue';
20
  public $important = '';
21
 
22
- public function __construct( $name, $value = null, $index = null, $currentFileInfo = null ) {
23
  $this->name = $name;
24
  $this->value = $value;
25
  $this->index = $index;
26
  $this->currentFileInfo = $currentFileInfo;
27
  }
28
 
29
- public function genCSS( $output ) {
 
30
  $output->add(
31
  $this->name
32
  . Less_Environment::$_outputMap[': ']
33
  . $this->value
34
  . $this->important
35
- . ( ( ( Less_Environment::$lastRule && Less_Parser::$options['compress'] ) ) ? "" : ";" ),
36
- $this->currentFileInfo, $this->index );
37
  }
38
 
39
- public function compile( $env ) {
40
  return $this;
41
  }
42
 
43
- public function makeImportant() {
44
- $new = new Less_Tree_NameValue( $this->name, $this->value, $this->index, $this->currentFileInfo );
45
  $new->important = ' !important';
46
  return $new;
47
  }
48
 
 
49
  }
10
  * @package Less
11
  * @subpackage tree
12
  */
13
+ class Less_Tree_NameValue extends Less_Tree{
14
 
15
  public $name;
16
  public $value;
19
  public $type = 'NameValue';
20
  public $important = '';
21
 
22
+ public function __construct($name, $value = null, $index = null, $currentFileInfo = null ){
23
  $this->name = $name;
24
  $this->value = $value;
25
  $this->index = $index;
26
  $this->currentFileInfo = $currentFileInfo;
27
  }
28
 
29
+ public function genCSS( $output ){
30
+
31
  $output->add(
32
  $this->name
33
  . Less_Environment::$_outputMap[': ']
34
  . $this->value
35
  . $this->important
36
+ . (((Less_Environment::$lastRule && Less_Parser::$options['compress'])) ? "" : ";")
37
+ , $this->currentFileInfo, $this->index);
38
  }
39
 
40
+ public function compile ($env){
41
  return $this;
42
  }
43
 
44
+ public function makeImportant(){
45
+ $new = new Less_Tree_NameValue($this->name, $this->value, $this->index, $this->currentFileInfo);
46
  $new->important = ' !important';
47
  return $new;
48
  }
49
 
50
+
51
  }
base/inc/lib/Less/Tree/Negative.php CHANGED
@@ -6,32 +6,32 @@
6
  * @package Less
7
  * @subpackage tree
8
  */
9
- class Less_Tree_Negative extends Less_Tree {
10
 
11
  public $value;
12
  public $type = 'Negative';
13
 
14
- public function __construct( $node ) {
15
  $this->value = $node;
16
  }
17
 
18
- // function accept($visitor) {
19
  // $this->value = $visitor->visit($this->value);
20
  //}
21
 
22
- /**
23
- * @see Less_Tree::genCSS
24
- */
25
- public function genCSS( $output ) {
26
  $output->add( '-' );
27
  $this->value->genCSS( $output );
28
  }
29
 
30
- public function compile( $env ) {
31
- if ( Less_Environment::isMathOn() ) {
32
- $ret = new Less_Tree_Operation( '*', array( new Less_Tree_Dimension( -1 ), $this->value ) );
33
- return $ret->compile( $env );
34
  }
35
- return new Less_Tree_Negative( $this->value->compile( $env ) );
36
  }
37
- }
6
  * @package Less
7
  * @subpackage tree
8
  */
9
+ class Less_Tree_Negative extends Less_Tree{
10
 
11
  public $value;
12
  public $type = 'Negative';
13
 
14
+ public function __construct($node){
15
  $this->value = $node;
16
  }
17
 
18
+ //function accept($visitor) {
19
  // $this->value = $visitor->visit($this->value);
20
  //}
21
 
22
+ /**
23
+ * @see Less_Tree::genCSS
24
+ */
25
+ public function genCSS( $output ){
26
  $output->add( '-' );
27
  $this->value->genCSS( $output );
28
  }
29
 
30
+ public function compile($env) {
31
+ if( Less_Environment::isMathOn() ){
32
+ $ret = new Less_Tree_Operation('*', array( new Less_Tree_Dimension(-1), $this->value ) );
33
+ return $ret->compile($env);
34
  }
35
+ return new Less_Tree_Negative( $this->value->compile($env) );
36
  }
37
+ }
base/inc/lib/Less/Tree/Operation.php CHANGED
@@ -6,7 +6,7 @@
6
  * @package Less
7
  * @subpackage tree
8
  */
9
- class Less_Tree_Operation extends Less_Tree {
10
 
11
  public $op;
12
  public $operands;
@@ -16,50 +16,52 @@ class Less_Tree_Operation extends Less_Tree {
16
  /**
17
  * @param string $op
18
  */
19
- public function __construct( $op, $operands, $isSpaced = false ) {
20
- $this->op = trim( $op );
21
  $this->operands = $operands;
22
  $this->isSpaced = $isSpaced;
23
  }
24
 
25
- public function accept( $visitor ) {
26
- $this->operands = $visitor->visitArray( $this->operands );
27
  }
28
 
29
- public function compile( $env ) {
30
- $a = $this->operands[0]->compile( $env );
31
- $b = $this->operands[1]->compile( $env );
32
 
33
- if ( Less_Environment::isMathOn() ) {
34
 
35
- if ( $a instanceof Less_Tree_Dimension && $b instanceof Less_Tree_Color ) {
 
 
36
  $a = $a->toColor();
37
 
38
- } elseif ( $b instanceof Less_Tree_Dimension && $a instanceof Less_Tree_Color ) {
39
  $b = $b->toColor();
40
 
41
  }
42
 
43
- if ( !method_exists( $a, 'operate' ) ) {
44
- throw new Less_Exception_Compiler( "Operation on an invalid type" );
45
  }
46
 
47
- return $a->operate( $this->op, $b );
48
  }
49
 
50
- return new Less_Tree_Operation( $this->op, array( $a, $b ), $this->isSpaced );
51
  }
52
 
53
- /**
54
- * @see Less_Tree::genCSS
55
- */
56
- public function genCSS( $output ) {
 
57
  $this->operands[0]->genCSS( $output );
58
- if ( $this->isSpaced ) {
59
  $output->add( " " );
60
  }
61
  $output->add( $this->op );
62
- if ( $this->isSpaced ) {
63
  $output->add( ' ' );
64
  }
65
  $this->operands[1]->genCSS( $output );
6
  * @package Less
7
  * @subpackage tree
8
  */
9
+ class Less_Tree_Operation extends Less_Tree{
10
 
11
  public $op;
12
  public $operands;
16
  /**
17
  * @param string $op
18
  */
19
+ public function __construct($op, $operands, $isSpaced = false){
20
+ $this->op = trim($op);
21
  $this->operands = $operands;
22
  $this->isSpaced = $isSpaced;
23
  }
24
 
25
+ public function accept($visitor) {
26
+ $this->operands = $visitor->visitArray($this->operands);
27
  }
28
 
29
+ public function compile($env){
30
+ $a = $this->operands[0]->compile($env);
31
+ $b = $this->operands[1]->compile($env);
32
 
 
33
 
34
+ if( Less_Environment::isMathOn() ){
35
+
36
+ if( $a instanceof Less_Tree_Dimension && $b instanceof Less_Tree_Color ){
37
  $a = $a->toColor();
38
 
39
+ }elseif( $b instanceof Less_Tree_Dimension && $a instanceof Less_Tree_Color ){
40
  $b = $b->toColor();
41
 
42
  }
43
 
44
+ if( !method_exists($a,'operate') ){
45
+ throw new Less_Exception_Compiler("Operation on an invalid type");
46
  }
47
 
48
+ return $a->operate( $this->op, $b);
49
  }
50
 
51
+ return new Less_Tree_Operation($this->op, array($a, $b), $this->isSpaced );
52
  }
53
 
54
+
55
+ /**
56
+ * @see Less_Tree::genCSS
57
+ */
58
+ public function genCSS( $output ){
59
  $this->operands[0]->genCSS( $output );
60
+ if( $this->isSpaced ){
61
  $output->add( " " );
62
  }
63
  $output->add( $this->op );
64
+ if( $this->isSpaced ){
65
  $output->add( ' ' );
66
  }
67
  $this->operands[1]->genCSS( $output );
base/inc/lib/Less/Tree/Paren.php CHANGED
@@ -6,30 +6,30 @@
6
  * @package Less
7
  * @subpackage tree
8
  */
9
- class Less_Tree_Paren extends Less_Tree {
10
 
11
  public $value;
12
  public $type = 'Paren';
13
 
14
- public function __construct( $value ) {
15
  $this->value = $value;
16
  }
17
 
18
- public function accept( $visitor ) {
19
- $this->value = $visitor->visitObj( $this->value );
20
  }
21
 
22
- /**
23
- * @see Less_Tree::genCSS
24
- */
25
- public function genCSS( $output ) {
26
  $output->add( '(' );
27
  $this->value->genCSS( $output );
28
  $output->add( ')' );
29
  }
30
 
31
- public function compile( $env ) {
32
- return new Less_Tree_Paren( $this->value->compile( $env ) );
33
  }
34
 
35
  }
6
  * @package Less
7
  * @subpackage tree
8
  */
9
+ class Less_Tree_Paren extends Less_Tree{
10
 
11
  public $value;
12
  public $type = 'Paren';
13
 
14
+ public function __construct($value) {
15
  $this->value = $value;
16
  }
17
 
18
+ public function accept($visitor){
19
+ $this->value = $visitor->visitObj($this->value);
20
  }
21
 
22
+ /**
23
+ * @see Less_Tree::genCSS
24
+ */
25
+ public function genCSS( $output ){
26
  $output->add( '(' );
27
  $this->value->genCSS( $output );
28
  $output->add( ')' );
29
  }
30
 
31
+ public function compile($env) {
32
+ return new Less_Tree_Paren($this->value->compile($env));
33
  }
34
 
35
  }
base/inc/lib/Less/Tree/Quoted.php CHANGED
@@ -6,7 +6,7 @@
6
  * @package Less
7
  * @subpackage tree
8
  */
9
- class Less_Tree_Quoted extends Less_Tree {
10
  public $escaped;
11
  public $value;
12
  public $quote;
@@ -17,60 +17,62 @@ class Less_Tree_Quoted extends Less_Tree {
17
  /**
18
  * @param string $str
19
  */
20
- public function __construct( $str, $content = '', $escaped = false, $index = false, $currentFileInfo = null ) {
21
  $this->escaped = $escaped;
22
  $this->value = $content;
23
- if ( $str ) {
24
  $this->quote = $str[0];
25
  }
26
  $this->index = $index;
27
  $this->currentFileInfo = $currentFileInfo;
28
  }
29
 
30
- /**
31
- * @see Less_Tree::genCSS
32
- */
33
- public function genCSS( $output ) {
34
- if ( !$this->escaped ) {
35
  $output->add( $this->quote, $this->currentFileInfo, $this->index );
36
- }
37
- $output->add( $this->value );
38
- if ( !$this->escaped ) {
39
  $output->add( $this->quote );
40
- }
41
- }
 
 
42
 
43
- public function compile( $env ) {
44
  $value = $this->value;
45
- if ( preg_match_all( '/`([^`]+)`/', $this->value, $matches ) ) {
46
- foreach ( $matches as $i => $match ) {
47
- $js = new Less_Tree_JavaScript( $matches[1], $this->index, true );
48
  $js = $js->compile()->value;
49
- $value = str_replace( $matches[0][$i], $js, $value );
50
  }
51
  }
52
 
53
- if ( preg_match_all( '/@\{([\w-]+)\}/', $value, $matches ) ) {
54
- foreach ( $matches[1] as $i => $match ) {
55
- $v = new Less_Tree_Variable( '@' . $match, $this->index, $this->currentFileInfo );
56
- $v = $v->compile( $env );
57
- $v = ( $v instanceof Less_Tree_Quoted ) ? $v->value : $v->toCSS();
58
- $value = str_replace( $matches[0][$i], $v, $value );
59
  }
60
  }
61
 
62
- return new Less_Tree_Quoted( $this->quote . $value . $this->quote, $value, $this->escaped, $this->index, $this->currentFileInfo );
63
  }
64
 
65
- public function compare( $x ) {
66
- if ( !Less_Parser::is_method( $x, 'toCSS' ) ) {
 
67
  return -1;
68
  }
69
 
70
  $left = $this->toCSS();
71
  $right = $x->toCSS();
72
 
73
- if ( $left === $right ) {
74
  return 0;
75
  }
76
 
6
  * @package Less
7
  * @subpackage tree
8
  */
9
+ class Less_Tree_Quoted extends Less_Tree{
10
  public $escaped;
11
  public $value;
12
  public $quote;
17
  /**
18
  * @param string $str
19
  */
20
+ public function __construct($str, $content = '', $escaped = false, $index = false, $currentFileInfo = null ){
21
  $this->escaped = $escaped;
22
  $this->value = $content;
23
+ if( $str ){
24
  $this->quote = $str[0];
25
  }
26
  $this->index = $index;
27
  $this->currentFileInfo = $currentFileInfo;
28
  }
29
 
30
+ /**
31
+ * @see Less_Tree::genCSS
32
+ */
33
+ public function genCSS( $output ){
34
+ if( !$this->escaped ){
35
  $output->add( $this->quote, $this->currentFileInfo, $this->index );
36
+ }
37
+ $output->add( $this->value );
38
+ if( !$this->escaped ){
39
  $output->add( $this->quote );
40
+ }
41
+ }
42
+
43
+ public function compile($env){
44
 
 
45
  $value = $this->value;
46
+ if( preg_match_all('/`([^`]+)`/', $this->value, $matches) ){
47
+ foreach($matches as $i => $match){
48
+ $js = new Less_Tree_JavaScript($matches[1], $this->index, true);
49
  $js = $js->compile()->value;
50
+ $value = str_replace($matches[0][$i], $js, $value);
51
  }
52
  }
53
 
54
+ if( preg_match_all('/@\{([\w-]+)\}/',$value,$matches) ){
55
+ foreach($matches[1] as $i => $match){
56
+ $v = new Less_Tree_Variable('@' . $match, $this->index, $this->currentFileInfo );
57
+ $v = $v->compile($env);
58
+ $v = ($v instanceof Less_Tree_Quoted) ? $v->value : $v->toCSS();
59
+ $value = str_replace($matches[0][$i], $v, $value);
60
  }
61
  }
62
 
63
+ return new Less_Tree_Quoted($this->quote . $value . $this->quote, $value, $this->escaped, $this->index, $this->currentFileInfo);
64
  }
65
 
66
+ public function compare($x) {
67
+
68
+ if( !Less_Parser::is_method($x, 'toCSS') ){
69
  return -1;
70
  }
71
 
72
  $left = $this->toCSS();
73
  $right = $x->toCSS();
74
 
75
+ if ($left === $right) {
76
  return 0;
77
  }
78
 
base/inc/lib/Less/Tree/Rule.php CHANGED
@@ -6,7 +6,7 @@
6
  * @package Less
7
  * @subpackage tree
8
  */
9
- class Less_Tree_Rule extends Less_Tree {
10
 
11
  public $name;
12
  public $value;
@@ -21,71 +21,73 @@ class Less_Tree_Rule extends Less_Tree {
21
  /**
22
  * @param string $important
23
  */
24
- public function __construct( $name, $value = null, $important = null, $merge = null, $index = null, $currentFileInfo = null, $inline = false ) {
25
  $this->name = $name;
26
- $this->value = ( $value instanceof Less_Tree_Value || $value instanceof Less_Tree_Ruleset ) ? $value : new Less_Tree_Value( array( $value ) );
27
- $this->important = $important ? ' ' . trim( $important ) : '';
28
  $this->merge = $merge;
29
  $this->index = $index;
30
  $this->currentFileInfo = $currentFileInfo;
31
  $this->inline = $inline;
32
- $this->variable = ( is_string( $name ) && $name[0] === '@' );
33
  }
34
 
35
- public function accept( $visitor ) {
36
  $this->value = $visitor->visitObj( $this->value );
37
  }
38
 
39
- /**
40
- * @see Less_Tree::genCSS
41
- */
42
- public function genCSS( $output ) {
43
- $output->add( $this->name . Less_Environment::$_outputMap[': '], $this->currentFileInfo, $this->index );
 
44
  try{
45
- $this->value->genCSS( $output );
46
 
47
- }catch ( Less_Exception_Parser $e ) {
48
  $e->index = $this->index;
49
  $e->currentFile = $this->currentFileInfo;
50
  throw $e;
51
  }
52
- $output->add( $this->important . ( ( $this->inline || ( Less_Environment::$lastRule && Less_Parser::$options['compress'] ) ) ? "" : ";" ), $this->currentFileInfo, $this->index );
53
  }
54
 
55
- public function compile( $env ) {
 
56
  $name = $this->name;
57
- if ( is_array( $name ) ) {
58
  // expand 'primitive' name directly to get
59
  // things faster (~10% for benchmark.less):
60
- if ( count( $name ) === 1 && $name[0] instanceof Less_Tree_Keyword ) {
61
  $name = $name[0]->value;
62
- } else {
63
- $name = $this->CompileName( $env, $name );
64
  }
65
  }
66
 
67
  $strictMathBypass = Less_Parser::$options['strictMath'];
68
- if ( $name === "font" && !Less_Parser::$options['strictMath'] ) {
69
  Less_Parser::$options['strictMath'] = true;
70
  }
71
 
72
  try {
73
- $evaldValue = $this->value->compile( $env );
74
 
75
- if ( !$this->variable && $evaldValue->type === "DetachedRuleset" ) {
76
- throw new Less_Exception_Compiler( "Rulesets cannot be evaluated on a property.", null, $this->index, $this->currentFileInfo );
77
  }
78
 
79
- if ( Less_Environment::$mixin_stack ) {
80
- $return = new Less_Tree_Rule( $name, $evaldValue, $this->important, $this->merge, $this->index, $this->currentFileInfo, $this->inline );
81
- } else {
82
  $this->name = $name;
83
  $this->value = $evaldValue;
84
  $return = $this;
85
  }
86
 
87
- }catch ( Less_Exception_Parser $e ) {
88
- if ( !is_numeric( $e->index ) ) {
89
  $e->index = $this->index;
90
  $e->currentFile = $this->currentFileInfo;
91
  }
@@ -97,16 +99,17 @@ class Less_Tree_Rule extends Less_Tree {
97
  return $return;
98
  }
99
 
100
- public function CompileName( $env, $name ) {
 
101
  $output = new Less_Output();
102
- foreach ( $name as $n ) {
103
- $n->compile( $env )->genCSS( $output );
104
  }
105
  return $output->toString();
106
  }
107
 
108
- public function makeImportant() {
109
- return new Less_Tree_Rule( $this->name, $this->value, '!important', $this->merge, $this->index, $this->currentFileInfo, $this->inline );
110
  }
111
 
112
  }
6
  * @package Less
7
  * @subpackage tree
8
  */
9
+ class Less_Tree_Rule extends Less_Tree{
10
 
11
  public $name;
12
  public $value;
21
  /**
22
  * @param string $important
23
  */
24
+ public function __construct($name, $value = null, $important = null, $merge = null, $index = null, $currentFileInfo = null, $inline = false){
25
  $this->name = $name;
26
+ $this->value = ($value instanceof Less_Tree_Value || $value instanceof Less_Tree_Ruleset) ? $value : new Less_Tree_Value(array($value));
27
+ $this->important = $important ? ' ' . trim($important) : '';
28
  $this->merge = $merge;
29
  $this->index = $index;
30
  $this->currentFileInfo = $currentFileInfo;
31
  $this->inline = $inline;
32
+ $this->variable = ( is_string($name) && $name[0] === '@');
33
  }
34
 
35
+ public function accept($visitor) {
36
  $this->value = $visitor->visitObj( $this->value );
37
  }
38
 
39
+ /**
40
+ * @see Less_Tree::genCSS
41
+ */
42
+ public function genCSS( $output ){
43
+
44
+ $output->add( $this->name . Less_Environment::$_outputMap[': '], $this->currentFileInfo, $this->index);
45
  try{
46
+ $this->value->genCSS( $output);
47
 
48
+ }catch( Less_Exception_Parser $e ){
49
  $e->index = $this->index;
50
  $e->currentFile = $this->currentFileInfo;
51
  throw $e;
52
  }
53
+ $output->add( $this->important . (($this->inline || (Less_Environment::$lastRule && Less_Parser::$options['compress'])) ? "" : ";"), $this->currentFileInfo, $this->index);
54
  }
55
 
56
+ public function compile ($env){
57
+
58
  $name = $this->name;
59
+ if( is_array($name) ){
60
  // expand 'primitive' name directly to get
61
  // things faster (~10% for benchmark.less):
62
+ if( count($name) === 1 && $name[0] instanceof Less_Tree_Keyword ){
63
  $name = $name[0]->value;
64
+ }else{
65
+ $name = $this->CompileName($env,$name);
66
  }
67
  }
68
 
69
  $strictMathBypass = Less_Parser::$options['strictMath'];
70
+ if( $name === "font" && !Less_Parser::$options['strictMath'] ){
71
  Less_Parser::$options['strictMath'] = true;
72
  }
73
 
74
  try {
75
+ $evaldValue = $this->value->compile($env);
76
 
77
+ if( !$this->variable && $evaldValue->type === "DetachedRuleset") {
78
+ throw new Less_Exception_Compiler("Rulesets cannot be evaluated on a property.", null, $this->index, $this->currentFileInfo);
79
  }
80
 
81
+ if( Less_Environment::$mixin_stack ){
82
+ $return = new Less_Tree_Rule($name, $evaldValue, $this->important, $this->merge, $this->index, $this->currentFileInfo, $this->inline);
83
+ }else{
84
  $this->name = $name;
85
  $this->value = $evaldValue;
86
  $return = $this;
87
  }
88
 
89
+ }catch( Less_Exception_Parser $e ){
90
+ if( !is_numeric($e->index) ){
91
  $e->index = $this->index;
92
  $e->currentFile = $this->currentFileInfo;
93
  }
99
  return $return;
100
  }
101
 
102
+
103
+ public function CompileName( $env, $name ){
104
  $output = new Less_Output();
105
+ foreach($name as $n){
106
+ $n->compile($env)->genCSS($output);
107
  }
108
  return $output->toString();
109
  }
110
 
111
+ public function makeImportant(){
112
+ return new Less_Tree_Rule($this->name, $this->value, '!important', $this->merge, $this->index, $this->currentFileInfo, $this->inline);
113
  }
114
 
115
  }
base/inc/lib/Less/Tree/Ruleset.php CHANGED
@@ -6,7 +6,7 @@
6
  * @package Less
7
  * @subpackage tree
8
  */
9
- class Less_Tree_Ruleset extends Less_Tree {
10
 
11
  protected $lookups;
12
  public $_variables;
@@ -29,20 +29,20 @@ class Less_Tree_Ruleset extends Less_Tree {
29
 
30
  public $first_oelements;
31
 
32
- public function SetRulesetIndex() {
33
  $this->ruleset_id = Less_Parser::$next_id++;
34
  $this->originalRuleset = $this->ruleset_id;
35
 
36
- if ( $this->selectors ) {
37
- foreach ( $this->selectors as $sel ) {
38
- if ( $sel->_oelements ) {
39
  $this->first_oelements[$sel->_oelements[0]] = true;
40
  }
41
  }
42
  }
43
  }
44
 
45
- public function __construct( $selectors, $rules, $strictImports = null ) {
46
  $this->selectors = $selectors;
47
  $this->rules = $rules;
48
  $this->lookups = array();
@@ -50,79 +50,83 @@ class Less_Tree_Ruleset extends Less_Tree {
50
  $this->SetRulesetIndex();
51
  }
52
 
53
- public function accept( $visitor ) {
54
- if ( $this->paths ) {
55
- $paths_len = count( $this->paths );
56
- for ( $i = 0,$paths_len; $i < $paths_len; $i++ ) {
57
- $this->paths[$i] = $visitor->visitArray( $this->paths[$i] );
58
  }
59
- } elseif ( $this->selectors ) {
60
- $this->selectors = $visitor->visitArray( $this->selectors );
61
  }
62
 
63
- if ( $this->rules ) {
64
- $this->rules = $visitor->visitArray( $this->rules );
65
  }
66
  }
67
 
68
- public function compile( $env ) {
69
- $ruleset = $this->PrepareRuleset( $env );
 
 
70
 
71
  // Store the frames around mixin definitions,
72
  // so they can be evaluated like closures when the time comes.
73
- $rsRuleCnt = count( $ruleset->rules );
74
- for ( $i = 0; $i < $rsRuleCnt; $i++ ) {
75
- if ( $ruleset->rules[$i] instanceof Less_Tree_Mixin_Definition || $ruleset->rules[$i] instanceof Less_Tree_DetachedRuleset ) {
76
- $ruleset->rules[$i] = $ruleset->rules[$i]->compile( $env );
77
  }
78
  }
79
 
80
  $mediaBlockCount = 0;
81
- if ( $env instanceof Less_Environment ) {
82
- $mediaBlockCount = count( $env->mediaBlocks );
83
  }
84
 
85
  // Evaluate mixin calls.
86
  $this->EvalMixinCalls( $ruleset, $env, $rsRuleCnt );
87
 
 
88
  // Evaluate everything else
89
- for ( $i = 0; $i < $rsRuleCnt; $i++ ) {
90
- if ( !( $ruleset->rules[$i] instanceof Less_Tree_Mixin_Definition || $ruleset->rules[$i] instanceof Less_Tree_DetachedRuleset ) ) {
91
- $ruleset->rules[$i] = $ruleset->rules[$i]->compile( $env );
92
  }
93
  }
94
 
95
- // Evaluate everything else
96
- for ( $i = 0; $i < $rsRuleCnt; $i++ ) {
97
  $rule = $ruleset->rules[$i];
98
 
99
- // for rulesets, check if it is a css guard and can be removed
100
- if ( $rule instanceof Less_Tree_Ruleset && $rule->selectors && count( $rule->selectors ) === 1 ) {
101
 
102
- // check if it can be folded in (e.g. & where)
103
- if ( $rule->selectors[0]->isJustParentSelector() ) {
104
- array_splice( $ruleset->rules, $i--, 1 );
105
  $rsRuleCnt--;
106
 
107
- for ( $j = 0; $j < count( $rule->rules ); $j++ ) {
108
  $subRule = $rule->rules[$j];
109
- if ( !( $subRule instanceof Less_Tree_Rule ) || !$subRule->variable ) {
110
- array_splice( $ruleset->rules, ++$i, 0, array( $subRule ) );
111
  $rsRuleCnt++;
112
  }
113
  }
114
 
115
- }
116
- }
117
- }
 
118
 
119
  // Pop the stack
120
  $env->shiftFrame();
121
 
122
- if ( $mediaBlockCount ) {
123
- $len = count( $env->mediaBlocks );
124
- for ( $i = $mediaBlockCount; $i < $len; $i++ ) {
125
- $env->mediaBlocks[$i]->bubbleSelectors( $ruleset->selectors );
126
  }
127
  }
128
 
@@ -135,67 +139,69 @@ class Less_Tree_Ruleset extends Less_Tree {
135
  * @param Less_Tree_Ruleset $ruleset
136
  * @param integer $rsRuleCnt
137
  */
138
- private function EvalMixinCalls( $ruleset, $env, &$rsRuleCnt ) {
139
- for ( $i = 0; $i < $rsRuleCnt; $i++ ) {
140
  $rule = $ruleset->rules[$i];
141
 
142
- if ( $rule instanceof Less_Tree_Mixin_Call ) {
143
- $rule = $rule->compile( $env );
144
 
145
  $temp = array();
146
- foreach ( $rule as $r ) {
147
- if ( ( $r instanceof Less_Tree_Rule ) && $r->variable ) {
148
  // do not pollute the scope if the variable is
149
  // already there. consider returning false here
150
  // but we need a way to "return" variable from mixins
151
- if ( !$ruleset->variable( $r->name ) ) {
152
  $temp[] = $r;
153
  }
154
- } else {
155
  $temp[] = $r;
156
  }
157
  }
158
- $temp_count = count( $temp ) - 1;
159
- array_splice( $ruleset->rules, $i, 1, $temp );
160
  $rsRuleCnt += $temp_count;
161
  $i += $temp_count;
162
  $ruleset->resetCache();
163
 
164
- } elseif ( $rule instanceof Less_Tree_RulesetCall ) {
165
 
166
- $rule = $rule->compile( $env );
167
  $rules = array();
168
- foreach ( $rule->rules as $r ) {
169
- if ( ( $r instanceof Less_Tree_Rule ) && $r->variable ) {
170
  continue;
171
  }
172
  $rules[] = $r;
173
  }
174
 
175
- array_splice( $ruleset->rules, $i, 1, $rules );
176
- $temp_count = count( $rules );
177
  $rsRuleCnt += $temp_count - 1;
178
- $i += $temp_count - 1;
179
  $ruleset->resetCache();
180
  }
181
 
182
  }
183
  }
184
 
 
185
  /**
186
  * Compile the selectors and create a new ruleset object for the compile() method
187
  *
188
  */
189
- private function PrepareRuleset( $env ) {
 
190
  $hasOnePassingSelector = false;
191
  $selectors = array();
192
- if ( $this->selectors ) {
193
- Less_Tree_DefaultFunc::error( "it is currently only allowed in parametric mixin guards," );
194
 
195
- foreach ( $this->selectors as $s ) {
196
- $selector = $s->compile( $env );
197
  $selectors[] = $selector;
198
- if ( $selector->evaldCondition ) {
199
  $hasOnePassingSelector = true;
200
  }
201
  }
@@ -205,13 +211,13 @@ class Less_Tree_Ruleset extends Less_Tree {
205
  $hasOnePassingSelector = true;
206
  }
207
 
208
- if ( $this->rules && $hasOnePassingSelector ) {
209
  $rules = $this->rules;
210
- } else {
211
  $rules = array();
212
  }
213
 
214
- $ruleset = new Less_Tree_Ruleset( $selectors, $rules, $this->strictImports );
215
 
216
  $ruleset->originalRuleset = $this->ruleset_id;
217
 
@@ -219,31 +225,34 @@ class Less_Tree_Ruleset extends Less_Tree {
219
  $ruleset->firstRoot = $this->firstRoot;
220
  $ruleset->allowImports = $this->allowImports;
221
 
 
222
  // push the current ruleset to the frames stack
223
- $env->unshiftFrame( $ruleset );
 
224
 
225
  // Evaluate imports
226
- if ( $ruleset->root || $ruleset->allowImports || !$ruleset->strictImports ) {
227
- $ruleset->evalImports( $env );
228
  }
229
 
230
  return $ruleset;
231
  }
232
 
233
- function evalImports( $env ) {
234
- $rules_len = count( $this->rules );
235
- for ( $i = 0; $i < $rules_len; $i++ ) {
 
236
  $rule = $this->rules[$i];
237
 
238
- if ( $rule instanceof Less_Tree_Import ) {
239
- $rules = $rule->compile( $env );
240
- if ( is_array( $rules ) ) {
241
- array_splice( $this->rules, $i, 1, $rules );
242
- $temp_count = count( $rules ) - 1;
243
  $i += $temp_count;
244
  $rules_len += $temp_count;
245
- } else {
246
- array_splice( $this->rules, $i, 1, array( $rules ) );
247
  }
248
 
249
  $this->resetCache();
@@ -251,64 +260,67 @@ class Less_Tree_Ruleset extends Less_Tree {
251
  }
252
  }
253
 
254
- function makeImportant() {
 
255
  $important_rules = array();
256
- foreach ( $this->rules as $rule ) {
257
- if ( $rule instanceof Less_Tree_Rule || $rule instanceof Less_Tree_Ruleset || $rule instanceof Less_Tree_NameValue ) {
258
  $important_rules[] = $rule->makeImportant();
259
- } else {
260
  $important_rules[] = $rule;
261
  }
262
  }
263
 
264
- return new Less_Tree_Ruleset( $this->selectors, $important_rules, $this->strictImports );
265
  }
266
 
267
- public function matchArgs( $args ) {
268
  return !$args;
269
  }
270
 
271
  // lets you call a css selector with a guard
272
- public function matchCondition( $args, $env ) {
273
- $lastSelector = end( $this->selectors );
274
 
275
- if ( !$lastSelector->evaldCondition ) {
276
  return false;
277
  }
278
- if ( $lastSelector->condition && !$lastSelector->condition->compile( $env->copyEvalEnv( $env->frames ) ) ) {
279
  return false;
280
  }
281
  return true;
282
  }
283
 
284
- function resetCache() {
285
  $this->_rulesets = null;
286
  $this->_variables = null;
287
  $this->lookups = array();
288
  }
289
 
290
- public function variables() {
291
  $this->_variables = array();
292
- foreach ( $this->rules as $r ) {
293
- if ( $r instanceof Less_Tree_Rule && $r->variable === true ) {
294
  $this->_variables[$r->name] = $r;
295
  }
296
  }
297
  }
298
 
299
- public function variable( $name ) {
300
- if ( is_null( $this->_variables ) ) {
 
301
  $this->variables();
302
  }
303
- return isset( $this->_variables[$name] ) ? $this->_variables[$name] : null;
304
  }
305
 
306
- public function find( $selector, $self = null ) {
307
- $key = implode( ' ', $selector->_oelements );
308
 
309
- if ( !isset( $this->lookups[$key] ) ) {
310
 
311
- if ( !$self ) {
 
 
312
  $self = $this->ruleset_id;
313
  }
314
 
@@ -316,16 +328,16 @@ class Less_Tree_Ruleset extends Less_Tree {
316
 
317
  $first_oelement = $selector->_oelements[0];
318
 
319
- foreach ( $this->rules as $rule ) {
320
- if ( $rule instanceof Less_Tree_Ruleset && $rule->ruleset_id != $self ) {
321
 
322
- if ( isset( $rule->first_oelements[$first_oelement] ) ) {
323
 
324
- foreach ( $rule->selectors as $ruleSelector ) {
325
- $match = $selector->match( $ruleSelector );
326
- if ( $match ) {
327
- if ( $selector->elements_len > $match ) {
328
- $this->lookups[$key] = array_merge( $this->lookups[$key], $rule->find( new Less_Tree_Selector( array_slice( $selector->elements, $match ) ), $self ) );
329
  } else {
330
  $this->lookups[$key][] = $rule;
331
  }
@@ -340,39 +352,42 @@ class Less_Tree_Ruleset extends Less_Tree {
340
  return $this->lookups[$key];
341
  }
342
 
 
343
  /**
344
  * @see Less_Tree::genCSS
345
  */
346
- public function genCSS( $output ) {
347
- if ( !$this->root ) {
 
348
  Less_Environment::$tabLevel++;
349
  }
350
 
351
  $tabRuleStr = $tabSetStr = '';
352
- if ( !Less_Parser::$options['compress'] ) {
353
- if ( Less_Environment::$tabLevel ) {
354
- $tabRuleStr = "\n".str_repeat( Less_Parser::$options['indentation'], Less_Environment::$tabLevel );
355
- $tabSetStr = "\n".str_repeat( Less_Parser::$options['indentation'], Less_Environment::$tabLevel - 1 );
356
- } else {
357
  $tabSetStr = $tabRuleStr = "\n";
358
  }
359
  }
360
 
 
361
  $ruleNodes = array();
362
  $rulesetNodes = array();
363
- foreach ( $this->rules as $rule ) {
364
 
365
- $class = get_class( $rule );
366
- if ( ( $class === 'Less_Tree_Media' ) || ( $class === 'Less_Tree_Directive' ) || ( $this->root && $class === 'Less_Tree_Comment' ) || ( $class === 'Less_Tree_Ruleset' && $rule->rules ) ) {
367
  $rulesetNodes[] = $rule;
368
- } else {
369
  $ruleNodes[] = $rule;
370
  }
371
  }
372
 
373
  // If this is the root node, we don't render
374
  // a selector, or {}.
375
- if ( !$this->root ) {
376
 
377
  /*
378
  debugInfo = tree.debugInfo(env, this, tabSetStr);
@@ -383,108 +398,111 @@ class Less_Tree_Ruleset extends Less_Tree {
383
  }
384
  */
385
 
386
- $paths_len = count( $this->paths );
387
- for ( $i = 0; $i < $paths_len; $i++ ) {
388
  $path = $this->paths[$i];
389
  $firstSelector = true;
390
 
391
- foreach ( $path as $p ) {
392
  $p->genCSS( $output, $firstSelector );
393
  $firstSelector = false;
394
  }
395
 
396
- if ( $i + 1 < $paths_len ) {
397
  $output->add( ',' . $tabSetStr );
398
  }
399
  }
400
 
401
- $output->add( ( Less_Parser::$options['compress'] ? '{' : " {" ) . $tabRuleStr );
402
  }
403
 
404
  // Compile rules and rulesets
405
- $ruleNodes_len = count( $ruleNodes );
406
- $rulesetNodes_len = count( $rulesetNodes );
407
- for ( $i = 0; $i < $ruleNodes_len; $i++ ) {
408
  $rule = $ruleNodes[$i];
409
 
410
  // @page{ directive ends up with root elements inside it, a mix of rules and rulesets
411
  // In this instance we do not know whether it is the last property
412
- if ( $i + 1 === $ruleNodes_len && ( !$this->root || $rulesetNodes_len === 0 || $this->firstRoot ) ) {
413
  Less_Environment::$lastRule = true;
414
  }
415
 
416
  $rule->genCSS( $output );
417
 
418
- if ( !Less_Environment::$lastRule ) {
419
  $output->add( $tabRuleStr );
420
- } else {
421
  Less_Environment::$lastRule = false;
422
  }
423
  }
424
 
425
- if ( !$this->root ) {
426
  $output->add( $tabSetStr . '}' );
427
  Less_Environment::$tabLevel--;
428
  }
429
 
430
  $firstRuleset = true;
431
- $space = ( $this->root ? $tabRuleStr : $tabSetStr );
432
- for ( $i = 0; $i < $rulesetNodes_len; $i++ ) {
433
 
434
- if ( $ruleNodes_len && $firstRuleset ) {
435
  $output->add( $space );
436
- } elseif ( !$firstRuleset ) {
437
  $output->add( $space );
438
  }
439
  $firstRuleset = false;
440
- $rulesetNodes[$i]->genCSS( $output );
441
  }
442
 
443
- if ( !Less_Parser::$options['compress'] && $this->firstRoot ) {
444
  $output->add( "\n" );
445
  }
446
 
447
  }
448
 
449
- function markReferenced() {
450
- if ( !$this->selectors ) {
 
451
  return;
452
  }
453
- foreach ( $this->selectors as $selector ) {
454
  $selector->markReferenced();
455
  }
456
  }
457
 
458
- public function joinSelectors( $context, $selectors ) {
459
  $paths = array();
460
- if ( is_array( $selectors ) ) {
461
- foreach ( $selectors as $selector ) {
462
- $this->joinSelector( $paths, $context, $selector );
463
  }
464
  }
465
  return $paths;
466
  }
467
 
468
- public function joinSelector( &$paths, $context, $selector ) {
 
469
  $hasParentSelector = false;
470
 
471
- foreach ( $selector->elements as $el ) {
472
- if ( $el->value === '&' ) {
473
  $hasParentSelector = true;
474
  }
475
  }
476
 
477
- if ( !$hasParentSelector ) {
478
- if ( $context ) {
479
- foreach ( $context as $context_el ) {
480
- $paths[] = array_merge( $context_el, array( $selector ) );
481
  }
482
- } else {
483
- $paths[] = array( $selector );
484
  }
485
  return;
486
  }
487
 
 
488
  // The paths are [[Selector]]
489
  // The first list is a list of comma separated selectors
490
  // The inner list is a list of inheritance separated selectors
@@ -501,12 +519,13 @@ class Less_Tree_Ruleset extends Less_Tree {
501
  // the current list of new selectors to add to the path.
502
  // We will build it up. We initiate it with one empty selector as we "multiply" the new selectors
503
  // by the parents
504
- $newSelectors = array( array() );
 
505
 
506
- foreach ( $selector->elements as $el ) {
507
 
508
  // non parent reference elements just get added
509
- if ( $el->value !== '&' ) {
510
  $currentElements[] = $el;
511
  } else {
512
  // the new list of selectors to add
@@ -514,27 +533,27 @@ class Less_Tree_Ruleset extends Less_Tree {
514
 
515
  // merge the current list of non parent selector elements
516
  // on to the current list of selectors to add
517
- if ( $currentElements ) {
518
- $this->mergeElementsOnToSelectors( $currentElements, $newSelectors );
519
  }
520
 
521
  // loop through our current selectors
522
- foreach ( $newSelectors as $sel ) {
523
 
524
  // if we don't have any parent paths, the & might be in a mixin so that it can be used
525
  // whether there are parents or not
526
- if ( !$context ) {
527
  // the combinator used on el should now be applied to the next element instead so that
528
  // it is not lost
529
- if ( $sel ) {
530
- $sel[0]->elements = array_slice( $sel[0]->elements, 0 );
531
- $sel[0]->elements[] = new Less_Tree_Element( $el->combinator, '', $el->index, $el->currentFileInfo );
532
  }
533
  $selectorsMultiplied[] = $sel;
534
- } else {
535
 
536
  // and the parent selectors
537
- foreach ( $context as $parentSel ) {
538
  // We need to put the current selectors
539
  // then join the last selector's elements on to the parents selectors
540
 
@@ -544,38 +563,39 @@ class Less_Tree_Ruleset extends Less_Tree {
544
  $afterParentJoin = array();
545
  $newJoinedSelectorEmpty = true;
546
 
547
- // construct the joined selector - if & is the first thing this will be empty,
548
  // if not newJoinedSelector will be the last set of elements in the selector
549
- if ( $sel ) {
550
  $newSelectorPath = $sel;
551
- $lastSelector = array_pop( $newSelectorPath );
552
- $newJoinedSelector = $selector->createDerived( array_slice( $lastSelector->elements, 0 ) );
553
  $newJoinedSelectorEmpty = false;
554
- } else {
555
- $newJoinedSelector = $selector->createDerived( array() );
 
556
  }
557
 
558
- // put together the parent selectors after the join
559
- if ( count( $parentSel ) > 1 ) {
560
- $afterParentJoin = array_merge( $afterParentJoin, array_slice( $parentSel, 1 ) );
561
  }
562
 
563
- if ( $parentSel ) {
564
  $newJoinedSelectorEmpty = false;
565
 
566
  // join the elements so far with the first part of the parent
567
- $newJoinedSelector->elements[] = new Less_Tree_Element( $el->combinator, $parentSel[0]->elements[0]->value, $el->index, $el->currentFileInfo );
568
 
569
- $newJoinedSelector->elements = array_merge( $newJoinedSelector->elements, array_slice( $parentSel[0]->elements, 1 ) );
570
  }
571
 
572
- if ( !$newJoinedSelectorEmpty ) {
573
  // now add the joined selector
574
  $newSelectorPath[] = $newJoinedSelector;
575
  }
576
 
577
  // and the rest of the parent
578
- $newSelectorPath = array_merge( $newSelectorPath, $afterParentJoin );
579
 
580
  // add that to our new set of selectors
581
  $selectorsMultiplied[] = $newSelectorPath;
@@ -591,29 +611,31 @@ class Less_Tree_Ruleset extends Less_Tree {
591
 
592
  // if we have any elements left over (e.g. .a& .b == .b)
593
  // add them on to all the current selectors
594
- if ( $currentElements ) {
595
- $this->mergeElementsOnToSelectors( $currentElements, $newSelectors );
596
  }
597
- foreach ( $newSelectors as $new_sel ) {
598
- if ( $new_sel ) {
599
  $paths[] = $new_sel;
600
  }
601
  }
602
  }
603
 
604
- function mergeElementsOnToSelectors( $elements, &$selectors ) {
605
- if ( !$selectors ) {
606
- $selectors[] = array( new Less_Tree_Selector( $elements ) );
 
607
  return;
608
  }
609
 
610
- foreach ( $selectors as &$sel ) {
 
611
 
612
  // if the previous thing in sel is a parent this needs to join on to it
613
- if ( $sel ) {
614
- $last = count( $sel ) - 1;
615
- $sel[$last] = $sel[$last]->createDerived( array_merge( $sel[$last]->elements, $elements ) );
616
- } else {
617
  $sel[] = new Less_Tree_Selector( $elements );
618
  }
619
  }
6
  * @package Less
7
  * @subpackage tree
8
  */
9
+ class Less_Tree_Ruleset extends Less_Tree{
10
 
11
  protected $lookups;
12
  public $_variables;
29
 
30
  public $first_oelements;
31
 
32
+ public function SetRulesetIndex(){
33
  $this->ruleset_id = Less_Parser::$next_id++;
34
  $this->originalRuleset = $this->ruleset_id;
35
 
36
+ if( $this->selectors ){
37
+ foreach($this->selectors as $sel){
38
+ if( $sel->_oelements ){
39
  $this->first_oelements[$sel->_oelements[0]] = true;
40
  }
41
  }
42
  }
43
  }
44
 
45
+ public function __construct($selectors, $rules, $strictImports = null){
46
  $this->selectors = $selectors;
47
  $this->rules = $rules;
48
  $this->lookups = array();
50
  $this->SetRulesetIndex();
51
  }
52
 
53
+ public function accept( $visitor ){
54
+ if( $this->paths ){
55
+ $paths_len = count($this->paths);
56
+ for($i = 0,$paths_len; $i < $paths_len; $i++ ){
57
+ $this->paths[$i] = $visitor->visitArray($this->paths[$i]);
58
  }
59
+ }elseif( $this->selectors ){
60
+ $this->selectors = $visitor->visitArray($this->selectors);
61
  }
62
 
63
+ if( $this->rules ){
64
+ $this->rules = $visitor->visitArray($this->rules);
65
  }
66
  }
67
 
68
+ public function compile($env){
69
+
70
+ $ruleset = $this->PrepareRuleset($env);
71
+
72
 
73
  // Store the frames around mixin definitions,
74
  // so they can be evaluated like closures when the time comes.
75
+ $rsRuleCnt = count($ruleset->rules);
76
+ for( $i = 0; $i < $rsRuleCnt; $i++ ){
77
+ if( $ruleset->rules[$i] instanceof Less_Tree_Mixin_Definition || $ruleset->rules[$i] instanceof Less_Tree_DetachedRuleset ){
78
+ $ruleset->rules[$i] = $ruleset->rules[$i]->compile($env);
79
  }
80
  }
81
 
82
  $mediaBlockCount = 0;
83
+ if( $env instanceof Less_Environment ){
84
+ $mediaBlockCount = count($env->mediaBlocks);
85
  }
86
 
87
  // Evaluate mixin calls.
88
  $this->EvalMixinCalls( $ruleset, $env, $rsRuleCnt );
89
 
90
+
91
  // Evaluate everything else
92
+ for( $i=0; $i<$rsRuleCnt; $i++ ){
93
+ if(! ($ruleset->rules[$i] instanceof Less_Tree_Mixin_Definition || $ruleset->rules[$i] instanceof Less_Tree_DetachedRuleset) ){
94
+ $ruleset->rules[$i] = $ruleset->rules[$i]->compile($env);
95
  }
96
  }
97
 
98
+ // Evaluate everything else
99
+ for( $i=0; $i<$rsRuleCnt; $i++ ){
100
  $rule = $ruleset->rules[$i];
101
 
102
+ // for rulesets, check if it is a css guard and can be removed
103
+ if( $rule instanceof Less_Tree_Ruleset && $rule->selectors && count($rule->selectors) === 1 ){
104
 
105
+ // check if it can be folded in (e.g. & where)
106
+ if( $rule->selectors[0]->isJustParentSelector() ){
107
+ array_splice($ruleset->rules,$i--,1);
108
  $rsRuleCnt--;
109
 
110
+ for($j = 0; $j < count($rule->rules); $j++ ){
111
  $subRule = $rule->rules[$j];
112
+ if( !($subRule instanceof Less_Tree_Rule) || !$subRule->variable ){
113
+ array_splice($ruleset->rules, ++$i, 0, array($subRule));
114
  $rsRuleCnt++;
115
  }
116
  }
117
 
118
+ }
119
+ }
120
+ }
121
+
122
 
123
  // Pop the stack
124
  $env->shiftFrame();
125
 
126
+ if ($mediaBlockCount) {
127
+ $len = count($env->mediaBlocks);
128
+ for($i = $mediaBlockCount; $i < $len; $i++ ){
129
+ $env->mediaBlocks[$i]->bubbleSelectors($ruleset->selectors);
130
  }
131
  }
132
 
139
  * @param Less_Tree_Ruleset $ruleset
140
  * @param integer $rsRuleCnt
141
  */
142
+ private function EvalMixinCalls( $ruleset, $env, &$rsRuleCnt ){
143
+ for($i=0; $i < $rsRuleCnt; $i++){
144
  $rule = $ruleset->rules[$i];
145
 
146
+ if( $rule instanceof Less_Tree_Mixin_Call ){
147
+ $rule = $rule->compile($env);
148
 
149
  $temp = array();
150
+ foreach($rule as $r){
151
+ if( ($r instanceof Less_Tree_Rule) && $r->variable ){
152
  // do not pollute the scope if the variable is
153
  // already there. consider returning false here
154
  // but we need a way to "return" variable from mixins
155
+ if( !$ruleset->variable($r->name) ){
156
  $temp[] = $r;
157
  }
158
+ }else{
159
  $temp[] = $r;
160
  }
161
  }
162
+ $temp_count = count($temp)-1;
163
+ array_splice($ruleset->rules, $i, 1, $temp);
164
  $rsRuleCnt += $temp_count;
165
  $i += $temp_count;
166
  $ruleset->resetCache();
167
 
168
+ }elseif( $rule instanceof Less_Tree_RulesetCall ){
169
 
170
+ $rule = $rule->compile($env);
171
  $rules = array();
172
+ foreach($rule->rules as $r){
173
+ if( ($r instanceof Less_Tree_Rule) && $r->variable ){
174
  continue;
175
  }
176
  $rules[] = $r;
177
  }
178
 
179
+ array_splice($ruleset->rules, $i, 1, $rules);
180
+ $temp_count = count($rules);
181
  $rsRuleCnt += $temp_count - 1;
182
+ $i += $temp_count-1;
183
  $ruleset->resetCache();
184
  }
185
 
186
  }
187
  }
188
 
189
+
190
  /**
191
  * Compile the selectors and create a new ruleset object for the compile() method
192
  *
193
  */
194
+ private function PrepareRuleset($env){
195
+
196
  $hasOnePassingSelector = false;
197
  $selectors = array();
198
+ if( $this->selectors ){
199
+ Less_Tree_DefaultFunc::error("it is currently only allowed in parametric mixin guards,");
200
 
201
+ foreach($this->selectors as $s){
202
+ $selector = $s->compile($env);
203
  $selectors[] = $selector;
204
+ if( $selector->evaldCondition ){
205
  $hasOnePassingSelector = true;
206
  }
207
  }
211
  $hasOnePassingSelector = true;
212
  }
213
 
214
+ if( $this->rules && $hasOnePassingSelector ){
215
  $rules = $this->rules;
216
+ }else{
217
  $rules = array();
218
  }
219
 
220
+ $ruleset = new Less_Tree_Ruleset($selectors, $rules, $this->strictImports);
221
 
222
  $ruleset->originalRuleset = $this->ruleset_id;
223
 
225
  $ruleset->firstRoot = $this->firstRoot;
226
  $ruleset->allowImports = $this->allowImports;
227
 
228
+
229
  // push the current ruleset to the frames stack
230
+ $env->unshiftFrame($ruleset);
231
+
232
 
233
  // Evaluate imports
234
+ if( $ruleset->root || $ruleset->allowImports || !$ruleset->strictImports ){
235
+ $ruleset->evalImports($env);
236
  }
237
 
238
  return $ruleset;
239
  }
240
 
241
+ function evalImports($env) {
242
+
243
+ $rules_len = count($this->rules);
244
+ for($i=0; $i < $rules_len; $i++){
245
  $rule = $this->rules[$i];
246
 
247
+ if( $rule instanceof Less_Tree_Import ){
248
+ $rules = $rule->compile($env);
249
+ if( is_array($rules) ){
250
+ array_splice($this->rules, $i, 1, $rules);
251
+ $temp_count = count($rules)-1;
252
  $i += $temp_count;
253
  $rules_len += $temp_count;
254
+ }else{
255
+ array_splice($this->rules, $i, 1, array($rules));
256
  }
257
 
258
  $this->resetCache();
260
  }
261
  }
262
 
263
+ function makeImportant(){
264
+
265
  $important_rules = array();
266
+ foreach($this->rules as $rule){
267
+ if( $rule instanceof Less_Tree_Rule || $rule instanceof Less_Tree_Ruleset || $rule instanceof Less_Tree_NameValue ){
268
  $important_rules[] = $rule->makeImportant();
269
+ }else{
270
  $important_rules[] = $rule;
271
  }
272
  }
273
 
274
+ return new Less_Tree_Ruleset($this->selectors, $important_rules, $this->strictImports );
275
  }
276
 
277
+ public function matchArgs($args){
278
  return !$args;
279
  }
280
 
281
  // lets you call a css selector with a guard
282
+ public function matchCondition( $args, $env ){
283
+ $lastSelector = end($this->selectors);
284
 
285
+ if( !$lastSelector->evaldCondition ){
286
  return false;
287
  }
288
+ if( $lastSelector->condition && !$lastSelector->condition->compile( $env->copyEvalEnv( $env->frames ) ) ){
289
  return false;
290
  }
291
  return true;
292
  }
293
 
294
+ function resetCache(){
295
  $this->_rulesets = null;
296
  $this->_variables = null;
297
  $this->lookups = array();
298
  }
299
 
300
+ public function variables(){
301
  $this->_variables = array();
302
+ foreach( $this->rules as $r){
303
+ if ($r instanceof Less_Tree_Rule && $r->variable === true) {
304
  $this->_variables[$r->name] = $r;
305
  }
306
  }
307
  }
308
 
309
+ public function variable($name){
310
+
311
+ if( is_null($this->_variables) ){
312
  $this->variables();
313
  }
314
+ return isset($this->_variables[$name]) ? $this->_variables[$name] : null;
315
  }
316
 
317
+ public function find( $selector, $self = null ){
 
318
 
319
+ $key = implode(' ',$selector->_oelements);
320
 
321
+ if( !isset($this->lookups[$key]) ){
322
+
323
+ if( !$self ){
324
  $self = $this->ruleset_id;
325
  }
326
 
328
 
329
  $first_oelement = $selector->_oelements[0];
330
 
331
+ foreach($this->rules as $rule){
332
+ if( $rule instanceof Less_Tree_Ruleset && $rule->ruleset_id != $self ){
333
 
334
+ if( isset($rule->first_oelements[$first_oelement]) ){
335
 
336
+ foreach( $rule->selectors as $ruleSelector ){
337
+ $match = $selector->match($ruleSelector);
338
+ if( $match ){
339
+ if( $selector->elements_len > $match ){
340
+ $this->lookups[$key] = array_merge($this->lookups[$key], $rule->find( new Less_Tree_Selector(array_slice($selector->elements, $match)), $self));
341
  } else {
342
  $this->lookups[$key][] = $rule;
343
  }
352
  return $this->lookups[$key];
353
  }
354
 
355
+
356
  /**
357
  * @see Less_Tree::genCSS
358
  */
359
+ public function genCSS( $output ){
360
+
361
+ if( !$this->root ){
362
  Less_Environment::$tabLevel++;
363
  }
364
 
365
  $tabRuleStr = $tabSetStr = '';
366
+ if( !Less_Parser::$options['compress'] ){
367
+ if( Less_Environment::$tabLevel ){
368
+ $tabRuleStr = "\n".str_repeat( Less_Parser::$options['indentation'] , Less_Environment::$tabLevel );
369
+ $tabSetStr = "\n".str_repeat( Less_Parser::$options['indentation'] , Less_Environment::$tabLevel-1 );
370
+ }else{
371
  $tabSetStr = $tabRuleStr = "\n";
372
  }
373
  }
374
 
375
+
376
  $ruleNodes = array();
377
  $rulesetNodes = array();
378
+ foreach($this->rules as $rule){
379
 
380
+ $class = get_class($rule);
381
+ if( ($class === 'Less_Tree_Media') || ($class === 'Less_Tree_Directive') || ($this->root && $class === 'Less_Tree_Comment') || ($class === 'Less_Tree_Ruleset' && $rule->rules) ){
382
  $rulesetNodes[] = $rule;
383
+ }else{
384
  $ruleNodes[] = $rule;
385
  }
386
  }
387
 
388
  // If this is the root node, we don't render
389
  // a selector, or {}.
390
+ if( !$this->root ){
391
 
392
  /*
393
  debugInfo = tree.debugInfo(env, this, tabSetStr);
398
  }
399
  */
400
 
401
+ $paths_len = count($this->paths);
402
+ for( $i = 0; $i < $paths_len; $i++ ){
403
  $path = $this->paths[$i];
404
  $firstSelector = true;
405
 
406
+ foreach($path as $p){
407
  $p->genCSS( $output, $firstSelector );
408
  $firstSelector = false;
409
  }
410
 
411
+ if( $i + 1 < $paths_len ){
412
  $output->add( ',' . $tabSetStr );
413
  }
414
  }
415
 
416
+ $output->add( (Less_Parser::$options['compress'] ? '{' : " {") . $tabRuleStr );
417
  }
418
 
419
  // Compile rules and rulesets
420
+ $ruleNodes_len = count($ruleNodes);
421
+ $rulesetNodes_len = count($rulesetNodes);
422
+ for( $i = 0; $i < $ruleNodes_len; $i++ ){
423
  $rule = $ruleNodes[$i];
424
 
425
  // @page{ directive ends up with root elements inside it, a mix of rules and rulesets
426
  // In this instance we do not know whether it is the last property
427
+ if( $i + 1 === $ruleNodes_len && (!$this->root || $rulesetNodes_len === 0 || $this->firstRoot ) ){
428
  Less_Environment::$lastRule = true;
429
  }
430
 
431
  $rule->genCSS( $output );
432
 
433
+ if( !Less_Environment::$lastRule ){
434
  $output->add( $tabRuleStr );
435
+ }else{
436
  Less_Environment::$lastRule = false;
437
  }
438
  }
439
 
440
+ if( !$this->root ){
441
  $output->add( $tabSetStr . '}' );
442
  Less_Environment::$tabLevel--;
443
  }
444
 
445
  $firstRuleset = true;
446
+ $space = ($this->root ? $tabRuleStr : $tabSetStr);
447
+ for( $i = 0; $i < $rulesetNodes_len; $i++ ){
448
 
449
+ if( $ruleNodes_len && $firstRuleset ){
450
  $output->add( $space );
451
+ }elseif( !$firstRuleset ){
452
  $output->add( $space );
453
  }
454
  $firstRuleset = false;
455
+ $rulesetNodes[$i]->genCSS( $output);
456
  }
457
 
458
+ if( !Less_Parser::$options['compress'] && $this->firstRoot ){
459
  $output->add( "\n" );
460
  }
461
 
462
  }
463
 
464
+
465
+ function markReferenced(){
466
+ if( !$this->selectors ){
467
  return;
468
  }
469
+ foreach($this->selectors as $selector){
470
  $selector->markReferenced();
471
  }
472
  }
473
 
474
+ public function joinSelectors( $context, $selectors ){
475
  $paths = array();
476
+ if( is_array($selectors) ){
477
+ foreach($selectors as $selector) {
478
+ $this->joinSelector( $paths, $context, $selector);
479
  }
480
  }
481
  return $paths;
482
  }
483
 
484
+ public function joinSelector( &$paths, $context, $selector){
485
+
486
  $hasParentSelector = false;
487
 
488
+ foreach($selector->elements as $el) {
489
+ if( $el->value === '&') {
490
  $hasParentSelector = true;
491
  }
492
  }
493
 
494
+ if( !$hasParentSelector ){
495
+ if( $context ){
496
+ foreach($context as $context_el){
497
+ $paths[] = array_merge($context_el, array($selector) );
498
  }
499
+ }else {
500
+ $paths[] = array($selector);
501
  }
502
  return;
503
  }
504
 
505
+
506
  // The paths are [[Selector]]
507
  // The first list is a list of comma separated selectors
508
  // The inner list is a list of inheritance separated selectors
519
  // the current list of new selectors to add to the path.
520
  // We will build it up. We initiate it with one empty selector as we "multiply" the new selectors
521
  // by the parents
522
+ $newSelectors = array(array());
523
+
524
 
525
+ foreach( $selector->elements as $el){
526
 
527
  // non parent reference elements just get added
528
+ if( $el->value !== '&' ){
529
  $currentElements[] = $el;
530
  } else {
531
  // the new list of selectors to add
533
 
534
  // merge the current list of non parent selector elements
535
  // on to the current list of selectors to add
536
+ if( $currentElements ){
537
+ $this->mergeElementsOnToSelectors( $currentElements, $newSelectors);
538
  }
539
 
540
  // loop through our current selectors
541
+ foreach($newSelectors as $sel){
542
 
543
  // if we don't have any parent paths, the & might be in a mixin so that it can be used
544
  // whether there are parents or not
545
+ if( !$context ){
546
  // the combinator used on el should now be applied to the next element instead so that
547
  // it is not lost
548
+ if( $sel ){
549
+ $sel[0]->elements = array_slice($sel[0]->elements,0);
550
+ $sel[0]->elements[] = new Less_Tree_Element($el->combinator, '', $el->index, $el->currentFileInfo );
551
  }
552
  $selectorsMultiplied[] = $sel;
553
+ }else {
554
 
555
  // and the parent selectors
556
+ foreach($context as $parentSel){
557
  // We need to put the current selectors
558
  // then join the last selector's elements on to the parents selectors
559
 
563
  $afterParentJoin = array();
564
  $newJoinedSelectorEmpty = true;
565
 
566
+ //construct the joined selector - if & is the first thing this will be empty,
567
  // if not newJoinedSelector will be the last set of elements in the selector
568
+ if( $sel ){
569
  $newSelectorPath = $sel;
570
+ $lastSelector = array_pop($newSelectorPath);
571
+ $newJoinedSelector = $selector->createDerived( array_slice($lastSelector->elements,0) );
572
  $newJoinedSelectorEmpty = false;
573
+ }
574
+ else {
575
+ $newJoinedSelector = $selector->createDerived(array());
576
  }
577
 
578
+ //put together the parent selectors after the join
579
+ if ( count($parentSel) > 1) {
580
+ $afterParentJoin = array_merge($afterParentJoin, array_slice($parentSel,1) );
581
  }
582
 
583
+ if ( $parentSel ){
584
  $newJoinedSelectorEmpty = false;
585
 
586
  // join the elements so far with the first part of the parent
587
+ $newJoinedSelector->elements[] = new Less_Tree_Element( $el->combinator, $parentSel[0]->elements[0]->value, $el->index, $el->currentFileInfo);
588
 
589
+ $newJoinedSelector->elements = array_merge( $newJoinedSelector->elements, array_slice($parentSel[0]->elements, 1) );
590
  }
591
 
592
+ if (!$newJoinedSelectorEmpty) {
593
  // now add the joined selector
594
  $newSelectorPath[] = $newJoinedSelector;
595
  }
596
 
597
  // and the rest of the parent
598
+ $newSelectorPath = array_merge($newSelectorPath, $afterParentJoin);
599
 
600
  // add that to our new set of selectors
601
  $selectorsMultiplied[] = $newSelectorPath;
611
 
612
  // if we have any elements left over (e.g. .a& .b == .b)
613
  // add them on to all the current selectors
614
+ if( $currentElements ){
615
+ $this->mergeElementsOnToSelectors($currentElements, $newSelectors);
616
  }
617
+ foreach( $newSelectors as $new_sel){
618
+ if( $new_sel ){
619
  $paths[] = $new_sel;
620
  }
621
  }
622
  }
623
 
624
+ function mergeElementsOnToSelectors( $elements, &$selectors){
625
+
626
+ if( !$selectors ){
627
+ $selectors[] = array( new Less_Tree_Selector($elements) );
628
  return;
629
  }
630
 
631
+
632
+ foreach( $selectors as &$sel){
633
 
634
  // if the previous thing in sel is a parent this needs to join on to it
635
+ if( $sel ){
636
+ $last = count($sel)-1;
637
+ $sel[$last] = $sel[$last]->createDerived( array_merge($sel[$last]->elements, $elements) );
638
+ }else{
639
  $sel[] = new Less_Tree_Selector( $elements );
640
  }
641
  }
base/inc/lib/Less/Tree/RulesetCall.php CHANGED
@@ -6,21 +6,21 @@
6
  * @package Less
7
  * @subpackage tree
8
  */
9
- class Less_Tree_RulesetCall extends Less_Tree {
10
 
11
  public $variable;
12
  public $type = "RulesetCall";
13
 
14
- public function __construct( $variable ) {
15
  $this->variable = $variable;
16
  }
17
 
18
- public function accept( $visitor ) {
19
- }
20
 
21
- public function compile( $env ) {
22
- $variable = new Less_Tree_Variable( $this->variable );
23
- $detachedRuleset = $variable->compile( $env );
24
- return $detachedRuleset->callEval( $env );
25
  }
26
  }
 
6
  * @package Less
7
  * @subpackage tree
8
  */
9
+ class Less_Tree_RulesetCall extends Less_Tree{
10
 
11
  public $variable;
12
  public $type = "RulesetCall";
13
 
14
+ public function __construct($variable){
15
  $this->variable = $variable;
16
  }
17
 
18
+ public function accept($visitor) {}
 
19
 
20
+ public function compile( $env ){
21
+ $variable = new Less_Tree_Variable($this->variable);
22
+ $detachedRuleset = $variable->compile($env);
23
+ return $detachedRuleset->callEval($env);
24
  }
25
  }
26
+
base/inc/lib/Less/Tree/Selector.php CHANGED
@@ -6,7 +6,7 @@
6
  * @package Less
7
  * @subpackage tree
8
  */
9
- class Less_Tree_Selector extends Less_Tree {
10
 
11
  public $elements;
12
  public $condition;
@@ -29,47 +29,50 @@ class Less_Tree_Selector extends Less_Tree {
29
  /**
30
  * @param boolean $isReferenced
31
  */
32
- public function __construct( $elements, $extendList = array(), $condition = null, $index = null, $currentFileInfo = null, $isReferenced = null ) {
 
33
  $this->elements = $elements;
34
- $this->elements_len = count( $elements );
35
  $this->extendList = $extendList;
36
  $this->condition = $condition;
37
- if ( $currentFileInfo ) {
38
  $this->currentFileInfo = $currentFileInfo;
39
  }
40
  $this->isReferenced = $isReferenced;
41
- if ( !$condition ) {
42
  $this->evaldCondition = true;
43
  }
44
 
45
  $this->CacheElements();
46
  }
47
 
48
- public function accept( $visitor ) {
49
- $this->elements = $visitor->visitArray( $this->elements );
50
- $this->extendList = $visitor->visitArray( $this->extendList );
51
- if ( $this->condition ) {
52
- $this->condition = $visitor->visitObj( $this->condition );
53
  }
54
 
55
- if ( $visitor instanceof Less_Visitor_extendFinder ) {
56
  $this->CacheElements();
57
  }
58
  }
59
 
60
- public function createDerived( $elements, $extendList = null, $evaldCondition = null ) {
61
- $newSelector = new Less_Tree_Selector( $elements, ( $extendList ? $extendList : $this->extendList ), null, $this->index, $this->currentFileInfo, $this->isReferenced );
62
  $newSelector->evaldCondition = $evaldCondition ? $evaldCondition : $this->evaldCondition;
63
  return $newSelector;
64
  }
65
 
66
- public function match( $other ) {
67
- if ( !$other->_oelements || ( $this->elements_len < $other->_oelements_len ) ) {
 
 
68
  return 0;
69
  }
70
 
71
- for ( $i = 0; $i < $other->_oelements_len; $i++ ) {
72
- if ( $this->elements[$i]->value !== $other->_oelements[$i] ) {
73
  return 0;
74
  }
75
  }
@@ -77,88 +80,93 @@ class Less_Tree_Selector extends Less_Tree {
77
  return $other->_oelements_len; // return number of matched elements
78
  }
79
 
80
- public function CacheElements() {
 
 
81
  $this->_oelements = array();
82
  $this->_oelements_assoc = array();
83
 
84
  $css = '';
85
 
86
- foreach ( $this->elements as $v ) {
87
 
88
  $css .= $v->combinator;
89
- if ( !$v->value_is_object ) {
90
  $css .= $v->value;
91
  continue;
92
  }
93
 
94
- if ( !property_exists( $v->value, 'value' ) || !is_string( $v->value->value ) ) {
95
  $this->cacheable = false;
96
  return;
97
  }
98
  $css .= $v->value->value;
99
  }
100
 
101
- $this->_oelements_len = preg_match_all( '/[,&#\.\w-](?:[\w-]|(?:\\\\.))*/', $css, $matches );
102
- if ( $this->_oelements_len ) {
103
  $this->_oelements = $matches[0];
104
 
105
- if ( $this->_oelements[0] === '&' ) {
106
- array_shift( $this->_oelements );
107
  $this->_oelements_len--;
108
  }
109
 
110
- $this->_oelements_assoc = array_fill_keys( $this->_oelements, true );
111
  }
112
  }
113
 
114
- public function isJustParentSelector() {
115
  return !$this->mediaEmpty &&
116
- count( $this->elements ) === 1 &&
117
  $this->elements[0]->value === '&' &&
118
- ( $this->elements[0]->combinator === ' ' || $this->elements[0]->combinator === '' );
119
  }
120
 
121
- public function compile( $env ) {
 
122
  $elements = array();
123
- foreach ( $this->elements as $el ) {
124
- $elements[] = $el->compile( $env );
125
  }
126
 
127
  $extendList = array();
128
- foreach ( $this->extendList as $el ) {
129
- $extendList[] = $el->compile( $el );
130
  }
131
 
132
  $evaldCondition = false;
133
- if ( $this->condition ) {
134
- $evaldCondition = $this->condition->compile( $env );
135
  }
136
 
137
  return $this->createDerived( $elements, $extendList, $evaldCondition );
138
  }
139
 
 
140
  /**
141
  * @see Less_Tree::genCSS
142
  */
143
- public function genCSS( $output, $firstSelector = true ) {
144
- if ( !$firstSelector && $this->elements[0]->combinator === "" ) {
145
- $output->add( ' ', $this->currentFileInfo, $this->index );
 
146
  }
147
 
148
- foreach ( $this->elements as $element ) {
149
  $element->genCSS( $output );
150
  }
151
  }
152
 
153
- public function markReferenced() {
154
  $this->isReferenced = true;
155
  }
156
 
157
- public function getIsReferenced() {
158
- return !isset( $this->currentFileInfo['reference'] ) || !$this->currentFileInfo['reference'] || $this->isReferenced;
159
  }
160
 
161
- public function getIsOutput() {
162
  return $this->evaldCondition;
163
  }
164
 
6
  * @package Less
7
  * @subpackage tree
8
  */
9
+ class Less_Tree_Selector extends Less_Tree{
10
 
11
  public $elements;
12
  public $condition;
29
  /**
30
  * @param boolean $isReferenced
31
  */
32
+ public function __construct( $elements, $extendList = array() , $condition = null, $index=null, $currentFileInfo=null, $isReferenced=null ){
33
+
34
  $this->elements = $elements;
35
+ $this->elements_len = count($elements);
36
  $this->extendList = $extendList;
37
  $this->condition = $condition;
38
+ if( $currentFileInfo ){
39
  $this->currentFileInfo = $currentFileInfo;
40
  }
41
  $this->isReferenced = $isReferenced;
42
+ if( !$condition ){
43
  $this->evaldCondition = true;
44
  }
45
 
46
  $this->CacheElements();
47
  }
48
 
49
+ public function accept($visitor) {
50
+ $this->elements = $visitor->visitArray($this->elements);
51
+ $this->extendList = $visitor->visitArray($this->extendList);
52
+ if( $this->condition ){
53
+ $this->condition = $visitor->visitObj($this->condition);
54
  }
55
 
56
+ if( $visitor instanceof Less_Visitor_extendFinder ){
57
  $this->CacheElements();
58
  }
59
  }
60
 
61
+ public function createDerived( $elements, $extendList = null, $evaldCondition = null ){
62
+ $newSelector = new Less_Tree_Selector( $elements, ($extendList ? $extendList : $this->extendList), null, $this->index, $this->currentFileInfo, $this->isReferenced);
63
  $newSelector->evaldCondition = $evaldCondition ? $evaldCondition : $this->evaldCondition;
64
  return $newSelector;
65
  }
66
 
67
+
68
+ public function match( $other ){
69
+
70
+ if( !$other->_oelements || ($this->elements_len < $other->_oelements_len) ){
71
  return 0;
72
  }
73
 
74
+ for( $i = 0; $i < $other->_oelements_len; $i++ ){
75
+ if( $this->elements[$i]->value !== $other->_oelements[$i]) {
76
  return 0;
77
  }
78
  }
80
  return $other->_oelements_len; // return number of matched elements
81
  }
82
 
83
+
84
+ public function CacheElements(){
85
+
86
  $this->_oelements = array();
87
  $this->_oelements_assoc = array();
88
 
89
  $css = '';
90
 
91
+ foreach($this->elements as $v){
92
 
93
  $css .= $v->combinator;
94
+ if( !$v->value_is_object ){
95
  $css .= $v->value;
96
  continue;
97
  }
98
 
99
+ if( !property_exists($v->value,'value') || !is_string($v->value->value) ){
100
  $this->cacheable = false;
101
  return;
102
  }
103
  $css .= $v->value->value;
104
  }
105
 
106
+ $this->_oelements_len = preg_match_all('/[,&#\.\w-](?:[\w-]|(?:\\\\.))*/', $css, $matches);
107
+ if( $this->_oelements_len ){
108
  $this->_oelements = $matches[0];
109
 
110
+ if( $this->_oelements[0] === '&' ){
111
+ array_shift($this->_oelements);
112
  $this->_oelements_len--;
113
  }
114
 
115
+ $this->_oelements_assoc = array_fill_keys($this->_oelements, true);
116
  }
117
  }
118
 
119
+ public function isJustParentSelector(){
120
  return !$this->mediaEmpty &&
121
+ count($this->elements) === 1 &&
122
  $this->elements[0]->value === '&' &&
123
+ ($this->elements[0]->combinator === ' ' || $this->elements[0]->combinator === '');
124
  }
125
 
126
+ public function compile($env) {
127
+
128
  $elements = array();
129
+ foreach($this->elements as $el){
130
+ $elements[] = $el->compile($env);
131
  }
132
 
133
  $extendList = array();
134
+ foreach($this->extendList as $el){
135
+ $extendList[] = $el->compile($el);
136
  }
137
 
138
  $evaldCondition = false;
139
+ if( $this->condition ){
140
+ $evaldCondition = $this->condition->compile($env);
141
  }
142
 
143
  return $this->createDerived( $elements, $extendList, $evaldCondition );
144
  }
145
 
146
+
147
  /**
148
  * @see Less_Tree::genCSS
149
  */
150
+ public function genCSS( $output, $firstSelector = true ){
151
+
152
+ if( !$firstSelector && $this->elements[0]->combinator === "" ){
153
+ $output->add(' ', $this->currentFileInfo, $this->index);
154
  }
155
 
156
+ foreach($this->elements as $element){
157
  $element->genCSS( $output );
158
  }
159
  }
160
 
161
+ public function markReferenced(){
162
  $this->isReferenced = true;
163
  }
164
 
165
+ public function getIsReferenced(){
166
+ return !isset($this->currentFileInfo['reference']) || !$this->currentFileInfo['reference'] || $this->isReferenced;
167
  }
168
 
169
+ public function getIsOutput(){
170
  return $this->evaldCondition;
171
  }
172
 
base/inc/lib/Less/Tree/UnicodeDescriptor.php CHANGED
@@ -6,23 +6,24 @@
6
  * @package Less
7
  * @subpackage tree
8
  */
9
- class Less_Tree_UnicodeDescriptor extends Less_Tree {
10
 
11
  public $value;
12
  public $type = 'UnicodeDescriptor';
13
 
14
- public function __construct( $value ) {
15
  $this->value = $value;
16
  }
17
 
18
- /**
19
- * @see Less_Tree::genCSS
20
- */
21
- public function genCSS( $output ) {
22
  $output->add( $this->value );
23
  }
24
 
25
- public function compile() {
26
  return $this;
27
  }
28
  }
 
6
  * @package Less
7
  * @subpackage tree
8
  */
9
+ class Less_Tree_UnicodeDescriptor extends Less_Tree{
10
 
11
  public $value;
12
  public $type = 'UnicodeDescriptor';
13
 
14
+ public function __construct($value){
15
  $this->value = $value;
16
  }
17
 
18
+ /**
19
+ * @see Less_Tree::genCSS
20
+ */
21
+ public function genCSS( $output ){
22
  $output->add( $this->value );
23
  }
24
 
25
+ public function compile(){
26
  return $this;
27
  }
28
  }
29
+
base/inc/lib/Less/Tree/Unit.php CHANGED
@@ -6,90 +6,93 @@
6
  * @package Less
7
  * @subpackage tree
8
  */
9
- class Less_Tree_Unit extends Less_Tree {
10
 
11
  var $numerator = array();
12
  var $denominator = array();
13
  public $backupUnit;
14
  public $type = 'Unit';
15
 
16
- public function __construct( $numerator = array(), $denominator = array(), $backupUnit = null ) {
17
  $this->numerator = $numerator;
18
  $this->denominator = $denominator;
19
  $this->backupUnit = $backupUnit;
20
  }
21
 
22
- public function __clone() {
23
  }
24
 
25
- /**
26
- * @see Less_Tree::genCSS
27
- */
28
- public function genCSS( $output ) {
29
- if ( $this->numerator ) {
 
30
  $output->add( $this->numerator[0] );
31
- } elseif ( $this->denominator ) {
32
  $output->add( $this->denominator[0] );
33
- } elseif ( !Less_Parser::$options['strictUnits'] && $this->backupUnit ) {
34
  $output->add( $this->backupUnit );
35
- return;
36
  }
37
  }
38
 
39
- public function toString() {
40
- $returnStr = implode( '*', $this->numerator );
41
- foreach ( $this->denominator as $d ) {
42
  $returnStr .= '/'.$d;
43
  }
44
  return $returnStr;
45
  }
46
 
47
- public function __toString() {
48
  return $this->toString();
49
  }
50
 
 
51
  /**
52
  * @param Less_Tree_Unit $other
53
  */
54
- public function compare( $other ) {
55
  return $this->is( $other->toString() ) ? 0 : -1;
56
  }
57
 
58
- public function is( $unitString ) {
59
  return $this->toString() === $unitString;
60
  }
61
 
62
- public function isLength() {
63
  $css = $this->toCSS();
64
- return !!preg_match( '/px|em|%|in|cm|mm|pc|pt|ex/', $css );
65
  }
66
 
67
- public function isAngle() {
68
  return isset( Less_Tree_UnitConversions::$angle[$this->toCSS()] );
69
  }
70
 
71
- public function isEmpty() {
72
  return !$this->numerator && !$this->denominator;
73
  }
74
 
75
- public function isSingular() {
76
- return count( $this->numerator ) <= 1 && !$this->denominator;
77
  }
78
 
79
- public function usedUnits() {
 
80
  $result = array();
81
 
82
- foreach ( Less_Tree_UnitConversions::$groups as $groupName ) {
83
  $group = Less_Tree_UnitConversions::${$groupName};
84
 
85
- foreach ( $this->numerator as $atomicUnit ) {
86
- if ( isset( $group[$atomicUnit] ) && !isset( $result[$groupName] ) ) {
87
  $result[$groupName] = $atomicUnit;
88
  }
89
  }
90
 
91
- foreach ( $this->denominator as $atomicUnit ) {
92
- if ( isset( $group[$atomicUnit] ) && !isset( $result[$groupName] ) ) {
93
  $result[$groupName] = $atomicUnit;
94
  }
95
  }
@@ -98,45 +101,47 @@ class Less_Tree_Unit extends Less_Tree {
98
  return $result;
99
  }
100
 
101
- public function cancel() {
102
  $counter = array();
103
  $backup = null;
104
 
105
- foreach ( $this->numerator as $atomicUnit ) {
106
- if ( !$backup ) {
107
  $backup = $atomicUnit;
108
  }
109
- $counter[$atomicUnit] = ( isset( $counter[$atomicUnit] ) ? $counter[$atomicUnit] : 0 ) + 1;
110
  }
111
 
112
- foreach ( $this->denominator as $atomicUnit ) {
113
- if ( !$backup ) {
114
  $backup = $atomicUnit;
115
  }
116
- $counter[$atomicUnit] = ( isset( $counter[$atomicUnit] ) ? $counter[$atomicUnit] : 0 ) - 1;
117
  }
118
 
119
  $this->numerator = array();
120
  $this->denominator = array();
121
 
122
- foreach ( $counter as $atomicUnit => $count ) {
123
- if ( $count > 0 ) {
124
- for ( $i = 0; $i < $count; $i++ ) {
125
  $this->numerator[] = $atomicUnit;
126
  }
127
- } elseif ( $count < 0 ) {
128
- for ( $i = 0; $i < -$count; $i++ ) {
129
  $this->denominator[] = $atomicUnit;
130
  }
131
  }
132
  }
133
 
134
- if ( !$this->numerator && !$this->denominator && $backup ) {
135
  $this->backupUnit = $backup;
136
  }
137
 
138
- sort( $this->numerator );
139
- sort( $this->denominator );
140
  }
141
 
 
142
  }
 
6
  * @package Less
7
  * @subpackage tree
8
  */
9
+ class Less_Tree_Unit extends Less_Tree{
10
 
11
  var $numerator = array();
12
  var $denominator = array();
13
  public $backupUnit;
14
  public $type = 'Unit';
15
 
16
+ public function __construct($numerator = array(), $denominator = array(), $backupUnit = null ){
17
  $this->numerator = $numerator;
18
  $this->denominator = $denominator;
19
  $this->backupUnit = $backupUnit;
20
  }
21
 
22
+ public function __clone(){
23
  }
24
 
25
+ /**
26
+ * @see Less_Tree::genCSS
27
+ */
28
+ public function genCSS( $output ){
29
+
30
+ if( $this->numerator ){
31
  $output->add( $this->numerator[0] );
32
+ }elseif( $this->denominator ){
33
  $output->add( $this->denominator[0] );
34
+ }elseif( !Less_Parser::$options['strictUnits'] && $this->backupUnit ){
35
  $output->add( $this->backupUnit );
36
+ return ;
37
  }
38
  }
39
 
40
+ public function toString(){
41
+ $returnStr = implode('*',$this->numerator);
42
+ foreach($this->denominator as $d){
43
  $returnStr .= '/'.$d;
44
  }
45
  return $returnStr;
46
  }
47
 
48
+ public function __toString(){
49
  return $this->toString();
50
  }
51
 
52
+
53
  /**
54
  * @param Less_Tree_Unit $other
55
  */
56
+ public function compare($other) {
57
  return $this->is( $other->toString() ) ? 0 : -1;
58
  }
59
 
60
+ public function is($unitString){
61
  return $this->toString() === $unitString;
62
  }
63
 
64
+ public function isLength(){
65
  $css = $this->toCSS();
66
+ return !!preg_match('/px|em|%|in|cm|mm|pc|pt|ex/',$css);
67
  }
68
 
69
+ public function isAngle() {
70
  return isset( Less_Tree_UnitConversions::$angle[$this->toCSS()] );
71
  }
72
 
73
+ public function isEmpty(){
74
  return !$this->numerator && !$this->denominator;
75
  }
76
 
77
+ public function isSingular() {
78
+ return count($this->numerator) <= 1 && !$this->denominator;
79
  }
80
 
81
+
82
+ public function usedUnits(){
83
  $result = array();
84
 
85
+ foreach(Less_Tree_UnitConversions::$groups as $groupName){
86
  $group = Less_Tree_UnitConversions::${$groupName};
87
 
88
+ foreach($this->numerator as $atomicUnit){
89
+ if( isset($group[$atomicUnit]) && !isset($result[$groupName]) ){
90
  $result[$groupName] = $atomicUnit;
91
  }
92
  }
93
 
94
+ foreach($this->denominator as $atomicUnit){
95
+ if( isset($group[$atomicUnit]) && !isset($result[$groupName]) ){
96
  $result[$groupName] = $atomicUnit;
97
  }
98
  }
101
  return $result;
102
  }
103
 
104
+ public function cancel(){
105
  $counter = array();
106
  $backup = null;
107
 
108
+ foreach($this->numerator as $atomicUnit){
109
+ if( !$backup ){
110
  $backup = $atomicUnit;
111
  }
112
+ $counter[$atomicUnit] = ( isset($counter[$atomicUnit]) ? $counter[$atomicUnit] : 0) + 1;
113
  }
114
 
115
+ foreach($this->denominator as $atomicUnit){
116
+ if( !$backup ){
117
  $backup = $atomicUnit;
118
  }
119
+ $counter[$atomicUnit] = ( isset($counter[$atomicUnit]) ? $counter[$atomicUnit] : 0) - 1;
120
  }
121
 
122
  $this->numerator = array();
123
  $this->denominator = array();
124
 
125
+ foreach($counter as $atomicUnit => $count){
126
+ if( $count > 0 ){
127
+ for( $i = 0; $i < $count; $i++ ){
128
  $this->numerator[] = $atomicUnit;
129
  }
130
+ }elseif( $count < 0 ){
131
+ for( $i = 0; $i < -$count; $i++ ){
132
  $this->denominator[] = $atomicUnit;
133
  }
134
  }
135
  }
136
 
137
+ if( !$this->numerator && !$this->denominator && $backup ){
138
  $this->backupUnit = $backup;
139
  }
140
 
141
+ sort($this->numerator);
142
+ sort($this->denominator);
143
  }
144
 
145
+
146
  }
147
+
base/inc/lib/Less/Tree/UnitConversions.php CHANGED
@@ -6,30 +6,30 @@
6
  * @package Less
7
  * @subpackage tree
8
  */
9
- class Less_Tree_UnitConversions {
10
 
11
- public static $groups = array( 'length','duration','angle' );
12
 
13
  public static $length = array(
14
- 'm' => 1,
15
- 'cm' => 0.01,
16
- 'mm' => 0.001,
17
- 'in' => 0.0254,
18
- 'px' => 0.000264583, // 0.0254 / 96,
19
- 'pt' => 0.000352778, // 0.0254 / 72,
20
- 'pc' => 0.004233333, // 0.0254 / 72 * 12
21
  );
22
 
23
  public static $duration = array(
24
- 's' => 1,
25
- 'ms' => 0.001
26
  );
27
 
28
  public static $angle = array(
29
  'rad' => 0.1591549430919, // 1/(2*M_PI),
30
  'deg' => 0.002777778, // 1/360,
31
- 'grad' => 0.0025, // 1/400,
32
- 'turn' => 1
33
  );
34
 
35
- }
6
  * @package Less
7
  * @subpackage tree
8
  */
9
+ class Less_Tree_UnitConversions{
10
 
11
+ public static $groups = array('length','duration','angle');
12
 
13
  public static $length = array(
14
+ 'm'=> 1,
15
+ 'cm'=> 0.01,
16
+ 'mm'=> 0.001,
17
+ 'in'=> 0.0254,
18
+ 'px'=> 0.000264583, // 0.0254 / 96,
19
+ 'pt'=> 0.000352778, // 0.0254 / 72,
20
+ 'pc'=> 0.004233333, // 0.0254 / 72 * 12
21
  );
22
 
23
  public static $duration = array(
24
+ 's'=> 1,
25
+ 'ms'=> 0.001
26
  );
27
 
28
  public static $angle = array(
29
  'rad' => 0.1591549430919, // 1/(2*M_PI),
30
  'deg' => 0.002777778, // 1/360,
31
+ 'grad'=> 0.0025, // 1/400,
32
+ 'turn'=> 1
33
  );
34
 
35
+ }
base/inc/lib/Less/Tree/Url.php CHANGED
@@ -6,7 +6,7 @@
6
  * @package Less
7
  * @subpackage tree
8
  */
9
- class Less_Tree_Url extends Less_Tree {
10
 
11
  public $attrs;
12
  public $value;
@@ -14,20 +14,20 @@ class Less_Tree_Url extends Less_Tree {
14
  public $isEvald;
15
  public $type = 'Url';
16
 
17
- public function __construct( $value, $currentFileInfo = null, $isEvald = null ) {
18
  $this->value = $value;
19
  $this->currentFileInfo = $currentFileInfo;
20
  $this->isEvald = $isEvald;
21
  }
22
 
23
- public function accept( $visitor ) {
24
- $this->value = $visitor->visitObj( $this->value );
25
  }
26
 
27
- /**
28
- * @see Less_Tree::genCSS
29
- */
30
- public function genCSS( $output ) {
31
  $output->add( 'url(' );
32
  $this->value->genCSS( $output );
33
  $output->add( ')' );
@@ -36,41 +36,41 @@ class Less_Tree_Url extends Less_Tree {
36
  /**
37
  * @param Less_Functions $ctx
38
  */
39
- public function compile( $ctx ) {
40
- $val = $this->value->compile( $ctx );
41
 
42
- if ( !$this->isEvald ) {
43
  // Add the base path if the URL is relative
44
- if ( Less_Parser::$options['relativeUrls']
45
  && $this->currentFileInfo
46
- && is_string( $val->value )
47
- && Less_Environment::isPathRelative( $val->value )
48
- ) {
49
  $rootpath = $this->currentFileInfo['uri_root'];
50
- if ( !$val->quote ) {
51
- $rootpath = preg_replace( '/[\(\)\'"\s]/', '\\$1', $rootpath );
52
  }
53
  $val->value = $rootpath . $val->value;
54
  }
55
 
56
- $val->value = Less_Environment::normalizePath( $val->value );
57
  }
58
 
59
  // Add cache buster if enabled
60
- if ( Less_Parser::$options['urlArgs'] ) {
61
- if ( !preg_match( '/^\s*data:/', $val->value ) ) {
62
- $delimiter = strpos( $val->value, '?' ) === false ? '?' : '&';
63
  $urlArgs = $delimiter . Less_Parser::$options['urlArgs'];
64
- $hash_pos = strpos( $val->value, '#' );
65
- if ( $hash_pos !== false ) {
66
- $val->value = substr_replace( $val->value, $urlArgs, $hash_pos, 0 );
67
  } else {
68
  $val->value .= $urlArgs;
69
  }
70
  }
71
  }
72
 
73
- return new Less_Tree_URL( $val, $this->currentFileInfo, true );
74
  }
75
 
76
  }
6
  * @package Less
7
  * @subpackage tree
8
  */
9
+ class Less_Tree_Url extends Less_Tree{
10
 
11
  public $attrs;
12
  public $value;
14
  public $isEvald;
15
  public $type = 'Url';
16
 
17
+ public function __construct($value, $currentFileInfo = null, $isEvald = null){
18
  $this->value = $value;
19
  $this->currentFileInfo = $currentFileInfo;
20
  $this->isEvald = $isEvald;
21
  }
22
 
23
+ public function accept( $visitor ){
24
+ $this->value = $visitor->visitObj($this->value);
25
  }
26
 
27
+ /**
28
+ * @see Less_Tree::genCSS
29
+ */
30
+ public function genCSS( $output ){
31
  $output->add( 'url(' );
32
  $this->value->genCSS( $output );
33
  $output->add( ')' );
36
  /**
37
  * @param Less_Functions $ctx
38
  */
39
+ public function compile($ctx){
40
+ $val = $this->value->compile($ctx);
41
 
42
+ if( !$this->isEvald ){
43
  // Add the base path if the URL is relative
44
+ if( Less_Parser::$options['relativeUrls']
45
  && $this->currentFileInfo
46
+ && is_string($val->value)
47
+ && Less_Environment::isPathRelative($val->value)
48
+ ){
49
  $rootpath = $this->currentFileInfo['uri_root'];
50
+ if ( !$val->quote ){
51
+ $rootpath = preg_replace('/[\(\)\'"\s]/', '\\$1', $rootpath );
52
  }
53
  $val->value = $rootpath . $val->value;
54
  }
55
 
56
+ $val->value = Less_Environment::normalizePath( $val->value);
57
  }
58
 
59
  // Add cache buster if enabled
60
+ if( Less_Parser::$options['urlArgs'] ){
61
+ if( !preg_match('/^\s*data:/',$val->value) ){
62
+ $delimiter = strpos($val->value,'?') === false ? '?' : '&';
63
  $urlArgs = $delimiter . Less_Parser::$options['urlArgs'];
64
+ $hash_pos = strpos($val->value,'#');
65
+ if( $hash_pos !== false ){
66
+ $val->value = substr_replace($val->value,$urlArgs, $hash_pos, 0);
67
  } else {
68
  $val->value .= $urlArgs;
69
  }
70
  }
71
  }
72
 
73
+ return new Less_Tree_URL($val, $this->currentFileInfo, true);
74
  }
75
 
76
  }
base/inc/lib/Less/Tree/Value.php CHANGED
@@ -6,39 +6,40 @@
6
  * @package Less
7
  * @subpackage tree
8
  */
9
- class Less_Tree_Value extends Less_Tree {
10
 
11
  public $type = 'Value';
12
  public $value;
13
 
14
- public function __construct( $value ) {
15
  $this->value = $value;
16
  }
17
 
18
- public function accept( $visitor ) {
19
- $this->value = $visitor->visitArray( $this->value );
20
  }
21
 
22
- public function compile( $env ) {
 
23
  $ret = array();
24
  $i = 0;
25
- foreach ( $this->value as $i => $v ) {
26
- $ret[] = $v->compile( $env );
27
  }
28
- if ( $i > 0 ) {
29
- return new Less_Tree_Value( $ret );
30
  }
31
  return $ret[0];
32
  }
33
 
34
- /**
35
- * @see Less_Tree::genCSS
36
- */
37
- function genCSS( $output ) {
38
- $len = count( $this->value );
39
- for ( $i = 0; $i < $len; $i++ ) {
40
  $this->value[$i]->genCSS( $output );
41
- if ( $i + 1 < $len ) {
42
  $output->add( Less_Environment::$_outputMap[','] );
43
  }
44
  }
6
  * @package Less
7
  * @subpackage tree
8
  */
9
+ class Less_Tree_Value extends Less_Tree{
10
 
11
  public $type = 'Value';
12
  public $value;
13
 
14
+ public function __construct($value){
15
  $this->value = $value;
16
  }
17
 
18
+ public function accept($visitor) {
19
+ $this->value = $visitor->visitArray($this->value);
20
  }
21
 
22
+ public function compile($env){
23
+
24
  $ret = array();
25
  $i = 0;
26
+ foreach($this->value as $i => $v){
27
+ $ret[] = $v->compile($env);
28
  }
29
+ if( $i > 0 ){
30
+ return new Less_Tree_Value($ret);
31
  }
32
  return $ret[0];
33
  }
34
 
35
+ /**
36
+ * @see Less_Tree::genCSS
37
+ */
38
+ function genCSS( $output ){
39
+ $len = count($this->value);
40
+ for($i = 0; $i < $len; $i++ ){
41
  $this->value[$i]->genCSS( $output );
42
+ if( $i+1 < $len ){
43
  $output->add( Less_Environment::$_outputMap[','] );
44
  }
45
  }
base/inc/lib/Less/Tree/Variable.php CHANGED
@@ -6,7 +6,7 @@
6
  * @package Less
7
  * @subpackage tree
8
  */
9
- class Less_Tree_Variable extends Less_Tree {
10
 
11
  public $name;
12
  public $index;
@@ -14,38 +14,39 @@ class Less_Tree_Variable extends Less_Tree {
14
  public $evaluating = false;
15
  public $type = 'Variable';
16
 
17
- /**
18
- * @param string $name
19
- */
20
- public function __construct( $name, $index = null, $currentFileInfo = null ) {
21
- $this->name = $name;
22
- $this->index = $index;
23
  $this->currentFileInfo = $currentFileInfo;
24
- }
 
 
25
 
26
- public function compile( $env ) {
27
- if ( $this->name[1] === '@' ) {
28
- $v = new Less_Tree_Variable( substr( $this->name, 1 ), $this->index + 1, $this->currentFileInfo );
29
- $name = '@' . $v->compile( $env )->value;
30
- } else {
31
  $name = $this->name;
32
  }
33
 
34
- if ( $this->evaluating ) {
35
- throw new Less_Exception_Compiler( "Recursive variable definition for " . $name, null, $this->index, $this->currentFileInfo );
36
  }
37
 
38
  $this->evaluating = true;
39
 
40
- foreach ( $env->frames as $frame ) {
41
- if ( $v = $frame->variable( $name ) ) {
42
- $r = $v->value->compile( $env );
43
  $this->evaluating = false;
44
  return $r;
45
  }
46
  }
47
 
48
- throw new Less_Exception_Compiler( "variable " . $name . " is undefined in file ".$this->currentFileInfo["filename"], null, $this->index, $this->currentFileInfo );
49
  }
50
 
51
  }
6
  * @package Less
7
  * @subpackage tree
8
  */
9
+ class Less_Tree_Variable extends Less_Tree{
10
 
11
  public $name;
12
  public $index;
14
  public $evaluating = false;
15
  public $type = 'Variable';
16
 
17
+ /**
18
+ * @param string $name
19
+ */
20
+ public function __construct($name, $index = null, $currentFileInfo = null) {
21
+ $this->name = $name;
22
+ $this->index = $index;
23
  $this->currentFileInfo = $currentFileInfo;
24
+ }
25
+
26
+ public function compile($env) {
27
 
28
+ if( $this->name[1] === '@' ){
29
+ $v = new Less_Tree_Variable(substr($this->name, 1), $this->index + 1, $this->currentFileInfo);
30
+ $name = '@' . $v->compile($env)->value;
31
+ }else{
 
32
  $name = $this->name;
33
  }
34
 
35
+ if ($this->evaluating) {
36
+ throw new Less_Exception_Compiler("Recursive variable definition for " . $name, null, $this->index, $this->currentFileInfo);
37
  }
38
 
39
  $this->evaluating = true;
40
 
41
+ foreach($env->frames as $frame){
42
+ if( $v = $frame->variable($name) ){
43
+ $r = $v->value->compile($env);
44
  $this->evaluating = false;
45
  return $r;
46
  }
47
  }
48
 
49
+ throw new Less_Exception_Compiler("variable " . $name . " is undefined in file ".$this->currentFileInfo["filename"], null, $this->index, $this->currentFileInfo);
50
  }
51
 
52
  }
base/inc/lib/Less/Version.php CHANGED
@@ -6,10 +6,10 @@
6
  * @package Less
7
  * @subpackage version
8
  */
9
- class Less_Version {
10
 
11
- public const version = '3.1.0'; // The current build number of less.php
12
- public const less_version = '2.5.3'; // The less.js version that this build should be compatible with
13
- public const cache_version = '253'; // The parser cache version
14
 
15
  }
6
  * @package Less
7
  * @subpackage version
8
  */
9
+ class Less_Version{
10
 
11
+ const version = '3.0.0'; // The current build number of less.php
12
+ const less_version = '2.5.3'; // The less.js version that this build should be compatible with
13
+ const cache_version = '253'; // The parser cache version
14
 
15
  }
base/inc/lib/Less/Visitor.php CHANGED
@@ -6,41 +6,44 @@
6
  * @package Less
7
  * @subpackage visitor
8
  */
9
- class Less_Visitor {
10
 
11
  protected $methods = array();
12
  protected $_visitFnCache = array();
13
 
14
- public function __construct() {
15
- $this->_visitFnCache = get_class_methods( get_class( $this ) );
16
- $this->_visitFnCache = array_flip( $this->_visitFnCache );
17
  }
18
 
19
- public function visitObj( $node ) {
 
20
  $funcName = 'visit'.$node->type;
21
- if ( isset( $this->_visitFnCache[$funcName] ) ) {
22
 
23
  $visitDeeper = true;
24
  $this->$funcName( $node, $visitDeeper );
25
 
26
- if ( $visitDeeper ) {
27
- $node->accept( $this );
28
  }
29
 
30
  $funcName = $funcName . "Out";
31
- if ( isset( $this->_visitFnCache[$funcName] ) ) {
32
  $this->$funcName( $node );
33
  }
34
 
35
- } else {
36
- $node->accept( $this );
37
  }
38
 
39
  return $node;
40
  }
41
 
42
- public function visitArray( $nodes ) {
43
- array_map( array( $this,'visitObj' ), $nodes );
 
44
  return $nodes;
45
  }
46
  }
 
6
  * @package Less
7
  * @subpackage visitor
8
  */
9
+ class Less_Visitor{
10
 
11
  protected $methods = array();
12
  protected $_visitFnCache = array();
13
 
14
+ public function __construct(){
15
+ $this->_visitFnCache = get_class_methods(get_class($this));
16
+ $this->_visitFnCache = array_flip($this->_visitFnCache);
17
  }
18
 
19
+ public function visitObj( $node ){
20
+
21
  $funcName = 'visit'.$node->type;
22
+ if( isset($this->_visitFnCache[$funcName]) ){
23
 
24
  $visitDeeper = true;
25
  $this->$funcName( $node, $visitDeeper );
26
 
27
+ if( $visitDeeper ){
28
+ $node->accept($this);
29
  }
30
 
31
  $funcName = $funcName . "Out";
32
+ if( isset($this->_visitFnCache[$funcName]) ){
33
  $this->$funcName( $node );
34
  }
35
 
36
+ }else{
37
+ $node->accept($this);
38
  }
39
 
40
  return $node;
41
  }
42
 
43
+ public function visitArray( $nodes ){
44
+
45
+ array_map( array($this,'visitObj'), $nodes);
46
  return $nodes;
47
  }
48
  }
49
+
base/inc/lib/Less/Visitor/extendFinder.php CHANGED
@@ -6,104 +6,109 @@
6
  * @package Less
7
  * @subpackage visitor
8
  */
9
- class Less_Visitor_extendFinder extends Less_Visitor {
10
 
11
  public $contexts = array();
12
  public $allExtendsStack;
13
  public $foundExtends;
14
 
15
- public function __construct() {
16
  $this->contexts = array();
17
- $this->allExtendsStack = array( array() );
18
  parent::__construct();
19
  }
20
 
21
  /**
22
  * @param Less_Tree_Ruleset $root
23
  */
24
- public function run( $root ) {
25
- $root = $this->visitObj( $root );
26
  $root->allExtends =& $this->allExtendsStack[0];
27
  return $root;
28
  }
29
 
30
- public function visitRule( $ruleNode, &$visitDeeper ) {
31
  $visitDeeper = false;
32
  }
33
 
34
- public function visitMixinDefinition( $mixinDefinitionNode, &$visitDeeper ) {
35
  $visitDeeper = false;
36
  }
37
 
38
- public function visitRuleset( $rulesetNode ) {
39
- if ( $rulesetNode->root ) {
 
40
  return;
41
  }
42
 
43
  $allSelectorsExtendList = array();
44
 
45
  // get &:extend(.a); rules which apply to all selectors in this ruleset
46
- if ( $rulesetNode->rules ) {
47
- foreach ( $rulesetNode->rules as $rule ) {
48
- if ( $rule instanceof Less_Tree_Extend ) {
49
  $allSelectorsExtendList[] = $rule;
50
  $rulesetNode->extendOnEveryPath = true;
51
  }
52
  }
53
  }
54
 
 
55
  // now find every selector and apply the extends that apply to all extends
56
  // and the ones which apply to an individual extend
57
- foreach ( $rulesetNode->paths as $selectorPath ) {
58
- $selector = end( $selectorPath ); // $selectorPath[ count($selectorPath)-1];
59
 
60
  $j = 0;
61
- foreach ( $selector->extendList as $extend ) {
62
- $this->allExtendsStackPush( $rulesetNode, $selectorPath, $extend, $j );
63
  }
64
- foreach ( $allSelectorsExtendList as $extend ) {
65
- $this->allExtendsStackPush( $rulesetNode, $selectorPath, $extend, $j );
66
  }
67
  }
68
 
69
  $this->contexts[] = $rulesetNode->selectors;
70
  }
71
 
72
- public function allExtendsStackPush( $rulesetNode, $selectorPath, $extend, &$j ) {
73
  $this->foundExtends = true;
74
  $extend = clone $extend;
75
  $extend->findSelfSelectors( $selectorPath );
76
  $extend->ruleset = $rulesetNode;
77
- if ( $j === 0 ) {
78
  $extend->firstExtendOnThisSelectorPath = true;
79
  }
80
 
81
- $end_key = count( $this->allExtendsStack ) - 1;
82
  $this->allExtendsStack[$end_key][] = $extend;
83
  $j++;
84
  }
85
 
86
- public function visitRulesetOut( $rulesetNode ) {
87
- if ( !is_object( $rulesetNode ) || !$rulesetNode->root ) {
88
- array_pop( $this->contexts );
 
89
  }
90
  }
91
 
92
- public function visitMedia( $mediaNode ) {
93
  $mediaNode->allExtends = array();
94
  $this->allExtendsStack[] =& $mediaNode->allExtends;
95
  }
96
 
97
- public function visitMediaOut() {
98
- array_pop( $this->allExtendsStack );
99
  }
100
 
101
- public function visitDirective( $directiveNode ) {
102
  $directiveNode->allExtends = array();
103
  $this->allExtendsStack[] =& $directiveNode->allExtends;
104
  }
105
 
106
- public function visitDirectiveOut() {
107
- array_pop( $this->allExtendsStack );
108
  }
109
  }
 
 
6
  * @package Less
7
  * @subpackage visitor
8
  */
9
+ class Less_Visitor_extendFinder extends Less_Visitor{
10
 
11
  public $contexts = array();
12
  public $allExtendsStack;
13
  public $foundExtends;
14
 
15
+ public function __construct(){
16
  $this->contexts = array();
17
+ $this->allExtendsStack = array(array());
18
  parent::__construct();
19
  }
20
 
21
  /**
22
  * @param Less_Tree_Ruleset $root
23
  */
24
+ public function run($root){
25
+ $root = $this->visitObj($root);
26
  $root->allExtends =& $this->allExtendsStack[0];
27
  return $root;
28
  }
29
 
30
+ public function visitRule($ruleNode, &$visitDeeper ){
31
  $visitDeeper = false;
32
  }
33
 
34
+ public function visitMixinDefinition( $mixinDefinitionNode, &$visitDeeper ){
35
  $visitDeeper = false;
36
  }
37
 
38
+ public function visitRuleset($rulesetNode){
39
+
40
+ if( $rulesetNode->root ){
41
  return;
42
  }
43
 
44
  $allSelectorsExtendList = array();
45
 
46
  // get &:extend(.a); rules which apply to all selectors in this ruleset
47
+ if( $rulesetNode->rules ){
48
+ foreach($rulesetNode->rules as $rule){
49
+ if( $rule instanceof Less_Tree_Extend ){
50
  $allSelectorsExtendList[] = $rule;
51
  $rulesetNode->extendOnEveryPath = true;
52
  }
53
  }
54
  }
55
 
56
+
57
  // now find every selector and apply the extends that apply to all extends
58
  // and the ones which apply to an individual extend
59
+ foreach($rulesetNode->paths as $selectorPath){
60
+ $selector = end($selectorPath); //$selectorPath[ count($selectorPath)-1];
61
 
62
  $j = 0;
63
+ foreach($selector->extendList as $extend){
64
+ $this->allExtendsStackPush($rulesetNode, $selectorPath, $extend, $j);
65
  }
66
+ foreach($allSelectorsExtendList as $extend){
67
+ $this->allExtendsStackPush($rulesetNode, $selectorPath, $extend, $j);
68
  }
69
  }
70
 
71
  $this->contexts[] = $rulesetNode->selectors;
72
  }
73
 
74
+ public function allExtendsStackPush($rulesetNode, $selectorPath, $extend, &$j){
75
  $this->foundExtends = true;
76
  $extend = clone $extend;
77
  $extend->findSelfSelectors( $selectorPath );
78
  $extend->ruleset = $rulesetNode;
79
+ if( $j === 0 ){
80
  $extend->firstExtendOnThisSelectorPath = true;
81
  }
82
 
83
+ $end_key = count($this->allExtendsStack)-1;
84
  $this->allExtendsStack[$end_key][] = $extend;
85
  $j++;
86
  }
87
 
88
+
89
+ public function visitRulesetOut( $rulesetNode ){
90
+ if( !is_object($rulesetNode) || !$rulesetNode->root ){
91
+ array_pop($this->contexts);
92
  }
93
  }
94
 
95
+ public function visitMedia( $mediaNode ){
96
  $mediaNode->allExtends = array();
97
  $this->allExtendsStack[] =& $mediaNode->allExtends;
98
  }
99
 
100
+ public function visitMediaOut(){
101
+ array_pop($this->allExtendsStack);
102
  }
103
 
104
+ public function visitDirective( $directiveNode ){
105
  $directiveNode->allExtends = array();
106
  $this->allExtendsStack[] =& $directiveNode->allExtends;
107
  }
108
 
109
+ public function visitDirectiveOut(){
110
+ array_pop($this->allExtendsStack);
111
  }
112
  }
113
+
114
+
base/inc/lib/Less/Visitor/import.php CHANGED
@@ -135,3 +135,5 @@ class Less_Visitor_import extends Less_VisitorReplacing{
135
 
136
  }
137
  */
 
 
135
 
136
  }
137
  */
138
+
139
+
base/inc/lib/Less/Visitor/joinSelector.php CHANGED
@@ -6,63 +6,65 @@
6
  * @package Less
7
  * @subpackage visitor
8
  */
9
- class Less_Visitor_joinSelector extends Less_Visitor {
10
 
11
  public $contexts = array( array() );
12
 
13
  /**
14
  * @param Less_Tree_Ruleset $root
15
  */
16
- public function run( $root ) {
17
- return $this->visitObj( $root );
18
  }
19
 
20
- public function visitRule( $ruleNode, &$visitDeeper ) {
21
  $visitDeeper = false;
22
  }
23
 
24
- public function visitMixinDefinition( $mixinDefinitionNode, &$visitDeeper ) {
25
  $visitDeeper = false;
26
  }
27
 
28
- public function visitRuleset( $rulesetNode ) {
 
29
  $paths = array();
30
 
31
- if ( !$rulesetNode->root ) {
32
  $selectors = array();
33
 
34
- if ( $rulesetNode->selectors && $rulesetNode->selectors ) {
35
- foreach ( $rulesetNode->selectors as $selector ) {
36
- if ( $selector->getIsOutput() ) {
37
  $selectors[] = $selector;
38
  }
39
  }
40
  }
41
 
42
- if ( !$selectors ) {
43
  $rulesetNode->selectors = null;
44
  $rulesetNode->rules = null;
45
- } else {
46
- $context = end( $this->contexts ); // $context = $this->contexts[ count($this->contexts) - 1];
47
- $paths = $rulesetNode->joinSelectors( $context, $selectors );
48
  }
49
 
50
  $rulesetNode->paths = $paths;
51
  }
52
 
53
- $this->contexts[] = $paths; // different from less.js. Placed after joinSelectors() so that $this->contexts will get correct $paths
54
  }
55
 
56
- public function visitRulesetOut() {
57
- array_pop( $this->contexts );
58
  }
59
 
60
- public function visitMedia( $mediaNode ) {
61
- $context = end( $this->contexts ); // $context = $this->contexts[ count($this->contexts) - 1];
62
 
63
- if ( !count( $context ) || ( is_object( $context[0] ) && $context[0]->multiMedia ) ) {
64
  $mediaNode->rules[0]->root = true;
65
  }
66
  }
67
 
68
  }
 
6
  * @package Less
7
  * @subpackage visitor
8
  */
9
+ class Less_Visitor_joinSelector extends Less_Visitor{
10
 
11
  public $contexts = array( array() );
12
 
13
  /**
14
  * @param Less_Tree_Ruleset $root
15
  */
16
+ public function run( $root ){
17
+ return $this->visitObj($root);
18
  }
19
 
20
+ public function visitRule( $ruleNode, &$visitDeeper ){
21
  $visitDeeper = false;
22
  }
23
 
24
+ public function visitMixinDefinition( $mixinDefinitionNode, &$visitDeeper ){
25
  $visitDeeper = false;
26
  }
27
 
28
+ public function visitRuleset( $rulesetNode ){
29
+
30
  $paths = array();
31
 
32
+ if( !$rulesetNode->root ){
33
  $selectors = array();
34
 
35
+ if( $rulesetNode->selectors && $rulesetNode->selectors ){
36
+ foreach($rulesetNode->selectors as $selector){
37
+ if( $selector->getIsOutput() ){
38
  $selectors[] = $selector;
39
  }
40
  }
41
  }
42
 
43
+ if( !$selectors ){
44
  $rulesetNode->selectors = null;
45
  $rulesetNode->rules = null;
46
+ }else{
47
+ $context = end($this->contexts); //$context = $this->contexts[ count($this->contexts) - 1];
48
+ $paths = $rulesetNode->joinSelectors( $context, $selectors);
49
  }
50
 
51
  $rulesetNode->paths = $paths;
52
  }
53
 
54
+ $this->contexts[] = $paths; //different from less.js. Placed after joinSelectors() so that $this->contexts will get correct $paths
55
  }
56
 
57
+ public function visitRulesetOut(){
58
+ array_pop($this->contexts);
59
  }
60
 
61
+ public function visitMedia($mediaNode) {
62
+ $context = end($this->contexts); //$context = $this->contexts[ count($this->contexts) - 1];
63
 
64
+ if( !count($context) || (is_object($context[0]) && $context[0]->multiMedia) ){
65
  $mediaNode->rules[0]->root = true;
66
  }
67
  }
68
 
69
  }
70
+
base/inc/lib/Less/Visitor/processExtends.php CHANGED
@@ -6,21 +6,21 @@
6
  * @package Less
7
  * @subpackage visitor
8
  */
9
- class Less_Visitor_processExtends extends Less_Visitor {
10
 
11
  public $allExtendsStack;
12
 
13
  /**
14
  * @param Less_Tree_Ruleset $root
15
  */
16
- public function run( $root ) {
17
  $extendFinder = new Less_Visitor_extendFinder();
18
  $extendFinder->run( $root );
19
- if ( !$extendFinder->foundExtends ) {
20
  return $root;
21
  }
22
 
23
- $root->allExtends = $this->doExtendChaining( $root->allExtends, $root->allExtends );
24
 
25
  $this->allExtendsStack = array();
26
  $this->allExtendsStack[] = &$root->allExtends;
@@ -28,7 +28,7 @@ class Less_Visitor_processExtends extends Less_Visitor {
28
  return $this->visitObj( $root );
29
  }
30
 
31
- private function doExtendChaining( $extendsList, $extendsListTarget, $iterationCount = 0 ) {
32
  //
33
  // chaining is different from normal extension.. if we extend an extend then we are not just copying, altering and pasting
34
  // the selector we would do normally, but we are also adding an extend with the same target selector
@@ -40,55 +40,58 @@ class Less_Visitor_processExtends extends Less_Visitor {
40
 
41
  $extendsToAdd = array();
42
 
43
- // loop through comparing every extend with every target extend.
 
44
  // a target extend is the one on the ruleset we are looking at copy/edit/pasting in place
45
  // e.g. .a:extend(.b) {} and .b:extend(.c) {} then the first extend extends the second one
46
  // and the second is the target.
47
  // the separation into two lists allows us to process a subset of chains with a bigger set, as is the
48
  // case when processing media queries
49
- for ( $extendIndex = 0, $extendsList_len = count( $extendsList ); $extendIndex < $extendsList_len; $extendIndex++ ) {
50
- for ( $targetExtendIndex = 0; $targetExtendIndex < count( $extendsListTarget ); $targetExtendIndex++ ) {
51
 
52
  $extend = $extendsList[$extendIndex];
53
  $targetExtend = $extendsListTarget[$targetExtendIndex];
54
 
55
  // Optimisation: Explicit reference, <https://github.com/wikimedia/less.php/pull/14>
56
- if ( \array_key_exists( $targetExtend->object_id, $extend->parent_ids ) ) {
57
  // ignore circular references
58
  continue;
59
  }
60
 
61
  // find a match in the target extends self selector (the bit before :extend)
62
  $selectorPath = array( $targetExtend->selfSelectors[0] );
63
- $matches = $this->findMatch( $extend, $selectorPath );
 
64
 
65
- if ( $matches ) {
66
 
67
  // we found a match, so for each self selector..
68
- foreach ( $extend->selfSelectors as $selfSelector ) {
 
69
 
70
  // process the extend as usual
71
- $newSelector = $this->extendSelector( $matches, $selectorPath, $selfSelector );
72
 
73
  // but now we create a new extend from it
74
- $newExtend = new Less_Tree_Extend( $targetExtend->selector, $targetExtend->option, 0 );
75
  $newExtend->selfSelectors = $newSelector;
76
 
77
  // add the extend onto the list of extends for that selector
78
- end( $newSelector )->extendList = array( $newExtend );
79
- // $newSelector[ count($newSelector)-1]->extendList = array($newExtend);
80
 
81
  // record that we need to add it.
82
  $extendsToAdd[] = $newExtend;
83
  $newExtend->ruleset = $targetExtend->ruleset;
84
 
85
- // remember its parents for circular references
86
- $newExtend->parent_ids = array_merge( $newExtend->parent_ids, $targetExtend->parent_ids, $extend->parent_ids );
87
 
88
  // only process the selector once.. if we have :extend(.a,.b) then multiple
89
  // extends will look at the same selector path, so when extending
90
  // we know that any others will be duplicates in terms of what is added to the css
91
- if ( $targetExtend->firstExtendOnThisSelectorPath ) {
92
  $newExtend->firstExtendOnThisSelectorPath = true;
93
  $targetExtend->ruleset->paths[] = $newSelector;
94
  }
@@ -97,85 +100,94 @@ class Less_Visitor_processExtends extends Less_Visitor {
97
  }
98
  }
99
 
100
- if ( $extendsToAdd ) {
101
  // try to detect circular references to stop a stack overflow.
102
  // may no longer be needed. $this->extendChainCount++;
103
- if ( $iterationCount > 100 ) {
104
 
105
  try{
106
  $selectorOne = $extendsToAdd[0]->selfSelectors[0]->toCSS();
107
  $selectorTwo = $extendsToAdd[0]->selector->toCSS();
108
- }catch ( Exception $e ) {
109
  $selectorOne = "{unable to calculate}";
110
  $selectorTwo = "{unable to calculate}";
111
  }
112
 
113
- throw new Less_Exception_Parser( "extend circular reference detected. One of the circular extends is currently:" . $selectorOne . ":extend(" . $selectorTwo . ")" );
114
  }
115
 
116
  // now process the new extends on the existing rules so that we can handle a extending b extending c ectending d extending e...
117
- $extendsToAdd = $this->doExtendChaining( $extendsToAdd, $extendsListTarget, $iterationCount + 1 );
118
  }
119
 
120
- return array_merge( $extendsList, $extendsToAdd );
121
  }
122
 
123
- protected function visitRule( $ruleNode, &$visitDeeper ) {
 
124
  $visitDeeper = false;
125
  }
126
 
127
- protected function visitMixinDefinition( $mixinDefinitionNode, &$visitDeeper ) {
128
  $visitDeeper = false;
129
  }
130
 
131
- protected function visitSelector( $selectorNode, &$visitDeeper ) {
132
  $visitDeeper = false;
133
  }
134
 
135
- protected function visitRuleset( $rulesetNode ) {
136
- if ( $rulesetNode->root ) {
 
 
137
  return;
138
  }
139
 
140
- $allExtends = end( $this->allExtendsStack );
141
- $paths_len = count( $rulesetNode->paths );
142
 
143
  // look at each selector path in the ruleset, find any extend matches and then copy, find and replace
144
- foreach ( $allExtends as $allExtend ) {
145
- for ( $pathIndex = 0; $pathIndex < $paths_len; $pathIndex++ ) {
146
 
147
  // extending extends happens initially, before the main pass
148
- if ( isset( $rulesetNode->extendOnEveryPath ) && $rulesetNode->extendOnEveryPath ) {
149
  continue;
150
  }
151
 
152
  $selectorPath = $rulesetNode->paths[$pathIndex];
153
 
154
- if ( end( $selectorPath )->extendList ) {
155
  continue;
156
  }
157
 
158
- $this->ExtendMatch( $rulesetNode, $allExtend, $selectorPath );
159
 
160
  }
161
  }
162
  }
163
 
164
- private function ExtendMatch( $rulesetNode, $extend, $selectorPath ) {
165
- $matches = $this->findMatch( $extend, $selectorPath );
166
 
167
- if ( $matches ) {
168
- foreach ( $extend->selfSelectors as $selfSelector ) {
169
- $rulesetNode->paths[] = $this->extendSelector( $matches, $selectorPath, $selfSelector );
 
 
 
170
  }
171
  }
172
  }
173
 
174
- private function findMatch( $extend, $haystackSelectorPath ) {
175
- if ( !$this->HasMatches( $extend, $haystackSelectorPath ) ) {
 
 
 
 
176
  return false;
177
  }
178
 
 
179
  //
180
  // look through the haystack selector path to try and find the needle - extend.selector
181
  // returns an array of selector matches that can then be replaced
@@ -186,39 +198,42 @@ class Less_Visitor_processExtends extends Less_Visitor {
186
  $potentialMatch = null;
187
  $matches = array();
188
 
 
 
189
  // loop through the haystack elements
190
- $haystack_path_len = count( $haystackSelectorPath );
191
- for ( $haystackSelectorIndex = 0; $haystackSelectorIndex < $haystack_path_len; $haystackSelectorIndex++ ) {
192
  $hackstackSelector = $haystackSelectorPath[$haystackSelectorIndex];
193
 
194
- $haystack_elements_len = count( $hackstackSelector->elements );
195
- for ( $hackstackElementIndex = 0; $hackstackElementIndex < $haystack_elements_len; $hackstackElementIndex++ ) {
196
 
197
  $haystackElement = $hackstackSelector->elements[$hackstackElementIndex];
198
 
199
  // if we allow elements before our match we can add a potential match every time. otherwise only at the first element.
200
- if ( $extend->allowBefore || ( $haystackSelectorIndex === 0 && $hackstackElementIndex === 0 ) ) {
201
- $potentialMatches[] = array( 'pathIndex' => $haystackSelectorIndex, 'index' => $hackstackElementIndex, 'matched' => 0, 'initialCombinator' => $haystackElement->combinator );
202
  $potentialMatches_len++;
203
  }
204
 
205
- for ( $i = 0; $i < $potentialMatches_len; $i++ ) {
206
 
207
  $potentialMatch = &$potentialMatches[$i];
208
  $potentialMatch = $this->PotentialMatch( $potentialMatch, $needleElements, $haystackElement, $hackstackElementIndex );
209
 
 
210
  // if we are still valid and have finished, test whether we have elements after and whether these are allowed
211
- if ( $potentialMatch && $potentialMatch['matched'] === $extend->selector->elements_len ) {
212
  $potentialMatch['finished'] = true;
213
 
214
- if ( !$extend->allowAfter && ( $hackstackElementIndex + 1 < $haystack_elements_len || $haystackSelectorIndex + 1 < $haystack_path_len ) ) {
215
  $potentialMatch = null;
216
  }
217
  }
218
 
219
  // if null we remove, if not, we are still valid, so either push as a valid match or continue
220
- if ( $potentialMatch ) {
221
- if ( $potentialMatch['finished'] ) {
222
  $potentialMatch['length'] = $extend->selector->elements_len;
223
  $potentialMatch['endPathIndex'] = $haystackSelectorIndex;
224
  $potentialMatch['endPathElementIndex'] = $hackstackElementIndex + 1; // index after end of match
@@ -229,7 +244,7 @@ class Less_Visitor_processExtends extends Less_Visitor {
229
  continue;
230
  }
231
 
232
- array_splice( $potentialMatches, $i, 1 );
233
  $potentialMatches_len--;
234
  $i--;
235
  }
@@ -239,22 +254,24 @@ class Less_Visitor_processExtends extends Less_Visitor {
239
  return $matches;
240
  }
241
 
 
242
  // Before going through all the nested loops, lets check to see if a match is possible
243
  // Reduces Bootstrap 3.1 compile time from ~6.5s to ~5.6s
244
- private function HasMatches( $extend, $haystackSelectorPath ) {
245
- if ( !$extend->selector->cacheable ) {
 
246
  return true;
247
  }
248
 
249
  $first_el = $extend->selector->_oelements[0];
250
 
251
- foreach ( $haystackSelectorPath as $hackstackSelector ) {
252
- if ( !$hackstackSelector->cacheable ) {
253
  return true;
254
  }
255
 
256
  // Optimisation: Explicit reference, <https://github.com/wikimedia/less.php/pull/14>
257
- if ( \array_key_exists( $first_el, $hackstackSelector->_oelements_assoc ) ) {
258
  return true;
259
  }
260
  }
@@ -262,27 +279,30 @@ class Less_Visitor_processExtends extends Less_Visitor {
262
  return false;
263
  }
264
 
 
265
  /**
266
  * @param integer $hackstackElementIndex
267
  */
268
- private function PotentialMatch( $potentialMatch, $needleElements, $haystackElement, $hackstackElementIndex ) {
269
- if ( $potentialMatch['matched'] > 0 ) {
 
 
270
 
271
  // selectors add " " onto the first element. When we use & it joins the selectors together, but if we don't
272
  // then each selector in haystackSelectorPath has a space before it added in the toCSS phase. so we need to work out
273
  // what the resulting combinator will be
274
  $targetCombinator = $haystackElement->combinator;
275
- if ( $targetCombinator === '' && $hackstackElementIndex === 0 ) {
276
  $targetCombinator = ' ';
277
  }
278
 
279
- if ( $needleElements[ $potentialMatch['matched'] ]->combinator !== $targetCombinator ) {
280
  return null;
281
  }
282
  }
283
 
284
  // if we don't match, null our match to indicate failure
285
- if ( !$this->isElementValuesEqual( $needleElements[$potentialMatch['matched'] ]->value, $haystackElement->value ) ) {
286
  return null;
287
  }
288
 
@@ -292,45 +312,49 @@ class Less_Visitor_processExtends extends Less_Visitor {
292
  return $potentialMatch;
293
  }
294
 
295
- private function isElementValuesEqual( $elementValue1, $elementValue2 ) {
296
- if ( $elementValue1 === $elementValue2 ) {
 
 
297
  return true;
298
  }
299
 
300
- if ( is_string( $elementValue1 ) || is_string( $elementValue2 ) ) {
301
  return false;
302
  }
303
 
304
- if ( $elementValue1 instanceof Less_Tree_Attribute ) {
305
  return $this->isAttributeValuesEqual( $elementValue1, $elementValue2 );
306
  }
307
 
308
  $elementValue1 = $elementValue1->value;
309
- if ( $elementValue1 instanceof Less_Tree_Selector ) {
310
  return $this->isSelectorValuesEqual( $elementValue1, $elementValue2 );
311
  }
312
 
313
  return false;
314
  }
315
 
 
316
  /**
317
  * @param Less_Tree_Selector $elementValue1
318
  */
319
- private function isSelectorValuesEqual( $elementValue1, $elementValue2 ) {
 
320
  $elementValue2 = $elementValue2->value;
321
- if ( !( $elementValue2 instanceof Less_Tree_Selector ) || $elementValue1->elements_len !== $elementValue2->elements_len ) {
322
  return false;
323
  }
324
 
325
- for ( $i = 0; $i < $elementValue1->elements_len; $i++ ) {
326
 
327
- if ( $elementValue1->elements[$i]->combinator !== $elementValue2->elements[$i]->combinator ) {
328
- if ( $i !== 0 || ( $elementValue1->elements[$i]->combinator || ' ' ) !== ( $elementValue2->elements[$i]->combinator || ' ' ) ) {
329
  return false;
330
  }
331
  }
332
 
333
- if ( !$this->isElementValuesEqual( $elementValue1->elements[$i]->value, $elementValue2->elements[$i]->value ) ) {
334
  return false;
335
  }
336
  }
@@ -338,36 +362,41 @@ class Less_Visitor_processExtends extends Less_Visitor {
338
  return true;
339
  }
340
 
 
341
  /**
342
  * @param Less_Tree_Attribute $elementValue1
343
  */
344
- private function isAttributeValuesEqual( $elementValue1, $elementValue2 ) {
345
- if ( $elementValue1->op !== $elementValue2->op || $elementValue1->key !== $elementValue2->key ) {
 
346
  return false;
347
  }
348
 
349
- if ( !$elementValue1->value || !$elementValue2->value ) {
350
- if ( $elementValue1->value || $elementValue2->value ) {
351
  return false;
352
  }
353
  return true;
354
  }
355
 
356
- $elementValue1 = ( $elementValue1->value->value ? $elementValue1->value->value : $elementValue1->value );
357
- $elementValue2 = ( $elementValue2->value->value ? $elementValue2->value->value : $elementValue2->value );
358
 
359
  return $elementValue1 === $elementValue2;
360
  }
361
 
362
- private function extendSelector( $matches, $selectorPath, $replacementSelector ) {
363
- // for a set of matches, replace each match with the replacement selector
 
 
364
 
365
  $currentSelectorPathIndex = 0;
366
  $currentSelectorPathElementIndex = 0;
367
  $path = array();
368
- $selectorPath_len = count( $selectorPath );
 
 
369
 
370
- for ( $matchIndex = 0, $matches_len = count( $matches ); $matchIndex < $matches_len; $matchIndex++ ) {
371
 
372
  $match = $matches[$matchIndex];
373
  $selector = $selectorPath[ $match['pathIndex'] ];
@@ -379,63 +408,64 @@ class Less_Visitor_processExtends extends Less_Visitor {
379
  $replacementSelector->elements[0]->currentFileInfo
380
  );
381
 
382
- if ( $match['pathIndex'] > $currentSelectorPathIndex && $currentSelectorPathElementIndex > 0 ) {
383
- $last_path = end( $path );
384
- $last_path->elements = array_merge( $last_path->elements, array_slice( $selectorPath[$currentSelectorPathIndex]->elements, $currentSelectorPathElementIndex ) );
385
  $currentSelectorPathElementIndex = 0;
386
  $currentSelectorPathIndex++;
387
  }
388
 
389
  $newElements = array_merge(
390
- array_slice( $selector->elements, $currentSelectorPathElementIndex, ( $match['index'] - $currentSelectorPathElementIndex ) ), // last parameter of array_slice is different than the last parameter of javascript's slice
391
- array( $firstElement ),
392
- array_slice( $replacementSelector->elements, 1 )
393
  );
394
 
395
- if ( $currentSelectorPathIndex === $match['pathIndex'] && $matchIndex > 0 ) {
396
- $last_key = count( $path ) - 1;
397
- $path[$last_key]->elements = array_merge( $path[$last_key]->elements, $newElements );
398
- } else {
399
- $path = array_merge( $path, array_slice( $selectorPath, $currentSelectorPathIndex, $match['pathIndex'] ) );
400
  $path[] = new Less_Tree_Selector( $newElements );
401
  }
402
 
403
  $currentSelectorPathIndex = $match['endPathIndex'];
404
  $currentSelectorPathElementIndex = $match['endPathElementIndex'];
405
- if ( $currentSelectorPathElementIndex >= count( $selectorPath[$currentSelectorPathIndex]->elements ) ) {
406
  $currentSelectorPathElementIndex = 0;
407
  $currentSelectorPathIndex++;
408
  }
409
  }
410
 
411
- if ( $currentSelectorPathIndex < $selectorPath_len && $currentSelectorPathElementIndex > 0 ) {
412
- $last_path = end( $path );
413
- $last_path->elements = array_merge( $last_path->elements, array_slice( $selectorPath[$currentSelectorPathIndex]->elements, $currentSelectorPathElementIndex ) );
414
  $currentSelectorPathIndex++;
415
  }
416
 
417
  $slice_len = $selectorPath_len - $currentSelectorPathIndex;
418
- $path = array_merge( $path, array_slice( $selectorPath, $currentSelectorPathIndex, $slice_len ) );
419
 
420
  return $path;
421
  }
422
 
423
- protected function visitMedia( $mediaNode ) {
424
- $newAllExtends = array_merge( $mediaNode->allExtends, end( $this->allExtendsStack ) );
425
- $this->allExtendsStack[] = $this->doExtendChaining( $newAllExtends, $mediaNode->allExtends );
 
426
  }
427
 
428
- protected function visitMediaOut() {
429
  array_pop( $this->allExtendsStack );
430
  }
431
 
432
- protected function visitDirective( $directiveNode ) {
433
- $newAllExtends = array_merge( $directiveNode->allExtends, end( $this->allExtendsStack ) );
434
- $this->allExtendsStack[] = $this->doExtendChaining( $newAllExtends, $directiveNode->allExtends );
435
  }
436
 
437
- protected function visitDirectiveOut() {
438
- array_pop( $this->allExtendsStack );
439
  }
440
 
441
  }
6
  * @package Less
7
  * @subpackage visitor
8
  */
9
+ class Less_Visitor_processExtends extends Less_Visitor{
10
 
11
  public $allExtendsStack;
12
 
13
  /**
14
  * @param Less_Tree_Ruleset $root
15
  */
16
+ public function run( $root ){
17
  $extendFinder = new Less_Visitor_extendFinder();
18
  $extendFinder->run( $root );
19
+ if( !$extendFinder->foundExtends){
20
  return $root;
21
  }
22
 
23
+ $root->allExtends = $this->doExtendChaining( $root->allExtends, $root->allExtends);
24
 
25
  $this->allExtendsStack = array();
26
  $this->allExtendsStack[] = &$root->allExtends;
28
  return $this->visitObj( $root );
29
  }
30
 
31
+ private function doExtendChaining( $extendsList, $extendsListTarget, $iterationCount = 0){
32
  //
33
  // chaining is different from normal extension.. if we extend an extend then we are not just copying, altering and pasting
34
  // the selector we would do normally, but we are also adding an extend with the same target selector
40
 
41
  $extendsToAdd = array();
42
 
43
+
44
+ //loop through comparing every extend with every target extend.
45
  // a target extend is the one on the ruleset we are looking at copy/edit/pasting in place
46
  // e.g. .a:extend(.b) {} and .b:extend(.c) {} then the first extend extends the second one
47
  // and the second is the target.
48
  // the separation into two lists allows us to process a subset of chains with a bigger set, as is the
49
  // case when processing media queries
50
+ for( $extendIndex = 0, $extendsList_len = count($extendsList); $extendIndex < $extendsList_len; $extendIndex++ ){
51
+ for( $targetExtendIndex = 0; $targetExtendIndex < count($extendsListTarget); $targetExtendIndex++ ){
52
 
53
  $extend = $extendsList[$extendIndex];
54
  $targetExtend = $extendsListTarget[$targetExtendIndex];
55
 
56
  // Optimisation: Explicit reference, <https://github.com/wikimedia/less.php/pull/14>
57
+ if( \array_key_exists($targetExtend->object_id, $extend->parent_ids) ){
58
  // ignore circular references
59
  continue;
60
  }
61
 
62
  // find a match in the target extends self selector (the bit before :extend)
63
  $selectorPath = array( $targetExtend->selfSelectors[0] );
64
+ $matches = $this->findMatch( $extend, $selectorPath);
65
+
66
 
67
+ if( $matches ){
68
 
69
  // we found a match, so for each self selector..
70
+ foreach($extend->selfSelectors as $selfSelector ){
71
+
72
 
73
  // process the extend as usual
74
+ $newSelector = $this->extendSelector( $matches, $selectorPath, $selfSelector);
75
 
76
  // but now we create a new extend from it
77
+ $newExtend = new Less_Tree_Extend( $targetExtend->selector, $targetExtend->option, 0);
78
  $newExtend->selfSelectors = $newSelector;
79
 
80
  // add the extend onto the list of extends for that selector
81
+ end($newSelector)->extendList = array($newExtend);
82
+ //$newSelector[ count($newSelector)-1]->extendList = array($newExtend);
83
 
84
  // record that we need to add it.
85
  $extendsToAdd[] = $newExtend;
86
  $newExtend->ruleset = $targetExtend->ruleset;
87
 
88
+ //remember its parents for circular references
89
+ $newExtend->parent_ids = array_merge($newExtend->parent_ids,$targetExtend->parent_ids,$extend->parent_ids);
90
 
91
  // only process the selector once.. if we have :extend(.a,.b) then multiple
92
  // extends will look at the same selector path, so when extending
93
  // we know that any others will be duplicates in terms of what is added to the css
94
+ if( $targetExtend->firstExtendOnThisSelectorPath ){
95
  $newExtend->firstExtendOnThisSelectorPath = true;
96
  $targetExtend->ruleset->paths[] = $newSelector;
97
  }
100
  }
101
  }
102
 
103
+ if( $extendsToAdd ){
104
  // try to detect circular references to stop a stack overflow.
105
  // may no longer be needed. $this->extendChainCount++;
106
+ if( $iterationCount > 100) {
107
 
108
  try{
109
  $selectorOne = $extendsToAdd[0]->selfSelectors[0]->toCSS();
110
  $selectorTwo = $extendsToAdd[0]->selector->toCSS();
111
+ }catch(Exception $e){
112
  $selectorOne = "{unable to calculate}";
113
  $selectorTwo = "{unable to calculate}";
114
  }
115
 
116
+ throw new Less_Exception_Parser("extend circular reference detected. One of the circular extends is currently:" . $selectorOne . ":extend(" . $selectorTwo . ")");
117
  }
118
 
119
  // now process the new extends on the existing rules so that we can handle a extending b extending c ectending d extending e...
120
+ $extendsToAdd = $this->doExtendChaining( $extendsToAdd, $extendsListTarget, $iterationCount+1);
121
  }
122
 
123
+ return array_merge($extendsList, $extendsToAdd);
124
  }
125
 
126
+
127
+ protected function visitRule( $ruleNode, &$visitDeeper ){
128
  $visitDeeper = false;
129
  }
130
 
131
+ protected function visitMixinDefinition( $mixinDefinitionNode, &$visitDeeper ){
132
  $visitDeeper = false;
133
  }
134
 
135
+ protected function visitSelector( $selectorNode, &$visitDeeper ){
136
  $visitDeeper = false;
137
  }
138
 
139
+ protected function visitRuleset($rulesetNode){
140
+
141
+
142
+ if( $rulesetNode->root ){
143
  return;
144
  }
145
 
146
+ $allExtends = end($this->allExtendsStack);
147
+ $paths_len = count($rulesetNode->paths);
148
 
149
  // look at each selector path in the ruleset, find any extend matches and then copy, find and replace
150
+ foreach($allExtends as $allExtend){
151
+ for($pathIndex = 0; $pathIndex < $paths_len; $pathIndex++ ){
152
 
153
  // extending extends happens initially, before the main pass
154
+ if( isset($rulesetNode->extendOnEveryPath) && $rulesetNode->extendOnEveryPath ){
155
  continue;
156
  }
157
 
158
  $selectorPath = $rulesetNode->paths[$pathIndex];
159
 
160
+ if( end($selectorPath)->extendList ){
161
  continue;
162
  }
163
 
164
+ $this->ExtendMatch( $rulesetNode, $allExtend, $selectorPath);
165
 
166
  }
167
  }
168
  }
169
 
 
 
170
 
171
+ private function ExtendMatch( $rulesetNode, $extend, $selectorPath ){
172
+ $matches = $this->findMatch($extend, $selectorPath);
173
+
174
+ if( $matches ){
175
+ foreach($extend->selfSelectors as $selfSelector ){
176
+ $rulesetNode->paths[] = $this->extendSelector($matches, $selectorPath, $selfSelector);
177
  }
178
  }
179
  }
180
 
181
+
182
+
183
+ private function findMatch($extend, $haystackSelectorPath ){
184
+
185
+
186
+ if( !$this->HasMatches($extend, $haystackSelectorPath) ){
187
  return false;
188
  }
189
 
190
+
191
  //
192
  // look through the haystack selector path to try and find the needle - extend.selector
193
  // returns an array of selector matches that can then be replaced
198
  $potentialMatch = null;
199
  $matches = array();
200
 
201
+
202
+
203
  // loop through the haystack elements
204
+ $haystack_path_len = count($haystackSelectorPath);
205
+ for($haystackSelectorIndex = 0; $haystackSelectorIndex < $haystack_path_len; $haystackSelectorIndex++ ){
206
  $hackstackSelector = $haystackSelectorPath[$haystackSelectorIndex];
207
 
208
+ $haystack_elements_len = count($hackstackSelector->elements);
209
+ for($hackstackElementIndex = 0; $hackstackElementIndex < $haystack_elements_len; $hackstackElementIndex++ ){
210
 
211
  $haystackElement = $hackstackSelector->elements[$hackstackElementIndex];
212
 
213
  // if we allow elements before our match we can add a potential match every time. otherwise only at the first element.
214
+ if( $extend->allowBefore || ($haystackSelectorIndex === 0 && $hackstackElementIndex === 0) ){
215
+ $potentialMatches[] = array('pathIndex'=> $haystackSelectorIndex, 'index'=> $hackstackElementIndex, 'matched'=> 0, 'initialCombinator'=> $haystackElement->combinator);
216
  $potentialMatches_len++;
217
  }
218
 
219
+ for($i = 0; $i < $potentialMatches_len; $i++ ){
220
 
221
  $potentialMatch = &$potentialMatches[$i];
222
  $potentialMatch = $this->PotentialMatch( $potentialMatch, $needleElements, $haystackElement, $hackstackElementIndex );
223
 
224
+
225
  // if we are still valid and have finished, test whether we have elements after and whether these are allowed
226
+ if( $potentialMatch && $potentialMatch['matched'] === $extend->selector->elements_len ){
227
  $potentialMatch['finished'] = true;
228
 
229
+ if( !$extend->allowAfter && ($hackstackElementIndex+1 < $haystack_elements_len || $haystackSelectorIndex+1 < $haystack_path_len) ){
230
  $potentialMatch = null;
231
  }
232
  }
233
 
234
  // if null we remove, if not, we are still valid, so either push as a valid match or continue
235
+ if( $potentialMatch ){
236
+ if( $potentialMatch['finished'] ){
237
  $potentialMatch['length'] = $extend->selector->elements_len;
238
  $potentialMatch['endPathIndex'] = $haystackSelectorIndex;
239
  $potentialMatch['endPathElementIndex'] = $hackstackElementIndex + 1; // index after end of match
244
  continue;
245
  }
246
 
247
+ array_splice($potentialMatches, $i, 1);
248
  $potentialMatches_len--;
249
  $i--;
250
  }
254
  return $matches;
255
  }
256
 
257
+
258
  // Before going through all the nested loops, lets check to see if a match is possible
259
  // Reduces Bootstrap 3.1 compile time from ~6.5s to ~5.6s
260
+ private function HasMatches($extend, $haystackSelectorPath){
261
+
262
+ if( !$extend->selector->cacheable ){
263
  return true;
264
  }
265
 
266
  $first_el = $extend->selector->_oelements[0];
267
 
268
+ foreach($haystackSelectorPath as $hackstackSelector){
269
+ if( !$hackstackSelector->cacheable ){
270
  return true;
271
  }
272
 
273
  // Optimisation: Explicit reference, <https://github.com/wikimedia/less.php/pull/14>
274
+ if( \array_key_exists($first_el, $hackstackSelector->_oelements_assoc) ){
275
  return true;
276
  }
277
  }
279
  return false;
280
  }
281
 
282
+
283
  /**
284
  * @param integer $hackstackElementIndex
285
  */
286
+ private function PotentialMatch( $potentialMatch, $needleElements, $haystackElement, $hackstackElementIndex ){
287
+
288
+
289
+ if( $potentialMatch['matched'] > 0 ){
290
 
291
  // selectors add " " onto the first element. When we use & it joins the selectors together, but if we don't
292
  // then each selector in haystackSelectorPath has a space before it added in the toCSS phase. so we need to work out
293
  // what the resulting combinator will be
294
  $targetCombinator = $haystackElement->combinator;
295
+ if( $targetCombinator === '' && $hackstackElementIndex === 0 ){
296
  $targetCombinator = ' ';
297
  }
298
 
299
+ if( $needleElements[ $potentialMatch['matched'] ]->combinator !== $targetCombinator ){
300
  return null;
301
  }
302
  }
303
 
304
  // if we don't match, null our match to indicate failure
305
+ if( !$this->isElementValuesEqual( $needleElements[$potentialMatch['matched'] ]->value, $haystackElement->value) ){
306
  return null;
307
  }
308
 
312
  return $potentialMatch;
313
  }
314
 
315
+
316
+ private function isElementValuesEqual( $elementValue1, $elementValue2 ){
317
+
318
+ if( $elementValue1 === $elementValue2 ){
319
  return true;
320
  }
321
 
322
+ if( is_string($elementValue1) || is_string($elementValue2) ) {
323
  return false;
324
  }
325
 
326
+ if( $elementValue1 instanceof Less_Tree_Attribute ){
327
  return $this->isAttributeValuesEqual( $elementValue1, $elementValue2 );
328
  }
329
 
330
  $elementValue1 = $elementValue1->value;
331
+ if( $elementValue1 instanceof Less_Tree_Selector ){
332
  return $this->isSelectorValuesEqual( $elementValue1, $elementValue2 );
333
  }
334
 
335
  return false;
336
  }
337
 
338
+
339
  /**
340
  * @param Less_Tree_Selector $elementValue1
341
  */
342
+ private function isSelectorValuesEqual( $elementValue1, $elementValue2 ){
343
+
344
  $elementValue2 = $elementValue2->value;
345
+ if( !($elementValue2 instanceof Less_Tree_Selector) || $elementValue1->elements_len !== $elementValue2->elements_len ){
346
  return false;
347
  }
348
 
349
+ for( $i = 0; $i < $elementValue1->elements_len; $i++ ){
350
 
351
+ if( $elementValue1->elements[$i]->combinator !== $elementValue2->elements[$i]->combinator ){
352
+ if( $i !== 0 || ($elementValue1->elements[$i]->combinator || ' ') !== ($elementValue2->elements[$i]->combinator || ' ') ){
353
  return false;
354
  }
355
  }
356
 
357
+ if( !$this->isElementValuesEqual($elementValue1->elements[$i]->value, $elementValue2->elements[$i]->value) ){
358
  return false;
359
  }
360
  }
362
  return true;
363
  }
364
 
365
+
366
  /**
367
  * @param Less_Tree_Attribute $elementValue1
368
  */
369
+ private function isAttributeValuesEqual( $elementValue1, $elementValue2 ){
370
+
371
+ if( $elementValue1->op !== $elementValue2->op || $elementValue1->key !== $elementValue2->key ){
372
  return false;
373
  }
374
 
375
+ if( !$elementValue1->value || !$elementValue2->value ){
376
+ if( $elementValue1->value || $elementValue2->value ) {
377
  return false;
378
  }
379
  return true;
380
  }
381
 
382
+ $elementValue1 = ($elementValue1->value->value ? $elementValue1->value->value : $elementValue1->value );
383
+ $elementValue2 = ($elementValue2->value->value ? $elementValue2->value->value : $elementValue2->value );
384
 
385
  return $elementValue1 === $elementValue2;
386
  }
387
 
388
+
389
+ private function extendSelector($matches, $selectorPath, $replacementSelector){
390
+
391
+ //for a set of matches, replace each match with the replacement selector
392
 
393
  $currentSelectorPathIndex = 0;
394
  $currentSelectorPathElementIndex = 0;
395
  $path = array();
396
+ $selectorPath_len = count($selectorPath);
397
+
398
+ for($matchIndex = 0, $matches_len = count($matches); $matchIndex < $matches_len; $matchIndex++ ){
399
 
 
400
 
401
  $match = $matches[$matchIndex];
402
  $selector = $selectorPath[ $match['pathIndex'] ];
408
  $replacementSelector->elements[0]->currentFileInfo
409
  );
410
 
411
+ if( $match['pathIndex'] > $currentSelectorPathIndex && $currentSelectorPathElementIndex > 0 ){
412
+ $last_path = end($path);
413
+ $last_path->elements = array_merge( $last_path->elements, array_slice( $selectorPath[$currentSelectorPathIndex]->elements, $currentSelectorPathElementIndex));
414
  $currentSelectorPathElementIndex = 0;
415
  $currentSelectorPathIndex++;
416
  }
417
 
418
  $newElements = array_merge(
419
+ array_slice($selector->elements, $currentSelectorPathElementIndex, ($match['index'] - $currentSelectorPathElementIndex) ) // last parameter of array_slice is different than the last parameter of javascript's slice
420
+ , array($firstElement)
421
+ , array_slice($replacementSelector->elements,1)
422
  );
423
 
424
+ if( $currentSelectorPathIndex === $match['pathIndex'] && $matchIndex > 0 ){
425
+ $last_key = count($path)-1;
426
+ $path[$last_key]->elements = array_merge($path[$last_key]->elements,$newElements);
427
+ }else{
428
+ $path = array_merge( $path, array_slice( $selectorPath, $currentSelectorPathIndex, $match['pathIndex'] ));
429
  $path[] = new Less_Tree_Selector( $newElements );
430
  }
431
 
432
  $currentSelectorPathIndex = $match['endPathIndex'];
433
  $currentSelectorPathElementIndex = $match['endPathElementIndex'];
434
+ if( $currentSelectorPathElementIndex >= count($selectorPath[$currentSelectorPathIndex]->elements) ){
435
  $currentSelectorPathElementIndex = 0;
436
  $currentSelectorPathIndex++;
437
  }
438
  }
439
 
440
+ if( $currentSelectorPathIndex < $selectorPath_len && $currentSelectorPathElementIndex > 0 ){
441
+ $last_path = end($path);
442
+ $last_path->elements = array_merge( $last_path->elements, array_slice($selectorPath[$currentSelectorPathIndex]->elements, $currentSelectorPathElementIndex));
443
  $currentSelectorPathIndex++;
444
  }
445
 
446
  $slice_len = $selectorPath_len - $currentSelectorPathIndex;
447
+ $path = array_merge($path, array_slice($selectorPath, $currentSelectorPathIndex, $slice_len));
448
 
449
  return $path;
450
  }
451
 
452
+
453
+ protected function visitMedia( $mediaNode ){
454
+ $newAllExtends = array_merge( $mediaNode->allExtends, end($this->allExtendsStack) );
455
+ $this->allExtendsStack[] = $this->doExtendChaining($newAllExtends, $mediaNode->allExtends);
456
  }
457
 
458
+ protected function visitMediaOut(){
459
  array_pop( $this->allExtendsStack );
460
  }
461
 
462
+ protected function visitDirective( $directiveNode ){
463
+ $newAllExtends = array_merge( $directiveNode->allExtends, end($this->allExtendsStack) );
464
+ $this->allExtendsStack[] = $this->doExtendChaining($newAllExtends, $directiveNode->allExtends);
465
  }
466
 
467
+ protected function visitDirectiveOut(){
468
+ array_pop($this->allExtendsStack);
469
  }
470
 
471
  }
base/inc/lib/Less/Visitor/toCSS.php CHANGED
@@ -6,72 +6,73 @@
6
  * @package Less
7
  * @subpackage visitor
8
  */
9
- class Less_Visitor_toCSS extends Less_VisitorReplacing {
10
 
11
  private $charset;
12
 
13
- public function __construct() {
14
  parent::__construct();
15
  }
16
 
17
  /**
18
  * @param Less_Tree_Ruleset $root
19
  */
20
- public function run( $root ) {
21
- return $this->visitObj( $root );
22
  }
23
 
24
- public function visitRule( $ruleNode ) {
25
- if ( $ruleNode->variable ) {
26
  return array();
27
  }
28
  return $ruleNode;
29
  }
30
 
31
- public function visitMixinDefinition( $mixinNode ) {
32
  // mixin definitions do not get eval'd - this means they keep state
33
  // so we have to clear that state here so it isn't used if toCSS is called twice
34
  $mixinNode->frames = array();
35
  return array();
36
  }
37
 
38
- public function visitExtend() {
39
  return array();
40
  }
41
 
42
- public function visitComment( $commentNode ) {
43
- if ( $commentNode->isSilent() ) {
44
  return array();
45
  }
46
  return $commentNode;
47
  }
48
 
49
- public function visitMedia( $mediaNode, &$visitDeeper ) {
50
- $mediaNode->accept( $this );
51
  $visitDeeper = false;
52
 
53
- if ( !$mediaNode->rules ) {
54
  return array();
55
  }
56
  return $mediaNode;
57
  }
58
 
59
- public function visitDirective( $directiveNode ) {
60
- if ( isset( $directiveNode->currentFileInfo['reference'] ) && ( !property_exists( $directiveNode, 'isReferenced' ) || !$directiveNode->isReferenced ) ) {
61
  return array();
62
  }
63
- if ( $directiveNode->name === '@charset' ) {
64
  // Only output the debug info together with subsequent @charset definitions
65
  // a comment (or @media statement) before the actual @charset directive would
66
  // be considered illegal css as it has to be on the first line
67
- if ( isset( $this->charset ) && $this->charset ) {
68
 
69
- // if( $directiveNode->debugInfo ){
70
  // $comment = new Less_Tree_Comment('/* ' . str_replace("\n",'',$directiveNode->toCSS())." */\n");
71
  // $comment->debugInfo = $directiveNode->debugInfo;
72
  // return $this->visit($comment);
73
  //}
74
 
 
75
  return array();
76
  }
77
  $this->charset = true;
@@ -79,100 +80,109 @@ class Less_Visitor_toCSS extends Less_VisitorReplacing {
79
  return $directiveNode;
80
  }
81
 
82
- public function checkPropertiesInRoot( $rulesetNode ) {
83
- if ( !$rulesetNode->firstRoot ) {
 
84
  return;
85
  }
86
 
87
- foreach ( $rulesetNode->rules as $ruleNode ) {
88
- if ( $ruleNode instanceof Less_Tree_Rule && !$ruleNode->variable ) {
89
- $msg = "properties must be inside selector blocks, they cannot be in the root. Index ".$ruleNode->index.( $ruleNode->currentFileInfo ? ( ' Filename: '.$ruleNode->currentFileInfo['filename'] ) : null );
90
- throw new Less_Exception_Compiler( $msg );
91
  }
92
  }
93
  }
94
 
95
- public function visitRuleset( $rulesetNode, &$visitDeeper ) {
 
 
96
  $visitDeeper = false;
97
 
98
  $this->checkPropertiesInRoot( $rulesetNode );
99
 
100
- if ( $rulesetNode->root ) {
101
  return $this->visitRulesetRoot( $rulesetNode );
102
  }
103
 
104
  $rulesets = array();
105
- $rulesetNode->paths = $this->visitRulesetPaths( $rulesetNode );
 
106
 
107
  // Compile rules and rulesets
108
- $nodeRuleCnt = $rulesetNode->rules ? count( $rulesetNode->rules ) : 0;
109
- for ( $i = 0; $i < $nodeRuleCnt; ) {
110
  $rule = $rulesetNode->rules[$i];
111
 
112
- if ( property_exists( $rule, 'rules' ) ) {
113
  // visit because we are moving them out from being a child
114
- $rulesets[] = $this->visitObj( $rule );
115
- array_splice( $rulesetNode->rules, $i, 1 );
116
  $nodeRuleCnt--;
117
  continue;
118
  }
119
  $i++;
120
  }
121
 
 
122
  // accept the visitor to remove rules and refactor itself
123
  // then we can decide now whether we want it or not
124
- if ( $nodeRuleCnt > 0 ) {
125
- $rulesetNode->accept( $this );
126
 
127
- if ( $rulesetNode->rules ) {
128
 
129
- if ( count( $rulesetNode->rules ) > 1 ) {
130
  $this->_mergeRules( $rulesetNode->rules );
131
  $this->_removeDuplicateRules( $rulesetNode->rules );
132
  }
133
 
134
  // now decide whether we keep the ruleset
135
- if ( $rulesetNode->paths ) {
136
- // array_unshift($rulesets, $rulesetNode);
137
- array_splice( $rulesets, 0, 0, array( $rulesetNode ) );
138
  }
139
  }
140
 
141
  }
142
 
143
- if ( count( $rulesets ) === 1 ) {
 
144
  return $rulesets[0];
145
  }
146
  return $rulesets;
147
  }
148
 
 
149
  /**
150
  * Helper function for visitiRuleset
151
  *
152
  * return array|Less_Tree_Ruleset
153
  */
154
- private function visitRulesetRoot( $rulesetNode ) {
155
  $rulesetNode->accept( $this );
156
- if ( $rulesetNode->firstRoot || $rulesetNode->rules ) {
157
  return $rulesetNode;
158
  }
159
  return array();
160
  }
161
 
 
162
  /**
163
  * Helper function for visitRuleset()
164
  *
165
  * @return array
166
  */
167
- private function visitRulesetPaths( $rulesetNode ) {
 
168
  $paths = array();
169
- foreach ( $rulesetNode->paths as $p ) {
170
- if ( $p[0]->elements[0]->combinator === ' ' ) {
171
  $p[0]->elements[0]->combinator = '';
172
  }
173
 
174
- foreach ( $p as $pi ) {
175
- if ( $pi->getIsReferenced() && $pi->getIsOutput() ) {
176
  $paths[] = $p;
177
  break;
178
  }
@@ -182,26 +192,26 @@ class Less_Visitor_toCSS extends Less_VisitorReplacing {
182
  return $paths;
183
  }
184
 
185
- protected function _removeDuplicateRules( &$rules ) {
186
  // remove duplicates
187
  $ruleCache = array();
188
- for ( $i = count( $rules ) - 1; $i >= 0; $i-- ) {
189
  $rule = $rules[$i];
190
- if ( $rule instanceof Less_Tree_Rule || $rule instanceof Less_Tree_NameValue ) {
191
 
192
- if ( !isset( $ruleCache[$rule->name] ) ) {
193
  $ruleCache[$rule->name] = $rule;
194
- } else {
195
  $ruleList =& $ruleCache[$rule->name];
196
 
197
- if ( $ruleList instanceof Less_Tree_Rule || $ruleList instanceof Less_Tree_NameValue ) {
198
  $ruleList = $ruleCache[$rule->name] = array( $ruleCache[$rule->name]->toCSS() );
199
  }
200
 
201
  $ruleCSS = $rule->toCSS();
202
- if ( array_search( $ruleCSS, $ruleList ) !== false ) {
203
- array_splice( $rules, $i, 1 );
204
- } else {
205
  $ruleList[] = $ruleCSS;
206
  }
207
  }
@@ -209,26 +219,26 @@ class Less_Visitor_toCSS extends Less_VisitorReplacing {
209
  }
210
  }
211
 
212
- protected function _mergeRules( &$rules ) {
213
  $groups = array();
214
 
215
- // obj($rules);
216
 
217
- $rules_len = count( $rules );
218
- for ( $i = 0; $i < $rules_len; $i++ ) {
219
  $rule = $rules[$i];
220
 
221
- if ( ( $rule instanceof Less_Tree_Rule ) && $rule->merge ) {
222
 
223
  $key = $rule->name;
224
- if ( $rule->important ) {
225
  $key .= ',!';
226
  }
227
 
228
- if ( !isset( $groups[$key] ) ) {
229
  $groups[$key] = array();
230
- } else {
231
- array_splice( $rules, $i--, 1 );
232
  $rules_len--;
233
  }
234
 
@@ -236,45 +246,47 @@ class Less_Visitor_toCSS extends Less_VisitorReplacing {
236
  }
237
  }
238
 
239
- foreach ( $groups as $parts ) {
240
 
241
- if ( count( $parts ) > 1 ) {
 
 
242
  $rule = $parts[0];
243
  $spacedGroups = array();
244
  $lastSpacedGroup = array();
245
  $parts_mapped = array();
246
- foreach ( $parts as $p ) {
247
- if ( $p->merge === '+' ) {
248
- if ( $lastSpacedGroup ) {
249
- $spacedGroups[] = self::toExpression( $lastSpacedGroup );
250
  }
251
  $lastSpacedGroup = array();
252
  }
253
  $lastSpacedGroup[] = $p;
254
  }
255
 
256
- $spacedGroups[] = self::toExpression( $lastSpacedGroup );
257
- $rule->value = self::toValue( $spacedGroups );
258
  }
259
  }
260
 
261
  }
262
 
263
- public static function toExpression( $values ) {
264
  $mapped = array();
265
- foreach ( $values as $p ) {
266
  $mapped[] = $p->value;
267
  }
268
  return new Less_Tree_Expression( $mapped );
269
  }
270
 
271
- public static function toValue( $values ) {
272
- // return new Less_Tree_Value($values); ??
273
 
274
  $mapped = array();
275
- foreach ( $values as $p ) {
276
  $mapped[] = $p;
277
  }
278
- return new Less_Tree_Value( $mapped );
279
  }
280
  }
 
6
  * @package Less
7
  * @subpackage visitor
8
  */
9
+ class Less_Visitor_toCSS extends Less_VisitorReplacing{
10
 
11
  private $charset;
12
 
13
+ public function __construct(){
14
  parent::__construct();
15
  }
16
 
17
  /**
18
  * @param Less_Tree_Ruleset $root
19
  */
20
+ public function run( $root ){
21
+ return $this->visitObj($root);
22
  }
23
 
24
+ public function visitRule( $ruleNode ){
25
+ if( $ruleNode->variable ){
26
  return array();
27
  }
28
  return $ruleNode;
29
  }
30
 
31
+ public function visitMixinDefinition($mixinNode){
32
  // mixin definitions do not get eval'd - this means they keep state
33
  // so we have to clear that state here so it isn't used if toCSS is called twice
34
  $mixinNode->frames = array();
35
  return array();
36
  }
37
 
38
+ public function visitExtend(){
39
  return array();
40
  }
41
 
42
+ public function visitComment( $commentNode ){
43
+ if( $commentNode->isSilent() ){
44
  return array();
45
  }
46
  return $commentNode;
47
  }
48
 
49
+ public function visitMedia( $mediaNode, &$visitDeeper ){
50
+ $mediaNode->accept($this);
51
  $visitDeeper = false;
52
 
53
+ if( !$mediaNode->rules ){
54
  return array();
55
  }
56
  return $mediaNode;
57
  }
58
 
59
+ public function visitDirective( $directiveNode ){
60
+ if( isset($directiveNode->currentFileInfo['reference']) && (!property_exists($directiveNode,'isReferenced') || !$directiveNode->isReferenced) ){
61
  return array();
62
  }
63
+ if( $directiveNode->name === '@charset' ){
64
  // Only output the debug info together with subsequent @charset definitions
65
  // a comment (or @media statement) before the actual @charset directive would
66
  // be considered illegal css as it has to be on the first line
67
+ if( isset($this->charset) && $this->charset ){
68
 
69
+ //if( $directiveNode->debugInfo ){
70
  // $comment = new Less_Tree_Comment('/* ' . str_replace("\n",'',$directiveNode->toCSS())." */\n");
71
  // $comment->debugInfo = $directiveNode->debugInfo;
72
  // return $this->visit($comment);
73
  //}
74
 
75
+
76
  return array();
77
  }
78
  $this->charset = true;
80
  return $directiveNode;
81
  }
82
 
83
+ public function checkPropertiesInRoot( $rulesetNode ){
84
+
85
+ if( !$rulesetNode->firstRoot ){
86
  return;
87
  }
88
 
89
+ foreach($rulesetNode->rules as $ruleNode){
90
+ if( $ruleNode instanceof Less_Tree_Rule && !$ruleNode->variable ){
91
+ $msg = "properties must be inside selector blocks, they cannot be in the root. Index ".$ruleNode->index.($ruleNode->currentFileInfo ? (' Filename: '.$ruleNode->currentFileInfo['filename']) : null);
92
+ throw new Less_Exception_Compiler($msg);
93
  }
94
  }
95
  }
96
 
97
+
98
+ public function visitRuleset( $rulesetNode, &$visitDeeper ){
99
+
100
  $visitDeeper = false;
101
 
102
  $this->checkPropertiesInRoot( $rulesetNode );
103
 
104
+ if( $rulesetNode->root ){
105
  return $this->visitRulesetRoot( $rulesetNode );
106
  }
107
 
108
  $rulesets = array();
109
+ $rulesetNode->paths = $this->visitRulesetPaths($rulesetNode);
110
+
111
 
112
  // Compile rules and rulesets
113
+ $nodeRuleCnt = $rulesetNode->rules?count($rulesetNode->rules):0;
114
+ for( $i = 0; $i < $nodeRuleCnt; ){
115
  $rule = $rulesetNode->rules[$i];
116
 
117
+ if( property_exists($rule,'rules') ){
118
  // visit because we are moving them out from being a child
119
+ $rulesets[] = $this->visitObj($rule);
120
+ array_splice($rulesetNode->rules,$i,1);
121
  $nodeRuleCnt--;
122
  continue;
123
  }
124
  $i++;
125
  }
126
 
127
+
128
  // accept the visitor to remove rules and refactor itself
129
  // then we can decide now whether we want it or not
130
+ if( $nodeRuleCnt > 0 ){
131
+ $rulesetNode->accept($this);
132
 
133
+ if( $rulesetNode->rules ){
134
 
135
+ if( count($rulesetNode->rules) > 1 ){
136
  $this->_mergeRules( $rulesetNode->rules );
137
  $this->_removeDuplicateRules( $rulesetNode->rules );
138
  }
139
 
140
  // now decide whether we keep the ruleset
141
+ if( $rulesetNode->paths ){
142
+ //array_unshift($rulesets, $rulesetNode);
143
+ array_splice($rulesets,0,0,array($rulesetNode));
144
  }
145
  }
146
 
147
  }
148
 
149
+
150
+ if( count($rulesets) === 1 ){
151
  return $rulesets[0];
152
  }
153
  return $rulesets;
154
  }
155
 
156
+
157
  /**
158
  * Helper function for visitiRuleset
159
  *
160
  * return array|Less_Tree_Ruleset
161
  */
162
+ private function visitRulesetRoot( $rulesetNode ){
163
  $rulesetNode->accept( $this );
164
+ if( $rulesetNode->firstRoot || $rulesetNode->rules ){
165
  return $rulesetNode;
166
  }
167
  return array();
168
  }
169
 
170
+
171
  /**
172
  * Helper function for visitRuleset()
173
  *
174
  * @return array
175
  */
176
+ private function visitRulesetPaths($rulesetNode){
177
+
178
  $paths = array();
179
+ foreach($rulesetNode->paths as $p){
180
+ if( $p[0]->elements[0]->combinator === ' ' ){
181
  $p[0]->elements[0]->combinator = '';
182
  }
183
 
184
+ foreach($p as $pi){
185
+ if( $pi->getIsReferenced() && $pi->getIsOutput() ){
186
  $paths[] = $p;
187
  break;
188
  }
192
  return $paths;
193
  }
194
 
195
+ protected function _removeDuplicateRules( &$rules ){
196
  // remove duplicates
197
  $ruleCache = array();
198
+ for( $i = count($rules)-1; $i >= 0 ; $i-- ){
199
  $rule = $rules[$i];
200
+ if( $rule instanceof Less_Tree_Rule || $rule instanceof Less_Tree_NameValue ){
201
 
202
+ if( !isset($ruleCache[$rule->name]) ){
203
  $ruleCache[$rule->name] = $rule;
204
+ }else{
205
  $ruleList =& $ruleCache[$rule->name];
206
 
207
+ if( $ruleList instanceof Less_Tree_Rule || $ruleList instanceof Less_Tree_NameValue ){
208
  $ruleList = $ruleCache[$rule->name] = array( $ruleCache[$rule->name]->toCSS() );
209
  }
210
 
211
  $ruleCSS = $rule->toCSS();
212
+ if( array_search($ruleCSS,$ruleList) !== false ){
213
+ array_splice($rules,$i,1);
214
+ }else{
215
  $ruleList[] = $ruleCSS;
216
  }
217
  }
219
  }
220
  }
221
 
222
+ protected function _mergeRules( &$rules ){
223
  $groups = array();
224
 
225
+ //obj($rules);
226
 
227
+ $rules_len = count($rules);
228
+ for( $i = 0; $i < $rules_len; $i++ ){
229
  $rule = $rules[$i];
230
 
231
+ if( ($rule instanceof Less_Tree_Rule) && $rule->merge ){
232
 
233
  $key = $rule->name;
234
+ if( $rule->important ){
235
  $key .= ',!';
236
  }
237
 
238
+ if( !isset($groups[$key]) ){
239
  $groups[$key] = array();
240
+ }else{
241
+ array_splice($rules, $i--, 1);
242
  $rules_len--;
243
  }
244
 
246
  }
247
  }
248
 
 
249
 
250
+ foreach($groups as $parts){
251
+
252
+ if( count($parts) > 1 ){
253
  $rule = $parts[0];
254
  $spacedGroups = array();
255
  $lastSpacedGroup = array();
256
  $parts_mapped = array();
257
+ foreach($parts as $p){
258
+ if( $p->merge === '+' ){
259
+ if( $lastSpacedGroup ){
260
+ $spacedGroups[] = self::toExpression($lastSpacedGroup);
261
  }
262
  $lastSpacedGroup = array();
263
  }
264
  $lastSpacedGroup[] = $p;
265
  }
266
 
267
+ $spacedGroups[] = self::toExpression($lastSpacedGroup);
268
+ $rule->value = self::toValue($spacedGroups);
269
  }
270
  }
271
 
272
  }
273
 
274
+ public static function toExpression($values){
275
  $mapped = array();
276
+ foreach($values as $p){
277
  $mapped[] = $p->value;
278
  }
279
  return new Less_Tree_Expression( $mapped );
280
  }
281
 
282
+ public static function toValue($values){
283
+ //return new Less_Tree_Value($values); ??
284
 
285
  $mapped = array();
286
+ foreach($values as $p){
287
  $mapped[] = $p;
288
  }
289
+ return new Less_Tree_Value($mapped);
290
  }
291
  }
292
+
base/inc/lib/Less/VisitorReplacing.php CHANGED
@@ -6,41 +6,43 @@
6
  * @package Less
7
  * @subpackage visitor
8
  */
9
- class Less_VisitorReplacing extends Less_Visitor {
 
 
10
 
11
- public function visitObj( $node ) {
12
  $funcName = 'visit'.$node->type;
13
- if ( isset( $this->_visitFnCache[$funcName] ) ) {
14
 
15
  $visitDeeper = true;
16
  $node = $this->$funcName( $node, $visitDeeper );
17
 
18
- if ( $node ) {
19
- if ( $visitDeeper && is_object( $node ) ) {
20
- $node->accept( $this );
21
  }
22
 
23
  $funcName = $funcName . "Out";
24
- if ( isset( $this->_visitFnCache[$funcName] ) ) {
25
  $this->$funcName( $node );
26
  }
27
  }
28
 
29
- } else {
30
- $node->accept( $this );
31
  }
32
 
33
  return $node;
34
  }
35
 
36
- public function visitArray( $nodes ) {
 
37
  $newNodes = array();
38
- foreach ( $nodes as $node ) {
39
- $evald = $this->visitObj( $node );
40
- if ( $evald ) {
41
- if ( is_array( $evald ) ) {
42
- self::flatten( $evald, $newNodes );
43
- } else {
44
  $newNodes[] = $evald;
45
  }
46
  }
@@ -48,17 +50,18 @@ class Less_VisitorReplacing extends Less_Visitor {
48
  return $newNodes;
49
  }
50
 
51
- public function flatten( $arr, &$out ) {
52
- foreach ( $arr as $item ) {
53
- if ( !is_array( $item ) ) {
 
54
  $out[] = $item;
55
  continue;
56
  }
57
 
58
- foreach ( $item as $nestedItem ) {
59
- if ( is_array( $nestedItem ) ) {
60
- self::flatten( $nestedItem, $out );
61
- } else {
62
  $out[] = $nestedItem;
63
  }
64
  }
@@ -68,3 +71,5 @@ class Less_VisitorReplacing extends Less_Visitor {
68
  }
69
 
70
  }
 
 
6
  * @package Less
7
  * @subpackage visitor
8
  */
9
+ class Less_VisitorReplacing extends Less_Visitor{
10
+
11
+ public function visitObj( $node ){
12
 
 
13
  $funcName = 'visit'.$node->type;
14
+ if( isset($this->_visitFnCache[$funcName]) ){
15
 
16
  $visitDeeper = true;
17
  $node = $this->$funcName( $node, $visitDeeper );
18
 
19
+ if( $node ){
20
+ if( $visitDeeper && is_object($node) ){
21
+ $node->accept($this);
22
  }
23
 
24
  $funcName = $funcName . "Out";
25
+ if( isset($this->_visitFnCache[$funcName]) ){
26
  $this->$funcName( $node );
27
  }
28
  }
29
 
30
+ }else{
31
+ $node->accept($this);
32
  }
33
 
34
  return $node;
35
  }
36
 
37
+ public function visitArray( $nodes ){
38
+
39
  $newNodes = array();
40
+ foreach($nodes as $node){
41
+ $evald = $this->visitObj($node);
42
+ if( $evald ){
43
+ if( is_array($evald) ){
44
+ self::flatten($evald,$newNodes);
45
+ }else{
46
  $newNodes[] = $evald;
47
  }
48
  }
50
  return $newNodes;
51
  }
52
 
53
+ public function flatten( $arr, &$out ){
54
+
55
+ foreach($arr as $item){
56
+ if( !is_array($item) ){
57
  $out[] = $item;
58
  continue;
59
  }
60
 
61
+ foreach($item as $nestedItem){
62
+ if( is_array($nestedItem) ){
63
+ self::flatten( $nestedItem, $out);
64
+ }else{
65
  $out[] = $nestedItem;
66
  }
67
  }
71
  }
72
 
73
  }
74
+
75
+
readme.txt CHANGED
@@ -1,50 +1,50 @@
1
  === SiteOrigin Widgets Bundle ===
2
- Tags: bundle, widget, button, slider, image, carousel, price table, google maps, tinymce, social links
3
  Requires at least: 4.2
4
  Tested up to: 5.7
5
  Requires PHP: 5.6.20
6
- Stable tag: 1.18.0
7
- Build time: 2021-04-20T17:13:52+02:00
8
  License: GPLv3 or later
9
  Contributors: gpriday, braam-genis, alexgso
10
  Donate link: https://siteorigin.com/downloads/premium/
11
 
12
- The SiteOrigin Widgets Bundle gives you a collection of widgets that you can use and customize. All the widgets are built on our powerful framework, giving you advanced forms, unlimited colours and 1500+ icons.
13
 
14
  == Description ==
15
 
16
- The SiteOrigin Widgets Bundle gives you a collection of widgets that you can use and customize. All the widgets are built on our powerful framework, giving you advanced forms, unlimited colours and 1500+ icons.
17
 
18
- Widgets are great. No matter where youre using them. In a [Page Builder](http://siteorigin.com/page-builder/) page or on your widgetized areas. It's even compatible with other popular page building plugins.
19
 
20
  [vimeo https://vimeo.com/102103379]
21
 
22
- The collection is growing, but heres what we have so far.
23
-
24
- * Google Maps Widget that's going places.
25
- * Button Widget that you’ll love to click.
26
- * Image Widget that’ll let you add images everywhere.
27
- * Call To Action Widget that’ll get your users performing the actions you want.
28
- * Slider Widget that slides images and HTML5 videos.
29
- * Price Table Widget that’ll help you sell more.
30
- * Post Carousel Widget that displays your posts as a carousel.
31
- * Features Widget that lets you display a set of site or service features.
32
- * Video Widget to get your videos out there.
33
- * Headline Widget to get you noticed.
34
- * Social Media Buttons which link to all your social media profiles.
35
- * Accordion Widget to compress your content.
36
- * Contact Form Widget to let people know you care.
37
- * Editor Widget let's you richly edit text anywhere.
38
- * Hero Widget that'll save your site design.
39
- * Icon Widget for when only icons will do.
40
- * Image Grid Widget that'll let you add images everywhere... in a grid.
41
- * Layout Slider Widget lets you build layouts on slides using SiteOrigin Page Builder.
42
- * Masonry Widget to add images in a masonry layout.
43
- * Tabs Widget that'll let you group content into tabbed sections.
44
- * Taxonomy Widget to display the selected taxonomy for the current post.
45
- * Testimonials Widget to show people what your users/customers think of you.
46
-
47
- Once you enable a widget, you'll be able to use it anywhere standard widgets are used. You can manage your widgets by going to Plugins > SiteOrigin Widgets in your WordPress admin.
48
 
49
  == Documentation ==
50
 
@@ -54,9 +54,15 @@ Once you enable a widget, you'll be able to use it anywhere standard widgets are
54
 
55
  We offer free support on the [SiteOrigin support forums](https://siteorigin.com/thread/).
56
 
 
 
 
 
 
 
57
  = Create Custom Widgets =
58
 
59
- The SiteOrigin Widgets Bundle is the perfect platform to build widgets for your theme or plugin. Read more on our [developer docs](https://siteorigin.com/docs/widgets-bundle/).
60
 
61
  == Screenshots ==
62
 
@@ -66,7 +72,10 @@ The SiteOrigin Widgets Bundle is the perfect platform to build widgets for your
66
 
67
  == Changelog ==
68
 
69
- == 1.18.0 - 20 April 2021 ==
 
 
 
70
  * Updated the Google fonts list.
71
  * Updated Font Awesome from `5.14.0` to `5.15.1`.
72
  * Block Editor: Styling fixes including specific fixes for the Twenty Twenty-One theme.
@@ -91,7 +100,7 @@ The SiteOrigin Widgets Bundle is the perfect platform to build widgets for your
91
  * Developer: Add `units` and `default_unit` parameters for the measurement field.
92
  * Developer: Changes for PHP 8.
93
 
94
- == 1.17.11 - 09 December 2020 ==
95
  * Google Maps: Added Map Style button styling.
96
  * Accordion: Submitted contact forms added to Accordion will now open the panel on load.
97
  * Tabs: Prevented the Scroll To setting from affecting the currently selected tab.
@@ -101,10 +110,10 @@ The SiteOrigin Widgets Bundle is the perfect platform to build widgets for your
101
  * Developer: Exposed Google Maps instances. Once set up, maps can be accessed via `sowb.SiteOriginGoogleMapInstances`.
102
  * Developer: Add a Google Maps `maps_loaded` event that triggers after all of the Google Maps have finished being set up.
103
 
104
- == 1.17.10 - 13 November 2020 ==
105
  * TinyMCE: Resolved issue with the Add Media button.
106
 
107
- == 1.17.9 - 12 November 2020 ==
108
  * Block Editor: Resolved widget preview issue.
109
  * Elementor: Minor admin styling improvements.
110
  * Google Maps: Added a new global consent setting `Require consent before loading Maps API` at `Plugins > SiteOrigin Widgets > Google Maps`.
@@ -117,10 +126,10 @@ The SiteOrigin Widgets Bundle is the perfect platform to build widgets for your
117
  * Updated the widget title field to allow HTML in all SiteOrigin widgets.
118
  * Changed to `enqueue` Google Fonts rather than `@import`.
119
 
120
- == 1.17.8 - 28 September 2020 ==
121
  * Image: Prevent image stretching while using Safari
122
 
123
- == 1.17.7 - 10 September 2020 ==
124
  * Hero and Layout Sliders: Disable image preload for images added to frames.
125
  * Resolved a preview issue with widgets that attempt to render sub widgets but don't output them.
126
  * Contact Form: Added a field max-width setting.
@@ -134,21 +143,21 @@ The SiteOrigin Widgets Bundle is the perfect platform to build widgets for your
134
  * IE 11: Prevented images from exceeding the widget container.
135
  * Removed wpColorPickerL10n if WP >= 5.5.
136
 
137
- == 1.17.6 - 17 August 2020 ==
138
  * Hero and Layout Sliders: Disabled lazy loading for images added to the slide content area.
139
  * Slider: Disabled lazy loading in the Block Editor.
140
 
141
- == 1.17.5 - 12 August 2020 ==
142
  * Slider: Disable WordPress 5.5 Lazy Loading.
143
 
144
- == 1.17.4 - 06 August 2020 ==
145
  * Post Carousel: Improved support for multiple carousel widgets on the same page.
146
  * Post Carousel: Improved mobile support.
147
  * Sliders: Added `Autoplay` and `Autoplay Pause on Hover` settings to the Slider, Layout Slider and Hero widgets.
148
  * Features: Allowed shortcodes to be used in the Text field.
149
  * Contact Form: Fix to ensure the correct fields are passed to Akismet.
150
 
151
- == 1.17.3 - 23 July 2020 ==
152
  * Reverted: Features: Prevented icon overlapping text in edge cases.
153
  * Social Media Buttons: Added TikTok.
154
  * Button: Removed `:visited` color targeting.
@@ -158,10 +167,10 @@ The SiteOrigin Widgets Bundle is the perfect platform to build widgets for your
158
  * Carousel: Contextualized variables as needed.
159
  * Carousel: Find the `ItemWidth` as needed rather than predefining it.
160
 
161
- == 1.17.2 - 21 July 2020 ==
162
  * Resolved button Less CSS bug.
163
 
164
- == 1.17.1 - 20 July 2020 ==
165
  * Post Carousel: Prevented all posts from outputting. `10` will be output at a time if `Posts per page` isn't set.
166
  * Post Carousel: Inlined navigation buttons if a title is set.
167
  * Updated the Google Fonts array.
@@ -187,7 +196,7 @@ The SiteOrigin Widgets Bundle is the perfect platform to build widgets for your
187
  * Mobile: Improved admin form field responsive behavior.
188
  * Updated Font Awesome from `5.13.0` to `5.14.0`.
189
 
190
- == 1.17.0 - 22 May 2020 ==
191
  * Simple Masonry: Added a preloader and related settings.
192
  * Post Carousel: Migrated to Slick Carousel.
193
  * Post Carousel: Added RTL support.
@@ -199,7 +208,7 @@ The SiteOrigin Widgets Bundle is the perfect platform to build widgets for your
199
  * Switched to Wikimedia's fork of less.php.
200
  * Added error suppression for external Less library.
201
 
202
- == 1.16.1 - 20 April 2020 ==
203
  * Accordion: When hiding panels, only search the current accordion and not nested accordions.
204
  * Slider Widgets: Removed slide navigation numbers from excerpt output.
205
  * Added support for custom icon styles.
1
  === SiteOrigin Widgets Bundle ===
2
+ Tags: widget, button, slider, hero, google maps, image, carousel, features, icons, price table, tinymce, social icons
3
  Requires at least: 4.2
4
  Tested up to: 5.7
5
  Requires PHP: 5.6.20
6
+ Stable tag: 1.18.1
7
+ Build time: 2021-04-20T20:58:45+02:00
8
  License: GPLv3 or later
9
  Contributors: gpriday, braam-genis, alexgso
10
  Donate link: https://siteorigin.com/downloads/premium/
11
 
12
+ The SiteOrigin Widgets Bundle gives you a collection of widgets that you can use and customize. All the widgets are built on our powerful framework, giving you advanced forms, unlimited colors, and 1500+ icons.
13
 
14
  == Description ==
15
 
16
+ The SiteOrigin Widgets Bundle gives you a collection of widgets that you can use and customize. All the widgets are built on our powerful framework, giving you advanced forms, unlimited colors, and 1500+ icons.
17
 
18
+ Widgets are great, no matter where you're using them. In [Page Builder](https://siteorigin.com/page-builder/), in the Block Editor using the SiteOrigin Layout Block or SiteOrigin Widget Block or in your theme's widget areas. The Widgets Bundle is even compatible with other popular page-building plugins.
19
 
20
  [vimeo https://vimeo.com/102103379]
21
 
22
+ The collection is growing, but here's what we have so far:
23
+
24
+ - **Accordion:** Squeeze a lot of content into a small space.
25
+ - **Button:** A simple button widget with a variety of styling options.
26
+ - **Call To Action:** Get users moving in the right direction.
27
+ - **Contact Form:** A lightweight contact form builder.
28
+ - **Editor:** Rich text editing anywhere.
29
+ - **Features:** Displays a block of features with icons.
30
+ - **Google Maps:** Help your site find its place and give it some direction.
31
+ - **Headline:** A widget to get you noticed.
32
+ - **Hero Image:** More than just a large image. Support for headings, text, buttons, slides, and background videos.
33
+ - **Icon:** For when only icons will do.
34
+ - **Image:** A simple image widget with massive power.
35
+ - **Image Grid:** Display a grid of images. Also useful for displaying client logos.
36
+ - **Image Slider:** A simple, easy-to-use slider with support for image layering and HTML5 videos.
37
+ - **Layout Slider:** Build layouts on slides using SiteOrigin Page Builder.
38
+ - **Post Carousel:** Display your posts as a carousel.
39
+ - **Price Table:** A simple yet powerful interface for building a price table.
40
+ - **Simple Masonry:** A masonry layout for images. Images can link to your posts.
41
+ - **Social Media Buttons:** Customizable buttons which link to your social media profiles.
42
+ - **Tabs:** Group content into tabbed sections.
43
+ - **Taxonomy:** Display the selected taxonomy for the current post.
44
+ - **Testimonials:** Share your product or service testimonials in a variety of different layouts.
45
+ - **Video:** Play your self or externally hosted videos in a customizable player.
46
+
47
+ Once you enable a widget, you'll be able to use it in Page Builder, the Block Editor using the SiteOrigin Layout Block or SiteOrigin Widget Block, or in any theme widget area. You can manage your widgets by going to Plugins > SiteOrigin Widgets in WordPress.
48
 
49
  == Documentation ==
50
 
54
 
55
  We offer free support on the [SiteOrigin support forums](https://siteorigin.com/thread/).
56
 
57
+ == SiteOrigin Premium ==
58
+
59
+ [SiteOrigin Premium](https://siteorigin.com/downloads/premium/) enhances the Widgets Bundle with a vast array of additional features and settings. Take your layouts to the next level.
60
+
61
+ SiteOrigin Premium also includes access to our next-level email support service, perfect for those times when you need fast and effective technical support.
62
+
63
  = Create Custom Widgets =
64
 
65
+ The SiteOrigin Widgets Bundle is the perfect platform to build widgets for your theme or plugin. Read more in our [developer docs](https://siteorigin.com/docs/widgets-bundle/).
66
 
67
  == Screenshots ==
68
 
72
 
73
  == Changelog ==
74
 
75
+ = 1.18.1 - 20 April 2021 =
76
+ * Reverted Less `3.1.0` update to restore compatibility for PHP versions below `7.1`.
77
+
78
+ = 1.18.0 - 20 April 2021 =
79
  * Updated the Google fonts list.
80
  * Updated Font Awesome from `5.14.0` to `5.15.1`.
81
  * Block Editor: Styling fixes including specific fixes for the Twenty Twenty-One theme.
100
  * Developer: Add `units` and `default_unit` parameters for the measurement field.
101
  * Developer: Changes for PHP 8.
102
 
103
+ = 1.17.11 - 09 December 2020 =
104
  * Google Maps: Added Map Style button styling.
105
  * Accordion: Submitted contact forms added to Accordion will now open the panel on load.
106
  * Tabs: Prevented the Scroll To setting from affecting the currently selected tab.
110
  * Developer: Exposed Google Maps instances. Once set up, maps can be accessed via `sowb.SiteOriginGoogleMapInstances`.
111
  * Developer: Add a Google Maps `maps_loaded` event that triggers after all of the Google Maps have finished being set up.
112
 
113
+ = 1.17.10 - 13 November 2020 =
114
  * TinyMCE: Resolved issue with the Add Media button.
115
 
116
+ = 1.17.9 - 12 November 2020 =
117
  * Block Editor: Resolved widget preview issue.
118
  * Elementor: Minor admin styling improvements.
119
  * Google Maps: Added a new global consent setting `Require consent before loading Maps API` at `Plugins > SiteOrigin Widgets > Google Maps`.
126
  * Updated the widget title field to allow HTML in all SiteOrigin widgets.
127
  * Changed to `enqueue` Google Fonts rather than `@import`.
128
 
129
+ = 1.17.8 - 28 September 2020 =
130
  * Image: Prevent image stretching while using Safari
131
 
132
+ = 1.17.7 - 10 September 2020 =
133
  * Hero and Layout Sliders: Disable image preload for images added to frames.
134
  * Resolved a preview issue with widgets that attempt to render sub widgets but don't output them.
135
  * Contact Form: Added a field max-width setting.
143
  * IE 11: Prevented images from exceeding the widget container.
144
  * Removed wpColorPickerL10n if WP >= 5.5.
145
 
146
+ = 1.17.6 - 17 August 2020 =
147
  * Hero and Layout Sliders: Disabled lazy loading for images added to the slide content area.
148
  * Slider: Disabled lazy loading in the Block Editor.
149
 
150
+ = 1.17.5 - 12 August 2020 =
151
  * Slider: Disable WordPress 5.5 Lazy Loading.
152
 
153
+ = 1.17.4 - 06 August 2020 =
154
  * Post Carousel: Improved support for multiple carousel widgets on the same page.
155
  * Post Carousel: Improved mobile support.
156
  * Sliders: Added `Autoplay` and `Autoplay Pause on Hover` settings to the Slider, Layout Slider and Hero widgets.
157
  * Features: Allowed shortcodes to be used in the Text field.
158
  * Contact Form: Fix to ensure the correct fields are passed to Akismet.
159
 
160
+ = 1.17.3 - 23 July 2020 =
161
  * Reverted: Features: Prevented icon overlapping text in edge cases.
162
  * Social Media Buttons: Added TikTok.
163
  * Button: Removed `:visited` color targeting.
167
  * Carousel: Contextualized variables as needed.
168
  * Carousel: Find the `ItemWidth` as needed rather than predefining it.
169
 
170
+ = 1.17.2 - 21 July 2020 =
171
  * Resolved button Less CSS bug.
172
 
173
+ = 1.17.1 - 20 July 2020 =
174
  * Post Carousel: Prevented all posts from outputting. `10` will be output at a time if `Posts per page` isn't set.
175
  * Post Carousel: Inlined navigation buttons if a title is set.
176
  * Updated the Google Fonts array.
196
  * Mobile: Improved admin form field responsive behavior.
197
  * Updated Font Awesome from `5.13.0` to `5.14.0`.
198
 
199
+ = 1.17.0 - 22 May 2020 =
200
  * Simple Masonry: Added a preloader and related settings.
201
  * Post Carousel: Migrated to Slick Carousel.
202
  * Post Carousel: Added RTL support.
208
  * Switched to Wikimedia's fork of less.php.
209
  * Added error suppression for external Less library.
210
 
211
+ = 1.16.1 - 20 April 2020 =
212
  * Accordion: When hiding panels, only search the current accordion and not nested accordions.
213
  * Slider Widgets: Removed slide navigation numbers from excerpt output.
214
  * Added support for custom icon styles.
so-widgets-bundle.php CHANGED
@@ -2,7 +2,7 @@
2
  /*
3
  Plugin Name: SiteOrigin Widgets Bundle
4
  Description: A collection of all widgets, neatly bundled into a single plugin. It's also a framework to code your own widgets on top of.
5
- Version: 1.18.0
6
  Text Domain: so-widgets-bundle
7
  Domain Path: /lang
8
  Author: SiteOrigin
@@ -12,7 +12,7 @@ License: GPL3
12
  License URI: https://www.gnu.org/licenses/gpl-3.0.txt
13
  */
14
 
15
- define('SOW_BUNDLE_VERSION', '1.18.0');
16
  define('SOW_BUNDLE_BASE_FILE', __FILE__);
17
 
18
  // Allow JS suffix to be pre-set
2
  /*
3
  Plugin Name: SiteOrigin Widgets Bundle
4
  Description: A collection of all widgets, neatly bundled into a single plugin. It's also a framework to code your own widgets on top of.
5
+ Version: 1.18.1
6
  Text Domain: so-widgets-bundle
7
  Domain Path: /lang
8
  Author: SiteOrigin
12
  License URI: https://www.gnu.org/licenses/gpl-3.0.txt
13
  */
14
 
15
+ define('SOW_BUNDLE_VERSION', '1.18.1');
16
  define('SOW_BUNDLE_BASE_FILE', __FILE__);
17
 
18
  // Allow JS suffix to be pre-set