Commits

Christoffer Niska committed 625894b

Added the project files.

  • Participants

Comments (0)

Files changed (105)

File LessCompilationBehavior.php

+<?php
+/**
+ * LessCompilationBehavior class file.
+ * @author Christoffer Niska <ChristofferNiska@gmail.com>
+ * @copyright Copyright &copy; Christoffer Niska 2011-
+ * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
+ */
+
+class LessCompilationBehavior extends CBehavior
+{
+    /**
+     * Declares events and the corresponding event handler methods.
+     * @return array events (array keys) and the corresponding event handler methods (array values).
+     */
+	public function events()
+	{
+		return array(
+			'onBeginRequest'=>'beginRequest',
+		);
+	}
+
+	/**
+	 * Actions to take before doing the request.
+	 */
+	public function beginRequest()
+	{
+		if (YII_DEBUG)
+			$this->owner->lessCompiler->compile();
+	}
+}

File LessCompiler.php

+<?php
+/**
+ * LessCompiler class file.
+ * @author Christoffer Niska <ChristofferNiska@gmail.com>
+ * @copyright Copyright &copy; Christoffer Niska 2011-
+ * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
+ */
+
+Yii::setPathOfAlias('Less', dirname(__FILE__).'/vendors/lessphp/lib/Less');
+class LessCompiler extends CApplicationComponent
+{
+	/**
+	 * @property string the base path.
+	 */
+	public $basePath;
+	/**
+	 * @property array the paths for the files to parse.
+	 */
+	public $paths = array();
+	/**
+	 * @property \Less\Parser the less parser.
+	 */
+	protected $_parser;
+
+	/**
+	 * Initializes the component.
+	 * @throws CException if the base path does not exist
+	 */
+	public function init()
+	{
+		if ($this->basePath === null)
+			$this->basePath = Yii::getPathOfAlias('webroot');
+
+		if (!file_exists($this->basePath))
+			throw new CException(__CLASS__.': '.Yii::t('less','Failed to initialize compiler. Base path does not exist!'));
+
+		$this->_parser = new \Less\Parser();
+	}
+
+	/**
+	 * Compiles the less files.
+	 * @throws CException if the source path does not exist
+	 */
+	public function compile()
+	{
+		foreach ($this->paths as $lessPath => $cssPath)
+		{
+			$toPath = $this->basePath.'/'.$cssPath;
+			$fromPath = $this->basePath.'/'.$lessPath;
+
+			if (file_exists($fromPath))
+				file_put_contents($toPath,$this->parse($fromPath));
+			else
+				throw new CException(__CLASS__.': '.Yii::t('less','Failed to compile less file. Source path does not exist!'));
+
+			$this->_parser->clearCss();
+		}
+	}
+
+	/**
+	 * Parses the less code to css.
+	 * @param string $filename the file path to the less file
+	 * @return string the css
+	 */
+	public function parse($filename)
+	{
+		try
+		{
+			$css = $this->_parser->parseFile($filename);
+		}
+		catch (\Less\Exception\ParserException $e)
+		{
+			throw new CException(__CLASS__.': '.Yii::t('less','Failed to compile less file with message: `{message}`.',
+					array('{message}'=>$e->getMessage())));
+		}
+
+		return $css;
+	}
+}

File vendors/lessphp/.gitignore

+.buildpath
+.project
+.settings
+.idea
+.DS_Store?
+ehthumbs.db
+Icon?
+Thumbs.db

File vendors/lessphp/.travis.yml

+language: php
+
+php: 
+  - 5.3
+  - 5.4

File vendors/lessphp/README.md

+less.php
+========
+
+The **dynamic** stylesheet language.
+
+<http://lesscss.org>
+
+about
+-----
+
+This is a PHP port of the official JavaScript version of LESS <http://lesscss.org>.
+
+Most of the code structure remains the same, which should allow for fairly easy updates in the future. That does
+mean this library requires PHP5.3 as it makes heavy use of namespaces, anonymous functions and the shorthand ternary
+operator - `?:` (to replicate the way Javascript will return the value of the first valid operand when using  `||`).
+
+A couple of things have been omitted from this initial version:
+
+- Evaluation of JavaScript expressions within back-ticks (for obvious reasons).
+- Definition of custom functions - will be added to the `\Less\Environment` class.
+- A tidy up of the API is needed.
+
+use
+---
+
+### The parser
+
+```php
+<?php
+
+$parser = new \Less\Parser();
+$parser->getEnvironment()->setCompress(true);
+
+// parse css from a less source file or directly from a string
+$css = $parser
+            ->parseFile($path)
+            ->parse("@color: #4D926F; #header { color: @color; } h2 { color: @color; }")
+            ->getCss();
+```
+
+### The command line tool
+
+The `bin/lessc` command line tool will accept an input (and optionally an output) file name to process.
+
+```bash
+$ ./bin/lessc input.less output.css
+```
+
+### In your website
+
+The `bin/less.php` file can be moved to the directory containing your less source files. Including a links as follows
+will compile and cache the css.
+
+```html
+<link rel="stylesheet" type="text/css" href="/static/less/css.php?bootstrap.less" />
+```
+
+NB: You'll need to update this file to point to the `lib` directory, and also make sure the `./cache` directory is
+writable by the web server.
+
+license
+-------
+
+See `LICENSE` file.

File vendors/lessphp/bin/less.php

+<?php
+
+// Path to the less.php library files
+$lessLibraryPath = __DIR__.'/../lib/';
+
+// Path to the css cache directory
+$cachePath = __DIR__.'/cache/';
+
+// Register an autoload function
+spl_autoload_register(function($className) use ($lessLibraryPath) {
+    $fileName = $lessLibraryPath.str_replace('\\', DIRECTORY_SEPARATOR, $className) . '.php';
+    if (file_exists($fileName)) {
+        require_once $fileName;
+    }
+});
+
+// Create our environment
+$env = new \Less\Environment;
+$env->setCompress(true);
+
+// Grab a comma separated list of files to parse from the query string.
+$files = explode(',', isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : '');
+
+// Only allow inclusion of .less files in this directory
+foreach($files as $key => $file) {
+    $files[$key] = pathinfo($file, PATHINFO_BASENAME);
+    if (!file_exists($files[$key])) {
+        unset($files[$key]);
+    }
+    if (pathinfo($file, PATHINFO_EXTENSION) != 'less') {
+        unset($files[$key]);
+    }
+}
+
+if (count($files)) {
+    
+    // Check for a cached version of the query string hash
+    $hash = md5(array_reduce($files, function($a, $b) {
+        return $a . $b . filemtime($b);
+    }));
+
+    if ( ! file_exists($cachePath.$hash)) {
+
+        // Parse the selected files
+        $parser = new \Less\Parser($env);
+        foreach($files as $file) {
+            try {
+                $parser->parseFile($file);
+            } catch (\Exception $e) {
+                // Skip errors for now
+            }
+        }
+        file_put_contents($cachePath.$hash, $parser->getCss());
+    }
+
+    $modTime = filemtime($cachePath.$hash);
+
+    // Send 304 header if appropriate
+    if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'], $_SERVER['SERVER_PROTOCOL'])) {
+        if ($modTime <= strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
+            header("{$_SERVER['SERVER_PROTOCOL']} 304 Not Modified");
+            exit;
+        }
+    } else {
+
+        // Output the parsed content
+        header('Content-Type: text/css');
+        header('Last-Modified: '.date('r', $modTime));
+        ob_clean();
+        flush();
+        readfile($cachePath.$hash);
+        exit;
+
+    }
+
+}
+
+header("{$_SERVER['SERVER_PROTOCOL']} 404 Page Not Found");

File vendors/lessphp/bin/lessc

+#!/usr/bin/env php
+<?php
+
+/**
+ * Less class file loader
+ *
+ * @param $className
+ * @return void
+ */
+function loadLessClass($className)
+{
+    $fileName = __DIR__.'/../lib/'.str_replace('\\', DIRECTORY_SEPARATOR, $className) . '.php';
+    if (file_exists($fileName)) {
+        require_once $fileName;
+    }
+}
+
+// Register autoload function
+spl_autoload_register('loadLessClass');
+
+// Create our environment
+$env = new \Less\Environment;
+$env->setCompress(false);
+$color = true;
+$silent = false;
+$verbose = false;
+
+// Check for arguments
+array_shift($argv);
+if ( ! count($argv)) {
+    $argv[] = '-h';
+}
+
+foreach($argv as $key => $arg) {
+    if (preg_match('/^--?([a-z][0-9a-z-]*)(?:=([^\s]+))?$/i', $arg, $matches)) {
+
+        $option = $matches[1];
+        $value = isset($matches[2]) ? $matches[2] : false;
+        unset($argv[$key]);
+
+        switch ($option) {
+            case 'v':
+            case 'version':
+                echo "lessc " . \Less\Parser::$version . " (LESS Compiler) [PHP]\n\n";
+                exit();
+            case 's':
+            case 'silent':
+                $silent = true;
+                break;
+            case 'h':
+            case 'help':
+                echo <<<EOD
+Usage: lessc [options] source [destination]
+
+ -h, --help         display this help message
+ -s, --silent       hide error message output
+ -v, --version      display the version number
+ -x, --compress     output compressed css
+
+
+EOD;
+                exit;
+            case 'x':
+            case 'compress':
+                $env->setCompress(true);
+                break;
+        }
+    }
+}
+
+
+if (count($argv) > 1) {
+    $output = array_pop($argv);
+    $inputs = $argv;
+} else {
+    $inputs = $argv;
+    $output = false;
+}
+
+if ( ! count($inputs)) {
+    echo ("lessc: no input files\n");
+    exit;
+}
+
+// parse the selected files (or stdin if '-' is given)
+$parser = new \Less\Parser($env);
+foreach($inputs as $input) {
+    if ($input == '-') {
+        $content = file_get_contents('php://stdin');
+        $parser->parse($content);
+    } else {
+        try {
+            $parser->parseFile($input);
+        } catch (\Exception $e) {
+            if ( ! $silent) {
+                echo ("lessc: ".$e->getMessage()." \n");
+            }
+        }
+    }
+}
+
+if ($output) {
+    file_put_contents($output, $parser->getCss());
+} else {
+    echo $parser->getCss();
+}

File vendors/lessphp/lib/Less/Environment.php

+<?php
+
+namespace Less;
+
+class Environment
+{
+    /**
+     * @var array
+     */
+    public $frames;
+
+    /**
+     * @var bool
+     */
+    public $compress;
+
+    /**
+     * @var bool
+     */
+    public $debug;
+
+    public function __construct()
+    {
+        $this->frames = array();
+        $this->compress = false;
+        $this->debug = false;
+    }
+
+    /**
+     * @return bool
+     */
+    public function getCompress()
+    {
+        return $this->compress;
+    }
+
+    /**
+     * @param bool $compress
+     * @return void
+     */
+    public function setCompress($compress)
+    {
+        $this->compress = $compress;
+    }
+
+    /**
+     * @return bool
+     */
+    public function getDebug()
+    {
+        return $this->debug;
+    }
+
+    /**
+     * @param $debug
+     * @return void
+     */
+    public function setDebug($debug)
+    {
+        $this->debug = $debug;
+    }
+
+    public function unshiftFrame($frame)
+    {
+        array_unshift($this->frames, $frame);
+    }
+
+    public function shiftFrame()
+    {
+        return array_shift($this->frames);
+    }
+
+    public function addFrame($frame)
+    {
+        $this->frames[] = $frame;
+    }
+
+    public function addFrames(array $frames)
+    {
+        $this->frames = array_merge($this->frames, $frames);
+    }
+
+    static public function operate ($op, $a, $b)
+    {
+        switch ($op) {
+            case '+': return $a + $b;
+            case '-': return $a - $b;
+            case '*': return $a * $b;
+            case '/': return $a / $b;
+        }
+    }
+
+    static public function find ($obj, $fun)
+    {
+        foreach($obj as $i => $o) {
+
+            if ($r = call_user_func($fun, $o)) {
+
+                return $r;
+            }
+        }
+        return null;
+    }
+
+    static public function clamp($val)
+    {
+        return min(1, max(0, $val));
+    }
+
+    static public function number($n)
+    {
+        if ($n instanceof \Less\Node\Dimension) {
+            return floatval($n->unit == '%' ? $n->value / 100 : $n->value);
+        } else if (is_numeric($n)) {
+            return $n;
+        } else {
+            throw new \Less\Exception\CompilerException("color functions take numbers as parameters");
+        }
+    }
+
+    public function rgb ($r, $g, $b)
+    {
+        return $this->rgba($r, $g, $b, 1.0);
+    }
+
+    public function rgba($r, $g, $b, $a)
+    {
+        $rgb = array_map(function ($c) { return \Less\Environment::number($c); }, array($r, $g, $b));
+        $a = self::number($a);
+        return new \Less\Node\Color($rgb, $a);
+    }
+
+    public function hsl($h, $s, $l)
+    {
+        return $this->hsla($h, $s, $l, 1.0);
+    }
+
+    public function hsla($h, $s, $l, $a)
+    {
+        $h = fmod(self::number($h), 360) / 360; // Classic % operator will change float to int
+        $s = self::number($s);
+        $l = self::number($l);
+        $a = self::number($a);
+
+        $m2 = $l <= 0.5 ? $l * ($s + 1) : $l + $s - $l * $s;
+        $m1 = $l * 2 - $m2;
+
+        $hue = function ($h) use ($m1, $m2) {
+            $h = $h < 0 ? $h + 1 : ($h > 1 ? $h - 1 : $h);
+            if      ($h * 6 < 1) return $m1 + ($m2 - $m1) * $h * 6;
+            else if ($h * 2 < 1) return $m2;
+            else if ($h * 3 < 2) return $m1 + ($m2 - $m1) * (2/3 - $h) * 6;
+            else                 return $m1;
+        };
+
+        return $this->rgba($hue($h + 1/3) * 255,
+                           $hue($h)       * 255,
+                           $hue($h - 1/3) * 255,
+                           $a);
+    }
+
+    public function hue($color)
+    {
+        $c = $color->toHSL();
+        return new \Less\Node\Dimension(round($c['h']));
+    }
+
+    public function saturation($color)
+    {
+        $c = $color->toHSL();
+        return new \Less\Node\Dimension(round($c['s'] * 100), '%');
+    }
+
+    public function lightness($color)
+    {
+        $c = $color->toHSL();
+        return new \Less\Node\Dimension(round($c['l'] * 100), '%');
+    }
+
+    public function alpha($color)
+    {
+        $c = $color->toHSL();
+        return new \Less\Node\Dimension(round($c['a']));
+    }
+
+    public function saturate($color, $amount)
+    {
+        $hsl = $color->toHSL();
+
+        $hsl['s'] += $amount->value / 100;
+        $hsl['s'] = self::clamp($hsl['s']);
+
+        return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
+    }
+
+    public function desaturate($color, $amount)
+    {
+        $hsl = $color->toHSL();
+
+        $hsl['s'] -= $amount->value / 100;
+        $hsl['s'] = self::clamp($hsl['s']);
+
+        return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
+    }
+
+    public function lighten($color, $amount)
+    {
+        $hsl = $color->toHSL();
+
+        $hsl['l'] += $amount->value / 100;
+        $hsl['l'] = self::clamp($hsl['l']);
+
+        return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
+    }
+
+    public function darken($color, $amount)
+    {
+        $hsl = $color->toHSL();
+
+        $hsl['l'] -= $amount->value / 100;
+        $hsl['l'] = self::clamp($hsl['l']);
+
+        return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
+    }
+
+    public function fadein($color, $amount)
+    {
+        $hsl = $color->toHSL();
+
+        if ($amount->unit == '%') {
+            $hsl['a'] += $amount->value / 100;
+        } else {
+            $hsl['a'] += $amount->value;
+        }
+        $hsl['a'] = self::clamp($hsl['a']);
+
+        return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
+    }
+
+    public function fadeout($color, $amount)
+    {
+        $hsl = $color->toHSL();
+
+        if ($amount->unit == '%') {
+            $hsl['a'] -= $amount->value / 100;
+        } else {
+            $hsl['a'] -= $amount->value;
+        }
+        $hsl['a'] = self::clamp($hsl['a']);
+
+        return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
+    }
+
+    public function fade($color, $amount)
+    {
+        $hsl = $color->toHSL();
+
+        if ($amount->unit == '%') {
+            $hsl['a'] = $amount->value / 100;
+        } else {
+            $hsl['a'] = $amount->value;
+        }
+        $hsl['a'] = self::clamp($hsl['a']);
+
+        return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
+    }
+
+    public function spin($color, $amount)
+    {
+        $hsl = $color->toHSL();
+        $hue = ($hsl['h'] + $amount->value) % 360;
+
+        $hsl['h'] = $hue < 0 ? 360 + $hue : $hue;
+
+        return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
+    }
+
+    //
+    // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein
+    // http://sass-lang.com
+    //
+    public function mix($color1, $color2, $weight = null)
+    {
+        if (!$weight) {
+            $weight = new \Less\Node\Dimension('50', '%');
+        }
+
+        $p = $weight->value / 100.0;
+        $w = $p * 2 - 1;
+        $hsl1 = $color1->toHSL();
+        $hsl2 = $color2->toHSL();
+        $a = $hsl1['a'] - $hsl2['a'];
+
+        $w1 = (((($w * $a) == -1) ? $w : ($w + $a) / (1 + $w * $a)) + 1) / 2;
+        $w2 = 1 - $w1;
+
+        $rgb = array($color1->rgb[0] * $w1 + $color2->rgb[0] * $w2,
+                     $color1->rgb[1] * $w1 + $color2->rgb[1] * $w2,
+                     $color1->rgb[2] * $w1 + $color2->rgb[2] * $w2);
+
+        $alpha = $color1->alpha * $p + $color2->alpha * (1 - $p);
+
+        return new \Less\Node\Color($rgb, $alpha);
+    }
+
+    public function greyscale($color)
+    {
+        return $this->desaturate($color, new \Less\Node\Dimension(100));
+    }
+
+    public function e ($str)
+    {
+        return new \Less\Node\Anonymous($str instanceof \Less\Node\JavaScript ? $str->evaluated : $str);
+    }
+
+    public function escape ($str)
+    {
+        return new \Less\Node\Anonymous(urlencode($str->value));
+    }
+
+    public function _percent()
+    {
+        $numargs = func_num_args();
+        $quoted = func_get_arg(0);
+
+        $args = func_get_args();
+        array_shift($args);
+        $str = $quoted->value;
+
+        foreach($args as $arg) {
+            $str = preg_replace_callback('/%[sda]/i', function($token) use ($arg) {
+                $token = $token[0];
+                $value = stristr($token, 's') ? $arg->value : $arg->toCSS();
+                return preg_match('/[A-Z]$/', $token) ? urlencode($value) : $value;
+            }, $str, 1);
+        }
+        $str = str_replace('%%', '%', $str);
+
+        return new \Less\Node\Quoted('"' . $str . '"', $str);
+    }
+
+    public function round($n)
+    {
+        if ($n instanceof \Less\Node\Dimension) {
+            return new \Less\Node\Dimension(round(self::number($n)), $n->unit);
+        } else if (is_numeric($n)) {
+            return round($n);
+        } else {
+            throw new \Less\Exception\CompilerException("math functions take numbers as parameters");
+        }
+    }
+
+    public function argb($color)
+    {
+        return new \Less\Node\Anonymous($color->toARGB());
+    }
+}

File vendors/lessphp/lib/Less/Exception/CompilerException.php

+<?php
+
+namespace Less\Exception;
+
+class CompilerException extends \Exception
+{
+
+}

File vendors/lessphp/lib/Less/Exception/ParserException.php

+<?php
+
+namespace Less\Exception;
+
+class ParserException extends \Exception
+{
+
+}

File vendors/lessphp/lib/Less/Node/Alpha.php

+<?php
+
+namespace Less\Node;
+
+class Alpha
+{
+    private $value;
+
+    public function __construct($val)
+    {
+        $this->value = $val;
+    }
+
+    public function toCss($env)
+    {
+        return "alpha(opacity=" . (is_string($this->value) ? $this->value : $this->value->toCSS()) . ")";
+    }
+
+    public function compile($env)
+    {
+        if ( ! is_string($this->value)) {
+            $this->value = $this->value->compile($env);
+        }
+        return $this;
+    }
+}

File vendors/lessphp/lib/Less/Node/Anonymous.php

+<?php
+
+namespace Less\Node;
+
+class Anonymous
+{
+    public $value;
+
+    public function __construct($value)
+    {
+        $this->value = is_string($value) ? $value : $value->value;
+    }
+
+    public function toCss()
+    {
+        return $this->value;
+    }
+
+    public function compile($env)
+    {
+        return $this;
+    }
+}

File vendors/lessphp/lib/Less/Node/Call.php

+<?php
+
+namespace Less\Node;
+
+//
+// A function call node.
+//
+
+class Call
+{
+    private $value;
+
+    public function __construct($name, $args, $index)
+    {
+        $this->name = $name;
+        $this->args = $args;
+        $this->index = $index;
+    }
+
+    //
+    // When evaluating a function call,
+    // we either find the function in `tree.functions` [1],
+    // in which case we call it, passing the  evaluated arguments,
+    // or we simply print it out as it appeared originally [2].
+    //
+    // The *functions.js* file contains the built-in functions.
+    //
+    // The reason why we evaluate the arguments, is in the case where
+    // we try to pass a variable to a function, like: `saturate(@color)`.
+    // The function should receive the value, not the variable.
+    //
+    public function compile($env)
+    {
+        $args = array_map(function ($a) use($env) {
+                              return $a->compile($env);
+                          }, $this->args);
+
+        $name = $this->name == '%' ? '_percent' : $this->name;
+
+        if (method_exists($env, $name)) { // 1.
+            try {
+                return call_user_func_array(array($env, $name), $args);
+            } catch (Exception $e) {
+                throw \Less\FunctionCallError("error evaluating function `" . $this->name . "`", $this->index);
+            }
+        } else { // 2.
+
+            return new \Less\Node\Anonymous($this->name .
+                   "(" . implode(', ', array_map(function ($a) use ($env) { return $a->toCSS($env); }, $args)) . ")");
+        }
+    }
+
+    public function toCSS ($env) {
+        return $this->compile($env)->toCSS();
+    }
+
+}

File vendors/lessphp/lib/Less/Node/Color.php

+<?php
+
+namespace Less\Node;
+
+class Color
+{
+    public function __construct($rgb, $a = 1)
+    {
+        if (is_array($rgb)) {
+            $this->rgb = $rgb;
+        } else if (strlen($rgb) == 6) {
+            $this->rgb = array_map(function($c) { return hexdec($c); }, str_split($rgb, 2));
+        } else {
+            $this->rgb = array_map(function($c) { return hexdec($c.$c); }, str_split($rgb, 1));
+        }
+        $this->alpha = is_numeric($a) ? $a : 1;
+    }
+
+    public function compile($env = null)
+    {
+        return $this;
+    }
+
+    //
+    // If we have some transparency, the only way to represent it
+    // is via `rgba`. Otherwise, we use the hex representation,
+    // which has better compatibility with older browsers.
+    // Values are capped between `0` and `255`, rounded and zero-padded.
+    //
+    public function toCSS()
+    {
+        if ($this->alpha < 1.0) {
+            $values = array_map(function ($c) {
+                return round($c);
+            }, $this->rgb);
+            $values[] = $this->alpha;
+
+            return "rgba(" . implode(', ', $values) . ")";
+        } else {
+            return '#' . implode('', array_map(function ($i) {
+                $i = round($i);
+                $i = ($i > 255 ? 255 : ($i < 0 ? 0 : $i));
+                $i = dechex($i);
+                return str_pad($i, 2, '0', STR_PAD_LEFT);
+            }, $this->rgb));
+        }
+    }
+
+    //
+    // Operations have to be done per-channel, if not,
+    // channels will spill onto each other. Once we have
+    // our result, in the form of an integer triplet,
+    // we create a new Color node to hold the result.
+    //
+    public function operate($op, $other) {
+        $result = array();
+
+        if (! ($other instanceof \Less\Node\Color)) {
+            $other = $other->toColor();
+        }
+
+        for ($c = 0; $c < 3; $c++) {
+            $result[$c] = \Less\Environment::operate($op, $this->rgb[$c], $other->rgb[$c]);
+        }
+        return new \Less\Node\Color($result, $this->alpha + $other->alpha);
+    }
+
+    public function toHSL()
+    {
+        $r = $this->rgb[0] / 255;
+        $g = $this->rgb[1] / 255;
+        $b = $this->rgb[2] / 255;
+        $a = $this->alpha;
+
+        $max = max($r, $g, $b);
+        $min = min($r, $g, $b);
+        $l = ($max + $min) / 2;
+        $d = $max - $min;
+
+        if ($max === $min) {
+            $h = $s = 0;
+        } else {
+            $s = $l > 0.5 ? $d / (2 - $max - $min) : $d / ($max + $min);
+
+            switch ($max) {
+                case $r: $h = ($g - $b) / $d + ($g < $b ? 6 : 0); break;
+                case $g: $h = ($b - $r) / $d + 2;                 break;
+                case $b: $h = ($r - $g) / $d + 4;                 break;
+            }
+            $h /= 6;
+        }
+        return array('h' => $h * 360, 's' => $s, 'l' => $l, 'a' => $a );
+    }
+
+    public function toARGB()
+    {
+        $argb = array_merge( (array) round($this->alpha * 255), $this->rgb);
+        return '#' . implode('', array_map(function ($i) {
+            $i = round($i);
+            $i = dechex($i > 255 ? 255 : ($i < 0 ? 0 : $i));
+            return str_pad($i, 2, '0', STR_PAD_LEFT);
+        }, $argb));
+    }
+}

File vendors/lessphp/lib/Less/Node/Combinator.php

+<?php
+
+namespace Less\Node;
+
+class Combinator
+{
+    public $value;
+    public function __construct($value = '')
+    {
+        if ($value == ' ') {
+            $this->value = ' ';
+        } else if ($value == '& ') {
+            $this->value = '& ';
+        } else {
+            $this->value = trim($value);
+        }
+    }
+
+    public function toCSS ($env)
+    {
+        $v = array(
+            ''   => '',
+            ' '  => ' ',
+            '&'  => '',
+            '& ' => ' ',
+            ':'  => ' :',
+            '::' => '::',
+            '+'  => $env->compress ? '+' : ' + ',
+            '~'  => $env->compress ? '~' : ' ~ ',
+            '>'  => $env->compress ? '>' : ' > '
+        );
+
+        return $v[$this->value];
+    }
+}

File vendors/lessphp/lib/Less/Node/Comment.php

+<?php
+
+namespace Less\Node;
+
+class Comment
+{
+    public function __construct($value, $silent)
+    {
+        $this->value = $value;
+        $this->silent = !! $silent;
+    }
+
+    public function compile($env = null)
+    {
+        return $this;
+    }
+
+    public function toCSS($env)
+    {
+        return $env->compress ? '' : $this->value;
+    }
+
+}

File vendors/lessphp/lib/Less/Node/Dimension.php

+<?php
+
+namespace Less\Node;
+
+class Dimension
+{
+    public function __construct($value, $unit = false)
+    {
+        $this->value = floatval($value);
+        $this->unit = $unit;
+    }
+
+    public function compile($env = null) {
+        return $this;
+    }
+
+    public function toColor() {
+        return new \Less\Node\Color(array($this->value, $this->value, $this->value));
+    }
+
+    public function toCSS()
+    {
+        return $this->value . $this->unit;
+    }
+
+    public function __toString()
+    {
+        return $this->toCSS();
+    }
+
+    // In an operation between two Dimensions,
+    // we default to the first Dimension's unit,
+    // so `1px + 2em` will yield `3px`.
+    // In the future, we could implement some unit
+    // conversions such that `100cm + 10mm` would yield
+    // `101cm`.
+    public function operate($op, $other)
+    {
+        return new \Less\Node\Dimension( \Less\Environment::operate($op, $this->value, $other->value), $this->unit ?: $other->unit);
+    }
+}

File vendors/lessphp/lib/Less/Node/Directive.php

+<?php
+
+namespace Less\Node;
+
+class Directive
+{
+    public $name;
+    public $value;
+    public $ruleset;
+
+    public function __construct($name, $value)
+    {
+        $this->name = $name;
+        if (is_array($value)) {
+            $this->ruleset = new Ruleset(false, $value);
+            $this->ruleset->root = true;
+        } else {
+            $this->value = $value;
+        }
+    }
+
+    public function toCSS($ctx, $env)
+    {
+        if ($this->ruleset) {
+            $this->ruleset->root = true;
+            return $this->name . ($env->compress ? '{' : " {\n  ") .
+                   preg_replace('/\n/', "\n  ", trim($this->ruleset->toCSS($ctx, $env))) .
+                   ($env->compress ? '}': "\n}\n");
+        } else {
+            return $this->name . ' ' . $this->value->toCSS() . ";\n";
+        }
+    }
+
+    public function compile($env)
+    {
+        $env->unshiftFrame($this);
+        $this->ruleset = $this->ruleset ? $this->ruleset->compile($env) : null;
+        $env->shiftFrame();
+
+        return $this;
+    }
+    // TODO: Not sure if this is right...
+    public function variable($name)
+    {
+        return $this->ruleset->variable($name);
+    }
+
+    public function find($selector)
+    {
+        return $this->ruleset->find($selector, $this);
+    }
+
+    public function rulesets()
+    {
+        return $this->ruleset->rulesets();
+    }
+
+}

File vendors/lessphp/lib/Less/Node/Element.php

+<?php
+
+namespace Less\Node;
+
+class Element
+{
+    public $combinator;
+    public $value;
+    public function __construct($combinator, $value = '')
+    {
+        if ( ! ($combinator instanceof \Less\Node\Combinator)) {
+            $combinator = new Combinator($combinator);
+        }
+        $this->value = trim($value);
+        $this->combinator = $combinator;
+    }
+
+    public function toCSS ($env)
+    {
+        return $this->combinator->toCSS($env) . $this->value;
+    }
+}

File vendors/lessphp/lib/Less/Node/Expression.php

+<?php
+
+namespace Less\Node;
+
+class Expression
+{
+    public function __construct($value)
+    {
+        $this->value = $value;
+    }
+    public function compile($env) {
+        if (is_array($this->value) && count($this->value) > 1) {
+            return new \Less\Node\Expression(array_map(function ($e) use ($env) {
+                return $e->compile($env);
+            }, $this->value));
+        } else if (is_array($this->value) && count($this->value) == 1) {
+            return $this->value[0]->compile($env);
+        } else {
+            return $this;
+        }
+    }
+
+    public function toCSS ($env) {
+        return implode(' ', array_map(function ($e) use ($env) {
+            return $e->toCSS($env);
+        }, $this->value));
+    }
+
+}

File vendors/lessphp/lib/Less/Node/Import.php

+<?php
+
+namespace Less\Node;
+
+class Import
+{
+    /**
+     * @param $path
+     * @param string $includeDir
+     * @param \Less\Environment|null $env
+     */
+    public function __construct($path, $includeDir = '', $env = null)
+    {
+        $this->_path = $path;
+
+        // The '.less' extension is optional
+        if ($path instanceof \Less\Node\Quoted) {
+            $this->path = preg_match('/\.(le?|c)ss(\?.*)?$/', $path->value) ? $path->value : $path->value . '.less';
+        } else {
+            $this->path = isset($path->value->value) ? $path->value->value : $path->value;
+        }
+
+        $this->css = preg_match('/css(\?.*)?$/', $this->path);
+
+        // Only pre-compile .less files
+        if ( ! $this->css) {
+
+            $less = $includeDir . '/' . $this->path;
+            $parser = new \Less\Parser($env);
+            $this->root = $parser->parseFile($less, true);
+
+        }
+    }
+
+    public function toCSS()
+    {
+        if ($this->css) {
+            return "@import " . $this->_path->toCss() . ";\n";
+        } else {
+            return "";
+        }
+    }
+
+    public function compile($env)
+    {
+        if ($this->css) {
+            return $this;
+        } else {
+            $ruleset = new \Less\Node\Ruleset(null, isset($this->root->rules) ? $this->root->rules : array());
+            for ($i = 0; $i < count($ruleset->rules); $i++) {
+                if ($ruleset->rules[$i] instanceof \Less\Node\Import && ! $ruleset->rules[$i]->css) {
+
+                    $newRules = $ruleset->rules[$i]->compile($env);
+                    $ruleset->rules = array_merge(
+                        array_slice($ruleset->rules, 0, $i),
+                        (array) $newRules,
+                        array_slice($ruleset->rules, $i + 1)
+                    );
+                }
+            }
+
+            if ($env->getDebug()) {
+                array_unshift($ruleset->rules, new \Less\Node\Comment('/**** Start imported file `' . $this->path."` ****/\n", false));
+                array_push($ruleset->rules,    new \Less\Node\Comment('/**** End imported file `' . $this->path."` ****/\n", false));
+            }
+
+            return $ruleset->rules;
+        }
+
+    }
+}
+

File vendors/lessphp/lib/Less/Node/Javascript.php

+<?php
+
+namespace Less\Node;
+
+class Javascript
+{
+    public function __construct($string, $index, $escaped)
+    {
+        $this->escaped = $escaped;
+        $this->expression = $string;
+        $this->index = $index;
+    }
+
+    public function compile($env)
+    {
+        return $this;
+    }
+
+    public function toCss($env)
+    {
+        return $env->compress ? '' : '/* Sorry, can not do JavaScript evaluation in PHP... :( */';
+    }
+}

File vendors/lessphp/lib/Less/Node/Keyword.php

+<?php
+
+namespace Less\Node;
+
+class Keyword
+{
+    public function __construct($value)
+    {
+        $this->value = $value;
+    }
+
+    public function toCss()
+    {
+        return $this->value;
+    }
+
+    public function compile($env)
+    {
+        return $this;
+    }
+}

File vendors/lessphp/lib/Less/Node/Mixin/Call.php

+<?php
+
+namespace Less\Node\Mixin;
+
+class Call
+{
+    public function __construct($elements, $args, $index)
+    {
+        $this->selector =  new \Less\Node\Selector($elements);
+        $this->arguments = $args;
+        $this->index = $index;
+    }
+
+    public function compile($env)
+    {
+        $rules = array();
+        $match = false;
+
+        foreach($env->frames as $frame) {
+
+            if ($mixins = $frame->find($this->selector, null, $env)) {
+
+                $args = array_map(function ($a) use ($env) {
+                    return $a->compile($env);
+                }, $this->arguments);
+
+                foreach ($mixins as $mixin) {
+                    if ($mixin->match($args, $env)) {
+                        try {
+                            if ($env->getDebug() && isset($mixin->name)) {
+                                $rules[] = new \Less\Node\Comment('/**** Start rules from `' . $mixin->name.'` (defined in `'.$mixin->filename.'` on line: ' . $mixin->line.') ****/', false);
+                            }
+                            $rules = array_merge($rules, $mixin->compile($env, $this->arguments)->rules);
+                            if ($env->getDebug() && isset($mixin->name)) {
+                                $rules[] = new \Less\Node\Comment('/**** End rules from `' . $mixin->name.'` ****/', false);
+                            }
+                            $match = true;
+                        } catch (Exception $e) {
+                            throw new \Less\Exception\CompilerException($e->message, $e->index);
+                        }
+                    }
+                }
+                if ($match) {
+                    return $rules;
+                } else {
+                    throw new \Less\Exception\CompilerException('No matching definition was found for `'.
+                                                  trim($this->selector->toCSS($env)) . '(' .
+                                                  implode(', ', array_map(function ($a) use($env) {
+                                                    return $a->toCss($env);
+                                                  }, $this->arguments)) . ')`',
+                                                  $this->index);
+                }
+            }
+        }
+
+        throw new \Less\Exception\CompilerException(trim($this->selector->toCSS($env)) . " is undefined", $this->index);
+    }
+}
+
+

File vendors/lessphp/lib/Less/Node/Mixin/Definition.php

+<?php
+
+namespace Less\Node\Mixin;
+
+class Definition extends \Less\Node\Ruleset
+{
+    public function __construct($name, $params, $rules, $filename, $line)
+    {
+        $this->line = $line;
+        $this->filename = $filename;
+        $this->name = $name;
+        $this->selectors = array(new \Less\Node\Selector(array( new \Less\Node\Element(null, $name))));
+        $this->params = $params;
+        $this->arity = count($params);
+        $this->rules = $rules;
+        $this->lookups = array();
+        $this->required = array_reduce($params, function ($count, $p) {
+            if (! isset($p['name']) || ($p['name'] && !isset($p['value']))) {
+                return $count + 1;
+            } else {
+                return $count;
+            }
+        });
+        $this->frames = array();
+    }
+
+    public function toCss($context, $env)
+    {
+        return '';
+    }
+
+    public function compile($env, $args = array())
+    {
+        $frame = new \Less\Node\Ruleset(null, array());
+
+        foreach($this->params as $i => $param) {
+            if (isset($param['name']) && $param['name']) {
+                if ($val = (isset($args[$i]) ? $args[$i] : $param['value'])) {
+                    $rule = new \Less\Node\Rule($param['name'], $val->compile($env));
+                    array_unshift($frame->rules, $rule);
+                } else {
+                    throw new \Less\Exception\CompilerException("wrong number of arguments for " . $this->name . ' (' . count($args) . ' for ' . $this->arity . ')');
+                }
+            }
+        }
+        $_arguments = array();
+        for ($i = 0; $i < max(count($this->params), count($args)); $i++) {
+            $_arguments[] = isset($args[$i]) ? $args[$i] : $this->params[$i]['value'];
+        }
+        $ex = new \Less\Node\Expression($_arguments);
+        array_unshift($frame->rules, new \Less\Node\Rule('@arguments', $ex->compile($env)));
+
+        // duplicate the environment, adding new frames.
+        $ruleSetEnv = new \Less\Environment();
+        $ruleSetEnv->addFrame($this);
+        $ruleSetEnv->addFrame($frame);
+        $ruleSetEnv->addFrames($this->frames);
+        $ruleSetEnv->addFrames($env->frames);
+        $ruleset = new \Less\Node\Ruleset(null, $this->rules);
+
+        return $ruleset->compile($ruleSetEnv);
+    }
+
+    public function match($args, $env = NULL)
+    {
+        if (count($args) < $this->required) {
+            return false;
+        }
+        if (($this->required > 0) && (count($args) > count($this->params))) {
+            return false;
+        }
+
+        $len = min(count($args), $this->arity);
+
+        for ($i = 0; $i < $len; $i++) {
+            if ( ! isset($this->params[$i]['name'])) {
+                if ($args[$i]->compile($env)->toCSS() != $this->params[$i]['value']->compile($env)->toCSS()) {
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+}

File vendors/lessphp/lib/Less/Node/Operation.php

+<?php
+
+namespace Less\Node;
+
+class Operation
+{
+    public function __construct($op, $operands)
+    {
+        $this->op = trim($op);
+        $this->operands = $operands;
+    }
+
+    public function compile($env)
+    {
+        $a = $this->operands[0]->compile($env);
+        $b = $this->operands[1]->compile($env);
+
+        if ($a instanceof \Less\Node\Dimension && $b instanceof \Less\Node\Color) {
+            if ($this->op === '*' || $this->op === '+') {
+                $temp = $b;
+                $b = $a;
+                $a = $temp;
+            } else {
+                throw new \Less\CompilerError("Can't subtract or divide a color from a number");
+            }
+        }
+
+        return $a->operate($this->op, $b);
+    }
+}

File vendors/lessphp/lib/Less/Node/Quoted.php

+<?php
+
+namespace Less\Node;
+
+class Quoted
+{
+    public $escaped;
+    public $content;
+
+    public function __construct($str, $content, $escaped = false, $i = false)
+    {
+        $this->escaped = $escaped;
+        $this->value = $content ?: '';
+        $this->quote = $str[0];
+        $this->index = $i;
+    }
+
+    public function toCSS ()
+    {
+        if ($this->escaped) {
+            return $this->value;
+        } else {
+            return $this->quote . $this->value . $this->quote;
+        }
+    }
+
+    public function compile($env)
+    {
+        $that = $this;
+        $value = preg_replace_callback('/`([^`]+)`/', function ($matches) use ($env, $that) {
+                    $js = \Less\Node\JavaScript($matches[1], $that->index, true);
+                    return $js->eval($env)->value;
+                 }, $this->value);
+        $value = preg_replace_callback('/@\{([\w-]+)\}/', function ($matches) use ($env, $that) {
+                    $v = new \Less\Node\Variable('@' . $matches[1], $that->index);
+                    $v = $v->compile($env);
+                    return isset($v->value) ? $v->value : $v->toCSS();
+                 }, $value);
+
+        return new \Less\Node\Quoted($this->quote . $value . $this->quote, $value, $this->escaped, $this->index);
+    }
+}

File vendors/lessphp/lib/Less/Node/Rule.php

+<?php
+
+namespace Less\Node;
+
+class Rule
+{
+    public function __construct($name, $value, $important = null, $index = null)
+    {
+        $this->name = $name;
+        $this->value = ($value instanceof \Less\Node\Value) ? $value : new \Less\Node\Value(array($value));
+        $this->important = $important ? ' ' . trim($important) : '';
+        $this->index = $index;
+
+        if ($name[0] === '@') {
+            $this->variable = true;
+        } else {
+            $this->variable = false;
+        }
+    }
+
+
+    public function toCSS ($env)
+    {
+        if ($this->variable) {
+            return "";
+        } else {
+
+            return $this->name . ($env->compress ? ':' : ': ') . $this->value->toCSS($env) . $this->important . ";";
+        }
+    }
+
+    public function compile ($context)
+    {
+        return new \Less\Node\Rule($this->name, $this->value->compile($context), $this->important, $this->index);
+    }
+
+}

File vendors/lessphp/lib/Less/Node/Ruleset.php

+<?php
+
+namespace Less\Node;
+
+class Ruleset
+{
+    public $selectors;
+    public $rules;
+    protected $lookups;
+    public $root;
+    private $_variables;
+    private $_rulesets;
+
+    public function __construct($selectors, $rules)
+    {
+        $this->selectors = $selectors;
+        $this->rules = (array) $rules;
+        $this->lookups = array();
+
+    }
+
+    public function compile($env)
+    {
+        $ruleset = new Ruleset($this->selectors, $this->rules);
+        $ruleset->root = $this->root;
+
+        // push the current ruleset to the frames stack
+        $env->unshiftFrame($ruleset);
+
+        // Evaluate imports
+        if ($ruleset->root) {
+            for($i = 0; $i < count($ruleset->rules); $i++) {
+                if ($ruleset->rules[$i] instanceof \Less\Node\Import && ! $ruleset->rules[$i]->css) {
+                    $newRules = $ruleset->rules[$i]->compile($env);
+                    $ruleset->rules = array_merge(
+                        array_slice($ruleset->rules, 0, $i),
+                        (array) $newRules,
+                        array_slice($ruleset->rules, $i + 1)
+                    );
+                }
+            }
+        }
+
+        // Store the frames around mixin definitions,
+        // so they can be evaluated like closures when the time comes.
+        foreach($ruleset->rules as $i => $rule) {
+            if ($rule instanceof \Less\Node\Mixin\Definition) {
+                $ruleset->rules[$i]->frames = $env->frames;
+            }
+        }
+
+        // Evaluate mixin calls.
+        for($i = 0; $i < count($ruleset->rules); $i++) {
+            if ($ruleset->rules[$i] instanceof \Less\Node\Mixin\Call) {
+                $newRules = $ruleset->rules[$i]->compile($env);
+                $ruleset->rules = array_merge(
+                    array_slice($ruleset->rules, 0, $i),
+                    $newRules,
+                    array_slice($ruleset->rules, $i + 1)
+                );
+            }
+        }
+
+        // Evaluate everything else
+        foreach($ruleset->rules as $i => $rule) {
+            if (! ($rule instanceof \Less\Node\Mixin\Definition)) {
+                $ruleset->rules[$i] = is_string($rule) ? $rule : $rule->compile($env);
+            }
+        }
+
+        // Pop the stack
+        $env->shiftFrame();
+
+        return $ruleset;
+    }
+
+    public function match($args)
+    {
+        return ! is_array($args) || count($args) === 0;
+    }
+
+    public function variables()
+    {
+        if ( ! $this->_variables) {
+            $this->_variables = array_reduce($this->rules, function ($hash, $r) {
+                if ($r instanceof \Less\Node\Rule && $r->variable === true) {
+                    $hash[$r->name] = $r;
+                }
+                return $hash;
+            });
+        }
+
+        return $this->_variables;
+    }
+
+    public function variable($name)
+    {
+        $vars = $this->variables();
+
+        return isset($vars[$name]) ? $vars[$name] : null;
+    }
+
+    public function rulesets ()
+    {
+        if ($this->_rulesets) {
+            return $this->_rulesets;
+        } else {
+            return $this->_rulesets = array_filter($this->rules, function ($r) {
+                return ($r instanceof \Less\Node\Ruleset) || ($r instanceof \Less\Node\Mixin\Definition);
+            });
+        }
+    }
+
+    public function find ($selector, $self = null, $env = null)
+    {
+        $self = $self ?: $this;
+        $rules = array();
+        $key = $selector->toCSS($env);
+
+        if (array_key_exists($key, $this->lookups)) {
+            return $this->lookups[$key];
+        }
+
+        foreach($this->rulesets() as $rule) {
+            if ($rule !== $self) {
+                foreach($rule->selectors as $ruleSelector) {
+                    if ($selector->match($ruleSelector)) {
+
+                        if (count($selector->elements) > count($ruleSelector->elements)) {
+                            $rules = array_merge($rules, $rule->find( new \Less\Node\Selector(array_slice($selector->elements, 1)), $self, $env));
+                        } else {
+                            $rules[] = $rule;
+                        }
+                        break;
+                    }
+                }
+            }
+        }
+
+        $this->lookups[$key] = $rules;
+
+        return $this->lookups[$key];
+    }
+
+    //
+    // Entry point for code generation
+    //
+    //     `context` holds an array of arrays.
+    //
+    public function toCSS ($context, $env)
+    {
+        $css = array();      // The CSS output
+        $rules = array();    // node.Rule instances
+        $rulesets = array(); // node.Ruleset instances
+        $paths = array();    // Current selectors
+
+        if (! $this->root) {
+            if (count($context) === 0) {
+                $paths = array_map(function ($s) { return array($s); }, $this->selectors);
+            } else {
+                $this->joinSelectors($paths, $context, $this->selectors);
+            }
+        }
+
+        // Compile rules and rulesets
+        foreach($this->rules as $rule) {
+            if (isset($rule->rules) || ($rule instanceof \Less\Node\Directive)) {
+                $rulesets[] = $rule->toCSS($paths, $env);
+            } else if ($rule instanceof \Less\Node\Comment) {
+                if (!$rule->silent) {
+                    if ($this->root) {
+                        $rulesets[] = $rule->toCSS($env);
+                    } else {
+                        $rules[] = $rule->toCSS($env);
+                    }
+                }
+            } else {
+                if (method_exists($rule, 'toCSS') && ( ! isset($rule->variable) ||  ! $rule->variable)) {
+                    $rules[] = $rule->toCSS($env);
+                } else if (isset($rule->value) && $rule->value && ! $rule->variable) {
+                    $rules[] = (string) $rule->value;
+                }
+            }
+        }
+
+        $rulesets = implode('', $rulesets);
+
+        // If this is the root node, we don't render
+        // a selector, or {}.
+        // Otherwise, only output if this ruleset has rules.
+        if ($this->root) {
+            $css[] = implode($env->compress ? '' : "\n", $rules);
+        } else {
+            if (count($rules)) {
+                $selector = array_map(function ($p) use ($env) {
+                    return trim(implode('', array_map(function ($s) use ($env) {
+                        return $s->toCSS($env);
+                    }, $p)));
+                }, $paths);
+
+                $selector = implode($env->compress ? ',' : (count($paths) > 3 ? ",\n" : ', '), $selector);
+
+                $css[] = $selector;
+                $css[] = ($env->compress ? '{' : " {\n  ") .
+                         implode($env->compress ? '' : "\n  ", $rules) .
+                         ($env->compress ? '}' : "\n}\n");
+            }
+        }
+        $css[] = $rulesets;
+
+        return implode('', $css) . ($env->compress ? "\n" : '');
+    }
+
+    public function joinSelectors (&$paths, $context, $selectors)
+    {
+        foreach($selectors as $selector) {
+            $this->joinSelector($paths, $context, $selector);
+        }
+    }
+
+    public function joinSelector (&$paths, $context, $selector)
+    {
+        $before = array();
+        $after = array();
+        $beforeElements = array();
+        $afterElements = array();
+        $hasParentSelector = false;
+
+        foreach($selector->elements as $el) {
+            if (strlen($el->combinator->value) > 0 && $el->combinator->value[0] === '&') {
+                $hasParentSelector = true;
+            }
+            if ($hasParentSelector) {
+                $afterElements[] = $el;
+            } else {
+                $beforeElements[] = $el;
+            }
+        }
+        if (! $hasParentSelector) {
+            $afterElements = $beforeElements;
+            $beforeElements = array();
+        }
+        if (count($beforeElements) > 0) {
+            $before[] = new \Less\Node\Selector($beforeElements);
+        }
+        if (count($afterElements) > 0) {
+            $after[] = new \Less\Node\Selector($afterElements);
+        }
+        foreach($context as $c) {