Test Failed
Push — main ( 29a1e7...86a642 )
by Rafael
61:24
created
htdocs/contrat/contact.php 1 patch
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -54,7 +54,7 @@
 block discarded – undo
54 54
 // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context
55 55
 $hookmanager->initHooks(array('contractcontactcard', 'globalcard'));
56 56
 
57
-$permissiontoadd   = $user->hasRight('contrat', 'creer');     //  Used by the include of actions_addupdatedelete.inc.php and actions_lineupdown.inc.php
57
+$permissiontoadd = $user->hasRight('contrat', 'creer'); //  Used by the include of actions_addupdatedelete.inc.php and actions_lineupdown.inc.php
58 58
 
59 59
 $result = restrictedArea($user, 'contrat', $object->id);
60 60
 
Please login to merge, or discard this patch.
htdocs/admin/tools/listevents.php 1 patch
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -44,7 +44,7 @@
 block discarded – undo
44 44
 }
45 45
 
46 46
 // Load translation files required by the page
47
-$langs->loadLangs(array("companies", "admin", "users", "other","withdrawals"));
47
+$langs->loadLangs(array("companies", "admin", "users", "other", "withdrawals"));
48 48
 
49 49
 // Load variable for pagination
50 50
 $limit = GETPOSTINT('limit') ? GETPOSTINT('limit') : $conf->liste_limit;
Please login to merge, or discard this patch.
htdocs/core/class/lessc.class.php 2 patches
Indentation   +4330 added lines, -4330 removed lines patch added patch discarded remove patch
@@ -40,4331 +40,4331 @@  discard block
 block discarded – undo
40 40
 class Lessc
41 41
 {
42 42
 
43
-	public static $VERSION = "v0.8.0";
44
-
45
-	public static $TRUE = array("keyword", "true");
46
-	public static $FALSE = array("keyword", "false");
47
-
48
-	protected $libFunctions = array();
49
-	protected $registeredVars = array();
50
-	protected $preserveComments = false;
51
-
52
-	public $vPrefix = '@'; // prefix of abstract properties
53
-	public $mPrefix = '$'; // prefix of abstract blocks
54
-	public $parentSelector = '&';
55
-
56
-	public $importDisabled = false;
57
-	public $importDir = '';
58
-
59
-	public $scope;
60
-	public $formatter;
61
-	public $formatterName;
62
-	public $parser;
63
-	public $_parseFile;
64
-	public $env;
65
-	public $count;
66
-
67
-	protected $numberPrecision = null;
68
-
69
-	protected $allParsedFiles = array();
70
-
71
-	// set to the parser that generated the current line when compiling
72
-	// so we know how to create error messages
73
-	protected $sourceParser = null;
74
-	protected $sourceLoc = null;
75
-
76
-	protected static $nextImportId = 0; // uniquely identify imports
77
-
78
-	// attempts to find the path of an import url, returns null for css files
79
-	protected function findImport($url)
80
-	{
81
-		foreach ((array) $this->importDir as $dir) {
82
-			$full = $dir.(substr($dir, -1) != '/' ? '/' : '').$url;
83
-			if ($this->fileExists($file = $full.'.less') || $this->fileExists($file = $full)) {
84
-				return $file;
85
-			}
86
-		}
87
-
88
-		return null;
89
-	}
90
-
91
-	/**
92
-	 * fileExists
93
-	 *
94
-	 * @param       string  $name   Filename
95
-	 * @return      boolean
96
-	 */
97
-	protected function fileExists($name)
98
-	{
99
-		return is_file($name);
100
-	}
101
-
102
-	public static function compressList($items, $delim)
103
-	{
104
-		if (!isset($items[1]) && isset($items[0])) {
105
-			return $items[0];
106
-		} else {
107
-			return array('list', $delim, $items);
108
-		}
109
-	}
110
-
111
-	public static function preg_quote($what)
112
-	{
113
-		return preg_quote($what, '/');
114
-	}
115
-
116
-	protected function tryImport($importPath, $parentBlock, $out)
117
-	{
118
-		if ($importPath[0] == "function" && $importPath[1] == "url") {
119
-			$importPath = $this->flattenList($importPath[2]);
120
-		}
121
-
122
-		$str = $this->coerceString($importPath);
123
-		if ($str === null) {
124
-			return false;
125
-		}
126
-
127
-		$url = $this->compileValue($this->lib_e($str));
128
-
129
-		// don't import if it ends in css
130
-		if (substr_compare($url, '.css', -4, 4) === 0) {
131
-			return false;
132
-		}
133
-
134
-		$realPath = $this->findImport($url);
135
-
136
-		if ($realPath === null) {
137
-			return false;
138
-		}
139
-
140
-		if ($this->importDisabled) {
141
-			return array(false, "/* import disabled */");
142
-		}
143
-
144
-		if (isset($this->allParsedFiles[realpath($realPath)])) {
145
-			return array(false, null);
146
-		}
147
-
148
-		$this->addParsedFile($realPath);
149
-		$parser = $this->makeParser($realPath);
150
-		$root = $parser->parse(file_get_contents($realPath));
151
-
152
-		// set the parents of all the block props
153
-		foreach ($root->props as $prop) {
154
-			if ($prop[0] == "block") {
155
-				$prop[1]->parent = $parentBlock;
156
-			}
157
-		}
158
-
159
-		// copy mixins into scope, set their parents
160
-		// bring blocks from import into current block
161
-		// TODO: need to mark the source parser these came from this file
162
-		foreach ($root->children as $childName => $child) {
163
-			if (isset($parentBlock->children[$childName])) {
164
-				$parentBlock->children[$childName] = array_merge(
165
-					$parentBlock->children[$childName],
166
-					$child
167
-				);
168
-			} else {
169
-				$parentBlock->children[$childName] = $child;
170
-			}
171
-		}
172
-
173
-		$pi = pathinfo($realPath);
174
-		$dir = $pi["dirname"];
175
-
176
-		list($top, $bottom) = $this->sortProps($root->props, true);
177
-		$this->compileImportedProps($top, $parentBlock, $out, $parser, $dir);
178
-
179
-		return array(true, $bottom, $parser, $dir);
180
-	}
181
-
182
-	protected function compileImportedProps($props, $block, $out, $sourceParser, $importDir)
183
-	{
184
-		$oldSourceParser = $this->sourceParser;
185
-
186
-		$oldImport = $this->importDir;
187
-
188
-		// TODO: this is because the importDir api is stupid
189
-		$this->importDir = (array) $this->importDir;
190
-		array_unshift($this->importDir, $importDir);
191
-
192
-		foreach ($props as $prop) {
193
-			$this->compileProp($prop, $block, $out);
194
-		}
195
-
196
-		$this->importDir = $oldImport;
197
-		$this->sourceParser = $oldSourceParser;
198
-	}
199
-
200
-	/**
201
-	 * Recursively compiles a block.
202
-	 *
203
-	 * A block is analogous to a CSS block in most cases. A single LESS document
204
-	 * is encapsulated in a block when parsed, but it does not have parent tags
205
-	 * so all of it's children appear on the root level when compiled.
206
-	 *
207
-	 * Blocks are made up of props and children.
208
-	 *
209
-	 * Props are property instructions, array tuples which describe an action
210
-	 * to be taken, eg. write a property, set a variable, mixin a block.
211
-	 *
212
-	 * The children of a block are just all the blocks that are defined within.
213
-	 * This is used to look up mixins when performing a mixin.
214
-	 *
215
-	 * Compiling the block involves pushing a fresh environment on the stack,
216
-	 * and iterating through the props, compiling each one.
217
-	 *
218
-	 * See Lessc::compileProp()
219
-	 *
220
-	 */
221
-	protected function compileBlock($block)
222
-	{
223
-		switch ($block->type) {
224
-			case "root":
225
-				$this->compileRoot($block);
226
-				break;
227
-			case null:
228
-				$this->compileCSSBlock($block);
229
-				break;
230
-			case "media":
231
-				$this->compileMedia($block);
232
-				break;
233
-			case "directive":
234
-				$name = "@".$block->name;
235
-				if (!empty($block->value)) {
236
-					$name .= " ".$this->compileValue($this->reduce($block->value));
237
-				}
238
-
239
-				$this->compileNestedBlock($block, array($name));
240
-				break;
241
-			default:
242
-				$this->throwError("unknown block type: $block->type\n");
243
-		}
244
-	}
245
-
246
-	protected function compileCSSBlock($block)
247
-	{
248
-		$env = $this->pushEnv();
249
-
250
-		$selectors = $this->compileSelectors($block->tags);
251
-		$env->selectors = $this->multiplySelectors($selectors);
252
-		$out = $this->makeOutputBlock(null, $env->selectors);
253
-
254
-		$this->scope->children[] = $out;
255
-		$this->compileProps($block, $out);
256
-
257
-		$block->scope = $env; // mixins carry scope with them!
258
-		$this->popEnv();
259
-	}
260
-
261
-	protected function compileMedia($media)
262
-	{
263
-		$env = $this->pushEnv($media);
264
-		$parentScope = $this->mediaParent($this->scope);
265
-
266
-		$query = $this->compileMediaQuery($this->multiplyMedia($env));
267
-
268
-		$this->scope = $this->makeOutputBlock($media->type, array($query));
269
-		$parentScope->children[] = $this->scope;
270
-
271
-		$this->compileProps($media, $this->scope);
272
-
273
-		if (count($this->scope->lines) > 0) {
274
-			$orphanSelelectors = $this->findClosestSelectors();
275
-			if (!is_null($orphanSelelectors)) {
276
-				$orphan = $this->makeOutputBlock(null, $orphanSelelectors);
277
-				$orphan->lines = $this->scope->lines;
278
-				array_unshift($this->scope->children, $orphan);
279
-				$this->scope->lines = array();
280
-			}
281
-		}
282
-
283
-		$this->scope = $this->scope->parent;
284
-		$this->popEnv();
285
-	}
286
-
287
-	protected function mediaParent($scope)
288
-	{
289
-		while (!empty($scope->parent)) {
290
-			if (!empty($scope->type) && $scope->type != "media") {
291
-				break;
292
-			}
293
-			$scope = $scope->parent;
294
-		}
295
-
296
-		return $scope;
297
-	}
298
-
299
-	protected function compileNestedBlock($block, $selectors)
300
-	{
301
-		$this->pushEnv($block);
302
-		$this->scope = $this->makeOutputBlock($block->type, $selectors);
303
-		$this->scope->parent->children[] = $this->scope;
304
-
305
-		$this->compileProps($block, $this->scope);
306
-
307
-		$this->scope = $this->scope->parent;
308
-		$this->popEnv();
309
-	}
310
-
311
-	protected function compileRoot($root)
312
-	{
313
-		$this->pushEnv();
314
-		$this->scope = $this->makeOutputBlock($root->type);
315
-		$this->compileProps($root, $this->scope);
316
-		$this->popEnv();
317
-	}
318
-
319
-	protected function compileProps($block, $out)
320
-	{
321
-		foreach ($this->sortProps($block->props) as $prop) {
322
-			$this->compileProp($prop, $block, $out);
323
-		}
324
-		$out->lines = $this->deduplicate($out->lines);
325
-	}
326
-
327
-	/**
328
-	 * Deduplicate lines in a block. Comments are not deduplicated. If a
329
-	 * duplicate rule is detected, the comments immediately preceding each
330
-	 * occurence are consolidated.
331
-	 */
332
-	protected function deduplicate($lines)
333
-	{
334
-		$unique = array();
335
-		$comments = array();
336
-
337
-		foreach ($lines as $line) {
338
-			if (strpos($line, '/*') === 0) {
339
-				$comments[] = $line;
340
-				continue;
341
-			}
342
-			if (!in_array($line, $unique)) {
343
-				$unique[] = $line;
344
-			}
345
-			array_splice($unique, array_search($line, $unique), 0, $comments);
346
-			$comments = array();
347
-		}
348
-		return array_merge($unique, $comments);
349
-	}
350
-
351
-	protected function sortProps($props, $split = false)
352
-	{
353
-		$vars = array();
354
-		$imports = array();
355
-		$other = array();
356
-		$stack = array();
357
-
358
-		foreach ($props as $prop) {
359
-			switch ($prop[0]) {
360
-				case "comment":
361
-					$stack[] = $prop;
362
-					break;
363
-				case "assign":
364
-					$stack[] = $prop;
365
-					if (isset($prop[1][0]) && $prop[1][0] == $this->vPrefix) {
366
-						$vars = array_merge($vars, $stack);
367
-					} else {
368
-						$other = array_merge($other, $stack);
369
-					}
370
-					$stack = array();
371
-					break;
372
-				case "import":
373
-					$id = self::$nextImportId++;
374
-					$prop[] = $id;
375
-					$stack[] = $prop;
376
-					$imports = array_merge($imports, $stack);
377
-					$other[] = array("import_mixin", $id);
378
-					$stack = array();
379
-					break;
380
-				default:
381
-					$stack[] = $prop;
382
-					$other = array_merge($other, $stack);
383
-					$stack = array();
384
-					break;
385
-			}
386
-		}
387
-		$other = array_merge($other, $stack);
388
-
389
-		if ($split) {
390
-			return array(array_merge($imports, $vars), $other);
391
-		} else {
392
-			return array_merge($imports, $vars, $other);
393
-		}
394
-	}
395
-
396
-	protected function compileMediaQuery($queries)
397
-	{
398
-		$compiledQueries = array();
399
-		foreach ($queries as $query) {
400
-			$parts = array();
401
-			foreach ($query as $q) {
402
-				switch ($q[0]) {
403
-					case "mediaType":
404
-						$parts[] = implode(" ", array_slice($q, 1));
405
-						break;
406
-					case "mediaExp":
407
-						if (isset($q[2])) {
408
-							$parts[] = "($q[1]: ".
409
-								$this->compileValue($this->reduce($q[2])).")";
410
-						} else {
411
-							$parts[] = "($q[1])";
412
-						}
413
-						break;
414
-					case "variable":
415
-						$parts[] = $this->compileValue($this->reduce($q));
416
-						break;
417
-				}
418
-			}
419
-
420
-			if (count($parts) > 0) {
421
-				$compiledQueries[] = implode(" and ", $parts);
422
-			}
423
-		}
424
-
425
-		$out = "@media";
426
-		if (!empty($parts)) {
427
-			$out .= " ".
428
-				implode($this->formatter->selectorSeparator, $compiledQueries);
429
-		}
430
-		return $out;
431
-	}
432
-
433
-	protected function multiplyMedia($env, $childQueries = null)
434
-	{
435
-		if (is_null($env) ||
436
-			!empty($env->block->type) && $env->block->type != "media"
437
-		) {
438
-			return $childQueries;
439
-		}
440
-
441
-		// plain old block, skip
442
-		if (empty($env->block->type)) {
443
-			return $this->multiplyMedia($env->parent, $childQueries);
444
-		}
445
-
446
-		$out = array();
447
-		$queries = $env->block->queries;
448
-		if (is_null($childQueries)) {
449
-			$out = $queries;
450
-		} else {
451
-			foreach ($queries as $parent) {
452
-				foreach ($childQueries as $child) {
453
-					$out[] = array_merge($parent, $child);
454
-				}
455
-			}
456
-		}
457
-
458
-		return $this->multiplyMedia($env->parent, $out);
459
-	}
460
-
461
-	protected function expandParentSelectors(&$tag, $replace)
462
-	{
463
-		$parts = explode("$&$", $tag);
464
-		$count = 0;
465
-		foreach ($parts as &$part) {
466
-			$c = 0;
467
-			$part = str_replace($this->parentSelector, $replace, $part, $c);
468
-			$count += $c;
469
-		}
470
-		$tag = implode($this->parentSelector, $parts);
471
-		return $count;
472
-	}
473
-
474
-	protected function findClosestSelectors()
475
-	{
476
-		$env = $this->env;
477
-		$selectors = null;
478
-		while ($env !== null) {
479
-			if (isset($env->selectors)) {
480
-				$selectors = $env->selectors;
481
-				break;
482
-			}
483
-			$env = $env->parent;
484
-		}
485
-
486
-		return $selectors;
487
-	}
488
-
489
-
490
-	// multiply $selectors against the nearest selectors in env
491
-	protected function multiplySelectors($selectors)
492
-	{
493
-		// find parent selectors
494
-
495
-		$parentSelectors = $this->findClosestSelectors();
496
-		if (is_null($parentSelectors)) {
497
-			// kill parent reference in top level selector
498
-			foreach ($selectors as &$s) {
499
-				$this->expandParentSelectors($s, "");
500
-			}
501
-
502
-			return $selectors;
503
-		}
504
-
505
-		$out = array();
506
-		foreach ($parentSelectors as $parent) {
507
-			foreach ($selectors as $child) {
508
-				$count = $this->expandParentSelectors($child, $parent);
509
-
510
-				// don't prepend the parent tag if & was used
511
-				if ($count > 0) {
512
-					$out[] = trim($child);
513
-				} else {
514
-					$out[] = trim($parent.' '.$child);
515
-				}
516
-			}
517
-		}
518
-
519
-		return $out;
520
-	}
521
-
522
-	// reduces selector expressions
523
-	protected function compileSelectors($selectors)
524
-	{
525
-		$out = array();
526
-
527
-		foreach ($selectors as $s) {
528
-			if (is_array($s)) {
529
-				list(, $value) = $s;
530
-				$out[] = trim($this->compileValue($this->reduce($value)));
531
-			} else {
532
-				$out[] = $s;
533
-			}
534
-		}
535
-
536
-		return $out;
537
-	}
538
-
539
-	protected function eq($left, $right)
540
-	{
541
-		return $left == $right;
542
-	}
543
-
544
-	protected function patternMatch($block, $orderedArgs, $keywordArgs)
545
-	{
546
-		// match the guards if it has them
547
-		// any one of the groups must have all its guards pass for a match
548
-		if (!empty($block->guards)) {
549
-			$groupPassed = false;
550
-			foreach ($block->guards as $guardGroup) {
551
-				foreach ($guardGroup as $guard) {
552
-					$this->pushEnv();
553
-					$this->zipSetArgs($block->args, $orderedArgs, $keywordArgs);
554
-
555
-					$negate = false;
556
-					if ($guard[0] == "negate") {
557
-						$guard = $guard[1];
558
-						$negate = true;
559
-					}
560
-
561
-					$passed = $this->reduce($guard) == self::$TRUE;
562
-					if ($negate) {
563
-						$passed = !$passed;
564
-					}
565
-
566
-					$this->popEnv();
567
-
568
-					if ($passed) {
569
-						$groupPassed = true;
570
-					} else {
571
-						$groupPassed = false;
572
-						break;
573
-					}
574
-				}
575
-
576
-				if ($groupPassed) {
577
-					break;
578
-				}
579
-			}
580
-
581
-			if (!$groupPassed) {
582
-				return false;
583
-			}
584
-		}
585
-
586
-		if (empty($block->args)) {
587
-			return $block->isVararg || empty($orderedArgs) && empty($keywordArgs);
588
-		}
589
-
590
-		$remainingArgs = $block->args;
591
-		if ($keywordArgs) {
592
-			$remainingArgs = array();
593
-			foreach ($block->args as $arg) {
594
-				if ($arg[0] == "arg" && isset($keywordArgs[$arg[1]])) {
595
-					continue;
596
-				}
597
-
598
-				$remainingArgs[] = $arg;
599
-			}
600
-		}
601
-
602
-		$i = -1; // no args
603
-		// try to match by arity or by argument literal
604
-		foreach ($remainingArgs as $i => $arg) {
605
-			switch ($arg[0]) {
606
-				case "lit":
607
-					if (empty($orderedArgs[$i]) || !$this->eq($arg[1], $orderedArgs[$i])) {
608
-						return false;
609
-					}
610
-					break;
611
-				case "arg":
612
-					// no arg and no default value
613
-					if (!isset($orderedArgs[$i]) && !isset($arg[2])) {
614
-						return false;
615
-					}
616
-					break;
617
-				case "rest":
618
-					$i--; // rest can be empty
619
-					break 2;
620
-			}
621
-		}
622
-
623
-		if ($block->isVararg) {
624
-			return true; // not having enough is handled above
625
-		} else {
626
-			$numMatched = $i + 1;
627
-			// greater than because default values always match
628
-			return $numMatched >= count($orderedArgs);
629
-		}
630
-	}
631
-
632
-	protected function patternMatchAll($blocks, $orderedArgs, $keywordArgs, $skip = array())
633
-	{
634
-		$matches = null;
635
-		foreach ($blocks as $block) {
636
-			// skip seen blocks that don't have arguments
637
-			if (isset($skip[$block->id]) && !isset($block->args)) {
638
-				continue;
639
-			}
640
-
641
-			if ($this->patternMatch($block, $orderedArgs, $keywordArgs)) {
642
-				$matches[] = $block;
643
-			}
644
-		}
645
-
646
-		return $matches;
647
-	}
648
-
649
-	// attempt to find blocks matched by path and args
650
-	protected function findBlocks($searchIn, $path, $orderedArgs, $keywordArgs, $seen = array())
651
-	{
652
-		if ($searchIn == null) {
653
-			return null;
654
-		}
655
-		if (isset($seen[$searchIn->id])) {
656
-			return null;
657
-		}
658
-		$seen[$searchIn->id] = true;
659
-
660
-		$name = $path[0];
661
-
662
-		if (isset($searchIn->children[$name])) {
663
-			$blocks = $searchIn->children[$name];
664
-			if (count($path) == 1) {
665
-				$matches = $this->patternMatchAll($blocks, $orderedArgs, $keywordArgs, $seen);
666
-				if (!empty($matches)) {
667
-					// This will return all blocks that match in the closest
668
-					// scope that has any matching block, like lessjs
669
-					return $matches;
670
-				}
671
-			} else {
672
-				$matches = array();
673
-				foreach ($blocks as $subBlock) {
674
-					$subMatches = $this->findBlocks(
675
-						$subBlock,
676
-						array_slice($path, 1),
677
-						$orderedArgs,
678
-						$keywordArgs,
679
-						$seen
680
-					);
681
-
682
-					if (!is_null($subMatches)) {
683
-						foreach ($subMatches as $sm) {
684
-							$matches[] = $sm;
685
-						}
686
-					}
687
-				}
688
-
689
-				return count($matches) > 0 ? $matches : null;
690
-			}
691
-		}
692
-		if ($searchIn->parent === $searchIn) {
693
-			return null;
694
-		}
695
-		return $this->findBlocks($searchIn->parent, $path, $orderedArgs, $keywordArgs, $seen);
696
-	}
697
-
698
-	// sets all argument names in $args to either the default value
699
-	// or the one passed in through $values
700
-	protected function zipSetArgs($args, $orderedValues, $keywordValues)
701
-	{
702
-		$assignedValues = array();
703
-
704
-		$i = 0;
705
-		foreach ($args as $a) {
706
-			if ($a[0] == "arg") {
707
-				if (isset($keywordValues[$a[1]])) {
708
-					// has keyword arg
709
-					$value = $keywordValues[$a[1]];
710
-				} elseif (isset($orderedValues[$i])) {
711
-					// has ordered arg
712
-					$value = $orderedValues[$i];
713
-					$i++;
714
-				} elseif (isset($a[2])) {
715
-					// has default value
716
-					$value = $a[2];
717
-				} else {
718
-					$value = null; // :(
719
-					$this->throwError("Failed to assign arg ".$a[1]); // This ends function by throwing an exception
720
-				}
721
-
722
-				$value = $this->reduce($value);
723
-				$this->set($a[1], $value);
724
-				$assignedValues[] = $value;
725
-			} else {
726
-				// a lit
727
-				$i++;
728
-			}
729
-		}
730
-
731
-		// check for a rest
732
-		$last = end($args);
733
-		if ($last && $last[0] == "rest") {
734
-			$rest = array_slice($orderedValues, count($args) - 1);
735
-			$this->set($last[1], $this->reduce(array("list", " ", $rest)));
736
-		}
737
-
738
-		// wow is this the only true use of PHP's + operator for arrays?
739
-		$this->env->arguments = $assignedValues + $orderedValues;
740
-	}
741
-
742
-	// compile a prop and update $lines or $blocks appropriately
743
-	protected function compileProp($prop, $block, $out)
744
-	{
745
-		// set error position context
746
-		$this->sourceLoc = isset($prop[-1]) ? $prop[-1] : -1;
747
-
748
-		switch ($prop[0]) {
749
-			case 'assign':
750
-				list(, $name, $value) = $prop;
751
-				if ($name[0] == $this->vPrefix) {
752
-					$this->set($name, $value);
753
-				} else {
754
-					$out->lines[] = $this->formatter->property(
755
-						$name,
756
-						$this->compileValue($this->reduce($value))
757
-					);
758
-				}
759
-				break;
760
-			case 'block':
761
-				list(, $child) = $prop;
762
-				$this->compileBlock($child);
763
-				break;
764
-			case 'mixin':
765
-				list(, $path, $args, $suffix) = $prop;
766
-
767
-				$orderedArgs = array();
768
-				$keywordArgs = array();
769
-				foreach ((array) $args as $arg) {
770
-					$argval = null;
771
-					switch ($arg[0]) {
772
-						case "arg":
773
-							if (!isset($arg[2])) {
774
-								$orderedArgs[] = $this->reduce(array("variable", $arg[1]));
775
-							} else {
776
-								$keywordArgs[$arg[1]] = $this->reduce($arg[2]);
777
-							}
778
-							break;
779
-
780
-						case "lit":
781
-							$orderedArgs[] = $this->reduce($arg[1]);
782
-							break;
783
-						default:
784
-							$this->throwError("Unknown arg type: ".$arg[0]);
785
-					}
786
-				}
787
-
788
-				$mixins = $this->findBlocks($block, $path, $orderedArgs, $keywordArgs);
789
-
790
-				if ($mixins === null) {
791
-					$this->throwError("{$prop[1][0]} is undefined");
792
-				}
793
-
794
-				foreach ($mixins as $mixin) {
795
-					if ($mixin === $block && !$orderedArgs) {
796
-						continue;
797
-					}
798
-
799
-					$haveScope = false;
800
-					if (isset($mixin->parent->scope)) {
801
-						$haveScope = true;
802
-						$mixinParentEnv = $this->pushEnv();
803
-						$mixinParentEnv->storeParent = $mixin->parent->scope;
804
-					}
805
-
806
-					$haveArgs = false;
807
-					if (isset($mixin->args)) {
808
-						$haveArgs = true;
809
-						$this->pushEnv();
810
-						$this->zipSetArgs($mixin->args, $orderedArgs, $keywordArgs);
811
-					}
812
-
813
-					$oldParent = $mixin->parent;
814
-					if ($mixin != $block) {
815
-						$mixin->parent = $block;
816
-					}
817
-
818
-					foreach ($this->sortProps($mixin->props) as $subProp) {
819
-						if ($suffix !== null &&
820
-							$subProp[0] == "assign" &&
821
-							is_string($subProp[1]) &&
822
-							$subProp[1][0] != $this->vPrefix
823
-						) {
824
-							$subProp[2] = array(
825
-								'list', ' ',
826
-								array($subProp[2], array('keyword', $suffix))
827
-							);
828
-						}
829
-
830
-						$this->compileProp($subProp, $mixin, $out);
831
-					}
832
-
833
-					$mixin->parent = $oldParent;
834
-
835
-					if ($haveArgs) {
836
-						$this->popEnv();
837
-					}
838
-					if ($haveScope) {
839
-						$this->popEnv();
840
-					}
841
-				}
842
-
843
-				break;
844
-			case 'raw':
845
-				$out->lines[] = $prop[1];
846
-				break;
847
-			case "directive":
848
-				list(, $name, $value) = $prop;
849
-				$out->lines[] = "@$name ".$this->compileValue($this->reduce($value)).';';
850
-				break;
851
-			case "comment":
852
-				$out->lines[] = $prop[1];
853
-				break;
854
-			case "import":
855
-				list(, $importPath, $importId) = $prop;
856
-				$importPath = $this->reduce($importPath);
857
-
858
-				if (!isset($this->env->imports)) {
859
-					$this->env->imports = array();
860
-				}
861
-
862
-				$result = $this->tryImport($importPath, $block, $out);
863
-
864
-				$this->env->imports[$importId] = $result === false ?
865
-					array(false, "@import ".$this->compileValue($importPath).";") : $result;
866
-
867
-				break;
868
-			case "import_mixin":
869
-				list(, $importId) = $prop;
870
-				$import = $this->env->imports[$importId];
871
-				if ($import[0] === false) {
872
-					if (isset($import[1])) {
873
-						$out->lines[] = $import[1];
874
-					}
875
-				} else {
876
-					list(, $bottom, $parser, $importDir) = $import;
877
-					$this->compileImportedProps($bottom, $block, $out, $parser, $importDir);
878
-				}
879
-
880
-				break;
881
-			default:
882
-				$this->throwError("unknown op: {$prop[0]}\n");
883
-		}
884
-	}
885
-
886
-
887
-	/**
888
-	 * Compiles a primitive value into a CSS property value.
889
-	 *
890
-	 * Values in lessphp are typed by being wrapped in arrays, their format is
891
-	 * typically:
892
-	 *
893
-	 *     array(type, contents [, additional_contents]*)
894
-	 *
895
-	 * The input is expected to be reduced. This function will not work on
896
-	 * things like expressions and variables.
897
-	 */
898
-	public function compileValue($value)
899
-	{
900
-		switch ($value[0]) {
901
-			case 'list':
902
-				// [1] - delimiter
903
-				// [2] - array of values
904
-				return implode($value[1], array_map(array($this, 'compileValue'), $value[2]));
905
-			case 'raw_color':
906
-				if (!empty($this->formatter->compressColors)) {
907
-					return $this->compileValue($this->coerceColor($value));
908
-				}
909
-				return $value[1];
910
-			case 'keyword':
911
-				// [1] - the keyword
912
-				return $value[1];
913
-			case 'number':
914
-				list(, $num, $unit) = $value;
915
-				// [1] - the number
916
-				// [2] - the unit
917
-				if ($this->numberPrecision !== null) {
918
-					$num = round($num, $this->numberPrecision);
919
-				}
920
-				return $num.$unit;
921
-			case 'string':
922
-				// [1] - contents of string (includes quotes)
923
-				list(, $delim, $content) = $value;
924
-				foreach ($content as &$part) {
925
-					if (is_array($part)) {
926
-						$part = $this->compileValue($part);
927
-					}
928
-				}
929
-				return $delim.implode($content).$delim;
930
-			case 'color':
931
-				// [1] - red component (either number or a %)
932
-				// [2] - green component
933
-				// [3] - blue component
934
-				// [4] - optional alpha component
935
-				list(, $r, $g, $b) = $value;
936
-				$r = round($r);
937
-				$g = round($g);
938
-				$b = round($b);
939
-
940
-				if (count($value) == 5 && $value[4] != 1) { // rgba
941
-					return 'rgba('.$r.','.$g.','.$b.','.$value[4].')';
942
-				}
943
-
944
-				$h = sprintf("#%02x%02x%02x", $r, $g, $b);
945
-
946
-				if (!empty($this->formatter->compressColors)) {
947
-					// Converting hex color to short notation (e.g. #003399 to #039)
948
-					if ($h[1] === $h[2] && $h[3] === $h[4] && $h[5] === $h[6]) {
949
-						$h = '#'.$h[1].$h[3].$h[5];
950
-					}
951
-				}
952
-
953
-				return $h;
954
-
955
-			case 'function':
956
-				list(, $name, $args) = $value;
957
-				return $name.'('.$this->compileValue($args).')';
958
-			default: // assumed to be unit
959
-				$this->throwError("unknown value type: $value[0]");
960
-		}
961
-	}
962
-
963
-	protected function lib_pow($args)
964
-	{
965
-		list($base, $exp) = $this->assertArgs($args, 2, "pow");
966
-		return pow($this->assertNumber($base), $this->assertNumber($exp));
967
-	}
968
-
969
-	protected function lib_pi()
970
-	{
971
-		return pi();
972
-	}
973
-
974
-	protected function lib_mod($args)
975
-	{
976
-		list($a, $b) = $this->assertArgs($args, 2, "mod");
977
-		return $this->assertNumber($a) % $this->assertNumber($b);
978
-	}
979
-
980
-	protected function lib_tan($num)
981
-	{
982
-		return tan($this->assertNumber($num));
983
-	}
984
-
985
-	protected function lib_sin($num)
986
-	{
987
-		return sin($this->assertNumber($num));
988
-	}
989
-
990
-	protected function lib_cos($num)
991
-	{
992
-		return cos($this->assertNumber($num));
993
-	}
994
-
995
-	protected function lib_atan($num)
996
-	{
997
-		$num = atan($this->assertNumber($num));
998
-		return array("number", $num, "rad");
999
-	}
1000
-
1001
-	protected function lib_asin($num)
1002
-	{
1003
-		$num = asin($this->assertNumber($num));
1004
-		return array("number", $num, "rad");
1005
-	}
1006
-
1007
-	protected function lib_acos($num)
1008
-	{
1009
-		$num = acos($this->assertNumber($num));
1010
-		return array("number", $num, "rad");
1011
-	}
1012
-
1013
-	protected function lib_sqrt($num)
1014
-	{
1015
-		return sqrt($this->assertNumber($num));
1016
-	}
1017
-
1018
-	protected function lib_extract($value)
1019
-	{
1020
-		list($list, $idx) = $this->assertArgs($value, 2, "extract");
1021
-		$idx = $this->assertNumber($idx);
1022
-		// 1 indexed
1023
-		if ($list[0] == "list" && isset($list[2][$idx - 1])) {
1024
-			return $list[2][$idx - 1];
1025
-		}
1026
-		return '';
1027
-	}
1028
-
1029
-	protected function lib_isnumber($value)
1030
-	{
1031
-		return $this->toBool($value[0] == "number");
1032
-	}
1033
-
1034
-	protected function lib_isstring($value)
1035
-	{
1036
-		return $this->toBool($value[0] == "string");
1037
-	}
1038
-
1039
-	protected function lib_iscolor($value)
1040
-	{
1041
-		return $this->toBool($this->coerceColor($value));
1042
-	}
1043
-
1044
-	protected function lib_iskeyword($value)
1045
-	{
1046
-		return $this->toBool($value[0] == "keyword");
1047
-	}
1048
-
1049
-	protected function lib_ispixel($value)
1050
-	{
1051
-		return $this->toBool($value[0] == "number" && $value[2] == "px");
1052
-	}
1053
-
1054
-	protected function lib_ispercentage($value)
1055
-	{
1056
-		return $this->toBool($value[0] == "number" && $value[2] == "%");
1057
-	}
1058
-
1059
-	protected function lib_isem($value)
1060
-	{
1061
-		return $this->toBool($value[0] == "number" && $value[2] == "em");
1062
-	}
1063
-
1064
-	protected function lib_isrem($value)
1065
-	{
1066
-		return $this->toBool($value[0] == "number" && $value[2] == "rem");
1067
-	}
1068
-
1069
-	protected function lib_rgbahex($color)
1070
-	{
1071
-		$color = $this->coerceColor($color);
1072
-		if (is_null($color)) {
1073
-			$this->throwError("color expected for rgbahex");
1074
-		}
1075
-
1076
-		return sprintf(
1077
-			"#%02x%02x%02x%02x",
1078
-			isset($color[4]) ? $color[4] * 255 : 255,
1079
-			$color[1],
1080
-			$color[2],
1081
-			$color[3]
1082
-		);
1083
-	}
1084
-
1085
-	protected function lib_argb($color)
1086
-	{
1087
-		return $this->lib_rgbahex($color);
1088
-	}
1089
-
1090
-	/**
1091
-	 * Given an url, decide whether to output a regular link or the base64-encoded contents of the file
1092
-	 *
1093
-	 * @param  array  $value either an argument list (two strings) or a single string
1094
-	 * @return string        formatted url(), either as a link or base64-encoded
1095
-	 */
1096
-	protected function lib_data_uri($value)
1097
-	{
1098
-		$mime = ($value[0] === 'list') ? $value[2][0][2] : null;
1099
-		$url = ($value[0] === 'list') ? $value[2][1][2][0] : $value[2][0];
1100
-
1101
-		$fullpath = $this->findImport($url);
1102
-
1103
-		if ($fullpath && ($fsize = filesize($fullpath)) !== false) {
1104
-			// IE8 can't handle data uris larger than 32KB
1105
-			if ($fsize / 1024 < 32) {
1106
-				if (is_null($mime)) {
1107
-					if (class_exists('finfo')) { // php 5.3+
1108
-						$finfo = new finfo(FILEINFO_MIME);
1109
-						$mime = explode('; ', $finfo->file($fullpath));
1110
-						$mime = $mime[0];
1111
-					} elseif (function_exists('mime_content_type')) { // PHP 5.2
1112
-						$mime = mime_content_type($fullpath);
1113
-					}
1114
-				}
1115
-
1116
-				if (!is_null($mime)) { // fallback if the mime type is still unknown
1117
-					$url = sprintf('data:%s;base64,%s', $mime, base64_encode(file_get_contents($fullpath)));
1118
-				}
1119
-			}
1120
-		}
1121
-
1122
-		return 'url("'.$url.'")';
1123
-	}
1124
-
1125
-	// utility func to unquote a string
1126
-	protected function lib_e($arg)
1127
-	{
1128
-		switch ($arg[0]) {
1129
-			case "list":
1130
-				$items = $arg[2];
1131
-				if (isset($items[0])) {
1132
-					return $this->lib_e($items[0]);
1133
-				}
1134
-				$this->throwError("unrecognised input"); // This ends function by throwing an exception
1135
-				// no break
1136
-			case "string":
1137
-				$arg[1] = "";
1138
-				return $arg;
1139
-			case "keyword":
1140
-				return $arg;
1141
-			default:
1142
-				return array("keyword", $this->compileValue($arg));
1143
-		}
1144
-	}
1145
-
1146
-	protected function lib__sprintf($args)
1147
-	{
1148
-		if ($args[0] != "list") {
1149
-			return $args;
1150
-		}
1151
-		$values = $args[2];
1152
-		$string = array_shift($values);
1153
-		$template = $this->compileValue($this->lib_e($string));
1154
-
1155
-		$i = 0;
1156
-		$m = array();
1157
-		if (preg_match_all('/%[dsa]/', $template, $m)) {
1158
-			foreach ($m[0] as $match) {
1159
-				$val = isset($values[$i]) ?
1160
-					$this->reduce($values[$i]) : array('keyword', '');
1161
-
1162
-				// lessjs compat, renders fully expanded color, not raw color
1163
-				if ($color = $this->coerceColor($val)) {
1164
-					$val = $color;
1165
-				}
1166
-
1167
-				$i++;
1168
-				$rep = $this->compileValue($this->lib_e($val));
1169
-				$template = preg_replace(
1170
-					'/'.self::preg_quote($match).'/',
1171
-					$rep,
1172
-					$template,
1173
-					1
1174
-				);
1175
-			}
1176
-		}
1177
-
1178
-		$d = $string[0] == "string" ? $string[1] : '"';
1179
-		return array("string", $d, array($template));
1180
-	}
1181
-
1182
-	protected function lib_floor($arg)
1183
-	{
1184
-		$value = $this->assertNumber($arg);
1185
-		return array("number", floor($value), $arg[2]);
1186
-	}
1187
-
1188
-	protected function lib_ceil($arg)
1189
-	{
1190
-		$value = $this->assertNumber($arg);
1191
-		return array("number", ceil($value), $arg[2]);
1192
-	}
1193
-
1194
-	protected function lib_round($arg)
1195
-	{
1196
-		if ($arg[0] != "list") {
1197
-			$value = $this->assertNumber($arg);
1198
-			return array("number", round($value), $arg[2]);
1199
-		} else {
1200
-			$value = $this->assertNumber($arg[2][0]);
1201
-			$precision = $this->assertNumber($arg[2][1]);
1202
-			return array("number", round($value, $precision), $arg[2][0][2]);
1203
-		}
1204
-	}
1205
-
1206
-	protected function lib_unit($arg)
1207
-	{
1208
-		if ($arg[0] == "list") {
1209
-			list($number, $newUnit) = $arg[2];
1210
-			return array("number", $this->assertNumber($number),
1211
-				$this->compileValue($this->lib_e($newUnit)));
1212
-		} else {
1213
-			return array("number", $this->assertNumber($arg), "");
1214
-		}
1215
-	}
1216
-
1217
-	/**
1218
-	 * Helper function to get arguments for color manipulation functions.
1219
-	 * takes a list that contains a color like thing and a percentage
1220
-	 */
1221
-	public function colorArgs($args)
1222
-	{
1223
-		if ($args[0] != 'list' || count($args[2]) < 2) {
1224
-			return array(array('color', 0, 0, 0), 0);
1225
-		}
1226
-		list($color, $delta) = $args[2];
1227
-		$color = $this->assertColor($color);
1228
-		$delta = (float) $delta[1];
1229
-
1230
-		return array($color, $delta);
1231
-	}
1232
-
1233
-	protected function lib_darken($args)
1234
-	{
1235
-		list($color, $delta) = $this->colorArgs($args);
1236
-
1237
-		$hsl = $this->toHSL($color);
1238
-		$hsl[3] = $this->clamp($hsl[3] - $delta, 100);
1239
-		return $this->toRGB($hsl);
1240
-	}
1241
-
1242
-	protected function lib_lighten($args)
1243
-	{
1244
-		list($color, $delta) = $this->colorArgs($args);
1245
-
1246
-		$hsl = $this->toHSL($color);
1247
-		$hsl[3] = $this->clamp($hsl[3] + $delta, 100);
1248
-		return $this->toRGB($hsl);
1249
-	}
1250
-
1251
-	protected function lib_saturate($args)
1252
-	{
1253
-		list($color, $delta) = $this->colorArgs($args);
1254
-
1255
-		$hsl = $this->toHSL($color);
1256
-		$hsl[2] = $this->clamp($hsl[2] + $delta, 100);
1257
-		return $this->toRGB($hsl);
1258
-	}
1259
-
1260
-	protected function lib_desaturate($args)
1261
-	{
1262
-		list($color, $delta) = $this->colorArgs($args);
1263
-
1264
-		$hsl = $this->toHSL($color);
1265
-		$hsl[2] = $this->clamp($hsl[2] - $delta, 100);
1266
-		return $this->toRGB($hsl);
1267
-	}
1268
-
1269
-	protected function lib_spin($args)
1270
-	{
1271
-		list($color, $delta) = $this->colorArgs($args);
1272
-
1273
-		$hsl = $this->toHSL($color);
1274
-
1275
-		$hsl[1] = $hsl[1] + $delta % 360;
1276
-		if ($hsl[1] < 0) {
1277
-			$hsl[1] += 360;
1278
-		}
1279
-
1280
-		return $this->toRGB($hsl);
1281
-	}
1282
-
1283
-	protected function lib_fadeout($args)
1284
-	{
1285
-		list($color, $delta) = $this->colorArgs($args);
1286
-		$color[4] = $this->clamp((isset($color[4]) ? $color[4] : 1) - $delta / 100);
1287
-		return $color;
1288
-	}
1289
-
1290
-	protected function lib_fadein($args)
1291
-	{
1292
-		list($color, $delta) = $this->colorArgs($args);
1293
-		$color[4] = $this->clamp((isset($color[4]) ? $color[4] : 1) + $delta / 100);
1294
-		return $color;
1295
-	}
1296
-
1297
-	protected function lib_hue($color)
1298
-	{
1299
-		$hsl = $this->toHSL($this->assertColor($color));
1300
-		return round($hsl[1]);
1301
-	}
1302
-
1303
-	protected function lib_saturation($color)
1304
-	{
1305
-		$hsl = $this->toHSL($this->assertColor($color));
1306
-		return round($hsl[2]);
1307
-	}
1308
-
1309
-	protected function lib_lightness($color)
1310
-	{
1311
-		$hsl = $this->toHSL($this->assertColor($color));
1312
-		return round($hsl[3]);
1313
-	}
1314
-
1315
-	// get the alpha of a color
1316
-	// defaults to 1 for non-colors or colors without an alpha
1317
-	protected function lib_alpha($value)
1318
-	{
1319
-		if (!is_null($color = $this->coerceColor($value))) {
1320
-			return isset($color[4]) ? $color[4] : 1;
1321
-		}
1322
-		return '';
1323
-	}
1324
-
1325
-	// set the alpha of the color
1326
-	protected function lib_fade($args)
1327
-	{
1328
-		list($color, $alpha) = $this->colorArgs($args);
1329
-		$color[4] = $this->clamp($alpha / 100.0);
1330
-		return $color;
1331
-	}
1332
-
1333
-	protected function lib_percentage($arg)
1334
-	{
1335
-		$num = $this->assertNumber($arg);
1336
-		return array("number", $num * 100, "%");
1337
-	}
1338
-
1339
-	/**
1340
-	 * Mix color with white in variable proportion.
1341
-	 *
1342
-	 * It is the same as calling `mix(#ffffff, @color, @weight)`.
1343
-	 *
1344
-	 *     tint(@color, [@weight: 50%]);
1345
-	 *
1346
-	 * http://lesscss.org/functions/#color-operations-tint
1347
-	 *
1348
-	 * @return array Color
1349
-	 */
1350
-	protected function lib_tint($args)
1351
-	{
1352
-		$white = ['color', 255, 255, 255];
1353
-		if ($args[0] == 'color') {
1354
-			return $this->lib_mix(['list', ',', [$white, $args]]);
1355
-		} elseif ($args[0] == "list" && count($args[2]) == 2) {
1356
-			return $this->lib_mix([$args[0], $args[1], [$white, $args[2][0], $args[2][1]]]);
1357
-		} else {
1358
-			$this->throwError("tint expects (color, weight)");
1359
-		}
1360
-		return array();
1361
-	}
1362
-
1363
-	/**
1364
-	 * Mix color with black in variable proportion.
1365
-	 *
1366
-	 * It is the same as calling `mix(#000000, @color, @weight)`
1367
-	 *
1368
-	 *     shade(@color, [@weight: 50%]);
1369
-	 *
1370
-	 * http://lesscss.org/functions/#color-operations-shade
1371
-	 *
1372
-	 * @return array Color
1373
-	 */
1374
-	protected function lib_shade($args)
1375
-	{
1376
-		$black = ['color', 0, 0, 0];
1377
-		if ($args[0] == 'color') {
1378
-			return $this->lib_mix(['list', ',', [$black, $args]]);
1379
-		} elseif ($args[0] == "list" && count($args[2]) == 2) {
1380
-			return $this->lib_mix([$args[0], $args[1], [$black, $args[2][0], $args[2][1]]]);
1381
-		} else {
1382
-			$this->throwError("shade expects (color, weight)");
1383
-		}
1384
-		return array();
1385
-	}
1386
-
1387
-	/**
1388
-	 * lib_mix
1389
-	 * mixes two colors by weight
1390
-	 * mix(@color1, @color2, [@weight: 50%]);
1391
-	 * http://sass-lang.com/docs/yardoc/Sass/Script/Functions.html#mix-instance_method
1392
-	 *
1393
-	 * @param array         $args   Args
1394
-	 * @return array
1395
-	 */
1396
-	protected function lib_mix($args)
1397
-	{
1398
-		if ($args[0] != "list" || count($args[2]) < 2) {
1399
-			$this->throwError("mix expects (color1, color2, weight)");
1400
-		}
1401
-
1402
-		list($first, $second) = $args[2];
1403
-		$first = $this->assertColor($first);
1404
-		$second = $this->assertColor($second);
1405
-
1406
-		$first_a = $this->lib_alpha($first);
1407
-		$second_a = $this->lib_alpha($second);
1408
-
1409
-		if (isset($args[2][2])) {
1410
-			$weight = $args[2][2][1] / 100.0;
1411
-		} else {
1412
-			$weight = 0.5;
1413
-		}
1414
-
1415
-		$w = $weight * 2 - 1;
1416
-		$a = $first_a - $second_a;
1417
-
1418
-		$w1 = (($w * $a == -1 ? $w : ($w + $a) / (1 + $w * $a)) + 1) / 2.0;
1419
-		$w2 = 1.0 - $w1;
1420
-
1421
-		$new = array('color',
1422
-			$w1 * $first[1] + $w2 * $second[1],
1423
-			$w1 * $first[2] + $w2 * $second[2],
1424
-			$w1 * $first[3] + $w2 * $second[3],
1425
-		);
1426
-
1427
-		if ($first_a != 1.0 || $second_a != 1.0) {
1428
-			$new[] = $first_a * $weight + $second_a * ($weight - 1);
1429
-		}
1430
-
1431
-		return $this->fixColor($new);
1432
-	}
1433
-
1434
-	/**
1435
-	 * lib_contrast
1436
-	 *
1437
-	 * @param array         $args   Args
1438
-	 * @return array
1439
-	 */
1440
-	protected function lib_contrast($args)
1441
-	{
1442
-		$darkColor  = array('color', 0, 0, 0);
1443
-		$lightColor = array('color', 255, 255, 255);
1444
-		$threshold  = 0.43;
1445
-
1446
-		if ($args[0] == 'list') {
1447
-			$inputColor = (isset($args[2][0])) ? $this->assertColor($args[2][0]) : $lightColor;
1448
-			$darkColor  = (isset($args[2][1])) ? $this->assertColor($args[2][1]) : $darkColor;
1449
-			$lightColor = (isset($args[2][2])) ? $this->assertColor($args[2][2]) : $lightColor;
1450
-			$threshold  = (isset($args[2][3])) ? $this->assertNumber($args[2][3]) : $threshold;
1451
-		} else {
1452
-			$inputColor = $this->assertColor($args);
1453
-		}
1454
-
1455
-		$inputColor = $this->coerceColor($inputColor);
1456
-		$darkColor  = $this->coerceColor($darkColor);
1457
-		$lightColor = $this->coerceColor($lightColor);
1458
-
1459
-		//Figure out which is actually light and dark!
1460
-		if ($this->toLuma($darkColor) > $this->toLuma($lightColor)) {
1461
-			$t = $lightColor;
1462
-			$lightColor = $darkColor;
1463
-			$darkColor  = $t;
1464
-		}
1465
-
1466
-		$inputColor_alpha = $this->lib_alpha($inputColor);
1467
-		if (($this->toLuma($inputColor) * $inputColor_alpha) < $threshold) {
1468
-			return $lightColor;
1469
-		}
1470
-		return $darkColor;
1471
-	}
1472
-
1473
-	private function toLuma($color)
1474
-	{
1475
-		list(, $r, $g, $b) = $this->coerceColor($color);
1476
-
1477
-		$r = $r / 255;
1478
-		$g = $g / 255;
1479
-		$b = $b / 255;
1480
-
1481
-		$r = ($r <= 0.03928) ? $r / 12.92 : pow((($r + 0.055) / 1.055), 2.4);
1482
-		$g = ($g <= 0.03928) ? $g / 12.92 : pow((($g + 0.055) / 1.055), 2.4);
1483
-		$b = ($b <= 0.03928) ? $b / 12.92 : pow((($b + 0.055) / 1.055), 2.4);
1484
-
1485
-		return (0.2126 * $r) + (0.7152 * $g) + (0.0722 * $b);
1486
-	}
1487
-
1488
-	protected function lib_luma($color)
1489
-	{
1490
-		return array("number", round($this->toLuma($color) * 100, 8), "%");
1491
-	}
1492
-
1493
-
1494
-	public function assertColor($value, $error = "expected color value")
1495
-	{
1496
-		$color = $this->coerceColor($value);
1497
-		if (is_null($color)) {
1498
-			$this->throwError($error);
1499
-		}
1500
-		return $color;
1501
-	}
1502
-
1503
-	public function assertNumber($value, $error = "expecting number")
1504
-	{
1505
-		if ($value[0] == "number") {
1506
-			return $value[1];
1507
-		}
1508
-		$this->throwError($error);
1509
-	}
1510
-
1511
-	public function assertArgs($value, $expectedArgs, $name = "")
1512
-	{
1513
-		if ($expectedArgs == 1) {
1514
-			return $value;
1515
-		} else {
1516
-			if ($value[0] !== "list" || $value[1] != ",") {
1517
-				$this->throwError("expecting list");
1518
-			}
1519
-			$values = $value[2];
1520
-			$numValues = count($values);
1521
-			if ($expectedArgs != $numValues) {
1522
-				if ($name) {
1523
-					$name = $name.": ";
1524
-				}
1525
-
1526
-				$this->throwError("{$name}expecting $expectedArgs arguments, got $numValues");
1527
-			}
1528
-
1529
-			return $values;
1530
-		}
1531
-	}
1532
-
1533
-	protected function toHSL($color)
1534
-	{
1535
-		if ($color[0] === 'hsl') {
1536
-			return $color;
1537
-		}
1538
-
1539
-		$r = $color[1] / 255;
1540
-		$g = $color[2] / 255;
1541
-		$b = $color[3] / 255;
1542
-
1543
-		$min = min($r, $g, $b);
1544
-		$max = max($r, $g, $b);
1545
-
1546
-		$L = ($min + $max) / 2;
1547
-		if ($min == $max) {
1548
-			$S = $H = 0;
1549
-		} else {
1550
-			if ($L < 0.5) {
1551
-				$S = ($max - $min) / ($max + $min);
1552
-			} else {
1553
-				$S = ($max - $min) / (2.0 - $max - $min);
1554
-			}
1555
-			if ($r == $max) {
1556
-				$H = ($g - $b) / ($max - $min);
1557
-			} elseif ($g == $max) {
1558
-				$H = 2.0 + ($b - $r) / ($max - $min);
1559
-			} elseif ($b == $max) {
1560
-				$H = 4.0 + ($r - $g) / ($max - $min);
1561
-			}
1562
-		}
1563
-
1564
-		$out = array('hsl',
1565
-			($H < 0 ? $H + 6 : $H) * 60,
1566
-			$S * 100,
1567
-			$L * 100,
1568
-		);
1569
-
1570
-		if (count($color) > 4) {
1571
-			// copy alpha
1572
-			$out[] = $color[4];
1573
-		}
1574
-		return $out;
1575
-	}
1576
-
1577
-	protected function toRGB_helper($comp, $temp1, $temp2)
1578
-	{
1579
-		if ($comp < 0) {
1580
-			$comp += 1.0;
1581
-		} elseif ($comp > 1) {
1582
-			$comp -= 1.0;
1583
-		}
1584
-
1585
-		if (6 * $comp < 1) {
1586
-			return $temp1 + ($temp2 - $temp1) * 6 * $comp;
1587
-		}
1588
-		if (2 * $comp < 1) {
1589
-			return $temp2;
1590
-		}
1591
-		if (3 * $comp < 2) {
1592
-			return $temp1 + ($temp2 - $temp1) * ((2 / 3) - $comp) * 6;
1593
-		}
1594
-
1595
-		return $temp1;
1596
-	}
1597
-
1598
-	/**
1599
-	 * Converts a hsl array into a color value in rgb.
1600
-	 * Expects H to be in range of 0 to 360, S and L in 0 to 100
1601
-	 */
1602
-	protected function toRGB($color)
1603
-	{
1604
-		if ($color[0] === 'color') {
1605
-			return $color;
1606
-		}
1607
-
1608
-		$H = $color[1] / 360;
1609
-		$S = $color[2] / 100;
1610
-		$L = $color[3] / 100;
1611
-
1612
-		if ($S == 0) {
1613
-			$r = $g = $b = $L;
1614
-		} else {
1615
-			$temp2 = $L < 0.5 ?
1616
-				$L * (1.0 + $S) : $L + $S - $L * $S;
1617
-
1618
-			$temp1 = 2.0 * $L - $temp2;
1619
-
1620
-			$r = $this->toRGB_helper($H + 1 / 3, $temp1, $temp2);
1621
-			$g = $this->toRGB_helper($H, $temp1, $temp2);
1622
-			$b = $this->toRGB_helper($H - 1 / 3, $temp1, $temp2);
1623
-		}
1624
-
1625
-		// $out = array('color', round($r*255), round($g*255), round($b*255));
1626
-		$out = array('color', $r * 255, $g * 255, $b * 255);
1627
-		if (count($color) > 4) {
1628
-			// copy alpha
1629
-			$out[] = $color[4];
1630
-		}
1631
-		return $out;
1632
-	}
1633
-
1634
-	protected function clamp($v, $max = 1, $min = 0)
1635
-	{
1636
-		return min($max, max($min, $v));
1637
-	}
1638
-
1639
-	/**
1640
-	 * Convert the rgb, rgba, hsl color literals of function type
1641
-	 * as returned by the parser into values of color type.
1642
-	 */
1643
-	protected function funcToColor($func)
1644
-	{
1645
-		$fname = $func[1];
1646
-		if ($func[2][0] != 'list') {
1647
-			// need a list of arguments
1648
-			return false;
1649
-		}
1650
-		$rawComponents = $func[2][2];
1651
-
1652
-		if ($fname == 'hsl' || $fname == 'hsla') {
1653
-			$hsl = array('hsl');
1654
-			$i = 0;
1655
-			foreach ($rawComponents as $c) {
1656
-				$val = $this->reduce($c);
1657
-				$val = isset($val[1]) ? (float) $val[1] : 0;
1658
-
1659
-				if ($i == 0) {
1660
-					$clamp = 360;
1661
-				} elseif ($i < 3) {
1662
-					$clamp = 100;
1663
-				} else {
1664
-					$clamp = 1;
1665
-				}
1666
-
1667
-				$hsl[] = $this->clamp($val, $clamp);
1668
-				$i++;
1669
-			}
1670
-
1671
-			while (count($hsl) < 4) {
1672
-				$hsl[] = 0;
1673
-			}
1674
-			return $this->toRGB($hsl);
1675
-
1676
-		} elseif ($fname == 'rgb' || $fname == 'rgba') {
1677
-			$components = array();
1678
-			$i = 1;
1679
-			foreach ($rawComponents as $c) {
1680
-				$c = $this->reduce($c);
1681
-				if ($i < 4) {
1682
-					if ($c[0] == "number" && $c[2] == "%") {
1683
-						$components[] = 255 * ($c[1] / 100);
1684
-					} else {
1685
-						$components[] = (float) $c[1];
1686
-					}
1687
-				} elseif ($i == 4) {
1688
-					if ($c[0] == "number" && $c[2] == "%") {
1689
-						$components[] = 1.0 * ($c[1] / 100);
1690
-					} else {
1691
-						$components[] = (float) $c[1];
1692
-					}
1693
-				} else {
1694
-					break;
1695
-				}
1696
-
1697
-				$i++;
1698
-			}
1699
-			while (count($components) < 3) {
1700
-				$components[] = 0;
1701
-			}
1702
-			array_unshift($components, 'color');
1703
-			return $this->fixColor($components);
1704
-		}
1705
-
1706
-		return false;
1707
-	}
1708
-
1709
-	protected function reduce($value, $forExpression = false)
1710
-	{
1711
-		switch ($value[0]) {
1712
-			case "interpolate":
1713
-				$reduced = $this->reduce($value[1]);
1714
-				$var = $this->compileValue($reduced);
1715
-				$res = $this->reduce(array("variable", $this->vPrefix.$var));
1716
-
1717
-				if ($res[0] == "raw_color") {
1718
-					$res = $this->coerceColor($res);
1719
-				}
1720
-
1721
-				if (empty($value[2])) {
1722
-					$res = $this->lib_e($res);
1723
-				}
1724
-
1725
-				return $res;
1726
-			case "variable":
1727
-				$key = $value[1];
1728
-				if (is_array($key)) {
1729
-					$key = $this->reduce($key);
1730
-					$key = $this->vPrefix.$this->compileValue($this->lib_e($key));
1731
-				}
1732
-
1733
-				$seen = & $this->env->seenNames;
1734
-
1735
-				if (!empty($seen[$key])) {
1736
-					$this->throwError("infinite loop detected: $key");
1737
-				}
1738
-
1739
-				$seen[$key] = true;
1740
-				$out = $this->reduce($this->get($key));
1741
-				$seen[$key] = false;
1742
-				return $out;
1743
-			case "list":
1744
-				foreach ($value[2] as &$item) {
1745
-					$item = $this->reduce($item, $forExpression);
1746
-				}
1747
-				return $value;
1748
-			case "expression":
1749
-				return $this->evaluate($value);
1750
-			case "string":
1751
-				foreach ($value[2] as &$part) {
1752
-					if (is_array($part)) {
1753
-						$strip = $part[0] == "variable";
1754
-						$part = $this->reduce($part);
1755
-						if ($strip) {
1756
-							$part = $this->lib_e($part);
1757
-						}
1758
-					}
1759
-				}
1760
-				return $value;
1761
-			case "escape":
1762
-				list(, $inner) = $value;
1763
-				return $this->lib_e($this->reduce($inner));
1764
-			case "function":
1765
-				$color = $this->funcToColor($value);
1766
-				if ($color) {
1767
-					return $color;
1768
-				}
1769
-
1770
-				list(, $name, $args) = $value;
1771
-				if ($name == "%") {
1772
-					$name = "_sprintf";
1773
-				}
1774
-
1775
-				$f = isset($this->libFunctions[$name]) ?
1776
-					$this->libFunctions[$name] : array($this, 'lib_'.str_replace('-', '_', $name));
1777
-
1778
-				if (is_callable($f)) {
1779
-					if ($args[0] == 'list') {
1780
-						$args = self::compressList($args[2], $args[1]);
1781
-					}
1782
-
1783
-					$ret = call_user_func($f, $this->reduce($args, true), $this);
1784
-
1785
-					if (is_null($ret)) {
1786
-						return array("string", "", array(
1787
-							$name, "(", $args, ")"
1788
-						));
1789
-					}
1790
-
1791
-					// convert to a typed value if the result is a php primitive
1792
-					if (is_numeric($ret)) {
1793
-						$ret = array('number', $ret, "");
1794
-					} elseif (!is_array($ret)) {
1795
-						$ret = array('keyword', $ret);
1796
-					}
1797
-
1798
-					return $ret;
1799
-				}
1800
-
1801
-				// plain function, reduce args
1802
-				$value[2] = $this->reduce($value[2]);
1803
-				return $value;
1804
-			case "unary":
1805
-				list(, $op, $exp) = $value;
1806
-				$exp = $this->reduce($exp);
1807
-
1808
-				if ($exp[0] == "number") {
1809
-					switch ($op) {
1810
-						case "+":
1811
-							return $exp;
1812
-						case "-":
1813
-							$exp[1] *= -1;
1814
-							return $exp;
1815
-					}
1816
-				}
1817
-				return array("string", "", array($op, $exp));
1818
-		}
1819
-
1820
-		if ($forExpression) {
1821
-			switch ($value[0]) {
1822
-				case "keyword":
1823
-					if ($color = $this->coerceColor($value)) {
1824
-						return $color;
1825
-					}
1826
-					break;
1827
-				case "raw_color":
1828
-					return $this->coerceColor($value);
1829
-			}
1830
-		}
1831
-
1832
-		return $value;
1833
-	}
1834
-
1835
-
1836
-	// coerce a value for use in color operation
1837
-	protected function coerceColor($value)
1838
-	{
1839
-		switch ($value[0]) {
1840
-			case 'color':
1841
-				return $value;
1842
-			case 'raw_color':
1843
-				$c = array("color", 0, 0, 0);
1844
-				$colorStr = substr($value[1], 1);
1845
-				$num = hexdec($colorStr);
1846
-				$width = strlen($colorStr) == 3 ? 16 : 256;
1847
-
1848
-				for ($i = 3; $i > 0; $i--) { // 3 2 1
1849
-					$t = intval($num) % $width;
1850
-					$num /= $width;
1851
-
1852
-					$c[$i] = $t * (256 / $width) + $t * floor(16/$width);
1853
-				}
1854
-
1855
-				return $c;
1856
-			case 'keyword':
1857
-				$name = $value[1];
1858
-				if (isset(self::$cssColors[$name])) {
1859
-					$rgba = explode(',', self::$cssColors[$name]);
1860
-
1861
-					if (isset($rgba[3])) {
1862
-						return array('color', $rgba[0], $rgba[1], $rgba[2], $rgba[3]);
1863
-					}
1864
-					return array('color', $rgba[0], $rgba[1], $rgba[2]);
1865
-				}
1866
-				return null;
1867
-		}
1868
-		return null;
1869
-	}
1870
-
1871
-	// make something string like into a string
1872
-	protected function coerceString($value)
1873
-	{
1874
-		switch ($value[0]) {
1875
-			case "string":
1876
-				return $value;
1877
-			case "keyword":
1878
-				return array("string", "", array($value[1]));
1879
-		}
1880
-		return null;
1881
-	}
1882
-
1883
-	// turn list of length 1 into value type
1884
-	protected function flattenList($value)
1885
-	{
1886
-		if ($value[0] == "list" && count($value[2]) == 1) {
1887
-			return $this->flattenList($value[2][0]);
1888
-		}
1889
-		return $value;
1890
-	}
1891
-
1892
-	public function toBool($a)
1893
-	{
1894
-		return $a ? self::$TRUE : self::$FALSE;
1895
-	}
1896
-
1897
-	// evaluate an expression
1898
-	protected function evaluate($exp)
1899
-	{
1900
-		list(, $op, $left, $right, $whiteBefore, $whiteAfter) = $exp;
1901
-
1902
-		$left = $this->reduce($left, true);
1903
-		$right = $this->reduce($right, true);
1904
-
1905
-		if ($leftColor = $this->coerceColor($left)) {
1906
-			$left = $leftColor;
1907
-		}
1908
-
1909
-		if ($rightColor = $this->coerceColor($right)) {
1910
-			$right = $rightColor;
1911
-		}
1912
-
1913
-		$ltype = $left[0];
1914
-		$rtype = $right[0];
1915
-
1916
-		// operators that work on all types
1917
-		if ($op == "and") {
1918
-			return $this->toBool($left == self::$TRUE && $right == self::$TRUE);
1919
-		}
1920
-
1921
-		if ($op == "=") {
1922
-			return $this->toBool($this->eq($left, $right));
1923
-		}
1924
-
1925
-		if ($op == "+" && !is_null($str = $this->stringConcatenate($left, $right))) {
1926
-			return $str;
1927
-		}
1928
-
1929
-		// type based operators
1930
-		$fname = "op_{$ltype}_{$rtype}";
1931
-		if (is_callable(array($this, $fname))) {
1932
-			$out = $this->$fname($op, $left, $right);
1933
-			if (!is_null($out)) {
1934
-				return $out;
1935
-			}
1936
-		}
1937
-
1938
-		// make the expression look it did before being parsed
1939
-		$paddedOp = $op;
1940
-		if ($whiteBefore) {
1941
-			$paddedOp = " ".$paddedOp;
1942
-		}
1943
-		if ($whiteAfter) {
1944
-			$paddedOp .= " ";
1945
-		}
1946
-
1947
-		return array("string", "", array($left, $paddedOp, $right));
1948
-	}
1949
-
1950
-	protected function stringConcatenate($left, $right)
1951
-	{
1952
-		if ($strLeft = $this->coerceString($left)) {
1953
-			if ($right[0] == "string") {
1954
-				$right[1] = "";
1955
-			}
1956
-			$strLeft[2][] = $right;
1957
-			return $strLeft;
1958
-		}
1959
-
1960
-		if ($strRight = $this->coerceString($right)) {
1961
-			array_unshift($strRight[2], $left);
1962
-			return $strRight;
1963
-		}
1964
-		return '';
1965
-	}
1966
-
1967
-
1968
-	// make sure a color's components don't go out of bounds
1969
-	protected function fixColor($c)
1970
-	{
1971
-		foreach (range(1, 3) as $i) {
1972
-			if ($c[$i] < 0) {
1973
-				$c[$i] = 0;
1974
-			}
1975
-			if ($c[$i] > 255) {
1976
-				$c[$i] = 255;
1977
-			}
1978
-		}
1979
-
1980
-		return $c;
1981
-	}
1982
-
1983
-	protected function op_number_color($op, $lft, $rgt)
1984
-	{
1985
-		if ($op == '+' || $op == '*') {
1986
-			return $this->op_color_number($op, $rgt, $lft);
1987
-		}
1988
-		return array();
1989
-	}
1990
-
1991
-	protected function op_color_number($op, $lft, $rgt)
1992
-	{
1993
-		if ($rgt[0] == '%') {
1994
-			$rgt[1] /= 100;
1995
-		}
1996
-
1997
-		return $this->op_color_color(
1998
-			$op,
1999
-			$lft,
2000
-			array_fill(1, count($lft) - 1, $rgt[1])
2001
-		);
2002
-	}
2003
-
2004
-	protected function op_color_color($op, $left, $right)
2005
-	{
2006
-		$out = array('color');
2007
-		$max = count($left) > count($right) ? count($left) : count($right);
2008
-		foreach (range(1, $max - 1) as $i) {
2009
-			$lval = isset($left[$i]) ? $left[$i] : 0;
2010
-			$rval = isset($right[$i]) ? $right[$i] : 0;
2011
-			switch ($op) {
2012
-				case '+':
2013
-					$out[] = $lval + $rval;
2014
-					break;
2015
-				case '-':
2016
-					$out[] = $lval - $rval;
2017
-					break;
2018
-				case '*':
2019
-					$out[] = $lval * $rval;
2020
-					break;
2021
-				case '%':
2022
-					$out[] = $lval % $rval;
2023
-					break;
2024
-				case '/':
2025
-					if ($rval == 0) {
2026
-						$this->throwError("evaluate error: can't divide by zero");
2027
-					}
2028
-					$out[] = $lval / $rval;
2029
-					break;
2030
-				default:
2031
-					$this->throwError('evaluate error: color op number failed on op '.$op);
2032
-			}
2033
-		}
2034
-		return $this->fixColor($out);
2035
-	}
2036
-
2037
-	public function lib_red($color)
2038
-	{
2039
-		$color = $this->coerceColor($color);
2040
-		if (is_null($color)) {
2041
-			$this->throwError('color expected for red()');
2042
-		}
2043
-
2044
-		return $color[1];
2045
-	}
2046
-
2047
-	public function lib_green($color)
2048
-	{
2049
-		$color = $this->coerceColor($color);
2050
-		if (is_null($color)) {
2051
-			$this->throwError('color expected for green()');
2052
-		}
2053
-
2054
-		return $color[2];
2055
-	}
2056
-
2057
-	public function lib_blue($color)
2058
-	{
2059
-		$color = $this->coerceColor($color);
2060
-		if (is_null($color)) {
2061
-			$this->throwError('color expected for blue()');
2062
-		}
2063
-
2064
-		return $color[3];
2065
-	}
2066
-
2067
-
2068
-	// operator on two numbers
2069
-	protected function op_number_number($op, $left, $right)
2070
-	{
2071
-		$unit = empty($left[2]) ? $right[2] : $left[2];
2072
-
2073
-		$value = 0;
2074
-		switch ($op) {
2075
-			case '+':
2076
-				$value = $left[1] + $right[1];
2077
-				break;
2078
-			case '*':
2079
-				$value = $left[1] * $right[1];
2080
-				break;
2081
-			case '-':
2082
-				$value = $left[1] - $right[1];
2083
-				break;
2084
-			case '%':
2085
-				$value = $left[1] % $right[1];
2086
-				break;
2087
-			case '/':
2088
-				if ($right[1] == 0) {
2089
-					$this->throwError('parse error: divide by zero');
2090
-				}
2091
-				$value = $left[1] / $right[1];
2092
-				break;
2093
-			case '<':
2094
-				return $this->toBool($left[1] < $right[1]);
2095
-			case '>':
2096
-				return $this->toBool($left[1] > $right[1]);
2097
-			case '>=':
2098
-				return $this->toBool($left[1] >= $right[1]);
2099
-			case '=<':
2100
-				return $this->toBool($left[1] <= $right[1]);
2101
-			default:
2102
-				$this->throwError('parse error: unknown number operator: '.$op);
2103
-		}
2104
-
2105
-		return array("number", $value, $unit);
2106
-	}
2107
-
2108
-
2109
-	/* environment functions */
2110
-
2111
-	protected function makeOutputBlock($type, $selectors = null)
2112
-	{
2113
-		$b = new stdclass();
2114
-		$b->lines = array();
2115
-		$b->children = array();
2116
-		$b->selectors = $selectors;
2117
-		$b->type = $type;
2118
-		$b->parent = $this->scope;
2119
-		return $b;
2120
-	}
2121
-
2122
-	// the state of execution
2123
-	protected function pushEnv($block = null)
2124
-	{
2125
-		$e = new stdclass();
2126
-		$e->parent = $this->env;
2127
-		$e->store = array();
2128
-		$e->block = $block;
2129
-
2130
-		$this->env = $e;
2131
-		return $e;
2132
-	}
2133
-
2134
-	// pop something off the stack
2135
-	protected function popEnv()
2136
-	{
2137
-		$old = $this->env;
2138
-		$this->env = $this->env->parent;
2139
-		return $old;
2140
-	}
2141
-
2142
-	// set something in the current env
2143
-	protected function set($name, $value)
2144
-	{
2145
-		$this->env->store[$name] = $value;
2146
-	}
2147
-
2148
-
2149
-	// get the highest occurrence entry for a name
2150
-	protected function get($name)
2151
-	{
2152
-		$current = $this->env;
2153
-
2154
-		$isArguments = $name == $this->vPrefix.'arguments';
2155
-		while ($current) {
2156
-			if ($isArguments && isset($current->arguments)) {
2157
-				return array('list', ' ', $current->arguments);
2158
-			}
2159
-
2160
-			if (isset($current->store[$name])) {
2161
-				return $current->store[$name];
2162
-			}
2163
-
2164
-			$current = isset($current->storeParent) ?
2165
-				$current->storeParent : $current->parent;
2166
-		}
2167
-
2168
-		$this->throwError("variable $name is undefined");
2169
-	}
2170
-
2171
-	// inject array of unparsed strings into environment as variables
2172
-	protected function injectVariables($args)
2173
-	{
2174
-		$this->pushEnv();
2175
-		$parser = new lessc_parser($this, __METHOD__);
2176
-		$value = null;
2177
-		foreach ($args as $name => $strValue) {
2178
-			if ($name[0] !== '@') {
2179
-				$name = '@'.$name;
2180
-			}
2181
-			$parser->count = 0;
2182
-			$parser->buffer = (string) $strValue;
2183
-			if (!$parser->propertyValue($value)) {
2184
-				throw new Exception("failed to parse passed in variable $name: $strValue");
2185
-			}
2186
-
2187
-			$this->set($name, $value);
2188
-		}
2189
-	}
2190
-
2191
-	/**
2192
-	 * Initialize any static state, can initialize parser for a file
2193
-	 * $opts isn't used yet
2194
-	 */
2195
-	public function __construct($fname = null)
2196
-	{
2197
-		if ($fname !== null) {
2198
-			// used for deprecated parse method
2199
-			$this->_parseFile = $fname;
2200
-		}
2201
-	}
2202
-
2203
-	public function compile($string, $name = null)
2204
-	{
2205
-		$locale = setlocale(LC_NUMERIC, 0);
2206
-		setlocale(LC_NUMERIC, "C");
2207
-
2208
-		$this->parser = $this->makeParser($name);
2209
-		$root = $this->parser->parse($string);
2210
-
2211
-		$this->env = null;
2212
-		$this->scope = null;
2213
-
2214
-		$this->formatter = $this->newFormatter();
2215
-
2216
-		if (!empty($this->registeredVars)) {
2217
-			$this->injectVariables($this->registeredVars);
2218
-		}
2219
-
2220
-		$this->sourceParser = $this->parser; // used for error messages
2221
-		$this->compileBlock($root);
2222
-
2223
-		ob_start();
2224
-		$this->formatter->block($this->scope);
2225
-		$out = ob_get_clean();
2226
-		setlocale(LC_NUMERIC, $locale);
2227
-		return $out;
2228
-	}
2229
-
2230
-	public function compileFile($fname, $outFname = null)
2231
-	{
2232
-		if (!is_readable($fname)) {
2233
-			throw new Exception('load error: failed to find '.$fname);
2234
-		}
2235
-
2236
-		$pi = pathinfo($fname);
2237
-
2238
-		$oldImport = $this->importDir;
2239
-
2240
-		$this->importDir = (array) $this->importDir;
2241
-		$this->importDir[] = $pi['dirname'].'/';
2242
-
2243
-		$this->addParsedFile($fname);
2244
-
2245
-		$out = $this->compile(file_get_contents($fname), $fname);
2246
-
2247
-		$this->importDir = $oldImport;
2248
-
2249
-		if ($outFname !== null) {
2250
-			return file_put_contents($outFname, $out);
2251
-		}
2252
-
2253
-		return $out;
2254
-	}
2255
-
2256
-	// compile only if changed input has changed or output doesn't exist
2257
-	public function checkedCompile($in, $out)
2258
-	{
2259
-		if (!is_file($out) || filemtime($in) > filemtime($out)) {
2260
-			$this->compileFile($in, $out);
2261
-			return true;
2262
-		}
2263
-		return false;
2264
-	}
2265
-
2266
-	/**
2267
-	 * Execute lessphp on a .less file or a lessphp cache structure
2268
-	 *
2269
-	 * The lessphp cache structure contains information about a specific
2270
-	 * less file having been parsed. It can be used as a hint for future
2271
-	 * calls to determine whether or not a rebuild is required.
2272
-	 *
2273
-	 * The cache structure contains two important keys that may be used
2274
-	 * externally:
2275
-	 *
2276
-	 * compiled: The final compiled CSS
2277
-	 * updated: The time (in seconds) the CSS was last compiled
2278
-	 *
2279
-	 * The cache structure is a plain-ol' PHP associative array and can
2280
-	 * be serialized and unserialized without a hitch.
2281
-	 *
2282
-	 * @param mixed $in Input
2283
-	 * @param bool $force Force rebuild?
2284
-	 * @return array|null lessphp cache structure
2285
-	 */
2286
-	public function cachedCompile($in, $force = false)
2287
-	{
2288
-		// assume no root
2289
-		$root = null;
2290
-
2291
-		if (is_string($in)) {
2292
-			$root = $in;
2293
-		} elseif (is_array($in) && isset($in['root'])) {
2294
-			if ($force || !isset($in['files'])) {
2295
-				// If we are forcing a recompile or if for some reason the
2296
-				// structure does not contain any file information we should
2297
-				// specify the root to trigger a rebuild.
2298
-				$root = $in['root'];
2299
-			} elseif (isset($in['files']) && is_array($in['files'])) {
2300
-				foreach ($in['files'] as $fname => $ftime) {
2301
-					if (!file_exists($fname) || filemtime($fname) > $ftime) {
2302
-						// One of the files we knew about previously has changed
2303
-						// so we should look at our incoming root again.
2304
-						$root = $in['root'];
2305
-						break;
2306
-					}
2307
-				}
2308
-			}
2309
-		} else {
2310
-			// TODO: Throw an exception? We got neither a string nor something
2311
-			// that looks like a compatible lessphp cache structure.
2312
-			return null;
2313
-		}
2314
-
2315
-		if ($root !== null) {
2316
-			// If we have a root value which means we should rebuild.
2317
-			$out = array();
2318
-			$out['root'] = $root;
2319
-			$out['compiled'] = $this->compileFile($root);
2320
-			$out['files'] = $this->allParsedFiles();
2321
-			$out['updated'] = time();
2322
-			return $out;
2323
-		} else {
2324
-			// No changes, pass back the structure
2325
-			// we were given initially.
2326
-			return $in;
2327
-		}
2328
-	}
2329
-
2330
-	// parse and compile buffer
2331
-	// This is deprecated
2332
-	public function parse($str = null, $initialVariables = null)
2333
-	{
2334
-		if (is_array($str)) {
2335
-			$initialVariables = $str;
2336
-			$str = null;
2337
-		}
2338
-
2339
-		$oldVars = $this->registeredVars;
2340
-		if ($initialVariables !== null) {
2341
-			$this->setVariables($initialVariables);
2342
-		}
2343
-
2344
-		if ($str == null) {
2345
-			if (empty($this->_parseFile)) {
2346
-				throw new exception("nothing to parse");
2347
-			}
2348
-
2349
-			$out = $this->compileFile($this->_parseFile);
2350
-		} else {
2351
-			$out = $this->compile($str);
2352
-		}
2353
-
2354
-		$this->registeredVars = $oldVars;
2355
-		return $out;
2356
-	}
2357
-
2358
-	protected function makeParser($name)
2359
-	{
2360
-		$parser = new lessc_parser($this, $name);
2361
-		$parser->writeComments = $this->preserveComments;
2362
-
2363
-		return $parser;
2364
-	}
2365
-
2366
-	public function setFormatter($name)
2367
-	{
2368
-		$this->formatterName = $name;
2369
-	}
2370
-
2371
-	protected function newFormatter()
2372
-	{
2373
-		$className = "lessc_formatter_lessjs";
2374
-		if (!empty($this->formatterName)) {
2375
-			if (!is_string($this->formatterName)) {
2376
-				return $this->formatterName;
2377
-			}
2378
-			$className = "lessc_formatter_$this->formatterName";
2379
-		}
2380
-
2381
-		return new $className();
2382
-	}
2383
-
2384
-	public function setPreserveComments($preserve)
2385
-	{
2386
-		$this->preserveComments = $preserve;
2387
-	}
2388
-
2389
-	public function registerFunction($name, $func)
2390
-	{
2391
-		$this->libFunctions[$name] = $func;
2392
-	}
2393
-
2394
-	public function unregisterFunction($name)
2395
-	{
2396
-		unset($this->libFunctions[$name]);
2397
-	}
2398
-
2399
-	public function setVariables($variables)
2400
-	{
2401
-		$this->registeredVars = array_merge($this->registeredVars, $variables);
2402
-	}
2403
-
2404
-	public function unsetVariable($name)
2405
-	{
2406
-		unset($this->registeredVars[$name]);
2407
-	}
2408
-
2409
-	public function setImportDir($dirs)
2410
-	{
2411
-		$this->importDir = (array) $dirs;
2412
-	}
2413
-
2414
-	public function addImportDir($dir)
2415
-	{
2416
-		$this->importDir = (array) $this->importDir;
2417
-		$this->importDir[] = $dir;
2418
-	}
2419
-
2420
-	public function allParsedFiles()
2421
-	{
2422
-		return $this->allParsedFiles;
2423
-	}
2424
-
2425
-	public function addParsedFile($file)
2426
-	{
2427
-		$this->allParsedFiles[realpath($file)] = filemtime($file);
2428
-	}
2429
-
2430
-	/**
2431
-	 * Uses the current value of $this->count to show line and line number
2432
-	 */
2433
-	public function throwError($msg = null)
2434
-	{
2435
-		if ($this->sourceLoc >= 0) {
2436
-			$this->sourceParser->throwError($msg, $this->sourceLoc);
2437
-		}
2438
-		throw new exception($msg);
2439
-	}
2440
-
2441
-	// compile file $in to file $out if $in is newer than $out
2442
-	// returns true when it compiles, false otherwise
2443
-	public static function ccompile($in, $out, $less = null)
2444
-	{
2445
-		if ($less === null) {
2446
-			$less = new self();
2447
-		}
2448
-		return $less->checkedCompile($in, $out);
2449
-	}
2450
-
2451
-	public static function cexecute($in, $force = false, $less = null)
2452
-	{
2453
-		if ($less === null) {
2454
-			$less = new self();
2455
-		}
2456
-		return $less->cachedCompile($in, $force);
2457
-	}
2458
-
2459
-	protected static $cssColors = array(
2460
-		'aliceblue' => '240,248,255',
2461
-		'antiquewhite' => '250,235,215',
2462
-		'aqua' => '0,255,255',
2463
-		'aquamarine' => '127,255,212',
2464
-		'azure' => '240,255,255',
2465
-		'beige' => '245,245,220',
2466
-		'bisque' => '255,228,196',
2467
-		'black' => '0,0,0',
2468
-		'blanchedalmond' => '255,235,205',
2469
-		'blue' => '0,0,255',
2470
-		'blueviolet' => '138,43,226',
2471
-		'brown' => '165,42,42',
2472
-		'burlywood' => '222,184,135',
2473
-		'cadetblue' => '95,158,160',
2474
-		'chartreuse' => '127,255,0',
2475
-		'chocolate' => '210,105,30',
2476
-		'coral' => '255,127,80',
2477
-		'cornflowerblue' => '100,149,237',
2478
-		'cornsilk' => '255,248,220',
2479
-		'crimson' => '220,20,60',
2480
-		'cyan' => '0,255,255',
2481
-		'darkblue' => '0,0,139',
2482
-		'darkcyan' => '0,139,139',
2483
-		'darkgoldenrod' => '184,134,11',
2484
-		'darkgray' => '169,169,169',
2485
-		'darkgreen' => '0,100,0',
2486
-		'darkgrey' => '169,169,169',
2487
-		'darkkhaki' => '189,183,107',
2488
-		'darkmagenta' => '139,0,139',
2489
-		'darkolivegreen' => '85,107,47',
2490
-		'darkorange' => '255,140,0',
2491
-		'darkorchid' => '153,50,204',
2492
-		'darkred' => '139,0,0',
2493
-		'darksalmon' => '233,150,122',
2494
-		'darkseagreen' => '143,188,143',
2495
-		'darkslateblue' => '72,61,139',
2496
-		'darkslategray' => '47,79,79',
2497
-		'darkslategrey' => '47,79,79',
2498
-		'darkturquoise' => '0,206,209',
2499
-		'darkviolet' => '148,0,211',
2500
-		'deeppink' => '255,20,147',
2501
-		'deepskyblue' => '0,191,255',
2502
-		'dimgray' => '105,105,105',
2503
-		'dimgrey' => '105,105,105',
2504
-		'dodgerblue' => '30,144,255',
2505
-		'firebrick' => '178,34,34',
2506
-		'floralwhite' => '255,250,240',
2507
-		'forestgreen' => '34,139,34',
2508
-		'fuchsia' => '255,0,255',
2509
-		'gainsboro' => '220,220,220',
2510
-		'ghostwhite' => '248,248,255',
2511
-		'gold' => '255,215,0',
2512
-		'goldenrod' => '218,165,32',
2513
-		'gray' => '128,128,128',
2514
-		'green' => '0,128,0',
2515
-		'greenyellow' => '173,255,47',
2516
-		'grey' => '128,128,128',
2517
-		'honeydew' => '240,255,240',
2518
-		'hotpink' => '255,105,180',
2519
-		'indianred' => '205,92,92',
2520
-		'indigo' => '75,0,130',
2521
-		'ivory' => '255,255,240',
2522
-		'khaki' => '240,230,140',
2523
-		'lavender' => '230,230,250',
2524
-		'lavenderblush' => '255,240,245',
2525
-		'lawngreen' => '124,252,0',
2526
-		'lemonchiffon' => '255,250,205',
2527
-		'lightblue' => '173,216,230',
2528
-		'lightcoral' => '240,128,128',
2529
-		'lightcyan' => '224,255,255',
2530
-		'lightgoldenrodyellow' => '250,250,210',
2531
-		'lightgray' => '211,211,211',
2532
-		'lightgreen' => '144,238,144',
2533
-		'lightgrey' => '211,211,211',
2534
-		'lightpink' => '255,182,193',
2535
-		'lightsalmon' => '255,160,122',
2536
-		'lightseagreen' => '32,178,170',
2537
-		'lightskyblue' => '135,206,250',
2538
-		'lightslategray' => '119,136,153',
2539
-		'lightslategrey' => '119,136,153',
2540
-		'lightsteelblue' => '176,196,222',
2541
-		'lightyellow' => '255,255,224',
2542
-		'lime' => '0,255,0',
2543
-		'limegreen' => '50,205,50',
2544
-		'linen' => '250,240,230',
2545
-		'magenta' => '255,0,255',
2546
-		'maroon' => '128,0,0',
2547
-		'mediumaquamarine' => '102,205,170',
2548
-		'mediumblue' => '0,0,205',
2549
-		'mediumorchid' => '186,85,211',
2550
-		'mediumpurple' => '147,112,219',
2551
-		'mediumseagreen' => '60,179,113',
2552
-		'mediumslateblue' => '123,104,238',
2553
-		'mediumspringgreen' => '0,250,154',
2554
-		'mediumturquoise' => '72,209,204',
2555
-		'mediumvioletred' => '199,21,133',
2556
-		'midnightblue' => '25,25,112',
2557
-		'mintcream' => '245,255,250',
2558
-		'mistyrose' => '255,228,225',
2559
-		'moccasin' => '255,228,181',
2560
-		'navajowhite' => '255,222,173',
2561
-		'navy' => '0,0,128',
2562
-		'oldlace' => '253,245,230',
2563
-		'olive' => '128,128,0',
2564
-		'olivedrab' => '107,142,35',
2565
-		'orange' => '255,165,0',
2566
-		'orangered' => '255,69,0',
2567
-		'orchid' => '218,112,214',
2568
-		'palegoldenrod' => '238,232,170',
2569
-		'palegreen' => '152,251,152',
2570
-		'paleturquoise' => '175,238,238',
2571
-		'palevioletred' => '219,112,147',
2572
-		'papayawhip' => '255,239,213',
2573
-		'peachpuff' => '255,218,185',
2574
-		'peru' => '205,133,63',
2575
-		'pink' => '255,192,203',
2576
-		'plum' => '221,160,221',
2577
-		'powderblue' => '176,224,230',
2578
-		'purple' => '128,0,128',
2579
-		'red' => '255,0,0',
2580
-		'rosybrown' => '188,143,143',
2581
-		'royalblue' => '65,105,225',
2582
-		'saddlebrown' => '139,69,19',
2583
-		'salmon' => '250,128,114',
2584
-		'sandybrown' => '244,164,96',
2585
-		'seagreen' => '46,139,87',
2586
-		'seashell' => '255,245,238',
2587
-		'sienna' => '160,82,45',
2588
-		'silver' => '192,192,192',
2589
-		'skyblue' => '135,206,235',
2590
-		'slateblue' => '106,90,205',
2591
-		'slategray' => '112,128,144',
2592
-		'slategrey' => '112,128,144',
2593
-		'snow' => '255,250,250',
2594
-		'springgreen' => '0,255,127',
2595
-		'steelblue' => '70,130,180',
2596
-		'tan' => '210,180,140',
2597
-		'teal' => '0,128,128',
2598
-		'thistle' => '216,191,216',
2599
-		'tomato' => '255,99,71',
2600
-		'transparent' => '0,0,0,0',
2601
-		'turquoise' => '64,224,208',
2602
-		'violet' => '238,130,238',
2603
-		'wheat' => '245,222,179',
2604
-		'white' => '255,255,255',
2605
-		'whitesmoke' => '245,245,245',
2606
-		'yellow' => '255,255,0',
2607
-		'yellowgreen' => '154,205,50'
2608
-	);
43
+    public static $VERSION = "v0.8.0";
44
+
45
+    public static $TRUE = array("keyword", "true");
46
+    public static $FALSE = array("keyword", "false");
47
+
48
+    protected $libFunctions = array();
49
+    protected $registeredVars = array();
50
+    protected $preserveComments = false;
51
+
52
+    public $vPrefix = '@'; // prefix of abstract properties
53
+    public $mPrefix = '$'; // prefix of abstract blocks
54
+    public $parentSelector = '&';
55
+
56
+    public $importDisabled = false;
57
+    public $importDir = '';
58
+
59
+    public $scope;
60
+    public $formatter;
61
+    public $formatterName;
62
+    public $parser;
63
+    public $_parseFile;
64
+    public $env;
65
+    public $count;
66
+
67
+    protected $numberPrecision = null;
68
+
69
+    protected $allParsedFiles = array();
70
+
71
+    // set to the parser that generated the current line when compiling
72
+    // so we know how to create error messages
73
+    protected $sourceParser = null;
74
+    protected $sourceLoc = null;
75
+
76
+    protected static $nextImportId = 0; // uniquely identify imports
77
+
78
+    // attempts to find the path of an import url, returns null for css files
79
+    protected function findImport($url)
80
+    {
81
+        foreach ((array) $this->importDir as $dir) {
82
+            $full = $dir.(substr($dir, -1) != '/' ? '/' : '').$url;
83
+            if ($this->fileExists($file = $full.'.less') || $this->fileExists($file = $full)) {
84
+                return $file;
85
+            }
86
+        }
87
+
88
+        return null;
89
+    }
90
+
91
+    /**
92
+     * fileExists
93
+     *
94
+     * @param       string  $name   Filename
95
+     * @return      boolean
96
+     */
97
+    protected function fileExists($name)
98
+    {
99
+        return is_file($name);
100
+    }
101
+
102
+    public static function compressList($items, $delim)
103
+    {
104
+        if (!isset($items[1]) && isset($items[0])) {
105
+            return $items[0];
106
+        } else {
107
+            return array('list', $delim, $items);
108
+        }
109
+    }
110
+
111
+    public static function preg_quote($what)
112
+    {
113
+        return preg_quote($what, '/');
114
+    }
115
+
116
+    protected function tryImport($importPath, $parentBlock, $out)
117
+    {
118
+        if ($importPath[0] == "function" && $importPath[1] == "url") {
119
+            $importPath = $this->flattenList($importPath[2]);
120
+        }
121
+
122
+        $str = $this->coerceString($importPath);
123
+        if ($str === null) {
124
+            return false;
125
+        }
126
+
127
+        $url = $this->compileValue($this->lib_e($str));
128
+
129
+        // don't import if it ends in css
130
+        if (substr_compare($url, '.css', -4, 4) === 0) {
131
+            return false;
132
+        }
133
+
134
+        $realPath = $this->findImport($url);
135
+
136
+        if ($realPath === null) {
137
+            return false;
138
+        }
139
+
140
+        if ($this->importDisabled) {
141
+            return array(false, "/* import disabled */");
142
+        }
143
+
144
+        if (isset($this->allParsedFiles[realpath($realPath)])) {
145
+            return array(false, null);
146
+        }
147
+
148
+        $this->addParsedFile($realPath);
149
+        $parser = $this->makeParser($realPath);
150
+        $root = $parser->parse(file_get_contents($realPath));
151
+
152
+        // set the parents of all the block props
153
+        foreach ($root->props as $prop) {
154
+            if ($prop[0] == "block") {
155
+                $prop[1]->parent = $parentBlock;
156
+            }
157
+        }
158
+
159
+        // copy mixins into scope, set their parents
160
+        // bring blocks from import into current block
161
+        // TODO: need to mark the source parser these came from this file
162
+        foreach ($root->children as $childName => $child) {
163
+            if (isset($parentBlock->children[$childName])) {
164
+                $parentBlock->children[$childName] = array_merge(
165
+                    $parentBlock->children[$childName],
166
+                    $child
167
+                );
168
+            } else {
169
+                $parentBlock->children[$childName] = $child;
170
+            }
171
+        }
172
+
173
+        $pi = pathinfo($realPath);
174
+        $dir = $pi["dirname"];
175
+
176
+        list($top, $bottom) = $this->sortProps($root->props, true);
177
+        $this->compileImportedProps($top, $parentBlock, $out, $parser, $dir);
178
+
179
+        return array(true, $bottom, $parser, $dir);
180
+    }
181
+
182
+    protected function compileImportedProps($props, $block, $out, $sourceParser, $importDir)
183
+    {
184
+        $oldSourceParser = $this->sourceParser;
185
+
186
+        $oldImport = $this->importDir;
187
+
188
+        // TODO: this is because the importDir api is stupid
189
+        $this->importDir = (array) $this->importDir;
190
+        array_unshift($this->importDir, $importDir);
191
+
192
+        foreach ($props as $prop) {
193
+            $this->compileProp($prop, $block, $out);
194
+        }
195
+
196
+        $this->importDir = $oldImport;
197
+        $this->sourceParser = $oldSourceParser;
198
+    }
199
+
200
+    /**
201
+     * Recursively compiles a block.
202
+     *
203
+     * A block is analogous to a CSS block in most cases. A single LESS document
204
+     * is encapsulated in a block when parsed, but it does not have parent tags
205
+     * so all of it's children appear on the root level when compiled.
206
+     *
207
+     * Blocks are made up of props and children.
208
+     *
209
+     * Props are property instructions, array tuples which describe an action
210
+     * to be taken, eg. write a property, set a variable, mixin a block.
211
+     *
212
+     * The children of a block are just all the blocks that are defined within.
213
+     * This is used to look up mixins when performing a mixin.
214
+     *
215
+     * Compiling the block involves pushing a fresh environment on the stack,
216
+     * and iterating through the props, compiling each one.
217
+     *
218
+     * See Lessc::compileProp()
219
+     *
220
+     */
221
+    protected function compileBlock($block)
222
+    {
223
+        switch ($block->type) {
224
+            case "root":
225
+                $this->compileRoot($block);
226
+                break;
227
+            case null:
228
+                $this->compileCSSBlock($block);
229
+                break;
230
+            case "media":
231
+                $this->compileMedia($block);
232
+                break;
233
+            case "directive":
234
+                $name = "@".$block->name;
235
+                if (!empty($block->value)) {
236
+                    $name .= " ".$this->compileValue($this->reduce($block->value));
237
+                }
238
+
239
+                $this->compileNestedBlock($block, array($name));
240
+                break;
241
+            default:
242
+                $this->throwError("unknown block type: $block->type\n");
243
+        }
244
+    }
245
+
246
+    protected function compileCSSBlock($block)
247
+    {
248
+        $env = $this->pushEnv();
249
+
250
+        $selectors = $this->compileSelectors($block->tags);
251
+        $env->selectors = $this->multiplySelectors($selectors);
252
+        $out = $this->makeOutputBlock(null, $env->selectors);
253
+
254
+        $this->scope->children[] = $out;
255
+        $this->compileProps($block, $out);
256
+
257
+        $block->scope = $env; // mixins carry scope with them!
258
+        $this->popEnv();
259
+    }
260
+
261
+    protected function compileMedia($media)
262
+    {
263
+        $env = $this->pushEnv($media);
264
+        $parentScope = $this->mediaParent($this->scope);
265
+
266
+        $query = $this->compileMediaQuery($this->multiplyMedia($env));
267
+
268
+        $this->scope = $this->makeOutputBlock($media->type, array($query));
269
+        $parentScope->children[] = $this->scope;
270
+
271
+        $this->compileProps($media, $this->scope);
272
+
273
+        if (count($this->scope->lines) > 0) {
274
+            $orphanSelelectors = $this->findClosestSelectors();
275
+            if (!is_null($orphanSelelectors)) {
276
+                $orphan = $this->makeOutputBlock(null, $orphanSelelectors);
277
+                $orphan->lines = $this->scope->lines;
278
+                array_unshift($this->scope->children, $orphan);
279
+                $this->scope->lines = array();
280
+            }
281
+        }
282
+
283
+        $this->scope = $this->scope->parent;
284
+        $this->popEnv();
285
+    }
286
+
287
+    protected function mediaParent($scope)
288
+    {
289
+        while (!empty($scope->parent)) {
290
+            if (!empty($scope->type) && $scope->type != "media") {
291
+                break;
292
+            }
293
+            $scope = $scope->parent;
294
+        }
295
+
296
+        return $scope;
297
+    }
298
+
299
+    protected function compileNestedBlock($block, $selectors)
300
+    {
301
+        $this->pushEnv($block);
302
+        $this->scope = $this->makeOutputBlock($block->type, $selectors);
303
+        $this->scope->parent->children[] = $this->scope;
304
+
305
+        $this->compileProps($block, $this->scope);
306
+
307
+        $this->scope = $this->scope->parent;
308
+        $this->popEnv();
309
+    }
310
+
311
+    protected function compileRoot($root)
312
+    {
313
+        $this->pushEnv();
314
+        $this->scope = $this->makeOutputBlock($root->type);
315
+        $this->compileProps($root, $this->scope);
316
+        $this->popEnv();
317
+    }
318
+
319
+    protected function compileProps($block, $out)
320
+    {
321
+        foreach ($this->sortProps($block->props) as $prop) {
322
+            $this->compileProp($prop, $block, $out);
323
+        }
324
+        $out->lines = $this->deduplicate($out->lines);
325
+    }
326
+
327
+    /**
328
+     * Deduplicate lines in a block. Comments are not deduplicated. If a
329
+     * duplicate rule is detected, the comments immediately preceding each
330
+     * occurence are consolidated.
331
+     */
332
+    protected function deduplicate($lines)
333
+    {
334
+        $unique = array();
335
+        $comments = array();
336
+
337
+        foreach ($lines as $line) {
338
+            if (strpos($line, '/*') === 0) {
339
+                $comments[] = $line;
340
+                continue;
341
+            }
342
+            if (!in_array($line, $unique)) {
343
+                $unique[] = $line;
344
+            }
345
+            array_splice($unique, array_search($line, $unique), 0, $comments);
346
+            $comments = array();
347
+        }
348
+        return array_merge($unique, $comments);
349
+    }
350
+
351
+    protected function sortProps($props, $split = false)
352
+    {
353
+        $vars = array();
354
+        $imports = array();
355
+        $other = array();
356
+        $stack = array();
357
+
358
+        foreach ($props as $prop) {
359
+            switch ($prop[0]) {
360
+                case "comment":
361
+                    $stack[] = $prop;
362
+                    break;
363
+                case "assign":
364
+                    $stack[] = $prop;
365
+                    if (isset($prop[1][0]) && $prop[1][0] == $this->vPrefix) {
366
+                        $vars = array_merge($vars, $stack);
367
+                    } else {
368
+                        $other = array_merge($other, $stack);
369
+                    }
370
+                    $stack = array();
371
+                    break;
372
+                case "import":
373
+                    $id = self::$nextImportId++;
374
+                    $prop[] = $id;
375
+                    $stack[] = $prop;
376
+                    $imports = array_merge($imports, $stack);
377
+                    $other[] = array("import_mixin", $id);
378
+                    $stack = array();
379
+                    break;
380
+                default:
381
+                    $stack[] = $prop;
382
+                    $other = array_merge($other, $stack);
383
+                    $stack = array();
384
+                    break;
385
+            }
386
+        }
387
+        $other = array_merge($other, $stack);
388
+
389
+        if ($split) {
390
+            return array(array_merge($imports, $vars), $other);
391
+        } else {
392
+            return array_merge($imports, $vars, $other);
393
+        }
394
+    }
395
+
396
+    protected function compileMediaQuery($queries)
397
+    {
398
+        $compiledQueries = array();
399
+        foreach ($queries as $query) {
400
+            $parts = array();
401
+            foreach ($query as $q) {
402
+                switch ($q[0]) {
403
+                    case "mediaType":
404
+                        $parts[] = implode(" ", array_slice($q, 1));
405
+                        break;
406
+                    case "mediaExp":
407
+                        if (isset($q[2])) {
408
+                            $parts[] = "($q[1]: ".
409
+                                $this->compileValue($this->reduce($q[2])).")";
410
+                        } else {
411
+                            $parts[] = "($q[1])";
412
+                        }
413
+                        break;
414
+                    case "variable":
415
+                        $parts[] = $this->compileValue($this->reduce($q));
416
+                        break;
417
+                }
418
+            }
419
+
420
+            if (count($parts) > 0) {
421
+                $compiledQueries[] = implode(" and ", $parts);
422
+            }
423
+        }
424
+
425
+        $out = "@media";
426
+        if (!empty($parts)) {
427
+            $out .= " ".
428
+                implode($this->formatter->selectorSeparator, $compiledQueries);
429
+        }
430
+        return $out;
431
+    }
432
+
433
+    protected function multiplyMedia($env, $childQueries = null)
434
+    {
435
+        if (is_null($env) ||
436
+            !empty($env->block->type) && $env->block->type != "media"
437
+        ) {
438
+            return $childQueries;
439
+        }
440
+
441
+        // plain old block, skip
442
+        if (empty($env->block->type)) {
443
+            return $this->multiplyMedia($env->parent, $childQueries);
444
+        }
445
+
446
+        $out = array();
447
+        $queries = $env->block->queries;
448
+        if (is_null($childQueries)) {
449
+            $out = $queries;
450
+        } else {
451
+            foreach ($queries as $parent) {
452
+                foreach ($childQueries as $child) {
453
+                    $out[] = array_merge($parent, $child);
454
+                }
455
+            }
456
+        }
457
+
458
+        return $this->multiplyMedia($env->parent, $out);
459
+    }
460
+
461
+    protected function expandParentSelectors(&$tag, $replace)
462
+    {
463
+        $parts = explode("$&$", $tag);
464
+        $count = 0;
465
+        foreach ($parts as &$part) {
466
+            $c = 0;
467
+            $part = str_replace($this->parentSelector, $replace, $part, $c);
468
+            $count += $c;
469
+        }
470
+        $tag = implode($this->parentSelector, $parts);
471
+        return $count;
472
+    }
473
+
474
+    protected function findClosestSelectors()
475
+    {
476
+        $env = $this->env;
477
+        $selectors = null;
478
+        while ($env !== null) {
479
+            if (isset($env->selectors)) {
480
+                $selectors = $env->selectors;
481
+                break;
482
+            }
483
+            $env = $env->parent;
484
+        }
485
+
486
+        return $selectors;
487
+    }
488
+
489
+
490
+    // multiply $selectors against the nearest selectors in env
491
+    protected function multiplySelectors($selectors)
492
+    {
493
+        // find parent selectors
494
+
495
+        $parentSelectors = $this->findClosestSelectors();
496
+        if (is_null($parentSelectors)) {
497
+            // kill parent reference in top level selector
498
+            foreach ($selectors as &$s) {
499
+                $this->expandParentSelectors($s, "");
500
+            }
501
+
502
+            return $selectors;
503
+        }
504
+
505
+        $out = array();
506
+        foreach ($parentSelectors as $parent) {
507
+            foreach ($selectors as $child) {
508
+                $count = $this->expandParentSelectors($child, $parent);
509
+
510
+                // don't prepend the parent tag if & was used
511
+                if ($count > 0) {
512
+                    $out[] = trim($child);
513
+                } else {
514
+                    $out[] = trim($parent.' '.$child);
515
+                }
516
+            }
517
+        }
518
+
519
+        return $out;
520
+    }
521
+
522
+    // reduces selector expressions
523
+    protected function compileSelectors($selectors)
524
+    {
525
+        $out = array();
526
+
527
+        foreach ($selectors as $s) {
528
+            if (is_array($s)) {
529
+                list(, $value) = $s;
530
+                $out[] = trim($this->compileValue($this->reduce($value)));
531
+            } else {
532
+                $out[] = $s;
533
+            }
534
+        }
535
+
536
+        return $out;
537
+    }
538
+
539
+    protected function eq($left, $right)
540
+    {
541
+        return $left == $right;
542
+    }
543
+
544
+    protected function patternMatch($block, $orderedArgs, $keywordArgs)
545
+    {
546
+        // match the guards if it has them
547
+        // any one of the groups must have all its guards pass for a match
548
+        if (!empty($block->guards)) {
549
+            $groupPassed = false;
550
+            foreach ($block->guards as $guardGroup) {
551
+                foreach ($guardGroup as $guard) {
552
+                    $this->pushEnv();
553
+                    $this->zipSetArgs($block->args, $orderedArgs, $keywordArgs);
554
+
555
+                    $negate = false;
556
+                    if ($guard[0] == "negate") {
557
+                        $guard = $guard[1];
558
+                        $negate = true;
559
+                    }
560
+
561
+                    $passed = $this->reduce($guard) == self::$TRUE;
562
+                    if ($negate) {
563
+                        $passed = !$passed;
564
+                    }
565
+
566
+                    $this->popEnv();
567
+
568
+                    if ($passed) {
569
+                        $groupPassed = true;
570
+                    } else {
571
+                        $groupPassed = false;
572
+                        break;
573
+                    }
574
+                }
575
+
576
+                if ($groupPassed) {
577
+                    break;
578
+                }
579
+            }
580
+
581
+            if (!$groupPassed) {
582
+                return false;
583
+            }
584
+        }
585
+
586
+        if (empty($block->args)) {
587
+            return $block->isVararg || empty($orderedArgs) && empty($keywordArgs);
588
+        }
589
+
590
+        $remainingArgs = $block->args;
591
+        if ($keywordArgs) {
592
+            $remainingArgs = array();
593
+            foreach ($block->args as $arg) {
594
+                if ($arg[0] == "arg" && isset($keywordArgs[$arg[1]])) {
595
+                    continue;
596
+                }
597
+
598
+                $remainingArgs[] = $arg;
599
+            }
600
+        }
601
+
602
+        $i = -1; // no args
603
+        // try to match by arity or by argument literal
604
+        foreach ($remainingArgs as $i => $arg) {
605
+            switch ($arg[0]) {
606
+                case "lit":
607
+                    if (empty($orderedArgs[$i]) || !$this->eq($arg[1], $orderedArgs[$i])) {
608
+                        return false;
609
+                    }
610
+                    break;
611
+                case "arg":
612
+                    // no arg and no default value
613
+                    if (!isset($orderedArgs[$i]) && !isset($arg[2])) {
614
+                        return false;
615
+                    }
616
+                    break;
617
+                case "rest":
618
+                    $i--; // rest can be empty
619
+                    break 2;
620
+            }
621
+        }
622
+
623
+        if ($block->isVararg) {
624
+            return true; // not having enough is handled above
625
+        } else {
626
+            $numMatched = $i + 1;
627
+            // greater than because default values always match
628
+            return $numMatched >= count($orderedArgs);
629
+        }
630
+    }
631
+
632
+    protected function patternMatchAll($blocks, $orderedArgs, $keywordArgs, $skip = array())
633
+    {
634
+        $matches = null;
635
+        foreach ($blocks as $block) {
636
+            // skip seen blocks that don't have arguments
637
+            if (isset($skip[$block->id]) && !isset($block->args)) {
638
+                continue;
639
+            }
640
+
641
+            if ($this->patternMatch($block, $orderedArgs, $keywordArgs)) {
642
+                $matches[] = $block;
643
+            }
644
+        }
645
+
646
+        return $matches;
647
+    }
648
+
649
+    // attempt to find blocks matched by path and args
650
+    protected function findBlocks($searchIn, $path, $orderedArgs, $keywordArgs, $seen = array())
651
+    {
652
+        if ($searchIn == null) {
653
+            return null;
654
+        }
655
+        if (isset($seen[$searchIn->id])) {
656
+            return null;
657
+        }
658
+        $seen[$searchIn->id] = true;
659
+
660
+        $name = $path[0];
661
+
662
+        if (isset($searchIn->children[$name])) {
663
+            $blocks = $searchIn->children[$name];
664
+            if (count($path) == 1) {
665
+                $matches = $this->patternMatchAll($blocks, $orderedArgs, $keywordArgs, $seen);
666
+                if (!empty($matches)) {
667
+                    // This will return all blocks that match in the closest
668
+                    // scope that has any matching block, like lessjs
669
+                    return $matches;
670
+                }
671
+            } else {
672
+                $matches = array();
673
+                foreach ($blocks as $subBlock) {
674
+                    $subMatches = $this->findBlocks(
675
+                        $subBlock,
676
+                        array_slice($path, 1),
677
+                        $orderedArgs,
678
+                        $keywordArgs,
679
+                        $seen
680
+                    );
681
+
682
+                    if (!is_null($subMatches)) {
683
+                        foreach ($subMatches as $sm) {
684
+                            $matches[] = $sm;
685
+                        }
686
+                    }
687
+                }
688
+
689
+                return count($matches) > 0 ? $matches : null;
690
+            }
691
+        }
692
+        if ($searchIn->parent === $searchIn) {
693
+            return null;
694
+        }
695
+        return $this->findBlocks($searchIn->parent, $path, $orderedArgs, $keywordArgs, $seen);
696
+    }
697
+
698
+    // sets all argument names in $args to either the default value
699
+    // or the one passed in through $values
700
+    protected function zipSetArgs($args, $orderedValues, $keywordValues)
701
+    {
702
+        $assignedValues = array();
703
+
704
+        $i = 0;
705
+        foreach ($args as $a) {
706
+            if ($a[0] == "arg") {
707
+                if (isset($keywordValues[$a[1]])) {
708
+                    // has keyword arg
709
+                    $value = $keywordValues[$a[1]];
710
+                } elseif (isset($orderedValues[$i])) {
711
+                    // has ordered arg
712
+                    $value = $orderedValues[$i];
713
+                    $i++;
714
+                } elseif (isset($a[2])) {
715
+                    // has default value
716
+                    $value = $a[2];
717
+                } else {
718
+                    $value = null; // :(
719
+                    $this->throwError("Failed to assign arg ".$a[1]); // This ends function by throwing an exception
720
+                }
721
+
722
+                $value = $this->reduce($value);
723
+                $this->set($a[1], $value);
724
+                $assignedValues[] = $value;
725
+            } else {
726
+                // a lit
727
+                $i++;
728
+            }
729
+        }
730
+
731
+        // check for a rest
732
+        $last = end($args);
733
+        if ($last && $last[0] == "rest") {
734
+            $rest = array_slice($orderedValues, count($args) - 1);
735
+            $this->set($last[1], $this->reduce(array("list", " ", $rest)));
736
+        }
737
+
738
+        // wow is this the only true use of PHP's + operator for arrays?
739
+        $this->env->arguments = $assignedValues + $orderedValues;
740
+    }
741
+
742
+    // compile a prop and update $lines or $blocks appropriately
743
+    protected function compileProp($prop, $block, $out)
744
+    {
745
+        // set error position context
746
+        $this->sourceLoc = isset($prop[-1]) ? $prop[-1] : -1;
747
+
748
+        switch ($prop[0]) {
749
+            case 'assign':
750
+                list(, $name, $value) = $prop;
751
+                if ($name[0] == $this->vPrefix) {
752
+                    $this->set($name, $value);
753
+                } else {
754
+                    $out->lines[] = $this->formatter->property(
755
+                        $name,
756
+                        $this->compileValue($this->reduce($value))
757
+                    );
758
+                }
759
+                break;
760
+            case 'block':
761
+                list(, $child) = $prop;
762
+                $this->compileBlock($child);
763
+                break;
764
+            case 'mixin':
765
+                list(, $path, $args, $suffix) = $prop;
766
+
767
+                $orderedArgs = array();
768
+                $keywordArgs = array();
769
+                foreach ((array) $args as $arg) {
770
+                    $argval = null;
771
+                    switch ($arg[0]) {
772
+                        case "arg":
773
+                            if (!isset($arg[2])) {
774
+                                $orderedArgs[] = $this->reduce(array("variable", $arg[1]));
775
+                            } else {
776
+                                $keywordArgs[$arg[1]] = $this->reduce($arg[2]);
777
+                            }
778
+                            break;
779
+
780
+                        case "lit":
781
+                            $orderedArgs[] = $this->reduce($arg[1]);
782
+                            break;
783
+                        default:
784
+                            $this->throwError("Unknown arg type: ".$arg[0]);
785
+                    }
786
+                }
787
+
788
+                $mixins = $this->findBlocks($block, $path, $orderedArgs, $keywordArgs);
789
+
790
+                if ($mixins === null) {
791
+                    $this->throwError("{$prop[1][0]} is undefined");
792
+                }
793
+
794
+                foreach ($mixins as $mixin) {
795
+                    if ($mixin === $block && !$orderedArgs) {
796
+                        continue;
797
+                    }
798
+
799
+                    $haveScope = false;
800
+                    if (isset($mixin->parent->scope)) {
801
+                        $haveScope = true;
802
+                        $mixinParentEnv = $this->pushEnv();
803
+                        $mixinParentEnv->storeParent = $mixin->parent->scope;
804
+                    }
805
+
806
+                    $haveArgs = false;
807
+                    if (isset($mixin->args)) {
808
+                        $haveArgs = true;
809
+                        $this->pushEnv();
810
+                        $this->zipSetArgs($mixin->args, $orderedArgs, $keywordArgs);
811
+                    }
812
+
813
+                    $oldParent = $mixin->parent;
814
+                    if ($mixin != $block) {
815
+                        $mixin->parent = $block;
816
+                    }
817
+
818
+                    foreach ($this->sortProps($mixin->props) as $subProp) {
819
+                        if ($suffix !== null &&
820
+                            $subProp[0] == "assign" &&
821
+                            is_string($subProp[1]) &&
822
+                            $subProp[1][0] != $this->vPrefix
823
+                        ) {
824
+                            $subProp[2] = array(
825
+                                'list', ' ',
826
+                                array($subProp[2], array('keyword', $suffix))
827
+                            );
828
+                        }
829
+
830
+                        $this->compileProp($subProp, $mixin, $out);
831
+                    }
832
+
833
+                    $mixin->parent = $oldParent;
834
+
835
+                    if ($haveArgs) {
836
+                        $this->popEnv();
837
+                    }
838
+                    if ($haveScope) {
839
+                        $this->popEnv();
840
+                    }
841
+                }
842
+
843
+                break;
844
+            case 'raw':
845
+                $out->lines[] = $prop[1];
846
+                break;
847
+            case "directive":
848
+                list(, $name, $value) = $prop;
849
+                $out->lines[] = "@$name ".$this->compileValue($this->reduce($value)).';';
850
+                break;
851
+            case "comment":
852
+                $out->lines[] = $prop[1];
853
+                break;
854
+            case "import":
855
+                list(, $importPath, $importId) = $prop;
856
+                $importPath = $this->reduce($importPath);
857
+
858
+                if (!isset($this->env->imports)) {
859
+                    $this->env->imports = array();
860
+                }
861
+
862
+                $result = $this->tryImport($importPath, $block, $out);
863
+
864
+                $this->env->imports[$importId] = $result === false ?
865
+                    array(false, "@import ".$this->compileValue($importPath).";") : $result;
866
+
867
+                break;
868
+            case "import_mixin":
869
+                list(, $importId) = $prop;
870
+                $import = $this->env->imports[$importId];
871
+                if ($import[0] === false) {
872
+                    if (isset($import[1])) {
873
+                        $out->lines[] = $import[1];
874
+                    }
875
+                } else {
876
+                    list(, $bottom, $parser, $importDir) = $import;
877
+                    $this->compileImportedProps($bottom, $block, $out, $parser, $importDir);
878
+                }
879
+
880
+                break;
881
+            default:
882
+                $this->throwError("unknown op: {$prop[0]}\n");
883
+        }
884
+    }
885
+
886
+
887
+    /**
888
+     * Compiles a primitive value into a CSS property value.
889
+     *
890
+     * Values in lessphp are typed by being wrapped in arrays, their format is
891
+     * typically:
892
+     *
893
+     *     array(type, contents [, additional_contents]*)
894
+     *
895
+     * The input is expected to be reduced. This function will not work on
896
+     * things like expressions and variables.
897
+     */
898
+    public function compileValue($value)
899
+    {
900
+        switch ($value[0]) {
901
+            case 'list':
902
+                // [1] - delimiter
903
+                // [2] - array of values
904
+                return implode($value[1], array_map(array($this, 'compileValue'), $value[2]));
905
+            case 'raw_color':
906
+                if (!empty($this->formatter->compressColors)) {
907
+                    return $this->compileValue($this->coerceColor($value));
908
+                }
909
+                return $value[1];
910
+            case 'keyword':
911
+                // [1] - the keyword
912
+                return $value[1];
913
+            case 'number':
914
+                list(, $num, $unit) = $value;
915
+                // [1] - the number
916
+                // [2] - the unit
917
+                if ($this->numberPrecision !== null) {
918
+                    $num = round($num, $this->numberPrecision);
919
+                }
920
+                return $num.$unit;
921
+            case 'string':
922
+                // [1] - contents of string (includes quotes)
923
+                list(, $delim, $content) = $value;
924
+                foreach ($content as &$part) {
925
+                    if (is_array($part)) {
926
+                        $part = $this->compileValue($part);
927
+                    }
928
+                }
929
+                return $delim.implode($content).$delim;
930
+            case 'color':
931
+                // [1] - red component (either number or a %)
932
+                // [2] - green component
933
+                // [3] - blue component
934
+                // [4] - optional alpha component
935
+                list(, $r, $g, $b) = $value;
936
+                $r = round($r);
937
+                $g = round($g);
938
+                $b = round($b);
939
+
940
+                if (count($value) == 5 && $value[4] != 1) { // rgba
941
+                    return 'rgba('.$r.','.$g.','.$b.','.$value[4].')';
942
+                }
943
+
944
+                $h = sprintf("#%02x%02x%02x", $r, $g, $b);
945
+
946
+                if (!empty($this->formatter->compressColors)) {
947
+                    // Converting hex color to short notation (e.g. #003399 to #039)
948
+                    if ($h[1] === $h[2] && $h[3] === $h[4] && $h[5] === $h[6]) {
949
+                        $h = '#'.$h[1].$h[3].$h[5];
950
+                    }
951
+                }
952
+
953
+                return $h;
954
+
955
+            case 'function':
956
+                list(, $name, $args) = $value;
957
+                return $name.'('.$this->compileValue($args).')';
958
+            default: // assumed to be unit
959
+                $this->throwError("unknown value type: $value[0]");
960
+        }
961
+    }
962
+
963
+    protected function lib_pow($args)
964
+    {
965
+        list($base, $exp) = $this->assertArgs($args, 2, "pow");
966
+        return pow($this->assertNumber($base), $this->assertNumber($exp));
967
+    }
968
+
969
+    protected function lib_pi()
970
+    {
971
+        return pi();
972
+    }
973
+
974
+    protected function lib_mod($args)
975
+    {
976
+        list($a, $b) = $this->assertArgs($args, 2, "mod");
977
+        return $this->assertNumber($a) % $this->assertNumber($b);
978
+    }
979
+
980
+    protected function lib_tan($num)
981
+    {
982
+        return tan($this->assertNumber($num));
983
+    }
984
+
985
+    protected function lib_sin($num)
986
+    {
987
+        return sin($this->assertNumber($num));
988
+    }
989
+
990
+    protected function lib_cos($num)
991
+    {
992
+        return cos($this->assertNumber($num));
993
+    }
994
+
995
+    protected function lib_atan($num)
996
+    {
997
+        $num = atan($this->assertNumber($num));
998
+        return array("number", $num, "rad");
999
+    }
1000
+
1001
+    protected function lib_asin($num)
1002
+    {
1003
+        $num = asin($this->assertNumber($num));
1004
+        return array("number", $num, "rad");
1005
+    }
1006
+
1007
+    protected function lib_acos($num)
1008
+    {
1009
+        $num = acos($this->assertNumber($num));
1010
+        return array("number", $num, "rad");
1011
+    }
1012
+
1013
+    protected function lib_sqrt($num)
1014
+    {
1015
+        return sqrt($this->assertNumber($num));
1016
+    }
1017
+
1018
+    protected function lib_extract($value)
1019
+    {
1020
+        list($list, $idx) = $this->assertArgs($value, 2, "extract");
1021
+        $idx = $this->assertNumber($idx);
1022
+        // 1 indexed
1023
+        if ($list[0] == "list" && isset($list[2][$idx - 1])) {
1024
+            return $list[2][$idx - 1];
1025
+        }
1026
+        return '';
1027
+    }
1028
+
1029
+    protected function lib_isnumber($value)
1030
+    {
1031
+        return $this->toBool($value[0] == "number");
1032
+    }
1033
+
1034
+    protected function lib_isstring($value)
1035
+    {
1036
+        return $this->toBool($value[0] == "string");
1037
+    }
1038
+
1039
+    protected function lib_iscolor($value)
1040
+    {
1041
+        return $this->toBool($this->coerceColor($value));
1042
+    }
1043
+
1044
+    protected function lib_iskeyword($value)
1045
+    {
1046
+        return $this->toBool($value[0] == "keyword");
1047
+    }
1048
+
1049
+    protected function lib_ispixel($value)
1050
+    {
1051
+        return $this->toBool($value[0] == "number" && $value[2] == "px");
1052
+    }
1053
+
1054
+    protected function lib_ispercentage($value)
1055
+    {
1056
+        return $this->toBool($value[0] == "number" && $value[2] == "%");
1057
+    }
1058
+
1059
+    protected function lib_isem($value)
1060
+    {
1061
+        return $this->toBool($value[0] == "number" && $value[2] == "em");
1062
+    }
1063
+
1064
+    protected function lib_isrem($value)
1065
+    {
1066
+        return $this->toBool($value[0] == "number" && $value[2] == "rem");
1067
+    }
1068
+
1069
+    protected function lib_rgbahex($color)
1070
+    {
1071
+        $color = $this->coerceColor($color);
1072
+        if (is_null($color)) {
1073
+            $this->throwError("color expected for rgbahex");
1074
+        }
1075
+
1076
+        return sprintf(
1077
+            "#%02x%02x%02x%02x",
1078
+            isset($color[4]) ? $color[4] * 255 : 255,
1079
+            $color[1],
1080
+            $color[2],
1081
+            $color[3]
1082
+        );
1083
+    }
1084
+
1085
+    protected function lib_argb($color)
1086
+    {
1087
+        return $this->lib_rgbahex($color);
1088
+    }
1089
+
1090
+    /**
1091
+     * Given an url, decide whether to output a regular link or the base64-encoded contents of the file
1092
+     *
1093
+     * @param  array  $value either an argument list (two strings) or a single string
1094
+     * @return string        formatted url(), either as a link or base64-encoded
1095
+     */
1096
+    protected function lib_data_uri($value)
1097
+    {
1098
+        $mime = ($value[0] === 'list') ? $value[2][0][2] : null;
1099
+        $url = ($value[0] === 'list') ? $value[2][1][2][0] : $value[2][0];
1100
+
1101
+        $fullpath = $this->findImport($url);
1102
+
1103
+        if ($fullpath && ($fsize = filesize($fullpath)) !== false) {
1104
+            // IE8 can't handle data uris larger than 32KB
1105
+            if ($fsize / 1024 < 32) {
1106
+                if (is_null($mime)) {
1107
+                    if (class_exists('finfo')) { // php 5.3+
1108
+                        $finfo = new finfo(FILEINFO_MIME);
1109
+                        $mime = explode('; ', $finfo->file($fullpath));
1110
+                        $mime = $mime[0];
1111
+                    } elseif (function_exists('mime_content_type')) { // PHP 5.2
1112
+                        $mime = mime_content_type($fullpath);
1113
+                    }
1114
+                }
1115
+
1116
+                if (!is_null($mime)) { // fallback if the mime type is still unknown
1117
+                    $url = sprintf('data:%s;base64,%s', $mime, base64_encode(file_get_contents($fullpath)));
1118
+                }
1119
+            }
1120
+        }
1121
+
1122
+        return 'url("'.$url.'")';
1123
+    }
1124
+
1125
+    // utility func to unquote a string
1126
+    protected function lib_e($arg)
1127
+    {
1128
+        switch ($arg[0]) {
1129
+            case "list":
1130
+                $items = $arg[2];
1131
+                if (isset($items[0])) {
1132
+                    return $this->lib_e($items[0]);
1133
+                }
1134
+                $this->throwError("unrecognised input"); // This ends function by throwing an exception
1135
+                // no break
1136
+            case "string":
1137
+                $arg[1] = "";
1138
+                return $arg;
1139
+            case "keyword":
1140
+                return $arg;
1141
+            default:
1142
+                return array("keyword", $this->compileValue($arg));
1143
+        }
1144
+    }
1145
+
1146
+    protected function lib__sprintf($args)
1147
+    {
1148
+        if ($args[0] != "list") {
1149
+            return $args;
1150
+        }
1151
+        $values = $args[2];
1152
+        $string = array_shift($values);
1153
+        $template = $this->compileValue($this->lib_e($string));
1154
+
1155
+        $i = 0;
1156
+        $m = array();
1157
+        if (preg_match_all('/%[dsa]/', $template, $m)) {
1158
+            foreach ($m[0] as $match) {
1159
+                $val = isset($values[$i]) ?
1160
+                    $this->reduce($values[$i]) : array('keyword', '');
1161
+
1162
+                // lessjs compat, renders fully expanded color, not raw color
1163
+                if ($color = $this->coerceColor($val)) {
1164
+                    $val = $color;
1165
+                }
1166
+
1167
+                $i++;
1168
+                $rep = $this->compileValue($this->lib_e($val));
1169
+                $template = preg_replace(
1170
+                    '/'.self::preg_quote($match).'/',
1171
+                    $rep,
1172
+                    $template,
1173
+                    1
1174
+                );
1175
+            }
1176
+        }
1177
+
1178
+        $d = $string[0] == "string" ? $string[1] : '"';
1179
+        return array("string", $d, array($template));
1180
+    }
1181
+
1182
+    protected function lib_floor($arg)
1183
+    {
1184
+        $value = $this->assertNumber($arg);
1185
+        return array("number", floor($value), $arg[2]);
1186
+    }
1187
+
1188
+    protected function lib_ceil($arg)
1189
+    {
1190
+        $value = $this->assertNumber($arg);
1191
+        return array("number", ceil($value), $arg[2]);
1192
+    }
1193
+
1194
+    protected function lib_round($arg)
1195
+    {
1196
+        if ($arg[0] != "list") {
1197
+            $value = $this->assertNumber($arg);
1198
+            return array("number", round($value), $arg[2]);
1199
+        } else {
1200
+            $value = $this->assertNumber($arg[2][0]);
1201
+            $precision = $this->assertNumber($arg[2][1]);
1202
+            return array("number", round($value, $precision), $arg[2][0][2]);
1203
+        }
1204
+    }
1205
+
1206
+    protected function lib_unit($arg)
1207
+    {
1208
+        if ($arg[0] == "list") {
1209
+            list($number, $newUnit) = $arg[2];
1210
+            return array("number", $this->assertNumber($number),
1211
+                $this->compileValue($this->lib_e($newUnit)));
1212
+        } else {
1213
+            return array("number", $this->assertNumber($arg), "");
1214
+        }
1215
+    }
1216
+
1217
+    /**
1218
+     * Helper function to get arguments for color manipulation functions.
1219
+     * takes a list that contains a color like thing and a percentage
1220
+     */
1221
+    public function colorArgs($args)
1222
+    {
1223
+        if ($args[0] != 'list' || count($args[2]) < 2) {
1224
+            return array(array('color', 0, 0, 0), 0);
1225
+        }
1226
+        list($color, $delta) = $args[2];
1227
+        $color = $this->assertColor($color);
1228
+        $delta = (float) $delta[1];
1229
+
1230
+        return array($color, $delta);
1231
+    }
1232
+
1233
+    protected function lib_darken($args)
1234
+    {
1235
+        list($color, $delta) = $this->colorArgs($args);
1236
+
1237
+        $hsl = $this->toHSL($color);
1238
+        $hsl[3] = $this->clamp($hsl[3] - $delta, 100);
1239
+        return $this->toRGB($hsl);
1240
+    }
1241
+
1242
+    protected function lib_lighten($args)
1243
+    {
1244
+        list($color, $delta) = $this->colorArgs($args);
1245
+
1246
+        $hsl = $this->toHSL($color);
1247
+        $hsl[3] = $this->clamp($hsl[3] + $delta, 100);
1248
+        return $this->toRGB($hsl);
1249
+    }
1250
+
1251
+    protected function lib_saturate($args)
1252
+    {
1253
+        list($color, $delta) = $this->colorArgs($args);
1254
+
1255
+        $hsl = $this->toHSL($color);
1256
+        $hsl[2] = $this->clamp($hsl[2] + $delta, 100);
1257
+        return $this->toRGB($hsl);
1258
+    }
1259
+
1260
+    protected function lib_desaturate($args)
1261
+    {
1262
+        list($color, $delta) = $this->colorArgs($args);
1263
+
1264
+        $hsl = $this->toHSL($color);
1265
+        $hsl[2] = $this->clamp($hsl[2] - $delta, 100);
1266
+        return $this->toRGB($hsl);
1267
+    }
1268
+
1269
+    protected function lib_spin($args)
1270
+    {
1271
+        list($color, $delta) = $this->colorArgs($args);
1272
+
1273
+        $hsl = $this->toHSL($color);
1274
+
1275
+        $hsl[1] = $hsl[1] + $delta % 360;
1276
+        if ($hsl[1] < 0) {
1277
+            $hsl[1] += 360;
1278
+        }
1279
+
1280
+        return $this->toRGB($hsl);
1281
+    }
1282
+
1283
+    protected function lib_fadeout($args)
1284
+    {
1285
+        list($color, $delta) = $this->colorArgs($args);
1286
+        $color[4] = $this->clamp((isset($color[4]) ? $color[4] : 1) - $delta / 100);
1287
+        return $color;
1288
+    }
1289
+
1290
+    protected function lib_fadein($args)
1291
+    {
1292
+        list($color, $delta) = $this->colorArgs($args);
1293
+        $color[4] = $this->clamp((isset($color[4]) ? $color[4] : 1) + $delta / 100);
1294
+        return $color;
1295
+    }
1296
+
1297
+    protected function lib_hue($color)
1298
+    {
1299
+        $hsl = $this->toHSL($this->assertColor($color));
1300
+        return round($hsl[1]);
1301
+    }
1302
+
1303
+    protected function lib_saturation($color)
1304
+    {
1305
+        $hsl = $this->toHSL($this->assertColor($color));
1306
+        return round($hsl[2]);
1307
+    }
1308
+
1309
+    protected function lib_lightness($color)
1310
+    {
1311
+        $hsl = $this->toHSL($this->assertColor($color));
1312
+        return round($hsl[3]);
1313
+    }
1314
+
1315
+    // get the alpha of a color
1316
+    // defaults to 1 for non-colors or colors without an alpha
1317
+    protected function lib_alpha($value)
1318
+    {
1319
+        if (!is_null($color = $this->coerceColor($value))) {
1320
+            return isset($color[4]) ? $color[4] : 1;
1321
+        }
1322
+        return '';
1323
+    }
1324
+
1325
+    // set the alpha of the color
1326
+    protected function lib_fade($args)
1327
+    {
1328
+        list($color, $alpha) = $this->colorArgs($args);
1329
+        $color[4] = $this->clamp($alpha / 100.0);
1330
+        return $color;
1331
+    }
1332
+
1333
+    protected function lib_percentage($arg)
1334
+    {
1335
+        $num = $this->assertNumber($arg);
1336
+        return array("number", $num * 100, "%");
1337
+    }
1338
+
1339
+    /**
1340
+     * Mix color with white in variable proportion.
1341
+     *
1342
+     * It is the same as calling `mix(#ffffff, @color, @weight)`.
1343
+     *
1344
+     *     tint(@color, [@weight: 50%]);
1345
+     *
1346
+     * http://lesscss.org/functions/#color-operations-tint
1347
+     *
1348
+     * @return array Color
1349
+     */
1350
+    protected function lib_tint($args)
1351
+    {
1352
+        $white = ['color', 255, 255, 255];
1353
+        if ($args[0] == 'color') {
1354
+            return $this->lib_mix(['list', ',', [$white, $args]]);
1355
+        } elseif ($args[0] == "list" && count($args[2]) == 2) {
1356
+            return $this->lib_mix([$args[0], $args[1], [$white, $args[2][0], $args[2][1]]]);
1357
+        } else {
1358
+            $this->throwError("tint expects (color, weight)");
1359
+        }
1360
+        return array();
1361
+    }
1362
+
1363
+    /**
1364
+     * Mix color with black in variable proportion.
1365
+     *
1366
+     * It is the same as calling `mix(#000000, @color, @weight)`
1367
+     *
1368
+     *     shade(@color, [@weight: 50%]);
1369
+     *
1370
+     * http://lesscss.org/functions/#color-operations-shade
1371
+     *
1372
+     * @return array Color
1373
+     */
1374
+    protected function lib_shade($args)
1375
+    {
1376
+        $black = ['color', 0, 0, 0];
1377
+        if ($args[0] == 'color') {
1378
+            return $this->lib_mix(['list', ',', [$black, $args]]);
1379
+        } elseif ($args[0] == "list" && count($args[2]) == 2) {
1380
+            return $this->lib_mix([$args[0], $args[1], [$black, $args[2][0], $args[2][1]]]);
1381
+        } else {
1382
+            $this->throwError("shade expects (color, weight)");
1383
+        }
1384
+        return array();
1385
+    }
1386
+
1387
+    /**
1388
+     * lib_mix
1389
+     * mixes two colors by weight
1390
+     * mix(@color1, @color2, [@weight: 50%]);
1391
+     * http://sass-lang.com/docs/yardoc/Sass/Script/Functions.html#mix-instance_method
1392
+     *
1393
+     * @param array         $args   Args
1394
+     * @return array
1395
+     */
1396
+    protected function lib_mix($args)
1397
+    {
1398
+        if ($args[0] != "list" || count($args[2]) < 2) {
1399
+            $this->throwError("mix expects (color1, color2, weight)");
1400
+        }
1401
+
1402
+        list($first, $second) = $args[2];
1403
+        $first = $this->assertColor($first);
1404
+        $second = $this->assertColor($second);
1405
+
1406
+        $first_a = $this->lib_alpha($first);
1407
+        $second_a = $this->lib_alpha($second);
1408
+
1409
+        if (isset($args[2][2])) {
1410
+            $weight = $args[2][2][1] / 100.0;
1411
+        } else {
1412
+            $weight = 0.5;
1413
+        }
1414
+
1415
+        $w = $weight * 2 - 1;
1416
+        $a = $first_a - $second_a;
1417
+
1418
+        $w1 = (($w * $a == -1 ? $w : ($w + $a) / (1 + $w * $a)) + 1) / 2.0;
1419
+        $w2 = 1.0 - $w1;
1420
+
1421
+        $new = array('color',
1422
+            $w1 * $first[1] + $w2 * $second[1],
1423
+            $w1 * $first[2] + $w2 * $second[2],
1424
+            $w1 * $first[3] + $w2 * $second[3],
1425
+        );
1426
+
1427
+        if ($first_a != 1.0 || $second_a != 1.0) {
1428
+            $new[] = $first_a * $weight + $second_a * ($weight - 1);
1429
+        }
1430
+
1431
+        return $this->fixColor($new);
1432
+    }
1433
+
1434
+    /**
1435
+     * lib_contrast
1436
+     *
1437
+     * @param array         $args   Args
1438
+     * @return array
1439
+     */
1440
+    protected function lib_contrast($args)
1441
+    {
1442
+        $darkColor  = array('color', 0, 0, 0);
1443
+        $lightColor = array('color', 255, 255, 255);
1444
+        $threshold  = 0.43;
1445
+
1446
+        if ($args[0] == 'list') {
1447
+            $inputColor = (isset($args[2][0])) ? $this->assertColor($args[2][0]) : $lightColor;
1448
+            $darkColor  = (isset($args[2][1])) ? $this->assertColor($args[2][1]) : $darkColor;
1449
+            $lightColor = (isset($args[2][2])) ? $this->assertColor($args[2][2]) : $lightColor;
1450
+            $threshold  = (isset($args[2][3])) ? $this->assertNumber($args[2][3]) : $threshold;
1451
+        } else {
1452
+            $inputColor = $this->assertColor($args);
1453
+        }
1454
+
1455
+        $inputColor = $this->coerceColor($inputColor);
1456
+        $darkColor  = $this->coerceColor($darkColor);
1457
+        $lightColor = $this->coerceColor($lightColor);
1458
+
1459
+        //Figure out which is actually light and dark!
1460
+        if ($this->toLuma($darkColor) > $this->toLuma($lightColor)) {
1461
+            $t = $lightColor;
1462
+            $lightColor = $darkColor;
1463
+            $darkColor  = $t;
1464
+        }
1465
+
1466
+        $inputColor_alpha = $this->lib_alpha($inputColor);
1467
+        if (($this->toLuma($inputColor) * $inputColor_alpha) < $threshold) {
1468
+            return $lightColor;
1469
+        }
1470
+        return $darkColor;
1471
+    }
1472
+
1473
+    private function toLuma($color)
1474
+    {
1475
+        list(, $r, $g, $b) = $this->coerceColor($color);
1476
+
1477
+        $r = $r / 255;
1478
+        $g = $g / 255;
1479
+        $b = $b / 255;
1480
+
1481
+        $r = ($r <= 0.03928) ? $r / 12.92 : pow((($r + 0.055) / 1.055), 2.4);
1482
+        $g = ($g <= 0.03928) ? $g / 12.92 : pow((($g + 0.055) / 1.055), 2.4);
1483
+        $b = ($b <= 0.03928) ? $b / 12.92 : pow((($b + 0.055) / 1.055), 2.4);
1484
+
1485
+        return (0.2126 * $r) + (0.7152 * $g) + (0.0722 * $b);
1486
+    }
1487
+
1488
+    protected function lib_luma($color)
1489
+    {
1490
+        return array("number", round($this->toLuma($color) * 100, 8), "%");
1491
+    }
1492
+
1493
+
1494
+    public function assertColor($value, $error = "expected color value")
1495
+    {
1496
+        $color = $this->coerceColor($value);
1497
+        if (is_null($color)) {
1498
+            $this->throwError($error);
1499
+        }
1500
+        return $color;
1501
+    }
1502
+
1503
+    public function assertNumber($value, $error = "expecting number")
1504
+    {
1505
+        if ($value[0] == "number") {
1506
+            return $value[1];
1507
+        }
1508
+        $this->throwError($error);
1509
+    }
1510
+
1511
+    public function assertArgs($value, $expectedArgs, $name = "")
1512
+    {
1513
+        if ($expectedArgs == 1) {
1514
+            return $value;
1515
+        } else {
1516
+            if ($value[0] !== "list" || $value[1] != ",") {
1517
+                $this->throwError("expecting list");
1518
+            }
1519
+            $values = $value[2];
1520
+            $numValues = count($values);
1521
+            if ($expectedArgs != $numValues) {
1522
+                if ($name) {
1523
+                    $name = $name.": ";
1524
+                }
1525
+
1526
+                $this->throwError("{$name}expecting $expectedArgs arguments, got $numValues");
1527
+            }
1528
+
1529
+            return $values;
1530
+        }
1531
+    }
1532
+
1533
+    protected function toHSL($color)
1534
+    {
1535
+        if ($color[0] === 'hsl') {
1536
+            return $color;
1537
+        }
1538
+
1539
+        $r = $color[1] / 255;
1540
+        $g = $color[2] / 255;
1541
+        $b = $color[3] / 255;
1542
+
1543
+        $min = min($r, $g, $b);
1544
+        $max = max($r, $g, $b);
1545
+
1546
+        $L = ($min + $max) / 2;
1547
+        if ($min == $max) {
1548
+            $S = $H = 0;
1549
+        } else {
1550
+            if ($L < 0.5) {
1551
+                $S = ($max - $min) / ($max + $min);
1552
+            } else {
1553
+                $S = ($max - $min) / (2.0 - $max - $min);
1554
+            }
1555
+            if ($r == $max) {
1556
+                $H = ($g - $b) / ($max - $min);
1557
+            } elseif ($g == $max) {
1558
+                $H = 2.0 + ($b - $r) / ($max - $min);
1559
+            } elseif ($b == $max) {
1560
+                $H = 4.0 + ($r - $g) / ($max - $min);
1561
+            }
1562
+        }
1563
+
1564
+        $out = array('hsl',
1565
+            ($H < 0 ? $H + 6 : $H) * 60,
1566
+            $S * 100,
1567
+            $L * 100,
1568
+        );
1569
+
1570
+        if (count($color) > 4) {
1571
+            // copy alpha
1572
+            $out[] = $color[4];
1573
+        }
1574
+        return $out;
1575
+    }
1576
+
1577
+    protected function toRGB_helper($comp, $temp1, $temp2)
1578
+    {
1579
+        if ($comp < 0) {
1580
+            $comp += 1.0;
1581
+        } elseif ($comp > 1) {
1582
+            $comp -= 1.0;
1583
+        }
1584
+
1585
+        if (6 * $comp < 1) {
1586
+            return $temp1 + ($temp2 - $temp1) * 6 * $comp;
1587
+        }
1588
+        if (2 * $comp < 1) {
1589
+            return $temp2;
1590
+        }
1591
+        if (3 * $comp < 2) {
1592
+            return $temp1 + ($temp2 - $temp1) * ((2 / 3) - $comp) * 6;
1593
+        }
1594
+
1595
+        return $temp1;
1596
+    }
1597
+
1598
+    /**
1599
+     * Converts a hsl array into a color value in rgb.
1600
+     * Expects H to be in range of 0 to 360, S and L in 0 to 100
1601
+     */
1602
+    protected function toRGB($color)
1603
+    {
1604
+        if ($color[0] === 'color') {
1605
+            return $color;
1606
+        }
1607
+
1608
+        $H = $color[1] / 360;
1609
+        $S = $color[2] / 100;
1610
+        $L = $color[3] / 100;
1611
+
1612
+        if ($S == 0) {
1613
+            $r = $g = $b = $L;
1614
+        } else {
1615
+            $temp2 = $L < 0.5 ?
1616
+                $L * (1.0 + $S) : $L + $S - $L * $S;
1617
+
1618
+            $temp1 = 2.0 * $L - $temp2;
1619
+
1620
+            $r = $this->toRGB_helper($H + 1 / 3, $temp1, $temp2);
1621
+            $g = $this->toRGB_helper($H, $temp1, $temp2);
1622
+            $b = $this->toRGB_helper($H - 1 / 3, $temp1, $temp2);
1623
+        }
1624
+
1625
+        // $out = array('color', round($r*255), round($g*255), round($b*255));
1626
+        $out = array('color', $r * 255, $g * 255, $b * 255);
1627
+        if (count($color) > 4) {
1628
+            // copy alpha
1629
+            $out[] = $color[4];
1630
+        }
1631
+        return $out;
1632
+    }
1633
+
1634
+    protected function clamp($v, $max = 1, $min = 0)
1635
+    {
1636
+        return min($max, max($min, $v));
1637
+    }
1638
+
1639
+    /**
1640
+     * Convert the rgb, rgba, hsl color literals of function type
1641
+     * as returned by the parser into values of color type.
1642
+     */
1643
+    protected function funcToColor($func)
1644
+    {
1645
+        $fname = $func[1];
1646
+        if ($func[2][0] != 'list') {
1647
+            // need a list of arguments
1648
+            return false;
1649
+        }
1650
+        $rawComponents = $func[2][2];
1651
+
1652
+        if ($fname == 'hsl' || $fname == 'hsla') {
1653
+            $hsl = array('hsl');
1654
+            $i = 0;
1655
+            foreach ($rawComponents as $c) {
1656
+                $val = $this->reduce($c);
1657
+                $val = isset($val[1]) ? (float) $val[1] : 0;
1658
+
1659
+                if ($i == 0) {
1660
+                    $clamp = 360;
1661
+                } elseif ($i < 3) {
1662
+                    $clamp = 100;
1663
+                } else {
1664
+                    $clamp = 1;
1665
+                }
1666
+
1667
+                $hsl[] = $this->clamp($val, $clamp);
1668
+                $i++;
1669
+            }
1670
+
1671
+            while (count($hsl) < 4) {
1672
+                $hsl[] = 0;
1673
+            }
1674
+            return $this->toRGB($hsl);
1675
+
1676
+        } elseif ($fname == 'rgb' || $fname == 'rgba') {
1677
+            $components = array();
1678
+            $i = 1;
1679
+            foreach ($rawComponents as $c) {
1680
+                $c = $this->reduce($c);
1681
+                if ($i < 4) {
1682
+                    if ($c[0] == "number" && $c[2] == "%") {
1683
+                        $components[] = 255 * ($c[1] / 100);
1684
+                    } else {
1685
+                        $components[] = (float) $c[1];
1686
+                    }
1687
+                } elseif ($i == 4) {
1688
+                    if ($c[0] == "number" && $c[2] == "%") {
1689
+                        $components[] = 1.0 * ($c[1] / 100);
1690
+                    } else {
1691
+                        $components[] = (float) $c[1];
1692
+                    }
1693
+                } else {
1694
+                    break;
1695
+                }
1696
+
1697
+                $i++;
1698
+            }
1699
+            while (count($components) < 3) {
1700
+                $components[] = 0;
1701
+            }
1702
+            array_unshift($components, 'color');
1703
+            return $this->fixColor($components);
1704
+        }
1705
+
1706
+        return false;
1707
+    }
1708
+
1709
+    protected function reduce($value, $forExpression = false)
1710
+    {
1711
+        switch ($value[0]) {
1712
+            case "interpolate":
1713
+                $reduced = $this->reduce($value[1]);
1714
+                $var = $this->compileValue($reduced);
1715
+                $res = $this->reduce(array("variable", $this->vPrefix.$var));
1716
+
1717
+                if ($res[0] == "raw_color") {
1718
+                    $res = $this->coerceColor($res);
1719
+                }
1720
+
1721
+                if (empty($value[2])) {
1722
+                    $res = $this->lib_e($res);
1723
+                }
1724
+
1725
+                return $res;
1726
+            case "variable":
1727
+                $key = $value[1];
1728
+                if (is_array($key)) {
1729
+                    $key = $this->reduce($key);
1730
+                    $key = $this->vPrefix.$this->compileValue($this->lib_e($key));
1731
+                }
1732
+
1733
+                $seen = & $this->env->seenNames;
1734
+
1735
+                if (!empty($seen[$key])) {
1736
+                    $this->throwError("infinite loop detected: $key");
1737
+                }
1738
+
1739
+                $seen[$key] = true;
1740
+                $out = $this->reduce($this->get($key));
1741
+                $seen[$key] = false;
1742
+                return $out;
1743
+            case "list":
1744
+                foreach ($value[2] as &$item) {
1745
+                    $item = $this->reduce($item, $forExpression);
1746
+                }
1747
+                return $value;
1748
+            case "expression":
1749
+                return $this->evaluate($value);
1750
+            case "string":
1751
+                foreach ($value[2] as &$part) {
1752
+                    if (is_array($part)) {
1753
+                        $strip = $part[0] == "variable";
1754
+                        $part = $this->reduce($part);
1755
+                        if ($strip) {
1756
+                            $part = $this->lib_e($part);
1757
+                        }
1758
+                    }
1759
+                }
1760
+                return $value;
1761
+            case "escape":
1762
+                list(, $inner) = $value;
1763
+                return $this->lib_e($this->reduce($inner));
1764
+            case "function":
1765
+                $color = $this->funcToColor($value);
1766
+                if ($color) {
1767
+                    return $color;
1768
+                }
1769
+
1770
+                list(, $name, $args) = $value;
1771
+                if ($name == "%") {
1772
+                    $name = "_sprintf";
1773
+                }
1774
+
1775
+                $f = isset($this->libFunctions[$name]) ?
1776
+                    $this->libFunctions[$name] : array($this, 'lib_'.str_replace('-', '_', $name));
1777
+
1778
+                if (is_callable($f)) {
1779
+                    if ($args[0] == 'list') {
1780
+                        $args = self::compressList($args[2], $args[1]);
1781
+                    }
1782
+
1783
+                    $ret = call_user_func($f, $this->reduce($args, true), $this);
1784
+
1785
+                    if (is_null($ret)) {
1786
+                        return array("string", "", array(
1787
+                            $name, "(", $args, ")"
1788
+                        ));
1789
+                    }
1790
+
1791
+                    // convert to a typed value if the result is a php primitive
1792
+                    if (is_numeric($ret)) {
1793
+                        $ret = array('number', $ret, "");
1794
+                    } elseif (!is_array($ret)) {
1795
+                        $ret = array('keyword', $ret);
1796
+                    }
1797
+
1798
+                    return $ret;
1799
+                }
1800
+
1801
+                // plain function, reduce args
1802
+                $value[2] = $this->reduce($value[2]);
1803
+                return $value;
1804
+            case "unary":
1805
+                list(, $op, $exp) = $value;
1806
+                $exp = $this->reduce($exp);
1807
+
1808
+                if ($exp[0] == "number") {
1809
+                    switch ($op) {
1810
+                        case "+":
1811
+                            return $exp;
1812
+                        case "-":
1813
+                            $exp[1] *= -1;
1814
+                            return $exp;
1815
+                    }
1816
+                }
1817
+                return array("string", "", array($op, $exp));
1818
+        }
1819
+
1820
+        if ($forExpression) {
1821
+            switch ($value[0]) {
1822
+                case "keyword":
1823
+                    if ($color = $this->coerceColor($value)) {
1824
+                        return $color;
1825
+                    }
1826
+                    break;
1827
+                case "raw_color":
1828
+                    return $this->coerceColor($value);
1829
+            }
1830
+        }
1831
+
1832
+        return $value;
1833
+    }
1834
+
1835
+
1836
+    // coerce a value for use in color operation
1837
+    protected function coerceColor($value)
1838
+    {
1839
+        switch ($value[0]) {
1840
+            case 'color':
1841
+                return $value;
1842
+            case 'raw_color':
1843
+                $c = array("color", 0, 0, 0);
1844
+                $colorStr = substr($value[1], 1);
1845
+                $num = hexdec($colorStr);
1846
+                $width = strlen($colorStr) == 3 ? 16 : 256;
1847
+
1848
+                for ($i = 3; $i > 0; $i--) { // 3 2 1
1849
+                    $t = intval($num) % $width;
1850
+                    $num /= $width;
1851
+
1852
+                    $c[$i] = $t * (256 / $width) + $t * floor(16/$width);
1853
+                }
1854
+
1855
+                return $c;
1856
+            case 'keyword':
1857
+                $name = $value[1];
1858
+                if (isset(self::$cssColors[$name])) {
1859
+                    $rgba = explode(',', self::$cssColors[$name]);
1860
+
1861
+                    if (isset($rgba[3])) {
1862
+                        return array('color', $rgba[0], $rgba[1], $rgba[2], $rgba[3]);
1863
+                    }
1864
+                    return array('color', $rgba[0], $rgba[1], $rgba[2]);
1865
+                }
1866
+                return null;
1867
+        }
1868
+        return null;
1869
+    }
1870
+
1871
+    // make something string like into a string
1872
+    protected function coerceString($value)
1873
+    {
1874
+        switch ($value[0]) {
1875
+            case "string":
1876
+                return $value;
1877
+            case "keyword":
1878
+                return array("string", "", array($value[1]));
1879
+        }
1880
+        return null;
1881
+    }
1882
+
1883
+    // turn list of length 1 into value type
1884
+    protected function flattenList($value)
1885
+    {
1886
+        if ($value[0] == "list" && count($value[2]) == 1) {
1887
+            return $this->flattenList($value[2][0]);
1888
+        }
1889
+        return $value;
1890
+    }
1891
+
1892
+    public function toBool($a)
1893
+    {
1894
+        return $a ? self::$TRUE : self::$FALSE;
1895
+    }
1896
+
1897
+    // evaluate an expression
1898
+    protected function evaluate($exp)
1899
+    {
1900
+        list(, $op, $left, $right, $whiteBefore, $whiteAfter) = $exp;
1901
+
1902
+        $left = $this->reduce($left, true);
1903
+        $right = $this->reduce($right, true);
1904
+
1905
+        if ($leftColor = $this->coerceColor($left)) {
1906
+            $left = $leftColor;
1907
+        }
1908
+
1909
+        if ($rightColor = $this->coerceColor($right)) {
1910
+            $right = $rightColor;
1911
+        }
1912
+
1913
+        $ltype = $left[0];
1914
+        $rtype = $right[0];
1915
+
1916
+        // operators that work on all types
1917
+        if ($op == "and") {
1918
+            return $this->toBool($left == self::$TRUE && $right == self::$TRUE);
1919
+        }
1920
+
1921
+        if ($op == "=") {
1922
+            return $this->toBool($this->eq($left, $right));
1923
+        }
1924
+
1925
+        if ($op == "+" && !is_null($str = $this->stringConcatenate($left, $right))) {
1926
+            return $str;
1927
+        }
1928
+
1929
+        // type based operators
1930
+        $fname = "op_{$ltype}_{$rtype}";
1931
+        if (is_callable(array($this, $fname))) {
1932
+            $out = $this->$fname($op, $left, $right);
1933
+            if (!is_null($out)) {
1934
+                return $out;
1935
+            }
1936
+        }
1937
+
1938
+        // make the expression look it did before being parsed
1939
+        $paddedOp = $op;
1940
+        if ($whiteBefore) {
1941
+            $paddedOp = " ".$paddedOp;
1942
+        }
1943
+        if ($whiteAfter) {
1944
+            $paddedOp .= " ";
1945
+        }
1946
+
1947
+        return array("string", "", array($left, $paddedOp, $right));
1948
+    }
1949
+
1950
+    protected function stringConcatenate($left, $right)
1951
+    {
1952
+        if ($strLeft = $this->coerceString($left)) {
1953
+            if ($right[0] == "string") {
1954
+                $right[1] = "";
1955
+            }
1956
+            $strLeft[2][] = $right;
1957
+            return $strLeft;
1958
+        }
1959
+
1960
+        if ($strRight = $this->coerceString($right)) {
1961
+            array_unshift($strRight[2], $left);
1962
+            return $strRight;
1963
+        }
1964
+        return '';
1965
+    }
1966
+
1967
+
1968
+    // make sure a color's components don't go out of bounds
1969
+    protected function fixColor($c)
1970
+    {
1971
+        foreach (range(1, 3) as $i) {
1972
+            if ($c[$i] < 0) {
1973
+                $c[$i] = 0;
1974
+            }
1975
+            if ($c[$i] > 255) {
1976
+                $c[$i] = 255;
1977
+            }
1978
+        }
1979
+
1980
+        return $c;
1981
+    }
1982
+
1983
+    protected function op_number_color($op, $lft, $rgt)
1984
+    {
1985
+        if ($op == '+' || $op == '*') {
1986
+            return $this->op_color_number($op, $rgt, $lft);
1987
+        }
1988
+        return array();
1989
+    }
1990
+
1991
+    protected function op_color_number($op, $lft, $rgt)
1992
+    {
1993
+        if ($rgt[0] == '%') {
1994
+            $rgt[1] /= 100;
1995
+        }
1996
+
1997
+        return $this->op_color_color(
1998
+            $op,
1999
+            $lft,
2000
+            array_fill(1, count($lft) - 1, $rgt[1])
2001
+        );
2002
+    }
2003
+
2004
+    protected function op_color_color($op, $left, $right)
2005
+    {
2006
+        $out = array('color');
2007
+        $max = count($left) > count($right) ? count($left) : count($right);
2008
+        foreach (range(1, $max - 1) as $i) {
2009
+            $lval = isset($left[$i]) ? $left[$i] : 0;
2010
+            $rval = isset($right[$i]) ? $right[$i] : 0;
2011
+            switch ($op) {
2012
+                case '+':
2013
+                    $out[] = $lval + $rval;
2014
+                    break;
2015
+                case '-':
2016
+                    $out[] = $lval - $rval;
2017
+                    break;
2018
+                case '*':
2019
+                    $out[] = $lval * $rval;
2020
+                    break;
2021
+                case '%':
2022
+                    $out[] = $lval % $rval;
2023
+                    break;
2024
+                case '/':
2025
+                    if ($rval == 0) {
2026
+                        $this->throwError("evaluate error: can't divide by zero");
2027
+                    }
2028
+                    $out[] = $lval / $rval;
2029
+                    break;
2030
+                default:
2031
+                    $this->throwError('evaluate error: color op number failed on op '.$op);
2032
+            }
2033
+        }
2034
+        return $this->fixColor($out);
2035
+    }
2036
+
2037
+    public function lib_red($color)
2038
+    {
2039
+        $color = $this->coerceColor($color);
2040
+        if (is_null($color)) {
2041
+            $this->throwError('color expected for red()');
2042
+        }
2043
+
2044
+        return $color[1];
2045
+    }
2046
+
2047
+    public function lib_green($color)
2048
+    {
2049
+        $color = $this->coerceColor($color);
2050
+        if (is_null($color)) {
2051
+            $this->throwError('color expected for green()');
2052
+        }
2053
+
2054
+        return $color[2];
2055
+    }
2056
+
2057
+    public function lib_blue($color)
2058
+    {
2059
+        $color = $this->coerceColor($color);
2060
+        if (is_null($color)) {
2061
+            $this->throwError('color expected for blue()');
2062
+        }
2063
+
2064
+        return $color[3];
2065
+    }
2066
+
2067
+
2068
+    // operator on two numbers
2069
+    protected function op_number_number($op, $left, $right)
2070
+    {
2071
+        $unit = empty($left[2]) ? $right[2] : $left[2];
2072
+
2073
+        $value = 0;
2074
+        switch ($op) {
2075
+            case '+':
2076
+                $value = $left[1] + $right[1];
2077
+                break;
2078
+            case '*':
2079
+                $value = $left[1] * $right[1];
2080
+                break;
2081
+            case '-':
2082
+                $value = $left[1] - $right[1];
2083
+                break;
2084
+            case '%':
2085
+                $value = $left[1] % $right[1];
2086
+                break;
2087
+            case '/':
2088
+                if ($right[1] == 0) {
2089
+                    $this->throwError('parse error: divide by zero');
2090
+                }
2091
+                $value = $left[1] / $right[1];
2092
+                break;
2093
+            case '<':
2094
+                return $this->toBool($left[1] < $right[1]);
2095
+            case '>':
2096
+                return $this->toBool($left[1] > $right[1]);
2097
+            case '>=':
2098
+                return $this->toBool($left[1] >= $right[1]);
2099
+            case '=<':
2100
+                return $this->toBool($left[1] <= $right[1]);
2101
+            default:
2102
+                $this->throwError('parse error: unknown number operator: '.$op);
2103
+        }
2104
+
2105
+        return array("number", $value, $unit);
2106
+    }
2107
+
2108
+
2109
+    /* environment functions */
2110
+
2111
+    protected function makeOutputBlock($type, $selectors = null)
2112
+    {
2113
+        $b = new stdclass();
2114
+        $b->lines = array();
2115
+        $b->children = array();
2116
+        $b->selectors = $selectors;
2117
+        $b->type = $type;
2118
+        $b->parent = $this->scope;
2119
+        return $b;
2120
+    }
2121
+
2122
+    // the state of execution
2123
+    protected function pushEnv($block = null)
2124
+    {
2125
+        $e = new stdclass();
2126
+        $e->parent = $this->env;
2127
+        $e->store = array();
2128
+        $e->block = $block;
2129
+
2130
+        $this->env = $e;
2131
+        return $e;
2132
+    }
2133
+
2134
+    // pop something off the stack
2135
+    protected function popEnv()
2136
+    {
2137
+        $old = $this->env;
2138
+        $this->env = $this->env->parent;
2139
+        return $old;
2140
+    }
2141
+
2142
+    // set something in the current env
2143
+    protected function set($name, $value)
2144
+    {
2145
+        $this->env->store[$name] = $value;
2146
+    }
2147
+
2148
+
2149
+    // get the highest occurrence entry for a name
2150
+    protected function get($name)
2151
+    {
2152
+        $current = $this->env;
2153
+
2154
+        $isArguments = $name == $this->vPrefix.'arguments';
2155
+        while ($current) {
2156
+            if ($isArguments && isset($current->arguments)) {
2157
+                return array('list', ' ', $current->arguments);
2158
+            }
2159
+
2160
+            if (isset($current->store[$name])) {
2161
+                return $current->store[$name];
2162
+            }
2163
+
2164
+            $current = isset($current->storeParent) ?
2165
+                $current->storeParent : $current->parent;
2166
+        }
2167
+
2168
+        $this->throwError("variable $name is undefined");
2169
+    }
2170
+
2171
+    // inject array of unparsed strings into environment as variables
2172
+    protected function injectVariables($args)
2173
+    {
2174
+        $this->pushEnv();
2175
+        $parser = new lessc_parser($this, __METHOD__);
2176
+        $value = null;
2177
+        foreach ($args as $name => $strValue) {
2178
+            if ($name[0] !== '@') {
2179
+                $name = '@'.$name;
2180
+            }
2181
+            $parser->count = 0;
2182
+            $parser->buffer = (string) $strValue;
2183
+            if (!$parser->propertyValue($value)) {
2184
+                throw new Exception("failed to parse passed in variable $name: $strValue");
2185
+            }
2186
+
2187
+            $this->set($name, $value);
2188
+        }
2189
+    }
2190
+
2191
+    /**
2192
+     * Initialize any static state, can initialize parser for a file
2193
+     * $opts isn't used yet
2194
+     */
2195
+    public function __construct($fname = null)
2196
+    {
2197
+        if ($fname !== null) {
2198
+            // used for deprecated parse method
2199
+            $this->_parseFile = $fname;
2200
+        }
2201
+    }
2202
+
2203
+    public function compile($string, $name = null)
2204
+    {
2205
+        $locale = setlocale(LC_NUMERIC, 0);
2206
+        setlocale(LC_NUMERIC, "C");
2207
+
2208
+        $this->parser = $this->makeParser($name);
2209
+        $root = $this->parser->parse($string);
2210
+
2211
+        $this->env = null;
2212
+        $this->scope = null;
2213
+
2214
+        $this->formatter = $this->newFormatter();
2215
+
2216
+        if (!empty($this->registeredVars)) {
2217
+            $this->injectVariables($this->registeredVars);
2218
+        }
2219
+
2220
+        $this->sourceParser = $this->parser; // used for error messages
2221
+        $this->compileBlock($root);
2222
+
2223
+        ob_start();
2224
+        $this->formatter->block($this->scope);
2225
+        $out = ob_get_clean();
2226
+        setlocale(LC_NUMERIC, $locale);
2227
+        return $out;
2228
+    }
2229
+
2230
+    public function compileFile($fname, $outFname = null)
2231
+    {
2232
+        if (!is_readable($fname)) {
2233
+            throw new Exception('load error: failed to find '.$fname);
2234
+        }
2235
+
2236
+        $pi = pathinfo($fname);
2237
+
2238
+        $oldImport = $this->importDir;
2239
+
2240
+        $this->importDir = (array) $this->importDir;
2241
+        $this->importDir[] = $pi['dirname'].'/';
2242
+
2243
+        $this->addParsedFile($fname);
2244
+
2245
+        $out = $this->compile(file_get_contents($fname), $fname);
2246
+
2247
+        $this->importDir = $oldImport;
2248
+
2249
+        if ($outFname !== null) {
2250
+            return file_put_contents($outFname, $out);
2251
+        }
2252
+
2253
+        return $out;
2254
+    }
2255
+
2256
+    // compile only if changed input has changed or output doesn't exist
2257
+    public function checkedCompile($in, $out)
2258
+    {
2259
+        if (!is_file($out) || filemtime($in) > filemtime($out)) {
2260
+            $this->compileFile($in, $out);
2261
+            return true;
2262
+        }
2263
+        return false;
2264
+    }
2265
+
2266
+    /**
2267
+     * Execute lessphp on a .less file or a lessphp cache structure
2268
+     *
2269
+     * The lessphp cache structure contains information about a specific
2270
+     * less file having been parsed. It can be used as a hint for future
2271
+     * calls to determine whether or not a rebuild is required.
2272
+     *
2273
+     * The cache structure contains two important keys that may be used
2274
+     * externally:
2275
+     *
2276
+     * compiled: The final compiled CSS
2277
+     * updated: The time (in seconds) the CSS was last compiled
2278
+     *
2279
+     * The cache structure is a plain-ol' PHP associative array and can
2280
+     * be serialized and unserialized without a hitch.
2281
+     *
2282
+     * @param mixed $in Input
2283
+     * @param bool $force Force rebuild?
2284
+     * @return array|null lessphp cache structure
2285
+     */
2286
+    public function cachedCompile($in, $force = false)
2287
+    {
2288
+        // assume no root
2289
+        $root = null;
2290
+
2291
+        if (is_string($in)) {
2292
+            $root = $in;
2293
+        } elseif (is_array($in) && isset($in['root'])) {
2294
+            if ($force || !isset($in['files'])) {
2295
+                // If we are forcing a recompile or if for some reason the
2296
+                // structure does not contain any file information we should
2297
+                // specify the root to trigger a rebuild.
2298
+                $root = $in['root'];
2299
+            } elseif (isset($in['files']) && is_array($in['files'])) {
2300
+                foreach ($in['files'] as $fname => $ftime) {
2301
+                    if (!file_exists($fname) || filemtime($fname) > $ftime) {
2302
+                        // One of the files we knew about previously has changed
2303
+                        // so we should look at our incoming root again.
2304
+                        $root = $in['root'];
2305
+                        break;
2306
+                    }
2307
+                }
2308
+            }
2309
+        } else {
2310
+            // TODO: Throw an exception? We got neither a string nor something
2311
+            // that looks like a compatible lessphp cache structure.
2312
+            return null;
2313
+        }
2314
+
2315
+        if ($root !== null) {
2316
+            // If we have a root value which means we should rebuild.
2317
+            $out = array();
2318
+            $out['root'] = $root;
2319
+            $out['compiled'] = $this->compileFile($root);
2320
+            $out['files'] = $this->allParsedFiles();
2321
+            $out['updated'] = time();
2322
+            return $out;
2323
+        } else {
2324
+            // No changes, pass back the structure
2325
+            // we were given initially.
2326
+            return $in;
2327
+        }
2328
+    }
2329
+
2330
+    // parse and compile buffer
2331
+    // This is deprecated
2332
+    public function parse($str = null, $initialVariables = null)
2333
+    {
2334
+        if (is_array($str)) {
2335
+            $initialVariables = $str;
2336
+            $str = null;
2337
+        }
2338
+
2339
+        $oldVars = $this->registeredVars;
2340
+        if ($initialVariables !== null) {
2341
+            $this->setVariables($initialVariables);
2342
+        }
2343
+
2344
+        if ($str == null) {
2345
+            if (empty($this->_parseFile)) {
2346
+                throw new exception("nothing to parse");
2347
+            }
2348
+
2349
+            $out = $this->compileFile($this->_parseFile);
2350
+        } else {
2351
+            $out = $this->compile($str);
2352
+        }
2353
+
2354
+        $this->registeredVars = $oldVars;
2355
+        return $out;
2356
+    }
2357
+
2358
+    protected function makeParser($name)
2359
+    {
2360
+        $parser = new lessc_parser($this, $name);
2361
+        $parser->writeComments = $this->preserveComments;
2362
+
2363
+        return $parser;
2364
+    }
2365
+
2366
+    public function setFormatter($name)
2367
+    {
2368
+        $this->formatterName = $name;
2369
+    }
2370
+
2371
+    protected function newFormatter()
2372
+    {
2373
+        $className = "lessc_formatter_lessjs";
2374
+        if (!empty($this->formatterName)) {
2375
+            if (!is_string($this->formatterName)) {
2376
+                return $this->formatterName;
2377
+            }
2378
+            $className = "lessc_formatter_$this->formatterName";
2379
+        }
2380
+
2381
+        return new $className();
2382
+    }
2383
+
2384
+    public function setPreserveComments($preserve)
2385
+    {
2386
+        $this->preserveComments = $preserve;
2387
+    }
2388
+
2389
+    public function registerFunction($name, $func)
2390
+    {
2391
+        $this->libFunctions[$name] = $func;
2392
+    }
2393
+
2394
+    public function unregisterFunction($name)
2395
+    {
2396
+        unset($this->libFunctions[$name]);
2397
+    }
2398
+
2399
+    public function setVariables($variables)
2400
+    {
2401
+        $this->registeredVars = array_merge($this->registeredVars, $variables);
2402
+    }
2403
+
2404
+    public function unsetVariable($name)
2405
+    {
2406
+        unset($this->registeredVars[$name]);
2407
+    }
2408
+
2409
+    public function setImportDir($dirs)
2410
+    {
2411
+        $this->importDir = (array) $dirs;
2412
+    }
2413
+
2414
+    public function addImportDir($dir)
2415
+    {
2416
+        $this->importDir = (array) $this->importDir;
2417
+        $this->importDir[] = $dir;
2418
+    }
2419
+
2420
+    public function allParsedFiles()
2421
+    {
2422
+        return $this->allParsedFiles;
2423
+    }
2424
+
2425
+    public function addParsedFile($file)
2426
+    {
2427
+        $this->allParsedFiles[realpath($file)] = filemtime($file);
2428
+    }
2429
+
2430
+    /**
2431
+     * Uses the current value of $this->count to show line and line number
2432
+     */
2433
+    public function throwError($msg = null)
2434
+    {
2435
+        if ($this->sourceLoc >= 0) {
2436
+            $this->sourceParser->throwError($msg, $this->sourceLoc);
2437
+        }
2438
+        throw new exception($msg);
2439
+    }
2440
+
2441
+    // compile file $in to file $out if $in is newer than $out
2442
+    // returns true when it compiles, false otherwise
2443
+    public static function ccompile($in, $out, $less = null)
2444
+    {
2445
+        if ($less === null) {
2446
+            $less = new self();
2447
+        }
2448
+        return $less->checkedCompile($in, $out);
2449
+    }
2450
+
2451
+    public static function cexecute($in, $force = false, $less = null)
2452
+    {
2453
+        if ($less === null) {
2454
+            $less = new self();
2455
+        }
2456
+        return $less->cachedCompile($in, $force);
2457
+    }
2458
+
2459
+    protected static $cssColors = array(
2460
+        'aliceblue' => '240,248,255',
2461
+        'antiquewhite' => '250,235,215',
2462
+        'aqua' => '0,255,255',
2463
+        'aquamarine' => '127,255,212',
2464
+        'azure' => '240,255,255',
2465
+        'beige' => '245,245,220',
2466
+        'bisque' => '255,228,196',
2467
+        'black' => '0,0,0',
2468
+        'blanchedalmond' => '255,235,205',
2469
+        'blue' => '0,0,255',
2470
+        'blueviolet' => '138,43,226',
2471
+        'brown' => '165,42,42',
2472
+        'burlywood' => '222,184,135',
2473
+        'cadetblue' => '95,158,160',
2474
+        'chartreuse' => '127,255,0',
2475
+        'chocolate' => '210,105,30',
2476
+        'coral' => '255,127,80',
2477
+        'cornflowerblue' => '100,149,237',
2478
+        'cornsilk' => '255,248,220',
2479
+        'crimson' => '220,20,60',
2480
+        'cyan' => '0,255,255',
2481
+        'darkblue' => '0,0,139',
2482
+        'darkcyan' => '0,139,139',
2483
+        'darkgoldenrod' => '184,134,11',
2484
+        'darkgray' => '169,169,169',
2485
+        'darkgreen' => '0,100,0',
2486
+        'darkgrey' => '169,169,169',
2487
+        'darkkhaki' => '189,183,107',
2488
+        'darkmagenta' => '139,0,139',
2489
+        'darkolivegreen' => '85,107,47',
2490
+        'darkorange' => '255,140,0',
2491
+        'darkorchid' => '153,50,204',
2492
+        'darkred' => '139,0,0',
2493
+        'darksalmon' => '233,150,122',
2494
+        'darkseagreen' => '143,188,143',
2495
+        'darkslateblue' => '72,61,139',
2496
+        'darkslategray' => '47,79,79',
2497
+        'darkslategrey' => '47,79,79',
2498
+        'darkturquoise' => '0,206,209',
2499
+        'darkviolet' => '148,0,211',
2500
+        'deeppink' => '255,20,147',
2501
+        'deepskyblue' => '0,191,255',
2502
+        'dimgray' => '105,105,105',
2503
+        'dimgrey' => '105,105,105',
2504
+        'dodgerblue' => '30,144,255',
2505
+        'firebrick' => '178,34,34',
2506
+        'floralwhite' => '255,250,240',
2507
+        'forestgreen' => '34,139,34',
2508
+        'fuchsia' => '255,0,255',
2509
+        'gainsboro' => '220,220,220',
2510
+        'ghostwhite' => '248,248,255',
2511
+        'gold' => '255,215,0',
2512
+        'goldenrod' => '218,165,32',
2513
+        'gray' => '128,128,128',
2514
+        'green' => '0,128,0',
2515
+        'greenyellow' => '173,255,47',
2516
+        'grey' => '128,128,128',
2517
+        'honeydew' => '240,255,240',
2518
+        'hotpink' => '255,105,180',
2519
+        'indianred' => '205,92,92',
2520
+        'indigo' => '75,0,130',
2521
+        'ivory' => '255,255,240',
2522
+        'khaki' => '240,230,140',
2523
+        'lavender' => '230,230,250',
2524
+        'lavenderblush' => '255,240,245',
2525
+        'lawngreen' => '124,252,0',
2526
+        'lemonchiffon' => '255,250,205',
2527
+        'lightblue' => '173,216,230',
2528
+        'lightcoral' => '240,128,128',
2529
+        'lightcyan' => '224,255,255',
2530
+        'lightgoldenrodyellow' => '250,250,210',
2531
+        'lightgray' => '211,211,211',
2532
+        'lightgreen' => '144,238,144',
2533
+        'lightgrey' => '211,211,211',
2534
+        'lightpink' => '255,182,193',
2535
+        'lightsalmon' => '255,160,122',
2536
+        'lightseagreen' => '32,178,170',
2537
+        'lightskyblue' => '135,206,250',
2538
+        'lightslategray' => '119,136,153',
2539
+        'lightslategrey' => '119,136,153',
2540
+        'lightsteelblue' => '176,196,222',
2541
+        'lightyellow' => '255,255,224',
2542
+        'lime' => '0,255,0',
2543
+        'limegreen' => '50,205,50',
2544
+        'linen' => '250,240,230',
2545
+        'magenta' => '255,0,255',
2546
+        'maroon' => '128,0,0',
2547
+        'mediumaquamarine' => '102,205,170',
2548
+        'mediumblue' => '0,0,205',
2549
+        'mediumorchid' => '186,85,211',
2550
+        'mediumpurple' => '147,112,219',
2551
+        'mediumseagreen' => '60,179,113',
2552
+        'mediumslateblue' => '123,104,238',
2553
+        'mediumspringgreen' => '0,250,154',
2554
+        'mediumturquoise' => '72,209,204',
2555
+        'mediumvioletred' => '199,21,133',
2556
+        'midnightblue' => '25,25,112',
2557
+        'mintcream' => '245,255,250',
2558
+        'mistyrose' => '255,228,225',
2559
+        'moccasin' => '255,228,181',
2560
+        'navajowhite' => '255,222,173',
2561
+        'navy' => '0,0,128',
2562
+        'oldlace' => '253,245,230',
2563
+        'olive' => '128,128,0',
2564
+        'olivedrab' => '107,142,35',
2565
+        'orange' => '255,165,0',
2566
+        'orangered' => '255,69,0',
2567
+        'orchid' => '218,112,214',
2568
+        'palegoldenrod' => '238,232,170',
2569
+        'palegreen' => '152,251,152',
2570
+        'paleturquoise' => '175,238,238',
2571
+        'palevioletred' => '219,112,147',
2572
+        'papayawhip' => '255,239,213',
2573
+        'peachpuff' => '255,218,185',
2574
+        'peru' => '205,133,63',
2575
+        'pink' => '255,192,203',
2576
+        'plum' => '221,160,221',
2577
+        'powderblue' => '176,224,230',
2578
+        'purple' => '128,0,128',
2579
+        'red' => '255,0,0',
2580
+        'rosybrown' => '188,143,143',
2581
+        'royalblue' => '65,105,225',
2582
+        'saddlebrown' => '139,69,19',
2583
+        'salmon' => '250,128,114',
2584
+        'sandybrown' => '244,164,96',
2585
+        'seagreen' => '46,139,87',
2586
+        'seashell' => '255,245,238',
2587
+        'sienna' => '160,82,45',
2588
+        'silver' => '192,192,192',
2589
+        'skyblue' => '135,206,235',
2590
+        'slateblue' => '106,90,205',
2591
+        'slategray' => '112,128,144',
2592
+        'slategrey' => '112,128,144',
2593
+        'snow' => '255,250,250',
2594
+        'springgreen' => '0,255,127',
2595
+        'steelblue' => '70,130,180',
2596
+        'tan' => '210,180,140',
2597
+        'teal' => '0,128,128',
2598
+        'thistle' => '216,191,216',
2599
+        'tomato' => '255,99,71',
2600
+        'transparent' => '0,0,0,0',
2601
+        'turquoise' => '64,224,208',
2602
+        'violet' => '238,130,238',
2603
+        'wheat' => '245,222,179',
2604
+        'white' => '255,255,255',
2605
+        'whitesmoke' => '245,245,245',
2606
+        'yellow' => '255,255,0',
2607
+        'yellowgreen' => '154,205,50'
2608
+    );
2609 2609
 }
2610 2610
 
2611 2611
 // responsible for taking a string of LESS code and converting it into a
2612 2612
 // syntax tree
2613 2613
 class lessc_parser
2614 2614
 {
2615
-	protected static $nextBlockId = 0; // used to uniquely identify blocks
2616
-
2617
-	protected static $precedence = array(
2618
-		'=<' => 0,
2619
-		'>=' => 0,
2620
-		'=' => 0,
2621
-		'<' => 0,
2622
-		'>' => 0,
2623
-
2624
-		'+' => 1,
2625
-		'-' => 1,
2626
-		'*' => 2,
2627
-		'/' => 2,
2628
-		'%' => 2,
2629
-	);
2630
-
2631
-	protected static $whitePattern;
2632
-	protected static $commentMulti;
2633
-
2634
-	protected static $commentSingle = "//";
2635
-	protected static $commentMultiLeft = "/*";
2636
-	protected static $commentMultiRight = "*/";
2637
-
2638
-	// regex string to match any of the operators
2639
-	protected static $operatorString;
2640
-
2641
-	// these properties will supress division unless it's inside parenthases
2642
-	protected static $supressDivisionProps =
2643
-		array('/border-radius$/i', '/^font$/i');
2644
-
2645
-	protected $blockDirectives = array("font-face", "keyframes", "page", "-moz-document", "viewport", "-moz-viewport", "-o-viewport", "-ms-viewport");
2646
-	protected $lineDirectives = array("charset");
2647
-
2648
-	/**
2649
-	 * if we are in parens we can be more liberal with whitespace around
2650
-	 * operators because it must evaluate to a single value and thus is less
2651
-	 * ambiguous.
2652
-	 *
2653
-	 * Consider:
2654
-	 *     property1: 10 -5; // is two numbers, 10 and -5
2655
-	 *     property2: (10 -5); // should evaluate to 5
2656
-	 */
2657
-	protected $inParens = false;
2658
-
2659
-	// caches preg escaped literals
2660
-	protected static $literalCache = array();
2661
-
2662
-	public $env;
2663
-	public $buffer;
2664
-	public $count;
2665
-	public $line;
2666
-	public $eatWhiteDefault;
2667
-	public $lessc;
2668
-	public $sourceName;
2669
-	public $writeComments;
2670
-	public $seenComments;
2671
-	public $currentProperty;
2672
-	public $inExp;
2673
-
2674
-
2675
-	public function __construct($lessc, $sourceName = null)
2676
-	{
2677
-		$this->eatWhiteDefault = true;
2678
-		// reference to less needed for vPrefix, mPrefix, and parentSelector
2679
-		$this->lessc = $lessc;
2680
-
2681
-		$this->sourceName = $sourceName; // name used for error messages
2682
-
2683
-		$this->writeComments = false;
2684
-
2685
-		if (!self::$operatorString) {
2686
-			self::$operatorString =
2687
-				'('.implode('|', array_map(
2688
-					array('lessc', 'preg_quote'),
2689
-					array_keys(self::$precedence)
2690
-				)).')';
2691
-
2692
-			$commentSingle = Lessc::preg_quote(self::$commentSingle);
2693
-			$commentMultiLeft = Lessc::preg_quote(self::$commentMultiLeft);
2694
-			$commentMultiRight = Lessc::preg_quote(self::$commentMultiRight);
2695
-
2696
-			self::$commentMulti = $commentMultiLeft.'.*?'.$commentMultiRight;
2697
-			self::$whitePattern = '/'.$commentSingle.'[^\n]*\s*|('.self::$commentMulti.')\s*|\s+/Ais';
2698
-		}
2699
-	}
2700
-
2701
-	/**
2702
-	 * Parse a string
2703
-	 *
2704
-	 * @param       string  $buffer         String to parse
2705
-	 * @throws exception
2706
-	 * @return NULL|stdclass
2707
-	 */
2708
-	public function parse($buffer)
2709
-	{
2710
-		$this->count = 0;
2711
-		$this->line = 1;
2712
-
2713
-		$this->env = null; // block stack
2714
-		$this->buffer = $this->writeComments ? $buffer : $this->removeComments($buffer);
2715
-		$this->pushSpecialBlock("root");
2716
-		$this->eatWhiteDefault = true;
2717
-		$this->seenComments = array();
2718
-
2719
-		// trim whitespace on head
2720
-		// if (preg_match('/^\s+/', $this->buffer, $m)) {
2721
-		//  $this->line += substr_count($m[0], "\n");
2722
-		//  $this->buffer = ltrim($this->buffer);
2723
-		// }
2724
-		$this->whitespace();
2725
-
2726
-		// parse the entire file
2727
-		while (false !== $this->parseChunk());
2728
-
2729
-		if ($this->count != strlen($this->buffer)) {
2730
-			$this->throwError('parse error count '.$this->count.' != len buffer '.strlen($this->buffer));
2731
-
2732
-		}
2733
-
2734
-		// TODO report where the block was opened
2735
-		if (!property_exists($this->env, 'parent') || !is_null($this->env->parent)) {
2736
-			throw new exception('parse error: unclosed block');
2737
-		}
2738
-
2739
-		return $this->env;
2740
-	}
2741
-
2742
-	/**
2743
-	 * Parse a single chunk off the head of the buffer and append it to the
2744
-	 * current parse environment.
2745
-	 * Returns false when the buffer is empty, or when there is an error.
2746
-	 *
2747
-	 * This function is called repeatedly until the entire document is
2748
-	 * parsed.
2749
-	 *
2750
-	 * This parser is most similar to a recursive descent parser. Single
2751
-	 * functions represent discrete grammatical rules for the language, and
2752
-	 * they are able to capture the text that represents those rules.
2753
-	 *
2754
-	 * Consider the function Lessc::keyword(). (all parse functions are
2755
-	 * structured the same)
2756
-	 *
2757
-	 * The function takes a single reference argument. When calling the
2758
-	 * function it will attempt to match a keyword on the head of the buffer.
2759
-	 * If it is successful, it will place the keyword in the referenced
2760
-	 * argument, advance the position in the buffer, and return true. If it
2761
-	 * fails then it won't advance the buffer and it will return false.
2762
-	 *
2763
-	 * All of these parse functions are powered by Lessc::match(), which behaves
2764
-	 * the same way, but takes a literal regular expression. Sometimes it is
2765
-	 * more convenient to use match instead of creating a new function.
2766
-	 *
2767
-	 * Because of the format of the functions, to parse an entire string of
2768
-	 * grammatical rules, you can chain them together using &&.
2769
-	 *
2770
-	 * But, if some of the rules in the chain succeed before one fails, then
2771
-	 * the buffer position will be left at an invalid state. In order to
2772
-	 * avoid this, Lessc::seek() is used to remember and set buffer positions.
2773
-	 *
2774
-	 * Before parsing a chain, use $s = $this->seek() to remember the current
2775
-	 * position into $s. Then if a chain fails, use $this->seek($s) to
2776
-	 * go back where we started.
2777
-	 */
2778
-	protected function parseChunk()
2779
-	{
2780
-		if (empty($this->buffer)) {
2781
-			return false;
2782
-		}
2783
-		$s = $this->seek();
2784
-
2785
-		if ($this->whitespace()) {
2786
-			return true;
2787
-		}
2788
-
2789
-		$key = null;
2790
-		$value = null;
2791
-		$mediaQueries = null;
2792
-		$dirName = null;
2793
-		$dirValue = null;
2794
-		$importValue = null;
2795
-		$guards = null;
2796
-		$tag = null;
2797
-		$args = null;
2798
-		$isVararg = null;
2799
-		$argv = null;
2800
-		$suffix = null;
2801
-		$var = null;
2802
-		$tags = null;
2803
-
2804
-		// setting a property
2805
-		if ($this->keyword($key) && $this->assign() &&
2806
-			$this->propertyValue($value, $key) && $this->end()
2807
-		) {
2808
-			$this->append(array('assign', $key, $value), $s);
2809
-			return true;
2810
-		} else {
2811
-			$this->seek($s);
2812
-		}
2813
-
2814
-
2815
-		// look for special css blocks
2816
-		if ($this->literal('@', false)) {
2817
-			$this->count--;
2818
-
2819
-			// media
2820
-			if ($this->literal('@media')) {
2821
-				if ($this->mediaQueryList($mediaQueries)
2822
-					&& $this->literal('{')
2823
-				) {
2824
-					$media = $this->pushSpecialBlock("media");
2825
-					$media->queries = is_null($mediaQueries) ? array() : $mediaQueries;
2826
-					return true;
2827
-				} else {
2828
-					$this->seek($s);
2829
-					return false;
2830
-				}
2831
-			}
2832
-
2833
-			if ($this->literal("@", false) && $this->keyword($dirName)) {
2834
-				if ($this->isDirective($dirName, $this->blockDirectives)) {
2835
-					if ($this->openString("{", $dirValue, null, array(";")) &&
2836
-						$this->literal("{")
2837
-					) {
2838
-						$dir = $this->pushSpecialBlock("directive");
2839
-						$dir->name = $dirName;
2840
-						if (isset($dirValue)) {
2841
-							$dir->value = $dirValue;
2842
-						}
2843
-						return true;
2844
-					}
2845
-				} elseif ($this->isDirective($dirName, $this->lineDirectives)) {
2846
-					if ($this->propertyValue($dirValue) && $this->end()) {
2847
-						$this->append(array("directive", $dirName, $dirValue));
2848
-						return true;
2849
-					}
2850
-				}
2851
-			}
2852
-
2853
-			$this->seek($s);
2854
-		}
2855
-
2856
-		// setting a variable
2857
-		if ($this->variable($var) && $this->assign() &&
2858
-			$this->propertyValue($value) && $this->end()
2859
-		) {
2860
-			$this->append(array('assign', $var, $value), $s);
2861
-			return true;
2862
-		} else {
2863
-			$this->seek($s);
2864
-		}
2865
-
2866
-		if ($this->import($importValue)) {
2867
-			$this->append($importValue, $s);
2868
-			return true;
2869
-		}
2870
-
2871
-		// opening parametric mixin
2872
-		if ($this->tag($tag, true) && $this->argumentDef($args, $isVararg) &&
2873
-			$this->guards($guards) &&
2874
-			$this->literal('{')
2875
-		) {
2876
-			$block = $this->pushBlock($this->fixTags(array($tag)));
2877
-			$block->args = $args;
2878
-			$block->isVararg = $isVararg;
2879
-			if (!empty($guards)) {
2880
-				$block->guards = $guards;
2881
-			}
2882
-			return true;
2883
-		} else {
2884
-			$this->seek($s);
2885
-		}
2886
-
2887
-		// opening a simple block
2888
-		if ($this->tags($tags) && $this->literal('{', false)) {
2889
-			$tags = $this->fixTags($tags);
2890
-			$this->pushBlock($tags);
2891
-			return true;
2892
-		} else {
2893
-			$this->seek($s);
2894
-		}
2895
-
2896
-		// closing a block
2897
-		if ($this->literal('}', false)) {
2898
-			try {
2899
-				$block = $this->pop();
2900
-			} catch (exception $e) {
2901
-				$this->seek($s);
2902
-				$this->throwError($e->getMessage());
2903
-			}
2904
-
2905
-			$hidden = false;
2906
-			if (is_null($block->type)) {
2907
-				$hidden = true;
2908
-				if (!isset($block->args)) {
2909
-					foreach ($block->tags as $tag) {
2910
-						if (!is_string($tag) || $tag[0] != $this->lessc->mPrefix) {
2911
-							$hidden = false;
2912
-							break;
2913
-						}
2914
-					}
2915
-				}
2916
-
2917
-				foreach ($block->tags as $tag) {
2918
-					if (is_string($tag)) {
2919
-						$this->env->children[$tag][] = $block;
2920
-					}
2921
-				}
2922
-			}
2923
-
2924
-			if (!$hidden) {
2925
-				$this->append(array('block', $block), $s);
2926
-			}
2927
-
2928
-			// this is done here so comments aren't bundled into he block that
2929
-			// was just closed
2930
-			$this->whitespace();
2931
-			return true;
2932
-		}
2933
-
2934
-		// mixin
2935
-		if ($this->mixinTags($tags) &&
2936
-			$this->argumentDef($argv, $isVararg) &&
2937
-			$this->keyword($suffix)  && $this->end()
2938
-		) {
2939
-			$tags = $this->fixTags($tags);
2940
-			$this->append(array('mixin', $tags, $argv, $suffix), $s);
2941
-			return true;
2942
-		} else {
2943
-			$this->seek($s);
2944
-		}
2945
-
2946
-		// spare ;
2947
-		if ($this->literal(';')) {
2948
-			return true;
2949
-		}
2950
-
2951
-		return false; // got nothing, throw error
2952
-	}
2953
-
2954
-	protected function isDirective($dirname, $directives)
2955
-	{
2956
-		// TODO: cache pattern in parser
2957
-		$pattern = implode(
2958
-			"|",
2959
-			array_map(array("lessc", "preg_quote"), $directives)
2960
-		);
2961
-		$pattern = '/^(-[a-z-]+-)?('.$pattern.')$/i';
2962
-
2963
-		return preg_match($pattern, $dirname);
2964
-	}
2965
-
2966
-	protected function fixTags($tags)
2967
-	{
2968
-		// move @ tags out of variable namespace
2969
-		foreach ($tags as &$tag) {
2970
-			if ($tag[0] == $this->lessc->vPrefix) {
2971
-				$tag[0] = $this->lessc->mPrefix;
2972
-			}
2973
-		}
2974
-		return $tags;
2975
-	}
2976
-
2977
-	// a list of expressions
2978
-	protected function expressionList(&$exps)
2979
-	{
2980
-		$exp = null;
2981
-
2982
-		$values = array();
2983
-
2984
-		while ($this->expression($exp)) {
2985
-			$values[] = $exp;
2986
-		}
2987
-
2988
-		if (count($values) == 0) {
2989
-			return false;
2990
-		}
2991
-
2992
-		$exps = Lessc::compressList($values, ' ');
2993
-		return true;
2994
-	}
2995
-
2996
-	/**
2997
-	 * Attempt to consume an expression.
2998
-	 * @link http://en.wikipedia.org/wiki/Operator-precedence_parser#Pseudo-code
2999
-	 */
3000
-	protected function expression(&$out)
3001
-	{
3002
-		$lhs = null;
3003
-		$rhs = null;
3004
-
3005
-		if ($this->value($lhs)) {
3006
-			$out = $this->expHelper($lhs, 0);
3007
-
3008
-			// look for / shorthand
3009
-			if (!empty($this->env->supressedDivision)) {
3010
-				unset($this->env->supressedDivision);
3011
-				$s = $this->seek();
3012
-				if ($this->literal("/") && $this->value($rhs)) {
3013
-					$out = array("list", "",
3014
-						array($out, array("keyword", "/"), $rhs));
3015
-				} else {
3016
-					$this->seek($s);
3017
-				}
3018
-			}
3019
-
3020
-			return true;
3021
-		}
3022
-		return false;
3023
-	}
3024
-
3025
-	/**
3026
-	 * recursively parse infix equation with $lhs at precedence $minP
3027
-	 */
3028
-	protected function expHelper($lhs, $minP)
3029
-	{
3030
-		$next = null;
3031
-		$rhs = null;
3032
-
3033
-		$this->inExp = true;
3034
-		$ss = $this->seek();
3035
-
3036
-		while (true) {
3037
-			$whiteBefore = isset($this->buffer[$this->count - 1]) &&
3038
-				ctype_space($this->buffer[$this->count - 1]);
3039
-
3040
-			// If there is whitespace before the operator, then we require
3041
-			// whitespace after the operator for it to be an expression
3042
-			$needWhite = $whiteBefore && !$this->inParens;
3043
-
3044
-			$m = array();
3045
-			if ($this->match(self::$operatorString.($needWhite ? '\s' : ''), $m) && self::$precedence[$m[1]] >= $minP) {
3046
-				if (!$this->inParens && isset($this->env->currentProperty) && $m[1] == "/" && empty($this->env->supressedDivision)) {
3047
-					foreach (self::$supressDivisionProps as $pattern) {
3048
-						if (preg_match($pattern, $this->env->currentProperty)) {
3049
-							$this->env->supressedDivision = true;
3050
-							break 2;
3051
-						}
3052
-					}
3053
-				}
3054
-
3055
-
3056
-				$whiteAfter = isset($this->buffer[$this->count - 1]) &&
3057
-					ctype_space($this->buffer[$this->count - 1]);
3058
-
3059
-				if (!$this->value($rhs)) {
3060
-					break;
3061
-				}
3062
-
3063
-				// peek for next operator to see what to do with rhs
3064
-				if ($this->peek(self::$operatorString, $next) && self::$precedence[$next[1]] > self::$precedence[$m[1]]) {
3065
-					$rhs = $this->expHelper($rhs, self::$precedence[$next[1]]);
3066
-				}
3067
-
3068
-				$lhs = array('expression', $m[1], $lhs, $rhs, $whiteBefore, $whiteAfter);
3069
-				$ss = $this->seek();
3070
-
3071
-				continue;
3072
-			}
3073
-
3074
-			break;
3075
-		}
3076
-
3077
-		$this->seek($ss);
3078
-
3079
-		return $lhs;
3080
-	}
3081
-
3082
-	// consume a list of values for a property
3083
-	public function propertyValue(&$value, $keyName = null)
3084
-	{
3085
-		$v = null;
3086
-		$values = array();
3087
-
3088
-		if ($keyName !== null) {
3089
-			$this->env->currentProperty = $keyName;
3090
-		}
3091
-
3092
-		$s = null;
3093
-		while ($this->expressionList($v)) {
3094
-			$values[] = $v;
3095
-			$s = $this->seek();
3096
-			if (!$this->literal(',')) {
3097
-				break;
3098
-			}
3099
-		}
3100
-
3101
-		if ($s) {
3102
-			$this->seek($s);
3103
-		}
3104
-
3105
-		if ($keyName !== null) {
3106
-			unset($this->env->currentProperty);
3107
-		}
3108
-
3109
-		if (count($values) == 0) {
3110
-			return false;
3111
-		}
3112
-
3113
-		$value = Lessc::compressList($values, ', ');
3114
-		return true;
3115
-	}
3116
-
3117
-	protected function parenValue(&$out)
3118
-	{
3119
-		$exp = null;
3120
-
3121
-		$s = $this->seek();
3122
-
3123
-		// speed shortcut
3124
-		if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] != "(") {
3125
-			return false;
3126
-		}
3127
-
3128
-		$inParens = $this->inParens;
3129
-		if ($this->literal("(") &&
3130
-			($this->inParens = true) && $this->expression($exp) &&
3131
-			$this->literal(")")
3132
-		) {
3133
-			$out = $exp;
3134
-			$this->inParens = $inParens;
3135
-			return true;
3136
-		} else {
3137
-			$this->inParens = $inParens;
3138
-			$this->seek($s);
3139
-		}
3140
-
3141
-		return false;
3142
-	}
3143
-
3144
-	// a single value
3145
-	protected function value(&$value)
3146
-	{
3147
-		$inner = null;
3148
-		$word = null;
3149
-		$str = null;
3150
-		$var = null;
3151
-
3152
-		$s = $this->seek();
3153
-
3154
-		// speed shortcut
3155
-		if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] == "-") {
3156
-			// negation
3157
-			if ($this->literal("-", false) &&
3158
-				(($this->variable($inner) && $inner = array("variable", $inner)) ||
3159
-				$this->unit($inner) ||
3160
-				$this->parenValue($inner))
3161
-			) {
3162
-				$value = array("unary", "-", $inner);
3163
-				return true;
3164
-			} else {
3165
-				$this->seek($s);
3166
-			}
3167
-		}
3168
-
3169
-		if ($this->parenValue($value)) {
3170
-			return true;
3171
-		}
3172
-		if ($this->unit($value)) {
3173
-			return true;
3174
-		}
3175
-		if ($this->color($value)) {
3176
-			return true;
3177
-		}
3178
-		if ($this->func($value)) {
3179
-			return true;
3180
-		}
3181
-		if ($this->string($value)) {
3182
-			return true;
3183
-		}
3184
-
3185
-		if ($this->keyword($word)) {
3186
-			$value = array('keyword', $word);
3187
-			return true;
3188
-		}
3189
-
3190
-		// try a variable
3191
-		if ($this->variable($var)) {
3192
-			$value = array('variable', $var);
3193
-			return true;
3194
-		}
3195
-
3196
-		// unquote string (should this work on any type?
3197
-		if ($this->literal("~") && $this->string($str)) {
3198
-			$value = array("escape", $str);
3199
-			return true;
3200
-		} else {
3201
-			$this->seek($s);
3202
-		}
3203
-
3204
-		// css hack: \0
3205
-		$m = array();
3206
-		if ($this->literal('\\') && $this->match('([0-9]+)', $m)) {
3207
-			$value = array('keyword', '\\'.$m[1]);
3208
-			return true;
3209
-		} else {
3210
-			$this->seek($s);
3211
-		}
3212
-
3213
-		return false;
3214
-	}
3215
-
3216
-	// an import statement
3217
-	protected function import(&$out, $value = '')
3218
-	{
3219
-		if (!$this->literal('@import')) {
3220
-			return false;
3221
-		}
3222
-
3223
-		// @import "something.css" media;
3224
-		// @import url("something.css") media;
3225
-		// @import url(something.css) media;
3226
-
3227
-		if ($this->propertyValue($value)) {
3228
-			$out = array("import", $value);
3229
-			return true;
3230
-		}
3231
-
3232
-		return false;
3233
-	}
3234
-
3235
-	protected function mediaQueryList(&$out)
3236
-	{
3237
-		$list = null;
3238
-
3239
-		if ($this->genericList($list, "mediaQuery", ",", false)) {
3240
-			$out = $list[2];
3241
-			return true;
3242
-		}
3243
-		return false;
3244
-	}
3245
-
3246
-	protected function mediaQuery(&$out)
3247
-	{
3248
-		$mediaType = null;
3249
-
3250
-		$s = $this->seek();
3251
-
3252
-		$expressions = null;
3253
-		$parts = array();
3254
-
3255
-		if ((($this->literal("only") && ($only = true)) || ($this->literal("not") && ($not = true))) && $this->keyword($mediaType)) {
3256
-			$prop = array("mediaType");
3257
-			if (isset($only)) {
3258
-				$prop[] = "only";
3259
-			}
3260
-			if (isset($not)) {
3261
-				$prop[] = "not";
3262
-			}
3263
-			$prop[] = $mediaType;
3264
-			$parts[] = $prop;
3265
-		} else {
3266
-			$this->seek($s);
3267
-		}
3268
-
3269
-
3270
-		if (!empty($mediaType) && !$this->literal("and")) {
3271
-			// ~
3272
-		} else {
3273
-			$this->genericList($expressions, "mediaExpression", "and", false);
3274
-			if (is_array($expressions)) {
3275
-				$parts = array_merge($parts, $expressions[2]);
3276
-			}
3277
-		}
3278
-
3279
-		if (count($parts) == 0) {
3280
-			$this->seek($s);
3281
-			return false;
3282
-		}
3283
-
3284
-		$out = $parts;
3285
-		return true;
3286
-	}
3287
-
3288
-	protected function mediaExpression(&$out)
3289
-	{
3290
-		$feature = null;
3291
-		$variable = null;
3292
-
3293
-		$s = $this->seek();
3294
-		$value = null;
3295
-		if ($this->literal("(") &&
3296
-			$this->keyword($feature) &&
3297
-			($this->literal(":") && $this->expression($value)) &&
3298
-			$this->literal(")")
3299
-		) {
3300
-			$out = array("mediaExp", $feature);
3301
-			if ($value) {
3302
-				$out[] = $value;
3303
-			}
3304
-			return true;
3305
-		} elseif ($this->variable($variable)) {
3306
-			$out = array('variable', $variable);
3307
-			return true;
3308
-		}
3309
-
3310
-		$this->seek($s);
3311
-		return false;
3312
-	}
3313
-
3314
-	// an unbounded string stopped by $end
3315
-	protected function openString($end, &$out, $nestingOpen = null, $rejectStrs = null)
3316
-	{
3317
-		$str = null;
3318
-		$inter = null;
3319
-
3320
-		$oldWhite = $this->eatWhiteDefault;
3321
-		$this->eatWhiteDefault = false;
3322
-
3323
-		$stop = array("'", '"', "@{", $end);
3324
-		$stop = array_map(array("lessc", "preg_quote"), $stop);
3325
-		// $stop[] = self::$commentMulti;
3326
-
3327
-		if (!is_null($rejectStrs)) {
3328
-			$stop = array_merge($stop, $rejectStrs);
3329
-		}
3330
-
3331
-		$patt = '(.*?)('.implode("|", $stop).')';
3332
-
3333
-		$nestingLevel = 0;
3334
-
3335
-		$content = array();
3336
-		$m = array();
3337
-		while ($this->match($patt, $m, false)) {
3338
-			if (!empty($m[1])) {
3339
-				$content[] = $m[1];
3340
-				if ($nestingOpen) {
3341
-					$nestingLevel += substr_count($m[1], $nestingOpen);
3342
-				}
3343
-			}
3344
-
3345
-			$tok = $m[2];
3346
-
3347
-			$this->count -= strlen($tok);
3348
-			if ($tok == $end) {
3349
-				if ($nestingLevel == 0) {
3350
-					break;
3351
-				} else {
3352
-					$nestingLevel--;
3353
-				}
3354
-			}
3355
-
3356
-			if (($tok == "'" || $tok == '"') && $this->string($str)) {
3357
-				$content[] = $str;
3358
-				continue;
3359
-			}
3360
-
3361
-			if ($tok == "@{" && $this->interpolation($inter)) {
3362
-				$content[] = $inter;
3363
-				continue;
3364
-			}
3365
-
3366
-			if (!empty($rejectStrs) && in_array($tok, $rejectStrs)) {
3367
-				break;
3368
-			}
3369
-
3370
-			$content[] = $tok;
3371
-			$this->count += strlen($tok);
3372
-		}
3373
-
3374
-		$this->eatWhiteDefault = $oldWhite;
3375
-
3376
-		if (count($content) == 0) {
3377
-			return false;
3378
-		}
3379
-
3380
-		// trim the end
3381
-		if (is_string(end($content))) {
3382
-			$content[count($content) - 1] = rtrim(end($content));
3383
-		}
3384
-
3385
-		$out = array("string", "", $content);
3386
-		return true;
3387
-	}
3388
-
3389
-	protected function string(&$out)
3390
-	{
3391
-		$inter = null;
3392
-
3393
-		$s = $this->seek();
3394
-		if ($this->literal('"', false)) {
3395
-			$delim = '"';
3396
-		} elseif ($this->literal("'", false)) {
3397
-			$delim = "'";
3398
-		} else {
3399
-			return false;
3400
-		}
3401
-
3402
-		$content = array();
3403
-
3404
-		// look for either ending delim , escape, or string interpolation
3405
-		$patt = '([^\n]*?)(@\{|\\\\|'.
3406
-			Lessc::preg_quote($delim).')';
3407
-
3408
-		$oldWhite = $this->eatWhiteDefault;
3409
-		$this->eatWhiteDefault = false;
3410
-
3411
-		$m = array();
3412
-		while ($this->match($patt, $m, false)) {
3413
-			$content[] = $m[1];
3414
-			if ($m[2] == "@{") {
3415
-				$this->count -= strlen($m[2]);
3416
-				if ($this->interpolation($inter)) {
3417
-					$content[] = $inter;
3418
-				} else {
3419
-					$this->count += strlen($m[2]);
3420
-					$content[] = "@{"; // ignore it
3421
-				}
3422
-			} elseif ($m[2] == '\\') {
3423
-				$content[] = $m[2];
3424
-				if ($this->literal($delim, false)) {
3425
-					$content[] = $delim;
3426
-				}
3427
-			} else {
3428
-				$this->count -= strlen($delim);
3429
-				break; // delim
3430
-			}
3431
-		}
3432
-
3433
-		$this->eatWhiteDefault = $oldWhite;
3434
-
3435
-		if ($this->literal($delim)) {
3436
-			$out = array("string", $delim, $content);
3437
-			return true;
3438
-		}
3439
-
3440
-		$this->seek($s);
3441
-		return false;
3442
-	}
3443
-
3444
-	protected function interpolation(&$out)
3445
-	{
3446
-		$interp = array();
3447
-
3448
-		$oldWhite = $this->eatWhiteDefault;
3449
-		$this->eatWhiteDefault = true;
3450
-
3451
-		$s = $this->seek();
3452
-		if ($this->literal("@{") &&
3453
-			$this->openString("}", $interp, null, array("'", '"', ";")) &&
3454
-			$this->literal("}", false)
3455
-		) {
3456
-			$out = array("interpolate", $interp);
3457
-			$this->eatWhiteDefault = $oldWhite;
3458
-			if ($this->eatWhiteDefault) {
3459
-				$this->whitespace();
3460
-			}
3461
-			return true;
3462
-		}
3463
-
3464
-		$this->eatWhiteDefault = $oldWhite;
3465
-		$this->seek($s);
3466
-		return false;
3467
-	}
3468
-
3469
-	protected function unit(&$unit)
3470
-	{
3471
-		$m = array();
3472
-
3473
-		// speed shortcut
3474
-		if (isset($this->buffer[$this->count])) {
3475
-			$char = $this->buffer[$this->count];
3476
-			if (!ctype_digit($char) && $char != ".") {
3477
-				return false;
3478
-			}
3479
-		}
3480
-
3481
-		if ($this->match('([0-9]+(?:\.[0-9]*)?|\.[0-9]+)([%a-zA-Z]+)?', $m)) {
3482
-			$unit = array("number", $m[1], empty($m[2]) ? "" : $m[2]);
3483
-			return true;
3484
-		}
3485
-		return false;
3486
-	}
3487
-
3488
-	// a # color
3489
-	protected function color(&$out)
3490
-	{
3491
-		$m = array();
3492
-
3493
-		if ($this->match('(#(?:[0-9a-f]{8}|[0-9a-f]{6}|[0-9a-f]{3}))', $m)) {
3494
-			if (strlen($m[1]) > 7) {
3495
-				$out = array("string", "", array($m[1]));
3496
-			} else {
3497
-				$out = array("raw_color", $m[1]);
3498
-			}
3499
-			return true;
3500
-		}
3501
-
3502
-		return false;
3503
-	}
3504
-
3505
-	// consume an argument definition list surrounded by ()
3506
-	// each argument is a variable name with optional value
3507
-	// or at the end a ... or a variable named followed by ...
3508
-	// arguments are separated by , unless a ; is in the list, then ; is the
3509
-	// delimiter.
3510
-	protected function argumentDef(&$args, &$isVararg)
3511
-	{
3512
-		$value = array();
3513
-		$rhs = null;
3514
-
3515
-		$s = $this->seek();
3516
-		if (!$this->literal('(')) {
3517
-			return false;
3518
-		}
3519
-
3520
-		$values = array();
3521
-		$delim = ",";
3522
-		$method = "expressionList";
3523
-
3524
-		$isVararg = false;
3525
-		while (true) {
3526
-			if ($this->literal("...")) {
3527
-				$isVararg = true;
3528
-				break;
3529
-			}
3530
-
3531
-			if ($this->$method($value)) {
3532
-				if ($value[0] == "variable") {
3533
-					$arg = array("arg", $value[1]);
3534
-					$ss = $this->seek();
3535
-
3536
-					if ($this->assign() && $this->$method($rhs)) {
3537
-						$arg[] = $rhs;
3538
-					} else {
3539
-						$this->seek($ss);
3540
-						if ($this->literal("...")) {
3541
-							$arg[0] = "rest";
3542
-							$isVararg = true;
3543
-						}
3544
-					}
3545
-
3546
-					$values[] = $arg;
3547
-					if ($isVararg) {
3548
-						break;
3549
-					}
3550
-					continue;
3551
-				} else {
3552
-					$values[] = array("lit", $value);
3553
-				}
3554
-			}
3555
-
3556
-
3557
-			if (!$this->literal($delim)) {
3558
-				if ($delim == "," && $this->literal(";")) {
3559
-					// found new delim, convert existing args
3560
-					$delim = ";";
3561
-					$method = "propertyValue";
3562
-
3563
-					// transform arg list
3564
-					if (isset($values[1])) { // 2 items
3565
-						$newList = array();
3566
-						foreach ($values as $i => $arg) {
3567
-							switch ($arg[0]) {
3568
-								case "arg":
3569
-									if ($i) {
3570
-										$this->throwError("Cannot mix ; and , as delimiter types");
3571
-									}
3572
-									$newList[] = $arg[2];
3573
-									break;
3574
-								case "lit":
3575
-									$newList[] = $arg[1];
3576
-									break;
3577
-								case "rest":
3578
-									$this->throwError("Unexpected rest before semicolon");
3579
-							}
3580
-						}
3581
-
3582
-						$newList = array("list", ", ", $newList);
3583
-
3584
-						switch ($values[0][0]) {
3585
-							case "arg":
3586
-								$newArg = array("arg", $values[0][1], $newList);
3587
-								break;
3588
-							case "lit":
3589
-								$newArg = array("lit", $newList);
3590
-								break;
3591
-						}
3592
-
3593
-					} elseif ($values) { // 1 item
3594
-						$newArg = $values[0];
3595
-					}
3596
-
3597
-					if ($newArg) {
3598
-						$values = array($newArg);
3599
-					}
3600
-				} else {
3601
-					break;
3602
-				}
3603
-			}
3604
-		}
3605
-
3606
-		if (!$this->literal(')')) {
3607
-			$this->seek($s);
3608
-			return false;
3609
-		}
3610
-
3611
-		$args = $values;
3612
-
3613
-		return true;
3614
-	}
3615
-
3616
-	// consume a list of tags
3617
-	// this accepts a hanging delimiter
3618
-	protected function tags(&$tags, $simple = false, $delim = ',')
3619
-	{
3620
-		$tt = array();
3621
-
3622
-		$tags = array();
3623
-		while ($this->tag($tt, $simple)) {
3624
-			$tags[] = $tt;
3625
-			if (!$this->literal($delim)) {
3626
-				break;
3627
-			}
3628
-		}
3629
-		if (count($tags) == 0) {
3630
-			return false;
3631
-		}
3632
-
3633
-		return true;
3634
-	}
3635
-
3636
-	// list of tags of specifying mixin path
3637
-	// optionally separated by > (lazy, accepts extra >)
3638
-	protected function mixinTags(&$tags)
3639
-	{
3640
-		$tt = array();
3641
-
3642
-		$tags = array();
3643
-		while ($this->tag($tt, true)) {
3644
-			$tags[] = $tt;
3645
-			$this->literal(">");
3646
-		}
3647
-
3648
-		if (!$tags) {
3649
-			return false;
3650
-		}
3651
-
3652
-		return true;
3653
-	}
3654
-
3655
-	// a bracketed value (contained within in a tag definition)
3656
-	protected function tagBracket(&$parts, &$hasExpression)
3657
-	{
3658
-		$str = null;
3659
-		$inter = null;
3660
-		$word = null;
3661
-
3662
-		// speed shortcut
3663
-		if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] != "[") {
3664
-			return false;
3665
-		}
3666
-
3667
-		$s = $this->seek();
3668
-
3669
-		$hasInterpolation = false;
3670
-
3671
-		if ($this->literal("[", false)) {
3672
-			$attrParts = array("[");
3673
-			// keyword, string, operator
3674
-			while (true) {
3675
-				if ($this->literal("]", false)) {
3676
-					$this->count--;
3677
-					break; // get out early
3678
-				}
3679
-
3680
-				$m = array();
3681
-				if ($this->match('\s+', $m)) {
3682
-					$attrParts[] = " ";
3683
-					continue;
3684
-				}
3685
-				if ($this->string($str)) {
3686
-					// escape parent selector, (yuck)
3687
-					foreach ($str[2] as &$chunk) {
3688
-						$chunk = str_replace($this->lessc->parentSelector, "$&$", $chunk);
3689
-					}
3690
-
3691
-					$attrParts[] = $str;
3692
-					$hasInterpolation = true;
3693
-					continue;
3694
-				}
3695
-
3696
-				if ($this->keyword($word)) {
3697
-					$attrParts[] = $word;
3698
-					continue;
3699
-				}
3700
-
3701
-				if ($this->interpolation($inter)) {
3702
-					$attrParts[] = $inter;
3703
-					$hasInterpolation = true;
3704
-					continue;
3705
-				}
3706
-
3707
-				// operator, handles attr namespace too
3708
-				if ($this->match('[|-~\$\*\^=]+', $m)) {
3709
-					$attrParts[] = $m[0];
3710
-					continue;
3711
-				}
3712
-
3713
-				break;
3714
-			}
3715
-
3716
-			if ($this->literal("]", false)) {
3717
-				$attrParts[] = "]";
3718
-				foreach ($attrParts as $part) {
3719
-					$parts[] = $part;
3720
-				}
3721
-				$hasExpression = $hasExpression || $hasInterpolation;
3722
-				return true;
3723
-			}
3724
-			$this->seek($s);
3725
-		}
3726
-
3727
-		$this->seek($s);
3728
-		return false;
3729
-	}
3730
-
3731
-	// a space separated list of selectors
3732
-	protected function tag(&$tag, $simple = false)
3733
-	{
3734
-		$interp = null;
3735
-		$unit = null;
3736
-
3737
-		if ($simple) {
3738
-			$chars = '^@,:;{}\][>\(\) "\'';
3739
-		} else {
3740
-			$chars = '^@,;{}["\'';
3741
-		}
3742
-		$s = $this->seek();
3743
-
3744
-		$hasExpression = false;
3745
-		$parts = array();
3746
-		while ($this->tagBracket($parts, $hasExpression));
3747
-
3748
-		$oldWhite = $this->eatWhiteDefault;
3749
-		$this->eatWhiteDefault = false;
3750
-
3751
-		while (true) {
3752
-			$m = array();
3753
-			if ($this->match('(['.$chars.'0-9]['.$chars.']*)', $m)) {
3754
-				$parts[] = $m[1];
3755
-				if ($simple) {
3756
-					break;
3757
-				}
3758
-
3759
-				while ($this->tagBracket($parts, $hasExpression));
3760
-				continue;
3761
-			}
3762
-
3763
-			if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] == "@") {
3764
-				if ($this->interpolation($interp)) {
3765
-					$hasExpression = true;
3766
-					$interp[2] = true; // don't unescape
3767
-					$parts[] = $interp;
3768
-					continue;
3769
-				}
3770
-
3771
-				if ($this->literal("@")) {
3772
-					$parts[] = "@";
3773
-					continue;
3774
-				}
3775
-			}
3776
-
3777
-			if ($this->unit($unit)) { // for keyframes
3778
-				$parts[] = $unit[1];
3779
-				$parts[] = $unit[2];
3780
-				continue;
3781
-			}
3782
-
3783
-			break;
3784
-		}
3785
-
3786
-		$this->eatWhiteDefault = $oldWhite;
3787
-		if (!$parts) {
3788
-			$this->seek($s);
3789
-			return false;
3790
-		}
3791
-
3792
-		if ($hasExpression) {
3793
-			$tag = array("exp", array("string", "", $parts));
3794
-		} else {
3795
-			$tag = trim(implode($parts));
3796
-		}
3797
-
3798
-		$this->whitespace();
3799
-		return true;
3800
-	}
3801
-
3802
-	// a css function
3803
-	protected function func(&$func)
3804
-	{
3805
-		$s = $this->seek();
3806
-
3807
-		$m = array();
3808
-		$value = array();
3809
-		$string = array();
3810
-		$name = null;
3811
-
3812
-		if ($this->match('(%|[\w\-_][\w\-_:\.]+|[\w_])', $m) && $this->literal('(')) {
3813
-			$fname = $m[1];
3814
-
3815
-			$sPreArgs = $this->seek();
3816
-
3817
-			$args = array();
3818
-			while (true) {
3819
-				$ss = $this->seek();
3820
-				// this ugly nonsense is for ie filter properties
3821
-				if ($this->keyword($name) && $this->literal('=') && $this->expressionList($value)) {
3822
-					$args[] = array("string", "", array($name, "=", $value));
3823
-				} else {
3824
-					$this->seek($ss);
3825
-					if ($this->expressionList($value)) {
3826
-						$args[] = $value;
3827
-					}
3828
-				}
3829
-
3830
-				if (!$this->literal(',')) {
3831
-					break;
3832
-				}
3833
-			}
3834
-			$args = array('list', ',', $args);
3835
-
3836
-			if ($this->literal(')')) {
3837
-				$func = array('function', $fname, $args);
3838
-				return true;
3839
-			} elseif ($fname == 'url') {
3840
-				// couldn't parse and in url? treat as string
3841
-				$this->seek($sPreArgs);
3842
-				if ($this->openString(")", $string) && $this->literal(")")) {
3843
-					$func = array('function', $fname, $string);
3844
-					return true;
3845
-				}
3846
-			}
3847
-		}
3848
-
3849
-		$this->seek($s);
3850
-		return false;
3851
-	}
3852
-
3853
-	// consume a less variable
3854
-	protected function variable(&$name)
3855
-	{
3856
-		$sub = null;
3857
-		$name = null;
3858
-
3859
-		$s = $this->seek();
3860
-		if ($this->literal($this->lessc->vPrefix, false) &&
3861
-			($this->variable($sub) || $this->keyword($name))
3862
-		) {
3863
-			if (!empty($sub)) {
3864
-				$name = array('variable', $sub);
3865
-			} else {
3866
-				$name = $this->lessc->vPrefix.$name;
3867
-			}
3868
-			return true;
3869
-		}
3870
-
3871
-		$name = null;
3872
-		$this->seek($s);
3873
-		return false;
3874
-	}
3875
-
3876
-	/**
3877
-	 * Consume an assignment operator
3878
-	 * Can optionally take a name that will be set to the current property name
3879
-	 */
3880
-	protected function assign($name = null)
3881
-	{
3882
-		if ($name) {
3883
-			$this->currentProperty = $name;
3884
-		}
3885
-		return $this->literal(':') || $this->literal('=');
3886
-	}
3887
-
3888
-	// consume a keyword
3889
-	protected function keyword(&$word)
3890
-	{
3891
-		$m = array();
3892
-		if ($this->match('([\w_\-\*!"][\w\-_"]*)', $m)) {
3893
-			$word = $m[1];
3894
-			return true;
3895
-		}
3896
-		return false;
3897
-	}
3898
-
3899
-	// consume an end of statement delimiter
3900
-	protected function end()
3901
-	{
3902
-		if ($this->literal(';', false)) {
3903
-			return true;
3904
-		} elseif ($this->count == strlen($this->buffer) || $this->buffer[$this->count] == '}') {
3905
-			// if there is end of file or a closing block next then we don't need a ;
3906
-			return true;
3907
-		}
3908
-		return false;
3909
-	}
3910
-
3911
-	protected function guards(&$guards)
3912
-	{
3913
-		$g = null;
3914
-
3915
-		$s = $this->seek();
3916
-
3917
-		if (!$this->literal("when")) {
3918
-			$this->seek($s);
3919
-			return false;
3920
-		}
3921
-
3922
-		$guards = array();
3923
-
3924
-		while ($this->guardGroup($g)) {
3925
-			$guards[] = $g;
3926
-			if (!$this->literal(",")) {
3927
-				break;
3928
-			}
3929
-		}
3930
-
3931
-		if (count($guards) == 0) {
3932
-			$guards = null;
3933
-			$this->seek($s);
3934
-			return false;
3935
-		}
3936
-
3937
-		return true;
3938
-	}
3939
-
3940
-	// a bunch of guards that are and'd together
3941
-	// TODO rename to guardGroup
3942
-	protected function guardGroup(&$guardGroup)
3943
-	{
3944
-		$guard = null;
3945
-
3946
-		$s = $this->seek();
3947
-		$guardGroup = array();
3948
-		while ($this->guard($guard)) {
3949
-			$guardGroup[] = $guard;
3950
-			if (!$this->literal("and")) {
3951
-				break;
3952
-			}
3953
-		}
3954
-
3955
-		if (count($guardGroup) == 0) {
3956
-			$guardGroup = null;
3957
-			$this->seek($s);
3958
-			return false;
3959
-		}
3960
-
3961
-		return true;
3962
-	}
3963
-
3964
-	protected function guard(&$guard)
3965
-	{
3966
-		$exp = null;
3967
-
3968
-		$s = $this->seek();
3969
-		$negate = $this->literal("not");
3970
-
3971
-		if ($this->literal("(") && $this->expression($exp) && $this->literal(")")) {
3972
-			$guard = $exp;
3973
-			if ($negate) {
3974
-				$guard = array("negate", $guard);
3975
-			}
3976
-			return true;
3977
-		}
3978
-
3979
-		$this->seek($s);
3980
-		return false;
3981
-	}
3982
-
3983
-	/* raw parsing functions */
3984
-
3985
-	protected function literal($what, $eatWhitespace = null)
3986
-	{
3987
-		if ($eatWhitespace === null) {
3988
-			$eatWhitespace = $this->eatWhiteDefault;
3989
-		}
3990
-
3991
-		// shortcut on single letter
3992
-		if (!isset($what[1]) && isset($this->buffer[$this->count])) {
3993
-			if ($this->buffer[$this->count] == $what) {
3994
-				if (!$eatWhitespace) {
3995
-					$this->count++;
3996
-					return true;
3997
-				}
3998
-			// goes below...
3999
-			} else {
4000
-				return false;
4001
-			}
4002
-		}
4003
-
4004
-		if (!isset(self::$literalCache[$what])) {
4005
-			self::$literalCache[$what] = Lessc::preg_quote($what);
4006
-		}
4007
-
4008
-		$m = array();
4009
-		return $this->match(self::$literalCache[$what], $m, $eatWhitespace);
4010
-	}
4011
-
4012
-	protected function genericList(&$out, $parseItem, $delim = "", $flatten = true)
4013
-	{
4014
-		$value = null;
4015
-
4016
-		$s = $this->seek();
4017
-		$items = array();
4018
-		while ($this->$parseItem($value)) {
4019
-			$items[] = $value;
4020
-			if ($delim) {
4021
-				if (!$this->literal($delim)) {
4022
-					break;
4023
-				}
4024
-			}
4025
-		}
4026
-
4027
-		if (count($items) == 0) {
4028
-			$this->seek($s);
4029
-			return false;
4030
-		}
4031
-
4032
-		if ($flatten && count($items) == 1) {
4033
-			$out = $items[0];
4034
-		} else {
4035
-			$out = array("list", $delim, $items);
4036
-		}
4037
-
4038
-		return true;
4039
-	}
4040
-
4041
-
4042
-	// advance counter to next occurrence of $what
4043
-	// $until - don't include $what in advance
4044
-	// $allowNewline, if string, will be used as valid char set
4045
-	protected function to($what, &$out, $until = false, $allowNewline = false)
4046
-	{
4047
-		if (is_string($allowNewline)) {
4048
-			$validChars = $allowNewline;
4049
-		} else {
4050
-			$validChars = $allowNewline ? "." : "[^\n]";
4051
-		}
4052
-		$m = array();
4053
-		if (!$this->match('('.$validChars.'*?)'.Lessc::preg_quote($what), $m, !$until)) {
4054
-			return false;
4055
-		}
4056
-		if ($until) {
4057
-			$this->count -= strlen($what); // give back $what
4058
-		}
4059
-		$out = $m[1];
4060
-		return true;
4061
-	}
4062
-
4063
-	// try to match something on head of buffer
4064
-	protected function match($regex, &$out, $eatWhitespace = null)
4065
-	{
4066
-		if ($eatWhitespace === null) {
4067
-			$eatWhitespace = $this->eatWhiteDefault;
4068
-		}
4069
-
4070
-		$r = '/'.$regex.($eatWhitespace && !$this->writeComments ? '\s*' : '').'/Ais';
4071
-		if (preg_match($r, $this->buffer, $out, 0, $this->count)) {
4072
-			$this->count += strlen($out[0]);
4073
-			if ($eatWhitespace && $this->writeComments) {
4074
-				$this->whitespace();
4075
-			}
4076
-			return true;
4077
-		}
4078
-		return false;
4079
-	}
4080
-
4081
-	// match some whitespace
4082
-	protected function whitespace()
4083
-	{
4084
-		if ($this->writeComments) {
4085
-			$gotWhite = false;
4086
-			$m = array();
4087
-			while (preg_match(self::$whitePattern, $this->buffer, $m, 0, $this->count)) {
4088
-				if (isset($m[1]) && empty($this->seenComments[$this->count])) {
4089
-					$this->append(array("comment", $m[1]));
4090
-					$this->seenComments[$this->count] = true;
4091
-				}
4092
-				$this->count += strlen($m[0]);
4093
-				$gotWhite = true;
4094
-			}
4095
-			return $gotWhite;
4096
-		} else {
4097
-			$this->match("", $m);
4098
-			return strlen($m[0]) > 0;
4099
-		}
4100
-	}
4101
-
4102
-	// match something without consuming it
4103
-	protected function peek($regex, &$out = null, $from = null)
4104
-	{
4105
-		if (is_null($from)) {
4106
-			$from = $this->count;
4107
-		}
4108
-		$r = '/'.$regex.'/Ais';
4109
-		$result = preg_match($r, $this->buffer, $out, 0, $from);
4110
-
4111
-		return $result;
4112
-	}
4113
-
4114
-	// seek to a spot in the buffer or return where we are on no argument
4115
-	protected function seek($where = null)
4116
-	{
4117
-		if ($where === null) {
4118
-			return $this->count;
4119
-		} else {
4120
-			$this->count = $where;
4121
-		}
4122
-		return true;
4123
-	}
4124
-
4125
-	/* misc functions */
4126
-
4127
-	public function throwError($msg = "parse error", $count = null)
4128
-	{
4129
-		$count = is_null($count) ? $this->count : $count;
4130
-
4131
-		$line = $this->line +
4132
-			substr_count(substr($this->buffer, 0, $count), "\n");
4133
-
4134
-		if (!empty($this->sourceName)) {
4135
-			$loc = "$this->sourceName on line $line";
4136
-		} else {
4137
-			$loc = "line: $line";
4138
-		}
4139
-
4140
-		// TODO this depends on $this->count
4141
-		$m = array();
4142
-		if ($this->peek("(.*?)(\n|$)", $m, $count)) {
4143
-			throw new exception("$msg: failed at `$m[1]` $loc");
4144
-		} else {
4145
-			throw new exception("$msg: $loc");
4146
-		}
4147
-	}
4148
-
4149
-	protected function pushBlock($selectors = null, $type = null)
4150
-	{
4151
-		$b = new stdclass();
4152
-		$b->parent = $this->env;
4153
-
4154
-		$b->type = $type;
4155
-		$b->id = self::$nextBlockId++;
4156
-
4157
-		$b->isVararg = false; // TODO: kill me from here
4158
-		$b->tags = $selectors;
4159
-
4160
-		$b->props = array();
4161
-		$b->children = array();
4162
-
4163
-		$this->env = $b;
4164
-		return $b;
4165
-	}
4166
-
4167
-	// push a block that doesn't multiply tags
4168
-	protected function pushSpecialBlock($type)
4169
-	{
4170
-		return $this->pushBlock(null, $type);
4171
-	}
4172
-
4173
-	// append a property to the current block
4174
-	protected function append($prop, $pos = null)
4175
-	{
4176
-		if ($pos !== null) {
4177
-			$prop[-1] = $pos;
4178
-		}
4179
-		$this->env->props[] = $prop;
4180
-	}
4181
-
4182
-	// pop something off the stack
4183
-	protected function pop()
4184
-	{
4185
-		$old = $this->env;
4186
-		$this->env = $this->env->parent;
4187
-		return $old;
4188
-	}
4189
-
4190
-	// remove comments from $text
4191
-	// todo: make it work for all functions, not just url
4192
-	protected function removeComments($text)
4193
-	{
4194
-		$look = array(
4195
-			'url(', '//', '/*', '"', "'"
4196
-		);
4197
-
4198
-		$out = '';
4199
-		$min = null;
4200
-		while (true) {
4201
-			// find the next item
4202
-			foreach ($look as $token) {
4203
-				$pos = strpos($text, $token);
4204
-				if ($pos !== false) {
4205
-					if (!isset($min) || $pos < $min[1]) {
4206
-						$min = array($token, $pos);
4207
-					}
4208
-				}
4209
-			}
4210
-
4211
-			if (is_null($min)) {
4212
-				break;
4213
-			}
4214
-
4215
-			$count = $min[1];
4216
-			$skip = 0;
4217
-			$newlines = 0;
4218
-			switch ($min[0]) {
4219
-				case 'url(':
4220
-					$m = array();
4221
-					if (preg_match('/url\(.*?\)/', $text, $m, 0, $count)) {
4222
-						$count += strlen($m[0]) - strlen($min[0]);
4223
-					}
4224
-					break;
4225
-				case '"':
4226
-				case "'":
4227
-					$m = array();
4228
-					if (preg_match('/'.$min[0].'.*?(?<!\\\\)'.$min[0].'/', $text, $m, 0, $count)) {
4229
-						$count += strlen($m[0]) - 1;
4230
-					}
4231
-					break;
4232
-				case '//':
4233
-					$skip = strpos($text, "\n", $count);
4234
-					if ($skip === false) {
4235
-						$skip = strlen($text) - $count;
4236
-					} else {
4237
-						$skip -= $count;
4238
-					}
4239
-					break;
4240
-				case '/*':
4241
-					$m = array();
4242
-					if (preg_match('/\/\*.*?\*\//s', $text, $m, 0, $count)) {
4243
-						$skip = strlen($m[0]);
4244
-						$newlines = substr_count($m[0], "\n");
4245
-					}
4246
-					break;
4247
-			}
4248
-
4249
-			if ($skip == 0) {
4250
-				$count += strlen($min[0]);
4251
-			}
4252
-
4253
-			$out .= substr($text, 0, $count).str_repeat("\n", $newlines);
4254
-			$text = substr($text, $count + $skip);
4255
-
4256
-			$min = null;
4257
-		}
4258
-
4259
-		return $out.$text;
4260
-	}
2615
+    protected static $nextBlockId = 0; // used to uniquely identify blocks
2616
+
2617
+    protected static $precedence = array(
2618
+        '=<' => 0,
2619
+        '>=' => 0,
2620
+        '=' => 0,
2621
+        '<' => 0,
2622
+        '>' => 0,
2623
+
2624
+        '+' => 1,
2625
+        '-' => 1,
2626
+        '*' => 2,
2627
+        '/' => 2,
2628
+        '%' => 2,
2629
+    );
2630
+
2631
+    protected static $whitePattern;
2632
+    protected static $commentMulti;
2633
+
2634
+    protected static $commentSingle = "//";
2635
+    protected static $commentMultiLeft = "/*";
2636
+    protected static $commentMultiRight = "*/";
2637
+
2638
+    // regex string to match any of the operators
2639
+    protected static $operatorString;
2640
+
2641
+    // these properties will supress division unless it's inside parenthases
2642
+    protected static $supressDivisionProps =
2643
+        array('/border-radius$/i', '/^font$/i');
2644
+
2645
+    protected $blockDirectives = array("font-face", "keyframes", "page", "-moz-document", "viewport", "-moz-viewport", "-o-viewport", "-ms-viewport");
2646
+    protected $lineDirectives = array("charset");
2647
+
2648
+    /**
2649
+     * if we are in parens we can be more liberal with whitespace around
2650
+     * operators because it must evaluate to a single value and thus is less
2651
+     * ambiguous.
2652
+     *
2653
+     * Consider:
2654
+     *     property1: 10 -5; // is two numbers, 10 and -5
2655
+     *     property2: (10 -5); // should evaluate to 5
2656
+     */
2657
+    protected $inParens = false;
2658
+
2659
+    // caches preg escaped literals
2660
+    protected static $literalCache = array();
2661
+
2662
+    public $env;
2663
+    public $buffer;
2664
+    public $count;
2665
+    public $line;
2666
+    public $eatWhiteDefault;
2667
+    public $lessc;
2668
+    public $sourceName;
2669
+    public $writeComments;
2670
+    public $seenComments;
2671
+    public $currentProperty;
2672
+    public $inExp;
2673
+
2674
+
2675
+    public function __construct($lessc, $sourceName = null)
2676
+    {
2677
+        $this->eatWhiteDefault = true;
2678
+        // reference to less needed for vPrefix, mPrefix, and parentSelector
2679
+        $this->lessc = $lessc;
2680
+
2681
+        $this->sourceName = $sourceName; // name used for error messages
2682
+
2683
+        $this->writeComments = false;
2684
+
2685
+        if (!self::$operatorString) {
2686
+            self::$operatorString =
2687
+                '('.implode('|', array_map(
2688
+                    array('lessc', 'preg_quote'),
2689
+                    array_keys(self::$precedence)
2690
+                )).')';
2691
+
2692
+            $commentSingle = Lessc::preg_quote(self::$commentSingle);
2693
+            $commentMultiLeft = Lessc::preg_quote(self::$commentMultiLeft);
2694
+            $commentMultiRight = Lessc::preg_quote(self::$commentMultiRight);
2695
+
2696
+            self::$commentMulti = $commentMultiLeft.'.*?'.$commentMultiRight;
2697
+            self::$whitePattern = '/'.$commentSingle.'[^\n]*\s*|('.self::$commentMulti.')\s*|\s+/Ais';
2698
+        }
2699
+    }
2700
+
2701
+    /**
2702
+     * Parse a string
2703
+     *
2704
+     * @param       string  $buffer         String to parse
2705
+     * @throws exception
2706
+     * @return NULL|stdclass
2707
+     */
2708
+    public function parse($buffer)
2709
+    {
2710
+        $this->count = 0;
2711
+        $this->line = 1;
2712
+
2713
+        $this->env = null; // block stack
2714
+        $this->buffer = $this->writeComments ? $buffer : $this->removeComments($buffer);
2715
+        $this->pushSpecialBlock("root");
2716
+        $this->eatWhiteDefault = true;
2717
+        $this->seenComments = array();
2718
+
2719
+        // trim whitespace on head
2720
+        // if (preg_match('/^\s+/', $this->buffer, $m)) {
2721
+        //  $this->line += substr_count($m[0], "\n");
2722
+        //  $this->buffer = ltrim($this->buffer);
2723
+        // }
2724
+        $this->whitespace();
2725
+
2726
+        // parse the entire file
2727
+        while (false !== $this->parseChunk());
2728
+
2729
+        if ($this->count != strlen($this->buffer)) {
2730
+            $this->throwError('parse error count '.$this->count.' != len buffer '.strlen($this->buffer));
2731
+
2732
+        }
2733
+
2734
+        // TODO report where the block was opened
2735
+        if (!property_exists($this->env, 'parent') || !is_null($this->env->parent)) {
2736
+            throw new exception('parse error: unclosed block');
2737
+        }
2738
+
2739
+        return $this->env;
2740
+    }
2741
+
2742
+    /**
2743
+     * Parse a single chunk off the head of the buffer and append it to the
2744
+     * current parse environment.
2745
+     * Returns false when the buffer is empty, or when there is an error.
2746
+     *
2747
+     * This function is called repeatedly until the entire document is
2748
+     * parsed.
2749
+     *
2750
+     * This parser is most similar to a recursive descent parser. Single
2751
+     * functions represent discrete grammatical rules for the language, and
2752
+     * they are able to capture the text that represents those rules.
2753
+     *
2754
+     * Consider the function Lessc::keyword(). (all parse functions are
2755
+     * structured the same)
2756
+     *
2757
+     * The function takes a single reference argument. When calling the
2758
+     * function it will attempt to match a keyword on the head of the buffer.
2759
+     * If it is successful, it will place the keyword in the referenced
2760
+     * argument, advance the position in the buffer, and return true. If it
2761
+     * fails then it won't advance the buffer and it will return false.
2762
+     *
2763
+     * All of these parse functions are powered by Lessc::match(), which behaves
2764
+     * the same way, but takes a literal regular expression. Sometimes it is
2765
+     * more convenient to use match instead of creating a new function.
2766
+     *
2767
+     * Because of the format of the functions, to parse an entire string of
2768
+     * grammatical rules, you can chain them together using &&.
2769
+     *
2770
+     * But, if some of the rules in the chain succeed before one fails, then
2771
+     * the buffer position will be left at an invalid state. In order to
2772
+     * avoid this, Lessc::seek() is used to remember and set buffer positions.
2773
+     *
2774
+     * Before parsing a chain, use $s = $this->seek() to remember the current
2775
+     * position into $s. Then if a chain fails, use $this->seek($s) to
2776
+     * go back where we started.
2777
+     */
2778
+    protected function parseChunk()
2779
+    {
2780
+        if (empty($this->buffer)) {
2781
+            return false;
2782
+        }
2783
+        $s = $this->seek();
2784
+
2785
+        if ($this->whitespace()) {
2786
+            return true;
2787
+        }
2788
+
2789
+        $key = null;
2790
+        $value = null;
2791
+        $mediaQueries = null;
2792
+        $dirName = null;
2793
+        $dirValue = null;
2794
+        $importValue = null;
2795
+        $guards = null;
2796
+        $tag = null;
2797
+        $args = null;
2798
+        $isVararg = null;
2799
+        $argv = null;
2800
+        $suffix = null;
2801
+        $var = null;
2802
+        $tags = null;
2803
+
2804
+        // setting a property
2805
+        if ($this->keyword($key) && $this->assign() &&
2806
+            $this->propertyValue($value, $key) && $this->end()
2807
+        ) {
2808
+            $this->append(array('assign', $key, $value), $s);
2809
+            return true;
2810
+        } else {
2811
+            $this->seek($s);
2812
+        }
2813
+
2814
+
2815
+        // look for special css blocks
2816
+        if ($this->literal('@', false)) {
2817
+            $this->count--;
2818
+
2819
+            // media
2820
+            if ($this->literal('@media')) {
2821
+                if ($this->mediaQueryList($mediaQueries)
2822
+                    && $this->literal('{')
2823
+                ) {
2824
+                    $media = $this->pushSpecialBlock("media");
2825
+                    $media->queries = is_null($mediaQueries) ? array() : $mediaQueries;
2826
+                    return true;
2827
+                } else {
2828
+                    $this->seek($s);
2829
+                    return false;
2830
+                }
2831
+            }
2832
+
2833
+            if ($this->literal("@", false) && $this->keyword($dirName)) {
2834
+                if ($this->isDirective($dirName, $this->blockDirectives)) {
2835
+                    if ($this->openString("{", $dirValue, null, array(";")) &&
2836
+                        $this->literal("{")
2837
+                    ) {
2838
+                        $dir = $this->pushSpecialBlock("directive");
2839
+                        $dir->name = $dirName;
2840
+                        if (isset($dirValue)) {
2841
+                            $dir->value = $dirValue;
2842
+                        }
2843
+                        return true;
2844
+                    }
2845
+                } elseif ($this->isDirective($dirName, $this->lineDirectives)) {
2846
+                    if ($this->propertyValue($dirValue) && $this->end()) {
2847
+                        $this->append(array("directive", $dirName, $dirValue));
2848
+                        return true;
2849
+                    }
2850
+                }
2851
+            }
2852
+
2853
+            $this->seek($s);
2854
+        }
2855
+
2856
+        // setting a variable
2857
+        if ($this->variable($var) && $this->assign() &&
2858
+            $this->propertyValue($value) && $this->end()
2859
+        ) {
2860
+            $this->append(array('assign', $var, $value), $s);
2861
+            return true;
2862
+        } else {
2863
+            $this->seek($s);
2864
+        }
2865
+
2866
+        if ($this->import($importValue)) {
2867
+            $this->append($importValue, $s);
2868
+            return true;
2869
+        }
2870
+
2871
+        // opening parametric mixin
2872
+        if ($this->tag($tag, true) && $this->argumentDef($args, $isVararg) &&
2873
+            $this->guards($guards) &&
2874
+            $this->literal('{')
2875
+        ) {
2876
+            $block = $this->pushBlock($this->fixTags(array($tag)));
2877
+            $block->args = $args;
2878
+            $block->isVararg = $isVararg;
2879
+            if (!empty($guards)) {
2880
+                $block->guards = $guards;
2881
+            }
2882
+            return true;
2883
+        } else {
2884
+            $this->seek($s);
2885
+        }
2886
+
2887
+        // opening a simple block
2888
+        if ($this->tags($tags) && $this->literal('{', false)) {
2889
+            $tags = $this->fixTags($tags);
2890
+            $this->pushBlock($tags);
2891
+            return true;
2892
+        } else {
2893
+            $this->seek($s);
2894
+        }
2895
+
2896
+        // closing a block
2897
+        if ($this->literal('}', false)) {
2898
+            try {
2899
+                $block = $this->pop();
2900
+            } catch (exception $e) {
2901
+                $this->seek($s);
2902
+                $this->throwError($e->getMessage());
2903
+            }
2904
+
2905
+            $hidden = false;
2906
+            if (is_null($block->type)) {
2907
+                $hidden = true;
2908
+                if (!isset($block->args)) {
2909
+                    foreach ($block->tags as $tag) {
2910
+                        if (!is_string($tag) || $tag[0] != $this->lessc->mPrefix) {
2911
+                            $hidden = false;
2912
+                            break;
2913
+                        }
2914
+                    }
2915
+                }
2916
+
2917
+                foreach ($block->tags as $tag) {
2918
+                    if (is_string($tag)) {
2919
+                        $this->env->children[$tag][] = $block;
2920
+                    }
2921
+                }
2922
+            }
2923
+
2924
+            if (!$hidden) {
2925
+                $this->append(array('block', $block), $s);
2926
+            }
2927
+
2928
+            // this is done here so comments aren't bundled into he block that
2929
+            // was just closed
2930
+            $this->whitespace();
2931
+            return true;
2932
+        }
2933
+
2934
+        // mixin
2935
+        if ($this->mixinTags($tags) &&
2936
+            $this->argumentDef($argv, $isVararg) &&
2937
+            $this->keyword($suffix)  && $this->end()
2938
+        ) {
2939
+            $tags = $this->fixTags($tags);
2940
+            $this->append(array('mixin', $tags, $argv, $suffix), $s);
2941
+            return true;
2942
+        } else {
2943
+            $this->seek($s);
2944
+        }
2945
+
2946
+        // spare ;
2947
+        if ($this->literal(';')) {
2948
+            return true;
2949
+        }
2950
+
2951
+        return false; // got nothing, throw error
2952
+    }
2953
+
2954
+    protected function isDirective($dirname, $directives)
2955
+    {
2956
+        // TODO: cache pattern in parser
2957
+        $pattern = implode(
2958
+            "|",
2959
+            array_map(array("lessc", "preg_quote"), $directives)
2960
+        );
2961
+        $pattern = '/^(-[a-z-]+-)?('.$pattern.')$/i';
2962
+
2963
+        return preg_match($pattern, $dirname);
2964
+    }
2965
+
2966
+    protected function fixTags($tags)
2967
+    {
2968
+        // move @ tags out of variable namespace
2969
+        foreach ($tags as &$tag) {
2970
+            if ($tag[0] == $this->lessc->vPrefix) {
2971
+                $tag[0] = $this->lessc->mPrefix;
2972
+            }
2973
+        }
2974
+        return $tags;
2975
+    }
2976
+
2977
+    // a list of expressions
2978
+    protected function expressionList(&$exps)
2979
+    {
2980
+        $exp = null;
2981
+
2982
+        $values = array();
2983
+
2984
+        while ($this->expression($exp)) {
2985
+            $values[] = $exp;
2986
+        }
2987
+
2988
+        if (count($values) == 0) {
2989
+            return false;
2990
+        }
2991
+
2992
+        $exps = Lessc::compressList($values, ' ');
2993
+        return true;
2994
+    }
2995
+
2996
+    /**
2997
+     * Attempt to consume an expression.
2998
+     * @link http://en.wikipedia.org/wiki/Operator-precedence_parser#Pseudo-code
2999
+     */
3000
+    protected function expression(&$out)
3001
+    {
3002
+        $lhs = null;
3003
+        $rhs = null;
3004
+
3005
+        if ($this->value($lhs)) {
3006
+            $out = $this->expHelper($lhs, 0);
3007
+
3008
+            // look for / shorthand
3009
+            if (!empty($this->env->supressedDivision)) {
3010
+                unset($this->env->supressedDivision);
3011
+                $s = $this->seek();
3012
+                if ($this->literal("/") && $this->value($rhs)) {
3013
+                    $out = array("list", "",
3014
+                        array($out, array("keyword", "/"), $rhs));
3015
+                } else {
3016
+                    $this->seek($s);
3017
+                }
3018
+            }
3019
+
3020
+            return true;
3021
+        }
3022
+        return false;
3023
+    }
3024
+
3025
+    /**
3026
+     * recursively parse infix equation with $lhs at precedence $minP
3027
+     */
3028
+    protected function expHelper($lhs, $minP)
3029
+    {
3030
+        $next = null;
3031
+        $rhs = null;
3032
+
3033
+        $this->inExp = true;
3034
+        $ss = $this->seek();
3035
+
3036
+        while (true) {
3037
+            $whiteBefore = isset($this->buffer[$this->count - 1]) &&
3038
+                ctype_space($this->buffer[$this->count - 1]);
3039
+
3040
+            // If there is whitespace before the operator, then we require
3041
+            // whitespace after the operator for it to be an expression
3042
+            $needWhite = $whiteBefore && !$this->inParens;
3043
+
3044
+            $m = array();
3045
+            if ($this->match(self::$operatorString.($needWhite ? '\s' : ''), $m) && self::$precedence[$m[1]] >= $minP) {
3046
+                if (!$this->inParens && isset($this->env->currentProperty) && $m[1] == "/" && empty($this->env->supressedDivision)) {
3047
+                    foreach (self::$supressDivisionProps as $pattern) {
3048
+                        if (preg_match($pattern, $this->env->currentProperty)) {
3049
+                            $this->env->supressedDivision = true;
3050
+                            break 2;
3051
+                        }
3052
+                    }
3053
+                }
3054
+
3055
+
3056
+                $whiteAfter = isset($this->buffer[$this->count - 1]) &&
3057
+                    ctype_space($this->buffer[$this->count - 1]);
3058
+
3059
+                if (!$this->value($rhs)) {
3060
+                    break;
3061
+                }
3062
+
3063
+                // peek for next operator to see what to do with rhs
3064
+                if ($this->peek(self::$operatorString, $next) && self::$precedence[$next[1]] > self::$precedence[$m[1]]) {
3065
+                    $rhs = $this->expHelper($rhs, self::$precedence[$next[1]]);
3066
+                }
3067
+
3068
+                $lhs = array('expression', $m[1], $lhs, $rhs, $whiteBefore, $whiteAfter);
3069
+                $ss = $this->seek();
3070
+
3071
+                continue;
3072
+            }
3073
+
3074
+            break;
3075
+        }
3076
+
3077
+        $this->seek($ss);
3078
+
3079
+        return $lhs;
3080
+    }
3081
+
3082
+    // consume a list of values for a property
3083
+    public function propertyValue(&$value, $keyName = null)
3084
+    {
3085
+        $v = null;
3086
+        $values = array();
3087
+
3088
+        if ($keyName !== null) {
3089
+            $this->env->currentProperty = $keyName;
3090
+        }
3091
+
3092
+        $s = null;
3093
+        while ($this->expressionList($v)) {
3094
+            $values[] = $v;
3095
+            $s = $this->seek();
3096
+            if (!$this->literal(',')) {
3097
+                break;
3098
+            }
3099
+        }
3100
+
3101
+        if ($s) {
3102
+            $this->seek($s);
3103
+        }
3104
+
3105
+        if ($keyName !== null) {
3106
+            unset($this->env->currentProperty);
3107
+        }
3108
+
3109
+        if (count($values) == 0) {
3110
+            return false;
3111
+        }
3112
+
3113
+        $value = Lessc::compressList($values, ', ');
3114
+        return true;
3115
+    }
3116
+
3117
+    protected function parenValue(&$out)
3118
+    {
3119
+        $exp = null;
3120
+
3121
+        $s = $this->seek();
3122
+
3123
+        // speed shortcut
3124
+        if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] != "(") {
3125
+            return false;
3126
+        }
3127
+
3128
+        $inParens = $this->inParens;
3129
+        if ($this->literal("(") &&
3130
+            ($this->inParens = true) && $this->expression($exp) &&
3131
+            $this->literal(")")
3132
+        ) {
3133
+            $out = $exp;
3134
+            $this->inParens = $inParens;
3135
+            return true;
3136
+        } else {
3137
+            $this->inParens = $inParens;
3138
+            $this->seek($s);
3139
+        }
3140
+
3141
+        return false;
3142
+    }
3143
+
3144
+    // a single value
3145
+    protected function value(&$value)
3146
+    {
3147
+        $inner = null;
3148
+        $word = null;
3149
+        $str = null;
3150
+        $var = null;
3151
+
3152
+        $s = $this->seek();
3153
+
3154
+        // speed shortcut
3155
+        if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] == "-") {
3156
+            // negation
3157
+            if ($this->literal("-", false) &&
3158
+                (($this->variable($inner) && $inner = array("variable", $inner)) ||
3159
+                $this->unit($inner) ||
3160
+                $this->parenValue($inner))
3161
+            ) {
3162
+                $value = array("unary", "-", $inner);
3163
+                return true;
3164
+            } else {
3165
+                $this->seek($s);
3166
+            }
3167
+        }
3168
+
3169
+        if ($this->parenValue($value)) {
3170
+            return true;
3171
+        }
3172
+        if ($this->unit($value)) {
3173
+            return true;
3174
+        }
3175
+        if ($this->color($value)) {
3176
+            return true;
3177
+        }
3178
+        if ($this->func($value)) {
3179
+            return true;
3180
+        }
3181
+        if ($this->string($value)) {
3182
+            return true;
3183
+        }
3184
+
3185
+        if ($this->keyword($word)) {
3186
+            $value = array('keyword', $word);
3187
+            return true;
3188
+        }
3189
+
3190
+        // try a variable
3191
+        if ($this->variable($var)) {
3192
+            $value = array('variable', $var);
3193
+            return true;
3194
+        }
3195
+
3196
+        // unquote string (should this work on any type?
3197
+        if ($this->literal("~") && $this->string($str)) {
3198
+            $value = array("escape", $str);
3199
+            return true;
3200
+        } else {
3201
+            $this->seek($s);
3202
+        }
3203
+
3204
+        // css hack: \0
3205
+        $m = array();
3206
+        if ($this->literal('\\') && $this->match('([0-9]+)', $m)) {
3207
+            $value = array('keyword', '\\'.$m[1]);
3208
+            return true;
3209
+        } else {
3210
+            $this->seek($s);
3211
+        }
3212
+
3213
+        return false;
3214
+    }
3215
+
3216
+    // an import statement
3217
+    protected function import(&$out, $value = '')
3218
+    {
3219
+        if (!$this->literal('@import')) {
3220
+            return false;
3221
+        }
3222
+
3223
+        // @import "something.css" media;
3224
+        // @import url("something.css") media;
3225
+        // @import url(something.css) media;
3226
+
3227
+        if ($this->propertyValue($value)) {
3228
+            $out = array("import", $value);
3229
+            return true;
3230
+        }
3231
+
3232
+        return false;
3233
+    }
3234
+
3235
+    protected function mediaQueryList(&$out)
3236
+    {
3237
+        $list = null;
3238
+
3239
+        if ($this->genericList($list, "mediaQuery", ",", false)) {
3240
+            $out = $list[2];
3241
+            return true;
3242
+        }
3243
+        return false;
3244
+    }
3245
+
3246
+    protected function mediaQuery(&$out)
3247
+    {
3248
+        $mediaType = null;
3249
+
3250
+        $s = $this->seek();
3251
+
3252
+        $expressions = null;
3253
+        $parts = array();
3254
+
3255
+        if ((($this->literal("only") && ($only = true)) || ($this->literal("not") && ($not = true))) && $this->keyword($mediaType)) {
3256
+            $prop = array("mediaType");
3257
+            if (isset($only)) {
3258
+                $prop[] = "only";
3259
+            }
3260
+            if (isset($not)) {
3261
+                $prop[] = "not";
3262
+            }
3263
+            $prop[] = $mediaType;
3264
+            $parts[] = $prop;
3265
+        } else {
3266
+            $this->seek($s);
3267
+        }
3268
+
3269
+
3270
+        if (!empty($mediaType) && !$this->literal("and")) {
3271
+            // ~
3272
+        } else {
3273
+            $this->genericList($expressions, "mediaExpression", "and", false);
3274
+            if (is_array($expressions)) {
3275
+                $parts = array_merge($parts, $expressions[2]);
3276
+            }
3277
+        }
3278
+
3279
+        if (count($parts) == 0) {
3280
+            $this->seek($s);
3281
+            return false;
3282
+        }
3283
+
3284
+        $out = $parts;
3285
+        return true;
3286
+    }
3287
+
3288
+    protected function mediaExpression(&$out)
3289
+    {
3290
+        $feature = null;
3291
+        $variable = null;
3292
+
3293
+        $s = $this->seek();
3294
+        $value = null;
3295
+        if ($this->literal("(") &&
3296
+            $this->keyword($feature) &&
3297
+            ($this->literal(":") && $this->expression($value)) &&
3298
+            $this->literal(")")
3299
+        ) {
3300
+            $out = array("mediaExp", $feature);
3301
+            if ($value) {
3302
+                $out[] = $value;
3303
+            }
3304
+            return true;
3305
+        } elseif ($this->variable($variable)) {
3306
+            $out = array('variable', $variable);
3307
+            return true;
3308
+        }
3309
+
3310
+        $this->seek($s);
3311
+        return false;
3312
+    }
3313
+
3314
+    // an unbounded string stopped by $end
3315
+    protected function openString($end, &$out, $nestingOpen = null, $rejectStrs = null)
3316
+    {
3317
+        $str = null;
3318
+        $inter = null;
3319
+
3320
+        $oldWhite = $this->eatWhiteDefault;
3321
+        $this->eatWhiteDefault = false;
3322
+
3323
+        $stop = array("'", '"', "@{", $end);
3324
+        $stop = array_map(array("lessc", "preg_quote"), $stop);
3325
+        // $stop[] = self::$commentMulti;
3326
+
3327
+        if (!is_null($rejectStrs)) {
3328
+            $stop = array_merge($stop, $rejectStrs);
3329
+        }
3330
+
3331
+        $patt = '(.*?)('.implode("|", $stop).')';
3332
+
3333
+        $nestingLevel = 0;
3334
+
3335
+        $content = array();
3336
+        $m = array();
3337
+        while ($this->match($patt, $m, false)) {
3338
+            if (!empty($m[1])) {
3339
+                $content[] = $m[1];
3340
+                if ($nestingOpen) {
3341
+                    $nestingLevel += substr_count($m[1], $nestingOpen);
3342
+                }
3343
+            }
3344
+
3345
+            $tok = $m[2];
3346
+
3347
+            $this->count -= strlen($tok);
3348
+            if ($tok == $end) {
3349
+                if ($nestingLevel == 0) {
3350
+                    break;
3351
+                } else {
3352
+                    $nestingLevel--;
3353
+                }
3354
+            }
3355
+
3356
+            if (($tok == "'" || $tok == '"') && $this->string($str)) {
3357
+                $content[] = $str;
3358
+                continue;
3359
+            }
3360
+
3361
+            if ($tok == "@{" && $this->interpolation($inter)) {
3362
+                $content[] = $inter;
3363
+                continue;
3364
+            }
3365
+
3366
+            if (!empty($rejectStrs) && in_array($tok, $rejectStrs)) {
3367
+                break;
3368
+            }
3369
+
3370
+            $content[] = $tok;
3371
+            $this->count += strlen($tok);
3372
+        }
3373
+
3374
+        $this->eatWhiteDefault = $oldWhite;
3375
+
3376
+        if (count($content) == 0) {
3377
+            return false;
3378
+        }
3379
+
3380
+        // trim the end
3381
+        if (is_string(end($content))) {
3382
+            $content[count($content) - 1] = rtrim(end($content));
3383
+        }
3384
+
3385
+        $out = array("string", "", $content);
3386
+        return true;
3387
+    }
3388
+
3389
+    protected function string(&$out)
3390
+    {
3391
+        $inter = null;
3392
+
3393
+        $s = $this->seek();
3394
+        if ($this->literal('"', false)) {
3395
+            $delim = '"';
3396
+        } elseif ($this->literal("'", false)) {
3397
+            $delim = "'";
3398
+        } else {
3399
+            return false;
3400
+        }
3401
+
3402
+        $content = array();
3403
+
3404
+        // look for either ending delim , escape, or string interpolation
3405
+        $patt = '([^\n]*?)(@\{|\\\\|'.
3406
+            Lessc::preg_quote($delim).')';
3407
+
3408
+        $oldWhite = $this->eatWhiteDefault;
3409
+        $this->eatWhiteDefault = false;
3410
+
3411
+        $m = array();
3412
+        while ($this->match($patt, $m, false)) {
3413
+            $content[] = $m[1];
3414
+            if ($m[2] == "@{") {
3415
+                $this->count -= strlen($m[2]);
3416
+                if ($this->interpolation($inter)) {
3417
+                    $content[] = $inter;
3418
+                } else {
3419
+                    $this->count += strlen($m[2]);
3420
+                    $content[] = "@{"; // ignore it
3421
+                }
3422
+            } elseif ($m[2] == '\\') {
3423
+                $content[] = $m[2];
3424
+                if ($this->literal($delim, false)) {
3425
+                    $content[] = $delim;
3426
+                }
3427
+            } else {
3428
+                $this->count -= strlen($delim);
3429
+                break; // delim
3430
+            }
3431
+        }
3432
+
3433
+        $this->eatWhiteDefault = $oldWhite;
3434
+
3435
+        if ($this->literal($delim)) {
3436
+            $out = array("string", $delim, $content);
3437
+            return true;
3438
+        }
3439
+
3440
+        $this->seek($s);
3441
+        return false;
3442
+    }
3443
+
3444
+    protected function interpolation(&$out)
3445
+    {
3446
+        $interp = array();
3447
+
3448
+        $oldWhite = $this->eatWhiteDefault;
3449
+        $this->eatWhiteDefault = true;
3450
+
3451
+        $s = $this->seek();
3452
+        if ($this->literal("@{") &&
3453
+            $this->openString("}", $interp, null, array("'", '"', ";")) &&
3454
+            $this->literal("}", false)
3455
+        ) {
3456
+            $out = array("interpolate", $interp);
3457
+            $this->eatWhiteDefault = $oldWhite;
3458
+            if ($this->eatWhiteDefault) {
3459
+                $this->whitespace();
3460
+            }
3461
+            return true;
3462
+        }
3463
+
3464
+        $this->eatWhiteDefault = $oldWhite;
3465
+        $this->seek($s);
3466
+        return false;
3467
+    }
3468
+
3469
+    protected function unit(&$unit)
3470
+    {
3471
+        $m = array();
3472
+
3473
+        // speed shortcut
3474
+        if (isset($this->buffer[$this->count])) {
3475
+            $char = $this->buffer[$this->count];
3476
+            if (!ctype_digit($char) && $char != ".") {
3477
+                return false;
3478
+            }
3479
+        }
3480
+
3481
+        if ($this->match('([0-9]+(?:\.[0-9]*)?|\.[0-9]+)([%a-zA-Z]+)?', $m)) {
3482
+            $unit = array("number", $m[1], empty($m[2]) ? "" : $m[2]);
3483
+            return true;
3484
+        }
3485
+        return false;
3486
+    }
3487
+
3488
+    // a # color
3489
+    protected function color(&$out)
3490
+    {
3491
+        $m = array();
3492
+
3493
+        if ($this->match('(#(?:[0-9a-f]{8}|[0-9a-f]{6}|[0-9a-f]{3}))', $m)) {
3494
+            if (strlen($m[1]) > 7) {
3495
+                $out = array("string", "", array($m[1]));
3496
+            } else {
3497
+                $out = array("raw_color", $m[1]);
3498
+            }
3499
+            return true;
3500
+        }
3501
+
3502
+        return false;
3503
+    }
3504
+
3505
+    // consume an argument definition list surrounded by ()
3506
+    // each argument is a variable name with optional value
3507
+    // or at the end a ... or a variable named followed by ...
3508
+    // arguments are separated by , unless a ; is in the list, then ; is the
3509
+    // delimiter.
3510
+    protected function argumentDef(&$args, &$isVararg)
3511
+    {
3512
+        $value = array();
3513
+        $rhs = null;
3514
+
3515
+        $s = $this->seek();
3516
+        if (!$this->literal('(')) {
3517
+            return false;
3518
+        }
3519
+
3520
+        $values = array();
3521
+        $delim = ",";
3522
+        $method = "expressionList";
3523
+
3524
+        $isVararg = false;
3525
+        while (true) {
3526
+            if ($this->literal("...")) {
3527
+                $isVararg = true;
3528
+                break;
3529
+            }
3530
+
3531
+            if ($this->$method($value)) {
3532
+                if ($value[0] == "variable") {
3533
+                    $arg = array("arg", $value[1]);
3534
+                    $ss = $this->seek();
3535
+
3536
+                    if ($this->assign() && $this->$method($rhs)) {
3537
+                        $arg[] = $rhs;
3538
+                    } else {
3539
+                        $this->seek($ss);
3540
+                        if ($this->literal("...")) {
3541
+                            $arg[0] = "rest";
3542
+                            $isVararg = true;
3543
+                        }
3544
+                    }
3545
+
3546
+                    $values[] = $arg;
3547
+                    if ($isVararg) {
3548
+                        break;
3549
+                    }
3550
+                    continue;
3551
+                } else {
3552
+                    $values[] = array("lit", $value);
3553
+                }
3554
+            }
3555
+
3556
+
3557
+            if (!$this->literal($delim)) {
3558
+                if ($delim == "," && $this->literal(";")) {
3559
+                    // found new delim, convert existing args
3560
+                    $delim = ";";
3561
+                    $method = "propertyValue";
3562
+
3563
+                    // transform arg list
3564
+                    if (isset($values[1])) { // 2 items
3565
+                        $newList = array();
3566
+                        foreach ($values as $i => $arg) {
3567
+                            switch ($arg[0]) {
3568
+                                case "arg":
3569
+                                    if ($i) {
3570
+                                        $this->throwError("Cannot mix ; and , as delimiter types");
3571
+                                    }
3572
+                                    $newList[] = $arg[2];
3573
+                                    break;
3574
+                                case "lit":
3575
+                                    $newList[] = $arg[1];
3576
+                                    break;
3577
+                                case "rest":
3578
+                                    $this->throwError("Unexpected rest before semicolon");
3579
+                            }
3580
+                        }
3581
+
3582
+                        $newList = array("list", ", ", $newList);
3583
+
3584
+                        switch ($values[0][0]) {
3585
+                            case "arg":
3586
+                                $newArg = array("arg", $values[0][1], $newList);
3587
+                                break;
3588
+                            case "lit":
3589
+                                $newArg = array("lit", $newList);
3590
+                                break;
3591
+                        }
3592
+
3593
+                    } elseif ($values) { // 1 item
3594
+                        $newArg = $values[0];
3595
+                    }
3596
+
3597
+                    if ($newArg) {
3598
+                        $values = array($newArg);
3599
+                    }
3600
+                } else {
3601
+                    break;
3602
+                }
3603
+            }
3604
+        }
3605
+
3606
+        if (!$this->literal(')')) {
3607
+            $this->seek($s);
3608
+            return false;
3609
+        }
3610
+
3611
+        $args = $values;
3612
+
3613
+        return true;
3614
+    }
3615
+
3616
+    // consume a list of tags
3617
+    // this accepts a hanging delimiter
3618
+    protected function tags(&$tags, $simple = false, $delim = ',')
3619
+    {
3620
+        $tt = array();
3621
+
3622
+        $tags = array();
3623
+        while ($this->tag($tt, $simple)) {
3624
+            $tags[] = $tt;
3625
+            if (!$this->literal($delim)) {
3626
+                break;
3627
+            }
3628
+        }
3629
+        if (count($tags) == 0) {
3630
+            return false;
3631
+        }
3632
+
3633
+        return true;
3634
+    }
3635
+
3636
+    // list of tags of specifying mixin path
3637
+    // optionally separated by > (lazy, accepts extra >)
3638
+    protected function mixinTags(&$tags)
3639
+    {
3640
+        $tt = array();
3641
+
3642
+        $tags = array();
3643
+        while ($this->tag($tt, true)) {
3644
+            $tags[] = $tt;
3645
+            $this->literal(">");
3646
+        }
3647
+
3648
+        if (!$tags) {
3649
+            return false;
3650
+        }
3651
+
3652
+        return true;
3653
+    }
3654
+
3655
+    // a bracketed value (contained within in a tag definition)
3656
+    protected function tagBracket(&$parts, &$hasExpression)
3657
+    {
3658
+        $str = null;
3659
+        $inter = null;
3660
+        $word = null;
3661
+
3662
+        // speed shortcut
3663
+        if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] != "[") {
3664
+            return false;
3665
+        }
3666
+
3667
+        $s = $this->seek();
3668
+
3669
+        $hasInterpolation = false;
3670
+
3671
+        if ($this->literal("[", false)) {
3672
+            $attrParts = array("[");
3673
+            // keyword, string, operator
3674
+            while (true) {
3675
+                if ($this->literal("]", false)) {
3676
+                    $this->count--;
3677
+                    break; // get out early
3678
+                }
3679
+
3680
+                $m = array();
3681
+                if ($this->match('\s+', $m)) {
3682
+                    $attrParts[] = " ";
3683
+                    continue;
3684
+                }
3685
+                if ($this->string($str)) {
3686
+                    // escape parent selector, (yuck)
3687
+                    foreach ($str[2] as &$chunk) {
3688
+                        $chunk = str_replace($this->lessc->parentSelector, "$&$", $chunk);
3689
+                    }
3690
+
3691
+                    $attrParts[] = $str;
3692
+                    $hasInterpolation = true;
3693
+                    continue;
3694
+                }
3695
+
3696
+                if ($this->keyword($word)) {
3697
+                    $attrParts[] = $word;
3698
+                    continue;
3699
+                }
3700
+
3701
+                if ($this->interpolation($inter)) {
3702
+                    $attrParts[] = $inter;
3703
+                    $hasInterpolation = true;
3704
+                    continue;
3705
+                }
3706
+
3707
+                // operator, handles attr namespace too
3708
+                if ($this->match('[|-~\$\*\^=]+', $m)) {
3709
+                    $attrParts[] = $m[0];
3710
+                    continue;
3711
+                }
3712
+
3713
+                break;
3714
+            }
3715
+
3716
+            if ($this->literal("]", false)) {
3717
+                $attrParts[] = "]";
3718
+                foreach ($attrParts as $part) {
3719
+                    $parts[] = $part;
3720
+                }
3721
+                $hasExpression = $hasExpression || $hasInterpolation;
3722
+                return true;
3723
+            }
3724
+            $this->seek($s);
3725
+        }
3726
+
3727
+        $this->seek($s);
3728
+        return false;
3729
+    }
3730
+
3731
+    // a space separated list of selectors
3732
+    protected function tag(&$tag, $simple = false)
3733
+    {
3734
+        $interp = null;
3735
+        $unit = null;
3736
+
3737
+        if ($simple) {
3738
+            $chars = '^@,:;{}\][>\(\) "\'';
3739
+        } else {
3740
+            $chars = '^@,;{}["\'';
3741
+        }
3742
+        $s = $this->seek();
3743
+
3744
+        $hasExpression = false;
3745
+        $parts = array();
3746
+        while ($this->tagBracket($parts, $hasExpression));
3747
+
3748
+        $oldWhite = $this->eatWhiteDefault;
3749
+        $this->eatWhiteDefault = false;
3750
+
3751
+        while (true) {
3752
+            $m = array();
3753
+            if ($this->match('(['.$chars.'0-9]['.$chars.']*)', $m)) {
3754
+                $parts[] = $m[1];
3755
+                if ($simple) {
3756
+                    break;
3757
+                }
3758
+
3759
+                while ($this->tagBracket($parts, $hasExpression));
3760
+                continue;
3761
+            }
3762
+
3763
+            if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] == "@") {
3764
+                if ($this->interpolation($interp)) {
3765
+                    $hasExpression = true;
3766
+                    $interp[2] = true; // don't unescape
3767
+                    $parts[] = $interp;
3768
+                    continue;
3769
+                }
3770
+
3771
+                if ($this->literal("@")) {
3772
+                    $parts[] = "@";
3773
+                    continue;
3774
+                }
3775
+            }
3776
+
3777
+            if ($this->unit($unit)) { // for keyframes
3778
+                $parts[] = $unit[1];
3779
+                $parts[] = $unit[2];
3780
+                continue;
3781
+            }
3782
+
3783
+            break;
3784
+        }
3785
+
3786
+        $this->eatWhiteDefault = $oldWhite;
3787
+        if (!$parts) {
3788
+            $this->seek($s);
3789
+            return false;
3790
+        }
3791
+
3792
+        if ($hasExpression) {
3793
+            $tag = array("exp", array("string", "", $parts));
3794
+        } else {
3795
+            $tag = trim(implode($parts));
3796
+        }
3797
+
3798
+        $this->whitespace();
3799
+        return true;
3800
+    }
3801
+
3802
+    // a css function
3803
+    protected function func(&$func)
3804
+    {
3805
+        $s = $this->seek();
3806
+
3807
+        $m = array();
3808
+        $value = array();
3809
+        $string = array();
3810
+        $name = null;
3811
+
3812
+        if ($this->match('(%|[\w\-_][\w\-_:\.]+|[\w_])', $m) && $this->literal('(')) {
3813
+            $fname = $m[1];
3814
+
3815
+            $sPreArgs = $this->seek();
3816
+
3817
+            $args = array();
3818
+            while (true) {
3819
+                $ss = $this->seek();
3820
+                // this ugly nonsense is for ie filter properties
3821
+                if ($this->keyword($name) && $this->literal('=') && $this->expressionList($value)) {
3822
+                    $args[] = array("string", "", array($name, "=", $value));
3823
+                } else {
3824
+                    $this->seek($ss);
3825
+                    if ($this->expressionList($value)) {
3826
+                        $args[] = $value;
3827
+                    }
3828
+                }
3829
+
3830
+                if (!$this->literal(',')) {
3831
+                    break;
3832
+                }
3833
+            }
3834
+            $args = array('list', ',', $args);
3835
+
3836
+            if ($this->literal(')')) {
3837
+                $func = array('function', $fname, $args);
3838
+                return true;
3839
+            } elseif ($fname == 'url') {
3840
+                // couldn't parse and in url? treat as string
3841
+                $this->seek($sPreArgs);
3842
+                if ($this->openString(")", $string) && $this->literal(")")) {
3843
+                    $func = array('function', $fname, $string);
3844
+                    return true;
3845
+                }
3846
+            }
3847
+        }
3848
+
3849
+        $this->seek($s);
3850
+        return false;
3851
+    }
3852
+
3853
+    // consume a less variable
3854
+    protected function variable(&$name)
3855
+    {
3856
+        $sub = null;
3857
+        $name = null;
3858
+
3859
+        $s = $this->seek();
3860
+        if ($this->literal($this->lessc->vPrefix, false) &&
3861
+            ($this->variable($sub) || $this->keyword($name))
3862
+        ) {
3863
+            if (!empty($sub)) {
3864
+                $name = array('variable', $sub);
3865
+            } else {
3866
+                $name = $this->lessc->vPrefix.$name;
3867
+            }
3868
+            return true;
3869
+        }
3870
+
3871
+        $name = null;
3872
+        $this->seek($s);
3873
+        return false;
3874
+    }
3875
+
3876
+    /**
3877
+     * Consume an assignment operator
3878
+     * Can optionally take a name that will be set to the current property name
3879
+     */
3880
+    protected function assign($name = null)
3881
+    {
3882
+        if ($name) {
3883
+            $this->currentProperty = $name;
3884
+        }
3885
+        return $this->literal(':') || $this->literal('=');
3886
+    }
3887
+
3888
+    // consume a keyword
3889
+    protected function keyword(&$word)
3890
+    {
3891
+        $m = array();
3892
+        if ($this->match('([\w_\-\*!"][\w\-_"]*)', $m)) {
3893
+            $word = $m[1];
3894
+            return true;
3895
+        }
3896
+        return false;
3897
+    }
3898
+
3899
+    // consume an end of statement delimiter
3900
+    protected function end()
3901
+    {
3902
+        if ($this->literal(';', false)) {
3903
+            return true;
3904
+        } elseif ($this->count == strlen($this->buffer) || $this->buffer[$this->count] == '}') {
3905
+            // if there is end of file or a closing block next then we don't need a ;
3906
+            return true;
3907
+        }
3908
+        return false;
3909
+    }
3910
+
3911
+    protected function guards(&$guards)
3912
+    {
3913
+        $g = null;
3914
+
3915
+        $s = $this->seek();
3916
+
3917
+        if (!$this->literal("when")) {
3918
+            $this->seek($s);
3919
+            return false;
3920
+        }
3921
+
3922
+        $guards = array();
3923
+
3924
+        while ($this->guardGroup($g)) {
3925
+            $guards[] = $g;
3926
+            if (!$this->literal(",")) {
3927
+                break;
3928
+            }
3929
+        }
3930
+
3931
+        if (count($guards) == 0) {
3932
+            $guards = null;
3933
+            $this->seek($s);
3934
+            return false;
3935
+        }
3936
+
3937
+        return true;
3938
+    }
3939
+
3940
+    // a bunch of guards that are and'd together
3941
+    // TODO rename to guardGroup
3942
+    protected function guardGroup(&$guardGroup)
3943
+    {
3944
+        $guard = null;
3945
+
3946
+        $s = $this->seek();
3947
+        $guardGroup = array();
3948
+        while ($this->guard($guard)) {
3949
+            $guardGroup[] = $guard;
3950
+            if (!$this->literal("and")) {
3951
+                break;
3952
+            }
3953
+        }
3954
+
3955
+        if (count($guardGroup) == 0) {
3956
+            $guardGroup = null;
3957
+            $this->seek($s);
3958
+            return false;
3959
+        }
3960
+
3961
+        return true;
3962
+    }
3963
+
3964
+    protected function guard(&$guard)
3965
+    {
3966
+        $exp = null;
3967
+
3968
+        $s = $this->seek();
3969
+        $negate = $this->literal("not");
3970
+
3971
+        if ($this->literal("(") && $this->expression($exp) && $this->literal(")")) {
3972
+            $guard = $exp;
3973
+            if ($negate) {
3974
+                $guard = array("negate", $guard);
3975
+            }
3976
+            return true;
3977
+        }
3978
+
3979
+        $this->seek($s);
3980
+        return false;
3981
+    }
3982
+
3983
+    /* raw parsing functions */
3984
+
3985
+    protected function literal($what, $eatWhitespace = null)
3986
+    {
3987
+        if ($eatWhitespace === null) {
3988
+            $eatWhitespace = $this->eatWhiteDefault;
3989
+        }
3990
+
3991
+        // shortcut on single letter
3992
+        if (!isset($what[1]) && isset($this->buffer[$this->count])) {
3993
+            if ($this->buffer[$this->count] == $what) {
3994
+                if (!$eatWhitespace) {
3995
+                    $this->count++;
3996
+                    return true;
3997
+                }
3998
+            // goes below...
3999
+            } else {
4000
+                return false;
4001
+            }
4002
+        }
4003
+
4004
+        if (!isset(self::$literalCache[$what])) {
4005
+            self::$literalCache[$what] = Lessc::preg_quote($what);
4006
+        }
4007
+
4008
+        $m = array();
4009
+        return $this->match(self::$literalCache[$what], $m, $eatWhitespace);
4010
+    }
4011
+
4012
+    protected function genericList(&$out, $parseItem, $delim = "", $flatten = true)
4013
+    {
4014
+        $value = null;
4015
+
4016
+        $s = $this->seek();
4017
+        $items = array();
4018
+        while ($this->$parseItem($value)) {
4019
+            $items[] = $value;
4020
+            if ($delim) {
4021
+                if (!$this->literal($delim)) {
4022
+                    break;
4023
+                }
4024
+            }
4025
+        }
4026
+
4027
+        if (count($items) == 0) {
4028
+            $this->seek($s);
4029
+            return false;
4030
+        }
4031
+
4032
+        if ($flatten && count($items) == 1) {
4033
+            $out = $items[0];
4034
+        } else {
4035
+            $out = array("list", $delim, $items);
4036
+        }
4037
+
4038
+        return true;
4039
+    }
4040
+
4041
+
4042
+    // advance counter to next occurrence of $what
4043
+    // $until - don't include $what in advance
4044
+    // $allowNewline, if string, will be used as valid char set
4045
+    protected function to($what, &$out, $until = false, $allowNewline = false)
4046
+    {
4047
+        if (is_string($allowNewline)) {
4048
+            $validChars = $allowNewline;
4049
+        } else {
4050
+            $validChars = $allowNewline ? "." : "[^\n]";
4051
+        }
4052
+        $m = array();
4053
+        if (!$this->match('('.$validChars.'*?)'.Lessc::preg_quote($what), $m, !$until)) {
4054
+            return false;
4055
+        }
4056
+        if ($until) {
4057
+            $this->count -= strlen($what); // give back $what
4058
+        }
4059
+        $out = $m[1];
4060
+        return true;
4061
+    }
4062
+
4063
+    // try to match something on head of buffer
4064
+    protected function match($regex, &$out, $eatWhitespace = null)
4065
+    {
4066
+        if ($eatWhitespace === null) {
4067
+            $eatWhitespace = $this->eatWhiteDefault;
4068
+        }
4069
+
4070
+        $r = '/'.$regex.($eatWhitespace && !$this->writeComments ? '\s*' : '').'/Ais';
4071
+        if (preg_match($r, $this->buffer, $out, 0, $this->count)) {
4072
+            $this->count += strlen($out[0]);
4073
+            if ($eatWhitespace && $this->writeComments) {
4074
+                $this->whitespace();
4075
+            }
4076
+            return true;
4077
+        }
4078
+        return false;
4079
+    }
4080
+
4081
+    // match some whitespace
4082
+    protected function whitespace()
4083
+    {
4084
+        if ($this->writeComments) {
4085
+            $gotWhite = false;
4086
+            $m = array();
4087
+            while (preg_match(self::$whitePattern, $this->buffer, $m, 0, $this->count)) {
4088
+                if (isset($m[1]) && empty($this->seenComments[$this->count])) {
4089
+                    $this->append(array("comment", $m[1]));
4090
+                    $this->seenComments[$this->count] = true;
4091
+                }
4092
+                $this->count += strlen($m[0]);
4093
+                $gotWhite = true;
4094
+            }
4095
+            return $gotWhite;
4096
+        } else {
4097
+            $this->match("", $m);
4098
+            return strlen($m[0]) > 0;
4099
+        }
4100
+    }
4101
+
4102
+    // match something without consuming it
4103
+    protected function peek($regex, &$out = null, $from = null)
4104
+    {
4105
+        if (is_null($from)) {
4106
+            $from = $this->count;
4107
+        }
4108
+        $r = '/'.$regex.'/Ais';
4109
+        $result = preg_match($r, $this->buffer, $out, 0, $from);
4110
+
4111
+        return $result;
4112
+    }
4113
+
4114
+    // seek to a spot in the buffer or return where we are on no argument
4115
+    protected function seek($where = null)
4116
+    {
4117
+        if ($where === null) {
4118
+            return $this->count;
4119
+        } else {
4120
+            $this->count = $where;
4121
+        }
4122
+        return true;
4123
+    }
4124
+
4125
+    /* misc functions */
4126
+
4127
+    public function throwError($msg = "parse error", $count = null)
4128
+    {
4129
+        $count = is_null($count) ? $this->count : $count;
4130
+
4131
+        $line = $this->line +
4132
+            substr_count(substr($this->buffer, 0, $count), "\n");
4133
+
4134
+        if (!empty($this->sourceName)) {
4135
+            $loc = "$this->sourceName on line $line";
4136
+        } else {
4137
+            $loc = "line: $line";
4138
+        }
4139
+
4140
+        // TODO this depends on $this->count
4141
+        $m = array();
4142
+        if ($this->peek("(.*?)(\n|$)", $m, $count)) {
4143
+            throw new exception("$msg: failed at `$m[1]` $loc");
4144
+        } else {
4145
+            throw new exception("$msg: $loc");
4146
+        }
4147
+    }
4148
+
4149
+    protected function pushBlock($selectors = null, $type = null)
4150
+    {
4151
+        $b = new stdclass();
4152
+        $b->parent = $this->env;
4153
+
4154
+        $b->type = $type;
4155
+        $b->id = self::$nextBlockId++;
4156
+
4157
+        $b->isVararg = false; // TODO: kill me from here
4158
+        $b->tags = $selectors;
4159
+
4160
+        $b->props = array();
4161
+        $b->children = array();
4162
+
4163
+        $this->env = $b;
4164
+        return $b;
4165
+    }
4166
+
4167
+    // push a block that doesn't multiply tags
4168
+    protected function pushSpecialBlock($type)
4169
+    {
4170
+        return $this->pushBlock(null, $type);
4171
+    }
4172
+
4173
+    // append a property to the current block
4174
+    protected function append($prop, $pos = null)
4175
+    {
4176
+        if ($pos !== null) {
4177
+            $prop[-1] = $pos;
4178
+        }
4179
+        $this->env->props[] = $prop;
4180
+    }
4181
+
4182
+    // pop something off the stack
4183
+    protected function pop()
4184
+    {
4185
+        $old = $this->env;
4186
+        $this->env = $this->env->parent;
4187
+        return $old;
4188
+    }
4189
+
4190
+    // remove comments from $text
4191
+    // todo: make it work for all functions, not just url
4192
+    protected function removeComments($text)
4193
+    {
4194
+        $look = array(
4195
+            'url(', '//', '/*', '"', "'"
4196
+        );
4197
+
4198
+        $out = '';
4199
+        $min = null;
4200
+        while (true) {
4201
+            // find the next item
4202
+            foreach ($look as $token) {
4203
+                $pos = strpos($text, $token);
4204
+                if ($pos !== false) {
4205
+                    if (!isset($min) || $pos < $min[1]) {
4206
+                        $min = array($token, $pos);
4207
+                    }
4208
+                }
4209
+            }
4210
+
4211
+            if (is_null($min)) {
4212
+                break;
4213
+            }
4214
+
4215
+            $count = $min[1];
4216
+            $skip = 0;
4217
+            $newlines = 0;
4218
+            switch ($min[0]) {
4219
+                case 'url(':
4220
+                    $m = array();
4221
+                    if (preg_match('/url\(.*?\)/', $text, $m, 0, $count)) {
4222
+                        $count += strlen($m[0]) - strlen($min[0]);
4223
+                    }
4224
+                    break;
4225
+                case '"':
4226
+                case "'":
4227
+                    $m = array();
4228
+                    if (preg_match('/'.$min[0].'.*?(?<!\\\\)'.$min[0].'/', $text, $m, 0, $count)) {
4229
+                        $count += strlen($m[0]) - 1;
4230
+                    }
4231
+                    break;
4232
+                case '//':
4233
+                    $skip = strpos($text, "\n", $count);
4234
+                    if ($skip === false) {
4235
+                        $skip = strlen($text) - $count;
4236
+                    } else {
4237
+                        $skip -= $count;
4238
+                    }
4239
+                    break;
4240
+                case '/*':
4241
+                    $m = array();
4242
+                    if (preg_match('/\/\*.*?\*\//s', $text, $m, 0, $count)) {
4243
+                        $skip = strlen($m[0]);
4244
+                        $newlines = substr_count($m[0], "\n");
4245
+                    }
4246
+                    break;
4247
+            }
4248
+
4249
+            if ($skip == 0) {
4250
+                $count += strlen($min[0]);
4251
+            }
4252
+
4253
+            $out .= substr($text, 0, $count).str_repeat("\n", $newlines);
4254
+            $text = substr($text, $count + $skip);
4255
+
4256
+            $min = null;
4257
+        }
4258
+
4259
+        return $out.$text;
4260
+    }
4261 4261
 }
4262 4262
 
4263 4263
 class lessc_formatter_classic
4264 4264
 {
4265
-	public $indentChar = "  ";
4266
-
4267
-	public $break = "\n";
4268
-	public $open = " {";
4269
-	public $close = "}";
4270
-	public $selectorSeparator = ", ";
4271
-	public $assignSeparator = ":";
4272
-
4273
-	public $openSingle = " { ";
4274
-	public $closeSingle = " }";
4275
-
4276
-	public $disableSingle = false;
4277
-	public $breakSelectors = false;
4278
-
4279
-	public $compressColors = false;
4280
-	public $indentLevel;
4281
-
4282
-	public function __construct()
4283
-	{
4284
-		$this->indentLevel = 0;
4285
-	}
4286
-
4287
-	public function indentStr($n = 0)
4288
-	{
4289
-		return str_repeat($this->indentChar, max($this->indentLevel + $n, 0));
4290
-	}
4291
-
4292
-	public function property($name, $value)
4293
-	{
4294
-		return $name.$this->assignSeparator.$value.";";
4295
-	}
4296
-
4297
-	protected function isEmpty($block)
4298
-	{
4299
-		if (empty($block->lines)) {
4300
-			foreach ($block->children as $child) {
4301
-				if (!$this->isEmpty($child)) {
4302
-					return false;
4303
-				}
4304
-			}
4305
-
4306
-			return true;
4307
-		}
4308
-		return false;
4309
-	}
4310
-
4311
-	public function block($block)
4312
-	{
4313
-		if ($this->isEmpty($block)) {
4314
-			return;
4315
-		}
4316
-
4317
-		$inner = $pre = $this->indentStr();
4318
-
4319
-		$isSingle = !$this->disableSingle &&
4320
-			is_null($block->type) && count($block->lines) == 1;
4321
-
4322
-		if (!empty($block->selectors)) {
4323
-			$this->indentLevel++;
4324
-
4325
-			if ($this->breakSelectors) {
4326
-				$selectorSeparator = $this->selectorSeparator.$this->break.$pre;
4327
-			} else {
4328
-				$selectorSeparator = $this->selectorSeparator;
4329
-			}
4330
-
4331
-			echo $pre.
4332
-				implode($selectorSeparator, $block->selectors);
4333
-			if ($isSingle) {
4334
-				echo $this->openSingle;
4335
-				$inner = "";
4336
-			} else {
4337
-				echo $this->open.$this->break;
4338
-				$inner = $this->indentStr();
4339
-			}
4340
-		}
4341
-
4342
-		if (!empty($block->lines)) {
4343
-			$glue = $this->break.$inner;
4344
-			echo $inner.implode($glue, $block->lines);
4345
-			if (!$isSingle && !empty($block->children)) {
4346
-				echo $this->break;
4347
-			}
4348
-		}
4349
-
4350
-		foreach ($block->children as $child) {
4351
-			$this->block($child);
4352
-		}
4353
-
4354
-		if (!empty($block->selectors)) {
4355
-			if (!$isSingle && empty($block->children)) {
4356
-				echo $this->break;
4357
-			}
4358
-
4359
-			if ($isSingle) {
4360
-				echo $this->closeSingle.$this->break;
4361
-			} else {
4362
-				echo $pre.$this->close.$this->break;
4363
-			}
4364
-
4365
-			$this->indentLevel--;
4366
-		}
4367
-	}
4265
+    public $indentChar = "  ";
4266
+
4267
+    public $break = "\n";
4268
+    public $open = " {";
4269
+    public $close = "}";
4270
+    public $selectorSeparator = ", ";
4271
+    public $assignSeparator = ":";
4272
+
4273
+    public $openSingle = " { ";
4274
+    public $closeSingle = " }";
4275
+
4276
+    public $disableSingle = false;
4277
+    public $breakSelectors = false;
4278
+
4279
+    public $compressColors = false;
4280
+    public $indentLevel;
4281
+
4282
+    public function __construct()
4283
+    {
4284
+        $this->indentLevel = 0;
4285
+    }
4286
+
4287
+    public function indentStr($n = 0)
4288
+    {
4289
+        return str_repeat($this->indentChar, max($this->indentLevel + $n, 0));
4290
+    }
4291
+
4292
+    public function property($name, $value)
4293
+    {
4294
+        return $name.$this->assignSeparator.$value.";";
4295
+    }
4296
+
4297
+    protected function isEmpty($block)
4298
+    {
4299
+        if (empty($block->lines)) {
4300
+            foreach ($block->children as $child) {
4301
+                if (!$this->isEmpty($child)) {
4302
+                    return false;
4303
+                }
4304
+            }
4305
+
4306
+            return true;
4307
+        }
4308
+        return false;
4309
+    }
4310
+
4311
+    public function block($block)
4312
+    {
4313
+        if ($this->isEmpty($block)) {
4314
+            return;
4315
+        }
4316
+
4317
+        $inner = $pre = $this->indentStr();
4318
+
4319
+        $isSingle = !$this->disableSingle &&
4320
+            is_null($block->type) && count($block->lines) == 1;
4321
+
4322
+        if (!empty($block->selectors)) {
4323
+            $this->indentLevel++;
4324
+
4325
+            if ($this->breakSelectors) {
4326
+                $selectorSeparator = $this->selectorSeparator.$this->break.$pre;
4327
+            } else {
4328
+                $selectorSeparator = $this->selectorSeparator;
4329
+            }
4330
+
4331
+            echo $pre.
4332
+                implode($selectorSeparator, $block->selectors);
4333
+            if ($isSingle) {
4334
+                echo $this->openSingle;
4335
+                $inner = "";
4336
+            } else {
4337
+                echo $this->open.$this->break;
4338
+                $inner = $this->indentStr();
4339
+            }
4340
+        }
4341
+
4342
+        if (!empty($block->lines)) {
4343
+            $glue = $this->break.$inner;
4344
+            echo $inner.implode($glue, $block->lines);
4345
+            if (!$isSingle && !empty($block->children)) {
4346
+                echo $this->break;
4347
+            }
4348
+        }
4349
+
4350
+        foreach ($block->children as $child) {
4351
+            $this->block($child);
4352
+        }
4353
+
4354
+        if (!empty($block->selectors)) {
4355
+            if (!$isSingle && empty($block->children)) {
4356
+                echo $this->break;
4357
+            }
4358
+
4359
+            if ($isSingle) {
4360
+                echo $this->closeSingle.$this->break;
4361
+            } else {
4362
+                echo $pre.$this->close.$this->break;
4363
+            }
4364
+
4365
+            $this->indentLevel--;
4366
+        }
4367
+    }
4368 4368
 }
4369 4369
 
4370 4370
 /**
@@ -4372,17 +4372,17 @@  discard block
 block discarded – undo
4372 4372
  */
4373 4373
 class lessc_formatter_compressed extends lessc_formatter_classic
4374 4374
 {
4375
-	public $disableSingle = true;
4376
-	public $open = "{";
4377
-	public $selectorSeparator = ",";
4378
-	public $assignSeparator = ":";
4379
-	public $break = "";
4380
-	public $compressColors = true;
4381
-
4382
-	public function indentStr($n = 0)
4383
-	{
4384
-		return "";
4385
-	}
4375
+    public $disableSingle = true;
4376
+    public $open = "{";
4377
+    public $selectorSeparator = ",";
4378
+    public $assignSeparator = ":";
4379
+    public $break = "";
4380
+    public $compressColors = true;
4381
+
4382
+    public function indentStr($n = 0)
4383
+    {
4384
+        return "";
4385
+    }
4386 4386
 }
4387 4387
 
4388 4388
 /**
@@ -4390,8 +4390,8 @@  discard block
 block discarded – undo
4390 4390
  */
4391 4391
 class lessc_formatter_lessjs extends lessc_formatter_classic
4392 4392
 {
4393
-	public $disableSingle = true;
4394
-	public $breakSelectors = true;
4395
-	public $assignSeparator = ": ";
4396
-	public $selectorSeparator = ",";
4393
+    public $disableSingle = true;
4394
+    public $breakSelectors = true;
4395
+    public $assignSeparator = ": ";
4396
+    public $selectorSeparator = ",";
4397 4397
 }
Please login to merge, or discard this patch.
Spacing   +59 added lines, -59 removed lines patch added patch discarded remove patch
@@ -79,8 +79,8 @@  discard block
 block discarded – undo
79 79
 	protected function findImport($url)
80 80
 	{
81 81
 		foreach ((array) $this->importDir as $dir) {
82
-			$full = $dir.(substr($dir, -1) != '/' ? '/' : '').$url;
83
-			if ($this->fileExists($file = $full.'.less') || $this->fileExists($file = $full)) {
82
+			$full = $dir . (substr($dir, -1) != '/' ? '/' : '') . $url;
83
+			if ($this->fileExists($file = $full . '.less') || $this->fileExists($file = $full)) {
84 84
 				return $file;
85 85
 			}
86 86
 		}
@@ -231,9 +231,9 @@  discard block
 block discarded – undo
231 231
 				$this->compileMedia($block);
232 232
 				break;
233 233
 			case "directive":
234
-				$name = "@".$block->name;
234
+				$name = "@" . $block->name;
235 235
 				if (!empty($block->value)) {
236
-					$name .= " ".$this->compileValue($this->reduce($block->value));
236
+					$name .= " " . $this->compileValue($this->reduce($block->value));
237 237
 				}
238 238
 
239 239
 				$this->compileNestedBlock($block, array($name));
@@ -405,8 +405,8 @@  discard block
 block discarded – undo
405 405
 						break;
406 406
 					case "mediaExp":
407 407
 						if (isset($q[2])) {
408
-							$parts[] = "($q[1]: ".
409
-								$this->compileValue($this->reduce($q[2])).")";
408
+							$parts[] = "($q[1]: " .
409
+								$this->compileValue($this->reduce($q[2])) . ")";
410 410
 						} else {
411 411
 							$parts[] = "($q[1])";
412 412
 						}
@@ -424,7 +424,7 @@  discard block
 block discarded – undo
424 424
 
425 425
 		$out = "@media";
426 426
 		if (!empty($parts)) {
427
-			$out .= " ".
427
+			$out .= " " .
428 428
 				implode($this->formatter->selectorSeparator, $compiledQueries);
429 429
 		}
430 430
 		return $out;
@@ -511,7 +511,7 @@  discard block
 block discarded – undo
511 511
 				if ($count > 0) {
512 512
 					$out[] = trim($child);
513 513
 				} else {
514
-					$out[] = trim($parent.' '.$child);
514
+					$out[] = trim($parent . ' ' . $child);
515 515
 				}
516 516
 			}
517 517
 		}
@@ -716,7 +716,7 @@  discard block
 block discarded – undo
716 716
 					$value = $a[2];
717 717
 				} else {
718 718
 					$value = null; // :(
719
-					$this->throwError("Failed to assign arg ".$a[1]); // This ends function by throwing an exception
719
+					$this->throwError("Failed to assign arg " . $a[1]); // This ends function by throwing an exception
720 720
 				}
721 721
 
722 722
 				$value = $this->reduce($value);
@@ -781,7 +781,7 @@  discard block
 block discarded – undo
781 781
 							$orderedArgs[] = $this->reduce($arg[1]);
782 782
 							break;
783 783
 						default:
784
-							$this->throwError("Unknown arg type: ".$arg[0]);
784
+							$this->throwError("Unknown arg type: " . $arg[0]);
785 785
 					}
786 786
 				}
787 787
 
@@ -846,7 +846,7 @@  discard block
 block discarded – undo
846 846
 				break;
847 847
 			case "directive":
848 848
 				list(, $name, $value) = $prop;
849
-				$out->lines[] = "@$name ".$this->compileValue($this->reduce($value)).';';
849
+				$out->lines[] = "@$name " . $this->compileValue($this->reduce($value)) . ';';
850 850
 				break;
851 851
 			case "comment":
852 852
 				$out->lines[] = $prop[1];
@@ -862,7 +862,7 @@  discard block
 block discarded – undo
862 862
 				$result = $this->tryImport($importPath, $block, $out);
863 863
 
864 864
 				$this->env->imports[$importId] = $result === false ?
865
-					array(false, "@import ".$this->compileValue($importPath).";") : $result;
865
+					array(false, "@import " . $this->compileValue($importPath) . ";") : $result;
866 866
 
867 867
 				break;
868 868
 			case "import_mixin":
@@ -917,7 +917,7 @@  discard block
 block discarded – undo
917 917
 				if ($this->numberPrecision !== null) {
918 918
 					$num = round($num, $this->numberPrecision);
919 919
 				}
920
-				return $num.$unit;
920
+				return $num . $unit;
921 921
 			case 'string':
922 922
 				// [1] - contents of string (includes quotes)
923 923
 				list(, $delim, $content) = $value;
@@ -926,7 +926,7 @@  discard block
 block discarded – undo
926 926
 						$part = $this->compileValue($part);
927 927
 					}
928 928
 				}
929
-				return $delim.implode($content).$delim;
929
+				return $delim . implode($content) . $delim;
930 930
 			case 'color':
931 931
 				// [1] - red component (either number or a %)
932 932
 				// [2] - green component
@@ -938,7 +938,7 @@  discard block
 block discarded – undo
938 938
 				$b = round($b);
939 939
 
940 940
 				if (count($value) == 5 && $value[4] != 1) { // rgba
941
-					return 'rgba('.$r.','.$g.','.$b.','.$value[4].')';
941
+					return 'rgba(' . $r . ',' . $g . ',' . $b . ',' . $value[4] . ')';
942 942
 				}
943 943
 
944 944
 				$h = sprintf("#%02x%02x%02x", $r, $g, $b);
@@ -946,7 +946,7 @@  discard block
 block discarded – undo
946 946
 				if (!empty($this->formatter->compressColors)) {
947 947
 					// Converting hex color to short notation (e.g. #003399 to #039)
948 948
 					if ($h[1] === $h[2] && $h[3] === $h[4] && $h[5] === $h[6]) {
949
-						$h = '#'.$h[1].$h[3].$h[5];
949
+						$h = '#' . $h[1] . $h[3] . $h[5];
950 950
 					}
951 951
 				}
952 952
 
@@ -954,7 +954,7 @@  discard block
 block discarded – undo
954 954
 
955 955
 			case 'function':
956 956
 				list(, $name, $args) = $value;
957
-				return $name.'('.$this->compileValue($args).')';
957
+				return $name . '(' . $this->compileValue($args) . ')';
958 958
 			default: // assumed to be unit
959 959
 				$this->throwError("unknown value type: $value[0]");
960 960
 		}
@@ -1119,7 +1119,7 @@  discard block
 block discarded – undo
1119 1119
 			}
1120 1120
 		}
1121 1121
 
1122
-		return 'url("'.$url.'")';
1122
+		return 'url("' . $url . '")';
1123 1123
 	}
1124 1124
 
1125 1125
 	// utility func to unquote a string
@@ -1167,7 +1167,7 @@  discard block
 block discarded – undo
1167 1167
 				$i++;
1168 1168
 				$rep = $this->compileValue($this->lib_e($val));
1169 1169
 				$template = preg_replace(
1170
-					'/'.self::preg_quote($match).'/',
1170
+					'/' . self::preg_quote($match) . '/',
1171 1171
 					$rep,
1172 1172
 					$template,
1173 1173
 					1
@@ -1520,7 +1520,7 @@  discard block
 block discarded – undo
1520 1520
 			$numValues = count($values);
1521 1521
 			if ($expectedArgs != $numValues) {
1522 1522
 				if ($name) {
1523
-					$name = $name.": ";
1523
+					$name = $name . ": ";
1524 1524
 				}
1525 1525
 
1526 1526
 				$this->throwError("{$name}expecting $expectedArgs arguments, got $numValues");
@@ -1712,7 +1712,7 @@  discard block
 block discarded – undo
1712 1712
 			case "interpolate":
1713 1713
 				$reduced = $this->reduce($value[1]);
1714 1714
 				$var = $this->compileValue($reduced);
1715
-				$res = $this->reduce(array("variable", $this->vPrefix.$var));
1715
+				$res = $this->reduce(array("variable", $this->vPrefix . $var));
1716 1716
 
1717 1717
 				if ($res[0] == "raw_color") {
1718 1718
 					$res = $this->coerceColor($res);
@@ -1727,7 +1727,7 @@  discard block
 block discarded – undo
1727 1727
 				$key = $value[1];
1728 1728
 				if (is_array($key)) {
1729 1729
 					$key = $this->reduce($key);
1730
-					$key = $this->vPrefix.$this->compileValue($this->lib_e($key));
1730
+					$key = $this->vPrefix . $this->compileValue($this->lib_e($key));
1731 1731
 				}
1732 1732
 
1733 1733
 				$seen = & $this->env->seenNames;
@@ -1773,7 +1773,7 @@  discard block
 block discarded – undo
1773 1773
 				}
1774 1774
 
1775 1775
 				$f = isset($this->libFunctions[$name]) ?
1776
-					$this->libFunctions[$name] : array($this, 'lib_'.str_replace('-', '_', $name));
1776
+					$this->libFunctions[$name] : array($this, 'lib_' . str_replace('-', '_', $name));
1777 1777
 
1778 1778
 				if (is_callable($f)) {
1779 1779
 					if ($args[0] == 'list') {
@@ -1849,7 +1849,7 @@  discard block
 block discarded – undo
1849 1849
 					$t = intval($num) % $width;
1850 1850
 					$num /= $width;
1851 1851
 
1852
-					$c[$i] = $t * (256 / $width) + $t * floor(16/$width);
1852
+					$c[$i] = $t * (256 / $width) + $t * floor(16 / $width);
1853 1853
 				}
1854 1854
 
1855 1855
 				return $c;
@@ -1938,7 +1938,7 @@  discard block
 block discarded – undo
1938 1938
 		// make the expression look it did before being parsed
1939 1939
 		$paddedOp = $op;
1940 1940
 		if ($whiteBefore) {
1941
-			$paddedOp = " ".$paddedOp;
1941
+			$paddedOp = " " . $paddedOp;
1942 1942
 		}
1943 1943
 		if ($whiteAfter) {
1944 1944
 			$paddedOp .= " ";
@@ -2028,7 +2028,7 @@  discard block
 block discarded – undo
2028 2028
 					$out[] = $lval / $rval;
2029 2029
 					break;
2030 2030
 				default:
2031
-					$this->throwError('evaluate error: color op number failed on op '.$op);
2031
+					$this->throwError('evaluate error: color op number failed on op ' . $op);
2032 2032
 			}
2033 2033
 		}
2034 2034
 		return $this->fixColor($out);
@@ -2099,7 +2099,7 @@  discard block
 block discarded – undo
2099 2099
 			case '=<':
2100 2100
 				return $this->toBool($left[1] <= $right[1]);
2101 2101
 			default:
2102
-				$this->throwError('parse error: unknown number operator: '.$op);
2102
+				$this->throwError('parse error: unknown number operator: ' . $op);
2103 2103
 		}
2104 2104
 
2105 2105
 		return array("number", $value, $unit);
@@ -2151,7 +2151,7 @@  discard block
 block discarded – undo
2151 2151
 	{
2152 2152
 		$current = $this->env;
2153 2153
 
2154
-		$isArguments = $name == $this->vPrefix.'arguments';
2154
+		$isArguments = $name == $this->vPrefix . 'arguments';
2155 2155
 		while ($current) {
2156 2156
 			if ($isArguments && isset($current->arguments)) {
2157 2157
 				return array('list', ' ', $current->arguments);
@@ -2176,7 +2176,7 @@  discard block
 block discarded – undo
2176 2176
 		$value = null;
2177 2177
 		foreach ($args as $name => $strValue) {
2178 2178
 			if ($name[0] !== '@') {
2179
-				$name = '@'.$name;
2179
+				$name = '@' . $name;
2180 2180
 			}
2181 2181
 			$parser->count = 0;
2182 2182
 			$parser->buffer = (string) $strValue;
@@ -2230,7 +2230,7 @@  discard block
 block discarded – undo
2230 2230
 	public function compileFile($fname, $outFname = null)
2231 2231
 	{
2232 2232
 		if (!is_readable($fname)) {
2233
-			throw new Exception('load error: failed to find '.$fname);
2233
+			throw new Exception('load error: failed to find ' . $fname);
2234 2234
 		}
2235 2235
 
2236 2236
 		$pi = pathinfo($fname);
@@ -2238,7 +2238,7 @@  discard block
 block discarded – undo
2238 2238
 		$oldImport = $this->importDir;
2239 2239
 
2240 2240
 		$this->importDir = (array) $this->importDir;
2241
-		$this->importDir[] = $pi['dirname'].'/';
2241
+		$this->importDir[] = $pi['dirname'] . '/';
2242 2242
 
2243 2243
 		$this->addParsedFile($fname);
2244 2244
 
@@ -2684,17 +2684,17 @@  discard block
 block discarded – undo
2684 2684
 
2685 2685
 		if (!self::$operatorString) {
2686 2686
 			self::$operatorString =
2687
-				'('.implode('|', array_map(
2687
+				'(' . implode('|', array_map(
2688 2688
 					array('lessc', 'preg_quote'),
2689 2689
 					array_keys(self::$precedence)
2690
-				)).')';
2690
+				)) . ')';
2691 2691
 
2692 2692
 			$commentSingle = Lessc::preg_quote(self::$commentSingle);
2693 2693
 			$commentMultiLeft = Lessc::preg_quote(self::$commentMultiLeft);
2694 2694
 			$commentMultiRight = Lessc::preg_quote(self::$commentMultiRight);
2695 2695
 
2696
-			self::$commentMulti = $commentMultiLeft.'.*?'.$commentMultiRight;
2697
-			self::$whitePattern = '/'.$commentSingle.'[^\n]*\s*|('.self::$commentMulti.')\s*|\s+/Ais';
2696
+			self::$commentMulti = $commentMultiLeft . '.*?' . $commentMultiRight;
2697
+			self::$whitePattern = '/' . $commentSingle . '[^\n]*\s*|(' . self::$commentMulti . ')\s*|\s+/Ais';
2698 2698
 		}
2699 2699
 	}
2700 2700
 
@@ -2727,7 +2727,7 @@  discard block
 block discarded – undo
2727 2727
 		while (false !== $this->parseChunk());
2728 2728
 
2729 2729
 		if ($this->count != strlen($this->buffer)) {
2730
-			$this->throwError('parse error count '.$this->count.' != len buffer '.strlen($this->buffer));
2730
+			$this->throwError('parse error count ' . $this->count . ' != len buffer ' . strlen($this->buffer));
2731 2731
 
2732 2732
 		}
2733 2733
 
@@ -2934,7 +2934,7 @@  discard block
 block discarded – undo
2934 2934
 		// mixin
2935 2935
 		if ($this->mixinTags($tags) &&
2936 2936
 			$this->argumentDef($argv, $isVararg) &&
2937
-			$this->keyword($suffix)  && $this->end()
2937
+			$this->keyword($suffix) && $this->end()
2938 2938
 		) {
2939 2939
 			$tags = $this->fixTags($tags);
2940 2940
 			$this->append(array('mixin', $tags, $argv, $suffix), $s);
@@ -2958,7 +2958,7 @@  discard block
 block discarded – undo
2958 2958
 			"|",
2959 2959
 			array_map(array("lessc", "preg_quote"), $directives)
2960 2960
 		);
2961
-		$pattern = '/^(-[a-z-]+-)?('.$pattern.')$/i';
2961
+		$pattern = '/^(-[a-z-]+-)?(' . $pattern . ')$/i';
2962 2962
 
2963 2963
 		return preg_match($pattern, $dirname);
2964 2964
 	}
@@ -3042,7 +3042,7 @@  discard block
 block discarded – undo
3042 3042
 			$needWhite = $whiteBefore && !$this->inParens;
3043 3043
 
3044 3044
 			$m = array();
3045
-			if ($this->match(self::$operatorString.($needWhite ? '\s' : ''), $m) && self::$precedence[$m[1]] >= $minP) {
3045
+			if ($this->match(self::$operatorString . ($needWhite ? '\s' : ''), $m) && self::$precedence[$m[1]] >= $minP) {
3046 3046
 				if (!$this->inParens && isset($this->env->currentProperty) && $m[1] == "/" && empty($this->env->supressedDivision)) {
3047 3047
 					foreach (self::$supressDivisionProps as $pattern) {
3048 3048
 						if (preg_match($pattern, $this->env->currentProperty)) {
@@ -3204,7 +3204,7 @@  discard block
 block discarded – undo
3204 3204
 		// css hack: \0
3205 3205
 		$m = array();
3206 3206
 		if ($this->literal('\\') && $this->match('([0-9]+)', $m)) {
3207
-			$value = array('keyword', '\\'.$m[1]);
3207
+			$value = array('keyword', '\\' . $m[1]);
3208 3208
 			return true;
3209 3209
 		} else {
3210 3210
 			$this->seek($s);
@@ -3328,7 +3328,7 @@  discard block
 block discarded – undo
3328 3328
 			$stop = array_merge($stop, $rejectStrs);
3329 3329
 		}
3330 3330
 
3331
-		$patt = '(.*?)('.implode("|", $stop).')';
3331
+		$patt = '(.*?)(' . implode("|", $stop) . ')';
3332 3332
 
3333 3333
 		$nestingLevel = 0;
3334 3334
 
@@ -3402,8 +3402,8 @@  discard block
 block discarded – undo
3402 3402
 		$content = array();
3403 3403
 
3404 3404
 		// look for either ending delim , escape, or string interpolation
3405
-		$patt = '([^\n]*?)(@\{|\\\\|'.
3406
-			Lessc::preg_quote($delim).')';
3405
+		$patt = '([^\n]*?)(@\{|\\\\|' .
3406
+			Lessc::preg_quote($delim) . ')';
3407 3407
 
3408 3408
 		$oldWhite = $this->eatWhiteDefault;
3409 3409
 		$this->eatWhiteDefault = false;
@@ -3750,7 +3750,7 @@  discard block
 block discarded – undo
3750 3750
 
3751 3751
 		while (true) {
3752 3752
 			$m = array();
3753
-			if ($this->match('(['.$chars.'0-9]['.$chars.']*)', $m)) {
3753
+			if ($this->match('([' . $chars . '0-9][' . $chars . ']*)', $m)) {
3754 3754
 				$parts[] = $m[1];
3755 3755
 				if ($simple) {
3756 3756
 					break;
@@ -3863,7 +3863,7 @@  discard block
 block discarded – undo
3863 3863
 			if (!empty($sub)) {
3864 3864
 				$name = array('variable', $sub);
3865 3865
 			} else {
3866
-				$name = $this->lessc->vPrefix.$name;
3866
+				$name = $this->lessc->vPrefix . $name;
3867 3867
 			}
3868 3868
 			return true;
3869 3869
 		}
@@ -4050,7 +4050,7 @@  discard block
 block discarded – undo
4050 4050
 			$validChars = $allowNewline ? "." : "[^\n]";
4051 4051
 		}
4052 4052
 		$m = array();
4053
-		if (!$this->match('('.$validChars.'*?)'.Lessc::preg_quote($what), $m, !$until)) {
4053
+		if (!$this->match('(' . $validChars . '*?)' . Lessc::preg_quote($what), $m, !$until)) {
4054 4054
 			return false;
4055 4055
 		}
4056 4056
 		if ($until) {
@@ -4067,7 +4067,7 @@  discard block
 block discarded – undo
4067 4067
 			$eatWhitespace = $this->eatWhiteDefault;
4068 4068
 		}
4069 4069
 
4070
-		$r = '/'.$regex.($eatWhitespace && !$this->writeComments ? '\s*' : '').'/Ais';
4070
+		$r = '/' . $regex . ($eatWhitespace && !$this->writeComments ? '\s*' : '') . '/Ais';
4071 4071
 		if (preg_match($r, $this->buffer, $out, 0, $this->count)) {
4072 4072
 			$this->count += strlen($out[0]);
4073 4073
 			if ($eatWhitespace && $this->writeComments) {
@@ -4105,7 +4105,7 @@  discard block
 block discarded – undo
4105 4105
 		if (is_null($from)) {
4106 4106
 			$from = $this->count;
4107 4107
 		}
4108
-		$r = '/'.$regex.'/Ais';
4108
+		$r = '/' . $regex . '/Ais';
4109 4109
 		$result = preg_match($r, $this->buffer, $out, 0, $from);
4110 4110
 
4111 4111
 		return $result;
@@ -4225,7 +4225,7 @@  discard block
 block discarded – undo
4225 4225
 				case '"':
4226 4226
 				case "'":
4227 4227
 					$m = array();
4228
-					if (preg_match('/'.$min[0].'.*?(?<!\\\\)'.$min[0].'/', $text, $m, 0, $count)) {
4228
+					if (preg_match('/' . $min[0] . '.*?(?<!\\\\)' . $min[0] . '/', $text, $m, 0, $count)) {
4229 4229
 						$count += strlen($m[0]) - 1;
4230 4230
 					}
4231 4231
 					break;
@@ -4250,13 +4250,13 @@  discard block
 block discarded – undo
4250 4250
 				$count += strlen($min[0]);
4251 4251
 			}
4252 4252
 
4253
-			$out .= substr($text, 0, $count).str_repeat("\n", $newlines);
4253
+			$out .= substr($text, 0, $count) . str_repeat("\n", $newlines);
4254 4254
 			$text = substr($text, $count + $skip);
4255 4255
 
4256 4256
 			$min = null;
4257 4257
 		}
4258 4258
 
4259
-		return $out.$text;
4259
+		return $out . $text;
4260 4260
 	}
4261 4261
 }
4262 4262
 
@@ -4291,7 +4291,7 @@  discard block
 block discarded – undo
4291 4291
 
4292 4292
 	public function property($name, $value)
4293 4293
 	{
4294
-		return $name.$this->assignSeparator.$value.";";
4294
+		return $name . $this->assignSeparator . $value . ";";
4295 4295
 	}
4296 4296
 
4297 4297
 	protected function isEmpty($block)
@@ -4323,25 +4323,25 @@  discard block
 block discarded – undo
4323 4323
 			$this->indentLevel++;
4324 4324
 
4325 4325
 			if ($this->breakSelectors) {
4326
-				$selectorSeparator = $this->selectorSeparator.$this->break.$pre;
4326
+				$selectorSeparator = $this->selectorSeparator . $this->break . $pre;
4327 4327
 			} else {
4328 4328
 				$selectorSeparator = $this->selectorSeparator;
4329 4329
 			}
4330 4330
 
4331
-			echo $pre.
4331
+			echo $pre .
4332 4332
 				implode($selectorSeparator, $block->selectors);
4333 4333
 			if ($isSingle) {
4334 4334
 				echo $this->openSingle;
4335 4335
 				$inner = "";
4336 4336
 			} else {
4337
-				echo $this->open.$this->break;
4337
+				echo $this->open . $this->break;
4338 4338
 				$inner = $this->indentStr();
4339 4339
 			}
4340 4340
 		}
4341 4341
 
4342 4342
 		if (!empty($block->lines)) {
4343
-			$glue = $this->break.$inner;
4344
-			echo $inner.implode($glue, $block->lines);
4343
+			$glue = $this->break . $inner;
4344
+			echo $inner . implode($glue, $block->lines);
4345 4345
 			if (!$isSingle && !empty($block->children)) {
4346 4346
 				echo $this->break;
4347 4347
 			}
@@ -4357,9 +4357,9 @@  discard block
 block discarded – undo
4357 4357
 			}
4358 4358
 
4359 4359
 			if ($isSingle) {
4360
-				echo $this->closeSingle.$this->break;
4360
+				echo $this->closeSingle . $this->break;
4361 4361
 			} else {
4362
-				echo $pre.$this->close.$this->break;
4362
+				echo $pre . $this->close . $this->break;
4363 4363
 			}
4364 4364
 
4365 4365
 			$this->indentLevel--;
Please login to merge, or discard this patch.
htdocs/index_dol.php 2 patches
Braces   +9 added lines, -3 removed lines patch added patch discarded remove patch
@@ -44,9 +44,15 @@
 block discarded – undo
44 44
  */
45 45
 
46 46
 $nbmodulesnotautoenabled = count($conf->modules);
47
-if (in_array('fckeditor', $conf->modules)) $nbmodulesnotautoenabled--;
48
-if (in_array('export', $conf->modules)) $nbmodulesnotautoenabled--;
49
-if (in_array('import', $conf->modules)) $nbmodulesnotautoenabled--;
47
+if (in_array('fckeditor', $conf->modules)) {
48
+    $nbmodulesnotautoenabled--;
49
+}
50
+if (in_array('export', $conf->modules)) {
51
+    $nbmodulesnotautoenabled--;
52
+}
53
+if (in_array('import', $conf->modules)) {
54
+    $nbmodulesnotautoenabled--;
55
+}
50 56
 
51 57
 // Check if company name is defined (first install)
52 58
 if (!isset($conf->global->MAIN_INFO_SOCIETE_NOM) || !getDolGlobalString('MAIN_INFO_SOCIETE_NOM')) {
Please login to merge, or discard this patch.
Spacing   +11 added lines, -11 removed lines patch added patch discarded remove patch
@@ -206,7 +206,7 @@  discard block
 block discarded – undo
206 206
     }
207 207
 
208 208
     // Number of supplier proposals open (expired)
209
-    if (isModEnabled('supplier_proposal')  && !getDolGlobalString('MAIN_DISABLE_BLOCK_SUPPLIER') && $user->hasRight('supplier_proposal', 'lire')) {
209
+    if (isModEnabled('supplier_proposal') && !getDolGlobalString('MAIN_DISABLE_BLOCK_SUPPLIER') && $user->hasRight('supplier_proposal', 'lire')) {
210 210
         $langs->load("supplier_proposal");
211 211
         $board = new SupplierProposal($db);
212 212
         $dashboardlines[$board->element . '_opened'] = $board->load_board($user, "opened");
@@ -215,7 +215,7 @@  discard block
 block discarded – undo
215 215
     }
216 216
 
217 217
     // Number of sales orders
218
-    if (isModEnabled('order')  && !getDolGlobalString('MAIN_DISABLE_BLOCK_CUSTOMER') && $user->hasRight('commande', 'lire')) {
218
+    if (isModEnabled('order') && !getDolGlobalString('MAIN_DISABLE_BLOCK_CUSTOMER') && $user->hasRight('commande', 'lire')) {
219 219
         $board = new Commande($db);
220 220
         // Number of customer orders to be shipped (validated and in progress)
221 221
         $dashboardlines[$board->element . '_toship'] = $board->load_board($user, 'toship');
@@ -228,14 +228,14 @@  discard block
 block discarded – undo
228 228
     }
229 229
 
230 230
     // Number of suppliers orders
231
-    if (isModEnabled('supplier_order')  && !getDolGlobalString('MAIN_DISABLE_BLOCK_SUPPLIER') && $user->hasRight('fournisseur', 'commande', 'lire')) {
231
+    if (isModEnabled('supplier_order') && !getDolGlobalString('MAIN_DISABLE_BLOCK_SUPPLIER') && $user->hasRight('fournisseur', 'commande', 'lire')) {
232 232
         $board = new CommandeFournisseur($db);
233 233
         $dashboardlines[$board->element . '_opened'] = $board->load_board($user, "opened");
234 234
         $dashboardlines[$board->element . '_awaiting'] = $board->load_board($user, 'awaiting');
235 235
     }
236 236
 
237 237
     // Number of contract / services enabled (delayed)
238
-    if (isModEnabled('contract')  && !getDolGlobalString('MAIN_DISABLE_BLOCK_CONTRACT') && $user->hasRight('contrat', 'lire')) {
238
+    if (isModEnabled('contract') && !getDolGlobalString('MAIN_DISABLE_BLOCK_CONTRACT') && $user->hasRight('contrat', 'lire')) {
239 239
         $board = new Contrat($db);
240 240
         $dashboardlines[$board->element . '_inactive'] = $board->load_board($user, "inactive");
241 241
         // Number of active services (expired)
@@ -243,7 +243,7 @@  discard block
 block discarded – undo
243 243
     }
244 244
 
245 245
     // Number of tickets open
246
-    if (isModEnabled('ticket')  && !getDolGlobalString('MAIN_DISABLE_BLOCK_TICKET') && $user->hasRight('ticket', 'read')) {
246
+    if (isModEnabled('ticket') && !getDolGlobalString('MAIN_DISABLE_BLOCK_TICKET') && $user->hasRight('ticket', 'read')) {
247 247
         $board = new Ticket($db);
248 248
         $dashboardlines[$board->element . '_opened'] = $board->load_board($user, "opened");
249 249
         // Number of active services (expired)
@@ -263,7 +263,7 @@  discard block
 block discarded – undo
263 263
     }
264 264
 
265 265
     // Number of transactions to conciliate
266
-    if (isModEnabled('bank')  && !getDolGlobalString('MAIN_DISABLE_BLOCK_BANK') && $user->hasRight('banque', 'lire') && !$user->socid) {
266
+    if (isModEnabled('bank') && !getDolGlobalString('MAIN_DISABLE_BLOCK_BANK') && $user->hasRight('banque', 'lire') && !$user->socid) {
267 267
         $board = new Account($db);
268 268
         $nb = $board->countAccountToReconcile(); // Get nb of account to reconciliate
269 269
         if ($nb > 0) {
@@ -273,7 +273,7 @@  discard block
 block discarded – undo
273 273
 
274 274
 
275 275
     // Number of cheque to send
276
-    if (isModEnabled('bank')  && !getDolGlobalString('MAIN_DISABLE_BLOCK_BANK') && $user->hasRight('banque', 'lire') && !$user->socid) {
276
+    if (isModEnabled('bank') && !getDolGlobalString('MAIN_DISABLE_BLOCK_BANK') && $user->hasRight('banque', 'lire') && !$user->socid) {
277 277
         if (!getDolGlobalString('BANK_DISABLE_CHECK_DEPOSIT')) {
278 278
             include_once DOL_DOCUMENT_ROOT . '/compta/paiement/cheque/class/remisecheque.class.php';
279 279
             $board = new RemiseCheque($db);
@@ -292,26 +292,26 @@  discard block
 block discarded – undo
292 292
     }
293 293
 
294 294
     // Number of foundation members
295
-    if (isModEnabled('member')  && !getDolGlobalString('MAIN_DISABLE_BLOCK_ADHERENT') && $user->hasRight('adherent', 'lire') && !$user->socid) {
295
+    if (isModEnabled('member') && !getDolGlobalString('MAIN_DISABLE_BLOCK_ADHERENT') && $user->hasRight('adherent', 'lire') && !$user->socid) {
296 296
         $board = new Adherent($db);
297 297
         $dashboardlines[$board->element . '_shift'] = $board->load_board($user, 'shift');
298 298
         $dashboardlines[$board->element . '_expired'] = $board->load_board($user, 'expired');
299 299
     }
300 300
 
301 301
     // Number of expense reports to approve
302
-    if (isModEnabled('expensereport')  && !getDolGlobalString('MAIN_DISABLE_BLOCK_EXPENSEREPORT') && $user->hasRight('expensereport', 'approve')) {
302
+    if (isModEnabled('expensereport') && !getDolGlobalString('MAIN_DISABLE_BLOCK_EXPENSEREPORT') && $user->hasRight('expensereport', 'approve')) {
303 303
         $board = new ExpenseReport($db);
304 304
         $dashboardlines[$board->element . '_toapprove'] = $board->load_board($user, 'toapprove');
305 305
     }
306 306
 
307 307
     // Number of expense reports to pay
308
-    if (isModEnabled('expensereport')  && !getDolGlobalString('MAIN_DISABLE_BLOCK_EXPENSEREPORT') && $user->hasRight('expensereport', 'to_paid')) {
308
+    if (isModEnabled('expensereport') && !getDolGlobalString('MAIN_DISABLE_BLOCK_EXPENSEREPORT') && $user->hasRight('expensereport', 'to_paid')) {
309 309
         $board = new ExpenseReport($db);
310 310
         $dashboardlines[$board->element . '_topay'] = $board->load_board($user, 'topay');
311 311
     }
312 312
 
313 313
     // Number of holidays to approve
314
-    if (isModEnabled('holiday')  && !getDolGlobalString('MAIN_DISABLE_BLOCK_HOLIDAY') && $user->hasRight('holiday', 'approve')) {
314
+    if (isModEnabled('holiday') && !getDolGlobalString('MAIN_DISABLE_BLOCK_HOLIDAY') && $user->hasRight('holiday', 'approve')) {
315 315
         $board = new Holiday($db);
316 316
         $dashboardlines[$board->element] = $board->load_board($user);
317 317
     }
Please login to merge, or discard this patch.
htdocs/webhook/target_card.php 1 patch
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -43,7 +43,7 @@
 block discarded – undo
43 43
 $contextpage = GETPOST('contextpage', 'aZ') ? GETPOST('contextpage', 'aZ') : 'targetcard'; // To manage different context of search
44 44
 $backtopage = GETPOST('backtopage', 'alpha');
45 45
 $backtopageforcancel = GETPOST('backtopageforcancel', 'alpha');
46
-$lineid   = GETPOSTINT('lineid');
46
+$lineid = GETPOSTINT('lineid');
47 47
 
48 48
 // Initialize technical objects
49 49
 $object = new Target($db);
Please login to merge, or discard this patch.
htdocs/eventorganization/conferenceorboothattendee_card.php 1 patch
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -47,7 +47,7 @@
 block discarded – undo
47 47
 $contextpage = GETPOST('contextpage', 'aZ') ? GETPOST('contextpage', 'aZ') : 'conferenceorboothattendeecard'; // To manage different context of search
48 48
 $backtopage = GETPOST('backtopage', 'alpha');
49 49
 $backtopageforcancel = GETPOST('backtopageforcancel', 'alpha');
50
-$lineid   = GETPOSTINT('lineid');
50
+$lineid = GETPOSTINT('lineid');
51 51
 $mode = GETPOST('mode', 'alpha');
52 52
 
53 53
 $conf_or_booth_id = GETPOSTINT('conforboothid');
Please login to merge, or discard this patch.
htdocs/delivery/class/delivery.class.php 1 patch
Indentation   +10 added lines, -10 removed lines patch added patch discarded remove patch
@@ -271,7 +271,7 @@  discard block
 block discarded – undo
271 271
         }
272 272
     }
273 273
 
274
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
274
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
275 275
     /**
276 276
      *  Create a line
277 277
      *
@@ -284,7 +284,7 @@  discard block
 block discarded – undo
284 284
      */
285 285
     public function create_line($origin_id, $qty, $fk_product, $description, $array_options = [])
286 286
     {
287
-		// phpcs:enable
287
+        // phpcs:enable
288 288
         $error = 0;
289 289
         $idprod = $fk_product;
290 290
 
@@ -546,7 +546,7 @@  discard block
 block discarded – undo
546 546
         }
547 547
     }
548 548
 
549
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
549
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
550 550
     /**
551 551
      *  Creating the delivery slip from an existing shipment
552 552
      *
@@ -556,7 +556,7 @@  discard block
 block discarded – undo
556 556
      */
557 557
     public function create_from_sending($user, $sending_id)
558 558
     {
559
-		// phpcs:enable
559
+        // phpcs:enable
560 560
         global $conf;
561 561
 
562 562
         $expedition = new Expedition($this->db);
@@ -598,7 +598,7 @@  discard block
 block discarded – undo
598 598
         return $this->create($user);
599 599
     }
600 600
 
601
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
601
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
602 602
     /**
603 603
      * Update a livraison line (only extrafields)
604 604
      *
@@ -608,7 +608,7 @@  discard block
 block discarded – undo
608 608
      */
609 609
     public function update_line($id, $array_options = [])
610 610
     {
611
-		// phpcs:enable
611
+        // phpcs:enable
612 612
         global $conf;
613 613
         $error = 0;
614 614
 
@@ -840,7 +840,7 @@  discard block
 block discarded – undo
840 840
         return $result;
841 841
     }
842 842
 
843
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
843
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
844 844
     /**
845 845
      *  Load lines insto $this->lines.
846 846
      *
@@ -848,7 +848,7 @@  discard block
 block discarded – undo
848 848
      */
849 849
     public function fetch_lines()
850 850
     {
851
-		// phpcs:enable
851
+        // phpcs:enable
852 852
         $this->lines = array();
853 853
 
854 854
         $sql = "SELECT ld.rowid, ld.fk_product, ld.description, ld.subprice, ld.total_ht, ld.qty as qty_shipped, ld.fk_origin_line, ";
@@ -928,7 +928,7 @@  discard block
 block discarded – undo
928 928
         return $this->LibStatut($this->statut, $mode);
929 929
     }
930 930
 
931
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
931
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
932 932
     /**
933 933
      *  Return the label of a given status
934 934
      *
@@ -938,7 +938,7 @@  discard block
 block discarded – undo
938 938
      */
939 939
     public function LibStatut($status, $mode)
940 940
     {
941
-		// phpcs:enable
941
+        // phpcs:enable
942 942
         global $langs;
943 943
 
944 944
         if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
Please login to merge, or discard this patch.
htdocs/stripe/ajax/ajax.php 2 patches
Indentation   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -1,6 +1,6 @@
 block discarded – undo
1 1
 <?php
2 2
 
3
- /*  Copyright (C) 2021     Thibault FOUCART    <[email protected]>
3
+    /*  Copyright (C) 2021     Thibault FOUCART    <[email protected]>
4 4
  *
5 5
  * This program is free software; you can redistribute it and/or modify
6 6
  * it under the terms of the GNU General Public License as published by
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -59,7 +59,7 @@
 block discarded – undo
59 59
 }
60 60
 
61 61
 $usestripeterminals = getDolGlobalString('STRIPE_LOCATION');
62
-if (! $usestripeterminals) {
62
+if (!$usestripeterminals) {
63 63
     accessforbidden('Feature to use Stripe terminals not enabled');
64 64
 }
65 65
 
Please login to merge, or discard this patch.
htdocs/opensurvey/class/opensurveysondage.class.php 1 patch
Indentation   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -579,7 +579,7 @@  discard block
 block discarded – undo
579 579
         return $result;
580 580
     }
581 581
 
582
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
582
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
583 583
     /**
584 584
      * Return array of lines
585 585
      *
@@ -587,7 +587,7 @@  discard block
 block discarded – undo
587 587
      */
588 588
     public function fetch_lines()
589 589
     {
590
-		// phpcs:enable
590
+        // phpcs:enable
591 591
         $this->lines = array();
592 592
 
593 593
         $sql = "SELECT id_users, nom as name, reponses";
@@ -736,7 +736,7 @@  discard block
 block discarded – undo
736 736
         return $this->LibStatut($this->status, $mode);
737 737
     }
738 738
 
739
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
739
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
740 740
     /**
741 741
      *  Return label of status
742 742
      *
@@ -746,7 +746,7 @@  discard block
 block discarded – undo
746 746
      */
747 747
     public function LibStatut($status, $mode)
748 748
     {
749
-		// phpcs:enable
749
+        // phpcs:enable
750 750
         global $langs, $conf;
751 751
 
752 752
         if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
Please login to merge, or discard this patch.